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