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