1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.support.v4.widget;
17
18import android.content.Context;
19import android.graphics.Canvas;
20import android.os.Build;
21
22/**
23 * Helper for accessing {@link android.widget.EdgeEffect} introduced after
24 * API level 4 in a backwards compatible fashion.
25 *
26 * This class is used to access {@link android.widget.EdgeEffect} on platform versions
27 * that support it. When running on older platforms it will result in no-ops. It should
28 * be used by views that wish to use the standard Android visual effects at the edges
29 * of scrolling containers.
30 */
31public class EdgeEffectCompat {
32    private Object mEdgeEffect;
33
34    private static final EdgeEffectImpl IMPL;
35
36    static {
37        if (Build.VERSION.SDK_INT >= 14) { // ICS
38            IMPL = new EdgeEffectIcsImpl();
39        } else {
40            IMPL = new BaseEdgeEffectImpl();
41        }
42    }
43
44    interface EdgeEffectImpl {
45        public Object newEdgeEffect(Context context);
46        public void setSize(Object edgeEffect, int width, int height);
47        public boolean isFinished(Object edgeEffect);
48        public void finish(Object edgeEffect);
49        public boolean onPull(Object edgeEffect, float deltaDistance);
50        public boolean onRelease(Object edgeEffect);
51        public boolean onAbsorb(Object edgeEffect, int velocity);
52        public boolean draw(Object edgeEffect, Canvas canvas);
53    }
54
55    /**
56     * Null implementation to use pre-ICS
57     */
58    static class BaseEdgeEffectImpl implements EdgeEffectImpl {
59        public Object newEdgeEffect(Context context) {
60            return null;
61        }
62
63        public void setSize(Object edgeEffect, int width, int height) {
64        }
65
66        public boolean isFinished(Object edgeEffect) {
67            return true;
68        }
69
70        public void finish(Object edgeEffect) {
71        }
72
73        public boolean onPull(Object edgeEffect, float deltaDistance) {
74            return false;
75        }
76
77        public boolean onRelease(Object edgeEffect) {
78            return false;
79        }
80
81        public boolean onAbsorb(Object edgeEffect, int velocity) {
82            return false;
83        }
84
85        public boolean draw(Object edgeEffect, Canvas canvas) {
86            return false;
87        }
88    }
89
90    static class EdgeEffectIcsImpl implements EdgeEffectImpl {
91        public Object newEdgeEffect(Context context) {
92            return EdgeEffectCompatIcs.newEdgeEffect(context);
93        }
94
95        public void setSize(Object edgeEffect, int width, int height) {
96            EdgeEffectCompatIcs.setSize(edgeEffect, width, height);
97        }
98
99        public boolean isFinished(Object edgeEffect) {
100            return EdgeEffectCompatIcs.isFinished(edgeEffect);
101        }
102
103        public void finish(Object edgeEffect) {
104            EdgeEffectCompatIcs.finish(edgeEffect);
105        }
106
107        public boolean onPull(Object edgeEffect, float deltaDistance) {
108            return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance);
109        }
110
111        public boolean onRelease(Object edgeEffect) {
112            return EdgeEffectCompatIcs.onRelease(edgeEffect);
113        }
114
115        public boolean onAbsorb(Object edgeEffect, int velocity) {
116            return EdgeEffectCompatIcs.onAbsorb(edgeEffect, velocity);
117        }
118
119        public boolean draw(Object edgeEffect, Canvas canvas) {
120            return EdgeEffectCompatIcs.draw(edgeEffect, canvas);
121        }
122    }
123
124    /**
125     * Construct a new EdgeEffect themed using the given context.
126     *
127     * <p>Note: On platform versions that do not support EdgeEffect, all operations
128     * on the newly constructed object will be mocked/no-ops.</p>
129     *
130     * @param context Context to use for theming the effect
131     */
132    public EdgeEffectCompat(Context context) {
133        mEdgeEffect = IMPL.newEdgeEffect(context);
134    }
135
136    /**
137     * Set the size of this edge effect in pixels.
138     *
139     * @param width Effect width in pixels
140     * @param height Effect height in pixels
141     */
142    public void setSize(int width, int height) {
143        IMPL.setSize(mEdgeEffect, width, height);
144    }
145
146    /**
147     * Reports if this EdgeEffectCompat's animation is finished. If this method returns false
148     * after a call to {@link #draw(Canvas)} the host widget should schedule another
149     * drawing pass to continue the animation.
150     *
151     * @return true if animation is finished, false if drawing should continue on the next frame.
152     */
153    public boolean isFinished() {
154        return IMPL.isFinished(mEdgeEffect);
155    }
156
157    /**
158     * Immediately finish the current animation.
159     * After this call {@link #isFinished()} will return true.
160     */
161    public void finish() {
162        IMPL.finish(mEdgeEffect);
163    }
164
165    /**
166     * A view should call this when content is pulled away from an edge by the user.
167     * This will update the state of the current visual effect and its associated animation.
168     * The host view should always {@link android.view.View#invalidate()} if this method
169     * returns true and draw the results accordingly.
170     *
171     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
172     *                      1.f (full length of the view) or negative values to express change
173     *                      back toward the edge reached to initiate the effect.
174     * @return true if the host view should call invalidate, false if it should not.
175     */
176    public boolean onPull(float deltaDistance) {
177        return IMPL.onPull(mEdgeEffect, deltaDistance);
178    }
179
180    /**
181     * Call when the object is released after being pulled.
182     * This will begin the "decay" phase of the effect. After calling this method
183     * the host view should {@link android.view.View#invalidate()} if this method
184     * returns true and thereby draw the results accordingly.
185     *
186     * @return true if the host view should invalidate, false if it should not.
187     */
188    public boolean onRelease() {
189        return IMPL.onRelease(mEdgeEffect);
190    }
191
192    /**
193     * Call when the effect absorbs an impact at the given velocity.
194     * Used when a fling reaches the scroll boundary.
195     *
196     * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
197     * the method <code>getCurrVelocity</code> will provide a reasonable approximation
198     * to use here.</p>
199     *
200     * @param velocity Velocity at impact in pixels per second.
201     * @return true if the host view should invalidate, false if it should not.
202     */
203    public boolean onAbsorb(int velocity) {
204        return IMPL.onAbsorb(mEdgeEffect, velocity);
205    }
206
207    /**
208     * Draw into the provided canvas. Assumes that the canvas has been rotated
209     * accordingly and the size has been set. The effect will be drawn the full
210     * width of X=0 to X=width, beginning from Y=0 and extending to some factor <
211     * 1.f of height.
212     *
213     * @param canvas Canvas to draw into
214     * @return true if drawing should continue beyond this frame to continue the
215     *         animation
216     */
217    public boolean draw(Canvas canvas) {
218        return IMPL.draw(mEdgeEffect, canvas);
219    }
220}
221