Color theming for VectorDrawable, general clean up

BUG: 13878079
Change-Id: I831d26fe3a2658161fc24e947bce382d0be6b548
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 33683ab..d6f75ba 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -14,7 +14,6 @@
 
 package android.graphics.drawable;
 
-import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.res.Resources;
@@ -28,11 +27,7 @@
 import android.graphics.PathMeasure;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.Region;
-import android.location.Address;
-import android.net.ParseException;
-import android.sax.StartElementListener;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
@@ -47,7 +42,6 @@
 import org.xmlpull.v1.XmlPullParserFactory;
 
 import java.io.IOException;
-import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -157,8 +151,8 @@
  * </dd>
  */
 public class VectorDrawable extends Drawable {
-    private static final String LOGTAG = "VectorDrawable";
-    public static final int INFINITE = ValueAnimator.INFINITE;
+    private static final String LOGTAG = VectorDrawable.class.getSimpleName();
+
     private static final String SHAPE_SIZE = "size";
     private static final String SHAPE_VIEWPORT = "viewport";
     private static final String SHAPE_GROUP = "group";
@@ -170,23 +164,32 @@
     private static final int LINECAP_BUTT = 0;
     private static final int LINECAP_ROUND = 1;
     private static final int LINECAP_SQUARE = 2;
+
     private static final int LINEJOIN_MITER = 0;
     private static final int LINEJOIN_ROUND = 1;
     private static final int LINEJOIN_BEVEL = 2;
+
     private static final int DEFAULT_DURATION = 1000;
     private static final long DEFAULT_INFINITE_DURATION = 60 * 60 * 1000;
+
     private VectorDrawableState mVectorState;
     private int mAlpha = 0xFF;
 
     public VectorDrawable() {
-        mVectorState = new VectorDrawableState();
+        mVectorState = new VectorDrawableState(null);
         mVectorState.mBasicAnimator = ObjectAnimator.ofFloat(this, "AnimationFraction", 0, 1);
+
         setDuration(DEFAULT_DURATION);
     }
 
-    private VectorDrawable(VectorDrawableState state) {
+    private VectorDrawable(VectorDrawableState state, Resources res, Theme theme) {
         mVectorState = new VectorDrawableState(state);
         mVectorState.mBasicAnimator = ObjectAnimator.ofFloat(this, "AnimationFraction", 0, 1);
+
+        if (theme != null && canApplyTheme()) {
+            applyTheme(theme);
+        }
+
         long duration = mVectorState.mVAnimatedPath.getTotalAnimationDuration();
         if (duration == -1) { // if it set to infinite set to 1 hour
             duration = DEFAULT_INFINITE_DURATION; // TODO define correct approach for infinite
@@ -197,6 +200,7 @@
     }
 
     final static class VectorDrawableState extends ConstantState {
+        int[] mThemeAttrs;
         int mChangingConfigurations;
         ValueAnimator mBasicAnimator;
         VAnimatedPath mVAnimatedPath = new VAnimatedPath();
@@ -204,20 +208,29 @@
         int mIntrinsicHeight;
         int mIntrinsicWidth;
 
-        public VectorDrawableState(){
-        }
-
-        public VectorDrawableState(VectorDrawableState copy){
-            mChangingConfigurations = copy.mChangingConfigurations;
-            mVAnimatedPath = new VAnimatedPath(copy.mVAnimatedPath);
-            mPadding = new Rect(copy.mPadding);
-            mIntrinsicHeight = copy.mIntrinsicHeight;
-            mIntrinsicWidth = copy.mIntrinsicWidth;
+        public VectorDrawableState(VectorDrawableState copy) {
+            if (copy != null) {
+                mChangingConfigurations = copy.mChangingConfigurations;
+                mVAnimatedPath = new VAnimatedPath(copy.mVAnimatedPath);
+                mPadding = new Rect(copy.mPadding);
+                mIntrinsicHeight = copy.mIntrinsicHeight;
+                mIntrinsicWidth = copy.mIntrinsicWidth;
+            }
         }
 
         @Override
         public Drawable newDrawable() {
-            return new VectorDrawable(this);
+            return new VectorDrawable(this, null, null);
+        }
+
+        @Override
+        public Drawable newDrawable(Resources res) {
+            return new VectorDrawable(this, res, null);
+        }
+
+        @Override
+        public Drawable newDrawable(Resources res, Theme theme) {
+            return new VectorDrawable(this, res, theme);
         }
 
         @Override
@@ -459,29 +472,49 @@
     @Override
     public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
             throws XmlPullParserException, IOException {
-        setAnimatedPath(inflateInternal(res, parser, attrs));
+        final VAnimatedPath p = inflateInternal(res, parser, attrs, theme);
+        setAnimatedPath(p);
     }
 
-    private VAnimatedPath inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs)
-            throws XmlPullParserException, IOException {
+    @Override
+    public boolean canApplyTheme() {
+        return super.canApplyTheme() || mVectorState != null && mVectorState.canApplyTheme();
+    }
+
+    @Override
+    public void applyTheme(Theme t) {
+        super.applyTheme(t);
+
+        final VectorDrawableState state = mVectorState;
+        final VAnimatedPath path = state.mVAnimatedPath;
+        if (path != null && path.canApplyTheme()) {
+            path.applyTheme(t);
+        }
+    }
+
+    private VAnimatedPath inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
+            Theme theme) throws XmlPullParserException, IOException {
+        final VAnimatedPath animatedPath = new VAnimatedPath();
+
         boolean noSizeTag = true;
         boolean noViewportTag = true;
         boolean noGroupTag = true;
         boolean noPathTag = true;
-        final VAnimatedPath animatedPath = new VAnimatedPath();
-        VectorDrawable.VGroup currentGroup = null;
+
+        VGroup currentGroup = null;
 
         int eventType = parser.getEventType();
         while (eventType != XmlPullParser.END_DOCUMENT) {
             if (eventType == XmlPullParser.START_TAG) {
-                String tagName = parser.getName();
+                final String tagName = parser.getName();
                 if (SHAPE_PATH.equals(tagName)) {
-                    VectorDrawable.VPath p = new VPath(res, attrs);
-                    currentGroup.add(p);
+                    final VPath path = new VPath();
+                    path.inflate(res, attrs, theme);
+                    currentGroup.add(path);
                     noPathTag = false;
                 } else if (SHAPE_ANIMATION.equals(tagName)) {
-                    VectorDrawable.VAnimation anim =
-                            new VAnimation(res, attrs, animatedPath.mGroupList);
+                    final VAnimation anim = new VAnimation();
+                    anim.inflate(animatedPath.mGroupList, res, attrs, theme);
                     animatedPath.addAnimation(anim);
                 } else if (SHAPE_SIZE.equals(tagName)) {
                     animatedPath.parseSize(res, attrs);
@@ -490,17 +523,17 @@
                     animatedPath.parseViewport(res, attrs);
                     noViewportTag = false;
                 } else if (SHAPE_GROUP.equals(tagName)) {
-                    currentGroup = new VectorDrawable.VGroup();
+                    currentGroup = new VGroup();
                     animatedPath.mGroupList.add(currentGroup);
                     noGroupTag = false;
                 }  else if (SHAPE_VECTOR.equals(tagName)) {
-                    TypedArray a = res.obtainAttributes(attrs, R.styleable.VectorDrawable);
+                    final TypedArray a = res.obtainAttributes(attrs, R.styleable.VectorDrawable);
                     animatedPath.setTrigger(a.getInteger(R.styleable.VectorDrawable_trigger, 0));
 
                     // Parsing the version information.
                     // Right now, we only support version "1".
                     // If the xml didn't specify the version number, the default version is "1".
-                    int versionCode = a.getInt(R.styleable.VectorDrawable_versionCode, 1);
+                    final int versionCode = a.getInt(R.styleable.VectorDrawable_versionCode, 1);
                     if (versionCode != 1) {
                         throw new IllegalArgumentException(
                                 "So far, VectorDrawable only support version 1");
@@ -509,33 +542,41 @@
                     a.recycle();
                 }
             }
+
             eventType = parser.next();
         }
+
         if (noSizeTag || noViewportTag || noGroupTag || noPathTag) {
-            StringBuffer tag = new StringBuffer();
+            final StringBuffer tag = new StringBuffer();
+
             if (noSizeTag) {
-                tag.append("size");
+                tag.append(SHAPE_SIZE);
             }
+
             if  (noViewportTag){
                 if (tag.length()>0) {
                     tag.append(" & ");
                 }
-                tag.append("size");
+                tag.append(SHAPE_SIZE);
             }
+
             if  (noGroupTag){
                 if (tag.length()>0) {
                     tag.append(" & ");
                 }
-                tag.append("group");
+                tag.append(SHAPE_GROUP);
             }
+
             if  (noPathTag){
                 if (tag.length()>0) {
                     tag.append(" or ");
                 }
-                tag.append("path");
+                tag.append(SHAPE_PATH);
             }
-            throw new XmlPullParserException("no "+tag+" defined");
+
+            throw new XmlPullParserException("no " + tag + " defined");
         }
+
         // post parse cleanup
         animatedPath.parseFinish();
         return animatedPath;
@@ -543,16 +584,18 @@
 
     private void setAnimatedPath(VAnimatedPath animatedPath) {
         mVectorState.mVAnimatedPath = animatedPath;
+
         setIntrinsicWidth((int) mVectorState.mVAnimatedPath.mBaseWidth);
         setIntrinsicHeight((int) mVectorState.mVAnimatedPath.mBaseHeight);
+
         long duration = mVectorState.mVAnimatedPath.getTotalAnimationDuration();
         if (duration == -1) { // if it set to infinite set to 1 hour
             duration = DEFAULT_INFINITE_DURATION; // TODO define correct approach for infinite
             mVectorState.mBasicAnimator.setFloatValues(0, duration / 1000);
             mVectorState.mBasicAnimator.setInterpolator(new LinearInterpolator());
         }
-        setDuration(duration);
 
+        setDuration(duration);
         setAnimationFraction(0);
     }
 
@@ -570,8 +613,6 @@
     }
 
     private static class VAnimatedPath {
-        private static final String LOGTAG = "VAnimatedPath";
-
         private ArrayList<VAnimation> mCurrentAnimList = null;
         private VPath[] mCurrentPaths;
         private float mAnimationValue = 0; // value goes from 0 to 1
@@ -585,7 +626,9 @@
         private int[] mCurrentState = new int[0];
         private int mTrigger;
         private boolean mTriggerState;
-        ArrayList<VGroup> mGroupList = new ArrayList<VGroup>();
+
+        final ArrayList<VGroup> mGroupList = new ArrayList<VGroup>();
+
         float mBaseWidth = 1;
         float mBaseHeight = 1;
         float mViewportWidth;
@@ -594,10 +637,11 @@
         public VAnimatedPath() {
             setup();
         }
+
         public VAnimatedPath(VAnimatedPath copy) {
             setup();
             mCurrentAnimList = new ArrayList<VAnimation>(copy.mCurrentAnimList);
-            mGroupList = new ArrayList<VGroup>(copy.mGroupList);
+            mGroupList.addAll(copy.mGroupList);
             if (copy.mCurrentPaths != null) {
                 mCurrentPaths = new VPath[copy.mCurrentPaths.length];
                 for (int i = 0; i < mCurrentPaths.length; i++) {
@@ -615,8 +659,53 @@
             mCurrentState = new int[0];
         }
 
+        public boolean canApplyTheme() {
+            final ArrayList<VGroup> groups = mGroupList;
+            for (int i = groups.size() - 1; i >= 0; i--) {
+                final ArrayList<VPath> paths = groups.get(i).mVGList;
+                for (int j = paths.size() - 1; j >= 0; j--) {
+                    final VPath path = paths.get(j);
+                    if (path.canApplyTheme()) {
+                        return true;
+                    }
+                }
+            }
+
+            final ArrayList<VAnimation> anims = mCurrentAnimList;
+            for (int i = anims.size() - 1; i >= 0; i--) {
+                final VAnimation anim = anims.get(i);
+                if (anim.canApplyTheme()) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        public void applyTheme(Theme t) {
+            final ArrayList<VGroup> groups = mGroupList;
+            for (int i = groups.size() - 1; i >= 0; i--) {
+                final ArrayList<VPath> paths = groups.get(i).mVGList;
+                for (int j = paths.size() - 1; j >= 0; j--) {
+                    final VPath path = paths.get(j);
+                    if (path.canApplyTheme()) {
+                        path.applyTheme(t);
+                    }
+                }
+            }
+
+            final ArrayList<VAnimation> anims = mCurrentAnimList;
+            for (int i = anims.size() - 1; i >= 0; i--) {
+                final VAnimation anim = anims.get(i);
+                if (anim.canApplyTheme()) {
+                    anim.applyTheme(t);
+                }
+            }
+        }
+
         public void setTrigger(int trigger){
-            int []lut = { 0,
+            final int [] lut = {
+                    0,
                     R.attr.state_pressed,
                     R.attr.state_focused,
                     R.attr.state_hovered,
@@ -626,6 +715,7 @@
                     R.attr.state_activated,
                     R.attr.state_focused
             };
+
             mTrigger = lut[trigger];
          }
 
@@ -663,23 +753,27 @@
          * @return true if you need to keep repeating
          */
         public boolean setAnimationFraction(float value) {
-            int len = mCurrentPaths.length;
             getTotalAnimationDuration();
+
             long animationTime = (long) (value * mTotalDuration);
 
+            final int len = mCurrentPaths.length;
             for (int i = 0; i < len; i++) {
-                VPath path = mCurrentPaths[i];
                 animationTime =
                         (long) ((mTotalDuration == -1) ? value * 1000 : mTotalDuration * value);
-                int size = mCurrentAnimList.size();
+
+                final VPath path = mCurrentPaths[i];
+                final int size = mCurrentAnimList.size();
                 for (int j = 0; j < size; j++) {
-                    VAnimation vAnimation = mCurrentAnimList.get(j);
+                    final VAnimation vAnimation = mCurrentAnimList.get(j);
                     if (vAnimation.doesAdjustPath(path)) {
                         mCurrentPaths[i] =  vAnimation.getPathAtTime(animationTime, path);
                     }
                 }
             }
-            this.mAnimationValue = value;
+
+            mAnimationValue = value;
+
             if (mTotalDuration == -1) {
                 return true;
             } else {
@@ -688,16 +782,15 @@
         }
 
         public void draw(Canvas canvas) {
-            int w = canvas.getWidth();
-            int h = canvas.getHeight();
-            float scale = w / mViewportWidth;
-            scale = Math.min(h / mViewportHeight, scale);
-
             if (mCurrentPaths == null) {
                 Log.e(LOGTAG,"mCurrentPaths == null");
                 return;
             }
 
+            // TODO: This should probably use getBounds().
+            final int w = canvas.getWidth();
+            final int h = canvas.getHeight();
+
             for (int i = 0; i < mCurrentPaths.length; i++) {
                 if (mCurrentPaths[i] != null && mCurrentPaths[i].isVisible(mCurrentState)) {
                     drawPath(mCurrentPaths[i], canvas, w, h);
@@ -706,8 +799,7 @@
         }
 
         private void drawPath(VPath vPath, Canvas canvas, int w, int h) {
-            float scale = w / mViewportWidth;
-            scale = Math.min(h / mViewportHeight, scale);
+            final float scale = Math.min(h / mViewportHeight, w / mViewportWidth);
 
             vPath.toPath(mPath);
             Path path = mPath;
@@ -746,12 +838,14 @@
             if (vPath.mClip) {
                 canvas.clipPath(mRenderPath, Region.Op.REPLACE);
             }
+
             if (vPath.mFillColor != 0) {
                 mFillPaint.setColor(vPath.mFillColor);
                 int alpha = 0xFF & (vPath.mFillColor >> 24);
                 mFillPaint.setAlpha(alpha);
                 canvas.drawPath(mRenderPath, mFillPaint);
             }
+
             if (vPath.mStrokeColor != 0) {
                 if (vPath.mStrokelineJoin != null) {
                     mStrokePaint.setStrokeJoin(vPath.mStrokelineJoin);
@@ -773,7 +867,7 @@
          * TODO: improve memory use & performance or move to C++
          */
         public void parseFinish() {
-            HashMap<String, VAnimation> newAnimations = new HashMap<String, VAnimation>();
+            final HashMap<String, VAnimation> newAnimations = new HashMap<String, VAnimation>();
             for (VGroup group : mGroupList) {
                 for (VPath vPath : group.getPaths()) {
                     if (!vPath.mAnimated) {
@@ -784,16 +878,19 @@
                         } else {
                             ap = newAnimations.get(vPath.getID());
                         }
+
                         ap.addPath(vPath);
                         vPath.mAnimated = true;
                     }
                 }
             }
+
             if (mCurrentAnimList == null) {
                 mCurrentAnimList = new ArrayList<VectorDrawable.VAnimation>();
             }
             mCurrentAnimList.addAll(newAnimations.values());
-            Collection<VPath> paths = mGroupList.get(0).getPaths();
+
+            final Collection<VPath> paths = mGroupList.get(0).getPaths();
             mCurrentPaths = paths.toArray(new VPath[paths.size()]);
             for (int i = 0; i < mCurrentPaths.length; i++) {
                 mCurrentPaths[i] = new VPath(mCurrentPaths[i]);
@@ -854,40 +951,41 @@
     }
 
     private static class VAnimation {
-        private static final String LOGTAG = "VAnimation";
+        private final static int DIRECTION_FORWARD = 0;
+        private final static int DIRECTION_IN_AND_OUT = 1;
+
         private VPath[] mPaths = new VPath[0];
 
         public enum Style {
             INTERPOLATE, CROSSFADE, WIPE
         }
+
         Interpolator mAnimInterpolator = new AccelerateDecelerateInterpolator();
+
+        private int[] mThemeAttrs;
         private Style mStyle;
         private int mLimitProperty = 0;
         private long[] mDuration = {DEFAULT_DURATION};
         private long mStartOffset;
         private long mRepeat = 1;
-        private HashSet<String>mSeqMap = new HashSet<String>();
+        private HashSet<String> mSeqMap = new HashSet<String>();
         private long mWipeDirection;
         private int mMode = 0; // forward = 0 inAndOut = 1;
         private int mInterpolatorType;
         private String mId;
-        private final static int DIRECTION_FORWARD = 0;
-        private final static int DIRECTION_IN_AND_OUT = 1;
 
         public VAnimation() {
+            // Empty constructor.
         }
 
-        public boolean doesAdjustPath(VPath path) {
-            return mSeqMap.contains(path.getID());
-        }
-
-        public VAnimation(Resources r, AttributeSet attrs, ArrayList<VGroup> groups)
+        public void inflate(ArrayList<VGroup> groups, Resources r, AttributeSet attrs, Theme theme)
                 throws XmlPullParserException {
             String value;
             String[] sp;
             int name;
 
-            TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableAnimation);
+            final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableAnimation);
+            mThemeAttrs = a.extractThemeAttrs();
 
             value = a.getString(R.styleable.VectorDrawableAnimation_sequence);
             if (value != null) {
@@ -910,28 +1008,42 @@
             if (value != null) {
                 long totalDuration = 0;
                 sp = value.split(",");
-                long[] dur = new long[sp.length];
+
+                final long[] dur = new long[sp.length];
                 for (int j = 0; j < dur.length; j++) {
                     dur[j] = Long.parseLong(sp[j]);
                     totalDuration +=  dur[j];
                 }
+
                 if (totalDuration == 0){
                     throw new XmlPullParserException(a.getPositionDescription()+
                             "total duration must not be zero");
                 }
+
                 setDuration(dur);
             }
 
             setRepeat(a.getInt(R.styleable.VectorDrawableAnimation_repeatCount, 1));
-
             setStartOffset(a.getInt(R.styleable.VectorDrawableAnimation_startDelay, 0));
-
             setMode(a.getInt(R.styleable.VectorDrawableAnimation_repeatStyle, 0));
 
             fixMissingParameters();
+
             a.recycle();
         }
 
+        public boolean canApplyTheme() {
+            return mThemeAttrs != null;
+        }
+
+        public void applyTheme(Theme t) {
+            // TODO: Apply theme.
+        }
+
+        public boolean doesAdjustPath(VPath path) {
+            return mSeqMap.contains(path.getID());
+        }
+
         public String getId() {
             if (mId == null) {
                 mId = mPaths[0].getID();
@@ -1031,7 +1143,7 @@
         }
 
         public void setPaths(VPath[] paths) {
-            this.mPaths = paths;
+            mPaths = paths;
         }
 
         public void addPath(VPath path) {
@@ -1049,7 +1161,7 @@
         }
 
         public void interpolate(VPath p1, VPath p2, float time, VPath dest) {
-            dest.interpolate(time, p1, p2, dest, mLimitProperty);
+            VPath.interpolate(time, p1, p2, dest, mLimitProperty);
         }
 
         public VPath getPathAtTime(long milliseconds, VPath dest) {
@@ -1150,8 +1262,8 @@
     }
 
     private static class VGroup {
-        private HashMap<String, VPath> mVGPathMap = new HashMap<String, VPath>();
-        private ArrayList<VPath> mVGList = new ArrayList<VPath>();
+        private final HashMap<String, VPath> mVGPathMap = new HashMap<String, VPath>();
+        private final ArrayList<VPath> mVGList = new ArrayList<VPath>();
 
         public void add(VPath path) {
             String id = path.getID();
@@ -1183,34 +1295,52 @@
         private static final int LIMIT_TRIM_PATH_START = 3;
         private static final int LIMIT_TRIM_PATH_OFFSET = 5;
         private static final int LIMIT_TRIM_PATH_END = 4;
+
         private static final int STATE_UNDEFINED=0;
         private static final int STATE_TRUE=1;
         private static final int STATE_FALSE=2;
+
         private static final int MAX_STATES = 10;
-        private VNode[] mNode = null;
-        private String mId;
+
+        private int[] mThemeAttrs;
+
         int mStrokeColor = 0;
         float mStrokeWidth = 0;
         float mStrokeOpacity = Float.NaN;
+
         int mFillColor = 0;
         int mFillRule;
         float mFillOpacity = Float.NaN;
+
         float mRotate = 0;
         float mPivotX = 0;
         float mPivotY = 0;
+
         float mTrimPathStart = 0;
         float mTrimPathEnd = 1;
         float mTrimPathOffset = 0;
+
         boolean mAnimated = false;
         boolean mClip = false;
-        public Paint.Cap mStrokelineCap = null;
-        public Paint.Join mStrokelineJoin = null;
+        Paint.Cap mStrokelineCap = null;
+        Paint.Join mStrokelineJoin = null;
         float mStrokeMiterlimit = 4;
+
+        private VNode[] mNode = null;
+        private String mId;
         private int[] mCheckState = new int[MAX_STATES];
         private boolean[] mCheckValue = new boolean[MAX_STATES];
         private int mNumberOfStates = 0;
         private int mNumberOfTrue = 0;
 
+        public VPath() {
+            // Empty constructor.
+        }
+
+        public VPath(VPath p) {
+            copyFrom(p);
+        }
+
         public void addStateFilter(int state, boolean condition) {
             int k = 0;
             while (k < mNumberOfStates) {
@@ -1228,7 +1358,7 @@
             }
         }
 
-        int getState(int state){
+        private int getState(int state){
             for (int i = 0; i < mNumberOfStates; i++) {
                 if (mCheckState[mNumberOfStates] == state){
                     return (mCheckValue[i])?STATE_TRUE:STATE_FALSE;
@@ -1250,34 +1380,33 @@
             }
         }
 
-        public VPath() {
-            mId = this.toString(); // to ensure paths have unique names
-        }
-
-        public VPath(VPath p) {
-            copyFrom(p);
-        }
-
         public String getID(){
             return mId;
         }
 
-        public VPath(Resources r, AttributeSet attrs) {
-            TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawablePath);
+        public void inflate(Resources r, AttributeSet attrs, Theme theme) {
+            final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawablePath);
+            final int[] themeAttrs = a.extractThemeAttrs();
+            mThemeAttrs = themeAttrs;
+
             mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, false);
             mId = a.getString(R.styleable.VectorDrawablePath_name);
             mNode = parsePath(a.getString(R.styleable.VectorDrawablePath_pathData));
-            mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, 0);
-            mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, Float.NaN);
 
-            if (!Float.isNaN(mFillOpacity)) {
-                mFillColor &= 0x00FFFFFF;
-                mFillColor |= ((int) (0xFF * mFillOpacity)) << 24;
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_fill] == 0) {
+                mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, 0);
             }
+
+            if (themeAttrs == null
+                    || themeAttrs[R.styleable.VectorDrawablePath_fillOpacity] == 0) {
+                mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, Float.NaN);
+            }
+
             mRotate = a.getFloat(R.styleable.VectorDrawablePath_rotation, 0);
             mPivotX = a.getFloat(R.styleable.VectorDrawablePath_pivotX, 0);
             mPivotY = a.getFloat(R.styleable.VectorDrawablePath_pivotY, 0);
-            int lineCap  = a.getInt(R.styleable.VectorDrawablePath_strokeLineCap, 0);
+
+            final int lineCap  = a.getInt(R.styleable.VectorDrawablePath_strokeLineCap, 0);
             switch (lineCap) {
                 case LINECAP_BUTT:
                     mStrokelineCap = Paint.Cap.BUTT;
@@ -1289,7 +1418,8 @@
                     mStrokelineCap = Paint.Cap.SQUARE;
                     break;
             }
-            int lineJoin =  a.getInt(R.styleable.VectorDrawablePath_strokeLineJoin, 0);
+
+            final int lineJoin =  a.getInt(R.styleable.VectorDrawablePath_strokeLineJoin, 0);
             switch (lineJoin) {
                 case LINEJOIN_MITER:
                     mStrokelineJoin = Paint.Join.MITER;
@@ -1301,19 +1431,27 @@
                     mStrokelineJoin = Paint.Join.BEVEL;
                     break;
             }
+
             mStrokeMiterlimit = a.getFloat(R.styleable.VectorDrawablePath_strokeMiterLimit,
                     mStrokeMiterlimit);
-            mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_stroke, mStrokeColor);
-            mStrokeOpacity = a.getFloat(R.styleable.VectorDrawablePath_strokeOpacity, Float.NaN);
-            if (!Float.isNaN(mStrokeOpacity)) {
-                mStrokeColor &= 0x00FFFFFF;
-                mStrokeColor |= ((int) (0xFF * mStrokeOpacity)) << 24;
+
+            if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_stroke] == 0) {
+                mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_stroke, mStrokeColor);
             }
+
+            if (themeAttrs == null
+                    || themeAttrs[R.styleable.VectorDrawablePath_strokeOpacity] == 0) {
+                mStrokeOpacity = a.getFloat(
+                        R.styleable.VectorDrawablePath_strokeOpacity, Float.NaN);
+            }
+
             mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, 0);
             mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, 1);
             mTrimPathOffset = a.getFloat(R.styleable.VectorDrawablePath_trimPathOffset, 0);
             mTrimPathStart = a.getFloat(R.styleable.VectorDrawablePath_trimPathStart, 0);
-            int[] states = {R.styleable.VectorDrawablePath_state_activated,
+
+            final int[] states = {
+                    R.styleable.VectorDrawablePath_state_activated,
                     R.styleable.VectorDrawablePath_state_checkable,
                     R.styleable.VectorDrawablePath_state_checked,
                     R.styleable.VectorDrawablePath_state_enabled,
@@ -1321,15 +1459,66 @@
                     R.styleable.VectorDrawablePath_state_hovered,
                     R.styleable.VectorDrawablePath_state_pressed,
                     R.styleable.VectorDrawablePath_state_selected,
-                    R.styleable.VectorDrawablePath_state_window_focused};
-            for (int state : states) {
+                    R.styleable.VectorDrawablePath_state_window_focused
+            };
+
+            final int N = states.length;
+            for (int i = 0; i < N; i++) {
+                final int state = states[i];
                 if (a.hasValue(state)) {
                     addStateFilter(state, a.getBoolean(state, false));
                 }
             }
+
+            updateColorAlphas();
+
             a.recycle();
         }
 
+        public boolean canApplyTheme() {
+            return mThemeAttrs != null;
+        }
+
+        public void applyTheme(Theme t) {
+            if (mThemeAttrs == null) {
+                return;
+            }
+
+            final TypedArray a = t.resolveAttributes(
+                    mThemeAttrs, R.styleable.VectorDrawablePath, 0, 0);
+
+            if (a.hasValue(R.styleable.VectorDrawablePath_fill)) {
+                mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, 0);
+            }
+
+            if (a.hasValue(R.styleable.VectorDrawablePath_fillOpacity)) {
+                mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, Float.NaN);
+            }
+
+            if (a.hasValue(R.styleable.VectorDrawablePath_stroke)) {
+                mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_stroke, mStrokeColor);
+            }
+
+            if (a.hasValue(R.styleable.VectorDrawablePath_strokeOpacity)) {
+                mStrokeOpacity = a.getFloat(
+                        R.styleable.VectorDrawablePath_strokeOpacity, Float.NaN);
+            }
+
+            updateColorAlphas();
+        }
+
+        private void updateColorAlphas() {
+            if (!Float.isNaN(mFillOpacity)) {
+                mFillColor &= 0x00FFFFFF;
+                mFillColor |= ((int) (0xFF * mFillOpacity)) << 24;
+            }
+
+            if (!Float.isNaN(mStrokeOpacity)) {
+                mStrokeColor &= 0x00FFFFFF;
+                mStrokeColor |= ((int) (0xFF * mStrokeOpacity)) << 24;
+            }
+        }
+
         private static int nextStart(String s, int end) {
             char c;
 
@@ -1564,9 +1753,11 @@
     }
 
     private static class VNode {
+        private static float[] current = new float[4];
+
         char type;
         float[] params;
-        private static float[] current = new float[4];
+
         public VNode(char type, float[] params) {
             this.type = type;
             this.params = params;