1532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang/*
2532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * Copyright (C) 2011 The Android Open Source Project
3532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang *
4532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * Licensed under the Apache License, Version 2.0 (the "License");
5532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * you may not use this file except in compliance with the License.
6532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * You may obtain a copy of the License at
7532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang *
8532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang *      http://www.apache.org/licenses/LICENSE-2.0
9532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang *
10532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * Unless required by applicable law or agreed to in writing, software
11532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * distributed under the License is distributed on an "AS IS" BASIS,
12532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * See the License for the specific language governing permissions and
14532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * limitations under the License.
15532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang */
16532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
17532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Changpackage com.android.gallery3d.ui;
18532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
19532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Changimport com.android.gallery3d.R;
20532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
21532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Changimport android.content.Context;
22532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Changimport android.graphics.Rect;
23532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Changimport android.view.animation.AnimationUtils;
24532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Changimport android.view.animation.DecelerateInterpolator;
25532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Changimport android.view.animation.Interpolator;
26532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
27532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang// This is copied from android.widget.EdgeEffect with some small modifications:
28532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang// (1) Copy the images (overscroll_{edge|glow}.png) to local resources.
29532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang// (2) Use "GLCanvas" instead of "Canvas" for draw()'s parameter.
30532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang// (3) Use a private Drawable class (which inherits from ResourceTexture)
31532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang//     instead of android.graphics.drawable.Drawable to hold the images.
32532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang//     The private Drawable class is used to translate original Canvas calls to
33532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang//     corresponding GLCanvas calls.
34532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
35532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang/**
36532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * This class performs the graphical effect used at the edges of scrollable widgets
37532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * when the user scrolls beyond the content bounds in 2D space.
38532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang *
39532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * <p>EdgeEffect is stateful. Custom widgets using EdgeEffect should create an
40532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * instance for each edge that should show the effect, feed it input data using
41532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * the methods {@link #onAbsorb(int)}, {@link #onPull(float)}, and {@link #onRelease()},
42532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * and draw the effect using {@link #draw(Canvas)} in the widget's overridden
43532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * {@link android.view.View#draw(Canvas)} method. If {@link #isFinished()} returns
44532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * false after drawing, the edge effect's animation is not yet complete and the widget
45532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * should schedule another drawing pass to continue the animation.</p>
46532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang *
47532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * <p>When drawing, widgets should draw their main content and child views first,
48532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * usually by invoking <code>super.draw(canvas)</code> from an overridden <code>draw</code>
49532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * method. (This will invoke onDraw and dispatch drawing to child views as needed.)
50532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * The edge effect may then be drawn on top of the view's content using the
51532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang * {@link #draw(Canvas)} method.</p>
52532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang */
53532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Changpublic class EdgeEffect {
54532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final String TAG = "EdgeEffect";
55532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
56532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    // Time it will take the effect to fully recede in ms
57532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int RECEDE_TIME = 1000;
58532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
59532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    // Time it will take before a pulled glow begins receding in ms
60532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int PULL_TIME = 167;
61532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
62532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    // Time it will take in ms for a pulled glow to decay to partial strength before release
63532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int PULL_DECAY_TIME = 1000;
64532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
65532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final float MAX_ALPHA = 0.8f;
66532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final float HELD_EDGE_ALPHA = 0.7f;
67532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final float HELD_EDGE_SCALE_Y = 0.5f;
68532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final float HELD_GLOW_ALPHA = 0.5f;
69532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final float HELD_GLOW_SCALE_Y = 0.5f;
70532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
71532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final float MAX_GLOW_HEIGHT = 4.f;
72532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
73532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final float PULL_GLOW_BEGIN = 1.f;
74532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final float PULL_EDGE_BEGIN = 0.6f;
75532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
76532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    // Minimum velocity that will be absorbed
77532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int MIN_VELOCITY = 100;
78532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
79532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final float EPSILON = 0.001f;
80532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
81532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private final Drawable mEdge;
82532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private final Drawable mGlow;
83532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private int mWidth;
84532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private int mHeight;
85532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private final int MIN_WIDTH = 300;
86532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private final int mMinWidth;
87532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
88532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mEdgeAlpha;
89532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mEdgeScaleY;
90532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mGlowAlpha;
91532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mGlowScaleY;
92532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
93532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mEdgeAlphaStart;
94532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mEdgeAlphaFinish;
95532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mEdgeScaleYStart;
96532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mEdgeScaleYFinish;
97532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mGlowAlphaStart;
98532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mGlowAlphaFinish;
99532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mGlowScaleYStart;
100532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mGlowScaleYFinish;
101532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
102532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private long mStartTime;
103532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mDuration;
104532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
105532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private final Interpolator mInterpolator;
106532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
107532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int STATE_IDLE = 0;
108532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int STATE_PULL = 1;
109532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int STATE_ABSORB = 2;
110532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int STATE_RECEDE = 3;
111532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int STATE_PULL_DECAY = 4;
112532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
113532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    // How much dragging should effect the height of the edge image.
114532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    // Number determined by user testing.
115532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int PULL_DISTANCE_EDGE_FACTOR = 7;
116532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
117532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    // How much dragging should effect the height of the glow image.
118532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    // Number determined by user testing.
119532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int PULL_DISTANCE_GLOW_FACTOR = 7;
120532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 1.1f;
121532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
122532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int VELOCITY_EDGE_FACTOR = 8;
123532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static final int VELOCITY_GLOW_FACTOR = 16;
124532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
125532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private int mState = STATE_IDLE;
126532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
127532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private float mPullDistance;
128532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
129532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    /**
130532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * Construct a new EdgeEffect with a theme appropriate for the provided context.
131532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * @param context Context used to provide theming and resource information for the EdgeEffect
132532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     */
133532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    public EdgeEffect(Context context) {
134532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdge = new Drawable(context, R.drawable.overscroll_edge);
135532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlow = new Drawable(context, R.drawable.overscroll_glow);
136532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
137532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mMinWidth = (int) (context.getResources().getDisplayMetrics().density * MIN_WIDTH + 0.5f);
138532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mInterpolator = new DecelerateInterpolator();
139532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
140532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
141532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    /**
142532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * Set the size of this edge effect in pixels.
143532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     *
144532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * @param width Effect width in pixels
145532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * @param height Effect height in pixels
146532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     */
147532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    public void setSize(int width, int height) {
148532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mWidth = width;
149532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mHeight = height;
150532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
151532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
152532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    /**
153532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * Reports if this EdgeEffect's animation is finished. If this method returns false
154532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * after a call to {@link #draw(Canvas)} the host widget should schedule another
155532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * drawing pass to continue the animation.
156532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     *
157532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * @return true if animation is finished, false if drawing should continue on the next frame.
158532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     */
159532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    public boolean isFinished() {
160532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        return mState == STATE_IDLE;
161532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
162532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
163532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    /**
164532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * Immediately finish the current animation.
165532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * After this call {@link #isFinished()} will return true.
166532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     */
167532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    public void finish() {
168532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mState = STATE_IDLE;
169532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
170532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
171532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    /**
172532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * A view should call this when content is pulled away from an edge by the user.
173532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * This will update the state of the current visual effect and its associated animation.
174532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * The host view should always {@link android.view.View#invalidate()} after this
175532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * and draw the results accordingly.
176532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     *
177532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
178532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     *                      1.f (full length of the view) or negative values to express change
179532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     *                      back toward the edge reached to initiate the effect.
180532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     */
181532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    public void onPull(float deltaDistance) {
182532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        final long now = AnimationUtils.currentAnimationTimeMillis();
183532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
184532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            return;
185532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
186532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        if (mState != STATE_PULL) {
187532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            mGlowScaleY = PULL_GLOW_BEGIN;
188532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
189532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mState = STATE_PULL;
190532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
191532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mStartTime = now;
192532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mDuration = PULL_TIME;
193532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
194532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mPullDistance += deltaDistance;
195532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        float distance = Math.abs(mPullDistance);
196532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
197532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, MAX_ALPHA));
198532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeScaleY = mEdgeScaleYStart = Math.max(
199532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f));
200532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
201532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
202532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                mGlowAlpha +
203532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
204532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
205532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        float glowChange = Math.abs(deltaDistance);
206532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        if (deltaDistance > 0 && mPullDistance < 0) {
207532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            glowChange = -glowChange;
208532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
209532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        if (mPullDistance == 0) {
210532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            mGlowScaleY = 0;
211532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
212532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
213532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // Do not allow glow to get larger than MAX_GLOW_HEIGHT.
214532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max(
215532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR));
216532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
217532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeAlphaFinish = mEdgeAlpha;
218532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeScaleYFinish = mEdgeScaleY;
219532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowAlphaFinish = mGlowAlpha;
220532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowScaleYFinish = mGlowScaleY;
221532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
222532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
223532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    /**
224532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * Call when the object is released after being pulled.
225532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * This will begin the "decay" phase of the effect. After calling this method
226532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * the host view should {@link android.view.View#invalidate()} and thereby
227532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * draw the results accordingly.
228532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     */
229532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    public void onRelease() {
230532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mPullDistance = 0;
231532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
232532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        if (mState != STATE_PULL && mState != STATE_PULL_DECAY) {
233532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            return;
234532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
235532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
236532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mState = STATE_RECEDE;
237532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeAlphaStart = mEdgeAlpha;
238532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeScaleYStart = mEdgeScaleY;
239532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowAlphaStart = mGlowAlpha;
240532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowScaleYStart = mGlowScaleY;
241532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
242532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeAlphaFinish = 0.f;
243532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeScaleYFinish = 0.f;
244532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowAlphaFinish = 0.f;
245532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowScaleYFinish = 0.f;
246532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
247532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mStartTime = AnimationUtils.currentAnimationTimeMillis();
248532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mDuration = RECEDE_TIME;
249532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
250532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
251532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    /**
252532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * Call when the effect absorbs an impact at the given velocity.
253532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * Used when a fling reaches the scroll boundary.
254532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     *
255532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
256532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * the method <code>getCurrVelocity</code> will provide a reasonable approximation
257532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * to use here.</p>
258532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     *
259532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * @param velocity Velocity at impact in pixels per second.
260532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     */
261532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    public void onAbsorb(int velocity) {
262532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mState = STATE_ABSORB;
263532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        velocity = Math.max(MIN_VELOCITY, Math.abs(velocity));
264532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
265532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mStartTime = AnimationUtils.currentAnimationTimeMillis();
266532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mDuration = 0.1f + (velocity * 0.03f);
267532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
268532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // The edge should always be at least partially visible, regardless
269532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // of velocity.
270532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeAlphaStart = 0.f;
271532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeScaleY = mEdgeScaleYStart = 0.f;
272532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // The glow depends more on the velocity, and therefore starts out
273532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // nearly invisible.
274532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowAlphaStart = 0.5f;
275532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowScaleYStart = 0.f;
276532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
277532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // Factor the velocity by 8. Testing on device shows this works best to
278532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // reflect the strength of the user's scrolling.
279532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1));
280532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // Edge should never get larger than the size of its asset.
281532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeScaleYFinish = Math.max(
282532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                HELD_EDGE_SCALE_Y, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1.f));
283532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
284532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // Growth for the size of the glow should be quadratic to properly
285532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // respond
286532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // to a user's scrolling speed. The faster the scrolling speed, the more
287532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // intense the effect should be for both the size and the saturation.
288532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
289532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        // Alpha should change for the glow as well as size.
290532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowAlphaFinish = Math.max(
291532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
292532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
293532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
294532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
295532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    /**
296532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * Draw into the provided canvas. Assumes that the canvas has been rotated
297532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * accordingly and the size has been set. The effect will be drawn the full
298532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * width of X=0 to X=width, beginning from Y=0 and extending to some factor <
299532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * 1.f of height.
300532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     *
301532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * @param canvas Canvas to draw into
302532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     * @return true if drawing should continue beyond this frame to continue the
303532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     *         animation
304532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang     */
305532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    public boolean draw(GLCanvas canvas) {
306532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        update();
307532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
308532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        final int edgeHeight = mEdge.getIntrinsicHeight();
309532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        final int edgeWidth = mEdge.getIntrinsicWidth();
310532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        final int glowHeight = mGlow.getIntrinsicHeight();
311532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        final int glowWidth = mGlow.getIntrinsicWidth();
312532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
313532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlow.setAlpha((int) (Math.max(0, Math.min(mGlowAlpha, 1)) * 255));
314532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
315532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        int glowBottom = (int) Math.min(
316532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                glowHeight * mGlowScaleY * glowHeight/ glowWidth * 0.6f,
317532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                glowHeight * MAX_GLOW_HEIGHT);
318532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        if (mWidth < mMinWidth) {
319532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            // Center the glow and clip it.
320532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            int glowLeft = (mWidth - mMinWidth)/2;
321532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            mGlow.setBounds(glowLeft, 0, mWidth - glowLeft, glowBottom);
322532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        } else {
323532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            // Stretch the glow to fit.
324532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            mGlow.setBounds(0, 0, mWidth, glowBottom);
325532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
326532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
327532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlow.draw(canvas);
328532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
329532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdge.setAlpha((int) (Math.max(0, Math.min(mEdgeAlpha, 1)) * 255));
330532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
331532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        int edgeBottom = (int) (edgeHeight * mEdgeScaleY);
332532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        if (mWidth < mMinWidth) {
333532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            // Center the edge and clip it.
334532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            int edgeLeft = (mWidth - mMinWidth)/2;
335532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            mEdge.setBounds(edgeLeft, 0, mWidth - edgeLeft, edgeBottom);
336532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        } else {
337532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            // Stretch the edge to fit.
338532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            mEdge.setBounds(0, 0, mWidth, edgeBottom);
339532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
340532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdge.draw(canvas);
341532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
342532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        return mState != STATE_IDLE;
343532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
344532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
345532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private void update() {
346532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        final long time = AnimationUtils.currentAnimationTimeMillis();
347532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        final float t = Math.min((time - mStartTime) / mDuration, 1.f);
348532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
349532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        final float interp = mInterpolator.getInterpolation(t);
350532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
351532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeAlpha = mEdgeAlphaStart + (mEdgeAlphaFinish - mEdgeAlphaStart) * interp;
352532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mEdgeScaleY = mEdgeScaleYStart + (mEdgeScaleYFinish - mEdgeScaleYStart) * interp;
353532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
354532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
355532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
356532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        if (t >= 1.f - EPSILON) {
357532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            switch (mState) {
358532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                case STATE_ABSORB:
359532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mState = STATE_RECEDE;
360532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
361532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mDuration = RECEDE_TIME;
362532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
363532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mEdgeAlphaStart = mEdgeAlpha;
364532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mEdgeScaleYStart = mEdgeScaleY;
365532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mGlowAlphaStart = mGlowAlpha;
366532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mGlowScaleYStart = mGlowScaleY;
367532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
368532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    // After absorb, the glow and edge should fade to nothing.
369532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mEdgeAlphaFinish = 0.f;
370532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mEdgeScaleYFinish = 0.f;
371532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mGlowAlphaFinish = 0.f;
372532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mGlowScaleYFinish = 0.f;
373532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    break;
374532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                case STATE_PULL:
375532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mState = STATE_PULL_DECAY;
376532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
377532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mDuration = PULL_DECAY_TIME;
378532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
379532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mEdgeAlphaStart = mEdgeAlpha;
380532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mEdgeScaleYStart = mEdgeScaleY;
381532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mGlowAlphaStart = mGlowAlpha;
382532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mGlowScaleYStart = mGlowScaleY;
383532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
384532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    // After pull, the glow and edge should fade to nothing.
385532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mEdgeAlphaFinish = 0.f;
386532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mEdgeScaleYFinish = 0.f;
387532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mGlowAlphaFinish = 0.f;
388532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mGlowScaleYFinish = 0.f;
389532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    break;
390532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                case STATE_PULL_DECAY:
391532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    // When receding, we want edge to decrease more slowly
392532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    // than the glow.
393532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    float factor = mGlowScaleYFinish != 0 ? 1
394532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                            / (mGlowScaleYFinish * mGlowScaleYFinish)
395532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                            : Float.MAX_VALUE;
396532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mEdgeScaleY = mEdgeScaleYStart +
397532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                        (mEdgeScaleYFinish - mEdgeScaleYStart) *
398532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                            interp * factor;
399532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mState = STATE_RECEDE;
400532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    break;
401532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                case STATE_RECEDE:
402532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    mState = STATE_IDLE;
403532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang                    break;
404532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            }
405532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
406532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
407532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
408532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    private static class Drawable extends ResourceTexture {
409532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        private Rect mBounds = new Rect();
410532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        private int mAlpha = 255;
411532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
412532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        public Drawable(Context context, int resId) {
413532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            super(context, resId);
414532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
415532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
416532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        public int getIntrinsicWidth() {
417532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            return getWidth();
418532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
419532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
420532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        public int getIntrinsicHeight() {
421532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            return getHeight();
422532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
423532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
424532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        public void setBounds(int left, int top, int right, int bottom) {
425532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            mBounds.set(left, top, right, bottom);
426532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
427532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
428532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        public void setAlpha(int alpha) {
429532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            mAlpha = alpha;
430532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
431532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang
432532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        public void draw(GLCanvas canvas) {
433532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            canvas.save(GLCanvas.SAVE_FLAG_ALPHA);
434532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            canvas.multiplyAlpha(mAlpha / 255.0f);
435532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            Rect b = mBounds;
436532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            draw(canvas, b.left, b.top, b.width(), b.height());
437532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang            canvas.restore();
438532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang        }
439532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang    }
440532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang}
441