Move legacy notification processing to Notification.Builder

Bug: 13485610
Change-Id: I5466d3dbc328c77876dc701c17e7a5a06777dbbe
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 13e74da..36d2635 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -17,12 +17,14 @@
 package android.app;
 
 import com.android.internal.R;
+import com.android.internal.util.LegacyNotificationUtil;
 
 import android.annotation.IntDef;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.PorterDuff;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.BadParcelableException;
@@ -653,13 +655,6 @@
     public static final String EXTRA_AS_HEADS_UP = "headsup";
 
     /**
-     * Extra added from {@link Notification.Builder} to indicate that the remote views were inflated
-     * from the builder, as opposed to being created directly from the application.
-     * @hide
-     */
-    public static final String EXTRA_BUILDER_REMOTE_VIEWS = "android.builderRemoteViews";
-
-    /**
      * Allow certain system-generated notifications to appear before the device is provisioned.
      * Only available to notifications coming from the android package.
      * @hide
@@ -1315,6 +1310,7 @@
         private int mVisibility = VISIBILITY_PRIVATE;
         private Notification mPublicVersion = null;
         private boolean mQuantumTheme;
+        private final LegacyNotificationUtil mLegacyNotificationUtil;
 
         /**
          * Constructs a new Builder with the defaults:
@@ -1345,6 +1341,10 @@
 
             // TODO: Decide on targetSdk from calling app whether to use quantum theme.
             mQuantumTheme = true;
+
+            // TODO: Decide on targetSdk from calling app whether to instantiate the processor at
+            // all.
+            mLegacyNotificationUtil = LegacyNotificationUtil.getInstance();
         }
 
         /**
@@ -1846,42 +1846,50 @@
             boolean showLine3 = false;
             boolean showLine2 = false;
             int smallIconImageViewId = R.id.icon;
-            if (mLargeIcon != null) {
-                contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
-                smallIconImageViewId = R.id.right_icon;
-            }
             if (!mQuantumTheme && mPriority < PRIORITY_LOW) {
                 contentView.setInt(R.id.icon,
                         "setBackgroundResource", R.drawable.notification_template_icon_low_bg);
                 contentView.setInt(R.id.status_bar_latest_event_content,
                         "setBackgroundResource", R.drawable.notification_bg_low);
             }
+            if (mLargeIcon != null) {
+                contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
+                processLegacyLargeIcon(mLargeIcon, contentView);
+                smallIconImageViewId = R.id.right_icon;
+            }
             if (mSmallIcon != 0) {
                 contentView.setImageViewResource(smallIconImageViewId, mSmallIcon);
                 contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE);
+                if (mLargeIcon != null) {
+                    processLegacySmallIcon(mSmallIcon, smallIconImageViewId, contentView);
+                } else {
+                    processLegacyLargeIcon(mSmallIcon, contentView);
+                }
+
             } else {
                 contentView.setViewVisibility(smallIconImageViewId, View.GONE);
             }
             if (mContentTitle != null) {
-                contentView.setTextViewText(R.id.title, mContentTitle);
+                contentView.setTextViewText(R.id.title, processLegacyText(mContentTitle));
             }
             if (mContentText != null) {
-                contentView.setTextViewText(R.id.text, mContentText);
+                contentView.setTextViewText(R.id.text, processLegacyText(mContentText));
                 showLine3 = true;
             }
             if (mContentInfo != null) {
-                contentView.setTextViewText(R.id.info, mContentInfo);
+                contentView.setTextViewText(R.id.info, processLegacyText(mContentInfo));
                 contentView.setViewVisibility(R.id.info, View.VISIBLE);
                 showLine3 = true;
             } else if (mNumber > 0) {
                 final int tooBig = mContext.getResources().getInteger(
                         R.integer.status_bar_notification_info_maxnum);
                 if (mNumber > tooBig) {
-                    contentView.setTextViewText(R.id.info, mContext.getResources().getString(
-                                R.string.status_bar_notification_info_overflow));
+                    contentView.setTextViewText(R.id.info, processLegacyText(
+                            mContext.getResources().getString(
+                                    R.string.status_bar_notification_info_overflow)));
                 } else {
                     NumberFormat f = NumberFormat.getIntegerInstance();
-                    contentView.setTextViewText(R.id.info, f.format(mNumber));
+                    contentView.setTextViewText(R.id.info, processLegacyText(f.format(mNumber)));
                 }
                 contentView.setViewVisibility(R.id.info, View.VISIBLE);
                 showLine3 = true;
@@ -1891,9 +1899,9 @@
 
             // Need to show three lines?
             if (mSubText != null) {
-                contentView.setTextViewText(R.id.text, mSubText);
+                contentView.setTextViewText(R.id.text, processLegacyText(mSubText));
                 if (mContentText != null) {
-                    contentView.setTextViewText(R.id.text2, mContentText);
+                    contentView.setTextViewText(R.id.text2, processLegacyText(mContentText));
                     contentView.setViewVisibility(R.id.text2, View.VISIBLE);
                     showLine2 = true;
                 } else {
@@ -2001,15 +2009,78 @@
                     tombstone ? getActionTombstoneLayoutResource()
                               : getActionLayoutResource());
             button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0);
-            button.setTextViewText(R.id.action0, action.title);
+            button.setTextViewText(R.id.action0, processLegacyText(action.title));
             if (!tombstone) {
                 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
             }
             button.setContentDescription(R.id.action0, action.title);
+            processLegacyAction(action, button);
             return button;
         }
 
         /**
+         * @return Whether we are currently building a notification from a legacy (an app that
+         *         doesn't create quantum notifications by itself) app.
+         */
+        private boolean isLegacy() {
+            return mLegacyNotificationUtil != null;
+        }
+
+        private void processLegacyAction(Action action, RemoteViews button) {
+            if (isLegacy()) {
+                if (mLegacyNotificationUtil.isGrayscale(mContext, action.icon)) {
+                    button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
+                            mContext.getResources().getColor(
+                                    R.color.notification_action_legacy_color_filter),
+                            PorterDuff.Mode.MULTIPLY);
+                }
+            }
+        }
+
+        private CharSequence processLegacyText(CharSequence charSequence) {
+            if (isLegacy()) {
+                return mLegacyNotificationUtil.invertCharSequenceColors(charSequence);
+            } else {
+                return charSequence;
+            }
+        }
+
+        private void processLegacyLargeIcon(int largeIconId, RemoteViews contentView) {
+            if (isLegacy()) {
+                processLegacyLargeIcon(
+                        mLegacyNotificationUtil.isGrayscale(mContext, largeIconId),
+                        contentView);
+            }
+        }
+
+        private void processLegacyLargeIcon(Bitmap largeIcon, RemoteViews contentView) {
+            if (isLegacy()) {
+                processLegacyLargeIcon(
+                        mLegacyNotificationUtil.isGrayscale(largeIcon),
+                        contentView);
+            }
+        }
+
+        private void processLegacyLargeIcon(boolean isGrayscale, RemoteViews contentView) {
+            if (isLegacy() && isGrayscale) {
+                contentView.setInt(R.id.icon, "setBackgroundResource",
+                        R.drawable.notification_icon_legacy_bg_inset);
+            }
+        }
+
+        private void processLegacySmallIcon(int smallIconDrawableId, int smallIconImageViewId,
+                RemoteViews contentView) {
+            if (isLegacy()) {
+                if (mLegacyNotificationUtil.isGrayscale(mContext, smallIconDrawableId)) {
+                    contentView.setDrawableParameters(smallIconImageViewId, false, -1,
+                            mContext.getResources().getColor(
+                                    R.color.notification_action_legacy_color_filter),
+                            PorterDuff.Mode.MULTIPLY, -1);
+                }
+            }
+        }
+
+        /**
          * Apply the unstyled operations and return a new {@link Notification} object.
          * @hide
          */
