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