Merge "Add support for using ColorStateList as GradientDrawable's stroke"
diff --git a/api/current.txt b/api/current.txt
index 3950924..9f80ef6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7634,11 +7634,9 @@
     ctor public ColorStateList(int[][], int[]);
     method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public int describeContents();
-    method public int getColorAt(int);
     method public int getColorForState(int[], int);
-    method public int getCount();
     method public int getDefaultColor();
-    method public int[] getStateSpecAt(int);
+    method public boolean isOpaque();
     method public boolean isStateful();
     method public static android.content.res.ColorStateList valueOf(int);
     method public android.content.res.ColorStateList withAlpha(int);
@@ -10284,6 +10282,7 @@
     method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public android.graphics.drawable.GradientDrawable.Orientation getOrientation();
+    method public boolean onStateChange(int[]);
     method public void setAlpha(int);
     method public void setColor(int);
     method public void setColor(android.content.res.ColorStateList);
@@ -10298,7 +10297,9 @@
     method public void setShape(int);
     method public void setSize(int, int);
     method public void setStroke(int, int);
+    method public void setStroke(int, android.content.res.ColorStateList);
     method public void setStroke(int, int, float, float);
+    method public void setStroke(int, android.content.res.ColorStateList, float, float);
     method public void setUseLevel(boolean);
     field public static final int LINE = 2; // 0x2
     field public static final int LINEAR_GRADIENT = 0; // 0x0
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index fe9305e..431226a 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import android.graphics.Color;
 import com.android.internal.util.ArrayUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -259,7 +260,17 @@
     public boolean isStateful() {
         return mStateSpecs.length > 1;
     }
