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