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 final class EdgeEffectCompat {
32    private Object mEdgeEffect;
33
34    private static final EdgeEffectImpl IMPL;
35
36    static {
37        if (Build.VERSION.SDK_INT >= 21) {
38            IMPL = new EdgeEffectLollipopImpl(); // Lollipop
39        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
40            IMPL = new EdgeEffectIcsImpl();
41        } else {
42            IMPL = new BaseEdgeEffectImpl();
43        }
44    }
45
46    interface EdgeEffectImpl {
47        public Object newEdgeEffect(Context context);
48        public void setSize(Object edgeEffect, int width, int height);
49        public boolean isFinished(Object edgeEffect);
50        public void finish(Object edgeEffect);
51        public boolean onPull(Object edgeEffect, float deltaDistance);
52        public boolean onRelease(Object edgeEffect);
53        public boolean onAbsorb(Object edgeEffect, int velocity);
54        public boolean draw(Object edgeEffect, Canvas canvas);
55        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement);
56    }
57
58    /**
59     * Null implementation to use pre-ICS
60     */
61    static class BaseEdgeEffectImpl implements EdgeEffectImpl {
62        public Object newEdgeEffect(Context context) {
63            return null;
64        }
65
66        public void setSize(Object edgeEffect, int width, int height) {
67        }
68
69        public boolean isFinished(Object edgeEffect) {
70            return true;
71        }
72
73        public void finish(Object edgeEffect) {
74        }
75
76        public boolean onPull(Object edgeEffect, float deltaDistance) {
77            return false;
78        }
79
80        public boolean onRelease(Object edgeEffect) {
81            return false;
82        }
83
84        public boolean onAbsorb(Object edgeEffect, int velocity) {
85            return false;
86        }
87
88        public boolean draw(Object edgeEffect, Canvas canvas) {
89            return false;
90        }
91
92        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
93            return false;
94        }
95    }
96
97    static class EdgeEffectIcsImpl implements EdgeEffectImpl {
98        public Object newEdgeEffect(Context context) {
99            return EdgeEffectCompatIcs.newEdgeEffect(context);
100        }
101
102        public void setSize(Object edgeEffect, int width, int height) {
103            EdgeEffectCompatIcs.setSize(edgeEffect, width, height);
104        }
105
106        public boolean isFinished(Object edgeEffect) {
107            return EdgeEffectCompatIcs.isFinished(edgeEffect);
108        }
109
110        public void finish(Object edgeEffect) {
111            EdgeEffectCompatIcs.finish(edgeEffect);
112        }
113
114        public boolean onPull(Object edgeEffect, float deltaDistance) {
115            return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance);
116        }
117
118        public boolean onRelease(Object edgeEffect) {
119            return EdgeEffectCompatIcs.onRelease(edgeEffect);
120        }
121
122        public boolean onAbsorb(Object edgeEffect, int velocity) {
123            return EdgeEffectCompatIcs.onAbsorb(edgeEffect, velocity);
124        }
125
126        public boolean draw(Object edgeEffect, Canvas canvas) {
127            return EdgeEffectCompatIcs.draw(edgeEffect, canvas);
128        }
129
130        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
131            return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance);
132        }
133    }
134
135    static class EdgeEffectLollipopImpl extends EdgeEffectIcsImpl {
136        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
137            return EdgeEffectCompatLollipop.onPull(edgeEffect, deltaDistance, displacement);
138        }
139    }
140
141    /**
142     * Construct a new EdgeEffect themed using the given context.
143     *
144     * <p>Note: On platform versions that do not support EdgeEffect, all operations
145     * on the newly constructed object will be mocked/no-ops.</p>
146     *
147     * @param context Context to use for theming the effect
148     */
149    public EdgeEffectCompat(Context context) {
150        mEdgeEffect = IMPL.newEdgeEffect(context);
151    }
152
153    /**
154     * Set the size of this edge effect in pixels.
155     *
156     * @param width Effect width in pixels
157     * @param height Effect height in pixels
158     */
159    public void setSize(int width, int height) {
160        IMPL.setSize(mEdgeEffect, width, height);
161    }
162
163    /**
164     * Reports if this EdgeEffectCompat's animation is finished. If this method returns false
165     * after a call to {@link #draw(Canvas)} the host widget should schedule another
166     * drawing pass to continue the animation.
167     *
168     * @return true if animation is finished, false if drawing should continue on the next frame.
169     */
170    public boolean isFinished() {
171        return IMPL.isFinished(mEdgeEffect);
172    }
173
174    /**
175     * Immediately finish the current animation.
176     * After this call {@link #isFinished()} will return true.
177     */
178    public void finish() {
179        IMPL.finish(mEdgeEffect);
180    }
181
182    /**
183     * A view should call this when content is pulled away from an edge by the user.
184     * This will update the state of the current visual effect and its associated animation.
185     * The host view should always {@link android.view.View#invalidate()} if this method
186     * returns true and draw the results accordingly.
187     *
188     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
189     *                      1.f (full length of the view) or negative values to express change
190     *                      back toward the edge reached to initiate the effect.
191     * @return true if the host view should call invalidate, false if it should not.
192     * @deprecated use {@link #onPull(float, float)}
193     */
194    @Deprecated
195    public boolean onPull(float deltaDistance) {
196        return IMPL.onPull(mEdgeEffect, deltaDistance);
197    }
198
199    /**
200     * A view should call this when content is pulled away from an edge by the user.
201     * This will update the state of the current visual effect and its associated animation.
202     * The host view should always {@link android.view.View#invalidate()} if this method
203     * returns true and draw the results accordingly.
204     *
205     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
206     *                      1.f (full length of the view) or negative values to express change
207     *                      back toward the edge reached to initiate the effect.
208     * @param displacement The displacement from the starting side of the effect of the point
209     *                     initiating the pull. In the case of touch this is the finger position.
210     *                     Values may be from 0-1.
211     * @return true if the host view should call invalidate, false if it should not.
212     */
213    public boolean onPull(float deltaDistance, float displacement) {
214        return IMPL.onPull(mEdgeEffect, deltaDistance, displacement);
215    }
216
217    /**
218     * Call when the object is released after being pulled.
219     * This will begin the "decay" phase of the effect. After calling this method
220     * the host view should {@link android.view.View#invalidate()} if this method
221     * returns true and thereby draw the results accordingly.
222     *
223     * @return true if the host view should invalidate, false if it should not.
224     */
225    public boolean onRelease() {
226        return IMPL.onRelease(mEdgeEffect);
227    }
228
229    /**
230     * Call when the effect absorbs an impact at the given velocity.
231     * Used when a fling reaches the scroll boundary.
232     *
233     * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
234     * the method <code>getCurrVelocity</code> will provide a reasonable approximation
235     * to use here.</p>
236     *
237     * @param velocity Velocity at impact in pixels per second.
238     * @return true if the host view should invalidate, false if it should not.
239     */
240    public boolean onAbsorb(int velocity) {
241        return IMPL.onAbsorb(mEdgeEffect, velocity);
242    }
243
244    /**
245     * Draw into the provided canvas. Assumes that the canvas has been rotated
246     * accordingly and the size has been set. The effect will be drawn the full
247     * width of X=0 to X=width, beginning from Y=0 and extending to some factor <
248     * 1.f of height.
249     *
250     * @param canvas Canvas to draw into
251     * @return true if drawing should continue beyond this frame to continue the
252     *         animation
253     */
254    public boolean draw(Canvas canvas) {
255        return IMPL.draw(mEdgeEffect, canvas);
256    }
257}
258