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;
6052964243dc95f74ac2ab9a96d031a36931a11931Gilles Debunne
616579b0b4ac0e781efab044aaaf3f66447cf5e067Adam Powell    private static final int DEFAULT_DURATION = 250;
626579b0b4ac0e781efab044aaaf3f66447cf5e067Adam Powell    private static final int SCROLL_MODE = 0;
636579b0b4ac0e781efab044aaaf3f66447cf5e067Adam Powell    private static final int FLING_MODE = 1;
6452964243dc95f74ac2ab9a96d031a36931a11931Gilles Debunne
65d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));
6604cdb78c6a2ac66505d0b29033353cfd7e2c3d78Gilles Debunne    private static float ALPHA = 800; // pixels / seconds
67d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    private static float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance)
68d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    private static float END_TENSION = 1.0f - START_TENSION;
69d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    private static final int NB_SAMPLES = 100;
70d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    private static final float[] SPLINE = new float[NB_SAMPLES + 1];
71d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
724bede9e425875542976a422222510fa4056a8339Romain Guy    private float mDeceleration;
734bede9e425875542976a422222510fa4056a8339Romain Guy    private final float mPpi;
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
75d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    static {
76d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        float x_min = 0.0f;
77d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        for (int i = 0; i <= NB_SAMPLES; i++) {
78d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            final float t = (float) i / NB_SAMPLES;
79d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float x_max = 1.0f;
80d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float x, tx, coef;
81d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            while (true) {
82d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                x = x_min + (x_max - x_min) / 2.0f;
83d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                coef = 3.0f * x * (1.0f - x);
84d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x;
85d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                if (Math.abs(tx - t) < 1E-5) break;
86d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                if (tx > t) x_max = x;
87d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                else x_min = x;
88d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            }
89d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            final float d = coef + x * x * x;
90d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            SPLINE[i] = d;
91d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        }
92d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        SPLINE[NB_SAMPLES] = 1.0f;
93637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell
94637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        // This controls the viscous fluid effect (how much of it)
95637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        sViscousFluidScale = 8.0f;
96637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        // must be set to 1.0 (used in viscousFluid())
97637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        sViscousFluidNormalize = 1.0f;
98637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
99d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    }
100d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
101637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell    private static float sViscousFluidScale;
102637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell    private static float sViscousFluidNormalize;
103637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1059d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Create a Scroller with the default duration and interpolator.
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Scroller(Context context) {
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, null);
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
111637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell    /**
112637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     * Create a Scroller with the specified interpolator. If the interpolator is
113637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
114637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     * be in effect for apps targeting Honeycomb or newer.
115637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     */
116d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    public Scroller(Context context, Interpolator interpolator) {
117d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        this(context, interpolator,
118d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
119d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    }
120d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Create a Scroller with the specified interpolator. If the interpolator is
123637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     * null, the default (viscous) interpolator will be used. Specify whether or
124637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell     * not to support progressive "flywheel" behavior in flinging.
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
126d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
1279d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = true;
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInterpolator = interpolator;
1294bede9e425875542976a422222510fa4056a8339Romain Guy        mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
1304bede9e425875542976a422222510fa4056a8339Romain Guy        mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
131d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        mFlywheel = flywheel;
1324bede9e425875542976a422222510fa4056a8339Romain Guy    }
1334bede9e425875542976a422222510fa4056a8339Romain Guy
1344bede9e425875542976a422222510fa4056a8339Romain Guy    /**
1354bede9e425875542976a422222510fa4056a8339Romain Guy     * The amount of friction applied to flings. The default value
1364bede9e425875542976a422222510fa4056a8339Romain Guy     * is {@link ViewConfiguration#getScrollFriction}.
1374bede9e425875542976a422222510fa4056a8339Romain Guy     *
138d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne     * @param friction A scalar dimension-less value representing the coefficient of
1394bede9e425875542976a422222510fa4056a8339Romain Guy     *         friction.
1404bede9e425875542976a422222510fa4056a8339Romain Guy     */
1414bede9e425875542976a422222510fa4056a8339Romain Guy    public final void setFriction(float friction) {
142a655c635820425a4c98314c3ba02eb382229b45dRomain Guy        mDeceleration = computeDeceleration(friction);
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1449d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
1454bede9e425875542976a422222510fa4056a8339Romain Guy    private float computeDeceleration(float friction) {
1464bede9e425875542976a422222510fa4056a8339Romain Guy        return SensorManager.GRAVITY_EARTH   // g (m/s^2)
1474bede9e425875542976a422222510fa4056a8339Romain Guy                      * 39.37f               // inch/meter
1484bede9e425875542976a422222510fa4056a8339Romain Guy                      * mPpi                 // pixels per inch
1494bede9e425875542976a422222510fa4056a8339Romain Guy                      * friction;
1504bede9e425875542976a422222510fa4056a8339Romain Guy    }
1514bede9e425875542976a422222510fa4056a8339Romain Guy
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns whether the scroller has finished scrolling.
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return True if the scroller has finished scrolling, false otherwise.
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final boolean isFinished() {
1599d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mFinished;
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1619d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Force the finished field to a particular value.
1649d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param finished The new finished value.
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final void forceFinished(boolean finished) {
1689d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = finished;
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1709d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns how long the scroll event will take, in milliseconds.
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The duration of the scroll in milliseconds.
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getDuration() {
1779d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mDuration;
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1799d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1819d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Returns the current X offset in the scroll.
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The new X offset as an absolute distance from the origin.
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getCurrX() {
1869d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mCurrX;
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1889d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1909d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Returns the current Y offset in the scroll.
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The new Y offset as an absolute distance from the origin.
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getCurrY() {
1959d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mCurrY;
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1979d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
199278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark     * Returns the current velocity.
2009d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
2019d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * @return The original velocity less the deceleration. Result may be
2029d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * negative.
203278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark     */
204278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark    public float getCurrVelocity() {
2059d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mVelocity - mDeceleration * timePassed() / 2000.0f;
206278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark    }
207278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark
208278ce05b52a47a09d2177435b533f4ecb1b9c4daCary Clark    /**
2099d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Returns the start X offset in the scroll.
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The start X offset as an absolute distance from the origin.
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getStartX() {
2149d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mStartX;
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2169d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2189d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Returns the start Y offset in the scroll.
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The start Y offset as an absolute distance from the origin.
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getStartY() {
2239d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mStartY;
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2259d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns where the scroll will end. Valid only for "fling" scrolls.
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The final X offset as an absolute distance from the origin.
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getFinalX() {
2329d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mFinalX;
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2349d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns where the scroll will end. Valid only for "fling" scrolls.
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The final Y offset as an absolute distance from the origin.
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public final int getFinalY() {
2419d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return mFinalY;
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2459d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Call this when you want to know the new location.  If it returns true,
2469d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * the animation is not yet finished.  loc will be altered to provide the
2479d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * new location.
2489d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     */
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean computeScrollOffset() {
2509d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        if (mFinished) {
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2549d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
2559d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2569d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        if (timePassed < mDuration) {
2579d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell            switch (mMode) {
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case SCROLL_MODE:
259d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                float x = timePassed * mDurationReciprocal;
2609d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2619d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                if (mInterpolator == null)
2629d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                    x = viscousFluid(x);
2639d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                else
2649d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                    x = mInterpolator.getInterpolation(x);
2659d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
2669d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrX = mStartX + Math.round(x * mDeltaX);
2679d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrY = mStartY + Math.round(x * mDeltaY);
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case FLING_MODE:
270d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                final float t = (float) timePassed / mDuration;
271d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                final int index = (int) (NB_SAMPLES * t);
272d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                final float t_inf = (float) index / NB_SAMPLES;
273d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                final float t_sup = (float) (index + 1) / NB_SAMPLES;
274d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                final float d_inf = SPLINE[index];
275d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                final float d_sup = SPLINE[index + 1];
276d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf);
2779d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
278d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
2799d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                // Pin to mMinX <= mCurrX <= mMaxX
2809d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrX = Math.min(mCurrX, mMaxX);
2819d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrX = Math.max(mCurrX, mMinX);
2829d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
283d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
2849d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                // Pin to mMinY <= mCurrY <= mMaxY
2859d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrY = Math.min(mCurrY, mMaxY);
2869d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell                mCurrY = Math.max(mCurrY, mMinY);
2871b088be8bb304e4ab7a1896d3ec3b9df9d9cf68eAdam Powell
2881b088be8bb304e4ab7a1896d3ec3b9df9d9cf68eAdam Powell                if (mCurrX == mFinalX && mCurrY == mFinalY) {
2891b088be8bb304e4ab7a1896d3ec3b9df9d9cf68eAdam Powell                    mFinished = true;
2901b088be8bb304e4ab7a1896d3ec3b9df9d9cf68eAdam Powell                }
2911b088be8bb304e4ab7a1896d3ec3b9df9d9cf68eAdam Powell
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
2939d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell            }
2949d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        }
2959d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        else {
2969d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell            mCurrX = mFinalX;
2979d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell            mCurrY = mFinalY;
2989d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell            mFinished = true;
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3029d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Start scrolling by providing a starting point and the distance to travel.
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The scroll will use the default value of 250 milliseconds for the
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * duration.
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startX Starting horizontal scroll offset in pixels. Positive
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        numbers will scroll the content to the left.
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startY Starting vertical scroll offset in pixels. Positive numbers
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        will scroll the content up.
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param dx Horizontal distance to travel. Positive numbers will scroll the
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        content to the left.
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param dy Vertical distance to travel. Positive numbers will scroll the
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        content up.
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void startScroll(int startX, int startY, int dx, int dy) {
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Start scrolling by providing a starting point and the distance to travel.
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startX Starting horizontal scroll offset in pixels. Positive
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        numbers will scroll the content to the left.
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startY Starting vertical scroll offset in pixels. Positive numbers
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        will scroll the content up.
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param dx Horizontal distance to travel. Positive numbers will scroll the
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        content to the left.
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param dy Vertical distance to travel. Positive numbers will scroll the
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *        content up.
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param duration Duration of the scroll in milliseconds.
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMode = SCROLL_MODE;
3369d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = false;
3379d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDuration = duration;
3389d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartTime = AnimationUtils.currentAnimationTimeMillis();
3399d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartX = startX;
3409d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartY = startY;
3419d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalX = startX + dx;
3429d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalY = startY + dy;
3439d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDeltaX = dx;
3449d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDeltaY = dy;
345637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        mDurationReciprocal = 1.0f / (float) mDuration;
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3499d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * Start scrolling based on a fling gesture. The distance travelled will
3509d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     * depend on the initial velocity of the fling.
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startX Starting point of the scroll (X)
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param startY Starting point of the scroll (Y)
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param velocityX Initial velocity of the fling (X) measured in pixels per
3559d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        second.
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
3579d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        second
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param minX Minimum X value. The scroller will not scroll past this
3599d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        point.
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param maxX Maximum X value. The scroller will not scroll past this
3619d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        point.
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param minY Minimum Y value. The scroller will not scroll past this
3639d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        point.
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param maxY Maximum Y value. The scroller will not scroll past this
3659d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *        point.
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void fling(int startX, int startY, int velocityX, int velocityY,
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int minX, int maxX, int minY, int maxY) {
369d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        // Continue a scroll or fling in progress
370d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        if (mFlywheel && !mFinished) {
371d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float oldVel = getCurrVelocity();
372d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
373d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float dx = (float) (mFinalX - mStartX);
374d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float dy = (float) (mFinalY - mStartY);
375d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float hyp = FloatMath.sqrt(dx * dx + dy * dy);
376d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
377d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float ndx = dx / hyp;
378d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float ndy = dy / hyp;
379d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
380d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float oldVelocityX = ndx * oldVel;
381d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            float oldVelocityY = ndy * oldVel;
382d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
383d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                    Math.signum(velocityY) == Math.signum(oldVelocityY)) {
384d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                velocityX += oldVelocityX;
385d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                velocityY += oldVelocityY;
386d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne            }
387d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        }
388d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMode = FLING_MODE;
3909d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = false;
3919d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
392d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);
3939d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
3949d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mVelocity = velocity;
395d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        final double l = Math.log(START_TENSION * velocity / ALPHA);
396d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        mDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0)));
3979d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartTime = AnimationUtils.currentAnimationTimeMillis();
3989d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartX = startX;
3999d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mStartY = startY;
4009d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
401637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
402637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
4039d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
404d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        int totalDistance =
405d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                (int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
4069d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
4079d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mMinX = minX;
4089d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mMaxX = maxX;
4099d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mMinY = minY;
4109d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mMaxY = maxY;
411d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
412637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        mFinalX = startX + Math.round(totalDistance * coeffX);
4139d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        // Pin to mMinX <= mFinalX <= mMaxX
4149d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalX = Math.min(mFinalX, mMaxX);
4159d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalX = Math.max(mFinalX, mMinX);
4169d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
417637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        mFinalY = startY + Math.round(totalDistance * coeffY);
4189d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        // Pin to mMinY <= mFinalY <= mMaxY
4199d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalY = Math.min(mFinalY, mMaxY);
4209d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalY = Math.max(mFinalY, mMinY);
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4229d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
423637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell    static float viscousFluid(float x)
4249d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell    {
425637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        x *= sViscousFluidScale;
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (x < 1.0f) {
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x -= (1.0f - (float)Math.exp(-x));
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float start = 0.36787944117f;   // 1/e == exp(-1)
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x = 1.0f - (float)Math.exp(1.0f - x);
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            x = start + x * (1.0f - start);
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
433637d337b58d8eec6de19230a5dd5ca5581c0478dAdam Powell        x *= sViscousFluidNormalize;
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return x;
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4369d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
438c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * Stops the animation. Contrary to {@link #forceFinished(boolean)},
439c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * aborting the animating cause the scroller to move to the final x and y
440c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * position
4419d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
442c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #forceFinished(boolean)
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void abortAnimation() {
4459d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mCurrX = mFinalX;
4469d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mCurrY = mFinalY;
4479d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = true;
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4499d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
451c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * Extend the scroll animation. This allows a running animation to scroll
452d663dab7dbefb49877efd2976994fb94cfa4fbd0djken     * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
4539d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param extend Additional time to scroll in milliseconds.
455c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #setFinalX(int)
456c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #setFinalY(int)
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void extendDuration(int extend) {
4599d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        int passed = timePassed();
4609d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDuration = passed + extend;
461d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        mDurationReciprocal = 1.0f / mDuration;
4629d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = false;
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
464c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier
465c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier    /**
466c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * Returns the time elapsed since the beginning of the scrolling.
4679d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
468c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @return The elapsed time in milliseconds.
469c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     */
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int timePassed() {
4719d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
473c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier
474c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier    /**
475c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * Sets the final position (X) for this scroller.
4769d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
477c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @param newX The new X offset as an absolute distance from the origin.
478c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #extendDuration(int)
479c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #setFinalY(int)
480c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     */
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFinalX(int newX) {
4829d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalX = newX;
4839d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDeltaX = mFinalX - mStartX;
4849d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = false;
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
487c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier    /**
488c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * Sets the final position (Y) for this scroller.
4899d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell     *
490c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @param newY The new Y offset as an absolute distance from the origin.
491c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #extendDuration(int)
492c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     * @see #setFinalX(int)
493c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier     */
494c312942b773449f5c6a8a887052aec847ec73b2cCyril Mottier    public void setFinalY(int newY) {
4959d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinalY = newY;
4969d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mDeltaY = mFinalY - mStartY;
4979d32d24dbd8a015c9d5c44ed4901d5a666eb8e7fAdam Powell        mFinished = false;
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
499d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne
500d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    /**
501d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne     * @hide
502d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne     */
503d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    public boolean isScrollingInDirection(float xvel, float yvel) {
504d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne        return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX) &&
505d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne                Math.signum(yvel) == Math.signum(mFinalY - mStartY);
506d348bb4feff72d047a1037537be2d334a00c380cGilles Debunne    }
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
508