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