• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 package android.animation;
17 
18 import android.content.Context;
19 import android.content.res.Resources;
20 import android.content.res.TypedArray;
21 import android.content.res.XmlResourceParser;
22 import android.content.res.Resources.NotFoundException;
23 import android.util.AttributeSet;
24 import android.util.TypedValue;
25 import android.util.Xml;
26 import android.view.animation.AnimationUtils;
27 import org.xmlpull.v1.XmlPullParser;
28 import org.xmlpull.v1.XmlPullParserException;
29 
30 import java.io.IOException;
31 import java.util.ArrayList;
32 
33 /**
34  * This class is used to instantiate animator XML files into Animator objects.
35  * <p>
36  * For performance reasons, inflation relies heavily on pre-processing of
37  * XML files that is done at build time. Therefore, it is not currently possible
38  * to use this inflater with an XmlPullParser over a plain XML file at runtime;
39  * it only works with an XmlPullParser returned from a compiled resource (R.
40  * <em>something</em> file.)
41  */
42 public class AnimatorInflater {
43 
44     /**
45      * These flags are used when parsing AnimatorSet objects
46      */
47     private static final int TOGETHER = 0;
48     private static final int SEQUENTIALLY = 1;
49 
50     /**
51      * Enum values used in XML attributes to indicate the value for mValueType
52      */
53     private static final int VALUE_TYPE_FLOAT       = 0;
54     private static final int VALUE_TYPE_INT         = 1;
55     private static final int VALUE_TYPE_COLOR       = 4;
56     private static final int VALUE_TYPE_CUSTOM      = 5;
57 
58     /**
59      * Loads an {@link Animator} object from a resource
60      *
61      * @param context Application context used to access resources
62      * @param id The resource id of the animation to load
63      * @return The animator object reference by the specified id
64      * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
65      */
loadAnimator(Context context, int id)66     public static Animator loadAnimator(Context context, int id)
67             throws NotFoundException {
68 
69         XmlResourceParser parser = null;
70         try {
71             parser = context.getResources().getAnimation(id);
72             return createAnimatorFromXml(context, parser);
73         } catch (XmlPullParserException ex) {
74             Resources.NotFoundException rnf =
75                     new Resources.NotFoundException("Can't load animation resource ID #0x" +
76                     Integer.toHexString(id));
77             rnf.initCause(ex);
78             throw rnf;
79         } catch (IOException ex) {
80             Resources.NotFoundException rnf =
81                     new Resources.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 
createAnimatorFromXml(Context c, XmlPullParser parser)90     private static Animator createAnimatorFromXml(Context c, XmlPullParser parser)
91             throws XmlPullParserException, IOException {
92 
93         return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
94     }
95 
createAnimatorFromXml(Context c, XmlPullParser parser, AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)96     private static Animator createAnimatorFromXml(Context c, XmlPullParser parser,
97             AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)
98             throws XmlPullParserException, IOException {
99 
100         Animator anim = null;
101         ArrayList<Animator> childAnims = null;
102 
103         // Make sure we are on a start tag.
104         int type;
105         int depth = parser.getDepth();
106 
107         while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
108                && type != XmlPullParser.END_DOCUMENT) {
109 
110             if (type != XmlPullParser.START_TAG) {
111                 continue;
112             }
113 
114             String  name = parser.getName();
115 
116             if (name.equals("objectAnimator")) {
117                 anim = loadObjectAnimator(c, attrs);
118             } else if (name.equals("animator")) {
119                 anim = loadAnimator(c, attrs, null);
120             } else if (name.equals("set")) {
121                 anim = new AnimatorSet();
122                 TypedArray a = c.obtainStyledAttributes(attrs,
123                         com.android.internal.R.styleable.AnimatorSet);
124                 int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering,
125                         TOGETHER);
126                 createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim,  ordering);
127                 a.recycle();
128             } else {
129                 throw new RuntimeException("Unknown animator name: " + parser.getName());
130             }
131 
132             if (parent != null) {
133                 if (childAnims == null) {
134                     childAnims = new ArrayList<Animator>();
135                 }
136                 childAnims.add(anim);
137             }
138         }
139         if (parent != null && childAnims != null) {
140             Animator[] animsArray = new Animator[childAnims.size()];
141             int index = 0;
142             for (Animator a : childAnims) {
143                 animsArray[index++] = a;
144             }
145             if (sequenceOrdering == TOGETHER) {
146                 parent.playTogether(animsArray);
147             } else {
148                 parent.playSequentially(animsArray);
149             }
150         }
151 
152         return anim;
153 
154     }
155 
loadObjectAnimator(Context context, AttributeSet attrs)156     private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs)
157             throws NotFoundException {
158 
159         ObjectAnimator anim = new ObjectAnimator();
160 
161         loadAnimator(context, attrs, anim);
162 
163         TypedArray a =
164                 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.PropertyAnimator);
165 
166         String propertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName);
167 
168         anim.setPropertyName(propertyName);
169 
170         a.recycle();
171 
172         return anim;
173     }
174 
175     /**
176      * Creates a new animation whose parameters come from the specified context and
177      * attributes set.
178      *
179      * @param context the application environment
180      * @param attrs the set of attributes holding the animation parameters
181      */
loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim)182     private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim)
183             throws NotFoundException {
184 
185         TypedArray a =
186                 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator);
187 
188         long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 0);
189 
190         long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
191 
192         int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
193                 VALUE_TYPE_FLOAT);
194 
195         if (anim == null) {
196             anim = new ValueAnimator();
197         }
198         TypeEvaluator evaluator = null;
199 
200         int valueFromIndex = com.android.internal.R.styleable.Animator_valueFrom;
201         int valueToIndex = com.android.internal.R.styleable.Animator_valueTo;
202 
203         boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
204 
205         TypedValue tvFrom = a.peekValue(valueFromIndex);
206         boolean hasFrom = (tvFrom != null);
207         int fromType = hasFrom ? tvFrom.type : 0;
208         TypedValue tvTo = a.peekValue(valueToIndex);
209         boolean hasTo = (tvTo != null);
210         int toType = hasTo ? tvTo.type : 0;
211 
212         if ((hasFrom && (fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
213                 (fromType <= TypedValue.TYPE_LAST_COLOR_INT)) ||
214             (hasTo && (toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
215                 (toType <= TypedValue.TYPE_LAST_COLOR_INT))) {
216             // special case for colors: ignore valueType and get ints
217             getFloats = false;
218             anim.setEvaluator(new ArgbEvaluator());
219         }
220 
221         if (getFloats) {
222             float valueFrom;
223             float valueTo;
224             if (hasFrom) {
225                 if (fromType == TypedValue.TYPE_DIMENSION) {
226                     valueFrom = a.getDimension(valueFromIndex, 0f);
227                 } else {
228                     valueFrom = a.getFloat(valueFromIndex, 0f);
229                 }
230                 if (hasTo) {
231                     if (toType == TypedValue.TYPE_DIMENSION) {
232                         valueTo = a.getDimension(valueToIndex, 0f);
233                     } else {
234                         valueTo = a.getFloat(valueToIndex, 0f);
235                     }
236                     anim.setFloatValues(valueFrom, valueTo);
237                 } else {
238                     anim.setFloatValues(valueFrom);
239                 }
240             } else {
241                 if (toType == TypedValue.TYPE_DIMENSION) {
242                     valueTo = a.getDimension(valueToIndex, 0f);
243                 } else {
244                     valueTo = a.getFloat(valueToIndex, 0f);
245                 }
246                 anim.setFloatValues(valueTo);
247             }
248         } else {
249             int valueFrom;
250             int valueTo;
251             if (hasFrom) {
252                 if (fromType == TypedValue.TYPE_DIMENSION) {
253                     valueFrom = (int) a.getDimension(valueFromIndex, 0f);
254                 } else if ((fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
255                         (fromType <= TypedValue.TYPE_LAST_COLOR_INT)) {
256                     valueFrom = a.getColor(valueFromIndex, 0);
257                 } else {
258                     valueFrom = a.getInt(valueFromIndex, 0);
259                 }
260                 if (hasTo) {
261                     if (toType == TypedValue.TYPE_DIMENSION) {
262                         valueTo = (int) a.getDimension(valueToIndex, 0f);
263                     } else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
264                             (toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
265                         valueTo = a.getColor(valueToIndex, 0);
266                     } else {
267                         valueTo = a.getInt(valueToIndex, 0);
268                     }
269                     anim.setIntValues(valueFrom, valueTo);
270                 } else {
271                     anim.setIntValues(valueFrom);
272                 }
273             } else {
274                 if (hasTo) {
275                     if (toType == TypedValue.TYPE_DIMENSION) {
276                         valueTo = (int) a.getDimension(valueToIndex, 0f);
277                     } else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
278                         (toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
279                         valueTo = a.getColor(valueToIndex, 0);
280                     } else {
281                         valueTo = a.getInt(valueToIndex, 0);
282                     }
283                     anim.setIntValues(valueTo);
284                 }
285             }
286         }
287 
288         anim.setDuration(duration);
289         anim.setStartDelay(startDelay);
290 
291         if (a.hasValue(com.android.internal.R.styleable.Animator_repeatCount)) {
292             anim.setRepeatCount(
293                     a.getInt(com.android.internal.R.styleable.Animator_repeatCount, 0));
294         }
295         if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) {
296             anim.setRepeatMode(
297                     a.getInt(com.android.internal.R.styleable.Animator_repeatMode,
298                             ValueAnimator.RESTART));
299         }
300         if (evaluator != null) {
301             anim.setEvaluator(evaluator);
302         }
303 
304         final int resID =
305                 a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
306         if (resID > 0) {
307             anim.setInterpolator(AnimationUtils.loadInterpolator(context, resID));
308         }
309         a.recycle();
310 
311         return anim;
312     }
313 }
314