Allow an icon to carry a tint with it, wherever it goes.

Bug: 21031774
Change-Id: I2d06c7288e0c0a06bf6ff147dfbfdea5a46fd288
diff --git a/api/current.txt b/api/current.txt
index ec961be..ed05d99 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12480,6 +12480,9 @@
     method public android.graphics.drawable.Drawable loadDrawable(android.content.Context);
     method public void loadDrawableAsync(android.content.Context, android.os.Message);
     method public void loadDrawableAsync(android.content.Context, android.graphics.drawable.Icon.OnDrawableLoadedListener, android.os.Handler);
+    method public android.graphics.drawable.Icon setTint(int);
+    method public android.graphics.drawable.Icon setTintList(android.content.res.ColorStateList);
+    method public android.graphics.drawable.Icon setTintMode(android.graphics.PorterDuff.Mode);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.graphics.drawable.Icon> CREATOR;
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 7411cd1..5ecc791 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -12806,6 +12806,9 @@
     method public android.graphics.drawable.Drawable loadDrawable(android.content.Context);
     method public void loadDrawableAsync(android.content.Context, android.os.Message);
     method public void loadDrawableAsync(android.content.Context, android.graphics.drawable.Icon.OnDrawableLoadedListener, android.os.Handler);
+    method public android.graphics.drawable.Icon setTint(int);
+    method public android.graphics.drawable.Icon setTintList(android.content.res.ColorStateList);
+    method public android.graphics.drawable.Icon setTintMode(android.graphics.PorterDuff.Mode);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.graphics.drawable.Icon> CREATOR;
   }
diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java
index dcccf35..2bbbff3 100644
--- a/graphics/java/android/graphics/PorterDuff.java
+++ b/graphics/java/android/graphics/PorterDuff.java
@@ -67,4 +67,38 @@
          */
         public final int nativeInt;
     }
+
+    /**
+     * @hide
+     */
+    public static final int modeToInt(Mode mode) {
+        return mode.nativeInt;
+    }
+
+    /**
+     * @hide
+     */
+    public static final Mode intToMode(int val) {
+        switch (val) {
+            default:
+            case  0: return Mode.CLEAR;
+            case  1: return Mode.SRC;
+            case  2: return Mode.DST;
+            case  3: return Mode.SRC_OVER;
+            case  4: return Mode.DST_OVER;
+            case  5: return Mode.SRC_IN;
+            case  6: return Mode.DST_IN;
+            case  7: return Mode.SRC_OUT;
+            case  8: return Mode.DST_OUT;
+            case  9: return Mode.SRC_ATOP;
+            case 10: return Mode.DST_ATOP;
+            case 11: return Mode.XOR;
+            case 16: return Mode.DARKEN;
+            case 17: return Mode.LIGHTEN;
+            case 13: return Mode.MULTIPLY;
+            case 14: return Mode.SCREEN;
+            case 12: return Mode.ADD;
+            case 15: return Mode.OVERLAY;
+        }
+    }
 }
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 85db6a1..425225c 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -16,13 +16,16 @@
 
 package android.graphics.drawable;
 
+import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
+import android.content.res.ColorStateList;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.PorterDuff;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -67,6 +70,10 @@
 
     private final int mType;
 
+    private ColorStateList mTintList;
+    static final PorterDuff.Mode DEFAULT_TINT_MODE = Drawable.DEFAULT_TINT_MODE; // SRC_IN
+    private PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;
+
     // To avoid adding unnecessary overhead, we have a few basic objects that get repurposed
     // based on the value of mType.
 
@@ -254,6 +261,19 @@
      * @return A fresh instance of a drawable for this image, yours to keep.
      */
     public Drawable loadDrawable(Context context) {
+        final Drawable result = loadDrawableInner(context);
+        if (result != null && (mTintList != null || mTintMode != DEFAULT_TINT_MODE)) {
+            result.mutate();
+            result.setTintList(mTintList);
+            result.setTintMode(mTintMode);
+        }
+        return result;
+    }
+
+    /**
+     * Do the heavy lifting of loading the drawable, but stop short of applying any tint.
+     */
+    private Drawable loadDrawableInner(Context context) {
         switch (mType) {
             case TYPE_BITMAP:
                 return new BitmapDrawable(context.getResources(), getBitmap());
@@ -518,6 +538,38 @@
     }
 
     /**
+     * Store a color to use whenever this Icon is drawn.
+     *
+     * @param tint a color, as in {@link Drawable#setTint(int)}
+     * @return this same object, for use in chained construction
+     */
+    public Icon setTint(@ColorInt int tint) {
+        return setTintList(ColorStateList.valueOf(tint));
+    }
+
+    /**
+     * Store a color to use whenever this Icon is drawn.
+     *
+     * @param tintList as in {@link Drawable#setTintList(ColorStateList)}, null to remove tint
+     * @return this same object, for use in chained construction
+     */
+    public Icon setTintList(ColorStateList tintList) {
+        mTintList = tintList;
+        return this;
+    }
+
+    /**
+     * Store a blending mode to use whenever this Icon is drawn.
+     *
+     * @param mode a blending mode, as in {@link Drawable#setTintMode(PorterDuff.Mode)}, may be null
+     * @return this same object, for use in chained construction
+     */
+    public Icon setTintMode(PorterDuff.Mode mode) {
+        mTintMode = mode;
+        return this;
+    }
+
+    /**
      * Create an Icon pointing to an image file specified by path.
      *
      * @param path A path to a file that contains compressed bitmap data of
@@ -558,6 +610,15 @@
                 sb.append(" uri=").append(getUriString());
                 break;
         }
+        if (mTintList != null) {
+            sb.append(" tint=");
+            String sep = "";
+            for (int c : mTintList.getColors()) {
+                sb.append(String.format("%s0x%08x", sep, c));
+                sep = "|";
+            }
+        }
+        if (mTintMode != DEFAULT_TINT_MODE) sb.append(" mode=").append(mTintMode);
         sb.append(")");
         return sb.toString();
     }
@@ -603,31 +664,39 @@
                 throw new RuntimeException("invalid "
                         + this.getClass().getSimpleName() + " type in parcel: " + mType);
         }
+        if (in.readInt() == 1) {
+            mTintList = ColorStateList.CREATOR.createFromParcel(in);
+        }
+        mTintMode = PorterDuff.intToMode(in.readInt());
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
         switch (mType) {
             case TYPE_BITMAP:
                 final Bitmap bits = getBitmap();
-                dest.writeInt(TYPE_BITMAP);
                 getBitmap().writeToParcel(dest, flags);
                 break;
             case TYPE_RESOURCE:
-                dest.writeInt(TYPE_RESOURCE);
                 dest.writeString(getResPackage());
                 dest.writeInt(getResId());
                 break;
             case TYPE_DATA:
-                dest.writeInt(TYPE_DATA);
                 dest.writeInt(getDataLength());
                 dest.writeBlob(getDataBytes(), getDataOffset(), getDataLength());
                 break;
             case TYPE_URI:
-                dest.writeInt(TYPE_URI);
                 dest.writeString(getUriString());
                 break;
         }
+        if (mTintList == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            mTintList.writeToParcel(dest, flags);
+        }
+        dest.writeInt(PorterDuff.modeToInt(mTintMode));
     }
 
     public static final Parcelable.Creator<Icon> CREATOR