AnimatorInflater.java revision 804618d0863a5d8ad1b08a846bd5319be864a1cb
1d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase/*
2d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * Copyright (C) 2010 The Android Open Source Project
3d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase *
4d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * Licensed under the Apache License, Version 2.0 (the "License");
5d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * you may not use this file except in compliance with the License.
6d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * You may obtain a copy of the License at
7d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase *
8d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase *      http://www.apache.org/licenses/LICENSE-2.0
9d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase *
10d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * Unless required by applicable law or agreed to in writing, software
11d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * distributed under the License is distributed on an "AS IS" BASIS,
12d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * See the License for the specific language governing permissions and
14d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * limitations under the License.
15d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase */
16d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haasepackage android.animation;
17d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
187b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbyeimport android.annotation.AnimatorRes;
19d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.content.Context;
20d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyarimport android.content.res.ConfigurationBoundResourceCache;
21d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyarimport android.content.res.ConstantState;
22d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.content.res.Resources;
23cf4832f69c8786b098ce18c24319021f8cd6733aztenghuiimport android.content.res.Resources.NotFoundException;
24e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghuiimport android.content.res.Resources.Theme;
25d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.content.res.TypedArray;
26d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.content.res.XmlResourceParser;
27cf4832f69c8786b098ce18c24319021f8cd6733aztenghuiimport android.graphics.Path;
28d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.util.AttributeSet;
29eb034fbca40006c55db143047eb628c4b657730aztenghuiimport android.util.Log;
30cf4832f69c8786b098ce18c24319021f8cd6733aztenghuiimport android.util.PathParser;
31f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyarimport android.util.StateSet;
325bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haaseimport android.util.TypedValue;
33d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.util.Xml;
34eb034fbca40006c55db143047eb628c4b657730aztenghuiimport android.view.InflateException;
35d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.view.animation.AnimationUtils;
36d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyarimport android.view.animation.BaseInterpolator;
37d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyarimport android.view.animation.Interpolator;
38e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
39e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghuiimport com.android.internal.R;
40e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
41d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport org.xmlpull.v1.XmlPullParser;
42d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport org.xmlpull.v1.XmlPullParserException;
43d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
44d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport java.io.IOException;
45d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport java.util.ArrayList;
46d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
47d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase/**
486cfdf4538049e4b96f50d8c0fe3119664420cc34Chet Haase * This class is used to instantiate animator XML files into Animator objects.
49d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * <p>
506cfdf4538049e4b96f50d8c0fe3119664420cc34Chet Haase * For performance reasons, inflation relies heavily on pre-processing of
51d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * XML files that is done at build time. Therefore, it is not currently possible
526cfdf4538049e4b96f50d8c0fe3119664420cc34Chet Haase * to use this inflater with an XmlPullParser over a plain XML file at runtime;
53d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * it only works with an XmlPullParser returned from a compiled resource (R.
54d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * <em>something</em> file.)
55d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase */
56a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haasepublic class AnimatorInflater {
57eb034fbca40006c55db143047eb628c4b657730aztenghui    private static final String TAG = "AnimatorInflater";
58d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    /**
59a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * These flags are used when parsing AnimatorSet objects
60d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     */
61d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    private static final int TOGETHER = 0;
62d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    private static final int SEQUENTIALLY = 1;
63d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
64d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    /**
65d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     * Enum values used in XML attributes to indicate the value for mValueType
66d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     */
67d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    private static final int VALUE_TYPE_FLOAT       = 0;
68d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    private static final int VALUE_TYPE_INT         = 1;
69eb034fbca40006c55db143047eb628c4b657730aztenghui    private static final int VALUE_TYPE_PATH        = 2;
70d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static final int VALUE_TYPE_COLOR       = 3;
719032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu    private static final int VALUE_TYPE_UNDEFINED   = 4;
72d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
73eb034fbca40006c55db143047eb628c4b657730aztenghui    private static final boolean DBG_ANIMATOR_INFLATER = false;
74eb034fbca40006c55db143047eb628c4b657730aztenghui
75d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    // used to calculate changing configs for resource references
76d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    private static final TypedValue sTmpTypedValue = new TypedValue();
77d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar
78d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    /**
79a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * Loads an {@link Animator} object from a resource
80d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     *
817513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu     * @param context Application context used to access resources
82d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     * @param id The resource id of the animation to load
83a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * @return The animator object reference by the specified id
84d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
85d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     */
867b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye    public static Animator loadAnimator(Context context, @AnimatorRes int id)
87d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            throws NotFoundException {
887513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu        return loadAnimator(context.getResources(), context.getTheme(), id);
89e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    }
90e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
91e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    /**
92e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * Loads an {@link Animator} object from a resource
93e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     *
947513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu     * @param resources The resources
95e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @param theme The theme
96e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @param id The resource id of the animation to load
97e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @return The animator object reference by the specified id
98e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
99e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @hide
100e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     */
1017513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu    public static Animator loadAnimator(Resources resources, Theme theme, int id)
102e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            throws NotFoundException {
1037513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu        return loadAnimator(resources, theme, id, 1);
104fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount    }
105fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount
106fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount    /** @hide */
107fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount    public static Animator loadAnimator(Resources resources, Theme theme, int id,
108fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount            float pathErrorScale) throws NotFoundException {
109d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final ConfigurationBoundResourceCache<Animator> animatorCache = resources
110d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                .getAnimatorCache();
111e54d245b993e1347cb32c23a6bdc907a45fab324Alan Viverette        Animator animator = animatorCache.getInstance(id, theme);
112d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        if (animator != null) {
113d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            if (DBG_ANIMATOR_INFLATER) {
114d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
115d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            }
116d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            return animator;
117d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        } else if (DBG_ANIMATOR_INFLATER) {
118d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            Log.d(TAG, "cache miss for animator " + resources.getResourceName(id));
119d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        }
120d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        XmlResourceParser parser = null;
121d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        try {
122e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            parser = resources.getAnimation(id);
1237513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale);
124d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            if (animator != null) {
125d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                animator.appendChangingConfigurations(getChangingConfigs(resources, id));
126d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                final ConstantState<Animator> constantState = animator.createConstantState();
127d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                if (constantState != null) {
128d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    if (DBG_ANIMATOR_INFLATER) {
129d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                        Log.d(TAG, "caching animator for res " + resources.getResourceName(id));
130d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    }
131d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    animatorCache.put(id, theme, constantState);
132d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    // create a new animator so that cached version is never used by the user
133d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    animator = constantState.newInstance(resources, theme);
134d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                }
135d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            }
136d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            return animator;
137d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        } catch (XmlPullParserException ex) {
138d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            Resources.NotFoundException rnf =
139d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
140fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount                            Integer.toHexString(id));
141d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            rnf.initCause(ex);
142d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            throw rnf;
143d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        } catch (IOException ex) {
144d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            Resources.NotFoundException rnf =
145d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
146fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount                            Integer.toHexString(id));
147d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            rnf.initCause(ex);
148d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            throw rnf;
149d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        } finally {
150d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            if (parser != null) parser.close();
151d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        }
152d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    }
153d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
154f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar    public static StateListAnimator loadStateListAnimator(Context context, int id)
155f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            throws NotFoundException {
156d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final Resources resources = context.getResources();
157d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
158d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                .getStateListAnimatorCache();
159d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final Theme theme = context.getTheme();
160e54d245b993e1347cb32c23a6bdc907a45fab324Alan Viverette        StateListAnimator animator = cache.getInstance(id, theme);
161d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        if (animator != null) {
162d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            return animator;
163d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        }
164f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        XmlResourceParser parser = null;
165f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        try {
166d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            parser = resources.getAnimation(id);
167d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser));
168d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            if (animator != null) {
169d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                animator.appendChangingConfigurations(getChangingConfigs(resources, id));
170d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                final ConstantState<StateListAnimator> constantState = animator
171d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                        .createConstantState();
172d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                if (constantState != null) {
173d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    cache.put(id, theme, constantState);
174d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    // return a clone so that the animator in constant state is never used.
175d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    animator = constantState.newInstance(resources, theme);
176d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                }
177d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            }
178d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            return animator;
179f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        } catch (XmlPullParserException ex) {
180f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            Resources.NotFoundException rnf =
181f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    new Resources.NotFoundException(
182f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            "Can't load state list animator resource ID #0x" +
183f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                    Integer.toHexString(id)
184f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    );
185f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            rnf.initCause(ex);
186f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            throw rnf;
187f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        } catch (IOException ex) {
188f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            Resources.NotFoundException rnf =
189f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    new Resources.NotFoundException(
190f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            "Can't load state list animator resource ID #0x" +
191f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                    Integer.toHexString(id)
192f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    );
193f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            rnf.initCause(ex);
194f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            throw rnf;
195f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        } finally {
196f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            if (parser != null) {
197f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                parser.close();
198f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            }
199f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        }
200f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar    }
201f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar
202f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar    private static StateListAnimator createStateListAnimatorFromXml(Context context,
203f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            XmlPullParser parser, AttributeSet attributeSet)
204f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            throws IOException, XmlPullParserException {
205f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        int type;
206f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        StateListAnimator stateListAnimator = new StateListAnimator();
207f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar
208f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        while (true) {
209f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            type = parser.next();
210f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            switch (type) {
211f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                case XmlPullParser.END_DOCUMENT:
212f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                case XmlPullParser.END_TAG:
213f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    return stateListAnimator;
214f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar
215f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                case XmlPullParser.START_TAG:
216f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    // parse item
217f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    Animator animator = null;
218f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    if ("item".equals(parser.getName())) {
219f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        int attributeCount = parser.getAttributeCount();
220f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        int[] states = new int[attributeCount];
221f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        int stateIndex = 0;
222f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        for (int i = 0; i < attributeCount; i++) {
223f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            int attrName = attributeSet.getAttributeNameResource(i);
224cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                            if (attrName == R.attr.animation) {
225d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                                final int animId = attributeSet.getAttributeResourceValue(i, 0);
226d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                                animator = loadAnimator(context, animId);
227f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            } else {
228f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                states[stateIndex++] =
229f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                        attributeSet.getAttributeBooleanValue(i, false) ?
230f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                                attrName : -attrName;
231f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            }
232f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        }
233f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        if (animator == null) {
234e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                            animator = createAnimatorFromXml(context.getResources(),
2357513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu                                    context.getTheme(), parser, 1f);
236f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        }
237f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar
238f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        if (animator == null) {
239f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            throw new Resources.NotFoundException(
240f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                    "animation state item must have a valid animation");
241f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        }
242f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        stateListAnimator
243f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                .addState(StateSet.trimStateSet(states, stateIndex), animator);
244f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    }
245f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    break;
246f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            }
247f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        }
248f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar    }
249f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar
250cf4832f69c8786b098ce18c24319021f8cd6733aztenghui    /**
251eb034fbca40006c55db143047eb628c4b657730aztenghui     * PathDataEvaluator is used to interpolate between two paths which are
252eb034fbca40006c55db143047eb628c4b657730aztenghui     * represented in the same format but different control points' values.
253804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu     * The path is represented as verbs and points for each of the verbs.
254eb034fbca40006c55db143047eb628c4b657730aztenghui     */
255804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    private static class PathDataEvaluator implements TypeEvaluator<PathParser.PathData> {
256804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        private final PathParser.PathData mPathData = new PathParser.PathData();
257eb034fbca40006c55db143047eb628c4b657730aztenghui
258eb034fbca40006c55db143047eb628c4b657730aztenghui        @Override
259804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        public PathParser.PathData evaluate(float fraction, PathParser.PathData startPathData,
260804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    PathParser.PathData endPathData) {
261804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            if (!PathParser.interpolatePathData(mPathData, startPathData, endPathData, fraction)) {
262eb034fbca40006c55db143047eb628c4b657730aztenghui                throw new IllegalArgumentException("Can't interpolate between"
263eb034fbca40006c55db143047eb628c4b657730aztenghui                        + " two incompatible pathData");
264eb034fbca40006c55db143047eb628c4b657730aztenghui            }
265804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            return mPathData;
266eb034fbca40006c55db143047eb628c4b657730aztenghui        }
267eb034fbca40006c55db143047eb628c4b657730aztenghui    }
268eb034fbca40006c55db143047eb628c4b657730aztenghui
269d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static PropertyValuesHolder getPVH(TypedArray styledAttributes, int valueType,
270d430753cba09acb07af8b313286f247c78a41a32Chet Haase            int valueFromId, int valueToId, String propertyName) {
2715bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase
272d430753cba09acb07af8b313286f247c78a41a32Chet Haase        TypedValue tvFrom = styledAttributes.peekValue(valueFromId);
2735bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        boolean hasFrom = (tvFrom != null);
2745bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        int fromType = hasFrom ? tvFrom.type : 0;
275d430753cba09acb07af8b313286f247c78a41a32Chet Haase        TypedValue tvTo = styledAttributes.peekValue(valueToId);
2765bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        boolean hasTo = (tvTo != null);
2775bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        int toType = hasTo ? tvTo.type : 0;
2785bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase
2799032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        if (valueType == VALUE_TYPE_UNDEFINED) {
2809032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            // Check whether it's color type. If not, fall back to default type (i.e. float type)
2819032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            if ((hasFrom && isColorType(fromType)) || (hasTo && isColorType(toType))) {
2829032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                valueType = VALUE_TYPE_COLOR;
2839032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            } else {
2849032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                valueType = VALUE_TYPE_FLOAT;
2859032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            }
2869032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        }
2879032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu
2889032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
2899032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu
290d430753cba09acb07af8b313286f247c78a41a32Chet Haase        PropertyValuesHolder returnValue = null;
291d430753cba09acb07af8b313286f247c78a41a32Chet Haase
292eb034fbca40006c55db143047eb628c4b657730aztenghui        if (valueType == VALUE_TYPE_PATH) {
293d430753cba09acb07af8b313286f247c78a41a32Chet Haase            String fromString = styledAttributes.getString(valueFromId);
294d430753cba09acb07af8b313286f247c78a41a32Chet Haase            String toString = styledAttributes.getString(valueToId);
295804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            PathParser.PathData nodesFrom = fromString == null
296804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    ? null : new PathParser.PathData(fromString);
297804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            PathParser.PathData nodesTo = toString == null
298804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    ? null : new PathParser.PathData(toString);
299d430753cba09acb07af8b313286f247c78a41a32Chet Haase
300d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (nodesFrom != null || nodesTo != null) {
301d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (nodesFrom != null) {
302804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    TypeEvaluator evaluator = new PathDataEvaluator();
303d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (nodesTo != null) {
304d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        if (!PathParser.canMorph(nodesFrom, nodesTo)) {
305d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            throw new InflateException(" Can't morph from " + fromString + " to " +
306d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                    toString);
307d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        }
308d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofObject(propertyName, evaluator,
309d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                nodesFrom, nodesTo);
310d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
311d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofObject(propertyName, evaluator,
312d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                (Object) nodesFrom);
313d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
314d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else if (nodesTo != null) {
315804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    TypeEvaluator evaluator = new PathDataEvaluator();
316d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    returnValue = PropertyValuesHolder.ofObject(propertyName, evaluator,
317d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            (Object) nodesTo);
318d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
319d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
320eb034fbca40006c55db143047eb628c4b657730aztenghui        } else {
321d430753cba09acb07af8b313286f247c78a41a32Chet Haase            TypeEvaluator evaluator = null;
322eb034fbca40006c55db143047eb628c4b657730aztenghui            // Integer and float value types are handled here.
3239032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            if (valueType == VALUE_TYPE_COLOR) {
324eb034fbca40006c55db143047eb628c4b657730aztenghui                // special case for colors: ignore valueType and get ints
325eb034fbca40006c55db143047eb628c4b657730aztenghui                evaluator = ArgbEvaluator.getInstance();
326eb034fbca40006c55db143047eb628c4b657730aztenghui            }
327d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (getFloats) {
328d430753cba09acb07af8b313286f247c78a41a32Chet Haase                float valueFrom;
329d430753cba09acb07af8b313286f247c78a41a32Chet Haase                float valueTo;
330d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (hasFrom) {
331d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (fromType == TypedValue.TYPE_DIMENSION) {
332d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueFrom = styledAttributes.getDimension(valueFromId, 0f);
333d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
334d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueFrom = styledAttributes.getFloat(valueFromId, 0f);
335d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
336d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (hasTo) {
337d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        if (toType == TypedValue.TYPE_DIMENSION) {
338d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getDimension(valueToId, 0f);
339d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        } else {
340d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getFloat(valueToId, 0f);
341d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        }
342d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofFloat(propertyName,
343d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                valueFrom, valueTo);
344d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
345d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofFloat(propertyName, valueFrom);
346d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
347d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else {
348d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (toType == TypedValue.TYPE_DIMENSION) {
349d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueTo = styledAttributes.getDimension(valueToId, 0f);
350d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
351d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueTo = styledAttributes.getFloat(valueToId, 0f);
352d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
353d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    returnValue = PropertyValuesHolder.ofFloat(propertyName, valueTo);
354d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
355d430753cba09acb07af8b313286f247c78a41a32Chet Haase            } else {
356d430753cba09acb07af8b313286f247c78a41a32Chet Haase                int valueFrom;
357d430753cba09acb07af8b313286f247c78a41a32Chet Haase                int valueTo;
358d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (hasFrom) {
359d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (fromType == TypedValue.TYPE_DIMENSION) {
360d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueFrom = (int) styledAttributes.getDimension(valueFromId, 0f);
3619032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                    } else if (isColorType(fromType)) {
362d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueFrom = styledAttributes.getColor(valueFromId, 0);
363d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
364d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueFrom = styledAttributes.getInt(valueFromId, 0);
365d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
366d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (hasTo) {
367d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        if (toType == TypedValue.TYPE_DIMENSION) {
368d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = (int) styledAttributes.getDimension(valueToId, 0f);
3699032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                        } else if (isColorType(toType)) {
370d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getColor(valueToId, 0);
371d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        } else {
372d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getInt(valueToId, 0);
373d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        }
374d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofInt(propertyName, valueFrom, valueTo);
375d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
376d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofInt(propertyName, valueFrom);
377d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
378d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else {
379d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (hasTo) {
380d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        if (toType == TypedValue.TYPE_DIMENSION) {
381d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = (int) styledAttributes.getDimension(valueToId, 0f);
3829032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                        } else if (isColorType(toType)) {
383d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getColor(valueToId, 0);
384d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        } else {
385d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getInt(valueToId, 0);
386d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        }
387d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofInt(propertyName, valueTo);
388d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
389d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
390d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
391d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (returnValue != null && evaluator != null) {
392d430753cba09acb07af8b313286f247c78a41a32Chet Haase                returnValue.setEvaluator(evaluator);
393d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
394d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
395d430753cba09acb07af8b313286f247c78a41a32Chet Haase
396d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return returnValue;
397d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
398d430753cba09acb07af8b313286f247c78a41a32Chet Haase
399d430753cba09acb07af8b313286f247c78a41a32Chet Haase    /**
400d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param anim The animator, must not be null
401d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param arrayAnimator Incoming typed array for Animator's attributes.
402d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param arrayObjectAnimator Incoming typed array for Object Animator's
403d430753cba09acb07af8b313286f247c78a41a32Chet Haase     *            attributes.
404d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param pixelSize The relative pixel size, used to calculate the
405d430753cba09acb07af8b313286f247c78a41a32Chet Haase     *                  maximum error for path animations.
406d430753cba09acb07af8b313286f247c78a41a32Chet Haase     */
407d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static void parseAnimatorFromTypeArray(ValueAnimator anim,
408d430753cba09acb07af8b313286f247c78a41a32Chet Haase            TypedArray arrayAnimator, TypedArray arrayObjectAnimator, float pixelSize) {
409d430753cba09acb07af8b313286f247c78a41a32Chet Haase        long duration = arrayAnimator.getInt(R.styleable.Animator_duration, 300);
410d430753cba09acb07af8b313286f247c78a41a32Chet Haase
411d430753cba09acb07af8b313286f247c78a41a32Chet Haase        long startDelay = arrayAnimator.getInt(R.styleable.Animator_startOffset, 0);
412d430753cba09acb07af8b313286f247c78a41a32Chet Haase
4132787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        int valueType = arrayAnimator.getInt(R.styleable.Animator_valueType, VALUE_TYPE_UNDEFINED);
414d430753cba09acb07af8b313286f247c78a41a32Chet Haase
4152787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        if (valueType == VALUE_TYPE_UNDEFINED) {
4162787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu            valueType = inferValueTypeFromValues(arrayAnimator, R.styleable.Animator_valueFrom,
4172787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu                    R.styleable.Animator_valueTo);
4182787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        }
419d430753cba09acb07af8b313286f247c78a41a32Chet Haase        PropertyValuesHolder pvh = getPVH(arrayAnimator, valueType,
420d430753cba09acb07af8b313286f247c78a41a32Chet Haase                R.styleable.Animator_valueFrom, R.styleable.Animator_valueTo, "");
421d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (pvh != null) {
422d430753cba09acb07af8b313286f247c78a41a32Chet Haase            anim.setValues(pvh);
4235bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        }
4245bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase
425eb034fbca40006c55db143047eb628c4b657730aztenghui        anim.setDuration(duration);
426eb034fbca40006c55db143047eb628c4b657730aztenghui        anim.setStartDelay(startDelay);
427eb034fbca40006c55db143047eb628c4b657730aztenghui
428eb034fbca40006c55db143047eb628c4b657730aztenghui        if (arrayAnimator.hasValue(R.styleable.Animator_repeatCount)) {
429eb034fbca40006c55db143047eb628c4b657730aztenghui            anim.setRepeatCount(
430eb034fbca40006c55db143047eb628c4b657730aztenghui                    arrayAnimator.getInt(R.styleable.Animator_repeatCount, 0));
431eb034fbca40006c55db143047eb628c4b657730aztenghui        }
432eb034fbca40006c55db143047eb628c4b657730aztenghui        if (arrayAnimator.hasValue(R.styleable.Animator_repeatMode)) {
433eb034fbca40006c55db143047eb628c4b657730aztenghui            anim.setRepeatMode(
434eb034fbca40006c55db143047eb628c4b657730aztenghui                    arrayAnimator.getInt(R.styleable.Animator_repeatMode,
435eb034fbca40006c55db143047eb628c4b657730aztenghui                            ValueAnimator.RESTART));
436eb034fbca40006c55db143047eb628c4b657730aztenghui        }
437eb034fbca40006c55db143047eb628c4b657730aztenghui
438eb034fbca40006c55db143047eb628c4b657730aztenghui        if (arrayObjectAnimator != null) {
439d430753cba09acb07af8b313286f247c78a41a32Chet Haase            setupObjectAnimator(anim, arrayObjectAnimator, valueType == VALUE_TYPE_FLOAT,
440d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    pixelSize);
441eb034fbca40006c55db143047eb628c4b657730aztenghui        }
442eb034fbca40006c55db143047eb628c4b657730aztenghui    }
443eb034fbca40006c55db143047eb628c4b657730aztenghui
444eb034fbca40006c55db143047eb628c4b657730aztenghui    /**
445eb034fbca40006c55db143047eb628c4b657730aztenghui     * Setup the Animator to achieve path morphing.
446eb034fbca40006c55db143047eb628c4b657730aztenghui     *
447eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param anim The target Animator which will be updated.
448eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param arrayAnimator TypedArray for the ValueAnimator.
449eb034fbca40006c55db143047eb628c4b657730aztenghui     * @return the PathDataEvaluator.
450eb034fbca40006c55db143047eb628c4b657730aztenghui     */
451eb034fbca40006c55db143047eb628c4b657730aztenghui    private static TypeEvaluator setupAnimatorForPath(ValueAnimator anim,
452eb034fbca40006c55db143047eb628c4b657730aztenghui             TypedArray arrayAnimator) {
453eb034fbca40006c55db143047eb628c4b657730aztenghui        TypeEvaluator evaluator = null;
454eb034fbca40006c55db143047eb628c4b657730aztenghui        String fromString = arrayAnimator.getString(R.styleable.Animator_valueFrom);
455eb034fbca40006c55db143047eb628c4b657730aztenghui        String toString = arrayAnimator.getString(R.styleable.Animator_valueTo);
456804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        PathParser.PathData pathDataFrom = fromString == null
457804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                ? null : new PathParser.PathData(fromString);
458804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        PathParser.PathData pathDataTo = toString == null
459804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                ? null : new PathParser.PathData(toString);
460804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
461804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        if (pathDataFrom != null) {
462804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            if (pathDataTo != null) {
463804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                anim.setObjectValues(pathDataFrom, pathDataTo);
464804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                if (!PathParser.canMorph(pathDataFrom, pathDataTo)) {
465eb034fbca40006c55db143047eb628c4b657730aztenghui                    throw new InflateException(arrayAnimator.getPositionDescription()
466eb034fbca40006c55db143047eb628c4b657730aztenghui                            + " Can't morph from " + fromString + " to " + toString);
467eb034fbca40006c55db143047eb628c4b657730aztenghui                }
468eb034fbca40006c55db143047eb628c4b657730aztenghui            } else {
469804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                anim.setObjectValues((Object)pathDataFrom);
470eb034fbca40006c55db143047eb628c4b657730aztenghui            }
471804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            evaluator = new PathDataEvaluator();
472804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        } else if (pathDataTo != null) {
473804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            anim.setObjectValues((Object)pathDataTo);
474804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            evaluator = new PathDataEvaluator();
475eb034fbca40006c55db143047eb628c4b657730aztenghui        }
476eb034fbca40006c55db143047eb628c4b657730aztenghui
477eb034fbca40006c55db143047eb628c4b657730aztenghui        if (DBG_ANIMATOR_INFLATER && evaluator != null) {
478eb034fbca40006c55db143047eb628c4b657730aztenghui            Log.v(TAG, "create a new PathDataEvaluator here");
479eb034fbca40006c55db143047eb628c4b657730aztenghui        }
480eb034fbca40006c55db143047eb628c4b657730aztenghui
481eb034fbca40006c55db143047eb628c4b657730aztenghui        return evaluator;
482eb034fbca40006c55db143047eb628c4b657730aztenghui    }
483eb034fbca40006c55db143047eb628c4b657730aztenghui
484eb034fbca40006c55db143047eb628c4b657730aztenghui    /**
485eb034fbca40006c55db143047eb628c4b657730aztenghui     * Setup ObjectAnimator's property or values from pathData.
486eb034fbca40006c55db143047eb628c4b657730aztenghui     *
487eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param anim The target Animator which will be updated.
488eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param arrayObjectAnimator TypedArray for the ObjectAnimator.
489eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param getFloats True if the value type is float.
490fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount     * @param pixelSize The relative pixel size, used to calculate the
491fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount     *                  maximum error for path animations.
492eb034fbca40006c55db143047eb628c4b657730aztenghui     */
493eb034fbca40006c55db143047eb628c4b657730aztenghui    private static void setupObjectAnimator(ValueAnimator anim, TypedArray arrayObjectAnimator,
494fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount            boolean getFloats, float pixelSize) {
495eb034fbca40006c55db143047eb628c4b657730aztenghui        ObjectAnimator oa = (ObjectAnimator) anim;
496eb034fbca40006c55db143047eb628c4b657730aztenghui        String pathData = arrayObjectAnimator.getString(R.styleable.PropertyAnimator_pathData);
497eb034fbca40006c55db143047eb628c4b657730aztenghui
4982787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // Path can be involved in an ObjectAnimator in the following 3 ways:
4992787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // 1) Path morphing: the property to be animated is pathData, and valueFrom and valueTo
5002787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        //    are both of pathType. valueType = pathType needs to be explicitly defined.
5012787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // 2) A property in X or Y dimension can be animated along a path: the property needs to be
5022787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        //    defined in propertyXName or propertyYName attribute, the path will be defined in the
5032787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        //    pathData attribute. valueFrom and valueTo will not be necessary for this animation.
5042787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // 3) PathInterpolator can also define a path (in pathData) for its interpolation curve.
5052787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // Here we are dealing with case 2:
506eb034fbca40006c55db143047eb628c4b657730aztenghui        if (pathData != null) {
507eb034fbca40006c55db143047eb628c4b657730aztenghui            String propertyXName =
508eb034fbca40006c55db143047eb628c4b657730aztenghui                    arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyXName);
509eb034fbca40006c55db143047eb628c4b657730aztenghui            String propertyYName =
510eb034fbca40006c55db143047eb628c4b657730aztenghui                    arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyYName);
511eb034fbca40006c55db143047eb628c4b657730aztenghui
512eb034fbca40006c55db143047eb628c4b657730aztenghui            if (propertyXName == null && propertyYName == null) {
513eb034fbca40006c55db143047eb628c4b657730aztenghui                throw new InflateException(arrayObjectAnimator.getPositionDescription()
514eb034fbca40006c55db143047eb628c4b657730aztenghui                        + " propertyXName or propertyYName is needed for PathData");
515eb034fbca40006c55db143047eb628c4b657730aztenghui            } else {
516eb034fbca40006c55db143047eb628c4b657730aztenghui                Path path = PathParser.createPathFromPathData(pathData);
517fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount                float error = 0.5f * pixelSize; // max half a pixel error
518fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount                PathKeyframes keyframeSet = KeyframeSet.ofPath(path, error);
519984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                Keyframes xKeyframes;
520984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                Keyframes yKeyframes;
521984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                if (getFloats) {
522984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    xKeyframes = keyframeSet.createXFloatKeyframes();
523984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    yKeyframes = keyframeSet.createYFloatKeyframes();
524984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                } else {
525984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    xKeyframes = keyframeSet.createXIntKeyframes();
526984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    yKeyframes = keyframeSet.createYIntKeyframes();
527984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                }
528eb034fbca40006c55db143047eb628c4b657730aztenghui                PropertyValuesHolder x = null;
529eb034fbca40006c55db143047eb628c4b657730aztenghui                PropertyValuesHolder y = null;
530eb034fbca40006c55db143047eb628c4b657730aztenghui                if (propertyXName != null) {
531984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    x = PropertyValuesHolder.ofKeyframes(propertyXName, xKeyframes);
532eb034fbca40006c55db143047eb628c4b657730aztenghui                }
533eb034fbca40006c55db143047eb628c4b657730aztenghui                if (propertyYName != null) {
534984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    y = PropertyValuesHolder.ofKeyframes(propertyYName, yKeyframes);
535eb034fbca40006c55db143047eb628c4b657730aztenghui                }
536eb034fbca40006c55db143047eb628c4b657730aztenghui                if (x == null) {
537eb034fbca40006c55db143047eb628c4b657730aztenghui                    oa.setValues(y);
538eb034fbca40006c55db143047eb628c4b657730aztenghui                } else if (y == null) {
539eb034fbca40006c55db143047eb628c4b657730aztenghui                    oa.setValues(x);
540eb034fbca40006c55db143047eb628c4b657730aztenghui                } else {
541eb034fbca40006c55db143047eb628c4b657730aztenghui                    oa.setValues(x, y);
542eb034fbca40006c55db143047eb628c4b657730aztenghui                }
543eb034fbca40006c55db143047eb628c4b657730aztenghui            }
544eb034fbca40006c55db143047eb628c4b657730aztenghui        } else {
545eb034fbca40006c55db143047eb628c4b657730aztenghui            String propertyName =
546eb034fbca40006c55db143047eb628c4b657730aztenghui                    arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyName);
547eb034fbca40006c55db143047eb628c4b657730aztenghui            oa.setPropertyName(propertyName);
548eb034fbca40006c55db143047eb628c4b657730aztenghui        }
549eb034fbca40006c55db143047eb628c4b657730aztenghui    }
550eb034fbca40006c55db143047eb628c4b657730aztenghui
551eb034fbca40006c55db143047eb628c4b657730aztenghui    /**
552eb034fbca40006c55db143047eb628c4b657730aztenghui     * Setup ValueAnimator's values.
553eb034fbca40006c55db143047eb628c4b657730aztenghui     * This will handle all of the integer, float and color types.
554eb034fbca40006c55db143047eb628c4b657730aztenghui     *
555eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param anim The target Animator which will be updated.
556eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param arrayAnimator TypedArray for the ValueAnimator.
557eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param getFloats True if the value type is float.
558eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param hasFrom True if "valueFrom" exists.
559eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param fromType The type of "valueFrom".
560eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param hasTo True if "valueTo" exists.
561eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param toType The type of "valueTo".
562eb034fbca40006c55db143047eb628c4b657730aztenghui     */
563eb034fbca40006c55db143047eb628c4b657730aztenghui    private static void setupValues(ValueAnimator anim, TypedArray arrayAnimator,
564eb034fbca40006c55db143047eb628c4b657730aztenghui            boolean getFloats, boolean hasFrom, int fromType, boolean hasTo, int toType) {
565eb034fbca40006c55db143047eb628c4b657730aztenghui        int valueFromIndex = R.styleable.Animator_valueFrom;
566eb034fbca40006c55db143047eb628c4b657730aztenghui        int valueToIndex = R.styleable.Animator_valueTo;
5675bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        if (getFloats) {
5685bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            float valueFrom;
5695bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            float valueTo;
5705bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            if (hasFrom) {
5715bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (fromType == TypedValue.TYPE_DIMENSION) {
572cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueFrom = arrayAnimator.getDimension(valueFromIndex, 0f);
5735bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                } else {
574cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueFrom = arrayAnimator.getFloat(valueFromIndex, 0f);
5755bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                }
5765bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (hasTo) {
5775bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    if (toType == TypedValue.TYPE_DIMENSION) {
578cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getDimension(valueToIndex, 0f);
5792794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    } else {
580cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getFloat(valueToIndex, 0f);
5812794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    }
5825bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    anim.setFloatValues(valueFrom, valueTo);
5835bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                } else {
5845bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    anim.setFloatValues(valueFrom);
5855bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                }
5865bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            } else {
5875bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (toType == TypedValue.TYPE_DIMENSION) {
588cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueTo = arrayAnimator.getDimension(valueToIndex, 0f);
5892794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                } else {
590cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueTo = arrayAnimator.getFloat(valueToIndex, 0f);
591d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase                }
5925bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                anim.setFloatValues(valueTo);
5932794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase            }
5945bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        } else {
5955bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            int valueFrom;
5965bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            int valueTo;
5975bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            if (hasFrom) {
5985bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (fromType == TypedValue.TYPE_DIMENSION) {
599cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueFrom = (int) arrayAnimator.getDimension(valueFromIndex, 0f);
6009032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                } else if (isColorType(fromType)) {
601cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueFrom = arrayAnimator.getColor(valueFromIndex, 0);
6025bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                } else {
603cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueFrom = arrayAnimator.getInt(valueFromIndex, 0);
6045bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                }
6055bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (hasTo) {
6065bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    if (toType == TypedValue.TYPE_DIMENSION) {
607cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = (int) arrayAnimator.getDimension(valueToIndex, 0f);
6089032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                    } else if (isColorType(toType)) {
609cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getColor(valueToIndex, 0);
6102794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    } else {
611cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getInt(valueToIndex, 0);
6122794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    }
6135bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    anim.setIntValues(valueFrom, valueTo);
6142794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                } else {
6155bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    anim.setIntValues(valueFrom);
616d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase                }
6175bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            } else {
6185bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (hasTo) {
6195bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    if (toType == TypedValue.TYPE_DIMENSION) {
620cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = (int) arrayAnimator.getDimension(valueToIndex, 0f);
6219032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                    } else if (isColorType(toType)) {
622cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getColor(valueToIndex, 0);
6232794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    } else {
624cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getInt(valueToIndex, 0);
6252794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    }
6265bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    anim.setIntValues(valueTo);
627d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase                }
6282794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase            }
629d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        }
630e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    }
631e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
632fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount    private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser,
6337513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            float pixelSize)
634e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            throws XmlPullParserException, IOException {
635fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount        return createAnimatorFromXml(res, theme, parser, Xml.asAttributeSet(parser), null, 0,
6367513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu                pixelSize);
637e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    }
638e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
639e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser,
6407513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            AttributeSet attrs, AnimatorSet parent, int sequenceOrdering, float pixelSize)
6417513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            throws XmlPullParserException, IOException {
642e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        Animator anim = null;
643e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        ArrayList<Animator> childAnims = null;
644e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
645e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        // Make sure we are on a start tag.
646e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        int type;
647e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        int depth = parser.getDepth();
648e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
649e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
650e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                && type != XmlPullParser.END_DOCUMENT) {
651e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
652e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            if (type != XmlPullParser.START_TAG) {
653e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                continue;
654e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            }
655e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
656e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            String name = parser.getName();
657d430753cba09acb07af8b313286f247c78a41a32Chet Haase            boolean gotValues = false;
658e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
659e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            if (name.equals("objectAnimator")) {
6607513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu                anim = loadObjectAnimator(res, theme, attrs, pixelSize);
661e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            } else if (name.equals("animator")) {
6627513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu                anim = loadAnimator(res, theme, attrs, null, pixelSize);
663e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            } else if (name.equals("set")) {
664e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                anim = new AnimatorSet();
665e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                TypedArray a;
666e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                if (theme != null) {
667cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    a = theme.obtainStyledAttributes(attrs, R.styleable.AnimatorSet, 0, 0);
668e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                } else {
669cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    a = res.obtainAttributes(attrs, R.styleable.AnimatorSet);
670e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                }
671d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                anim.appendChangingConfigurations(a.getChangingConfigurations());
672d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER);
673fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount                createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering,
6747513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu                        pixelSize);
675e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                a.recycle();
676d430753cba09acb07af8b313286f247c78a41a32Chet Haase            } else if (name.equals("propertyValuesHolder")) {
677d430753cba09acb07af8b313286f247c78a41a32Chet Haase                PropertyValuesHolder[] values = loadValues(res, theme, parser,
678d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        Xml.asAttributeSet(parser));
679d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (values != null && anim != null && (anim instanceof ValueAnimator)) {
680d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    ((ValueAnimator) anim).setValues(values);
681d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
682d430753cba09acb07af8b313286f247c78a41a32Chet Haase                gotValues = true;
683e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            } else {
684e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                throw new RuntimeException("Unknown animator name: " + parser.getName());
685e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            }
686e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
687d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (parent != null && !gotValues) {
688e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                if (childAnims == null) {
689e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                    childAnims = new ArrayList<Animator>();
690e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                }
691e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                childAnims.add(anim);
692e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            }
693e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        }
694e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        if (parent != null && childAnims != null) {
695e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            Animator[] animsArray = new Animator[childAnims.size()];
696e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            int index = 0;
697e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            for (Animator a : childAnims) {
698e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                animsArray[index++] = a;
699e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            }
700e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            if (sequenceOrdering == TOGETHER) {
701e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                parent.playTogether(animsArray);
702e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            } else {
703e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                parent.playSequentially(animsArray);
704e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            }
705e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        }
706e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        return anim;
707d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
708d430753cba09acb07af8b313286f247c78a41a32Chet Haase
709d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static PropertyValuesHolder[] loadValues(Resources res, Theme theme,
710d430753cba09acb07af8b313286f247c78a41a32Chet Haase            XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException {
711d430753cba09acb07af8b313286f247c78a41a32Chet Haase        ArrayList<PropertyValuesHolder> values = null;
712d430753cba09acb07af8b313286f247c78a41a32Chet Haase
713d430753cba09acb07af8b313286f247c78a41a32Chet Haase        int type;
714d430753cba09acb07af8b313286f247c78a41a32Chet Haase        while ((type = parser.getEventType()) != XmlPullParser.END_TAG &&
715d430753cba09acb07af8b313286f247c78a41a32Chet Haase                type != XmlPullParser.END_DOCUMENT) {
716d430753cba09acb07af8b313286f247c78a41a32Chet Haase
717d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (type != XmlPullParser.START_TAG) {
718d430753cba09acb07af8b313286f247c78a41a32Chet Haase                parser.next();
719d430753cba09acb07af8b313286f247c78a41a32Chet Haase                continue;
720d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
721d430753cba09acb07af8b313286f247c78a41a32Chet Haase
722d430753cba09acb07af8b313286f247c78a41a32Chet Haase            String name = parser.getName();
723d430753cba09acb07af8b313286f247c78a41a32Chet Haase
724d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (name.equals("propertyValuesHolder")) {
725d430753cba09acb07af8b313286f247c78a41a32Chet Haase                TypedArray a;
726d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (theme != null) {
727d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    a = theme.obtainStyledAttributes(attrs, R.styleable.PropertyValuesHolder, 0, 0);
728d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else {
729d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    a = res.obtainAttributes(attrs, R.styleable.PropertyValuesHolder);
730d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
731d430753cba09acb07af8b313286f247c78a41a32Chet Haase                String propertyName = a.getString(R.styleable.PropertyValuesHolder_propertyName);
732d430753cba09acb07af8b313286f247c78a41a32Chet Haase                int valueType = a.getInt(R.styleable.PropertyValuesHolder_valueType,
7339032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                        VALUE_TYPE_UNDEFINED);
7349032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu
735d430753cba09acb07af8b313286f247c78a41a32Chet Haase                PropertyValuesHolder pvh = loadPvh(res, theme, parser, propertyName, valueType);
736d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (pvh == null) {
737d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    pvh = getPVH(a, valueType,
738d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            R.styleable.PropertyValuesHolder_valueFrom,
739d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            R.styleable.PropertyValuesHolder_valueTo, propertyName);
740d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
741d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (pvh != null) {
742d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (values == null) {
743d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        values = new ArrayList<PropertyValuesHolder>();
744d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
745d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    values.add(pvh);
746d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
747d430753cba09acb07af8b313286f247c78a41a32Chet Haase                a.recycle();
748d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
749d430753cba09acb07af8b313286f247c78a41a32Chet Haase
750d430753cba09acb07af8b313286f247c78a41a32Chet Haase            parser.next();
751d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
752d430753cba09acb07af8b313286f247c78a41a32Chet Haase
753d430753cba09acb07af8b313286f247c78a41a32Chet Haase        PropertyValuesHolder[] valuesArray = null;
754d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (values != null) {
755d430753cba09acb07af8b313286f247c78a41a32Chet Haase            int count = values.size();
756d430753cba09acb07af8b313286f247c78a41a32Chet Haase            valuesArray = new PropertyValuesHolder[count];
757d430753cba09acb07af8b313286f247c78a41a32Chet Haase            for (int i = 0; i < count; ++i) {
758d430753cba09acb07af8b313286f247c78a41a32Chet Haase                valuesArray[i] = values.get(i);
759d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
760d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
761d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return valuesArray;
762d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
763d430753cba09acb07af8b313286f247c78a41a32Chet Haase
764a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu    // When no value type is provided in keyframe, we need to infer the type from the value. i.e.
765a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu    // if value is defined in the style of a color value, then the color type is returned.
766a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu    // Otherwise, default float type is returned.
767a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu    private static int inferValueTypeOfKeyframe(Resources res, Theme theme, AttributeSet attrs) {
768a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        int valueType;
769a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        TypedArray a;
770a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        if (theme != null) {
771a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu            a = theme.obtainStyledAttributes(attrs, R.styleable.Keyframe, 0, 0);
772a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        } else {
773a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu            a = res.obtainAttributes(attrs, R.styleable.Keyframe);
774a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        }
775a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu
776a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        TypedValue keyframeValue = a.peekValue(R.styleable.Keyframe_value);
777a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        boolean hasValue = (keyframeValue != null);
778a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        // When no value type is provided, check whether it's a color type first.
779a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        // If not, fall back to default value type (i.e. float type).
780a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        if (hasValue && isColorType(keyframeValue.type)) {
781a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu            valueType = VALUE_TYPE_COLOR;
782a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        } else {
783a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu            valueType = VALUE_TYPE_FLOAT;
784a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        }
785a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        a.recycle();
786a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        return valueType;
787a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu    }
788a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu
7892787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu    private static int inferValueTypeFromValues(TypedArray styledAttributes, int valueFromId,
7902787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu            int valueToId) {
7912787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        TypedValue tvFrom = styledAttributes.peekValue(valueFromId);
7922787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        boolean hasFrom = (tvFrom != null);
7932787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        int fromType = hasFrom ? tvFrom.type : 0;
7942787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        TypedValue tvTo = styledAttributes.peekValue(valueToId);
7952787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        boolean hasTo = (tvTo != null);
7962787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        int toType = hasTo ? tvTo.type : 0;
7972787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu
7982787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        int valueType;
7992787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // Check whether it's color type. If not, fall back to default type (i.e. float type)
8002787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        if ((hasFrom && isColorType(fromType)) || (hasTo && isColorType(toType))) {
8012787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu            valueType = VALUE_TYPE_COLOR;
8022787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        } else {
8032787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu            valueType = VALUE_TYPE_FLOAT;
8042787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        }
8052787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        return valueType;
8062787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu    }
8072787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu
808d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static void dumpKeyframes(Object[] keyframes, String header) {
809d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (keyframes == null || keyframes.length == 0) {
810d430753cba09acb07af8b313286f247c78a41a32Chet Haase            return;
811d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
812d430753cba09acb07af8b313286f247c78a41a32Chet Haase        Log.d(TAG, header);
813d430753cba09acb07af8b313286f247c78a41a32Chet Haase        int count = keyframes.length;
814d430753cba09acb07af8b313286f247c78a41a32Chet Haase        for (int i = 0; i < count; ++i) {
815d430753cba09acb07af8b313286f247c78a41a32Chet Haase            Keyframe keyframe = (Keyframe) keyframes[i];
816d430753cba09acb07af8b313286f247c78a41a32Chet Haase            Log.d(TAG, "Keyframe " + i + ": fraction " +
817d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    (keyframe.getFraction() < 0 ? "null" : keyframe.getFraction()) + ", " +
818d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    ", value : " + ((keyframe.hasValue()) ? keyframe.getValue() : "null"));
819d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
820d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
821d430753cba09acb07af8b313286f247c78a41a32Chet Haase
8229032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu    // Load property values holder if there are keyframes defined in it. Otherwise return null.
823d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static PropertyValuesHolder loadPvh(Resources res, Theme theme, XmlPullParser parser,
824d430753cba09acb07af8b313286f247c78a41a32Chet Haase            String propertyName, int valueType)
825d430753cba09acb07af8b313286f247c78a41a32Chet Haase            throws XmlPullParserException, IOException {
826d430753cba09acb07af8b313286f247c78a41a32Chet Haase
827d430753cba09acb07af8b313286f247c78a41a32Chet Haase        PropertyValuesHolder value = null;
828d430753cba09acb07af8b313286f247c78a41a32Chet Haase        ArrayList<Keyframe> keyframes = null;
829d430753cba09acb07af8b313286f247c78a41a32Chet Haase
830d430753cba09acb07af8b313286f247c78a41a32Chet Haase        int type;
831d430753cba09acb07af8b313286f247c78a41a32Chet Haase        while ((type = parser.next()) != XmlPullParser.END_TAG &&
832d430753cba09acb07af8b313286f247c78a41a32Chet Haase                type != XmlPullParser.END_DOCUMENT) {
833d430753cba09acb07af8b313286f247c78a41a32Chet Haase            String name = parser.getName();
834d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (name.equals("keyframe")) {
835a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu                if (valueType == VALUE_TYPE_UNDEFINED) {
836a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu                    valueType = inferValueTypeOfKeyframe(res, theme, Xml.asAttributeSet(parser));
837a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu                }
838d430753cba09acb07af8b313286f247c78a41a32Chet Haase                Keyframe keyframe = loadKeyframe(res, theme, Xml.asAttributeSet(parser), valueType);
839d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (keyframe != null) {
840d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (keyframes == null) {
841d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        keyframes = new ArrayList<Keyframe>();
842d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
843d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    keyframes.add(keyframe);
844d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
845d430753cba09acb07af8b313286f247c78a41a32Chet Haase                parser.next();
846d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
847d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
848d430753cba09acb07af8b313286f247c78a41a32Chet Haase
849d430753cba09acb07af8b313286f247c78a41a32Chet Haase        int count;
850d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (keyframes != null && (count = keyframes.size()) > 0) {
851d430753cba09acb07af8b313286f247c78a41a32Chet Haase            // make sure we have keyframes at 0 and 1
852d430753cba09acb07af8b313286f247c78a41a32Chet Haase            // If we have keyframes with set fractions, add keyframes at start/end
853d430753cba09acb07af8b313286f247c78a41a32Chet Haase            // appropriately. If start/end have no set fractions:
854d430753cba09acb07af8b313286f247c78a41a32Chet Haase            // if there's only one keyframe, set its fraction to 1 and add one at 0
855d430753cba09acb07af8b313286f247c78a41a32Chet Haase            // if >1 keyframe, set the last fraction to 1, the first fraction to 0
856d430753cba09acb07af8b313286f247c78a41a32Chet Haase            Keyframe firstKeyframe = keyframes.get(0);
857d430753cba09acb07af8b313286f247c78a41a32Chet Haase            Keyframe lastKeyframe = keyframes.get(count - 1);
858d430753cba09acb07af8b313286f247c78a41a32Chet Haase            float endFraction = lastKeyframe.getFraction();
859d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (endFraction < 1) {
860d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (endFraction < 0) {
861d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    lastKeyframe.setFraction(1);
862d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else {
863d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    keyframes.add(keyframes.size(), createNewKeyframe(lastKeyframe, 1));
864d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    ++count;
865d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
866d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
867d430753cba09acb07af8b313286f247c78a41a32Chet Haase            float startFraction = firstKeyframe.getFraction();
868d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (startFraction != 0) {
869d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (startFraction < 0) {
870d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    firstKeyframe.setFraction(0);
871d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else {
872d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    keyframes.add(0, createNewKeyframe(firstKeyframe, 0));
873d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    ++count;
874d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
875d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
876d430753cba09acb07af8b313286f247c78a41a32Chet Haase            Keyframe[] keyframeArray = new Keyframe[count];
877d430753cba09acb07af8b313286f247c78a41a32Chet Haase            keyframes.toArray(keyframeArray);
878d430753cba09acb07af8b313286f247c78a41a32Chet Haase            for (int i = 0; i < count; ++i) {
879d430753cba09acb07af8b313286f247c78a41a32Chet Haase                Keyframe keyframe = keyframeArray[i];
880d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (keyframe.getFraction() < 0) {
881d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (i == 0) {
882d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        keyframe.setFraction(0);
883d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else if (i == count - 1) {
884d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        keyframe.setFraction(1);
885d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
886d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        // figure out the start/end parameters of the current gap
887d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        // in fractions and distribute the gap among those keyframes
888d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        int startIndex = i;
889d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        int endIndex = i;
890d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        for (int j = startIndex + 1; j < count - 1; ++j) {
891d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            if (keyframeArray[j].getFraction() >= 0) {
892d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                break;
893d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            }
894d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            endIndex = j;
895d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        }
896d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        float gap = keyframeArray[endIndex + 1].getFraction() -
897d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                keyframeArray[startIndex - 1].getFraction();
898d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        distributeKeyframes(keyframeArray, gap, startIndex, endIndex);
899d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
900d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
901d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
902d430753cba09acb07af8b313286f247c78a41a32Chet Haase            value = PropertyValuesHolder.ofKeyframe(propertyName, keyframeArray);
903d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (valueType == VALUE_TYPE_COLOR) {
904d430753cba09acb07af8b313286f247c78a41a32Chet Haase                value.setEvaluator(ArgbEvaluator.getInstance());
905d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
906d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
907d430753cba09acb07af8b313286f247c78a41a32Chet Haase
908d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return value;
909d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
910d430753cba09acb07af8b313286f247c78a41a32Chet Haase
911d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static Keyframe createNewKeyframe(Keyframe sampleKeyframe, float fraction) {
912d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return sampleKeyframe.getType() == float.class ?
913d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            Keyframe.ofFloat(fraction) :
914d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            (sampleKeyframe.getType() == int.class) ?
915d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                    Keyframe.ofInt(fraction) :
916d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                    Keyframe.ofObject(fraction);
917d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
918d430753cba09acb07af8b313286f247c78a41a32Chet Haase
919d430753cba09acb07af8b313286f247c78a41a32Chet Haase    /**
920d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * Utility function to set fractions on keyframes to cover a gap in which the
921d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * fractions are not currently set. Keyframe fractions will be distributed evenly
922d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * in this gap. For example, a gap of 1 keyframe in the range 0-1 will be at .5, a gap
923d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * of .6 spread between two keyframes will be at .2 and .4 beyond the fraction at the
924d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * keyframe before startIndex.
925d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * Assumptions:
926d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * - First and last keyframe fractions (bounding this spread) are already set. So,
927d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * for example, if no fractions are set, we will already set first and last keyframe
928d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * fraction values to 0 and 1.
929d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * - startIndex must be >0 (which follows from first assumption).
930d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * - endIndex must be >= startIndex.
931d430753cba09acb07af8b313286f247c78a41a32Chet Haase     *
932d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param keyframes the array of keyframes
933d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param gap The total gap we need to distribute
934d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param startIndex The index of the first keyframe whose fraction must be set
935d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param endIndex The index of the last keyframe whose fraction must be set
936d430753cba09acb07af8b313286f247c78a41a32Chet Haase     */
937d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static void distributeKeyframes(Keyframe[] keyframes, float gap,
938d430753cba09acb07af8b313286f247c78a41a32Chet Haase            int startIndex, int endIndex) {
939d430753cba09acb07af8b313286f247c78a41a32Chet Haase        int count = endIndex - startIndex + 2;
940d430753cba09acb07af8b313286f247c78a41a32Chet Haase        float increment = gap / count;
941d430753cba09acb07af8b313286f247c78a41a32Chet Haase        for (int i = startIndex; i <= endIndex; ++i) {
942d430753cba09acb07af8b313286f247c78a41a32Chet Haase            keyframes[i].setFraction(keyframes[i-1].getFraction() + increment);
943d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
944d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
945d430753cba09acb07af8b313286f247c78a41a32Chet Haase
946d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static Keyframe loadKeyframe(Resources res, Theme theme, AttributeSet attrs,
947d430753cba09acb07af8b313286f247c78a41a32Chet Haase            int valueType)
948d430753cba09acb07af8b313286f247c78a41a32Chet Haase            throws XmlPullParserException, IOException {
949d430753cba09acb07af8b313286f247c78a41a32Chet Haase
950d430753cba09acb07af8b313286f247c78a41a32Chet Haase        TypedArray a;
951d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (theme != null) {
952d430753cba09acb07af8b313286f247c78a41a32Chet Haase            a = theme.obtainStyledAttributes(attrs, R.styleable.Keyframe, 0, 0);
953d430753cba09acb07af8b313286f247c78a41a32Chet Haase        } else {
954d430753cba09acb07af8b313286f247c78a41a32Chet Haase            a = res.obtainAttributes(attrs, R.styleable.Keyframe);
955d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
956d430753cba09acb07af8b313286f247c78a41a32Chet Haase
957d430753cba09acb07af8b313286f247c78a41a32Chet Haase        Keyframe keyframe = null;
958d430753cba09acb07af8b313286f247c78a41a32Chet Haase
959d430753cba09acb07af8b313286f247c78a41a32Chet Haase        float fraction = a.getFloat(R.styleable.Keyframe_fraction, -1);
960d430753cba09acb07af8b313286f247c78a41a32Chet Haase
9619032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        TypedValue keyframeValue = a.peekValue(R.styleable.Keyframe_value);
9629032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        boolean hasValue = (keyframeValue != null);
9639032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        if (valueType == VALUE_TYPE_UNDEFINED) {
9649032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            // When no value type is provided, check whether it's a color type first.
9659032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            // If not, fall back to default value type (i.e. float type).
9669032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            if (hasValue && isColorType(keyframeValue.type)) {
9679032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                valueType = VALUE_TYPE_COLOR;
9689032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            } else {
9699032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                valueType = VALUE_TYPE_FLOAT;
9709032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            }
9719032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        }
972d430753cba09acb07af8b313286f247c78a41a32Chet Haase
973d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (hasValue) {
974d430753cba09acb07af8b313286f247c78a41a32Chet Haase            switch (valueType) {
975d430753cba09acb07af8b313286f247c78a41a32Chet Haase                case VALUE_TYPE_FLOAT:
976d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    float value = a.getFloat(R.styleable.Keyframe_value, 0);
977d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    keyframe = Keyframe.ofFloat(fraction, value);
978d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    break;
979d430753cba09acb07af8b313286f247c78a41a32Chet Haase                case VALUE_TYPE_COLOR:
980d430753cba09acb07af8b313286f247c78a41a32Chet Haase                case VALUE_TYPE_INT:
981d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    int intValue = a.getInt(R.styleable.Keyframe_value, 0);
982d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    keyframe = Keyframe.ofInt(fraction, intValue);
983d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    break;
984d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
985d430753cba09acb07af8b313286f247c78a41a32Chet Haase        } else {
986d430753cba09acb07af8b313286f247c78a41a32Chet Haase            keyframe = (valueType == VALUE_TYPE_FLOAT) ? Keyframe.ofFloat(fraction) :
987d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    Keyframe.ofInt(fraction);
988d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
989d430753cba09acb07af8b313286f247c78a41a32Chet Haase
9906aac06ab940566020d050fdaa0d5e8d2e6c128aeDoris Liu        final int resID = a.getResourceId(R.styleable.Keyframe_interpolator, 0);
9916aac06ab940566020d050fdaa0d5e8d2e6c128aeDoris Liu        if (resID > 0) {
9926aac06ab940566020d050fdaa0d5e8d2e6c128aeDoris Liu            final Interpolator interpolator = AnimationUtils.loadInterpolator(res, theme, resID);
9936aac06ab940566020d050fdaa0d5e8d2e6c128aeDoris Liu            keyframe.setInterpolator(interpolator);
9946aac06ab940566020d050fdaa0d5e8d2e6c128aeDoris Liu        }
995d430753cba09acb07af8b313286f247c78a41a32Chet Haase        a.recycle();
996e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
997d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return keyframe;
998e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    }
999e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
1000fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount    private static ObjectAnimator loadObjectAnimator(Resources res, Theme theme, AttributeSet attrs,
10017513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            float pathErrorScale) throws NotFoundException {
1002e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        ObjectAnimator anim = new ObjectAnimator();
1003e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
10047513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu        loadAnimator(res, theme, attrs, anim, pathErrorScale);
1005e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
1006e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        return anim;
1007e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    }
1008e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
1009e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    /**
1010e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * Creates a new animation whose parameters come from the specified context
1011e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * and attributes set.
1012e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     *
1013e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @param res The resources
1014e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @param attrs The set of attributes holding the animation parameters
1015cf4832f69c8786b098ce18c24319021f8cd6733aztenghui     * @param anim Null if this is a ValueAnimator, otherwise this is an
1016cf4832f69c8786b098ce18c24319021f8cd6733aztenghui     *            ObjectAnimator
1017e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     */
1018e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    private static ValueAnimator loadAnimator(Resources res, Theme theme,
10197513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            AttributeSet attrs, ValueAnimator anim, float pathErrorScale)
1020e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            throws NotFoundException {
1021cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        TypedArray arrayAnimator = null;
1022cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        TypedArray arrayObjectAnimator = null;
1023cf4832f69c8786b098ce18c24319021f8cd6733aztenghui
1024e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        if (theme != null) {
1025cf4832f69c8786b098ce18c24319021f8cd6733aztenghui            arrayAnimator = theme.obtainStyledAttributes(attrs, R.styleable.Animator, 0, 0);
1026e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        } else {
1027cf4832f69c8786b098ce18c24319021f8cd6733aztenghui            arrayAnimator = res.obtainAttributes(attrs, R.styleable.Animator);
1028e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        }
1029e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
1030cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        // If anim is not null, then it is an object animator.
1031cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        if (anim != null) {
1032cf4832f69c8786b098ce18c24319021f8cd6733aztenghui            if (theme != null) {
1033cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                arrayObjectAnimator = theme.obtainStyledAttributes(attrs,
1034cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        R.styleable.PropertyAnimator, 0, 0);
1035cf4832f69c8786b098ce18c24319021f8cd6733aztenghui            } else {
1036cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                arrayObjectAnimator = res.obtainAttributes(attrs, R.styleable.PropertyAnimator);
1037cf4832f69c8786b098ce18c24319021f8cd6733aztenghui            }
1038d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            anim.appendChangingConfigurations(arrayObjectAnimator.getChangingConfigurations());
1039cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        }
10407f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout
10417f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout        if (anim == null) {
10427f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout            anim = new ValueAnimator();
10437f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout        }
1044d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        anim.appendChangingConfigurations(arrayAnimator.getChangingConfigurations());
10457f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout
1046fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount        parseAnimatorFromTypeArray(anim, arrayAnimator, arrayObjectAnimator, pathErrorScale);
1047d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
1048d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final int resID = arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0);
1049d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        if (resID > 0) {
1050d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            final Interpolator interpolator = AnimationUtils.loadInterpolator(res, theme, resID);
1051d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            if (interpolator instanceof BaseInterpolator) {
1052d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                anim.appendChangingConfigurations(
1053d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                        ((BaseInterpolator) interpolator).getChangingConfiguration());
1054d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            }
1055d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            anim.setInterpolator(interpolator);
1056d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        }
1057cf4832f69c8786b098ce18c24319021f8cd6733aztenghui
1058cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        arrayAnimator.recycle();
10597f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout        if (arrayObjectAnimator != null) {
10607f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout            arrayObjectAnimator.recycle();
10617f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout        }
1062d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        return anim;
1063d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    }
1064d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar
1065d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    private static int getChangingConfigs(Resources resources, int id) {
1066d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        synchronized (sTmpTypedValue) {
1067d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            resources.getValue(id, sTmpTypedValue, true);
1068d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            return sTmpTypedValue.changingConfigurations;
1069d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        }
1070d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    }
10719032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu
10729032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu    private static boolean isColorType(int type) {
10739032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu       return (type >= TypedValue.TYPE_FIRST_COLOR_INT) && (type <= TypedValue.TYPE_LAST_COLOR_INT);
10749032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu    }
1075d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase}
1076