@@ -2075,7 +2146,6 @@
             extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate);
             extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer);
             extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen);
-            extras.putBoolean(EXTRA_BUILDER_REMOTE_VIEWS, mContentView == null);
             if (mLargeIcon != null) {
                 extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
             }
@@ -2226,7 +2296,7 @@
                     mSummaryTextSet ? mSummaryText
                                     : mBuilder.mSubText;
             if (overflowText != null) {
-                contentView.setTextViewText(R.id.text, overflowText);
+                contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
                 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
                 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
             } else {
@@ -2437,7 +2507,7 @@
                 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0);
             }
 
-            contentView.setTextViewText(R.id.big_text, mBigText);
+            contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
             contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
             contentView.setViewVisibility(R.id.text2, View.GONE);
 
@@ -2542,7 +2612,7 @@
                 CharSequence str = mTexts.get(i);
                 if (str != null && !str.equals("")) {
                     contentView.setViewVisibility(rowIds[i], View.VISIBLE);
-                    contentView.setTextViewText(rowIds[i], str);
+                    contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
                 }
                 i++;
             }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 0d3df51..f7d20b53 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1516,6 +1516,75 @@
     }
 
     /**
+     * Helper action to set a color filter on a compound drawable on a TextView. Supports relative
+     * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
+     */
+    private class TextViewDrawableColorFilterAction extends Action {
+        public TextViewDrawableColorFilterAction(int viewId, boolean isRelative, int index,
+                int color, PorterDuff.Mode mode) {
+            this.viewId = viewId;
+            this.isRelative = isRelative;
+            this.index = index;
+            this.color = color;
+            this.mode = mode;
+        }
+
+        public TextViewDrawableColorFilterAction(Parcel parcel) {
+            viewId = parcel.readInt();
+            isRelative = (parcel.readInt() != 0);
+            index = parcel.readInt();
+            color = parcel.readInt();
+            mode = readPorterDuffMode(parcel);
+        }
+
+        private PorterDuff.Mode readPorterDuffMode(Parcel parcel) {
+            int mode = parcel.readInt();
+            if (mode >= 0 && mode < PorterDuff.Mode.values().length) {
+                return PorterDuff.Mode.values()[mode];
+            } else {
+                return PorterDuff.Mode.CLEAR;
+            }
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(TAG);
+            dest.writeInt(viewId);
+            dest.writeInt(isRelative ? 1 : 0);
+            dest.writeInt(index);
+            dest.writeInt(color);
+            dest.writeInt(mode.ordinal());
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+            final TextView target = (TextView) root.findViewById(viewId);
+            if (target == null) return;
+            Drawable[] drawables = isRelative
+                    ? target.getCompoundDrawablesRelative()
+                    : target.getCompoundDrawables();
+            if (index < 0 || index >= 4) {
+                throw new IllegalStateException("index must be in range [0, 3].");
+            }
+            Drawable d = drawables[index];
+            if (d != null) {
+                d.mutate();
+                d.setColorFilter(color, mode);
+            }
+        }
+
+        public String getActionName() {
+            return "TextViewDrawableColorFilterAction";
+        }
+
+        final boolean isRelative;
+        final int index;
+        final int color;
+        final PorterDuff.Mode mode;
+
+        public final static int TAG = 17;
+    }
+
+    /**
      * Simple class used to keep track of memory usage in a RemoteViews.
      *
      */
