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