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