1 /*
2  * Copyright (C) 2007 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  */
16 
17 package android.view.animation;
18 
19 import org.xmlpull.v1.XmlPullParser;
20 import org.xmlpull.v1.XmlPullParserException;
21 
22 import android.content.Context;
23 import android.content.res.Resources;
24 import android.content.res.Resources.Theme;
25 import android.content.res.XmlResourceParser;
26 import android.content.res.Resources.NotFoundException;
27 import android.util.AttributeSet;
28 import android.util.Xml;
29 import android.os.SystemClock;
30 
31 import java.io.IOException;
32 
33 /**
34  * Defines common utilities for working with animations.
35  *
36  */
37 public class AnimationUtils {
38 
39     /**
40      * These flags are used when parsing AnimatorSet objects
41      */
42     private static final int TOGETHER = 0;
43     private static final int SEQUENTIALLY = 1;
44 
45 
46     /**
47      * Returns the current animation time in milliseconds. This time should be used when invoking
48      * {@link Animation#setStartTime(long)}. Refer to {@link android.os.SystemClock} for more
49      * information about the different available clocks. The clock used by this method is
50      * <em>not</em> the "wall" clock (it is not {@link System#currentTimeMillis}).
51      *
52      * @return the current animation time in milliseconds
53      *
54      * @see android.os.SystemClock
55      */
currentAnimationTimeMillis()56     public static long currentAnimationTimeMillis() {
57         return SystemClock.uptimeMillis();
58     }
59 
60     /**
61      * Loads an {@link Animation} object from a resource
62      *
63      * @param context Application context used to access resources
64      * @param id The resource id of the animation to load
65      * @return The animation object reference by the specified id
66      * @throws NotFoundException when the animation cannot be loaded
67      */
loadAnimation(Context context, int id)68     public static Animation loadAnimation(Context context, int id)
69             throws NotFoundException {
70 
71         XmlResourceParser parser = null;
72         try {
73             parser = context.getResources().getAnimation(id);
74             return createAnimationFromXml(context, parser);
75         } catch (XmlPullParserException ex) {
76             NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
77                     Integer.toHexString(id));
78             rnf.initCause(ex);
79             throw rnf;
80         } catch (IOException ex) {
81             NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
82                     Integer.toHexString(id));
83             rnf.initCause(ex);
84             throw rnf;
85         } finally {
86             if (parser != null) parser.close();
87         }
88     }
89 
createAnimationFromXml(Context c, XmlPullParser parser)90     private static Animation createAnimationFromXml(Context c, XmlPullParser parser)
91             throws XmlPullParserException, IOException {
92 
93         return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser));
94     }
95 
createAnimationFromXml(Context c, XmlPullParser parser, AnimationSet parent, AttributeSet attrs)96     private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
97             AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
98 
99         Animation anim = null;
100 
101         // Make sure we are on a start tag.
102         int type;
103         int depth = parser.getDepth();
104 
105         while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
106                && type != XmlPullParser.END_DOCUMENT) {
107 
108             if (type != XmlPullParser.START_TAG) {
109                 continue;
110             }
111 
112             String  name = parser.getName();
113 
114             if (name.equals("set")) {
115                 anim = new AnimationSet(c, attrs);
116                 createAnimationFromXml(c, parser, (AnimationSet)anim, attrs);
117             } else if (name.equals("alpha")) {
118                 anim = new AlphaAnimation(c, attrs);
119             } else if (name.equals("scale")) {
120                 anim = new ScaleAnimation(c, attrs);
121             }  else if (name.equals("rotate")) {
122                 anim = new RotateAnimation(c, attrs);
123             }  else if (name.equals("translate")) {
124                 anim = new TranslateAnimation(c, attrs);
125             } else {
126                 throw new RuntimeException("Unknown animation name: " + parser.getName());
127             }
128 
129             if (parent != null) {
130                 parent.addAnimation(anim);
131             }
132         }
133 
134         return anim;
135 
136     }
137 
138     /**
139      * Loads a {@link LayoutAnimationController} object from a resource
140      *
141      * @param context Application context used to access resources
142      * @param id The resource id of the animation to load
143      * @return The animation object reference by the specified id
144      * @throws NotFoundException when the layout animation controller cannot be loaded
145      */
loadLayoutAnimation(Context context, int id)146     public static LayoutAnimationController loadLayoutAnimation(Context context, int id)
147             throws NotFoundException {
148 
149         XmlResourceParser parser = null;
150         try {
151             parser = context.getResources().getAnimation(id);
152             return createLayoutAnimationFromXml(context, parser);
153         } catch (XmlPullParserException ex) {
154             NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
155                     Integer.toHexString(id));
156             rnf.initCause(ex);
157             throw rnf;
158         } catch (IOException ex) {
159             NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
160                     Integer.toHexString(id));
161             rnf.initCause(ex);
162             throw rnf;
163         } finally {
164             if (parser != null) parser.close();
165         }
166     }
167 
createLayoutAnimationFromXml(Context c, XmlPullParser parser)168     private static LayoutAnimationController createLayoutAnimationFromXml(Context c,
169             XmlPullParser parser) throws XmlPullParserException, IOException {
170 
171         return createLayoutAnimationFromXml(c, parser, Xml.asAttributeSet(parser));
172     }
173 
createLayoutAnimationFromXml(Context c, XmlPullParser parser, AttributeSet attrs)174     private static LayoutAnimationController createLayoutAnimationFromXml(Context c,
175             XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException {
176 
177         LayoutAnimationController controller = null;
178 
179         int type;
180         int depth = parser.getDepth();
181 
182         while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
183                 && type != XmlPullParser.END_DOCUMENT) {
184 
185             if (type != XmlPullParser.START_TAG) {
186                 continue;
187             }
188 
189             String name = parser.getName();
190 
191             if ("layoutAnimation".equals(name)) {
192                 controller = new LayoutAnimationController(c, attrs);
193             } else if ("gridLayoutAnimation".equals(name)) {
194                 controller = new GridLayoutAnimationController(c, attrs);
195             } else {
196                 throw new RuntimeException("Unknown layout animation name: " + name);
197             }
198         }
199 
200         return controller;
201     }
202 
203     /**
204      * Make an animation for objects becoming visible. Uses a slide and fade
205      * effect.
206      *
207      * @param c Context for loading resources
208      * @param fromLeft is the object to be animated coming from the left
209      * @return The new animation
210      */
makeInAnimation(Context c, boolean fromLeft)211     public static Animation makeInAnimation(Context c, boolean fromLeft) {
212         Animation a;
213         if (fromLeft) {
214             a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_left);
215         } else {
216             a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_right);
217         }
218 
219         a.setInterpolator(new DecelerateInterpolator());
220         a.setStartTime(currentAnimationTimeMillis());
221         return a;
222     }
223 
224     /**
225      * Make an animation for objects becoming invisible. Uses a slide and fade
226      * effect.
227      *
228      * @param c Context for loading resources
229      * @param toRight is the object to be animated exiting to the right
230      * @return The new animation
231      */
makeOutAnimation(Context c, boolean toRight)232     public static Animation makeOutAnimation(Context c, boolean toRight) {
233         Animation a;
234         if (toRight) {
235             a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_right);
236         } else {
237             a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_left);
238         }
239 
240         a.setInterpolator(new AccelerateInterpolator());
241         a.setStartTime(currentAnimationTimeMillis());
242         return a;
243     }
244 
245 
246     /**
247      * Make an animation for objects becoming visible. Uses a slide up and fade
248      * effect.
249      *
250      * @param c Context for loading resources
251      * @return The new animation
252      */
makeInChildBottomAnimation(Context c)253     public static Animation makeInChildBottomAnimation(Context c) {
254         Animation a;
255         a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_child_bottom);
256         a.setInterpolator(new AccelerateInterpolator());
257         a.setStartTime(currentAnimationTimeMillis());
258         return a;
259     }
260 
261     /**
262      * Loads an {@link Interpolator} object from a resource
263      *
264      * @param context Application context used to access resources
265      * @param id The resource id of the animation to load
266      * @return The animation object reference by the specified id
267      * @throws NotFoundException
268      */
loadInterpolator(Context context, int id)269     public static Interpolator loadInterpolator(Context context, int id) throws NotFoundException {
270         XmlResourceParser parser = null;
271         try {
272             parser = context.getResources().getAnimation(id);
273             return createInterpolatorFromXml(context.getResources(), context.getTheme(), parser);
274         } catch (XmlPullParserException ex) {
275             NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
276                     Integer.toHexString(id));
277             rnf.initCause(ex);
278             throw rnf;
279         } catch (IOException ex) {
280             NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
281                     Integer.toHexString(id));
282             rnf.initCause(ex);
283             throw rnf;
284         } finally {
285             if (parser != null) parser.close();
286         }
287 
288     }
289 
290     /**
291      * Loads an {@link Interpolator} object from a resource
292      *
293      * @param res The resources
294      * @param id The resource id of the animation to load
295      * @return The interpolator object reference by the specified id
296      * @throws NotFoundException
297      * @hide
298      */
loadInterpolator(Resources res, Theme theme, int id)299     public static Interpolator loadInterpolator(Resources res, Theme theme, int id) throws NotFoundException {
300         XmlResourceParser parser = null;
301         try {
302             parser = res.getAnimation(id);
303             return createInterpolatorFromXml(res, theme, parser);
304         } catch (XmlPullParserException ex) {
305             NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
306                     Integer.toHexString(id));
307             rnf.initCause(ex);
308             throw rnf;
309         } catch (IOException ex) {
310             NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
311                     Integer.toHexString(id));
312             rnf.initCause(ex);
313             throw rnf;
314         } finally {
315             if (parser != null)
316                 parser.close();
317         }
318 
319     }
320 
createInterpolatorFromXml(Resources res, Theme theme, XmlPullParser parser)321     private static Interpolator createInterpolatorFromXml(Resources res, Theme theme, XmlPullParser parser)
322             throws XmlPullParserException, IOException {
323 
324         BaseInterpolator interpolator = null;
325 
326         // Make sure we are on a start tag.
327         int type;
328         int depth = parser.getDepth();
329 
330         while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
331                 && type != XmlPullParser.END_DOCUMENT) {
332 
333             if (type != XmlPullParser.START_TAG) {
334                 continue;
335             }
336 
337             AttributeSet attrs = Xml.asAttributeSet(parser);
338 
339             String name = parser.getName();
340 
341             if (name.equals("linearInterpolator")) {
342                 interpolator = new LinearInterpolator();
343             } else if (name.equals("accelerateInterpolator")) {
344                 interpolator = new AccelerateInterpolator(res, theme, attrs);
345             } else if (name.equals("decelerateInterpolator")) {
346                 interpolator = new DecelerateInterpolator(res, theme, attrs);
347             } else if (name.equals("accelerateDecelerateInterpolator")) {
348                 interpolator = new AccelerateDecelerateInterpolator();
349             } else if (name.equals("cycleInterpolator")) {
350                 interpolator = new CycleInterpolator(res, theme, attrs);
351             } else if (name.equals("anticipateInterpolator")) {
352                 interpolator = new AnticipateInterpolator(res, theme, attrs);
353             } else if (name.equals("overshootInterpolator")) {
354                 interpolator = new OvershootInterpolator(res, theme, attrs);
355             } else if (name.equals("anticipateOvershootInterpolator")) {
356                 interpolator = new AnticipateOvershootInterpolator(res, theme, attrs);
357             } else if (name.equals("bounceInterpolator")) {
358                 interpolator = new BounceInterpolator();
359             } else if (name.equals("pathInterpolator")) {
360                 interpolator = new PathInterpolator(res, theme, attrs);
361             } else {
362                 throw new RuntimeException("Unknown interpolator name: " + parser.getName());
363             }
364         }
365         return interpolator;
366     }
367 }
368