VectorDrawable.java revision bb129294700d7c31a3793717efe14b06a7bd2305
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.annotation.NonNull;
18import android.annotation.Nullable;
19import android.content.pm.ActivityInfo.Config;
20import android.content.res.ColorStateList;
21import android.content.res.ComplexColor;
22import android.content.res.GradientColor;
23import android.content.res.Resources;
24import android.content.res.Resources.Theme;
25import android.content.res.TypedArray;
26import android.graphics.Canvas;
27import android.graphics.ColorFilter;
28import android.graphics.Insets;
29import android.graphics.PixelFormat;
30import android.graphics.PorterDuff.Mode;
31import android.graphics.PorterDuffColorFilter;
32import android.graphics.Rect;
33import android.graphics.Shader;
34import android.util.ArrayMap;
35import android.util.AttributeSet;
36import android.util.DisplayMetrics;
37import android.util.FloatProperty;
38import android.util.IntProperty;
39import android.util.LayoutDirection;
40import android.util.Log;
41import android.util.PathParser;
42import android.util.Property;
43import android.util.Xml;
44
45import com.android.internal.R;
46import com.android.internal.util.VirtualRefBasePtr;
47
48import org.xmlpull.v1.XmlPullParser;
49import org.xmlpull.v1.XmlPullParserException;
50
51import java.io.IOException;
52import java.nio.ByteBuffer;
53import java.nio.ByteOrder;
54import java.util.ArrayList;
55import java.util.HashMap;
56import java.util.Stack;
57
58import dalvik.system.VMRuntime;
59
60/**
61 * This lets you create a drawable based on an XML vector graphic.
62 * <p/>
63 * <strong>Note:</strong> To optimize for the re-drawing performance, one bitmap cache is created
64 * for each VectorDrawable. Therefore, referring to the same VectorDrawable means sharing the same
65 * bitmap cache. If these references don't agree upon on the same size, the bitmap will be recreated
66 * and redrawn every time size is changed. In other words, if a VectorDrawable is used for
67 * different sizes, it is more efficient to create multiple VectorDrawables, one for each size.
68 * <p/>
69 * VectorDrawable can be defined in an XML file with the <code>&lt;vector></code> element.
70 * <p/>
71 * The vector drawable has the following elements:
72 * <p/>
73 * <dt><code>&lt;vector></code></dt>
74 * <dl>
75 * <dd>Used to define a vector drawable
76 * <dl>
77 * <dt><code>android:name</code></dt>
78 * <dd>Defines the name of this vector drawable.</dd>
79 * <dd>Animatable : No.</dd>
80 * <dt><code>android:width</code></dt>
81 * <dd>Used to define the intrinsic width of the drawable.
82 * This support all the dimension units, normally specified with dp.</dd>
83 * <dd>Animatable : No.</dd>
84 * <dt><code>android:height</code></dt>
85 * <dd>Used to define the intrinsic height the drawable.
86 * This support all the dimension units, normally specified with dp.</dd>
87 * <dd>Animatable : No.</dd>
88 * <dt><code>android:viewportWidth</code></dt>
89 * <dd>Used to define the width of the viewport space. Viewport is basically
90 * the virtual canvas where the paths are drawn on.</dd>
91 * <dd>Animatable : No.</dd>
92 * <dt><code>android:viewportHeight</code></dt>
93 * <dd>Used to define the height of the viewport space. Viewport is basically
94 * the virtual canvas where the paths are drawn on.</dd>
95 * <dd>Animatable : No.</dd>
96 * <dt><code>android:tint</code></dt>
97 * <dd>The color to apply to the drawable as a tint. By default, no tint is applied.</dd>
98 * <dd>Animatable : No.</dd>
99 * <dt><code>android:tintMode</code></dt>
100 * <dd>The Porter-Duff blending mode for the tint color. The default value is src_in.</dd>
101 * <dd>Animatable : No.</dd>
102 * <dt><code>android:autoMirrored</code></dt>
103 * <dd>Indicates if the drawable needs to be mirrored when its layout direction is
104 * RTL (right-to-left).</dd>
105 * <dd>Animatable : No.</dd>
106 * <dt><code>android:alpha</code></dt>
107 * <dd>The opacity of this drawable.</dd>
108 * <dd>Animatable : Yes.</dd>
109 * </dl></dd>
110 * </dl>
111 *
112 * <dl>
113 * <dt><code>&lt;group></code></dt>
114 * <dd>Defines a group of paths or subgroups, plus transformation information.
115 * The transformations are defined in the same coordinates as the viewport.
116 * And the transformations are applied in the order of scale, rotate then translate.
117 * <dl>
118 * <dt><code>android:name</code></dt>
119 * <dd>Defines the name of the group.</dd>
120 * <dd>Animatable : No.</dd>
121 * <dt><code>android:rotation</code></dt>
122 * <dd>The degrees of rotation of the group.</dd>
123 * <dd>Animatable : Yes.</dd>
124 * <dt><code>android:pivotX</code></dt>
125 * <dd>The X coordinate of the pivot for the scale and rotation of the group.
126 * This is defined in the viewport space.</dd>
127 * <dd>Animatable : Yes.</dd>
128 * <dt><code>android:pivotY</code></dt>
129 * <dd>The Y coordinate of the pivot for the scale and rotation of the group.
130 * This is defined in the viewport space.</dd>
131 * <dd>Animatable : Yes.</dd>
132 * <dt><code>android:scaleX</code></dt>
133 * <dd>The amount of scale on the X Coordinate.</dd>
134 * <dd>Animatable : Yes.</dd>
135 * <dt><code>android:scaleY</code></dt>
136 * <dd>The amount of scale on the Y coordinate.</dd>
137 * <dd>Animatable : Yes.</dd>
138 * <dt><code>android:translateX</code></dt>
139 * <dd>The amount of translation on the X coordinate.
140 * This is defined in the viewport space.</dd>
141 * <dd>Animatable : Yes.</dd>
142 * <dt><code>android:translateY</code></dt>
143 * <dd>The amount of translation on the Y coordinate.
144 * This is defined in the viewport space.</dd>
145 * <dd>Animatable : Yes.</dd>
146 * </dl></dd>
147 * </dl>
148 *
149 * <dl>
150 * <dt><code>&lt;path></code></dt>
151 * <dd>Defines paths to be drawn.
152 * <dl>
153 * <dt><code>android:name</code></dt>
154 * <dd>Defines the name of the path.</dd>
155 * <dd>Animatable : No.</dd>
156 * <dt><code>android:pathData</code></dt>
157 * <dd>Defines path data using exactly same format as "d" attribute
158 * in the SVG's path data. This is defined in the viewport space.</dd>
159 * <dd>Animatable : Yes.</dd>
160 * <dt><code>android:fillColor</code></dt>
161 * <dd>Specifies the color used to fill the path. May be a color or, for SDK 24+, a color state list
162 * or a gradient color (See {@link android.R.styleable#GradientColor}
163 * and {@link android.R.styleable#GradientColorItem}).
164 * If this property is animated, any value set by the animation will override the original value.
165 * No path fill is drawn if this property is not specified.</dd>
166 * <dd>Animatable : Yes.</dd>
167 * <dt><code>android:strokeColor</code></dt>
168 * <dd>Specifies the color used to draw the path outline. May be a color or, for SDK 24+, a color
169 * state list or a gradient color (See {@link android.R.styleable#GradientColor}
170 * and {@link android.R.styleable#GradientColorItem}).
171 * If this property is animated, any value set by the animation will override the original value.
172 * No path outline is drawn if this property is not specified.</dd>
173 * <dd>Animatable : Yes.</dd>
174 * <dt><code>android:strokeWidth</code></dt>
175 * <dd>The width a path stroke.</dd>
176 * <dd>Animatable : Yes.</dd>
177 * <dt><code>android:strokeAlpha</code></dt>
178 * <dd>The opacity of a path stroke.</dd>
179 * <dd>Animatable : Yes.</dd>
180 * <dt><code>android:fillAlpha</code></dt>
181 * <dd>The opacity to fill the path with.</dd>
182 * <dd>Animatable : Yes.</dd>
183 * <dt><code>android:trimPathStart</code></dt>
184 * <dd>The fraction of the path to trim from the start, in the range from 0 to 1.</dd>
185 * <dd>Animatable : Yes.</dd>
186 * <dt><code>android:trimPathEnd</code></dt>
187 * <dd>The fraction of the path to trim from the end, in the range from 0 to 1.</dd>
188 * <dd>Animatable : Yes.</dd>
189 * <dt><code>android:trimPathOffset</code></dt>
190 * <dd>Shift trim region (allows showed region to include the start and end), in the range
191 * from 0 to 1.</dd>
192 * <dd>Animatable : Yes.</dd>
193 * <dt><code>android:strokeLineCap</code></dt>
194 * <dd>Sets the linecap for a stroked path: butt, round, square.</dd>
195 * <dd>Animatable : No.</dd>
196 * <dt><code>android:strokeLineJoin</code></dt>
197 * <dd>Sets the lineJoin for a stroked path: miter,round,bevel.</dd>
198 * <dd>Animatable : No.</dd>
199 * <dt><code>android:strokeMiterLimit</code></dt>
200 * <dd>Sets the Miter limit for a stroked path.</dd>
201 * <dd>Animatable : No.</dd>
202 * <dt><code>android:fillType</code></dt>
203 * <dd>Sets the fillType for a path. The types can be either "evenOdd" or "nonZero". They behave the
204 * same as SVG's "fill-rule" properties. For more details, see
205 * <a href="https://www.w3.org/TR/SVG/painting.html#FillRuleProperty">FillRuleProperty</a></dd>
206 * <dd>Animatable : No.</dd>
207 * </dl></dd>
208 *
209 * </dl>
210 *
211 * <dl>
212 * <dt><code>&lt;clip-path></code></dt>
213 * <dd>Defines path to be the current clip. Note that the clip path only apply to
214 * the current group and its children.
215 * <dl>
216 * <dt><code>android:name</code></dt>
217 * <dd>Defines the name of the clip path.</dd>
218 * <dd>Animatable : No.</dd>
219 * <dt><code>android:pathData</code></dt>
220 * <dd>Defines clip path using the same format as "d" attribute
221 * in the SVG's path data.</dd>
222 * <dd>Animatable : Yes.</dd>
223 * </dl></dd>
224 * </dl>
225 * <li>Here is a simple VectorDrawable in this vectordrawable.xml file.
226 * <pre>
227 * &lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android";
228 *     android:height=&quot;64dp&quot;
229 *     android:width=&quot;64dp&quot;
230 *     android:viewportHeight=&quot;600&quot;
231 *     android:viewportWidth=&quot;600&quot; &gt;
232 *     &lt;group
233 *         android:name=&quot;rotationGroup&quot;
234 *         android:pivotX=&quot;300.0&quot;
235 *         android:pivotY=&quot;300.0&quot;
236 *         android:rotation=&quot;45.0&quot; &gt;
237 *         &lt;path
238 *             android:name=&quot;v&quot;
239 *             android:fillColor=&quot;#000000&quot;
240 *             android:pathData=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot; /&gt;
241 *     &lt;/group&gt;
242 * &lt;/vector&gt;
243 * </pre>
244 * </li>
245 * <li>And here is an example of linear gradient color, which is supported in SDK 24+.
246 * See more details in {@link android.R.styleable#GradientColor} and
247 * {@link android.R.styleable#GradientColorItem}.
248 * <pre>
249 * &lt;gradient xmlns:android="http://schemas.android.com/apk/res/android"
250 *     android:angle="90"
251 *     android:startColor="?android:attr/colorPrimary"
252 *     android:endColor="?android:attr/colorControlActivated"
253 *     android:centerColor="#f00"
254 *     android:startX="0"
255 *     android:startY="0"
256 *     android:endX="100"
257 *     android:endY="100"
258 *     android:type="linear"&gt;
259 * &lt;/gradient&gt;
260 * </pre>
261 * </li>
262 *
263 */
264
265public class VectorDrawable extends Drawable {
266    private static final String LOGTAG = VectorDrawable.class.getSimpleName();
267
268    private static final String SHAPE_CLIP_PATH = "clip-path";
269    private static final String SHAPE_GROUP = "group";
270    private static final String SHAPE_PATH = "path";
271    private static final String SHAPE_VECTOR = "vector";
272
273    private VectorDrawableState mVectorState;
274
275    private PorterDuffColorFilter mTintFilter;
276    private ColorFilter mColorFilter;
277
278    private boolean mMutated;
279
280    /** The density of the display on which this drawable will be rendered. */
281    private int mTargetDensity;
282
283    // Given the virtual display setup, the dpi can be different than the inflation's dpi.
284    // Therefore, we need to scale the values we got from the getDimension*().
285    private int mDpiScaledWidth = 0;
286    private int mDpiScaledHeight = 0;
287    private Insets mDpiScaledInsets = Insets.NONE;
288
289    /** Whether DPI-scaled width, height, and insets need to be updated. */
290    private boolean mDpiScaledDirty = true;
291
292    // Temp variable, only for saving "new" operation at the draw() time.
293    private final Rect mTmpBounds = new Rect();
294
295    public VectorDrawable() {
296        this(new VectorDrawableState(null), null);
297    }
298
299    /**
300     * The one constructor to rule them all. This is called by all public
301     * constructors to set the state and initialize local properties.
302     */
303    private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) {
304        mVectorState = state;
305        updateLocalState(res);
306    }
307
308    /**
309     * Initializes local dynamic properties from state. This should be called
310     * after significant state changes, e.g. from the One True Constructor and
311     * after inflating or applying a theme.
312     *
313     * @param res resources of the context in which the drawable will be
314     *            displayed, or {@code null} to use the constant state defaults
315     */
316    private void updateLocalState(Resources res) {
317        final int density = Drawable.resolveDensity(res, mVectorState.mDensity);
318        if (mTargetDensity != density) {
319            mTargetDensity = density;
320            mDpiScaledDirty = true;
321        }
322
323        mTintFilter = updateTintFilter(mTintFilter, mVectorState.mTint, mVectorState.mTintMode);
324    }
325
326    @Override
327    public Drawable mutate() {
328        if (!mMutated && super.mutate() == this) {
329            mVectorState = new VectorDrawableState(mVectorState);
330            mMutated = true;
331        }
332        return this;
333    }
334
335    /**
336     * @hide
337     */
338    public void clearMutated() {
339        super.clearMutated();
340        mMutated = false;
341    }
342
343    Object getTargetByName(String name) {
344        return mVectorState.mVGTargetsMap.get(name);
345    }
346
347    @Override
348    public ConstantState getConstantState() {
349        mVectorState.mChangingConfigurations = getChangingConfigurations();
350        return mVectorState;
351    }
352
353    @Override
354    public void draw(Canvas canvas) {
355        // We will offset the bounds for drawBitmap, so copyBounds() here instead
356        // of getBounds().
357        copyBounds(mTmpBounds);
358        if (mTmpBounds.width() <= 0 || mTmpBounds.height() <= 0) {
359            // Nothing to draw
360            return;
361        }
362
363        // Color filters always override tint filters.
364        final ColorFilter colorFilter = (mColorFilter == null ? mTintFilter : mColorFilter);
365        final long colorFilterNativeInstance = colorFilter == null ? 0 :
366                colorFilter.native_instance;
367        boolean canReuseCache = mVectorState.canReuseCache();
368        int pixelCount = nDraw(mVectorState.getNativeRenderer(), canvas.getNativeCanvasWrapper(),
369                colorFilterNativeInstance, mTmpBounds, needMirroring(),
370                canReuseCache);
371        if (pixelCount == 0) {
372            // Invalid canvas matrix or drawable bounds. This would not affect existing bitmap
373            // cache, if any.
374            return;
375        }
376
377        int deltaInBytes;
378        // Track different bitmap cache based whether the canvas is hw accelerated. By doing so,
379        // we don't over count bitmap cache allocation: if the input canvas is always of the same
380        // type, only one bitmap cache is allocated.
381        if (canvas.isHardwareAccelerated()) {
382            // Each pixel takes 4 bytes.
383            deltaInBytes = (pixelCount - mVectorState.mLastHWCachePixelCount) * 4;
384            mVectorState.mLastHWCachePixelCount = pixelCount;
385        } else {
386            // Each pixel takes 4 bytes.
387            deltaInBytes = (pixelCount - mVectorState.mLastSWCachePixelCount) * 4;
388            mVectorState.mLastSWCachePixelCount = pixelCount;
389        }
390        if (deltaInBytes > 0) {
391            VMRuntime.getRuntime().registerNativeAllocation(deltaInBytes);
392        } else if (deltaInBytes < 0) {
393            VMRuntime.getRuntime().registerNativeFree(-deltaInBytes);
394        }
395    }
396
397
398    @Override
399    public int getAlpha() {
400        return (int) (mVectorState.getAlpha() * 255);
401    }
402
403    @Override
404    public void setAlpha(int alpha) {
405        if (mVectorState.setAlpha(alpha / 255f)) {
406            invalidateSelf();
407        }
408    }
409
410    @Override
411    public void setColorFilter(ColorFilter colorFilter) {
412        mColorFilter = colorFilter;
413        invalidateSelf();
414    }
415
416    @Override
417    public ColorFilter getColorFilter() {
418        return mColorFilter;
419    }
420
421    @Override
422    public void setTintList(ColorStateList tint) {
423        final VectorDrawableState state = mVectorState;
424        if (state.mTint != tint) {
425            state.mTint = tint;
426            mTintFilter = updateTintFilter(mTintFilter, tint, state.mTintMode);
427            invalidateSelf();
428        }
429    }
430
431    @Override
432    public void setTintMode(Mode tintMode) {
433        final VectorDrawableState state = mVectorState;
434        if (state.mTintMode != tintMode) {
435            state.mTintMode = tintMode;
436            mTintFilter = updateTintFilter(mTintFilter, state.mTint, tintMode);
437            invalidateSelf();
438        }
439    }
440
441    @Override
442    public boolean isStateful() {
443        return super.isStateful() || (mVectorState != null && mVectorState.isStateful());
444    }
445
446    @Override
447    protected boolean onStateChange(int[] stateSet) {
448        boolean changed = false;
449
450        // When the VD is stateful, we need to mutate the drawable such that we don't share the
451        // cache bitmap with others. Such that the state change only affect this new cached bitmap.
452        if (isStateful()) {
453            mutate();
454        }
455        final VectorDrawableState state = mVectorState;
456        if (state.onStateChange(stateSet)) {
457            changed = true;
458            state.mCacheDirty = true;
459        }
460        if (state.mTint != null && state.mTintMode != null) {
461            mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
462            changed = true;
463        }
464
465        return changed;
466    }
467
468    @Override
469    public int getOpacity() {
470        // We can't tell whether the drawable is fully opaque unless we examine all the pixels,
471        // but we could tell it is transparent if the root alpha is 0.
472        return getAlpha() == 0 ? PixelFormat.TRANSPARENT : PixelFormat.TRANSLUCENT;
473    }
474
475    @Override
476    public int getIntrinsicWidth() {
477        if (mDpiScaledDirty) {
478            computeVectorSize();
479        }
480        return mDpiScaledWidth;
481    }
482
483    @Override
484    public int getIntrinsicHeight() {
485        if (mDpiScaledDirty) {
486            computeVectorSize();
487        }
488        return mDpiScaledHeight;
489    }
490
491    /** @hide */
492    @Override
493    public Insets getOpticalInsets() {
494        if (mDpiScaledDirty) {
495            computeVectorSize();
496        }
497        return mDpiScaledInsets;
498    }
499
500    /*
501     * Update local dimensions to adjust for a target density that may differ
502     * from the source density against which the constant state was loaded.
503     */
504    void computeVectorSize() {
505        final Insets opticalInsets = mVectorState.mOpticalInsets;
506
507        final int sourceDensity = mVectorState.mDensity;
508        final int targetDensity = mTargetDensity;
509        if (targetDensity != sourceDensity) {
510            mDpiScaledWidth = Drawable.scaleFromDensity(
511                    (int) mVectorState.mBaseWidth, sourceDensity, targetDensity, true);
512            mDpiScaledHeight = Drawable.scaleFromDensity(
513                    (int) mVectorState.mBaseHeight,sourceDensity, targetDensity, true);
514            final int left = Drawable.scaleFromDensity(
515                    opticalInsets.left, sourceDensity, targetDensity, false);
516            final int right = Drawable.scaleFromDensity(
517                    opticalInsets.right, sourceDensity, targetDensity, false);
518            final int top = Drawable.scaleFromDensity(
519                    opticalInsets.top, sourceDensity, targetDensity, false);
520            final int bottom = Drawable.scaleFromDensity(
521                    opticalInsets.bottom, sourceDensity, targetDensity, false);
522            mDpiScaledInsets = Insets.of(left, top, right, bottom);
523        } else {
524            mDpiScaledWidth = (int) mVectorState.mBaseWidth;
525            mDpiScaledHeight = (int) mVectorState.mBaseHeight;
526            mDpiScaledInsets = opticalInsets;
527        }
528
529        mDpiScaledDirty = false;
530    }
531
532    @Override
533    public boolean canApplyTheme() {
534        return (mVectorState != null && mVectorState.canApplyTheme()) || super.canApplyTheme();
535    }
536
537    @Override
538    public void applyTheme(Theme t) {
539        super.applyTheme(t);
540
541        final VectorDrawableState state = mVectorState;
542        if (state == null) {
543            return;
544        }
545
546        final boolean changedDensity = mVectorState.setDensity(
547                Drawable.resolveDensity(t.getResources(), 0));
548        mDpiScaledDirty |= changedDensity;
549
550        if (state.mThemeAttrs != null) {
551            final TypedArray a = t.resolveAttributes(
552                    state.mThemeAttrs, R.styleable.VectorDrawable);
553            try {
554                state.mCacheDirty = true;
555                updateStateFromTypedArray(a);
556            } catch (XmlPullParserException e) {
557                throw new RuntimeException(e);
558            } finally {
559                a.recycle();
560            }
561
562            // May have changed size.
563            mDpiScaledDirty = true;
564        }
565
566        // Apply theme to contained color state list.
567        if (state.mTint != null && state.mTint.canApplyTheme()) {
568            state.mTint = state.mTint.obtainForTheme(t);
569        }
570
571        if (mVectorState != null && mVectorState.canApplyTheme()) {
572            mVectorState.applyTheme(t);
573        }
574
575        // Update local properties.
576        updateLocalState(t.getResources());
577    }
578
579    /**
580     * The size of a pixel when scaled from the intrinsic dimension to the viewport dimension.
581     * This is used to calculate the path animation accuracy.
582     *
583     * @hide
584     */
585    public float getPixelSize() {
586        if (mVectorState == null ||
587                mVectorState.mBaseWidth == 0 ||
588                mVectorState.mBaseHeight == 0 ||
589                mVectorState.mViewportHeight == 0 ||
590                mVectorState.mViewportWidth == 0) {
591            return 1; // fall back to 1:1 pixel mapping.
592        }
593        float intrinsicWidth = mVectorState.mBaseWidth;
594        float intrinsicHeight = mVectorState.mBaseHeight;
595        float viewportWidth = mVectorState.mViewportWidth;
596        float viewportHeight = mVectorState.mViewportHeight;
597        float scaleX = viewportWidth / intrinsicWidth;
598        float scaleY = viewportHeight / intrinsicHeight;
599        return Math.min(scaleX, scaleY);
600    }
601
602    /** @hide */
603    public static VectorDrawable create(Resources resources, int rid) {
604        try {
605            final XmlPullParser parser = resources.getXml(rid);
606            final AttributeSet attrs = Xml.asAttributeSet(parser);
607            int type;
608            while ((type=parser.next()) != XmlPullParser.START_TAG &&
609                    type != XmlPullParser.END_DOCUMENT) {
610                // Empty loop
611            }
612            if (type != XmlPullParser.START_TAG) {
613                throw new XmlPullParserException("No start tag found");
614            }
615
616            final VectorDrawable drawable = new VectorDrawable();
617            drawable.inflate(resources, parser, attrs);
618
619            return drawable;
620        } catch (XmlPullParserException e) {
621            Log.e(LOGTAG, "parser error", e);
622        } catch (IOException e) {
623            Log.e(LOGTAG, "parser error", e);
624        }
625        return null;
626    }
627
628    @Override
629    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
630            @NonNull AttributeSet attrs, @Nullable Theme theme)
631            throws XmlPullParserException, IOException {
632        if (mVectorState.mRootGroup != null || mVectorState.mNativeTree != null) {
633            // This VD has been used to display other VD resource content, clean up.
634            if (mVectorState.mRootGroup != null) {
635                // Subtract the native allocation for all the nodes.
636                VMRuntime.getRuntime().registerNativeFree(mVectorState.mRootGroup.getNativeSize());
637                // Remove child nodes' reference to tree
638                mVectorState.mRootGroup.setTree(null);
639            }
640            mVectorState.mRootGroup = new VGroup();
641            if (mVectorState.mNativeTree != null) {
642                // Subtract the native allocation for the tree wrapper, which contains root node
643                // as well as rendering related data.
644                VMRuntime.getRuntime().registerNativeFree(mVectorState.NATIVE_ALLOCATION_SIZE);
645                mVectorState.mNativeTree.release();
646            }
647            mVectorState.createNativeTree(mVectorState.mRootGroup);
648        }
649        final VectorDrawableState state = mVectorState;
650        state.setDensity(Drawable.resolveDensity(r, 0));
651
652        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawable);
653        updateStateFromTypedArray(a);
654        a.recycle();
655
656        mDpiScaledDirty = true;
657
658        state.mCacheDirty = true;
659        inflateChildElements(r, parser, attrs, theme);
660
661        state.onTreeConstructionFinished();
662        // Update local properties.
663        updateLocalState(r);
664    }
665
666    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
667        final VectorDrawableState state = mVectorState;
668
669        // Account for any configuration changes.
670        state.mChangingConfigurations |= a.getChangingConfigurations();
671
672        // Extract the theme attributes, if any.
673        state.mThemeAttrs = a.extractThemeAttrs();
674
675        final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
676        if (tintMode != -1) {
677            state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
678        }
679
680        final ColorStateList tint = a.getColorStateList(R.styleable.VectorDrawable_tint);
681        if (tint != null) {
682            state.mTint = tint;
683        }
684
685        state.mAutoMirrored = a.getBoolean(
686                R.styleable.VectorDrawable_autoMirrored, state.mAutoMirrored);
687
688        float viewportWidth = a.getFloat(
689                R.styleable.VectorDrawable_viewportWidth, state.mViewportWidth);
690        float viewportHeight = a.getFloat(
691                R.styleable.VectorDrawable_viewportHeight, state.mViewportHeight);
692        state.setViewportSize(viewportWidth, viewportHeight);
693
694        if (state.mViewportWidth <= 0) {
695            throw new XmlPullParserException(a.getPositionDescription() +
696                    "<vector> tag requires viewportWidth > 0");
697        } else if (state.mViewportHeight <= 0) {
698            throw new XmlPullParserException(a.getPositionDescription() +
699                    "<vector> tag requires viewportHeight > 0");
700        }
701
702        state.mBaseWidth = a.getDimension(
703                R.styleable.VectorDrawable_width, state.mBaseWidth);
704        state.mBaseHeight = a.getDimension(
705                R.styleable.VectorDrawable_height, state.mBaseHeight);
706
707        if (state.mBaseWidth <= 0) {
708            throw new XmlPullParserException(a.getPositionDescription() +
709                    "<vector> tag requires width > 0");
710        } else if (state.mBaseHeight <= 0) {
711            throw new XmlPullParserException(a.getPositionDescription() +
712                    "<vector> tag requires height > 0");
713        }
714
715        final int insetLeft = a.getDimensionPixelOffset(
716                R.styleable.VectorDrawable_opticalInsetLeft, state.mOpticalInsets.left);
717        final int insetTop = a.getDimensionPixelOffset(
718                R.styleable.VectorDrawable_opticalInsetTop, state.mOpticalInsets.top);
719        final int insetRight = a.getDimensionPixelOffset(
720                R.styleable.VectorDrawable_opticalInsetRight, state.mOpticalInsets.right);
721        final int insetBottom = a.getDimensionPixelOffset(
722                R.styleable.VectorDrawable_opticalInsetBottom, state.mOpticalInsets.bottom);
723        state.mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
724
725        final float alphaInFloat = a.getFloat(
726                R.styleable.VectorDrawable_alpha, state.getAlpha());
727        state.setAlpha(alphaInFloat);
728
729        final String name = a.getString(R.styleable.VectorDrawable_name);
730        if (name != null) {
731            state.mRootName = name;
732            state.mVGTargetsMap.put(name, state);
733        }
734    }
735
736    private void inflateChildElements(Resources res, XmlPullParser parser, AttributeSet attrs,
737            Theme theme) throws XmlPullParserException, IOException {
738        final VectorDrawableState state = mVectorState;
739        boolean noPathTag = true;
740
741        // Use a stack to help to build the group tree.
742        // The top of the stack is always the current group.
743        final Stack<VGroup> groupStack = new Stack<VGroup>();
744        groupStack.push(state.mRootGroup);
745
746        int eventType = parser.getEventType();
747        while (eventType != XmlPullParser.END_DOCUMENT) {
748            if (eventType == XmlPullParser.START_TAG) {
749                final String tagName = parser.getName();
750                final VGroup currentGroup = groupStack.peek();
751
752                if (SHAPE_PATH.equals(tagName)) {
753                    final VFullPath path = new VFullPath();
754                    path.inflate(res, attrs, theme);
755                    currentGroup.addChild(path);
756                    if (path.getPathName() != null) {
757                        state.mVGTargetsMap.put(path.getPathName(), path);
758                    }
759                    noPathTag = false;
760                    state.mChangingConfigurations |= path.mChangingConfigurations;
761                } else if (SHAPE_CLIP_PATH.equals(tagName)) {
762                    final VClipPath path = new VClipPath();
763                    path.inflate(res, attrs, theme);
764                    currentGroup.addChild(path);
765                    if (path.getPathName() != null) {
766                        state.mVGTargetsMap.put(path.getPathName(), path);
767                    }
768                    state.mChangingConfigurations |= path.mChangingConfigurations;
769                } else if (SHAPE_GROUP.equals(tagName)) {
770                    VGroup newChildGroup = new VGroup();
771                    newChildGroup.inflate(res, attrs, theme);
772                    currentGroup.addChild(newChildGroup);
773                    groupStack.push(newChildGroup);
774                    if (newChildGroup.getGroupName() != null) {
775                        state.mVGTargetsMap.put(newChildGroup.getGroupName(),
776                                newChildGroup);
777                    }
778                    state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
779                }
780            } else if (eventType == XmlPullParser.END_TAG) {
781                final String tagName = parser.getName();
782                if (SHAPE_GROUP.equals(tagName)) {
783                    groupStack.pop();
784                }
785            }
786            eventType = parser.next();
787        }
788
789        if (noPathTag) {
790            final StringBuffer tag = new StringBuffer();
791
792            if (tag.length() > 0) {
793                tag.append(" or ");
794            }
795            tag.append(SHAPE_PATH);
796
797            throw new XmlPullParserException("no " + tag + " defined");
798        }
799    }
800
801    @Override
802    public @Config int getChangingConfigurations() {
803        return super.getChangingConfigurations() | mVectorState.getChangingConfigurations();
804    }
805
806    void setAllowCaching(boolean allowCaching) {
807        nSetAllowCaching(mVectorState.getNativeRenderer(), allowCaching);
808    }
809
810    private boolean needMirroring() {
811        return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
812    }
813
814    @Override
815    public void setAutoMirrored(boolean mirrored) {
816        if (mVectorState.mAutoMirrored != mirrored) {
817            mVectorState.mAutoMirrored = mirrored;
818            invalidateSelf();
819        }
820    }
821
822    @Override
823    public boolean isAutoMirrored() {
824        return mVectorState.mAutoMirrored;
825    }
826
827    /**
828     * @hide
829     */
830    public long getNativeTree() {
831        return mVectorState.getNativeRenderer();
832    }
833
834    static class VectorDrawableState extends ConstantState {
835        // Variables below need to be copied (deep copy if applicable) for mutation.
836        int[] mThemeAttrs;
837        @Config int mChangingConfigurations;
838        ColorStateList mTint = null;
839        Mode mTintMode = DEFAULT_TINT_MODE;
840        boolean mAutoMirrored;
841
842        float mBaseWidth = 0;
843        float mBaseHeight = 0;
844        float mViewportWidth = 0;
845        float mViewportHeight = 0;
846        Insets mOpticalInsets = Insets.NONE;
847        String mRootName = null;
848        VGroup mRootGroup;
849        VirtualRefBasePtr mNativeTree = null;
850
851        int mDensity = DisplayMetrics.DENSITY_DEFAULT;
852        final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
853
854        // Fields for cache
855        int[] mCachedThemeAttrs;
856        ColorStateList mCachedTint;
857        Mode mCachedTintMode;
858        boolean mCachedAutoMirrored;
859        boolean mCacheDirty;
860
861        // Since sw canvas and hw canvas uses different bitmap caches, we track the allocation of
862        // these bitmaps separately.
863        int mLastSWCachePixelCount = 0;
864        int mLastHWCachePixelCount = 0;
865
866        final static Property<VectorDrawableState, Float> ALPHA =
867                new FloatProperty<VectorDrawableState>("alpha") {
868                    @Override
869                    public void setValue(VectorDrawableState state, float value) {
870                        state.setAlpha(value);
871                    }
872
873                    @Override
874                    public Float get(VectorDrawableState state) {
875                        return state.getAlpha();
876                    }
877                };
878
879        Property getProperty(String propertyName) {
880            if (ALPHA.getName().equals(propertyName)) {
881                return ALPHA;
882            }
883            return null;
884        }
885
886        // This tracks the total native allocation for all the nodes.
887        private int mAllocationOfAllNodes = 0;
888
889        private static final int NATIVE_ALLOCATION_SIZE = 316;
890
891        // If copy is not null, deep copy the given VectorDrawableState. Otherwise, create a
892        // native vector drawable tree with an empty root group.
893        public VectorDrawableState(VectorDrawableState copy) {
894            if (copy != null) {
895                mThemeAttrs = copy.mThemeAttrs;
896                mChangingConfigurations = copy.mChangingConfigurations;
897                mTint = copy.mTint;
898                mTintMode = copy.mTintMode;
899                mAutoMirrored = copy.mAutoMirrored;
900                mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
901                createNativeTreeFromCopy(copy, mRootGroup);
902
903                mBaseWidth = copy.mBaseWidth;
904                mBaseHeight = copy.mBaseHeight;
905                setViewportSize(copy.mViewportWidth, copy.mViewportHeight);
906                mOpticalInsets = copy.mOpticalInsets;
907
908                mRootName = copy.mRootName;
909                mDensity = copy.mDensity;
910                if (copy.mRootName != null) {
911                    mVGTargetsMap.put(copy.mRootName, this);
912                }
913            } else {
914                mRootGroup = new VGroup();
915                createNativeTree(mRootGroup);
916            }
917            onTreeConstructionFinished();
918        }
919
920        private void createNativeTree(VGroup rootGroup) {
921            mNativeTree = new VirtualRefBasePtr(nCreateTree(rootGroup.mNativePtr));
922            // Register tree size
923            VMRuntime.getRuntime().registerNativeAllocation(NATIVE_ALLOCATION_SIZE);
924        }
925
926        // Create a new native tree with the given root group, and copy the properties from the
927        // given VectorDrawableState's native tree.
928        private void createNativeTreeFromCopy(VectorDrawableState copy, VGroup rootGroup) {
929            mNativeTree = new VirtualRefBasePtr(nCreateTreeFromCopy(
930                    copy.mNativeTree.get(), rootGroup.mNativePtr));
931            // Register tree size
932            VMRuntime.getRuntime().registerNativeAllocation(NATIVE_ALLOCATION_SIZE);
933        }
934
935        // This should be called every time after a new RootGroup and all its subtrees are created
936        // (i.e. in constructors of VectorDrawableState and in inflate).
937        void onTreeConstructionFinished() {
938            mRootGroup.setTree(mNativeTree);
939            mAllocationOfAllNodes = mRootGroup.getNativeSize();
940            VMRuntime.getRuntime().registerNativeAllocation(mAllocationOfAllNodes);
941        }
942
943        long getNativeRenderer() {
944            if (mNativeTree == null) {
945                return 0;
946            }
947            return mNativeTree.get();
948        }
949
950        public boolean canReuseCache() {
951            if (!mCacheDirty
952                    && mCachedThemeAttrs == mThemeAttrs
953                    && mCachedTint == mTint
954                    && mCachedTintMode == mTintMode
955                    && mCachedAutoMirrored == mAutoMirrored) {
956                return true;
957            }
958            updateCacheStates();
959            return false;
960        }
961
962        public void updateCacheStates() {
963            // Use shallow copy here and shallow comparison in canReuseCache(),
964            // likely hit cache miss more, but practically not much difference.
965            mCachedThemeAttrs = mThemeAttrs;
966            mCachedTint = mTint;
967            mCachedTintMode = mTintMode;
968            mCachedAutoMirrored = mAutoMirrored;
969            mCacheDirty = false;
970        }
971
972        public void applyTheme(Theme t) {
973            mRootGroup.applyTheme(t);
974        }
975
976        @Override
977        public boolean canApplyTheme() {
978            return mThemeAttrs != null
979                    || (mRootGroup != null && mRootGroup.canApplyTheme())
980                    || (mTint != null && mTint.canApplyTheme())
981                    || super.canApplyTheme();
982        }
983
984        @Override
985        public Drawable newDrawable() {
986            return new VectorDrawable(this, null);
987        }
988
989        @Override
990        public Drawable newDrawable(Resources res) {
991            return new VectorDrawable(this, res);
992        }
993
994        @Override
995        public @Config int getChangingConfigurations() {
996            return mChangingConfigurations
997                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
998        }
999
1000        public boolean isStateful() {
1001            return (mTint != null && mTint.isStateful())
1002                    || (mRootGroup != null && mRootGroup.isStateful());
1003        }
1004
1005        void setViewportSize(float viewportWidth, float viewportHeight) {
1006            mViewportWidth = viewportWidth;
1007            mViewportHeight = viewportHeight;
1008            nSetRendererViewportSize(getNativeRenderer(), viewportWidth, viewportHeight);
1009        }
1010
1011        public final boolean setDensity(int targetDensity) {
1012            if (mDensity != targetDensity) {
1013                final int sourceDensity = mDensity;
1014                mDensity = targetDensity;
1015                applyDensityScaling(sourceDensity, targetDensity);
1016                return true;
1017            }
1018            return false;
1019        }
1020
1021        private void applyDensityScaling(int sourceDensity, int targetDensity) {
1022            mBaseWidth = Drawable.scaleFromDensity(mBaseWidth, sourceDensity, targetDensity);
1023            mBaseHeight = Drawable.scaleFromDensity(mBaseHeight, sourceDensity, targetDensity);
1024
1025            final int insetLeft = Drawable.scaleFromDensity(
1026                    mOpticalInsets.left, sourceDensity, targetDensity, false);
1027            final int insetTop = Drawable.scaleFromDensity(
1028                    mOpticalInsets.top, sourceDensity, targetDensity, false);
1029            final int insetRight = Drawable.scaleFromDensity(
1030                    mOpticalInsets.right, sourceDensity, targetDensity, false);
1031            final int insetBottom = Drawable.scaleFromDensity(
1032                    mOpticalInsets.bottom, sourceDensity, targetDensity, false);
1033            mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
1034        }
1035
1036        public boolean onStateChange(int[] stateSet) {
1037            return mRootGroup.onStateChange(stateSet);
1038        }
1039
1040        @Override
1041        public void finalize() throws Throwable {
1042            super.finalize();
1043            int bitmapCacheSize = mLastHWCachePixelCount * 4 + mLastSWCachePixelCount * 4;
1044            VMRuntime.getRuntime().registerNativeFree(NATIVE_ALLOCATION_SIZE
1045                    + mAllocationOfAllNodes + bitmapCacheSize);
1046        }
1047
1048        /**
1049         * setAlpha() and getAlpha() are used mostly for animation purpose. Return true if alpha
1050         * has changed.
1051         */
1052        public boolean setAlpha(float alpha) {
1053            return nSetRootAlpha(mNativeTree.get(), alpha);
1054        }
1055
1056        @SuppressWarnings("unused")
1057        public float getAlpha() {
1058            return nGetRootAlpha(mNativeTree.get());
1059        }
1060    }
1061
1062    static class VGroup extends VObject {
1063        private static final int ROTATION_INDEX = 0;
1064        private static final int PIVOT_X_INDEX = 1;
1065        private static final int PIVOT_Y_INDEX = 2;
1066        private static final int SCALE_X_INDEX = 3;
1067        private static final int SCALE_Y_INDEX = 4;
1068        private static final int TRANSLATE_X_INDEX = 5;
1069        private static final int TRANSLATE_Y_INDEX = 6;
1070        private static final int TRANSFORM_PROPERTY_COUNT = 7;
1071
1072        private static final int NATIVE_ALLOCATION_SIZE = 100;
1073
1074        private static final HashMap<String, Integer> sPropertyIndexMap =
1075                new HashMap<String, Integer>() {
1076                    {
1077                        put("translateX", TRANSLATE_X_INDEX);
1078                        put("translateY", TRANSLATE_Y_INDEX);
1079                        put("scaleX", SCALE_X_INDEX);
1080                        put("scaleY", SCALE_Y_INDEX);
1081                        put("pivotX", PIVOT_X_INDEX);
1082                        put("pivotY", PIVOT_Y_INDEX);
1083                        put("rotation", ROTATION_INDEX);
1084                    }
1085                };
1086
1087        static int getPropertyIndex(String propertyName) {
1088            if (sPropertyIndexMap.containsKey(propertyName)) {
1089                return sPropertyIndexMap.get(propertyName);
1090            } else {
1091                // property not found
1092                return -1;
1093            }
1094        }
1095
1096        // Below are the Properties that wrap the setters to avoid reflection overhead in animations
1097        private static final Property<VGroup, Float> TRANSLATE_X =
1098                new FloatProperty<VGroup> ("translateX") {
1099                    @Override
1100                    public void setValue(VGroup object, float value) {
1101                        object.setTranslateX(value);
1102                    }
1103
1104                    @Override
1105                    public Float get(VGroup object) {
1106                        return object.getTranslateX();
1107                    }
1108                };
1109
1110        private static final Property<VGroup, Float> TRANSLATE_Y =
1111                new FloatProperty<VGroup> ("translateY") {
1112                    @Override
1113                    public void setValue(VGroup object, float value) {
1114                        object.setTranslateY(value);
1115                    }
1116
1117                    @Override
1118                    public Float get(VGroup object) {
1119                        return object.getTranslateY();
1120                    }
1121        };
1122
1123        private static final Property<VGroup, Float> SCALE_X =
1124                new FloatProperty<VGroup> ("scaleX") {
1125                    @Override
1126                    public void setValue(VGroup object, float value) {
1127                        object.setScaleX(value);
1128                    }
1129
1130                    @Override
1131                    public Float get(VGroup object) {
1132                        return object.getScaleX();
1133                    }
1134                };
1135
1136        private static final Property<VGroup, Float> SCALE_Y =
1137                new FloatProperty<VGroup> ("scaleY") {
1138                    @Override
1139                    public void setValue(VGroup object, float value) {
1140                        object.setScaleY(value);
1141                    }
1142
1143                    @Override
1144                    public Float get(VGroup object) {
1145                        return object.getScaleY();
1146                    }
1147                };
1148
1149        private static final Property<VGroup, Float> PIVOT_X =
1150                new FloatProperty<VGroup> ("pivotX") {
1151                    @Override
1152                    public void setValue(VGroup object, float value) {
1153                        object.setPivotX(value);
1154                    }
1155
1156                    @Override
1157                    public Float get(VGroup object) {
1158                        return object.getPivotX();
1159                    }
1160                };
1161
1162        private static final Property<VGroup, Float> PIVOT_Y =
1163                new FloatProperty<VGroup> ("pivotY") {
1164                    @Override
1165                    public void setValue(VGroup object, float value) {
1166                        object.setPivotY(value);
1167                    }
1168
1169                    @Override
1170                    public Float get(VGroup object) {
1171                        return object.getPivotY();
1172                    }
1173                };
1174
1175        private static final Property<VGroup, Float> ROTATION =
1176                new FloatProperty<VGroup> ("rotation") {
1177                    @Override
1178                    public void setValue(VGroup object, float value) {
1179                        object.setRotation(value);
1180                    }
1181
1182                    @Override
1183                    public Float get(VGroup object) {
1184                        return object.getRotation();
1185                    }
1186                };
1187
1188        private static final HashMap<String, Property> sPropertyMap =
1189                new HashMap<String, Property>() {
1190                    {
1191                        put("translateX", TRANSLATE_X);
1192                        put("translateY", TRANSLATE_Y);
1193                        put("scaleX", SCALE_X);
1194                        put("scaleY", SCALE_Y);
1195                        put("pivotX", PIVOT_X);
1196                        put("pivotY", PIVOT_Y);
1197                        put("rotation", ROTATION);
1198                    }
1199                };
1200        // Temp array to store transform values obtained from native.
1201        private float[] mTransform;
1202        /////////////////////////////////////////////////////
1203        // Variables below need to be copied (deep copy if applicable) for mutation.
1204        private final ArrayList<VObject> mChildren = new ArrayList<>();
1205        private boolean mIsStateful;
1206
1207        // mLocalMatrix is updated based on the update of transformation information,
1208        // either parsed from the XML or by animation.
1209        private @Config int mChangingConfigurations;
1210        private int[] mThemeAttrs;
1211        private String mGroupName = null;
1212
1213        // The native object will be created in the constructor and will be destroyed in native
1214        // when the neither java nor native has ref to the tree. This pointer should be valid
1215        // throughout this VGroup Java object's life.
1216        private final long mNativePtr;
1217        public VGroup(VGroup copy, ArrayMap<String, Object> targetsMap) {
1218
1219            mIsStateful = copy.mIsStateful;
1220            mThemeAttrs = copy.mThemeAttrs;
1221            mGroupName = copy.mGroupName;
1222            mChangingConfigurations = copy.mChangingConfigurations;
1223            if (mGroupName != null) {
1224                targetsMap.put(mGroupName, this);
1225            }
1226            mNativePtr = nCreateGroup(copy.mNativePtr);
1227
1228            final ArrayList<VObject> children = copy.mChildren;
1229            for (int i = 0; i < children.size(); i++) {
1230                final VObject copyChild = children.get(i);
1231                if (copyChild instanceof VGroup) {
1232                    final VGroup copyGroup = (VGroup) copyChild;
1233                    addChild(new VGroup(copyGroup, targetsMap));
1234                } else {
1235                    final VPath newPath;
1236                    if (copyChild instanceof VFullPath) {
1237                        newPath = new VFullPath((VFullPath) copyChild);
1238                    } else if (copyChild instanceof VClipPath) {
1239                        newPath = new VClipPath((VClipPath) copyChild);
1240                    } else {
1241                        throw new IllegalStateException("Unknown object in the tree!");
1242                    }
1243                    addChild(newPath);
1244                    if (newPath.mPathName != null) {
1245                        targetsMap.put(newPath.mPathName, newPath);
1246                    }
1247                }
1248            }
1249        }
1250
1251        public VGroup() {
1252            mNativePtr = nCreateGroup();
1253        }
1254
1255        Property getProperty(String propertyName) {
1256            if (sPropertyMap.containsKey(propertyName)) {
1257                return sPropertyMap.get(propertyName);
1258            } else {
1259                // property not found
1260                return null;
1261            }
1262        }
1263
1264        public String getGroupName() {
1265            return mGroupName;
1266        }
1267
1268        public void addChild(VObject child) {
1269            nAddChild(mNativePtr, child.getNativePtr());
1270            mChildren.add(child);
1271            mIsStateful |= child.isStateful();
1272        }
1273
1274        @Override
1275        public void setTree(VirtualRefBasePtr treeRoot) {
1276            super.setTree(treeRoot);
1277            for (int i = 0; i < mChildren.size(); i++) {
1278                mChildren.get(i).setTree(treeRoot);
1279            }
1280        }
1281
1282        @Override
1283        public long getNativePtr() {
1284            return mNativePtr;
1285        }
1286
1287        @Override
1288        public void inflate(Resources res, AttributeSet attrs, Theme theme) {
1289            final TypedArray a = obtainAttributes(res, theme, attrs,
1290                    R.styleable.VectorDrawableGroup);
1291            updateStateFromTypedArray(a);
1292            a.recycle();
1293        }
1294
1295        void updateStateFromTypedArray(TypedArray a) {
1296            // Account for any configuration changes.
1297            mChangingConfigurations |= a.getChangingConfigurations();
1298
1299            // Extract the theme attributes, if any.
1300            mThemeAttrs = a.extractThemeAttrs();
1301            if (mTransform == null) {
1302                // Lazy initialization: If the group is created through copy constructor, this may
1303                // never get called.
1304                mTransform = new float[TRANSFORM_PROPERTY_COUNT];
1305            }
1306            boolean success = nGetGroupProperties(mNativePtr, mTransform, TRANSFORM_PROPERTY_COUNT);
1307            if (!success) {
1308                throw new RuntimeException("Error: inconsistent property count");
1309            }
1310            float rotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation,
1311                    mTransform[ROTATION_INDEX]);
1312            float pivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX,
1313                    mTransform[PIVOT_X_INDEX]);
1314            float pivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY,
1315                    mTransform[PIVOT_Y_INDEX]);
1316            float scaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX,
1317                    mTransform[SCALE_X_INDEX]);
1318            float scaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY,
1319                    mTransform[SCALE_Y_INDEX]);
1320            float translateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX,
1321                    mTransform[TRANSLATE_X_INDEX]);
1322            float translateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY,
1323                    mTransform[TRANSLATE_Y_INDEX]);
1324
1325            final String groupName = a.getString(R.styleable.VectorDrawableGroup_name);
1326            if (groupName != null) {
1327                mGroupName = groupName;
1328                nSetName(mNativePtr, mGroupName);
1329            }
1330             nUpdateGroupProperties(mNativePtr, rotate, pivotX, pivotY, scaleX, scaleY,
1331                     translateX, translateY);
1332        }
1333
1334        @Override
1335        public boolean onStateChange(int[] stateSet) {
1336            boolean changed = false;
1337
1338            final ArrayList<VObject> children = mChildren;
1339            for (int i = 0, count = children.size(); i < count; i++) {
1340                final VObject child = children.get(i);
1341                if (child.isStateful()) {
1342                    changed |= child.onStateChange(stateSet);
1343                }
1344            }
1345
1346            return changed;
1347        }
1348
1349        @Override
1350        public boolean isStateful() {
1351            return mIsStateful;
1352        }
1353
1354        @Override
1355        int getNativeSize() {
1356            // Return the native allocation needed for the subtree.
1357            int size = NATIVE_ALLOCATION_SIZE;
1358            for (int i = 0; i < mChildren.size(); i++) {
1359                size += mChildren.get(i).getNativeSize();
1360            }
1361            return size;
1362        }
1363
1364        @Override
1365        public boolean canApplyTheme() {
1366            if (mThemeAttrs != null) {
1367                return true;
1368            }
1369
1370            final ArrayList<VObject> children = mChildren;
1371            for (int i = 0, count = children.size(); i < count; i++) {
1372                final VObject child = children.get(i);
1373                if (child.canApplyTheme()) {
1374                    return true;
1375                }
1376            }
1377
1378            return false;
1379        }
1380
1381        @Override
1382        public void applyTheme(Theme t) {
1383            if (mThemeAttrs != null) {
1384                final TypedArray a = t.resolveAttributes(mThemeAttrs,
1385                        R.styleable.VectorDrawableGroup);
1386                updateStateFromTypedArray(a);
1387                a.recycle();
1388            }
1389
1390            final ArrayList<VObject> children = mChildren;
1391            for (int i = 0, count = children.size(); i < count; i++) {
1392                final VObject child = children.get(i);
1393                if (child.canApplyTheme()) {
1394                    child.applyTheme(t);
1395
1396                    // Applying a theme may have made the child stateful.
1397                    mIsStateful |= child.isStateful();
1398                }
1399            }
1400        }
1401
1402        /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
1403        @SuppressWarnings("unused")
1404        public float getRotation() {
1405            return isTreeValid() ? nGetRotation(mNativePtr) : 0;
1406        }
1407
1408        @SuppressWarnings("unused")
1409        public void setRotation(float rotation) {
1410            if (isTreeValid()) {
1411                nSetRotation(mNativePtr, rotation);
1412            }
1413        }
1414
1415        @SuppressWarnings("unused")
1416        public float getPivotX() {
1417            return isTreeValid() ? nGetPivotX(mNativePtr) : 0;
1418        }
1419
1420        @SuppressWarnings("unused")
1421        public void setPivotX(float pivotX) {
1422            if (isTreeValid()) {
1423                nSetPivotX(mNativePtr, pivotX);
1424            }
1425        }
1426
1427        @SuppressWarnings("unused")
1428        public float getPivotY() {
1429            return isTreeValid() ? nGetPivotY(mNativePtr) : 0;
1430        }
1431
1432        @SuppressWarnings("unused")
1433        public void setPivotY(float pivotY) {
1434            if (isTreeValid()) {
1435                nSetPivotY(mNativePtr, pivotY);
1436            }
1437        }
1438
1439        @SuppressWarnings("unused")
1440        public float getScaleX() {
1441            return isTreeValid() ? nGetScaleX(mNativePtr) : 0;
1442        }
1443
1444        @SuppressWarnings("unused")
1445        public void setScaleX(float scaleX) {
1446            if (isTreeValid()) {
1447                nSetScaleX(mNativePtr, scaleX);
1448            }
1449        }
1450
1451        @SuppressWarnings("unused")
1452        public float getScaleY() {
1453            return isTreeValid() ? nGetScaleY(mNativePtr) : 0;
1454        }
1455
1456        @SuppressWarnings("unused")
1457        public void setScaleY(float scaleY) {
1458            if (isTreeValid()) {
1459                nSetScaleY(mNativePtr, scaleY);
1460            }
1461        }
1462
1463        @SuppressWarnings("unused")
1464        public float getTranslateX() {
1465            return isTreeValid() ? nGetTranslateX(mNativePtr) : 0;
1466        }
1467
1468        @SuppressWarnings("unused")
1469        public void setTranslateX(float translateX) {
1470            if (isTreeValid()) {
1471                nSetTranslateX(mNativePtr, translateX);
1472            }
1473        }
1474
1475        @SuppressWarnings("unused")
1476        public float getTranslateY() {
1477            return isTreeValid() ? nGetTranslateY(mNativePtr) : 0;
1478        }
1479
1480        @SuppressWarnings("unused")
1481        public void setTranslateY(float translateY) {
1482            if (isTreeValid()) {
1483                nSetTranslateY(mNativePtr, translateY);
1484            }
1485        }
1486    }
1487
1488    /**
1489     * Common Path information for clip path and normal path.
1490     */
1491    static abstract class VPath extends VObject {
1492        protected PathParser.PathData mPathData = null;
1493
1494        String mPathName;
1495        @Config int mChangingConfigurations;
1496
1497        private static final Property<VPath, PathParser.PathData> PATH_DATA =
1498                new Property<VPath, PathParser.PathData>(PathParser.PathData.class, "pathData") {
1499                    @Override
1500                    public void set(VPath object, PathParser.PathData data) {
1501                        object.setPathData(data);
1502                    }
1503
1504                    @Override
1505                    public PathParser.PathData get(VPath object) {
1506                        return object.getPathData();
1507                    }
1508                };
1509
1510        Property getProperty(String propertyName) {
1511            if (PATH_DATA.getName().equals(propertyName)) {
1512                return PATH_DATA;
1513            }
1514            // property not found
1515            return null;
1516        }
1517
1518        public VPath() {
1519            // Empty constructor.
1520        }
1521
1522        public VPath(VPath copy) {
1523            mPathName = copy.mPathName;
1524            mChangingConfigurations = copy.mChangingConfigurations;
1525            mPathData = copy.mPathData == null ? null : new PathParser.PathData(copy.mPathData);
1526        }
1527
1528        public String getPathName() {
1529            return mPathName;
1530        }
1531
1532        /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
1533        @SuppressWarnings("unused")
1534        public PathParser.PathData getPathData() {
1535            return mPathData;
1536        }
1537
1538        // TODO: Move the PathEvaluator and this setter and the getter above into native.
1539        @SuppressWarnings("unused")
1540        public void setPathData(PathParser.PathData pathData) {
1541            mPathData.setPathData(pathData);
1542            if (isTreeValid()) {
1543                nSetPathData(getNativePtr(), mPathData.getNativePtr());
1544            }
1545        }
1546    }
1547
1548    /**
1549     * Clip path, which only has name and pathData.
1550     */
1551    private static class VClipPath extends VPath {
1552        private final long mNativePtr;
1553        private static final int NATIVE_ALLOCATION_SIZE = 120;
1554
1555        public VClipPath() {
1556            mNativePtr = nCreateClipPath();
1557        }
1558
1559        public VClipPath(VClipPath copy) {
1560            super(copy);
1561            mNativePtr = nCreateClipPath(copy.mNativePtr);
1562        }
1563
1564        @Override
1565        public long getNativePtr() {
1566            return mNativePtr;
1567        }
1568
1569        @Override
1570        public void inflate(Resources r, AttributeSet attrs, Theme theme) {
1571            final TypedArray a = obtainAttributes(r, theme, attrs,
1572                    R.styleable.VectorDrawableClipPath);
1573            updateStateFromTypedArray(a);
1574            a.recycle();
1575        }
1576
1577        @Override
1578        public boolean canApplyTheme() {
1579            return false;
1580        }
1581
1582        @Override
1583        public void applyTheme(Theme theme) {
1584            // No-op.
1585        }
1586
1587        @Override
1588        public boolean onStateChange(int[] stateSet) {
1589            return false;
1590        }
1591
1592        @Override
1593        public boolean isStateful() {
1594            return false;
1595        }
1596
1597        @Override
1598        int getNativeSize() {
1599            return NATIVE_ALLOCATION_SIZE;
1600        }
1601
1602        private void updateStateFromTypedArray(TypedArray a) {
1603            // Account for any configuration changes.
1604            mChangingConfigurations |= a.getChangingConfigurations();
1605
1606            final String pathName = a.getString(R.styleable.VectorDrawableClipPath_name);
1607            if (pathName != null) {
1608                mPathName = pathName;
1609                nSetName(mNativePtr, mPathName);
1610            }
1611
1612            final String pathDataString = a.getString(R.styleable.VectorDrawableClipPath_pathData);
1613            if (pathDataString != null) {
1614                mPathData = new PathParser.PathData(pathDataString);
1615                nSetPathString(mNativePtr, pathDataString, pathDataString.length());
1616            }
1617        }
1618    }
1619
1620    /**
1621     * Normal path, which contains all the fill / paint information.
1622     */
1623    static class VFullPath extends VPath {
1624        private static final int STROKE_WIDTH_INDEX = 0;
1625        private static final int STROKE_COLOR_INDEX = 1;
1626        private static final int STROKE_ALPHA_INDEX = 2;
1627        private static final int FILL_COLOR_INDEX = 3;
1628        private static final int FILL_ALPHA_INDEX = 4;
1629        private static final int TRIM_PATH_START_INDEX = 5;
1630        private static final int TRIM_PATH_END_INDEX = 6;
1631        private static final int TRIM_PATH_OFFSET_INDEX = 7;
1632        private static final int STROKE_LINE_CAP_INDEX = 8;
1633        private static final int STROKE_LINE_JOIN_INDEX = 9;
1634        private static final int STROKE_MITER_LIMIT_INDEX = 10;
1635        private static final int FILL_TYPE_INDEX = 11;
1636        private static final int TOTAL_PROPERTY_COUNT = 12;
1637
1638        private static final int NATIVE_ALLOCATION_SIZE = 264;
1639        // Property map for animatable attributes.
1640        private final static HashMap<String, Integer> sPropertyIndexMap
1641                = new HashMap<String, Integer> () {
1642            {
1643                put("strokeWidth", STROKE_WIDTH_INDEX);
1644                put("strokeColor", STROKE_COLOR_INDEX);
1645                put("strokeAlpha", STROKE_ALPHA_INDEX);
1646                put("fillColor", FILL_COLOR_INDEX);
1647                put("fillAlpha", FILL_ALPHA_INDEX);
1648                put("trimPathStart", TRIM_PATH_START_INDEX);
1649                put("trimPathEnd", TRIM_PATH_END_INDEX);
1650                put("trimPathOffset", TRIM_PATH_OFFSET_INDEX);
1651            }
1652        };
1653
1654        // Below are the Properties that wrap the setters to avoid reflection overhead in animations
1655        private static final Property<VFullPath, Float> STROKE_WIDTH =
1656                new FloatProperty<VFullPath> ("strokeWidth") {
1657                    @Override
1658                    public void setValue(VFullPath object, float value) {
1659                        object.setStrokeWidth(value);
1660                    }
1661
1662                    @Override
1663                    public Float get(VFullPath object) {
1664                        return object.getStrokeWidth();
1665                    }
1666                };
1667
1668        private static final Property<VFullPath, Integer> STROKE_COLOR =
1669                new IntProperty<VFullPath> ("strokeColor") {
1670                    @Override
1671                    public void setValue(VFullPath object, int value) {
1672                        object.setStrokeColor(value);
1673                    }
1674
1675                    @Override
1676                    public Integer get(VFullPath object) {
1677                        return object.getStrokeColor();
1678                    }
1679                };
1680
1681        private static final Property<VFullPath, Float> STROKE_ALPHA =
1682                new FloatProperty<VFullPath> ("strokeAlpha") {
1683                    @Override
1684                    public void setValue(VFullPath object, float value) {
1685                        object.setStrokeAlpha(value);
1686                    }
1687
1688                    @Override
1689                    public Float get(VFullPath object) {
1690                        return object.getStrokeAlpha();
1691                    }
1692                };
1693
1694        private static final Property<VFullPath, Integer> FILL_COLOR =
1695                new IntProperty<VFullPath>("fillColor") {
1696                    @Override
1697                    public void setValue(VFullPath object, int value) {
1698                        object.setFillColor(value);
1699                    }
1700
1701                    @Override
1702                    public Integer get(VFullPath object) {
1703                        return object.getFillColor();
1704                    }
1705                };
1706
1707        private static final Property<VFullPath, Float> FILL_ALPHA =
1708                new FloatProperty<VFullPath> ("fillAlpha") {
1709                    @Override
1710                    public void setValue(VFullPath object, float value) {
1711                        object.setFillAlpha(value);
1712                    }
1713
1714                    @Override
1715                    public Float get(VFullPath object) {
1716                        return object.getFillAlpha();
1717                    }
1718                };
1719
1720        private static final Property<VFullPath, Float> TRIM_PATH_START =
1721                new FloatProperty<VFullPath> ("trimPathStart") {
1722                    @Override
1723                    public void setValue(VFullPath object, float value) {
1724                        object.setTrimPathStart(value);
1725                    }
1726
1727                    @Override
1728                    public Float get(VFullPath object) {
1729                        return object.getTrimPathStart();
1730                    }
1731                };
1732
1733        private static final Property<VFullPath, Float> TRIM_PATH_END =
1734                new FloatProperty<VFullPath> ("trimPathEnd") {
1735                    @Override
1736                    public void setValue(VFullPath object, float value) {
1737                        object.setTrimPathEnd(value);
1738                    }
1739
1740                    @Override
1741                    public Float get(VFullPath object) {
1742                        return object.getTrimPathEnd();
1743                    }
1744                };
1745
1746        private static final Property<VFullPath, Float> TRIM_PATH_OFFSET =
1747                new FloatProperty<VFullPath> ("trimPathOffset") {
1748                    @Override
1749                    public void setValue(VFullPath object, float value) {
1750                        object.setTrimPathOffset(value);
1751                    }
1752
1753                    @Override
1754                    public Float get(VFullPath object) {
1755                        return object.getTrimPathOffset();
1756                    }
1757                };
1758
1759        private final static HashMap<String, Property> sPropertyMap
1760                = new HashMap<String, Property> () {
1761            {
1762                put("strokeWidth", STROKE_WIDTH);
1763                put("strokeColor", STROKE_COLOR);
1764                put("strokeAlpha", STROKE_ALPHA);
1765                put("fillColor", FILL_COLOR);
1766                put("fillAlpha", FILL_ALPHA);
1767                put("trimPathStart", TRIM_PATH_START);
1768                put("trimPathEnd", TRIM_PATH_END);
1769                put("trimPathOffset", TRIM_PATH_OFFSET);
1770            }
1771        };
1772
1773        // Temp array to store property data obtained from native getter.
1774        private byte[] mPropertyData;
1775        /////////////////////////////////////////////////////
1776        // Variables below need to be copied (deep copy if applicable) for mutation.
1777        private int[] mThemeAttrs;
1778
1779        ComplexColor mStrokeColors = null;
1780        ComplexColor mFillColors = null;
1781        private final long mNativePtr;
1782
1783        public VFullPath() {
1784            mNativePtr = nCreateFullPath();
1785        }
1786
1787        public VFullPath(VFullPath copy) {
1788            super(copy);
1789            mNativePtr = nCreateFullPath(copy.mNativePtr);
1790            mThemeAttrs = copy.mThemeAttrs;
1791            mStrokeColors = copy.mStrokeColors;
1792            mFillColors = copy.mFillColors;
1793        }
1794
1795        Property getProperty(String propertyName) {
1796            Property p = super.getProperty(propertyName);
1797            if (p != null) {
1798                return p;
1799            }
1800            if (sPropertyMap.containsKey(propertyName)) {
1801                return sPropertyMap.get(propertyName);
1802            } else {
1803                // property not found
1804                return null;
1805            }
1806        }
1807
1808        int getPropertyIndex(String propertyName) {
1809            if (!sPropertyIndexMap.containsKey(propertyName)) {
1810                return -1;
1811            } else {
1812                return sPropertyIndexMap.get(propertyName);
1813            }
1814        }
1815
1816        @Override
1817        public boolean onStateChange(int[] stateSet) {
1818            boolean changed = false;
1819
1820            if (mStrokeColors != null && mStrokeColors instanceof ColorStateList) {
1821                final int oldStrokeColor = getStrokeColor();
1822                final int newStrokeColor =
1823                        ((ColorStateList) mStrokeColors).getColorForState(stateSet, oldStrokeColor);
1824                changed |= oldStrokeColor != newStrokeColor;
1825                if (oldStrokeColor != newStrokeColor) {
1826                    nSetStrokeColor(mNativePtr, newStrokeColor);
1827                }
1828            }
1829
1830            if (mFillColors != null && mFillColors instanceof ColorStateList) {
1831                final int oldFillColor = getFillColor();
1832                final int newFillColor = ((ColorStateList) mFillColors).getColorForState(stateSet, oldFillColor);
1833                changed |= oldFillColor != newFillColor;
1834                if (oldFillColor != newFillColor) {
1835                    nSetFillColor(mNativePtr, newFillColor);
1836                }
1837            }
1838
1839            return changed;
1840        }
1841
1842        @Override
1843        public boolean isStateful() {
1844            return mStrokeColors != null || mFillColors != null;
1845        }
1846
1847        @Override
1848        int getNativeSize() {
1849            return NATIVE_ALLOCATION_SIZE;
1850        }
1851
1852        @Override
1853        public long getNativePtr() {
1854            return mNativePtr;
1855        }
1856
1857        @Override
1858        public void inflate(Resources r, AttributeSet attrs, Theme theme) {
1859            final TypedArray a = obtainAttributes(r, theme, attrs,
1860                    R.styleable.VectorDrawablePath);
1861            updateStateFromTypedArray(a);
1862            a.recycle();
1863        }
1864
1865        private void updateStateFromTypedArray(TypedArray a) {
1866            int byteCount = TOTAL_PROPERTY_COUNT * 4;
1867            if (mPropertyData == null) {
1868                // Lazy initialization: If the path is created through copy constructor, this may
1869                // never get called.
1870                mPropertyData = new byte[byteCount];
1871            }
1872            // The bulk getters/setters of property data (e.g. stroke width, color, etc) allows us
1873            // to pull current values from native and store modifications with only two methods,
1874            // minimizing JNI overhead.
1875            boolean success = nGetFullPathProperties(mNativePtr, mPropertyData, byteCount);
1876            if (!success) {
1877                throw new RuntimeException("Error: inconsistent property count");
1878            }
1879
1880            ByteBuffer properties = ByteBuffer.wrap(mPropertyData);
1881            properties.order(ByteOrder.nativeOrder());
1882            float strokeWidth = properties.getFloat(STROKE_WIDTH_INDEX * 4);
1883            int strokeColor = properties.getInt(STROKE_COLOR_INDEX * 4);
1884            float strokeAlpha = properties.getFloat(STROKE_ALPHA_INDEX * 4);
1885            int fillColor =  properties.getInt(FILL_COLOR_INDEX * 4);
1886            float fillAlpha = properties.getFloat(FILL_ALPHA_INDEX * 4);
1887            float trimPathStart = properties.getFloat(TRIM_PATH_START_INDEX * 4);
1888            float trimPathEnd = properties.getFloat(TRIM_PATH_END_INDEX * 4);
1889            float trimPathOffset = properties.getFloat(TRIM_PATH_OFFSET_INDEX * 4);
1890            int strokeLineCap =  properties.getInt(STROKE_LINE_CAP_INDEX * 4);
1891            int strokeLineJoin = properties.getInt(STROKE_LINE_JOIN_INDEX * 4);
1892            float strokeMiterLimit = properties.getFloat(STROKE_MITER_LIMIT_INDEX * 4);
1893            int fillType = properties.getInt(FILL_TYPE_INDEX * 4);
1894            Shader fillGradient = null;
1895            Shader strokeGradient = null;
1896            // Account for any configuration changes.
1897            mChangingConfigurations |= a.getChangingConfigurations();
1898
1899            // Extract the theme attributes, if any.
1900            mThemeAttrs = a.extractThemeAttrs();
1901
1902            final String pathName = a.getString(R.styleable.VectorDrawablePath_name);
1903            if (pathName != null) {
1904                mPathName = pathName;
1905                nSetName(mNativePtr, mPathName);
1906            }
1907
1908            final String pathString = a.getString(R.styleable.VectorDrawablePath_pathData);
1909            if (pathString != null) {
1910                mPathData = new PathParser.PathData(pathString);
1911                nSetPathString(mNativePtr, pathString, pathString.length());
1912            }
1913
1914            final ComplexColor fillColors = a.getComplexColor(
1915                    R.styleable.VectorDrawablePath_fillColor);
1916            if (fillColors != null) {
1917                // If the colors is a gradient color, or the color state list is stateful, keep the
1918                // colors information. Otherwise, discard the colors and keep the default color.
1919                if (fillColors instanceof  GradientColor) {
1920                    mFillColors = fillColors;
1921                    fillGradient = ((GradientColor) fillColors).getShader();
1922                } else if (fillColors.isStateful()) {
1923                    mFillColors = fillColors;
1924                } else {
1925                    mFillColors = null;
1926                }
1927                fillColor = fillColors.getDefaultColor();
1928            }
1929
1930            final ComplexColor strokeColors = a.getComplexColor(
1931                    R.styleable.VectorDrawablePath_strokeColor);
1932            if (strokeColors != null) {
1933                // If the colors is a gradient color, or the color state list is stateful, keep the
1934                // colors information. Otherwise, discard the colors and keep the default color.
1935                if (strokeColors instanceof GradientColor) {
1936                    mStrokeColors = strokeColors;
1937                    strokeGradient = ((GradientColor) strokeColors).getShader();
1938                } else if (strokeColors.isStateful()) {
1939                    mStrokeColors = strokeColors;
1940                } else {
1941                    mStrokeColors = null;
1942                }
1943                strokeColor = strokeColors.getDefaultColor();
1944            }
1945            // Update the gradient info, even if the gradiet is null.
1946            nUpdateFullPathFillGradient(mNativePtr,
1947                    fillGradient != null ? fillGradient.getNativeInstance() : 0);
1948            nUpdateFullPathStrokeGradient(mNativePtr,
1949                    strokeGradient != null ? strokeGradient.getNativeInstance() : 0);
1950
1951            fillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha, fillAlpha);
1952
1953            strokeLineCap = a.getInt(
1954                    R.styleable.VectorDrawablePath_strokeLineCap, strokeLineCap);
1955            strokeLineJoin = a.getInt(
1956                    R.styleable.VectorDrawablePath_strokeLineJoin, strokeLineJoin);
1957            strokeMiterLimit = a.getFloat(
1958                    R.styleable.VectorDrawablePath_strokeMiterLimit, strokeMiterLimit);
1959            strokeAlpha = a.getFloat(R.styleable.VectorDrawablePath_strokeAlpha,
1960                    strokeAlpha);
1961            strokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth,
1962                    strokeWidth);
1963            trimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd,
1964                    trimPathEnd);
1965            trimPathOffset = a.getFloat(
1966                    R.styleable.VectorDrawablePath_trimPathOffset, trimPathOffset);
1967            trimPathStart = a.getFloat(
1968                    R.styleable.VectorDrawablePath_trimPathStart, trimPathStart);
1969            fillType = a.getInt(R.styleable.VectorDrawablePath_fillType, fillType);
1970
1971            nUpdateFullPathProperties(mNativePtr, strokeWidth, strokeColor, strokeAlpha,
1972                    fillColor, fillAlpha, trimPathStart, trimPathEnd, trimPathOffset,
1973                    strokeMiterLimit, strokeLineCap, strokeLineJoin, fillType);
1974        }
1975
1976        @Override
1977        public boolean canApplyTheme() {
1978            if (mThemeAttrs != null) {
1979                return true;
1980            }
1981
1982            boolean fillCanApplyTheme = canComplexColorApplyTheme(mFillColors);
1983            boolean strokeCanApplyTheme = canComplexColorApplyTheme(mStrokeColors);
1984            if (fillCanApplyTheme || strokeCanApplyTheme) {
1985                return true;
1986            }
1987            return false;
1988
1989        }
1990
1991        @Override
1992        public void applyTheme(Theme t) {
1993            // Resolve the theme attributes directly referred by the VectorDrawable.
1994            if (mThemeAttrs != null) {
1995                final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawablePath);
1996                updateStateFromTypedArray(a);
1997                a.recycle();
1998            }
1999
2000            // Resolve the theme attributes in-directly referred by the VectorDrawable, for example,
2001            // fillColor can refer to a color state list which itself needs to apply theme.
2002            // And this is the reason we still want to keep partial update for the path's properties.
2003            boolean fillCanApplyTheme = canComplexColorApplyTheme(mFillColors);
2004            boolean strokeCanApplyTheme = canComplexColorApplyTheme(mStrokeColors);
2005
2006            if (fillCanApplyTheme) {
2007                mFillColors = mFillColors.obtainForTheme(t);
2008                if (mFillColors instanceof GradientColor) {
2009                    nUpdateFullPathFillGradient(mNativePtr,
2010                            ((GradientColor) mFillColors).getShader().getNativeInstance());
2011                } else if (mFillColors instanceof ColorStateList) {
2012                    nSetFillColor(mNativePtr, mFillColors.getDefaultColor());
2013                }
2014            }
2015
2016            if (strokeCanApplyTheme) {
2017                mStrokeColors = mStrokeColors.obtainForTheme(t);
2018                if (mStrokeColors instanceof GradientColor) {
2019                    nUpdateFullPathStrokeGradient(mNativePtr,
2020                            ((GradientColor) mStrokeColors).getShader().getNativeInstance());
2021                } else if (mStrokeColors instanceof ColorStateList) {
2022                    nSetStrokeColor(mNativePtr, mStrokeColors.getDefaultColor());
2023                }
2024            }
2025        }
2026
2027        private boolean canComplexColorApplyTheme(ComplexColor complexColor) {
2028            return complexColor != null && complexColor.canApplyTheme();
2029        }
2030
2031        /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
2032        @SuppressWarnings("unused")
2033        int getStrokeColor() {
2034            return isTreeValid() ? nGetStrokeColor(mNativePtr) : 0;
2035        }
2036
2037        @SuppressWarnings("unused")
2038        void setStrokeColor(int strokeColor) {
2039            mStrokeColors = null;
2040            if (isTreeValid()) {
2041                nSetStrokeColor(mNativePtr, strokeColor);
2042            }
2043        }
2044
2045        @SuppressWarnings("unused")
2046        float getStrokeWidth() {
2047            return isTreeValid() ? nGetStrokeWidth(mNativePtr) : 0;
2048        }
2049
2050        @SuppressWarnings("unused")
2051        void setStrokeWidth(float strokeWidth) {
2052            if (isTreeValid()) {
2053                nSetStrokeWidth(mNativePtr, strokeWidth);
2054            }
2055        }
2056
2057        @SuppressWarnings("unused")
2058        float getStrokeAlpha() {
2059            return isTreeValid() ? nGetStrokeAlpha(mNativePtr) : 0;
2060        }
2061
2062        @SuppressWarnings("unused")
2063        void setStrokeAlpha(float strokeAlpha) {
2064            if (isTreeValid()) {
2065                nSetStrokeAlpha(mNativePtr, strokeAlpha);
2066            }
2067        }
2068
2069        @SuppressWarnings("unused")
2070        int getFillColor() {
2071            return isTreeValid() ? nGetFillColor(mNativePtr) : 0;
2072        }
2073
2074        @SuppressWarnings("unused")
2075        void setFillColor(int fillColor) {
2076            mFillColors = null;
2077            if (isTreeValid()) {
2078                nSetFillColor(mNativePtr, fillColor);
2079            }
2080        }
2081
2082        @SuppressWarnings("unused")
2083        float getFillAlpha() {
2084            return isTreeValid() ? nGetFillAlpha(mNativePtr) : 0;
2085        }
2086
2087        @SuppressWarnings("unused")
2088        void setFillAlpha(float fillAlpha) {
2089            if (isTreeValid()) {
2090                nSetFillAlpha(mNativePtr, fillAlpha);
2091            }
2092        }
2093
2094        @SuppressWarnings("unused")
2095        float getTrimPathStart() {
2096            return isTreeValid() ? nGetTrimPathStart(mNativePtr) : 0;
2097        }
2098
2099        @SuppressWarnings("unused")
2100        void setTrimPathStart(float trimPathStart) {
2101            if (isTreeValid()) {
2102                nSetTrimPathStart(mNativePtr, trimPathStart);
2103            }
2104        }
2105
2106        @SuppressWarnings("unused")
2107        float getTrimPathEnd() {
2108            return isTreeValid() ? nGetTrimPathEnd(mNativePtr) : 0;
2109        }
2110
2111        @SuppressWarnings("unused")
2112        void setTrimPathEnd(float trimPathEnd) {
2113            if (isTreeValid()) {
2114                nSetTrimPathEnd(mNativePtr, trimPathEnd);
2115            }
2116        }
2117
2118        @SuppressWarnings("unused")
2119        float getTrimPathOffset() {
2120            return isTreeValid() ? nGetTrimPathOffset(mNativePtr) : 0;
2121        }
2122
2123        @SuppressWarnings("unused")
2124        void setTrimPathOffset(float trimPathOffset) {
2125            if (isTreeValid()) {
2126                nSetTrimPathOffset(mNativePtr, trimPathOffset);
2127            }
2128        }
2129    }
2130
2131    abstract static class VObject {
2132        VirtualRefBasePtr mTreePtr = null;
2133        boolean isTreeValid() {
2134            return mTreePtr != null && mTreePtr.get() != 0;
2135        }
2136        void setTree(VirtualRefBasePtr ptr) {
2137            mTreePtr = ptr;
2138        }
2139        abstract long getNativePtr();
2140        abstract void inflate(Resources r, AttributeSet attrs, Theme theme);
2141        abstract boolean canApplyTheme();
2142        abstract void applyTheme(Theme t);
2143        abstract boolean onStateChange(int[] state);
2144        abstract boolean isStateful();
2145        abstract int getNativeSize();
2146        abstract Property getProperty(String propertyName);
2147    }
2148
2149    private static native long nCreateTree(long rootGroupPtr);
2150    private static native long nCreateTreeFromCopy(long treeToCopy, long rootGroupPtr);
2151    private static native void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
2152            float viewportHeight);
2153    private static native boolean nSetRootAlpha(long rendererPtr, float alpha);
2154    private static native float nGetRootAlpha(long rendererPtr);
2155    private static native void nSetAllowCaching(long rendererPtr, boolean allowCaching);
2156
2157    private static native int nDraw(long rendererPtr, long canvasWrapperPtr,
2158            long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache);
2159    private static native long nCreateFullPath();
2160    private static native long nCreateFullPath(long nativeFullPathPtr);
2161    private static native boolean nGetFullPathProperties(long pathPtr, byte[] properties,
2162            int length);
2163
2164    private static native void nUpdateFullPathProperties(long pathPtr, float strokeWidth,
2165            int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart,
2166            float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap,
2167            int strokeLineJoin, int fillType);
2168    private static native void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr);
2169    private static native void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr);
2170
2171    private static native long nCreateClipPath();
2172    private static native long nCreateClipPath(long clipPathPtr);
2173
2174    private static native long nCreateGroup();
2175    private static native long nCreateGroup(long groupPtr);
2176    private static native void nSetName(long nodePtr, String name);
2177    private static native boolean nGetGroupProperties(long groupPtr, float[] properties,
2178            int length);
2179    private static native void nUpdateGroupProperties(long groupPtr, float rotate, float pivotX,
2180            float pivotY, float scaleX, float scaleY, float translateX, float translateY);
2181
2182    private static native void nAddChild(long groupPtr, long nodePtr);
2183    private static native void nSetPathString(long pathPtr, String pathString, int length);
2184
2185    /**
2186     * The setters and getters below for paths and groups are here temporarily, and will be
2187     * removed once the animation in AVD is replaced with RenderNodeAnimator, in which case the
2188     * animation will modify these properties in native. By then no JNI hopping would be necessary
2189     * for VD during animation, and these setters and getters will be obsolete.
2190     */
2191    // Setters and getters during animation.
2192    private static native float nGetRotation(long groupPtr);
2193    private static native void nSetRotation(long groupPtr, float rotation);
2194    private static native float nGetPivotX(long groupPtr);
2195    private static native void nSetPivotX(long groupPtr, float pivotX);
2196    private static native float nGetPivotY(long groupPtr);
2197    private static native void nSetPivotY(long groupPtr, float pivotY);
2198    private static native float nGetScaleX(long groupPtr);
2199    private static native void nSetScaleX(long groupPtr, float scaleX);
2200    private static native float nGetScaleY(long groupPtr);
2201    private static native void nSetScaleY(long groupPtr, float scaleY);
2202    private static native float nGetTranslateX(long groupPtr);
2203    private static native void nSetTranslateX(long groupPtr, float translateX);
2204    private static native float nGetTranslateY(long groupPtr);
2205    private static native void nSetTranslateY(long groupPtr, float translateY);
2206
2207    // Setters and getters for VPath during animation.
2208    private static native void nSetPathData(long pathPtr, long pathDataPtr);
2209    private static native float nGetStrokeWidth(long pathPtr);
2210    private static native void nSetStrokeWidth(long pathPtr, float width);
2211    private static native int nGetStrokeColor(long pathPtr);
2212    private static native void nSetStrokeColor(long pathPtr, int strokeColor);
2213    private static native float nGetStrokeAlpha(long pathPtr);
2214    private static native void nSetStrokeAlpha(long pathPtr, float alpha);
2215    private static native int nGetFillColor(long pathPtr);
2216    private static native void nSetFillColor(long pathPtr, int fillColor);
2217    private static native float nGetFillAlpha(long pathPtr);
2218    private static native void nSetFillAlpha(long pathPtr, float fillAlpha);
2219    private static native float nGetTrimPathStart(long pathPtr);
2220    private static native void nSetTrimPathStart(long pathPtr, float trimPathStart);
2221    private static native float nGetTrimPathEnd(long pathPtr);
2222    private static native void nSetTrimPathEnd(long pathPtr, float trimPathEnd);
2223    private static native float nGetTrimPathOffset(long pathPtr);
2224    private static native void nSetTrimPathOffset(long pathPtr, float trimPathOffset);
2225}
2226