@@ -1686,6 +1755,9 @@
                     case SetRemoteViewsAdapterList.TAG:
                         mActions.add(new SetRemoteViewsAdapterList(parcel));
                         break;
+                    case TextViewDrawableColorFilterAction.TAG:
+                        mActions.add(new TextViewDrawableColorFilterAction(parcel));
+                        break;
                     default:
                         throw new ActionException("Tag " + tag + " not found");
                     }
@@ -1921,6 +1993,28 @@
     }
 
     /**
+     * Equivalent to applying a color filter on one of the drawables in
+     * {@link android.widget.TextView#getCompoundDrawablesRelative()}.
+     *
+     * @param viewId The id of the view whose text should change.
+     * @param index  The index of the drawable in the array of
+     *               {@link android.widget.TextView#getCompoundDrawablesRelative()} to set the color
+     *               filter on. Must be in [0, 3].
+     * @param color  The color of the color filter. See
+     *               {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
+     * @param mode   The mode of the color filter. See
+     *               {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
+     * @hide
+     */
+    public void setTextViewCompoundDrawablesRelativeColorFilter(int viewId,
+            int index, int color, PorterDuff.Mode mode) {
+        if (index < 0 || index >= 4) {
+            throw new IllegalArgumentException("index must be in range [0, 3].");
+        }
+        addAction(new TextViewDrawableColorFilterAction(viewId, true, index, color, mode));
+    }
+
+    /**
      * Equivalent to calling ImageView.setImageResource
      *
      * @param viewId The id of the view whose drawable should change
diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
new file mode 100644
index 0000000..a5ce6e0ca
--- /dev/null
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.util;
+
+import android.graphics.Bitmap;
+
+/**
+ * Utility class for image analysis and processing.
+ *
+ * @hide
+ */
+public class ImageUtils {
+
+    // Amount (max is 255) that two channels can differ before the color is no longer "gray".
+    private static final int TOLERANCE = 20;
+
+    // Alpha amount for which values below are considered transparent.
+    private static final int ALPHA_TOLERANCE = 50;
+
+    private int[] mTempBuffer;
+
+    /**
+     * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect
+     * gray".
+     */
+    public boolean isGrayscale(Bitmap bitmap) {
+        final int height = bitmap.getHeight();
+        final int width = bitmap.getWidth();
+        int size = height*width;
+
+        ensureBufferSize(size);
+        bitmap.getPixels(mTempBuffer, 0, width, 0, 0, width, height);
+        for (int i = 0; i < size; i++) {
+            if (!isGrayscale(mTempBuffer[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Makes sure that {@code mTempBuffer} has at least length {@code size}.
+     */
+    private void ensureBufferSize(int size) {
+        if (mTempBuffer == null || mTempBuffer.length < size) {
+            mTempBuffer = new int[size];
+        }
+    }
+
+    /**
+     * Classifies a color as grayscale or not. Grayscale here means "very close to a perfect
+     * gray"; if all three channels are approximately equal, this will return true.
+     *
+     * Note that really transparent colors are always grayscale.
+     */
+    public static boolean isGrayscale(int color) {
+        int alpha = 0xFF & (color >> 24);
+        if (alpha < ALPHA_TOLERANCE) {
+            return true;
+        }
+
+        int r = 0xFF & (color >> 16);
+        int g = 0xFF & (color >> 8);
+        int b = 0xFF & color;
+
+        return Math.abs(r - g) < TOLERANCE
+                && Math.abs(r - b) < TOLERANCE
+                && Math.abs(g - b) < TOLERANCE;
+    }
+}
diff --git a/core/java/com/android/internal/util/LegacyNotificationUtil.java b/core/java/com/android/internal/util/LegacyNotificationUtil.java
new file mode 100644
index 0000000..0394bbc
--- /dev/null
+++ b/core/java/com/android/internal/util/LegacyNotificationUtil.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.util;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.TextAppearanceSpan;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.Arrays;
+import java.util.WeakHashMap;
+
+/**
+ * Helper class to process legacy (Holo) notifications to make them look like quantum notifications.
+ *
+ * @hide
+ */
+public class LegacyNotificationUtil {
+
+    private static final String TAG = "LegacyNotificationUtil";
+
+    private static final Object sLock = new Object();
+    private static LegacyNotificationUtil sInstance;
+
+    private final ImageUtils mImageUtils = new ImageUtils();
+    private final WeakHashMap<Bitmap, Pair<Boolean, Integer>> mGrayscaleBitmapCache =
+            new WeakHashMap<Bitmap, Pair<Boolean, Integer>>();
+
+    public static LegacyNotificationUtil getInstance() {
+        synchronized (sLock) {
+            if (sInstance == null) {
+                sInstance = new LegacyNotificationUtil();
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect
+     * gray".
+     *
+     * @param bitmap The bitmap to test.
+     * @return Whether the bitmap is grayscale.
+     */
+    public boolean isGrayscale(Bitmap bitmap) {
+        synchronized (sLock) {
+            Pair<Boolean, Integer> cached = mGrayscaleBitmapCache.get(bitmap);
+            if (cached != null) {
+                if (cached.second == bitmap.getGenerationId()) {
+                    return cached.first;
+                }
+            }
+        }
+        boolean result;
+        int generationId;
+        synchronized (mImageUtils) {
+            result = mImageUtils.isGrayscale(bitmap);
+
+            // generationId and the check whether the Bitmap is grayscale can't be read atomically
+            // here. However, since the thread is in the process of posting the notification, we can
+            // assume that it doesn't modify the bitmap while we are checking the pixels.
+            generationId = bitmap.getGenerationId();
+        }
+        synchronized (sLock) {
+            mGrayscaleBitmapCache.put(bitmap, Pair.create(result, generationId));
+        }
+        return result;
+    }
+
+    /**
+     * Checks whether a drawable is grayscale. Grayscale here means "very close to a perfect
+     * gray".
+     *
+     * @param d The drawable to test.
+     * @return Whether the drawable is grayscale.
+     */
+    public boolean isGrayscale(Drawable d) {
+        if (d == null) {
+            return false;
+        } else if (d instanceof BitmapDrawable) {
+            BitmapDrawable bd = (BitmapDrawable) d;
+            return bd.getBitmap() != null && isGrayscale(bd.getBitmap());
+        } else if (d instanceof AnimationDrawable) {
+            AnimationDrawable ad = (AnimationDrawable) d;
+            int count = ad.getNumberOfFrames();
+            return count > 0 && isGrayscale(ad.getFrame(0));
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Checks whether a drawable with a resoure id is grayscale. Grayscale here means "very close
+     * to a perfect gray".
+     *
+     * @param context The context to load the drawable from.
+     * @return Whether the drawable is grayscale.
+     */
+    public boolean isGrayscale(Context context, int drawableResId) {
+        if (drawableResId != 0) {
+            try {
+                return isGrayscale(context.getDrawable(drawableResId));
+            } catch (Resources.NotFoundException ex) {
+                Log.e(TAG, "Drawable not found: " + drawableResId);
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Inverts all the grayscale colors set by {@link android.text.style.TextAppearanceSpan}s on
+     * the text.
+     *
+     * @param charSequence The text to process.
+     * @return The color inverted text.
+     */
+    public CharSequence invertCharSequenceColors(CharSequence charSequence) {
+        if (charSequence instanceof Spanned) {
+            Spanned ss = (Spanned) charSequence;
+            Object[] spans = ss.getSpans(0, ss.length(), Object.class);
+            SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
+            for (Object span : spans) {
+                Object resultSpan = span;
+                if (span instanceof TextAppearanceSpan) {
+                    resultSpan = processTextAppearanceSpan((TextAppearanceSpan) span);
+                }
+                builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
+                        ss.getSpanFlags(span));
+            }
+            return builder;
+        }
+        return charSequence;
+    }
+
+    private TextAppearanceSpan processTextAppearanceSpan(TextAppearanceSpan span) {
+        ColorStateList colorStateList = span.getTextColor();
+        if (colorStateList != null) {
+            int[] colors = colorStateList.getColors();
+            boolean changed = false;
+            for (int i = 0; i < colors.length; i++) {
+                if (ImageUtils.isGrayscale(colors[i])) {
+
+                    // Allocate a new array so we don't change the colors in the old color state
+                    // list.
+                    if (!changed) {
+                        colors = Arrays.copyOf(colors, colors.length);
+                    }
+                    colors[i] = processColor(colors[i]);
+                    changed = true;
+                }
+            }
+            if (changed) {
+                return new TextAppearanceSpan(
+                        span.getFamily(), span.getTextStyle(), span.getTextSize(),
+                        new ColorStateList(colorStateList.getStates(), colors),
+                        span.getLinkTextColor());
+            }
+        }
+        return span;
+    }
+
+    private int processColor(int color) {
+        return Color.argb(Color.alpha(color),
+                255 - Color.red(color),
+                255 - Color.green(color),
+                255 - Color.blue(color));
+    }
+}
diff --git a/core/res/res/drawable/notification_icon_legacy_bg.xml b/core/res/res/drawable/notification_icon_legacy_bg.xml
new file mode 100644
index 0000000..4ac67c3
--- /dev/null
+++ b/core/res/res/drawable/notification_icon_legacy_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval">
+    <solid
+            android:color="@color/notification_icon_legacy_bg_color"/>
+</shape>
diff --git a/core/res/res/drawable/notification_icon_legacy_bg_inset.xml b/core/res/res/drawable/notification_icon_legacy_bg_inset.xml
new file mode 100644
index 0000000..96c5573
--- /dev/null
+++ b/core/res/res/drawable/notification_icon_legacy_bg_inset.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+       android:drawable="@drawable/notification_icon_legacy_bg" android:insetBottom="8dp"
+       android:insetLeft="8dp" android:insetRight="8dp" android:insetTop="8dp"
+       android:visible="true"/>
diff --git a/core/res/res/layout/notification_quantum_action.xml b/core/res/res/layout/notification_quantum_action.xml
index 775182f..0986343 100644
--- a/core/res/res/layout/notification_quantum_action.xml
+++ b/core/res/res/layout/notification_quantum_action.xml
@@ -16,7 +16,7 @@
   -->
 
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
-    style="?android:attr/borderlessButtonStyle" 
+    style="@android:style/Widget.Quantum.Light.Button.Borderless.Small"
     android:id="@+id/action0"
     android:layout_width="0dp"
     android:layout_height="48dp"
diff --git a/core/res/res/layout/notification_quantum_action_list.xml b/core/res/res/layout/notification_quantum_action_list.xml
index a8aef97..d723dcd 100644
--- a/core/res/res/layout/notification_quantum_action_list.xml
+++ b/core/res/res/layout/notification_quantum_action_list.xml
@@ -23,7 +23,7 @@
     android:visibility="gone"
     android:layout_marginBottom="8dp"
     android:showDividers="middle"
-    android:divider="?android:attr/listDivider"
+    android:divider="@drawable/list_divider_quantum_light"
     android:dividerPadding="12dp"
     >
     <!-- actions will be added here -->
diff --git a/core/res/res/layout/notification_quantum_action_tombstone.xml b/core/res/res/layout/notification_quantum_action_tombstone.xml
index 9104991..51e4205 100644
--- a/core/res/res/layout/notification_quantum_action_tombstone.xml
+++ b/core/res/res/layout/notification_quantum_action_tombstone.xml
@@ -16,7 +16,7 @@
   -->
 
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
-    style="?android:attr/borderlessButtonStyle" 
+    style="@android:style/Widget.Quantum.Light.Button.Borderless.Small"
     android:id="@+id/action0"
     android:layout_width="0dp"
     android:layout_height="48dp"
diff --git a/core/res/res/layout/notification_template_quantum_base.xml b/core/res/res/layout/notification_template_quantum_base.xml
index 3e97b2a..8863cfa 100644
--- a/core/res/res/layout/notification_template_quantum_base.xml
+++ b/core/res/res/layout/notification_template_quantum_base.xml
@@ -92,7 +92,7 @@
             android:layout_height="12dp"
             android:layout_marginStart="8dp"
             android:visibility="gone"
-            style="?android:attr/progressBarStyleHorizontal"
+            style="@style/Widget.Quantum.Light.ProgressBar.Horizontal"
             />
         <LinearLayout
             android:id="@+id/line3"
diff --git a/core/res/res/layout/notification_template_quantum_big_base.xml b/core/res/res/layout/notification_template_quantum_big_base.xml
index d860045..483ea07 100644
--- a/core/res/res/layout/notification_template_quantum_big_base.xml
+++ b/core/res/res/layout/notification_template_quantum_big_base.xml
@@ -148,7 +148,7 @@
                 android:layout_marginStart="8dp"
                 android:layout_marginEnd="8dp"
                 android:visibility="gone"
-                style="?android:attr/progressBarStyleHorizontal"
+                style="@style/Widget.Quantum.Light.ProgressBar.Horizontal"
                 />
         </LinearLayout>
         <ImageView
@@ -156,7 +156,7 @@
             android:layout_height="1dp"
             android:id="@+id/action_divider"
             android:visibility="gone"
-            android:background="?android:attr/dividerHorizontal" />
+            android:background="@drawable/list_divider_quantum_light" />
         <include
             layout="@layout/notification_quantum_action_list"
             android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_template_quantum_big_text.xml b/core/res/res/layout/notification_template_quantum_big_text.xml
index 585be80..4fe91c0 100644
--- a/core/res/res/layout/notification_template_quantum_big_text.xml
+++ b/core/res/res/layout/notification_template_quantum_big_text.xml
@@ -101,7 +101,7 @@
                 android:layout_marginEnd="8dp"
                 android:visibility="gone"
                 android:layout_weight="0"
-                style="?android:attr/progressBarStyleHorizontal"
+                style="@style/Widget.Quantum.Light.ProgressBar.Horizontal"
                 />
             <TextView android:id="@+id/big_text"
                 android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
@@ -121,7 +121,7 @@
             android:layout_height="1dip"
             android:id="@+id/action_divider"
             android:visibility="gone"
-            android:background="?android:attr/dividerHorizontal" />
+            android:background="@drawable/list_divider_quantum_light" />
         <include
             layout="@layout/notification_quantum_action_list"
             android:layout_width="match_parent"
@@ -135,7 +135,7 @@
             android:id="@+id/overflow_divider"
             android:layout_marginBottom="8dp"
             android:visibility="visible"
-            android:background="?android:attr/dividerHorizontal" />
+            android:background="@drawable/list_divider_quantum_light" />
         <LinearLayout
             android:id="@+id/line3"
             android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_template_quantum_inbox.xml b/core/res/res/layout/notification_template_quantum_inbox.xml
index 31ed508..fd9089b 100644
--- a/core/res/res/layout/notification_template_quantum_inbox.xml
+++ b/core/res/res/layout/notification_template_quantum_inbox.xml
@@ -103,7 +103,7 @@
                 android:layout_marginEnd="8dp"
                 android:visibility="gone"
                 android:layout_weight="0"
-                style="?android:attr/progressBarStyleHorizontal"
+                style="@style/Widget.Quantum.Light.ProgressBar.Horizontal"
                 />
             <TextView android:id="@+id/inbox_text0"
                 android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
@@ -206,7 +206,7 @@
             android:layout_height="1dip"
             android:id="@+id/action_divider"
             android:visibility="gone"
-            android:background="?android:attr/dividerHorizontal" />
+            android:background="@drawable/list_divider_quantum_light" />
         <include
             layout="@layout/notification_quantum_action_list"
             android:layout_width="match_parent"
@@ -218,7 +218,7 @@
             android:layout_height="1dip"
             android:id="@+id/overflow_divider"
             android:visibility="visible"
-            android:background="?android:attr/dividerHorizontal" />
+            android:background="@drawable/list_divider_quantum_light" />
         <LinearLayout
             android:id="@+id/line3"
             android:layout_width="match_parent"
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 1947c50..d0c455b 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -193,6 +193,9 @@
     <drawable name="notification_template_icon_bg">#3333B5E5</drawable>
     <drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
 
+    <color name="notification_icon_legacy_bg_color">#ff4285F4</color>
+    <color name="notification_action_legacy_color_filter">#ff555555</color>
+
     <!-- Keyguard colors -->
     <color name="keyguard_avatar_frame_color">#ffffffff</color>
     <color name="keyguard_avatar_frame_shadow_color">#80000000</color>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index efa873d..db5f66e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1637,6 +1637,8 @@
   <java-symbol type="layout" name="notification_template_quantum_big_picture" />
   <java-symbol type="layout" name="notification_template_quantum_big_text" />
   <java-symbol type="layout" name="notification_template_quantum_inbox" />
+  <java-symbol type="color" name="notification_action_legacy_color_filter" />
+  <java-symbol type="drawable" name="notification_icon_legacy_bg_inset" />
 
     <!-- From SystemUI -->
   <java-symbol type="anim" name="push_down_in" />