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