1b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang/*
2b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * Copyright (C) 2011 The Android Open Source Project
3b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang *
4b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * Licensed under the Apache License, Version 2.0 (the "License");
5b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * you may not use this file except in compliance with the License.
6b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * You may obtain a copy of the License at
7b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang *
8b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang *      http://www.apache.org/licenses/LICENSE-2.0
9b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang *
10b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * Unless required by applicable law or agreed to in writing, software
11b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * distributed under the License is distributed on an "AS IS" BASIS,
12b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * See the License for the specific language governing permissions and
14b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * limitations under the License.
15b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang */
16b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
17b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Changpackage com.android.gallery3d.ui;
18b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
19b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
20b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// This is a customized version of Scroller, with a interface similar to
21b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// android.widget.Scroller. It does fling only, not scroll.
22b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang//
23b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// The differences between the this Scroller and the system one are:
24b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang//
25b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// (1) The velocity does not change because of min/max limit.
26b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// (2) The duration is different.
27b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// (3) The deceleration curve is different.
28b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Changclass FlingScroller {
297817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @SuppressWarnings("unused")
30b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private static final String TAG = "FlingController";
31b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
32b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    // The fling duration (in milliseconds) when velocity is 1 pixel/second
33b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private static final float FLING_DURATION_PARAM = 50f;
34b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private static final int DECELERATED_FACTOR = 4;
35b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
36b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private int mStartX, mStartY;
37b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private int mMinX, mMinY, mMaxX, mMaxY;
38b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private double mSinAngle;
39b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private double mCosAngle;
40b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private int mDuration;
41b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private int mDistance;
42b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private int mFinalX, mFinalY;
43b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
44b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private int mCurrX, mCurrY;
45532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private double mCurrV;
46b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
47b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    public int getFinalX() {
48b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        return mFinalX;
49b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    }
50b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
51b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    public int getFinalY() {
52b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        return mFinalY;
53b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    }
54b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
55b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    public int getDuration() {
56b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        return mDuration;
57b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    }
58b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
59b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    public int getCurrX() {
60b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        return mCurrX;
61b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
62b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    }
63b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
64b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    public int getCurrY() {
65b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        return mCurrY;
66b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    }
67b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
68532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    public int getCurrVelocityX() {
69532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        return (int)Math.round(mCurrV * mCosAngle);
70532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
71532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
72532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    public int getCurrVelocityY() {
73532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        return (int)Math.round(mCurrV * mSinAngle);
74532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
75532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
76b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    public void fling(int startX, int startY, int velocityX, int velocityY,
77b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang            int minX, int maxX, int minY, int maxY) {
78b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mStartX = startX;
79b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mStartY = startY;
80b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mMinX = minX;
81b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mMinY = minY;
82b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mMaxX = maxX;
83b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mMaxY = maxY;
84b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
85b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        double velocity = Math.hypot(velocityX, velocityY);
86b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mSinAngle = velocityY / velocity;
87b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mCosAngle = velocityX / velocity;
88b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        //
89b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        // The position formula: x(t) = s + (e - s) * (1 - (1 - t / T) ^ d)
90b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        //     velocity formula: v(t) = d * (e - s) * (1 - t / T) ^ (d - 1) / T
91b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        // Thus,
92b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        //     v0 = d * (e - s) / T => (e - s) = v0 * T / d
93b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        //
94b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
95b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        // Ta = T_ref * (Va / V_ref) ^ (1 / (d - 1)); V_ref = 1 pixel/second;
96b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mDuration = (int)Math.round(FLING_DURATION_PARAM
97b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang                * Math.pow(Math.abs(velocity), 1.0 / (DECELERATED_FACTOR - 1)));
98b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
99b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        // (e - s) = v0 * T / d
100b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mDistance = (int)Math.round(
101b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang                velocity * mDuration / DECELERATED_FACTOR / 1000);
102b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
103b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mFinalX = getX(1.0f);
104b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mFinalY = getY(1.0f);
105b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    }
106b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
107b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    public void computeScrollOffset(float progress) {
108b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        progress = Math.min(progress, 1);
109b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        float f = 1 - progress;
110b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        f = 1 - (float) Math.pow(f, DECELERATED_FACTOR);
111b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mCurrX = getX(f);
112b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang        mCurrY = getY(f);
113532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mCurrV = getV(progress);
114b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    }
115b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
116b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private int getX(float f) {
1178f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang        int r = (int) Math.round(mStartX + f * mDistance * mCosAngle);
1188f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang        if (mCosAngle > 0 && mStartX <= mMaxX) {
1198f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang            r = Math.min(r, mMaxX);
1208f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang        } else if (mCosAngle < 0 && mStartX >= mMinX) {
1218f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang            r = Math.max(r, mMinX);
1228f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang        }
1238f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang        return r;
124b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    }
125b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang
126b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    private int getY(float f) {
1278f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang        int r = (int) Math.round(mStartY + f * mDistance * mSinAngle);
1288f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang        if (mSinAngle > 0 && mStartY <= mMaxY) {
1298f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang            r = Math.min(r, mMaxY);
1308f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang        } else if (mSinAngle < 0 && mStartY >= mMinY) {
1318f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang            r = Math.max(r, mMinY);
1328f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang        }
1338f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang        return r;
134b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang    }
135532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
136532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private double getV(float progress) {
137532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // velocity formula: v(t) = d * (e - s) * (1 - t / T) ^ (d - 1) / T
138532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        return DECELERATED_FACTOR * mDistance * 1000 *
139532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                Math.pow(1 - progress, DECELERATED_FACTOR - 1) / mDuration;
140532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
141b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang}
142