16772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang/*
26772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * Copyright (C) 2006 The Android Open Source Project
36772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang *
46772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * Licensed under the Apache License, Version 2.0 (the "License");
56772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * you may not use this file except in compliance with the License.
66772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * You may obtain a copy of the License at
76772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang *
86772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang *      http://www.apache.org/licenses/LICENSE-2.0
96772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang *
106772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * Unless required by applicable law or agreed to in writing, software
116772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * distributed under the License is distributed on an "AS IS" BASIS,
126772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * See the License for the specific language governing permissions and
146772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * limitations under the License.
156772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang */
166772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
176772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Changpackage com.android.gallery3d.common;
186772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
196772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Changimport android.content.Context;
206772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Changimport android.hardware.SensorManager;
216772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Changimport android.os.Build;
226772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Changimport android.util.FloatMath;
236772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Changimport android.view.ViewConfiguration;
246772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Changimport android.view.animation.AnimationUtils;
256772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Changimport android.view.animation.Interpolator;
266772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
276772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
286772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang/**
296772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * This class encapsulates scrolling.  The duration of the scroll
306772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * can be passed in the constructor and specifies the maximum time that
316772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * the scrolling animation should take.  Past this time, the scrolling is
326772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * automatically moved to its final stage and computeScrollOffset()
336772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang * will always return false to indicate that scrolling is over.
346772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang */
356772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Changpublic class Scroller  {
366772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mMode;
376772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
386772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mStartX;
396772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mStartY;
406772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mFinalX;
416772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mFinalY;
426772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
436772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mMinX;
446772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mMaxX;
456772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mMinY;
466772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mMaxY;
476772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
486772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mCurrX;
496772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mCurrY;
506772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private long mStartTime;
516772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private int mDuration;
526772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private float mDurationReciprocal;
536772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private float mDeltaX;
546772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private float mDeltaY;
556772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private boolean mFinished;
566772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private Interpolator mInterpolator;
576772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private boolean mFlywheel;
586772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
596772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private float mVelocity;
606772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
616772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static final int DEFAULT_DURATION = 250;
626772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static final int SCROLL_MODE = 0;
636772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static final int FLING_MODE = 1;
646772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
656772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));
666772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static float ALPHA = 800; // pixels / seconds
676772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance)
686772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static float END_TENSION = 1.0f - START_TENSION;
696772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static final int NB_SAMPLES = 100;
706772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static final float[] SPLINE = new float[NB_SAMPLES + 1];
716772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
726772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private float mDeceleration;
736772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private final float mPpi;
746772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
756772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    static {
766772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        float x_min = 0.0f;
776772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        for (int i = 0; i <= NB_SAMPLES; i++) {
786772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            final float t = (float) i / NB_SAMPLES;
796772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            float x_max = 1.0f;
806772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            float x, tx, coef;
816772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            while (true) {
826772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                x = x_min + (x_max - x_min) / 2.0f;
836772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                coef = 3.0f * x * (1.0f - x);
846772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x;
856772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                if (Math.abs(tx - t) < 1E-5) break;
866772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                if (tx > t) x_max = x;
876772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                else x_min = x;
886772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            }
896772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            final float d = coef + x * x * x;
906772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            SPLINE[i] = d;
916772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        }
926772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        SPLINE[NB_SAMPLES] = 1.0f;
936772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
946772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        // This controls the viscous fluid effect (how much of it)
956772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        sViscousFluidScale = 8.0f;
966772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        // must be set to 1.0 (used in viscousFluid())
976772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        sViscousFluidNormalize = 1.0f;
986772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
996772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1006772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1016772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static float sViscousFluidScale;
1026772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private static float sViscousFluidNormalize;
1036772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1046772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
1056772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Create a Scroller with the default duration and interpolator.
1066772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
1076772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public Scroller(Context context) {
1086772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        this(context, null);
1096772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1106772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1116772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
1126772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Create a Scroller with the specified interpolator. If the interpolator is
1136772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
1146772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * be in effect for apps targeting Honeycomb or newer.
1156772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
1166772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public Scroller(Context context, Interpolator interpolator) {
1176772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        this(context, interpolator,
1186772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
1196772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1206772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1216772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
1226772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Create a Scroller with the specified interpolator. If the interpolator is
1236772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * null, the default (viscous) interpolator will be used. Specify whether or
1246772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * not to support progressive "flywheel" behavior in flinging.
1256772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
1266772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
1276772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinished = true;
1286772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mInterpolator = interpolator;
1296772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
1306772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
1316772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFlywheel = flywheel;
1326772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1336772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1346772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
1356772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * The amount of friction applied to flings. The default value
1366772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * is {@link ViewConfiguration#getScrollFriction}.
1376772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
1386772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param friction A scalar dimension-less value representing the coefficient of
1396772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *         friction.
1406772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
1416772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public final void setFriction(float friction) {
1426772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mDeceleration = computeDeceleration(friction);
1436772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1446772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1456772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    private float computeDeceleration(float friction) {
1466772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return SensorManager.GRAVITY_EARTH   // g (m/s^2)
1476772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                      * 39.37f               // inch/meter
1486772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                      * mPpi                 // pixels per inch
1496772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                      * friction;
1506772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1516772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1526772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
1536772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
1546772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Returns whether the scroller has finished scrolling.
1556772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
1566772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @return True if the scroller has finished scrolling, false otherwise.
1576772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
1586772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public final boolean isFinished() {
1596772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return mFinished;
1606772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1616772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1626772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
1636772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Force the finished field to a particular value.
1646772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
1656772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param finished The new finished value.
1666772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
1676772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public final void forceFinished(boolean finished) {
1686772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinished = finished;
1696772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1706772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1716772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
1726772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Returns how long the scroll event will take, in milliseconds.
1736772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
1746772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @return The duration of the scroll in milliseconds.
1756772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
1766772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public final int getDuration() {
1776772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return mDuration;
1786772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1796772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1806772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
1816772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Returns the current X offset in the scroll.
1826772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
1836772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @return The new X offset as an absolute distance from the origin.
1846772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
1856772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public final int getCurrX() {
1866772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return mCurrX;
1876772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1886772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1896772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
1906772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Returns the current Y offset in the scroll.
1916772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
1926772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @return The new Y offset as an absolute distance from the origin.
1936772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
1946772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public final int getCurrY() {
1956772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return mCurrY;
1966772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
1976772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
1986772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
1996772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Returns the current velocity.
2006772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
2016772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @return The original velocity less the deceleration. Result may be
2026772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * negative.
2036772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
2046772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public float getCurrVelocity() {
2056772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return mVelocity - mDeceleration * timePassed() / 2000.0f;
2066772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
2076772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2086772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
2096772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Returns the start X offset in the scroll.
2106772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
2116772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @return The start X offset as an absolute distance from the origin.
2126772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
2136772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public final int getStartX() {
2146772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return mStartX;
2156772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
2166772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2176772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
2186772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Returns the start Y offset in the scroll.
2196772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
2206772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @return The start Y offset as an absolute distance from the origin.
2216772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
2226772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public final int getStartY() {
2236772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return mStartY;
2246772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
2256772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2266772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
2276772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Returns where the scroll will end. Valid only for "fling" scrolls.
2286772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
2296772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @return The final X offset as an absolute distance from the origin.
2306772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
2316772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public final int getFinalX() {
2326772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return mFinalX;
2336772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
2346772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2356772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
2366772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Returns where the scroll will end. Valid only for "fling" scrolls.
2376772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
2386772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @return The final Y offset as an absolute distance from the origin.
2396772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
2406772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public final int getFinalY() {
2416772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return mFinalY;
2426772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
2436772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2446772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
2456772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Call this when you want to know the new location.  If it returns true,
2466772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * the animation is not yet finished.  loc will be altered to provide the
2476772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * new location.
2486772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
2496772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public boolean computeScrollOffset() {
2506772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        if (mFinished) {
2516772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            return false;
2526772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        }
2536772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2546772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
2556772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2566772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        if (timePassed < mDuration) {
2576772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            switch (mMode) {
2586772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            case SCROLL_MODE:
2596772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                float x = timePassed * mDurationReciprocal;
2606772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2616772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                if (mInterpolator == null)
2626772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                    x = viscousFluid(x);
2636772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                else
2646772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                    x = mInterpolator.getInterpolation(x);
2656772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2666772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                mCurrX = mStartX + Math.round(x * mDeltaX);
2676772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                mCurrY = mStartY + Math.round(x * mDeltaY);
2686772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                break;
2696772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            case FLING_MODE:
2706772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                final float t = (float) timePassed / mDuration;
2716772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                final int index = (int) (NB_SAMPLES * t);
2726772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                final float t_inf = (float) index / NB_SAMPLES;
2736772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                final float t_sup = (float) (index + 1) / NB_SAMPLES;
2746772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                final float d_inf = SPLINE[index];
2756772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                final float d_sup = SPLINE[index + 1];
2766772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf);
2776772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2786772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
2796772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                // Pin to mMinX <= mCurrX <= mMaxX
2806772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                mCurrX = Math.min(mCurrX, mMaxX);
2816772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                mCurrX = Math.max(mCurrX, mMinX);
2826772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2836772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
2846772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                // Pin to mMinY <= mCurrY <= mMaxY
2856772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                mCurrY = Math.min(mCurrY, mMaxY);
2866772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                mCurrY = Math.max(mCurrY, mMinY);
2876772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2886772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                if (mCurrX == mFinalX && mCurrY == mFinalY) {
2896772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                    mFinished = true;
2906772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                }
2916772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
2926772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                break;
2936772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            }
2946772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        }
2956772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        else {
2966772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            mCurrX = mFinalX;
2976772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            mCurrY = mFinalY;
2986772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            mFinished = true;
2996772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        }
3006772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return true;
3016772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
3026772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
3036772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
3046772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Start scrolling by providing a starting point and the distance to travel.
3056772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * The scroll will use the default value of 250 milliseconds for the
3066772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * duration.
3076772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
3086772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param startX Starting horizontal scroll offset in pixels. Positive
3096772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        numbers will scroll the content to the left.
3106772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param startY Starting vertical scroll offset in pixels. Positive numbers
3116772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        will scroll the content up.
3126772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param dx Horizontal distance to travel. Positive numbers will scroll the
3136772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        content to the left.
3146772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param dy Vertical distance to travel. Positive numbers will scroll the
3156772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        content up.
3166772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
3176772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public void startScroll(int startX, int startY, int dx, int dy) {
3186772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
3196772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
3206772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
3216772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
3226772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Start scrolling by providing a starting point and the distance to travel.
3236772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
3246772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param startX Starting horizontal scroll offset in pixels. Positive
3256772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        numbers will scroll the content to the left.
3266772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param startY Starting vertical scroll offset in pixels. Positive numbers
3276772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        will scroll the content up.
3286772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param dx Horizontal distance to travel. Positive numbers will scroll the
3296772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        content to the left.
3306772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param dy Vertical distance to travel. Positive numbers will scroll the
3316772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        content up.
3326772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param duration Duration of the scroll in milliseconds.
3336772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
3346772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
3356772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mMode = SCROLL_MODE;
3366772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinished = false;
3376772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mDuration = duration;
3386772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mStartTime = AnimationUtils.currentAnimationTimeMillis();
3396772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mStartX = startX;
3406772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mStartY = startY;
3416772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinalX = startX + dx;
3426772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinalY = startY + dy;
3436772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mDeltaX = dx;
3446772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mDeltaY = dy;
3457817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        mDurationReciprocal = 1.0f / mDuration;
3466772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
3476772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
3486772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
3496772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Start scrolling based on a fling gesture. The distance travelled will
3506772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * depend on the initial velocity of the fling.
3516772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
3526772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param startX Starting point of the scroll (X)
3536772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param startY Starting point of the scroll (Y)
3546772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param velocityX Initial velocity of the fling (X) measured in pixels per
3556772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        second.
3566772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
3576772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        second
3586772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param minX Minimum X value. The scroller will not scroll past this
3596772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        point.
3606772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param maxX Maximum X value. The scroller will not scroll past this
3616772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        point.
3626772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param minY Minimum Y value. The scroller will not scroll past this
3636772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        point.
3646772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param maxY Maximum Y value. The scroller will not scroll past this
3656772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *        point.
3666772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
3676772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public void fling(int startX, int startY, int velocityX, int velocityY,
3686772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            int minX, int maxX, int minY, int maxY) {
3696772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        // Continue a scroll or fling in progress
3706772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        if (mFlywheel && !mFinished) {
3716772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            float oldVel = getCurrVelocity();
3726772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
3737817979db0c52ffeacb951625b1e821eba303285Ahbong Chang            float dx = mFinalX - mStartX;
3747817979db0c52ffeacb951625b1e821eba303285Ahbong Chang            float dy = mFinalY - mStartY;
3756772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            float hyp = FloatMath.sqrt(dx * dx + dy * dy);
3766772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
3776772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            float ndx = dx / hyp;
3786772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            float ndy = dy / hyp;
3796772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
3806772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            float oldVelocityX = ndx * oldVel;
3816772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            float oldVelocityY = ndy * oldVel;
3826772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
3836772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                    Math.signum(velocityY) == Math.signum(oldVelocityY)) {
3846772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                velocityX += oldVelocityX;
3856772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                velocityY += oldVelocityY;
3866772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            }
3876772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        }
3886772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
3896772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mMode = FLING_MODE;
3906772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinished = false;
3916772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
3926772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);
3936772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
3946772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mVelocity = velocity;
3956772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        final double l = Math.log(START_TENSION * velocity / ALPHA);
3966772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0)));
3976772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mStartTime = AnimationUtils.currentAnimationTimeMillis();
3986772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mStartX = startX;
3996772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mStartY = startY;
4006772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4016772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
4026772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
4036772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4046772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        int totalDistance =
4056772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                (int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
4066772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4076772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mMinX = minX;
4086772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mMaxX = maxX;
4096772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mMinY = minY;
4106772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mMaxY = maxY;
4116772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4126772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinalX = startX + Math.round(totalDistance * coeffX);
4136772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        // Pin to mMinX <= mFinalX <= mMaxX
4146772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinalX = Math.min(mFinalX, mMaxX);
4156772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinalX = Math.max(mFinalX, mMinX);
4166772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4176772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinalY = startY + Math.round(totalDistance * coeffY);
4186772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        // Pin to mMinY <= mFinalY <= mMaxY
4196772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinalY = Math.min(mFinalY, mMaxY);
4206772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinalY = Math.max(mFinalY, mMinY);
4216772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
4226772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4236772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    static float viscousFluid(float x)
4246772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    {
4256772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        x *= sViscousFluidScale;
4266772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        if (x < 1.0f) {
4276772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            x -= (1.0f - (float)Math.exp(-x));
4286772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        } else {
4296772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            float start = 0.36787944117f;   // 1/e == exp(-1)
4306772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            x = 1.0f - (float)Math.exp(1.0f - x);
4316772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang            x = start + x * (1.0f - start);
4326772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        }
4336772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        x *= sViscousFluidNormalize;
4346772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return x;
4356772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
4366772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4376772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
4386772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Stops the animation. Contrary to {@link #forceFinished(boolean)},
4396772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * aborting the animating cause the scroller to move to the final x and y
4406772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * position
4416772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
4426772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @see #forceFinished(boolean)
4436772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
4446772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public void abortAnimation() {
4456772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mCurrX = mFinalX;
4466772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mCurrY = mFinalY;
4476772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinished = true;
4486772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
4496772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4506772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
4516772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Extend the scroll animation. This allows a running animation to scroll
4526772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
4536772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
4546772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param extend Additional time to scroll in milliseconds.
4556772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @see #setFinalX(int)
4566772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @see #setFinalY(int)
4576772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
4586772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public void extendDuration(int extend) {
4596772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        int passed = timePassed();
4606772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mDuration = passed + extend;
4616772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mDurationReciprocal = 1.0f / mDuration;
4626772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinished = false;
4636772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
4646772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4656772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
4666772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Returns the time elapsed since the beginning of the scrolling.
4676772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
4686772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @return The elapsed time in milliseconds.
4696772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
4706772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public int timePassed() {
4716772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
4726772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
4736772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4746772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
4756772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Sets the final position (X) for this scroller.
4766772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
4776772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param newX The new X offset as an absolute distance from the origin.
4786772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @see #extendDuration(int)
4796772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @see #setFinalY(int)
4806772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
4816772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public void setFinalX(int newX) {
4826772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinalX = newX;
4836772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mDeltaX = mFinalX - mStartX;
4846772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinished = false;
4856772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
4866772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
4876772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
4886772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * Sets the final position (Y) for this scroller.
4896772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     *
4906772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @param newY The new Y offset as an absolute distance from the origin.
4916772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @see #extendDuration(int)
4926772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @see #setFinalX(int)
4936772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
4946772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public void setFinalY(int newY) {
4956772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinalY = newY;
4966772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mDeltaY = mFinalY - mStartY;
4976772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        mFinished = false;
4986772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
4996772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang
5006772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    /**
5016772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     * @hide
5026772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang     */
5036772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    public boolean isScrollingInDirection(float xvel, float yvel) {
5046772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang        return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX) &&
5056772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang                Math.signum(yvel) == Math.signum(mFinalY - mStartY);
5066772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang    }
5076772173d2a49b87a1d209f42fe52b5f830596f99Chih-Chung Chang}
508