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