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