-    
+
+    public boolean isOpaque() {
+        final int n = mColors.length;
+        for (int i = 0; i < n; i++) {
+            if (Color.alpha(mColors[i]) != 0xFF) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
      * Return the color associated with the given set of {@link android.view.View} states.
      *
@@ -281,31 +292,6 @@
     }
 
     /**
-     * @return the number of state spec to color mappings in the list
-     * @see #getColorAt(int)
-     * @see #getStateSpecAt(int)
-     */
-    public int getCount() {
-        return mColors.length;
-    }
-
-    /**
-     * @return the state spec at the specified index in the list
-     * @see #getCount()
-     */
-    public int[] getStateSpecAt(int index) {
-        return mStateSpecs[index];
-    }
-
-    /**
-     * @return the color at the specified index in the list
-     * @see #getCount()
-     */
-    public int getColorAt(int index) {
-        return mColors[index];
-    }
-
-    /**
      * Return the default color in this {@link ColorStateList}.
      *
      * @return the default color in this {@link ColorStateList}.
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 27618a5..e51dfbc 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -234,6 +234,23 @@
     }
 
     /**
+     * <p>Set the stroke width and color state list for the drawable. If width
+     * is zero, then no stroke is drawn.</p>
+     * <p><strong>Note</strong>: changing this property will affect all instances
+     * of a drawable loaded from a resource. It is recommended to invoke
+     * {@link #mutate()} before changing this property.</p>
+     *
+     * @param width The width in pixels of the stroke
+     * @param colorStateList The color state list of the stroke
+     *
+     * @see #mutate()
+     * @see #setStroke(int, ColorStateList, float, float)
+     */
+    public void setStroke(int width, ColorStateList colorStateList) {
+        setStroke(width, colorStateList, 0, 0);
+    }
+
+    /**
      * <p>Set the stroke width and color for the drawable. If width is zero,
      * then no stroke is drawn. This method can also be used to dash the stroke.</p>
      * <p><strong>Note</strong>: changing this property will affect all instances
@@ -250,7 +267,35 @@
      */
     public void setStroke(int width, int color, float dashWidth, float dashGap) {
         mGradientState.setStroke(width, color, dashWidth, dashGap);
+        setStrokeInternal(width, color, dashWidth, dashGap);
+    }
 
+    /**
+     * <p>Set the stroke width and color state list for the drawable. If width
+     * is zero, then no stroke is drawn. This method can also be used to dash
+     * the stroke.</p>
+     * <p><strong>Note</strong>: changing this property will affect all instances
+     * of a drawable loaded from a resource. It is recommended to invoke
+     * {@link #mutate()} before changing this property.</p>
+     *
+     * @param width The width in pixels of the stroke
+     * @param colorStateList The color state list of the stroke
+     * @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes
+     * @param dashGap The gap in pixels between dashes
+     *
+     * @see #mutate()
+     * @see #setStroke(int, ColorStateList)
+     */
+    public void setStroke(
+            int width, ColorStateList colorStateList, float dashWidth, float dashGap) {
+        mGradientState.setStroke(width, colorStateList, dashWidth, dashGap);
+
+        final int[] stateSet = getState();
+        final int color = colorStateList.getColorForState(stateSet, 0);
+        setStrokeInternal(width, color, dashWidth, dashGap);
+    }
+
+    private void setStrokeInternal(int width, int color, float dashWidth, float dashGap) {
         if (mStrokePaint == null)  {
             mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
             mStrokePaint.setStyle(Paint.Style.STROKE);
@@ -647,24 +692,44 @@
     }
 
     @Override
-    public boolean setState(int[] stateSet) {
-        final ColorStateList stateList = mGradientState.mColorStateList;
+    public boolean onStateChange(int[] stateSet) {
+        boolean invalidateSelf = false;
+
+        final GradientState s = mGradientState;
+        final ColorStateList stateList = s.mColorStateList;
         if (stateList != null) {
             final int newColor = stateList.getColorForState(stateSet, 0);
             final int oldColor = mFillPaint.getColor();
             if (oldColor != newColor) {
                 mFillPaint.setColor(newColor);
-                invalidateSelf();
-                return true;
+                invalidateSelf |= true;
             }
         }
 
-        return super.setState(stateSet);
+        final ColorStateList strokeStateList = s.mStrokeColorStateList;
+        if (strokeStateList != null) {
+            final int newColor = stateList.getColorForState(stateSet, 0);
+            final int oldColor = mStrokePaint.getColor();
+            if (oldColor != newColor) {
+                mStrokePaint.setColor(newColor);
+                invalidateSelf |= true;
+            }
+        }
+
+        if (invalidateSelf) {
+            invalidateSelf();
+            return true;
+        }
+
+        return false;
     }
 
     @Override
     public boolean isStateful() {
-        return super.isStateful() || mGradientState.mColorStateList != null;
+        final GradientState s = mGradientState;
+        return super.isStateful()
+                || (s.mColorStateList != null && s.mColorStateList.isStateful())
+                || (s.mStrokeColorStateList != null && s.mStrokeColorStateList.isStateful());
     }
 
     @Override
@@ -1015,18 +1080,18 @@
             } else if (name.equals("stroke")) {
                 a = r.obtainAttributes(attrs,
                         com.android.internal.R.styleable.GradientDrawableStroke);
-                int width = a.getDimensionPixelSize(
+                final int width = a.getDimensionPixelSize(
                         com.android.internal.R.styleable.GradientDrawableStroke_width, 0);
-                int color = a.getColor(
-                        com.android.internal.R.styleable.GradientDrawableStroke_color, 0);
-                float dashWidth = a.getDimension(
+                final ColorStateList colorStateList = a.getColorStateList(
+                        com.android.internal.R.styleable.GradientDrawableStroke_color);
+                final float dashWidth = a.getDimension(
                         com.android.internal.R.styleable.GradientDrawableStroke_dashWidth, 0);
                 if (dashWidth != 0.0f) {
-                    float dashGap = a.getDimension(
+                    final float dashGap = a.getDimension(
                             com.android.internal.R.styleable.GradientDrawableStroke_dashGap, 0);
-                    setStroke(width, color, dashWidth, dashGap);
+                    setStroke(width, colorStateList, dashWidth, dashGap);
                 } else {
-                    setStroke(width, color);
+                    setStroke(width, colorStateList);
                 }
                 a.recycle();
             } else if (name.equals("corners")) {
@@ -1119,6 +1184,7 @@
         public int mGradient = LINEAR_GRADIENT;
         public Orientation mOrientation;
         public ColorStateList mColorStateList;
+        public ColorStateList mStrokeColorStateList;
         public int[] mColors;
         public int[] mTempColors; // no need to copy
         public float[] mTempPositions; // no need to copy
@@ -1251,12 +1317,19 @@
                 return;
             }
 
-            if (mStrokeWidth > 0 && !isOpaque(mStrokeColor)) {
-                mOpaque = false;
-                return;
+            if (mStrokeWidth > 0) {
+                if (mStrokeColorStateList != null) {
+                    if (!mStrokeColorStateList.isOpaque()) {
+                        mOpaque = false;
+                        return;
+                    }
+                } else if (!isOpaque(mStrokeColor)) {
+                    mOpaque = false;
+                    return;
+                }
             }
 
-            if (mColorStateList != null && !isOpaque(mColorStateList)) {
+            if (mColorStateList != null && !mColorStateList.isOpaque()) {
                 mOpaque = false;
                 return;
             }
@@ -1282,25 +1355,26 @@
             return ((color >> 24) & 0xff) == 0xff;
         }
 
-        private static boolean isOpaque(ColorStateList colors) {
-            final int n = colors.getCount();
-            for (int i = 0; i < n; i++) {
-                if (!isOpaque(colors.getColorAt(i))) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
         public void setStroke(int width, int color) {
             mStrokeWidth = width;
             mStrokeColor = color;
+            mStrokeColorStateList = null;
             computeOpacity();
         }
 
         public void setStroke(int width, int color, float dashWidth, float dashGap) {
             mStrokeWidth = width;
             mStrokeColor = color;
+            mStrokeColorStateList = null;
+            mStrokeDashWidth = dashWidth;
+            mStrokeDashGap = dashGap;
+            computeOpacity();
+        }
+
+        public void setStroke(
+                int width, ColorStateList colorStateList, float dashWidth, float dashGap) {
+            mStrokeWidth = width;
+            mStrokeColorStateList = colorStateList;
             mStrokeDashWidth = dashWidth;
             mStrokeDashGap = dashGap;
             computeOpacity();