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