19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
20c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottierimport android.hardware.SensorManager;
21d348bb4feff72d047a1037537be2d334a00c380cGilles Debunneimport android.os.Build;
22d348bb4feff72d047a1037537be2d334a00c380cGilles Debunneimport android.util.FloatMath;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.ViewConfiguration;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.animation.AnimationUtils;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.animation.Interpolator;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This class encapsulates scrolling.  The duration of the scroll
309d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell * can be passed in the constructor and specifies the maximum time that
319d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell * the scrolling animation should take.  Past this time, the scrolling is
329d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell * automatically moved to its final stage and computeScrollOffset()
339d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell * will always return false to indicate that scrolling is over.
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class Scroller  {
366579b0b4ac0e781efab044aaaf3f66447cf5e067Adam Powell    private int mMode;
3752964243dc95f74ac2ab9a96d031a36931a11931Gilles Debunne
389d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mStartX;
399d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mStartY;
409d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mFinalX;
419d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mFinalY;
429d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
439d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mMinX;
449d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mMaxX;
459d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mMinY;
469d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mMaxY;
479d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
489d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mCurrX;
499d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mCurrY;
509d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private long mStartTime;
519d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private int mDuration;
529d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private float mDurationReciprocal;
539d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private float mDeltaX;
549d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private float mDeltaY;
559d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private boolean mFinished;
569d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private Interpolator mInterpolator;
57d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    private boolean mFlywheel;
589d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
599d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    private float mVelocity;
60990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private float mCurrVelocity;
61990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private int mDistance;
62990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
63990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private float mFlingFriction = ViewConfiguration.getScrollFriction();
6452964243dc95f74ac2ab9a96d031a36931a11931Gilles Debunne
656579b0b4ac0e781efab044aaaf3f66447cf5e067Adam Powell    private static final int DEFAULT_DURATION = 250;
666579b0b4ac0e781efab044aaaf3f66447cf5e067Adam Powell    private static final int SCROLL_MODE = 0;
676579b0b4ac0e781efab044aaaf3f66447cf5e067Adam Powell    private static final int FLING_MODE = 1;
6852964243dc95f74ac2ab9a96d031a36931a11931Gilles Debunne
69990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private static float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9));
70990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1)
71990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private static final float START_TENSION = 0.5f;
72990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private static final float END_TENSION = 1.0f;
73990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private static final float P1 = START_TENSION * INFLEXION;
74990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION);
75990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
76d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    private static final int NB_SAMPLES = 100;
77990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1];
78990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1];
79d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
804bede9e425875542976a422222510fa4056a8339Romain Guy    private float mDeceleration;
814bede9e425875542976a422222510fa4056a8339Romain Guy    private final float mPpi;
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
83990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    // A context-specific coefficient adjusted to physical values.
84990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private float mPhysicalCoeff;
85990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
86d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    static {
87d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        float x_min = 0.0f;
88990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        float y_min = 0.0f;
89990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        for (int i = 0; i < NB_SAMPLES; i++) {
90990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell            final float alpha = (float) i / NB_SAMPLES;
91990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
92d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float x_max = 1.0f;
93d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float x, tx, coef;
94d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            while (true) {
95d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                x = x_min + (x_max - x_min) / 2.0f;
96d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                coef = 3.0f * x * (1.0f - x);
97990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x;
98990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                if (Math.abs(tx - alpha) < 1E-5) break;
99990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                if (tx > alpha) x_max = x;
100d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                else x_min = x;
101d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            }
102990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell            SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x;
103990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
104990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell            float y_max = 1.0f;
105990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell            float y, dy;
106990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell            while (true) {
107990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                y = y_min + (y_max - y_min) / 2.0f;
108990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                coef = 3.0f * y * (1.0f - y);
109990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y;
110990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                if (Math.abs(dy - alpha) < 1E-5) break;
111990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                if (dy > alpha) y_max = y;
112990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                else y_min = y;
113990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell            }
114990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell            SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y;
115d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        }
116990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f;
117637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell
118637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        // This controls the viscous fluid effect (how much of it)
119637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        sViscousFluidScale = 8.0f;
120637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        // must be set to 1.0 (used in viscousFluid())
121637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        sViscousFluidNormalize = 1.0f;
122637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
123990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
124d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    }
125d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
126637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell    private static float sViscousFluidScale;
127637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell    private static float sViscousFluidNormalize;
128637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1309d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Create a Scroller with the default duration and interpolator.
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Scroller(Context context) {
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, null);
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
136637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell    /**
137637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     * Create a Scroller with the specified interpolator. If the interpolator is
138637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
139637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     * be in effect for apps targeting Honeycomb or newer.
140637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     */
141d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    public Scroller(Context context, Interpolator interpolator) {
142d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        this(context, interpolator,
143d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
144d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    }
145d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Create a Scroller with the specified interpolator. If the interpolator is
148637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     * null, the default (viscous) interpolator will be used. Specify whether or
149637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     * not to support progressive "flywheel" behavior in flinging.
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
151d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
1529d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = true;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInterpolator = interpolator;
1544bede9e425875542976a422222510fa4056a8339Romain Guy        mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
1554bede9e425875542976a422222510fa4056a8339Romain Guy        mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
156d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        mFlywheel = flywheel;
157990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
158990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
1594bede9e425875542976a422222510fa4056a8339Romain Guy    }
1604bede9e425875542976a422222510fa4056a8339Romain Guy
1614bede9e425875542976a422222510fa4056a8339Romain Guy    /**
1624bede9e425875542976a422222510fa4056a8339Romain Guy     * The amount of friction applied to flings. The default value
1634bede9e425875542976a422222510fa4056a8339Romain Guy     * is {@link ViewConfiguration#getScrollFriction}.
1644bede9e425875542976a422222510fa4056a8339Romain Guy     *
165d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne     * @param friction A scalar dimension-less value representing the coefficient of
1664bede9e425875542976a422222510fa4056a8339Romain Guy     *         friction.
1674bede9e425875542976a422222510fa4056a8339Romain Guy     */
1684bede9e425875542976a422222510fa4056a8339Romain Guy    public final void setFriction(float friction) {
169a655c635820425a4c98314c3ba02eb382229b45dRomain Guy        mDeceleration = computeDeceleration(friction);
170990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        mFlingFriction = friction;
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1729d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
1734bede9e425875542976a422222510fa4056a8339Romain Guy    private float computeDeceleration(float friction) {
1744bede9e425875542976a422222510fa4056a8339Romain Guy        return SensorManager.GRAVITY_EARTH   // g (m/s^2)
1754bede9e425875542976a422222510fa4056a8339Romain Guy                      * 39.37f               // inch/meter
1764bede9e425875542976a422222510fa4056a8339Romain Guy                      * mPpi                 // pixels per inch
1774bede9e425875542976a422222510fa4056a8339Romain Guy                      * friction;
1784bede9e425875542976a422222510fa4056a8339Romain Guy    }
1794bede9e425875542976a422222510fa4056a8339Romain Guy
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns whether the scroller has finished scrolling.
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return True if the scroller has finished scrolling, false otherwise.
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final boolean isFinished() {
1879d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mFinished;
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1899d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Force the finished field to a particular value.
1929d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param finished The new finished value.
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final void forceFinished(boolean finished) {
1969d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = finished;
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1989d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns how long the scroll event will take, in milliseconds.
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The duration of the scroll in milliseconds.
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getDuration() {
2059d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mDuration;
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2079d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2099d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Returns the current X offset in the scroll.
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The new X offset as an absolute distance from the origin.
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getCurrX() {
2149d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mCurrX;
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2169d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2189d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Returns the current Y offset in the scroll.
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The new Y offset as an absolute distance from the origin.
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getCurrY() {
2239d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mCurrY;
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2259d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
227278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark     * Returns the current velocity.
2289d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
2299d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * @return The original velocity less the deceleration. Result may be
2309d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * negative.
231278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark     */
232278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark    public float getCurrVelocity() {
233990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        return mMode == FLING_MODE ?
234990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                mCurrVelocity : mVelocity - mDeceleration * timePassed() / 2000.0f;
235278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark    }
236278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark
237278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark    /**
2389d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Returns the start X offset in the scroll.
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The start X offset as an absolute distance from the origin.
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getStartX() {
2439d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mStartX;
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2459d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2479d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Returns the start Y offset in the scroll.
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The start Y offset as an absolute distance from the origin.
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getStartY() {
2529d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mStartY;
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2549d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns where the scroll will end. Valid only for "fling" scrolls.
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The final X offset as an absolute distance from the origin.
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getFinalX() {
2619d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mFinalX;
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2639d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns where the scroll will end. Valid only for "fling" scrolls.
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The final Y offset as an absolute distance from the origin.
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getFinalY() {
2709d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mFinalY;
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2749d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Call this when you want to know the new location.  If it returns true,
2759d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * the animation is not yet finished.  loc will be altered to provide the
2769d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * new location.
2779d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     */
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean computeScrollOffset() {
2799d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        if (mFinished) {
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2839d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
2849d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2859d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        if (timePassed < mDuration) {
2869d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell            switch (mMode) {
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case SCROLL_MODE:
288d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                float x = timePassed * mDurationReciprocal;
2899d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2909d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                if (mInterpolator == null)
2919d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                    x = viscousFluid(x);
2929d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                else
2939d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                    x = mInterpolator.getInterpolation(x);
2949d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2959d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrX = mStartX + Math.round(x * mDeltaX);
2969d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrY = mStartY + Math.round(x * mDeltaY);
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case FLING_MODE:
299d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                final float t = (float) timePassed / mDuration;
300d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                final int index = (int) (NB_SAMPLES * t);
301990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                float distanceCoef = 1.f;
302990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                float velocityCoef = 0.f;
303990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                if (index < NB_SAMPLES) {
304990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                    final float t_inf = (float) index / NB_SAMPLES;
305990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                    final float t_sup = (float) (index + 1) / NB_SAMPLES;
306990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                    final float d_inf = SPLINE_POSITION[index];
307990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                    final float d_sup = SPLINE_POSITION[index + 1];
308990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                    velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
309990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                    distanceCoef = d_inf + (t - t_inf) * velocityCoef;
310990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                }
311990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
312990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell                mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
3139d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
314d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
3159d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                // Pin to mMinX <= mCurrX <= mMaxX
3169d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrX = Math.min(mCurrX, mMaxX);
3179d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrX = Math.max(mCurrX, mMinX);
3189d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
319d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
3209d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                // Pin to mMinY <= mCurrY <= mMaxY
3219d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrY = Math.min(mCurrY, mMaxY);
3229d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrY = Math.max(mCurrY, mMinY);
3231b088be8bb304e4ab7a1896d3ec3b9df9d9cf68eAdam Powell
3241b088be8bb304e4ab7a1896d3ec3b9df9d9cf68eAdam Powell                if (mCurrX == mFinalX && mCurrY == mFinalY) {
3251b088be8bb304e4ab7a1896d3ec3b9df9d9cf68eAdam Powell                    mFinished = true;
3261b088be8bb304e4ab7a1896d3ec3b9df9d9cf68eAdam Powell                }
3271b088be8bb304e4ab7a1896d3ec3b9df9d9cf68eAdam Powell
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
3299d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell            }
3309d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        }
3319d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        else {
3329d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell            mCurrX = mFinalX;
3339d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell            mCurrY = mFinalY;
3349d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell            mFinished = true;
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3389d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Start scrolling by providing a starting point and the distance to travel.
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The scroll will use the default value of 250 milliseconds for the
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * duration.
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startX Starting horizontal scroll offset in pixels. Positive
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        numbers will scroll the content to the left.
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startY Starting vertical scroll offset in pixels. Positive numbers
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        will scroll the content up.
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param dx Horizontal distance to travel. Positive numbers will scroll the
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        content to the left.
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param dy Vertical distance to travel. Positive numbers will scroll the
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        content up.
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void startScroll(int startX, int startY, int dx, int dy) {
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Start scrolling by providing a starting point and the distance to travel.
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startX Starting horizontal scroll offset in pixels. Positive
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        numbers will scroll the content to the left.
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startY Starting vertical scroll offset in pixels. Positive numbers
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        will scroll the content up.
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param dx Horizontal distance to travel. Positive numbers will scroll the
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        content to the left.
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param dy Vertical distance to travel. Positive numbers will scroll the
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        content up.
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param duration Duration of the scroll in milliseconds.
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMode = SCROLL_MODE;
3729d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = false;
3739d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDuration = duration;
3749d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartTime = AnimationUtils.currentAnimationTimeMillis();
3759d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartX = startX;
3769d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartY = startY;
3779d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalX = startX + dx;
3789d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalY = startY + dy;
3799d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDeltaX = dx;
3809d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDeltaY = dy;
381637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        mDurationReciprocal = 1.0f / (float) mDuration;
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3859d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Start scrolling based on a fling gesture. The distance travelled will
3869d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * depend on the initial velocity of the fling.
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startX Starting point of the scroll (X)
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startY Starting point of the scroll (Y)
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param velocityX Initial velocity of the fling (X) measured in pixels per
3919d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        second.
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
3939d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        second
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param minX Minimum X value. The scroller will not scroll past this
3959d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        point.
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param maxX Maximum X value. The scroller will not scroll past this
3979d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        point.
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param minY Minimum Y value. The scroller will not scroll past this
3999d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        point.
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param maxY Maximum Y value. The scroller will not scroll past this
4019d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        point.
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void fling(int startX, int startY, int velocityX, int velocityY,
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int minX, int maxX, int minY, int maxY) {
405d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        // Continue a scroll or fling in progress
406d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        if (mFlywheel && !mFinished) {
407d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float oldVel = getCurrVelocity();
408d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
409d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float dx = (float) (mFinalX - mStartX);
410d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float dy = (float) (mFinalY - mStartY);
411d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float hyp = FloatMath.sqrt(dx * dx + dy * dy);
412d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
413d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float ndx = dx / hyp;
414d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float ndy = dy / hyp;
415d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
416d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float oldVelocityX = ndx * oldVel;
417d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float oldVelocityY = ndy * oldVel;
418d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
419d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                    Math.signum(velocityY) == Math.signum(oldVelocityY)) {
420d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                velocityX += oldVelocityX;
421d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                velocityY += oldVelocityY;
422d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            }
423d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        }
424d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMode = FLING_MODE;
4269d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = false;
4279d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
428d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);
4299d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
4309d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mVelocity = velocity;
431990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        mDuration = getSplineFlingDuration(velocity);
4329d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartTime = AnimationUtils.currentAnimationTimeMillis();
4339d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartX = startX;
4349d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartY = startY;
4359d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
436637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
437637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
4389d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
439990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        double totalDistance = getSplineFlingDistance(velocity);
440990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        mDistance = (int) (totalDistance * Math.signum(velocity));
4419d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
4429d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mMinX = minX;
4439d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mMaxX = maxX;
4449d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mMinY = minY;
4459d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mMaxY = maxY;
446d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
447990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        mFinalX = startX + (int) Math.round(totalDistance * coeffX);
4489d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        // Pin to mMinX <= mFinalX <= mMaxX
4499d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalX = Math.min(mFinalX, mMaxX);
4509d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalX = Math.max(mFinalX, mMinX);
4519d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
452990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        mFinalY = startY + (int) Math.round(totalDistance * coeffY);
4539d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        // Pin to mMinY <= mFinalY <= mMaxY
4549d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalY = Math.min(mFinalY, mMaxY);
4559d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalY = Math.max(mFinalY, mMinY);
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4579d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
458990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private double getSplineDeceleration(float velocity) {
459990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff));
460990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    }
461990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
462990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private int getSplineFlingDuration(float velocity) {
463990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        final double l = getSplineDeceleration(velocity);
464990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        final double decelMinusOne = DECELERATION_RATE - 1.0;
465990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        return (int) (1000.0 * Math.exp(l / decelMinusOne));
466990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    }
467990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
468990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    private double getSplineFlingDistance(float velocity) {
469990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        final double l = getSplineDeceleration(velocity);
470990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        final double decelMinusOne = DECELERATION_RATE - 1.0;
471990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell        return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);
472990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell    }
473990dfc6f908c8fc22bb15a3e0a36699e954ec28cAdam Powell
474637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell    static float viscousFluid(float x)
4759d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    {
476637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        x *= sViscousFluidScale;
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (x < 1.0f) {
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x -= (1.0f - (float)Math.exp(-x));
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float start = 0.36787944117f;   // 1/e == exp(-1)
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x = 1.0f - (float)Math.exp(1.0f - x);
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x = start + x * (1.0f - start);
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
484637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        x *= sViscousFluidNormalize;
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return x;
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4879d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
489c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * Stops the animation. Contrary to {@link #forceFinished(boolean)},
490c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * aborting the animating cause the scroller to move to the final x and y
491c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * position
4929d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
493c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #forceFinished(boolean)
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void abortAnimation() {
4969d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mCurrX = mFinalX;
4979d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mCurrY = mFinalY;
4989d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = true;
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5009d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
502c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * Extend the scroll animation. This allows a running animation to scroll
503d663dab7dbefb49877efd2976994fb94cfa4fbd0djken     * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
5049d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param extend Additional time to scroll in milliseconds.
506c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #setFinalX(int)
507c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #setFinalY(int)
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void extendDuration(int extend) {
5109d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        int passed = timePassed();
5119d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDuration = passed + extend;
512d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        mDurationReciprocal = 1.0f / mDuration;
5139d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = false;
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
515c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier
516c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier    /**
517c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * Returns the time elapsed since the beginning of the scrolling.
5189d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
519c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @return The elapsed time in milliseconds.
520c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     */
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int timePassed() {
5229d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
524c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier
525c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier    /**
526c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * Sets the final position (X) for this scroller.
5279d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
528c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @param newX The new X offset as an absolute distance from the origin.
529c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #extendDuration(int)
530c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #setFinalY(int)
531c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     */
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFinalX(int newX) {
5339d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalX = newX;
5349d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDeltaX = mFinalX - mStartX;
5359d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = false;
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
538c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier    /**
539c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * Sets the final position (Y) for this scroller.
5409d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
541c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @param newY The new Y offset as an absolute distance from the origin.
542c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #extendDuration(int)
543c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #setFinalX(int)
544c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     */
545c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier    public void setFinalY(int newY) {
5469d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalY = newY;
5479d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDeltaY = mFinalY - mStartY;
5489d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = false;
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
550d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
551d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    /**
552d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne     * @hide
553d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne     */
554d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    public boolean isScrollingInDirection(float xvel, float yvel) {
555d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX) &&
556d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                Math.signum(yvel) == Math.signum(mFinalY - mStartY);
557d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    }
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
559