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;
19ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viveretteimport android.annotation.AnyRes;
20ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viveretteimport android.annotation.NonNull;
21d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.content.Context;
22ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viveretteimport android.content.pm.ActivityInfo.Config;
23d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyarimport android.content.res.ConfigurationBoundResourceCache;
24d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyarimport android.content.res.ConstantState;
25d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.content.res.Resources;
26cf4832f69c8786b098ce18c24319021f8cd6733aztenghuiimport android.content.res.Resources.NotFoundException;
27e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghuiimport android.content.res.Resources.Theme;
28d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.content.res.TypedArray;
29d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.content.res.XmlResourceParser;
30cf4832f69c8786b098ce18c24319021f8cd6733aztenghuiimport android.graphics.Path;
31d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.util.AttributeSet;
32eb034fbca40006c55db143047eb628c4b657730aztenghuiimport android.util.Log;
33cf4832f69c8786b098ce18c24319021f8cd6733aztenghuiimport android.util.PathParser;
34f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyarimport android.util.StateSet;
355bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haaseimport android.util.TypedValue;
36d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.util.Xml;
37eb034fbca40006c55db143047eb628c4b657730aztenghuiimport android.view.InflateException;
38d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport android.view.animation.AnimationUtils;
39d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyarimport android.view.animation.BaseInterpolator;
40d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyarimport android.view.animation.Interpolator;
41e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
42e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghuiimport com.android.internal.R;
43e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
44d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport org.xmlpull.v1.XmlPullParser;
45d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport org.xmlpull.v1.XmlPullParserException;
46d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
47d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport java.io.IOException;
48d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haaseimport java.util.ArrayList;
49d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
50d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase/**
516cfdf4538049e4b96f50d8c0fe3119664420cc34Chet Haase * This class is used to instantiate animator XML files into Animator objects.
52d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * <p>
536cfdf4538049e4b96f50d8c0fe3119664420cc34Chet Haase * For performance reasons, inflation relies heavily on pre-processing of
54d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * XML files that is done at build time. Therefore, it is not currently possible
556cfdf4538049e4b96f50d8c0fe3119664420cc34Chet Haase * to use this inflater with an XmlPullParser over a plain XML file at runtime;
56d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * it only works with an XmlPullParser returned from a compiled resource (R.
57d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase * <em>something</em> file.)
58d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase */
59a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haasepublic class AnimatorInflater {
60eb034fbca40006c55db143047eb628c4b657730aztenghui    private static final String TAG = "AnimatorInflater";
61d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    /**
62a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * These flags are used when parsing AnimatorSet objects
63d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     */
64d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    private static final int TOGETHER = 0;
65d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    private static final int SEQUENTIALLY = 1;
66d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
67d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    /**
68d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     * Enum values used in XML attributes to indicate the value for mValueType
69d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     */
70d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    private static final int VALUE_TYPE_FLOAT       = 0;
71d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    private static final int VALUE_TYPE_INT         = 1;
72eb034fbca40006c55db143047eb628c4b657730aztenghui    private static final int VALUE_TYPE_PATH        = 2;
73d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static final int VALUE_TYPE_COLOR       = 3;
749032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu    private static final int VALUE_TYPE_UNDEFINED   = 4;
75d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
76eb034fbca40006c55db143047eb628c4b657730aztenghui    private static final boolean DBG_ANIMATOR_INFLATER = false;
77eb034fbca40006c55db143047eb628c4b657730aztenghui
78d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    // used to calculate changing configs for resource references
79d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    private static final TypedValue sTmpTypedValue = new TypedValue();
80d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar
81d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    /**
82a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * Loads an {@link Animator} object from a resource
83d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     *
847513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu     * @param context Application context used to access resources
85d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     * @param id The resource id of the animation to load
86a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haase     * @return The animator object reference by the specified id
87d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
88d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase     */
897b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye    public static Animator loadAnimator(Context context, @AnimatorRes int id)
90d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            throws NotFoundException {
917513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu        return loadAnimator(context.getResources(), context.getTheme(), id);
92e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    }
93e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
94e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    /**
95e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * Loads an {@link Animator} object from a resource
96e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     *
977513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu     * @param resources The resources
98e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @param theme The theme
99e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @param id The resource id of the animation to load
100e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @return The animator object reference by the specified id
101e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
102e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @hide
103e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     */
1047513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu    public static Animator loadAnimator(Resources resources, Theme theme, int id)
105e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            throws NotFoundException {
1067513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu        return loadAnimator(resources, theme, id, 1);
107fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount    }
108fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount
109fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount    /** @hide */
110fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount    public static Animator loadAnimator(Resources resources, Theme theme, int id,
111fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount            float pathErrorScale) throws NotFoundException {
112d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final ConfigurationBoundResourceCache<Animator> animatorCache = resources
113d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                .getAnimatorCache();
114fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        Animator animator = animatorCache.getInstance(id, resources, theme);
115d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        if (animator != null) {
116d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            if (DBG_ANIMATOR_INFLATER) {
117d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
118d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            }
119d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            return animator;
120d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        } else if (DBG_ANIMATOR_INFLATER) {
121d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            Log.d(TAG, "cache miss for animator " + resources.getResourceName(id));
122d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        }
123d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        XmlResourceParser parser = null;
124d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        try {
125e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            parser = resources.getAnimation(id);
1267513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale);
127d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            if (animator != null) {
128d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                animator.appendChangingConfigurations(getChangingConfigs(resources, id));
129d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                final ConstantState<Animator> constantState = animator.createConstantState();
130d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                if (constantState != null) {
131d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    if (DBG_ANIMATOR_INFLATER) {
132d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                        Log.d(TAG, "caching animator for res " + resources.getResourceName(id));
133d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    }
134d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    animatorCache.put(id, theme, constantState);
135d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    // create a new animator so that cached version is never used by the user
136d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    animator = constantState.newInstance(resources, theme);
137d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                }
138d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            }
139d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            return animator;
140d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        } catch (XmlPullParserException ex) {
141d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            Resources.NotFoundException rnf =
142d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
143fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount                            Integer.toHexString(id));
144d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            rnf.initCause(ex);
145d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            throw rnf;
146d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        } catch (IOException ex) {
147d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            Resources.NotFoundException rnf =
148d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
149fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount                            Integer.toHexString(id));
150d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            rnf.initCause(ex);
151d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            throw rnf;
152d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        } finally {
153d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase            if (parser != null) parser.close();
154d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        }
155d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    }
156d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
157f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar    public static StateListAnimator loadStateListAnimator(Context context, int id)
158f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            throws NotFoundException {
159d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final Resources resources = context.getResources();
160d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
161d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                .getStateListAnimatorCache();
162d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final Theme theme = context.getTheme();
163fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        StateListAnimator animator = cache.getInstance(id, resources, theme);
164d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        if (animator != null) {
165d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            return animator;
166d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        }
167f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        XmlResourceParser parser = null;
168f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        try {
169d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            parser = resources.getAnimation(id);
170d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser));
171d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            if (animator != null) {
172d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                animator.appendChangingConfigurations(getChangingConfigs(resources, id));
173d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                final ConstantState<StateListAnimator> constantState = animator
174d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                        .createConstantState();
175d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                if (constantState != null) {
176d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    cache.put(id, theme, constantState);
177d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    // return a clone so that the animator in constant state is never used.
178d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                    animator = constantState.newInstance(resources, theme);
179d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                }
180d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            }
181d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            return animator;
182f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        } catch (XmlPullParserException ex) {
183f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            Resources.NotFoundException rnf =
184f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    new Resources.NotFoundException(
185f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            "Can't load state list animator resource ID #0x" +
186f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                    Integer.toHexString(id)
187f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    );
188f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            rnf.initCause(ex);
189f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            throw rnf;
190f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        } catch (IOException ex) {
191f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            Resources.NotFoundException rnf =
192f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    new Resources.NotFoundException(
193f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            "Can't load state list animator resource ID #0x" +
194f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                    Integer.toHexString(id)
195f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    );
196f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            rnf.initCause(ex);
197f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            throw rnf;
198f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        } finally {
199f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            if (parser != null) {
200f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                parser.close();
201f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            }
202f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        }
203f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar    }
204f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar
205f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar    private static StateListAnimator createStateListAnimatorFromXml(Context context,
206f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            XmlPullParser parser, AttributeSet attributeSet)
207f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            throws IOException, XmlPullParserException {
208f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        int type;
209f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        StateListAnimator stateListAnimator = new StateListAnimator();
210f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar
211f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        while (true) {
212f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            type = parser.next();
213f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            switch (type) {
214f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                case XmlPullParser.END_DOCUMENT:
215f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                case XmlPullParser.END_TAG:
216f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    return stateListAnimator;
217f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar
218f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                case XmlPullParser.START_TAG:
219f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    // parse item
220f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    Animator animator = null;
221f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    if ("item".equals(parser.getName())) {
222f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        int attributeCount = parser.getAttributeCount();
223f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        int[] states = new int[attributeCount];
224f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        int stateIndex = 0;
225f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        for (int i = 0; i < attributeCount; i++) {
226f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            int attrName = attributeSet.getAttributeNameResource(i);
227cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                            if (attrName == R.attr.animation) {
228d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                                final int animId = attributeSet.getAttributeResourceValue(i, 0);
229d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                                animator = loadAnimator(context, animId);
230f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            } else {
231f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                states[stateIndex++] =
232f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                        attributeSet.getAttributeBooleanValue(i, false) ?
233f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                                attrName : -attrName;
234f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            }
235f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        }
236f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        if (animator == null) {
237e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                            animator = createAnimatorFromXml(context.getResources(),
2387513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu                                    context.getTheme(), parser, 1f);
239f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        }
240f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar
241f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        if (animator == null) {
242f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                            throw new Resources.NotFoundException(
243f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                    "animation state item must have a valid animation");
244f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        }
245f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                        stateListAnimator
246f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                                .addState(StateSet.trimStateSet(states, stateIndex), animator);
247f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    }
248f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar                    break;
249f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar            }
250f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar        }
251f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar    }
252f4c5bf30b445874cf353e1b96cab94185a39ce6dYigit Boyar
253cf4832f69c8786b098ce18c24319021f8cd6733aztenghui    /**
254eb034fbca40006c55db143047eb628c4b657730aztenghui     * PathDataEvaluator is used to interpolate between two paths which are
255eb034fbca40006c55db143047eb628c4b657730aztenghui     * represented in the same format but different control points' values.
256804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu     * The path is represented as verbs and points for each of the verbs.
257eb034fbca40006c55db143047eb628c4b657730aztenghui     */
258804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu    private static class PathDataEvaluator implements TypeEvaluator<PathParser.PathData> {
259804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        private final PathParser.PathData mPathData = new PathParser.PathData();
260eb034fbca40006c55db143047eb628c4b657730aztenghui
261eb034fbca40006c55db143047eb628c4b657730aztenghui        @Override
262804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        public PathParser.PathData evaluate(float fraction, PathParser.PathData startPathData,
263804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    PathParser.PathData endPathData) {
264804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            if (!PathParser.interpolatePathData(mPathData, startPathData, endPathData, fraction)) {
265eb034fbca40006c55db143047eb628c4b657730aztenghui                throw new IllegalArgumentException("Can't interpolate between"
266eb034fbca40006c55db143047eb628c4b657730aztenghui                        + " two incompatible pathData");
267eb034fbca40006c55db143047eb628c4b657730aztenghui            }
268804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            return mPathData;
269eb034fbca40006c55db143047eb628c4b657730aztenghui        }
270eb034fbca40006c55db143047eb628c4b657730aztenghui    }
271eb034fbca40006c55db143047eb628c4b657730aztenghui
272d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static PropertyValuesHolder getPVH(TypedArray styledAttributes, int valueType,
273d430753cba09acb07af8b313286f247c78a41a32Chet Haase            int valueFromId, int valueToId, String propertyName) {
2745bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase
275d430753cba09acb07af8b313286f247c78a41a32Chet Haase        TypedValue tvFrom = styledAttributes.peekValue(valueFromId);
2765bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        boolean hasFrom = (tvFrom != null);
2775bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        int fromType = hasFrom ? tvFrom.type : 0;
278d430753cba09acb07af8b313286f247c78a41a32Chet Haase        TypedValue tvTo = styledAttributes.peekValue(valueToId);
2795bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        boolean hasTo = (tvTo != null);
2805bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        int toType = hasTo ? tvTo.type : 0;
2815bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase
2829032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        if (valueType == VALUE_TYPE_UNDEFINED) {
2839032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            // Check whether it's color type. If not, fall back to default type (i.e. float type)
2849032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            if ((hasFrom && isColorType(fromType)) || (hasTo && isColorType(toType))) {
2859032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                valueType = VALUE_TYPE_COLOR;
2869032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            } else {
2879032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                valueType = VALUE_TYPE_FLOAT;
2889032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            }
2899032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        }
2909032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu
2919032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
2929032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu
293d430753cba09acb07af8b313286f247c78a41a32Chet Haase        PropertyValuesHolder returnValue = null;
294d430753cba09acb07af8b313286f247c78a41a32Chet Haase
295eb034fbca40006c55db143047eb628c4b657730aztenghui        if (valueType == VALUE_TYPE_PATH) {
296d430753cba09acb07af8b313286f247c78a41a32Chet Haase            String fromString = styledAttributes.getString(valueFromId);
297d430753cba09acb07af8b313286f247c78a41a32Chet Haase            String toString = styledAttributes.getString(valueToId);
298804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            PathParser.PathData nodesFrom = fromString == null
299804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    ? null : new PathParser.PathData(fromString);
300804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            PathParser.PathData nodesTo = toString == null
301804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    ? null : new PathParser.PathData(toString);
302d430753cba09acb07af8b313286f247c78a41a32Chet Haase
303d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (nodesFrom != null || nodesTo != null) {
304d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (nodesFrom != null) {
305804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    TypeEvaluator evaluator = new PathDataEvaluator();
306d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (nodesTo != null) {
307d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        if (!PathParser.canMorph(nodesFrom, nodesTo)) {
308d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            throw new InflateException(" Can't morph from " + fromString + " to " +
309d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                    toString);
310d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        }
311d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofObject(propertyName, evaluator,
312d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                nodesFrom, nodesTo);
313d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
314d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofObject(propertyName, evaluator,
315d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                (Object) nodesFrom);
316d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
317d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else if (nodesTo != null) {
318804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                    TypeEvaluator evaluator = new PathDataEvaluator();
319d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    returnValue = PropertyValuesHolder.ofObject(propertyName, evaluator,
320d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            (Object) nodesTo);
321d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
322d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
323eb034fbca40006c55db143047eb628c4b657730aztenghui        } else {
324d430753cba09acb07af8b313286f247c78a41a32Chet Haase            TypeEvaluator evaluator = null;
325eb034fbca40006c55db143047eb628c4b657730aztenghui            // Integer and float value types are handled here.
3269032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            if (valueType == VALUE_TYPE_COLOR) {
327eb034fbca40006c55db143047eb628c4b657730aztenghui                // special case for colors: ignore valueType and get ints
328eb034fbca40006c55db143047eb628c4b657730aztenghui                evaluator = ArgbEvaluator.getInstance();
329eb034fbca40006c55db143047eb628c4b657730aztenghui            }
330d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (getFloats) {
331d430753cba09acb07af8b313286f247c78a41a32Chet Haase                float valueFrom;
332d430753cba09acb07af8b313286f247c78a41a32Chet Haase                float valueTo;
333d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (hasFrom) {
334d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (fromType == TypedValue.TYPE_DIMENSION) {
335d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueFrom = styledAttributes.getDimension(valueFromId, 0f);
336d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
337d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueFrom = styledAttributes.getFloat(valueFromId, 0f);
338d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
339d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (hasTo) {
340d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        if (toType == TypedValue.TYPE_DIMENSION) {
341d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getDimension(valueToId, 0f);
342d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        } else {
343d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getFloat(valueToId, 0f);
344d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        }
345d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofFloat(propertyName,
346d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                valueFrom, valueTo);
347d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
348d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofFloat(propertyName, valueFrom);
349d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
350d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else {
351d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (toType == TypedValue.TYPE_DIMENSION) {
352d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueTo = styledAttributes.getDimension(valueToId, 0f);
353d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
354d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueTo = styledAttributes.getFloat(valueToId, 0f);
355d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
356d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    returnValue = PropertyValuesHolder.ofFloat(propertyName, valueTo);
357d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
358d430753cba09acb07af8b313286f247c78a41a32Chet Haase            } else {
359d430753cba09acb07af8b313286f247c78a41a32Chet Haase                int valueFrom;
360d430753cba09acb07af8b313286f247c78a41a32Chet Haase                int valueTo;
361d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (hasFrom) {
362d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (fromType == TypedValue.TYPE_DIMENSION) {
363d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueFrom = (int) styledAttributes.getDimension(valueFromId, 0f);
3649032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                    } else if (isColorType(fromType)) {
365d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueFrom = styledAttributes.getColor(valueFromId, 0);
366d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
367d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        valueFrom = styledAttributes.getInt(valueFromId, 0);
368d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
369d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (hasTo) {
370d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        if (toType == TypedValue.TYPE_DIMENSION) {
371d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = (int) styledAttributes.getDimension(valueToId, 0f);
3729032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                        } else if (isColorType(toType)) {
373d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getColor(valueToId, 0);
374d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        } else {
375d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getInt(valueToId, 0);
376d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        }
377d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofInt(propertyName, valueFrom, valueTo);
378d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
379d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofInt(propertyName, valueFrom);
380d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
381d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else {
382d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (hasTo) {
383d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        if (toType == TypedValue.TYPE_DIMENSION) {
384d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = (int) styledAttributes.getDimension(valueToId, 0f);
3859032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                        } else if (isColorType(toType)) {
386d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getColor(valueToId, 0);
387d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        } else {
388d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            valueTo = styledAttributes.getInt(valueToId, 0);
389d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        }
390d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        returnValue = PropertyValuesHolder.ofInt(propertyName, valueTo);
391d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
392d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
393d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
394d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (returnValue != null && evaluator != null) {
395d430753cba09acb07af8b313286f247c78a41a32Chet Haase                returnValue.setEvaluator(evaluator);
396d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
397d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
398d430753cba09acb07af8b313286f247c78a41a32Chet Haase
399d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return returnValue;
400d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
401d430753cba09acb07af8b313286f247c78a41a32Chet Haase
402d430753cba09acb07af8b313286f247c78a41a32Chet Haase    /**
403d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param anim The animator, must not be null
404d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param arrayAnimator Incoming typed array for Animator's attributes.
405d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param arrayObjectAnimator Incoming typed array for Object Animator's
406d430753cba09acb07af8b313286f247c78a41a32Chet Haase     *            attributes.
407d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param pixelSize The relative pixel size, used to calculate the
408d430753cba09acb07af8b313286f247c78a41a32Chet Haase     *                  maximum error for path animations.
409d430753cba09acb07af8b313286f247c78a41a32Chet Haase     */
410d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static void parseAnimatorFromTypeArray(ValueAnimator anim,
411d430753cba09acb07af8b313286f247c78a41a32Chet Haase            TypedArray arrayAnimator, TypedArray arrayObjectAnimator, float pixelSize) {
412d430753cba09acb07af8b313286f247c78a41a32Chet Haase        long duration = arrayAnimator.getInt(R.styleable.Animator_duration, 300);
413d430753cba09acb07af8b313286f247c78a41a32Chet Haase
414d430753cba09acb07af8b313286f247c78a41a32Chet Haase        long startDelay = arrayAnimator.getInt(R.styleable.Animator_startOffset, 0);
415d430753cba09acb07af8b313286f247c78a41a32Chet Haase
4162787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        int valueType = arrayAnimator.getInt(R.styleable.Animator_valueType, VALUE_TYPE_UNDEFINED);
417d430753cba09acb07af8b313286f247c78a41a32Chet Haase
4182787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        if (valueType == VALUE_TYPE_UNDEFINED) {
4192787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu            valueType = inferValueTypeFromValues(arrayAnimator, R.styleable.Animator_valueFrom,
4202787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu                    R.styleable.Animator_valueTo);
4212787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        }
422d430753cba09acb07af8b313286f247c78a41a32Chet Haase        PropertyValuesHolder pvh = getPVH(arrayAnimator, valueType,
423d430753cba09acb07af8b313286f247c78a41a32Chet Haase                R.styleable.Animator_valueFrom, R.styleable.Animator_valueTo, "");
424d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (pvh != null) {
425d430753cba09acb07af8b313286f247c78a41a32Chet Haase            anim.setValues(pvh);
4265bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        }
4275bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase
428eb034fbca40006c55db143047eb628c4b657730aztenghui        anim.setDuration(duration);
429eb034fbca40006c55db143047eb628c4b657730aztenghui        anim.setStartDelay(startDelay);
430eb034fbca40006c55db143047eb628c4b657730aztenghui
431eb034fbca40006c55db143047eb628c4b657730aztenghui        if (arrayAnimator.hasValue(R.styleable.Animator_repeatCount)) {
432eb034fbca40006c55db143047eb628c4b657730aztenghui            anim.setRepeatCount(
433eb034fbca40006c55db143047eb628c4b657730aztenghui                    arrayAnimator.getInt(R.styleable.Animator_repeatCount, 0));
434eb034fbca40006c55db143047eb628c4b657730aztenghui        }
435eb034fbca40006c55db143047eb628c4b657730aztenghui        if (arrayAnimator.hasValue(R.styleable.Animator_repeatMode)) {
436eb034fbca40006c55db143047eb628c4b657730aztenghui            anim.setRepeatMode(
437eb034fbca40006c55db143047eb628c4b657730aztenghui                    arrayAnimator.getInt(R.styleable.Animator_repeatMode,
438eb034fbca40006c55db143047eb628c4b657730aztenghui                            ValueAnimator.RESTART));
439eb034fbca40006c55db143047eb628c4b657730aztenghui        }
440eb034fbca40006c55db143047eb628c4b657730aztenghui
441eb034fbca40006c55db143047eb628c4b657730aztenghui        if (arrayObjectAnimator != null) {
44202f03120a974c6ed17b0c7fa5e8e50d42f9b7768Doris Liu            setupObjectAnimator(anim, arrayObjectAnimator, valueType, pixelSize);
443eb034fbca40006c55db143047eb628c4b657730aztenghui        }
444eb034fbca40006c55db143047eb628c4b657730aztenghui    }
445eb034fbca40006c55db143047eb628c4b657730aztenghui
446eb034fbca40006c55db143047eb628c4b657730aztenghui    /**
447eb034fbca40006c55db143047eb628c4b657730aztenghui     * Setup the Animator to achieve path morphing.
448eb034fbca40006c55db143047eb628c4b657730aztenghui     *
449eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param anim The target Animator which will be updated.
450eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param arrayAnimator TypedArray for the ValueAnimator.
451eb034fbca40006c55db143047eb628c4b657730aztenghui     * @return the PathDataEvaluator.
452eb034fbca40006c55db143047eb628c4b657730aztenghui     */
453eb034fbca40006c55db143047eb628c4b657730aztenghui    private static TypeEvaluator setupAnimatorForPath(ValueAnimator anim,
454eb034fbca40006c55db143047eb628c4b657730aztenghui             TypedArray arrayAnimator) {
455eb034fbca40006c55db143047eb628c4b657730aztenghui        TypeEvaluator evaluator = null;
456eb034fbca40006c55db143047eb628c4b657730aztenghui        String fromString = arrayAnimator.getString(R.styleable.Animator_valueFrom);
457eb034fbca40006c55db143047eb628c4b657730aztenghui        String toString = arrayAnimator.getString(R.styleable.Animator_valueTo);
458804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        PathParser.PathData pathDataFrom = fromString == null
459804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                ? null : new PathParser.PathData(fromString);
460804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        PathParser.PathData pathDataTo = toString == null
461804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                ? null : new PathParser.PathData(toString);
462804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu
463804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        if (pathDataFrom != null) {
464804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            if (pathDataTo != null) {
465804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                anim.setObjectValues(pathDataFrom, pathDataTo);
466804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                if (!PathParser.canMorph(pathDataFrom, pathDataTo)) {
467eb034fbca40006c55db143047eb628c4b657730aztenghui                    throw new InflateException(arrayAnimator.getPositionDescription()
468eb034fbca40006c55db143047eb628c4b657730aztenghui                            + " Can't morph from " + fromString + " to " + toString);
469eb034fbca40006c55db143047eb628c4b657730aztenghui                }
470eb034fbca40006c55db143047eb628c4b657730aztenghui            } else {
471804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu                anim.setObjectValues((Object)pathDataFrom);
472eb034fbca40006c55db143047eb628c4b657730aztenghui            }
473804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            evaluator = new PathDataEvaluator();
474804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu        } else if (pathDataTo != null) {
475804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            anim.setObjectValues((Object)pathDataTo);
476804618d0863a5d8ad1b08a846bd5319be864a1cbDoris Liu            evaluator = new PathDataEvaluator();
477eb034fbca40006c55db143047eb628c4b657730aztenghui        }
478eb034fbca40006c55db143047eb628c4b657730aztenghui
479eb034fbca40006c55db143047eb628c4b657730aztenghui        if (DBG_ANIMATOR_INFLATER && evaluator != null) {
480eb034fbca40006c55db143047eb628c4b657730aztenghui            Log.v(TAG, "create a new PathDataEvaluator here");
481eb034fbca40006c55db143047eb628c4b657730aztenghui        }
482eb034fbca40006c55db143047eb628c4b657730aztenghui
483eb034fbca40006c55db143047eb628c4b657730aztenghui        return evaluator;
484eb034fbca40006c55db143047eb628c4b657730aztenghui    }
485eb034fbca40006c55db143047eb628c4b657730aztenghui
486eb034fbca40006c55db143047eb628c4b657730aztenghui    /**
487eb034fbca40006c55db143047eb628c4b657730aztenghui     * Setup ObjectAnimator's property or values from pathData.
488eb034fbca40006c55db143047eb628c4b657730aztenghui     *
489eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param anim The target Animator which will be updated.
490eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param arrayObjectAnimator TypedArray for the ObjectAnimator.
491eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param getFloats True if the value type is float.
492fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount     * @param pixelSize The relative pixel size, used to calculate the
493fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount     *                  maximum error for path animations.
494eb034fbca40006c55db143047eb628c4b657730aztenghui     */
495eb034fbca40006c55db143047eb628c4b657730aztenghui    private static void setupObjectAnimator(ValueAnimator anim, TypedArray arrayObjectAnimator,
49602f03120a974c6ed17b0c7fa5e8e50d42f9b7768Doris Liu            int valueType, float pixelSize) {
497eb034fbca40006c55db143047eb628c4b657730aztenghui        ObjectAnimator oa = (ObjectAnimator) anim;
498eb034fbca40006c55db143047eb628c4b657730aztenghui        String pathData = arrayObjectAnimator.getString(R.styleable.PropertyAnimator_pathData);
499eb034fbca40006c55db143047eb628c4b657730aztenghui
5002787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // Path can be involved in an ObjectAnimator in the following 3 ways:
5012787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // 1) Path morphing: the property to be animated is pathData, and valueFrom and valueTo
5022787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        //    are both of pathType. valueType = pathType needs to be explicitly defined.
5032787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // 2) A property in X or Y dimension can be animated along a path: the property needs to be
5042787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        //    defined in propertyXName or propertyYName attribute, the path will be defined in the
5052787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        //    pathData attribute. valueFrom and valueTo will not be necessary for this animation.
5062787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // 3) PathInterpolator can also define a path (in pathData) for its interpolation curve.
5072787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // Here we are dealing with case 2:
508eb034fbca40006c55db143047eb628c4b657730aztenghui        if (pathData != null) {
509eb034fbca40006c55db143047eb628c4b657730aztenghui            String propertyXName =
510eb034fbca40006c55db143047eb628c4b657730aztenghui                    arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyXName);
511eb034fbca40006c55db143047eb628c4b657730aztenghui            String propertyYName =
512eb034fbca40006c55db143047eb628c4b657730aztenghui                    arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyYName);
513eb034fbca40006c55db143047eb628c4b657730aztenghui
51402f03120a974c6ed17b0c7fa5e8e50d42f9b7768Doris Liu            if (valueType == VALUE_TYPE_PATH || valueType == VALUE_TYPE_UNDEFINED) {
51502f03120a974c6ed17b0c7fa5e8e50d42f9b7768Doris Liu                // When pathData is defined, we are in case #2 mentioned above. ValueType can only
51602f03120a974c6ed17b0c7fa5e8e50d42f9b7768Doris Liu                // be float type, or int type. Otherwise we fallback to default type.
51702f03120a974c6ed17b0c7fa5e8e50d42f9b7768Doris Liu                valueType = VALUE_TYPE_FLOAT;
51802f03120a974c6ed17b0c7fa5e8e50d42f9b7768Doris Liu            }
519eb034fbca40006c55db143047eb628c4b657730aztenghui            if (propertyXName == null && propertyYName == null) {
520eb034fbca40006c55db143047eb628c4b657730aztenghui                throw new InflateException(arrayObjectAnimator.getPositionDescription()
521eb034fbca40006c55db143047eb628c4b657730aztenghui                        + " propertyXName or propertyYName is needed for PathData");
522eb034fbca40006c55db143047eb628c4b657730aztenghui            } else {
523eb034fbca40006c55db143047eb628c4b657730aztenghui                Path path = PathParser.createPathFromPathData(pathData);
524fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount                float error = 0.5f * pixelSize; // max half a pixel error
525fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount                PathKeyframes keyframeSet = KeyframeSet.ofPath(path, error);
526984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                Keyframes xKeyframes;
527984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                Keyframes yKeyframes;
52802f03120a974c6ed17b0c7fa5e8e50d42f9b7768Doris Liu                if (valueType == VALUE_TYPE_FLOAT) {
529984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    xKeyframes = keyframeSet.createXFloatKeyframes();
530984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    yKeyframes = keyframeSet.createYFloatKeyframes();
531984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                } else {
532984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    xKeyframes = keyframeSet.createXIntKeyframes();
533984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    yKeyframes = keyframeSet.createYIntKeyframes();
534984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                }
535eb034fbca40006c55db143047eb628c4b657730aztenghui                PropertyValuesHolder x = null;
536eb034fbca40006c55db143047eb628c4b657730aztenghui                PropertyValuesHolder y = null;
537eb034fbca40006c55db143047eb628c4b657730aztenghui                if (propertyXName != null) {
538984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    x = PropertyValuesHolder.ofKeyframes(propertyXName, xKeyframes);
539eb034fbca40006c55db143047eb628c4b657730aztenghui                }
540eb034fbca40006c55db143047eb628c4b657730aztenghui                if (propertyYName != null) {
541984011f6850fd4b6ad4db6d6022bd475d7a2c712George Mount                    y = PropertyValuesHolder.ofKeyframes(propertyYName, yKeyframes);
542eb034fbca40006c55db143047eb628c4b657730aztenghui                }
543eb034fbca40006c55db143047eb628c4b657730aztenghui                if (x == null) {
544eb034fbca40006c55db143047eb628c4b657730aztenghui                    oa.setValues(y);
545eb034fbca40006c55db143047eb628c4b657730aztenghui                } else if (y == null) {
546eb034fbca40006c55db143047eb628c4b657730aztenghui                    oa.setValues(x);
547eb034fbca40006c55db143047eb628c4b657730aztenghui                } else {
548eb034fbca40006c55db143047eb628c4b657730aztenghui                    oa.setValues(x, y);
549eb034fbca40006c55db143047eb628c4b657730aztenghui                }
550eb034fbca40006c55db143047eb628c4b657730aztenghui            }
551eb034fbca40006c55db143047eb628c4b657730aztenghui        } else {
552eb034fbca40006c55db143047eb628c4b657730aztenghui            String propertyName =
553eb034fbca40006c55db143047eb628c4b657730aztenghui                    arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyName);
554eb034fbca40006c55db143047eb628c4b657730aztenghui            oa.setPropertyName(propertyName);
555eb034fbca40006c55db143047eb628c4b657730aztenghui        }
556eb034fbca40006c55db143047eb628c4b657730aztenghui    }
557eb034fbca40006c55db143047eb628c4b657730aztenghui
558eb034fbca40006c55db143047eb628c4b657730aztenghui    /**
559eb034fbca40006c55db143047eb628c4b657730aztenghui     * Setup ValueAnimator's values.
560eb034fbca40006c55db143047eb628c4b657730aztenghui     * This will handle all of the integer, float and color types.
561eb034fbca40006c55db143047eb628c4b657730aztenghui     *
562eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param anim The target Animator which will be updated.
563eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param arrayAnimator TypedArray for the ValueAnimator.
564eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param getFloats True if the value type is float.
565eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param hasFrom True if "valueFrom" exists.
566eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param fromType The type of "valueFrom".
567eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param hasTo True if "valueTo" exists.
568eb034fbca40006c55db143047eb628c4b657730aztenghui     * @param toType The type of "valueTo".
569eb034fbca40006c55db143047eb628c4b657730aztenghui     */
570eb034fbca40006c55db143047eb628c4b657730aztenghui    private static void setupValues(ValueAnimator anim, TypedArray arrayAnimator,
571eb034fbca40006c55db143047eb628c4b657730aztenghui            boolean getFloats, boolean hasFrom, int fromType, boolean hasTo, int toType) {
572eb034fbca40006c55db143047eb628c4b657730aztenghui        int valueFromIndex = R.styleable.Animator_valueFrom;
573eb034fbca40006c55db143047eb628c4b657730aztenghui        int valueToIndex = R.styleable.Animator_valueTo;
5745bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        if (getFloats) {
5755bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            float valueFrom;
5765bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            float valueTo;
5775bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            if (hasFrom) {
5785bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (fromType == TypedValue.TYPE_DIMENSION) {
579cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueFrom = arrayAnimator.getDimension(valueFromIndex, 0f);
5805bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                } else {
581cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueFrom = arrayAnimator.getFloat(valueFromIndex, 0f);
5825bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                }
5835bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (hasTo) {
5845bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    if (toType == TypedValue.TYPE_DIMENSION) {
585cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getDimension(valueToIndex, 0f);
5862794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    } else {
587cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getFloat(valueToIndex, 0f);
5882794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    }
5895bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    anim.setFloatValues(valueFrom, valueTo);
5905bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                } else {
5915bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    anim.setFloatValues(valueFrom);
5925bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                }
5935bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            } else {
5945bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (toType == TypedValue.TYPE_DIMENSION) {
595cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueTo = arrayAnimator.getDimension(valueToIndex, 0f);
5962794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                } else {
597cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueTo = arrayAnimator.getFloat(valueToIndex, 0f);
598d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase                }
5995bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                anim.setFloatValues(valueTo);
6002794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase            }
6015bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase        } else {
6025bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            int valueFrom;
6035bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            int valueTo;
6045bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            if (hasFrom) {
6055bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (fromType == TypedValue.TYPE_DIMENSION) {
606cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueFrom = (int) arrayAnimator.getDimension(valueFromIndex, 0f);
6079032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                } else if (isColorType(fromType)) {
608cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueFrom = arrayAnimator.getColor(valueFromIndex, 0);
6095bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                } else {
610cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    valueFrom = arrayAnimator.getInt(valueFromIndex, 0);
6115bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                }
6125bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (hasTo) {
6135bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    if (toType == TypedValue.TYPE_DIMENSION) {
614cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = (int) arrayAnimator.getDimension(valueToIndex, 0f);
6159032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                    } else if (isColorType(toType)) {
616cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getColor(valueToIndex, 0);
6172794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    } else {
618cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getInt(valueToIndex, 0);
6192794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    }
6205bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    anim.setIntValues(valueFrom, valueTo);
6212794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                } else {
6225bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    anim.setIntValues(valueFrom);
623d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase                }
6245bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase            } else {
6255bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                if (hasTo) {
6265bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    if (toType == TypedValue.TYPE_DIMENSION) {
627cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = (int) arrayAnimator.getDimension(valueToIndex, 0f);
6289032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                    } else if (isColorType(toType)) {
629cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getColor(valueToIndex, 0);
6302794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    } else {
631cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        valueTo = arrayAnimator.getInt(valueToIndex, 0);
6322794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase                    }
6335bed88e12d0b4e77483e287c068fbf551ecf8a10Chet Haase                    anim.setIntValues(valueTo);
634d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase                }
6352794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase            }
636d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        }
637e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    }
638e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
639fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount    private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser,
6407513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            float pixelSize)
641e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            throws XmlPullParserException, IOException {
642fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount        return createAnimatorFromXml(res, theme, parser, Xml.asAttributeSet(parser), null, 0,
6437513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu                pixelSize);
644e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    }
645e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
646e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser,
6477513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            AttributeSet attrs, AnimatorSet parent, int sequenceOrdering, float pixelSize)
6487513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            throws XmlPullParserException, IOException {
649e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        Animator anim = null;
650e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        ArrayList<Animator> childAnims = null;
651e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
652e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        // Make sure we are on a start tag.
653e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        int type;
654e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        int depth = parser.getDepth();
655e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
656e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
657e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                && type != XmlPullParser.END_DOCUMENT) {
658e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
659e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            if (type != XmlPullParser.START_TAG) {
660e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                continue;
661e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            }
662e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
663e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            String name = parser.getName();
664d430753cba09acb07af8b313286f247c78a41a32Chet Haase            boolean gotValues = false;
665e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
666e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            if (name.equals("objectAnimator")) {
6677513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu                anim = loadObjectAnimator(res, theme, attrs, pixelSize);
668e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            } else if (name.equals("animator")) {
6697513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu                anim = loadAnimator(res, theme, attrs, null, pixelSize);
670e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            } else if (name.equals("set")) {
671e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                anim = new AnimatorSet();
672e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                TypedArray a;
673e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                if (theme != null) {
674cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    a = theme.obtainStyledAttributes(attrs, R.styleable.AnimatorSet, 0, 0);
675e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                } else {
676cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                    a = res.obtainAttributes(attrs, R.styleable.AnimatorSet);
677e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                }
678d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                anim.appendChangingConfigurations(a.getChangingConfigurations());
679d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER);
680fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount                createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering,
6817513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu                        pixelSize);
682e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                a.recycle();
683d430753cba09acb07af8b313286f247c78a41a32Chet Haase            } else if (name.equals("propertyValuesHolder")) {
684d430753cba09acb07af8b313286f247c78a41a32Chet Haase                PropertyValuesHolder[] values = loadValues(res, theme, parser,
685d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        Xml.asAttributeSet(parser));
686d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (values != null && anim != null && (anim instanceof ValueAnimator)) {
687d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    ((ValueAnimator) anim).setValues(values);
688d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
689d430753cba09acb07af8b313286f247c78a41a32Chet Haase                gotValues = true;
690e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            } else {
691e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                throw new RuntimeException("Unknown animator name: " + parser.getName());
692e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            }
693e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
694d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (parent != null && !gotValues) {
695e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                if (childAnims == null) {
696e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                    childAnims = new ArrayList<Animator>();
697e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                }
698e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                childAnims.add(anim);
699e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            }
700e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        }
701e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        if (parent != null && childAnims != null) {
702e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            Animator[] animsArray = new Animator[childAnims.size()];
703e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            int index = 0;
704e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            for (Animator a : childAnims) {
705e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                animsArray[index++] = a;
706e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            }
707e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            if (sequenceOrdering == TOGETHER) {
708e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                parent.playTogether(animsArray);
709e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            } else {
710e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui                parent.playSequentially(animsArray);
711e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            }
712e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        }
713e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        return anim;
714d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
715d430753cba09acb07af8b313286f247c78a41a32Chet Haase
716d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static PropertyValuesHolder[] loadValues(Resources res, Theme theme,
717d430753cba09acb07af8b313286f247c78a41a32Chet Haase            XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException {
718d430753cba09acb07af8b313286f247c78a41a32Chet Haase        ArrayList<PropertyValuesHolder> values = null;
719d430753cba09acb07af8b313286f247c78a41a32Chet Haase
720d430753cba09acb07af8b313286f247c78a41a32Chet Haase        int type;
721d430753cba09acb07af8b313286f247c78a41a32Chet Haase        while ((type = parser.getEventType()) != XmlPullParser.END_TAG &&
722d430753cba09acb07af8b313286f247c78a41a32Chet Haase                type != XmlPullParser.END_DOCUMENT) {
723d430753cba09acb07af8b313286f247c78a41a32Chet Haase
724d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (type != XmlPullParser.START_TAG) {
725d430753cba09acb07af8b313286f247c78a41a32Chet Haase                parser.next();
726d430753cba09acb07af8b313286f247c78a41a32Chet Haase                continue;
727d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
728d430753cba09acb07af8b313286f247c78a41a32Chet Haase
729d430753cba09acb07af8b313286f247c78a41a32Chet Haase            String name = parser.getName();
730d430753cba09acb07af8b313286f247c78a41a32Chet Haase
731d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (name.equals("propertyValuesHolder")) {
732d430753cba09acb07af8b313286f247c78a41a32Chet Haase                TypedArray a;
733d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (theme != null) {
734d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    a = theme.obtainStyledAttributes(attrs, R.styleable.PropertyValuesHolder, 0, 0);
735d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else {
736d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    a = res.obtainAttributes(attrs, R.styleable.PropertyValuesHolder);
737d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
738d430753cba09acb07af8b313286f247c78a41a32Chet Haase                String propertyName = a.getString(R.styleable.PropertyValuesHolder_propertyName);
739d430753cba09acb07af8b313286f247c78a41a32Chet Haase                int valueType = a.getInt(R.styleable.PropertyValuesHolder_valueType,
7409032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                        VALUE_TYPE_UNDEFINED);
7419032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu
742d430753cba09acb07af8b313286f247c78a41a32Chet Haase                PropertyValuesHolder pvh = loadPvh(res, theme, parser, propertyName, valueType);
743d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (pvh == null) {
744d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    pvh = getPVH(a, valueType,
745d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            R.styleable.PropertyValuesHolder_valueFrom,
746d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            R.styleable.PropertyValuesHolder_valueTo, propertyName);
747d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
748d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (pvh != null) {
749d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (values == null) {
750d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        values = new ArrayList<PropertyValuesHolder>();
751d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
752d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    values.add(pvh);
753d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
754d430753cba09acb07af8b313286f247c78a41a32Chet Haase                a.recycle();
755d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
756d430753cba09acb07af8b313286f247c78a41a32Chet Haase
757d430753cba09acb07af8b313286f247c78a41a32Chet Haase            parser.next();
758d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
759d430753cba09acb07af8b313286f247c78a41a32Chet Haase
760d430753cba09acb07af8b313286f247c78a41a32Chet Haase        PropertyValuesHolder[] valuesArray = null;
761d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (values != null) {
762d430753cba09acb07af8b313286f247c78a41a32Chet Haase            int count = values.size();
763d430753cba09acb07af8b313286f247c78a41a32Chet Haase            valuesArray = new PropertyValuesHolder[count];
764d430753cba09acb07af8b313286f247c78a41a32Chet Haase            for (int i = 0; i < count; ++i) {
765d430753cba09acb07af8b313286f247c78a41a32Chet Haase                valuesArray[i] = values.get(i);
766d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
767d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
768d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return valuesArray;
769d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
770d430753cba09acb07af8b313286f247c78a41a32Chet Haase
771a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu    // When no value type is provided in keyframe, we need to infer the type from the value. i.e.
772a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu    // if value is defined in the style of a color value, then the color type is returned.
773a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu    // Otherwise, default float type is returned.
774a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu    private static int inferValueTypeOfKeyframe(Resources res, Theme theme, AttributeSet attrs) {
775a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        int valueType;
776a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        TypedArray a;
777a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        if (theme != null) {
778a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu            a = theme.obtainStyledAttributes(attrs, R.styleable.Keyframe, 0, 0);
779a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        } else {
780a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu            a = res.obtainAttributes(attrs, R.styleable.Keyframe);
781a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        }
782a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu
783a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        TypedValue keyframeValue = a.peekValue(R.styleable.Keyframe_value);
784a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        boolean hasValue = (keyframeValue != null);
785a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        // When no value type is provided, check whether it's a color type first.
786a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        // If not, fall back to default value type (i.e. float type).
787a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        if (hasValue && isColorType(keyframeValue.type)) {
788a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu            valueType = VALUE_TYPE_COLOR;
789a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        } else {
790a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu            valueType = VALUE_TYPE_FLOAT;
791a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        }
792a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        a.recycle();
793a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu        return valueType;
794a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu    }
795a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu
7962787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu    private static int inferValueTypeFromValues(TypedArray styledAttributes, int valueFromId,
7972787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu            int valueToId) {
7982787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        TypedValue tvFrom = styledAttributes.peekValue(valueFromId);
7992787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        boolean hasFrom = (tvFrom != null);
8002787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        int fromType = hasFrom ? tvFrom.type : 0;
8012787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        TypedValue tvTo = styledAttributes.peekValue(valueToId);
8022787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        boolean hasTo = (tvTo != null);
8032787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        int toType = hasTo ? tvTo.type : 0;
8042787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu
8052787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        int valueType;
8062787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        // Check whether it's color type. If not, fall back to default type (i.e. float type)
8072787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        if ((hasFrom && isColorType(fromType)) || (hasTo && isColorType(toType))) {
8082787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu            valueType = VALUE_TYPE_COLOR;
8092787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        } else {
8102787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu            valueType = VALUE_TYPE_FLOAT;
8112787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        }
8122787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu        return valueType;
8132787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu    }
8142787cebfdc78b662e8742b7eb8dacf9b4a6e80c1Doris Liu
815d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static void dumpKeyframes(Object[] keyframes, String header) {
816d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (keyframes == null || keyframes.length == 0) {
817d430753cba09acb07af8b313286f247c78a41a32Chet Haase            return;
818d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
819d430753cba09acb07af8b313286f247c78a41a32Chet Haase        Log.d(TAG, header);
820d430753cba09acb07af8b313286f247c78a41a32Chet Haase        int count = keyframes.length;
821d430753cba09acb07af8b313286f247c78a41a32Chet Haase        for (int i = 0; i < count; ++i) {
822d430753cba09acb07af8b313286f247c78a41a32Chet Haase            Keyframe keyframe = (Keyframe) keyframes[i];
823d430753cba09acb07af8b313286f247c78a41a32Chet Haase            Log.d(TAG, "Keyframe " + i + ": fraction " +
824d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    (keyframe.getFraction() < 0 ? "null" : keyframe.getFraction()) + ", " +
825d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    ", value : " + ((keyframe.hasValue()) ? keyframe.getValue() : "null"));
826d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
827d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
828d430753cba09acb07af8b313286f247c78a41a32Chet Haase
8299032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu    // Load property values holder if there are keyframes defined in it. Otherwise return null.
830d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static PropertyValuesHolder loadPvh(Resources res, Theme theme, XmlPullParser parser,
831d430753cba09acb07af8b313286f247c78a41a32Chet Haase            String propertyName, int valueType)
832d430753cba09acb07af8b313286f247c78a41a32Chet Haase            throws XmlPullParserException, IOException {
833d430753cba09acb07af8b313286f247c78a41a32Chet Haase
834d430753cba09acb07af8b313286f247c78a41a32Chet Haase        PropertyValuesHolder value = null;
835d430753cba09acb07af8b313286f247c78a41a32Chet Haase        ArrayList<Keyframe> keyframes = null;
836d430753cba09acb07af8b313286f247c78a41a32Chet Haase
837d430753cba09acb07af8b313286f247c78a41a32Chet Haase        int type;
838d430753cba09acb07af8b313286f247c78a41a32Chet Haase        while ((type = parser.next()) != XmlPullParser.END_TAG &&
839d430753cba09acb07af8b313286f247c78a41a32Chet Haase                type != XmlPullParser.END_DOCUMENT) {
840d430753cba09acb07af8b313286f247c78a41a32Chet Haase            String name = parser.getName();
841d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (name.equals("keyframe")) {
842a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu                if (valueType == VALUE_TYPE_UNDEFINED) {
843a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu                    valueType = inferValueTypeOfKeyframe(res, theme, Xml.asAttributeSet(parser));
844a01fbf35de21cd595d4cda2e83b67c1238e12550Doris Liu                }
845d430753cba09acb07af8b313286f247c78a41a32Chet Haase                Keyframe keyframe = loadKeyframe(res, theme, Xml.asAttributeSet(parser), valueType);
846d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (keyframe != null) {
847d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (keyframes == null) {
848d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        keyframes = new ArrayList<Keyframe>();
849d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
850d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    keyframes.add(keyframe);
851d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
852d430753cba09acb07af8b313286f247c78a41a32Chet Haase                parser.next();
853d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
854d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
855d430753cba09acb07af8b313286f247c78a41a32Chet Haase
856d430753cba09acb07af8b313286f247c78a41a32Chet Haase        int count;
857d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (keyframes != null && (count = keyframes.size()) > 0) {
858d430753cba09acb07af8b313286f247c78a41a32Chet Haase            // make sure we have keyframes at 0 and 1
859d430753cba09acb07af8b313286f247c78a41a32Chet Haase            // If we have keyframes with set fractions, add keyframes at start/end
860d430753cba09acb07af8b313286f247c78a41a32Chet Haase            // appropriately. If start/end have no set fractions:
861d430753cba09acb07af8b313286f247c78a41a32Chet Haase            // if there's only one keyframe, set its fraction to 1 and add one at 0
862d430753cba09acb07af8b313286f247c78a41a32Chet Haase            // if >1 keyframe, set the last fraction to 1, the first fraction to 0
863d430753cba09acb07af8b313286f247c78a41a32Chet Haase            Keyframe firstKeyframe = keyframes.get(0);
864d430753cba09acb07af8b313286f247c78a41a32Chet Haase            Keyframe lastKeyframe = keyframes.get(count - 1);
865d430753cba09acb07af8b313286f247c78a41a32Chet Haase            float endFraction = lastKeyframe.getFraction();
866d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (endFraction < 1) {
867d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (endFraction < 0) {
868d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    lastKeyframe.setFraction(1);
869d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else {
870d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    keyframes.add(keyframes.size(), createNewKeyframe(lastKeyframe, 1));
871d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    ++count;
872d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
873d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
874d430753cba09acb07af8b313286f247c78a41a32Chet Haase            float startFraction = firstKeyframe.getFraction();
875d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (startFraction != 0) {
876d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (startFraction < 0) {
877d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    firstKeyframe.setFraction(0);
878d430753cba09acb07af8b313286f247c78a41a32Chet Haase                } else {
879d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    keyframes.add(0, createNewKeyframe(firstKeyframe, 0));
880d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    ++count;
881d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
882d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
883d430753cba09acb07af8b313286f247c78a41a32Chet Haase            Keyframe[] keyframeArray = new Keyframe[count];
884d430753cba09acb07af8b313286f247c78a41a32Chet Haase            keyframes.toArray(keyframeArray);
885d430753cba09acb07af8b313286f247c78a41a32Chet Haase            for (int i = 0; i < count; ++i) {
886d430753cba09acb07af8b313286f247c78a41a32Chet Haase                Keyframe keyframe = keyframeArray[i];
887d430753cba09acb07af8b313286f247c78a41a32Chet Haase                if (keyframe.getFraction() < 0) {
888d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    if (i == 0) {
889d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        keyframe.setFraction(0);
890d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else if (i == count - 1) {
891d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        keyframe.setFraction(1);
892d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    } else {
893d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        // figure out the start/end parameters of the current gap
894d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        // in fractions and distribute the gap among those keyframes
895d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        int startIndex = i;
896d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        int endIndex = i;
897d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        for (int j = startIndex + 1; j < count - 1; ++j) {
898d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            if (keyframeArray[j].getFraction() >= 0) {
899d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                break;
900d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            }
901d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            endIndex = j;
902d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        }
903d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        float gap = keyframeArray[endIndex + 1].getFraction() -
904d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                keyframeArray[startIndex - 1].getFraction();
905d430753cba09acb07af8b313286f247c78a41a32Chet Haase                        distributeKeyframes(keyframeArray, gap, startIndex, endIndex);
906d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    }
907d430753cba09acb07af8b313286f247c78a41a32Chet Haase                }
908d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
909d430753cba09acb07af8b313286f247c78a41a32Chet Haase            value = PropertyValuesHolder.ofKeyframe(propertyName, keyframeArray);
910d430753cba09acb07af8b313286f247c78a41a32Chet Haase            if (valueType == VALUE_TYPE_COLOR) {
911d430753cba09acb07af8b313286f247c78a41a32Chet Haase                value.setEvaluator(ArgbEvaluator.getInstance());
912d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
913d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
914d430753cba09acb07af8b313286f247c78a41a32Chet Haase
915d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return value;
916d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
917d430753cba09acb07af8b313286f247c78a41a32Chet Haase
918d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static Keyframe createNewKeyframe(Keyframe sampleKeyframe, float fraction) {
919d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return sampleKeyframe.getType() == float.class ?
920d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            Keyframe.ofFloat(fraction) :
921d430753cba09acb07af8b313286f247c78a41a32Chet Haase                            (sampleKeyframe.getType() == int.class) ?
922d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                    Keyframe.ofInt(fraction) :
923d430753cba09acb07af8b313286f247c78a41a32Chet Haase                                    Keyframe.ofObject(fraction);
924d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
925d430753cba09acb07af8b313286f247c78a41a32Chet Haase
926d430753cba09acb07af8b313286f247c78a41a32Chet Haase    /**
927d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * Utility function to set fractions on keyframes to cover a gap in which the
928d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * fractions are not currently set. Keyframe fractions will be distributed evenly
929d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * in this gap. For example, a gap of 1 keyframe in the range 0-1 will be at .5, a gap
930d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * of .6 spread between two keyframes will be at .2 and .4 beyond the fraction at the
931d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * keyframe before startIndex.
932d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * Assumptions:
933d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * - First and last keyframe fractions (bounding this spread) are already set. So,
934d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * for example, if no fractions are set, we will already set first and last keyframe
935d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * fraction values to 0 and 1.
936d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * - startIndex must be >0 (which follows from first assumption).
937d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * - endIndex must be >= startIndex.
938d430753cba09acb07af8b313286f247c78a41a32Chet Haase     *
939d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param keyframes the array of keyframes
940d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param gap The total gap we need to distribute
941d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param startIndex The index of the first keyframe whose fraction must be set
942d430753cba09acb07af8b313286f247c78a41a32Chet Haase     * @param endIndex The index of the last keyframe whose fraction must be set
943d430753cba09acb07af8b313286f247c78a41a32Chet Haase     */
944d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static void distributeKeyframes(Keyframe[] keyframes, float gap,
945d430753cba09acb07af8b313286f247c78a41a32Chet Haase            int startIndex, int endIndex) {
946d430753cba09acb07af8b313286f247c78a41a32Chet Haase        int count = endIndex - startIndex + 2;
947d430753cba09acb07af8b313286f247c78a41a32Chet Haase        float increment = gap / count;
948d430753cba09acb07af8b313286f247c78a41a32Chet Haase        for (int i = startIndex; i <= endIndex; ++i) {
949d430753cba09acb07af8b313286f247c78a41a32Chet Haase            keyframes[i].setFraction(keyframes[i-1].getFraction() + increment);
950d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
951d430753cba09acb07af8b313286f247c78a41a32Chet Haase    }
952d430753cba09acb07af8b313286f247c78a41a32Chet Haase
953d430753cba09acb07af8b313286f247c78a41a32Chet Haase    private static Keyframe loadKeyframe(Resources res, Theme theme, AttributeSet attrs,
954d430753cba09acb07af8b313286f247c78a41a32Chet Haase            int valueType)
955d430753cba09acb07af8b313286f247c78a41a32Chet Haase            throws XmlPullParserException, IOException {
956d430753cba09acb07af8b313286f247c78a41a32Chet Haase
957d430753cba09acb07af8b313286f247c78a41a32Chet Haase        TypedArray a;
958d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (theme != null) {
959d430753cba09acb07af8b313286f247c78a41a32Chet Haase            a = theme.obtainStyledAttributes(attrs, R.styleable.Keyframe, 0, 0);
960d430753cba09acb07af8b313286f247c78a41a32Chet Haase        } else {
961d430753cba09acb07af8b313286f247c78a41a32Chet Haase            a = res.obtainAttributes(attrs, R.styleable.Keyframe);
962d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
963d430753cba09acb07af8b313286f247c78a41a32Chet Haase
964d430753cba09acb07af8b313286f247c78a41a32Chet Haase        Keyframe keyframe = null;
965d430753cba09acb07af8b313286f247c78a41a32Chet Haase
966d430753cba09acb07af8b313286f247c78a41a32Chet Haase        float fraction = a.getFloat(R.styleable.Keyframe_fraction, -1);
967d430753cba09acb07af8b313286f247c78a41a32Chet Haase
9689032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        TypedValue keyframeValue = a.peekValue(R.styleable.Keyframe_value);
9699032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        boolean hasValue = (keyframeValue != null);
9709032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        if (valueType == VALUE_TYPE_UNDEFINED) {
9719032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            // When no value type is provided, check whether it's a color type first.
9729032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            // If not, fall back to default value type (i.e. float type).
9739032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            if (hasValue && isColorType(keyframeValue.type)) {
9749032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                valueType = VALUE_TYPE_COLOR;
9759032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            } else {
9769032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu                valueType = VALUE_TYPE_FLOAT;
9779032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu            }
9789032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu        }
979d430753cba09acb07af8b313286f247c78a41a32Chet Haase
980d430753cba09acb07af8b313286f247c78a41a32Chet Haase        if (hasValue) {
981d430753cba09acb07af8b313286f247c78a41a32Chet Haase            switch (valueType) {
982d430753cba09acb07af8b313286f247c78a41a32Chet Haase                case VALUE_TYPE_FLOAT:
983d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    float value = a.getFloat(R.styleable.Keyframe_value, 0);
984d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    keyframe = Keyframe.ofFloat(fraction, value);
985d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    break;
986d430753cba09acb07af8b313286f247c78a41a32Chet Haase                case VALUE_TYPE_COLOR:
987d430753cba09acb07af8b313286f247c78a41a32Chet Haase                case VALUE_TYPE_INT:
988d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    int intValue = a.getInt(R.styleable.Keyframe_value, 0);
989d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    keyframe = Keyframe.ofInt(fraction, intValue);
990d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    break;
991d430753cba09acb07af8b313286f247c78a41a32Chet Haase            }
992d430753cba09acb07af8b313286f247c78a41a32Chet Haase        } else {
993d430753cba09acb07af8b313286f247c78a41a32Chet Haase            keyframe = (valueType == VALUE_TYPE_FLOAT) ? Keyframe.ofFloat(fraction) :
994d430753cba09acb07af8b313286f247c78a41a32Chet Haase                    Keyframe.ofInt(fraction);
995d430753cba09acb07af8b313286f247c78a41a32Chet Haase        }
996d430753cba09acb07af8b313286f247c78a41a32Chet Haase
9976aac06ab940566020d050fdaa0d5e8d2e6c128aeDoris Liu        final int resID = a.getResourceId(R.styleable.Keyframe_interpolator, 0);
9986aac06ab940566020d050fdaa0d5e8d2e6c128aeDoris Liu        if (resID > 0) {
9996aac06ab940566020d050fdaa0d5e8d2e6c128aeDoris Liu            final Interpolator interpolator = AnimationUtils.loadInterpolator(res, theme, resID);
10006aac06ab940566020d050fdaa0d5e8d2e6c128aeDoris Liu            keyframe.setInterpolator(interpolator);
10016aac06ab940566020d050fdaa0d5e8d2e6c128aeDoris Liu        }
1002d430753cba09acb07af8b313286f247c78a41a32Chet Haase        a.recycle();
1003e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
1004d430753cba09acb07af8b313286f247c78a41a32Chet Haase        return keyframe;
1005e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    }
1006e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
1007fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount    private static ObjectAnimator loadObjectAnimator(Resources res, Theme theme, AttributeSet attrs,
10087513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            float pathErrorScale) throws NotFoundException {
1009e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        ObjectAnimator anim = new ObjectAnimator();
1010e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
10117513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu        loadAnimator(res, theme, attrs, anim, pathErrorScale);
1012e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
1013e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        return anim;
1014e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    }
1015e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
1016e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    /**
1017e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * Creates a new animation whose parameters come from the specified context
1018e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * and attributes set.
1019e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     *
1020e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @param res The resources
1021e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     * @param attrs The set of attributes holding the animation parameters
1022cf4832f69c8786b098ce18c24319021f8cd6733aztenghui     * @param anim Null if this is a ValueAnimator, otherwise this is an
1023cf4832f69c8786b098ce18c24319021f8cd6733aztenghui     *            ObjectAnimator
1024e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui     */
1025e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui    private static ValueAnimator loadAnimator(Resources res, Theme theme,
10267513aab4dfa33130fd846bf033f4878ae1c75914Doris Liu            AttributeSet attrs, ValueAnimator anim, float pathErrorScale)
1027e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui            throws NotFoundException {
1028cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        TypedArray arrayAnimator = null;
1029cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        TypedArray arrayObjectAnimator = null;
1030cf4832f69c8786b098ce18c24319021f8cd6733aztenghui
1031e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        if (theme != null) {
1032cf4832f69c8786b098ce18c24319021f8cd6733aztenghui            arrayAnimator = theme.obtainStyledAttributes(attrs, R.styleable.Animator, 0, 0);
1033e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        } else {
1034cf4832f69c8786b098ce18c24319021f8cd6733aztenghui            arrayAnimator = res.obtainAttributes(attrs, R.styleable.Animator);
1035e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui        }
1036e5e92602a41a4ddc7b42cd1c171a0edfbd09b8daztenghui
1037cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        // If anim is not null, then it is an object animator.
1038cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        if (anim != null) {
1039cf4832f69c8786b098ce18c24319021f8cd6733aztenghui            if (theme != null) {
1040cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                arrayObjectAnimator = theme.obtainStyledAttributes(attrs,
1041cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                        R.styleable.PropertyAnimator, 0, 0);
1042cf4832f69c8786b098ce18c24319021f8cd6733aztenghui            } else {
1043cf4832f69c8786b098ce18c24319021f8cd6733aztenghui                arrayObjectAnimator = res.obtainAttributes(attrs, R.styleable.PropertyAnimator);
1044cf4832f69c8786b098ce18c24319021f8cd6733aztenghui            }
1045d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            anim.appendChangingConfigurations(arrayObjectAnimator.getChangingConfigurations());
1046cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        }
10477f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout
10487f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout        if (anim == null) {
10497f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout            anim = new ValueAnimator();
10507f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout        }
1051d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        anim.appendChangingConfigurations(arrayAnimator.getChangingConfigurations());
10527f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout
1053fd3c4744f265c5277e6e2641a18d5ec3dff19f6bGeorge Mount        parseAnimatorFromTypeArray(anim, arrayAnimator, arrayObjectAnimator, pathErrorScale);
1054d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase
1055d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        final int resID = arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0);
1056d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        if (resID > 0) {
1057d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            final Interpolator interpolator = AnimationUtils.loadInterpolator(res, theme, resID);
1058d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            if (interpolator instanceof BaseInterpolator) {
1059d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                anim.appendChangingConfigurations(
1060d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar                        ((BaseInterpolator) interpolator).getChangingConfiguration());
1061d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            }
1062d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            anim.setInterpolator(interpolator);
1063d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        }
1064cf4832f69c8786b098ce18c24319021f8cd6733aztenghui
1065cf4832f69c8786b098ce18c24319021f8cd6733aztenghui        arrayAnimator.recycle();
10667f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout        if (arrayObjectAnimator != null) {
10677f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout            arrayObjectAnimator.recycle();
10687f9988f0f51e181f20fa22e17635d61893e5b74aCraig Stout        }
1069d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase        return anim;
1070d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase    }
1071d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar
1072ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette    private static @Config int getChangingConfigs(@NonNull Resources resources, @AnyRes int id) {
1073d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        synchronized (sTmpTypedValue) {
1074d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            resources.getValue(id, sTmpTypedValue, true);
1075d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar            return sTmpTypedValue.changingConfigurations;
1076d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar        }
1077d422dc358f0100106dc07d7b903201eb9b043b11Yigit Boyar    }
10789032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu
10799032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu    private static boolean isColorType(int type) {
10809032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu       return (type >= TypedValue.TYPE_FIRST_COLOR_INT) && (type <= TypedValue.TYPE_LAST_COLOR_INT);
10819032fa5808d7808f54d31d646049d0b1f4a9491bDoris Liu    }
1082d51d368f2d512ab657b8ae45780c82c0dbea94c3Chet Haase}
1083