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