AnimatedVectorDrawable.java revision e5e92602a41a4ddc7b42cd1c171a0edfbd09b8da
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15package android.graphics.drawable;
16
17import android.animation.Animator;
18import android.animation.AnimatorInflater;
19import android.content.res.Resources;
20import android.content.res.Resources.Theme;
21import android.content.res.TypedArray;
22import android.graphics.Canvas;
23import android.graphics.ColorFilter;
24import android.graphics.Rect;
25import android.util.AttributeSet;
26import android.util.Log;
27
28import com.android.internal.R;
29
30import org.xmlpull.v1.XmlPullParser;
31import org.xmlpull.v1.XmlPullParserException;
32
33import java.io.IOException;
34import java.util.ArrayList;
35
36/**
37 * AnimatedVectorDrawable can use ObjectAnimator and AnimatorSet to animate
38 * the property of the VectorDrawable.
39 *
40 * @hide
41 */
42public class AnimatedVectorDrawable extends Drawable implements Animatable {
43    private static final String LOGTAG = AnimatedVectorDrawable.class.getSimpleName();
44
45    private static final String ANIMATED_VECTOR = "animated-vector";
46    private static final String TARGET = "target";
47
48    private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
49
50    private final AnimatedVectorDrawableState mAnimatedVectorState;
51
52
53    public AnimatedVectorDrawable() {
54        mAnimatedVectorState = new AnimatedVectorDrawableState(
55                new AnimatedVectorDrawableState(null));
56    }
57
58    private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res,
59            Theme theme) {
60        // TODO: Correctly handle the constant state for AVD.
61        mAnimatedVectorState = new AnimatedVectorDrawableState(state);
62        if (theme != null && canApplyTheme()) {
63            applyTheme(theme);
64        }
65    }
66
67    @Override
68    public ConstantState getConstantState() {
69        return null;
70    }
71
72    @Override
73    public void draw(Canvas canvas) {
74        mAnimatedVectorState.mVectorDrawable.draw(canvas);
75        if (isRunning()) {
76            invalidateSelf();
77        }
78    }
79
80    @Override
81    protected void onBoundsChange(Rect bounds) {
82        mAnimatedVectorState.mVectorDrawable.setBounds(bounds);
83    }
84
85    @Override
86    public int getAlpha() {
87        return mAnimatedVectorState.mVectorDrawable.getAlpha();
88    }
89
90    @Override
91    public void setAlpha(int alpha) {
92        mAnimatedVectorState.mVectorDrawable.setAlpha(alpha);
93    }
94
95    @Override
96    public void setColorFilter(ColorFilter colorFilter) {
97        mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter);
98    }
99
100    @Override
101    public int getOpacity() {
102        return mAnimatedVectorState.mVectorDrawable.getOpacity();
103    }
104
105    @Override
106    public int getIntrinsicWidth() {
107        return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth();
108    }
109
110    @Override
111    public int getIntrinsicHeight() {
112        return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight();
113    }
114
115    @Override
116    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
117            throws XmlPullParserException, IOException {
118
119        int eventType = parser.getEventType();
120        while (eventType != XmlPullParser.END_DOCUMENT) {
121            if (eventType == XmlPullParser.START_TAG) {
122                final String tagName = parser.getName();
123                if (ANIMATED_VECTOR.equals(tagName)) {
124                    final TypedArray a = obtainAttributes(res, theme, attrs,
125                            R.styleable.AnimatedVectorDrawable);
126                    int drawableRes = a.getResourceId(
127                            R.styleable.AnimatedVectorDrawable_drawable, 0);
128                    if (drawableRes != 0) {
129                        mAnimatedVectorState.mVectorDrawable = (VectorDrawable) res.getDrawable(
130                                drawableRes);
131                    }
132                    a.recycle();
133                } else if (TARGET.equals(tagName)) {
134                    final TypedArray a = obtainAttributes(res, theme, attrs,
135                            R.styleable.AnimatedVectorDrawableTarget);
136                    final String target = a.getString(
137                            R.styleable.AnimatedVectorDrawableTarget_name);
138
139                    int id = a.getResourceId(
140                            R.styleable.AnimatedVectorDrawableTarget_animation, 0);
141                    if (id != 0) {
142                        Animator objectAnimator = AnimatorInflater.loadAnimator(res, theme, id);
143                        setupAnimatorsForTarget(target, objectAnimator);
144                    }
145                    a.recycle();
146                }
147            }
148
149            eventType = parser.next();
150        }
151    }
152
153    @Override
154    public boolean canApplyTheme() {
155        return super.canApplyTheme() || mAnimatedVectorState != null
156                && mAnimatedVectorState.canApplyTheme();
157    }
158
159    @Override
160    public void applyTheme(Theme t) {
161        super.applyTheme(t);
162
163        final VectorDrawable vectorDrawable = mAnimatedVectorState.mVectorDrawable;
164        if (vectorDrawable != null && vectorDrawable.canApplyTheme()) {
165            vectorDrawable.applyTheme(t);
166        }
167    }
168
169    private static class AnimatedVectorDrawableState extends ConstantState {
170        int mChangingConfigurations;
171        VectorDrawable mVectorDrawable;
172        ArrayList<Animator> mAnimators;
173
174        public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy) {
175            if (copy != null) {
176                mChangingConfigurations = copy.mChangingConfigurations;
177                // TODO: Make sure the constant state are handled correctly.
178                mVectorDrawable = new VectorDrawable();
179                mAnimators = new ArrayList<Animator>();
180            }
181        }
182
183        @Override
184        public Drawable newDrawable() {
185            return new AnimatedVectorDrawable(this, null, null);
186        }
187
188        @Override
189        public Drawable newDrawable(Resources res) {
190            return new AnimatedVectorDrawable(this, res, null);
191        }
192
193        @Override
194        public Drawable newDrawable(Resources res, Theme theme) {
195            return new AnimatedVectorDrawable(this, res, theme);
196        }
197
198        @Override
199        public int getChangingConfigurations() {
200            return mChangingConfigurations;
201        }
202    }
203
204    private void setupAnimatorsForTarget(String name, Animator animator) {
205        Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name);
206        animator.setTarget(target);
207        mAnimatedVectorState.mAnimators.add(animator);
208        if (DBG_ANIMATION_VECTOR_DRAWABLE) {
209            Log.v(LOGTAG, "add animator  for target " + name + " " + animator);
210        }
211    }
212
213    @Override
214    public boolean isRunning() {
215        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
216        final int size = animators.size();
217        for (int i = 0; i < size; i++) {
218            final Animator animator = animators.get(i);
219            if (animator.isRunning()) {
220                return true;
221            }
222        }
223        return false;
224    }
225
226    @Override
227    public void start() {
228        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
229        final int size = animators.size();
230        for (int i = 0; i < size; i++) {
231            final Animator animator = animators.get(i);
232            if (animator.isPaused()) {
233                animator.resume();
234            } else if (!animator.isRunning()) {
235                animator.start();
236            }
237        }
238        invalidateSelf();
239    }
240
241    @Override
242    public void stop() {
243        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
244        final int size = animators.size();
245        for (int i = 0; i < size; i++) {
246            final Animator animator = animators.get(i);
247            animator.pause();
248        }
249    }
250}
251