blob: ed4036db7ff2d9e7c8c4829d9223615ee4228e40 [file] [log] [blame]
Chet Haased51d3682010-08-11 19:46:48 -07001/*
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 */
16package android.animation;
17
18import android.content.Context;
19import android.content.res.Resources;
20import android.content.res.TypedArray;
21import android.content.res.XmlResourceParser;
22import android.content.res.Resources.NotFoundException;
23import android.util.AttributeSet;
Chet Haase5bed88e12010-11-30 14:43:33 -080024import android.util.TypedValue;
Chet Haased51d3682010-08-11 19:46:48 -070025import android.util.Xml;
26import android.view.animation.AnimationUtils;
27import org.xmlpull.v1.XmlPullParser;
28import org.xmlpull.v1.XmlPullParserException;
29
30import java.io.IOException;
31import java.util.ArrayList;
32
33/**
Chet Haase6cfdf452011-04-22 16:42:10 -070034 * This class is used to instantiate animator XML files into Animator objects.
Chet Haased51d3682010-08-11 19:46:48 -070035 * <p>
Chet Haase6cfdf452011-04-22 16:42:10 -070036 * For performance reasons, inflation relies heavily on pre-processing of
Chet Haased51d3682010-08-11 19:46:48 -070037 * XML files that is done at build time. Therefore, it is not currently possible
Chet Haase6cfdf452011-04-22 16:42:10 -070038 * to use this inflater with an XmlPullParser over a plain XML file at runtime;
Chet Haased51d3682010-08-11 19:46:48 -070039 * it only works with an XmlPullParser returned from a compiled resource (R.
40 * <em>something</em> file.)
41 */
Chet Haasea18a86b2010-09-07 13:20:00 -070042public class AnimatorInflater {
Chet Haased51d3682010-08-11 19:46:48 -070043
44 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070045 * These flags are used when parsing AnimatorSet objects
Chet Haased51d3682010-08-11 19:46:48 -070046 */
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;
Chet Haase2794eb32010-10-12 16:29:28 -070055 private static final int VALUE_TYPE_COLOR = 4;
56 private static final int VALUE_TYPE_CUSTOM = 5;
Chet Haased51d3682010-08-11 19:46:48 -070057
58 /**
Chet Haasea18a86b2010-09-07 13:20:00 -070059 * Loads an {@link Animator} object from a resource
Chet Haased51d3682010-08-11 19:46:48 -070060 *
61 * @param context Application context used to access resources
62 * @param id The resource id of the animation to load
Chet Haasea18a86b2010-09-07 13:20:00 -070063 * @return The animator object reference by the specified id
Chet Haased51d3682010-08-11 19:46:48 -070064 * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
65 */
Chet Haasea18a86b2010-09-07 13:20:00 -070066 public static Animator loadAnimator(Context context, int id)
Chet Haased51d3682010-08-11 19:46:48 -070067 throws NotFoundException {
68
69 XmlResourceParser parser = null;
70 try {
71 parser = context.getResources().getAnimation(id);
Chet Haasea18a86b2010-09-07 13:20:00 -070072 return createAnimatorFromXml(context, parser);
Chet Haased51d3682010-08-11 19:46:48 -070073 } 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
Chet Haasea18a86b2010-09-07 13:20:00 -070090 private static Animator createAnimatorFromXml(Context c, XmlPullParser parser)
Chet Haased51d3682010-08-11 19:46:48 -070091 throws XmlPullParserException, IOException {
92
Chet Haasea18a86b2010-09-07 13:20:00 -070093 return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
Chet Haased51d3682010-08-11 19:46:48 -070094 }
95
Chet Haasea18a86b2010-09-07 13:20:00 -070096 private static Animator createAnimatorFromXml(Context c, XmlPullParser parser,
97 AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)
Chet Haased51d3682010-08-11 19:46:48 -070098 throws XmlPullParserException, IOException {
99
Chet Haasea18a86b2010-09-07 13:20:00 -0700100 Animator anim = null;
101 ArrayList<Animator> childAnims = null;
Chet Haased51d3682010-08-11 19:46:48 -0700102
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
Chet Haasea18a86b2010-09-07 13:20:00 -0700116 if (name.equals("objectAnimator")) {
117 anim = loadObjectAnimator(c, attrs);
Chet Haased51d3682010-08-11 19:46:48 -0700118 } else if (name.equals("animator")) {
119 anim = loadAnimator(c, attrs, null);
Chet Haasea18a86b2010-09-07 13:20:00 -0700120 } else if (name.equals("set")) {
121 anim = new AnimatorSet();
Chet Haased51d3682010-08-11 19:46:48 -0700122 TypedArray a = c.obtainStyledAttributes(attrs,
Chet Haasea18a86b2010-09-07 13:20:00 -0700123 com.android.internal.R.styleable.AnimatorSet);
124 int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering,
Chet Haased51d3682010-08-11 19:46:48 -0700125 TOGETHER);
Chet Haasea18a86b2010-09-07 13:20:00 -0700126 createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim, ordering);
Chet Haased51d3682010-08-11 19:46:48 -0700127 a.recycle();
128 } else {
129 throw new RuntimeException("Unknown animator name: " + parser.getName());
130 }
131
132 if (parent != null) {
133 if (childAnims == null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700134 childAnims = new ArrayList<Animator>();
Chet Haased51d3682010-08-11 19:46:48 -0700135 }
136 childAnims.add(anim);
137 }
138 }
139 if (parent != null && childAnims != null) {
Chet Haasea18a86b2010-09-07 13:20:00 -0700140 Animator[] animsArray = new Animator[childAnims.size()];
Chet Haased51d3682010-08-11 19:46:48 -0700141 int index = 0;
Chet Haasea18a86b2010-09-07 13:20:00 -0700142 for (Animator a : childAnims) {
Chet Haased51d3682010-08-11 19:46:48 -0700143 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
Chet Haasea18a86b2010-09-07 13:20:00 -0700156 private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs)
Chet Haased51d3682010-08-11 19:46:48 -0700157 throws NotFoundException {
158
Chet Haasea18a86b2010-09-07 13:20:00 -0700159 ObjectAnimator anim = new ObjectAnimator();
Chet Haased51d3682010-08-11 19:46:48 -0700160
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 */
Chet Haasea18a86b2010-09-07 13:20:00 -0700182 private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim)
Chet Haased51d3682010-08-11 19:46:48 -0700183 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
Chet Haase2794eb32010-10-12 16:29:28 -0700195 if (anim == null) {
196 anim = new ValueAnimator();
197 }
Chet Haased51d3682010-08-11 19:46:48 -0700198 TypeEvaluator evaluator = null;
199
Chet Haase5bed88e12010-11-30 14:43:33 -0800200 int valueFromIndex = com.android.internal.R.styleable.Animator_valueFrom;
201 int valueToIndex = com.android.internal.R.styleable.Animator_valueTo;
Chet Haase2794eb32010-10-12 16:29:28 -0700202
Chet Haase5bed88e12010-11-30 14:43:33 -0800203 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;
Chet Haase53ee3312011-01-10 15:56:56 -0800218 anim.setEvaluator(new ArgbEvaluator());
Chet Haase5bed88e12010-11-30 14:43:33 -0800219 }
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);
Chet Haase2794eb32010-10-12 16:29:28 -0700227 } else {
Chet Haase5bed88e12010-11-30 14:43:33 -0800228 valueFrom = a.getFloat(valueFromIndex, 0f);
Romain Guy83d6e822010-10-14 10:13:53 -0700229 }
Chet Haase5bed88e12010-11-30 14:43:33 -0800230 if (hasTo) {
231 if (toType == TypedValue.TYPE_DIMENSION) {
232 valueTo = a.getDimension(valueToIndex, 0f);
Chet Haase2794eb32010-10-12 16:29:28 -0700233 } else {
Chet Haase5bed88e12010-11-30 14:43:33 -0800234 valueTo = a.getFloat(valueToIndex, 0f);
Chet Haase2794eb32010-10-12 16:29:28 -0700235 }
Chet Haase5bed88e12010-11-30 14:43:33 -0800236 anim.setFloatValues(valueFrom, valueTo);
Chet Haase2794eb32010-10-12 16:29:28 -0700237 } else {
Chet Haase5bed88e12010-11-30 14:43:33 -0800238 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 }
Chet Haase2794eb32010-10-12 16:29:28 -0700283 anim.setIntValues(valueTo);
Chet Haased51d3682010-08-11 19:46:48 -0700284 }
Chet Haase2794eb32010-10-12 16:29:28 -0700285 }
Chet Haased51d3682010-08-11 19:46:48 -0700286 }
287
Chet Haase2794eb32010-10-12 16:29:28 -0700288 anim.setDuration(duration);
Chet Haased51d3682010-08-11 19:46:48 -0700289 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,
Chet Haasea18a86b2010-09-07 13:20:00 -0700298 ValueAnimator.RESTART));
Chet Haased51d3682010-08-11 19:46:48 -0700299 }
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}