diff --git a/api/current.txt b/api/current.txt index 5d7c4008251be..4e3ee3cd3dfc3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4438,6 +4438,7 @@ package android.app { field public static final java.lang.String CATEGORY_STATUS = "status"; field public static final java.lang.String CATEGORY_SYSTEM = "sys"; field public static final java.lang.String CATEGORY_TRANSPORT = "transport"; + field public static final int COLOR_DEFAULT = 0; // 0x0 field public static final android.os.Parcelable.Creator CREATOR; field public static final int DEFAULT_ALL = -1; // 0xffffffff field public static final int DEFAULT_LIGHTS = 4; // 0x4 @@ -4483,6 +4484,7 @@ package android.app { field public int audioStreamType; field public android.widget.RemoteViews bigContentView; field public java.lang.String category; + field public int color; field public android.app.PendingIntent contentIntent; field public android.widget.RemoteViews contentView; field public int defaults; @@ -4545,6 +4547,7 @@ package android.app { method public deprecated android.app.Notification getNotification(); method public android.app.Notification.Builder setAutoCancel(boolean); method public android.app.Notification.Builder setCategory(java.lang.String); + method public android.app.Notification.Builder setColor(int); method public android.app.Notification.Builder setContent(android.widget.RemoteViews); method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence); method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 25a1493b3b6c3..bba6caf8b05f7 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -17,7 +17,7 @@ package android.app; import com.android.internal.R; -import com.android.internal.util.LegacyNotificationUtil; +import com.android.internal.util.NotificationColorUtil; import android.annotation.IntDef; import android.content.Context; @@ -28,6 +28,7 @@ import android.graphics.PorterDuff; import android.media.AudioManager; import android.net.Uri; import android.os.BadParcelableException; +import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -420,6 +421,21 @@ public class Notification implements Parcelable @Priority public int priority; + /** + * Accent color (an ARGB integer like the constants in {@link android.graphics.Color}) + * to be applied by the standard Style templates when presenting this notification. + * + * The current template design constructs a colorful header image by overlaying the + * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are + * ignored. + */ + public int color = COLOR_DEFAULT; + + /** + * Special value of {@link #color} telling the system not to decorate this notification with + * any special color but instead use default colors when presenting this notification. + */ + public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT /** * Sphere of visibility of this notification, which affects how and when the SystemUI reveals @@ -877,6 +893,8 @@ public class Notification implements Parcelable if (parcel.readInt() != 0) { publicVersion = Notification.CREATOR.createFromParcel(parcel); } + + color = parcel.readInt(); } @Override @@ -968,6 +986,8 @@ public class Notification implements Parcelable this.publicVersion.cloneInto(that.publicVersion, heavy); } + that.color = this.color; + if (!heavy) { that.lightenPayload(); // will clean out extras } @@ -1110,6 +1130,8 @@ public class Notification implements Parcelable } else { parcel.writeInt(0); } + + parcel.writeInt(color); } /** @@ -1218,6 +1240,7 @@ public class Notification implements Parcelable sb.append(Integer.toHexString(this.defaults)); sb.append(" flags=0x"); sb.append(Integer.toHexString(this.flags)); + sb.append(String.format(" color=0x%08x", this.color)); sb.append(" category="); sb.append(this.category); if (actions != null) { sb.append(" "); @@ -1309,9 +1332,10 @@ public class Notification implements Parcelable private boolean mShowWhen = true; private int mVisibility = VISIBILITY_PRIVATE; private Notification mPublicVersion = null; - private boolean mQuantumTheme; - private final LegacyNotificationUtil mLegacyNotificationUtil; + private final NotificationColorUtil mColorUtil; private ArrayList mPeople; + private boolean mPreQuantum; + private int mColor = COLOR_DEFAULT; /** * Constructs a new Builder with the defaults: @@ -1341,12 +1365,8 @@ public class Notification implements Parcelable mPriority = PRIORITY_DEFAULT; mPeople = new ArrayList(); - // 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(); + mPreQuantum = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L; + mColorUtil = NotificationColorUtil.getInstance(); } /** @@ -1853,29 +1873,38 @@ public class Notification implements Parcelable } } + /** + * Sets {@link Notification#color}. + * + * @param argb The accent color to use + * + * @return The same Builder. + */ + public Builder setColor(int argb) { + mColor = argb; + return this; + } + private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) { RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId); boolean showLine3 = false; boolean showLine2 = false; int smallIconImageViewId = R.id.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 (mPriority < PRIORITY_LOW) { + // TODO: Low priority presentation } if (mLargeIcon != null) { contentView.setImageViewBitmap(R.id.icon, mLargeIcon); - processLegacyLargeIcon(mLargeIcon, contentView); + processLargeIcon(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); + processSmallRightIcon(mSmallIcon, smallIconImageViewId, contentView); } else { - processLegacyLargeIcon(mSmallIcon, contentView); + processSmallIconAsLarge(mSmallIcon, contentView); } } else { @@ -2035,12 +2064,12 @@ public class Notification implements Parcelable * doesn't create quantum notifications by itself) app. */ private boolean isLegacy() { - return mLegacyNotificationUtil != null; + return mColorUtil != null; } private void processLegacyAction(Action action, RemoteViews button) { if (isLegacy()) { - if (mLegacyNotificationUtil.isGrayscale(mContext, action.icon)) { + if (mColorUtil.isGrayscale(mContext, action.icon)) { button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0, mContext.getResources().getColor( R.color.notification_action_legacy_color_filter), @@ -2051,47 +2080,70 @@ public class Notification implements Parcelable private CharSequence processLegacyText(CharSequence charSequence) { if (isLegacy()) { - return mLegacyNotificationUtil.invertCharSequenceColors(charSequence); + return mColorUtil.invertCharSequenceColors(charSequence); } else { return charSequence; } } - private void processLegacyLargeIcon(int largeIconId, RemoteViews contentView) { - if (isLegacy()) { - processLegacyLargeIcon( - mLegacyNotificationUtil.isGrayscale(mContext, largeIconId), - contentView); + /** + * Apply any necessary background to smallIcons being used in the largeIcon spot. + */ + private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) { + if (!isLegacy() || mColorUtil.isGrayscale(mContext, largeIconId)) { + applyLargeIconBackground(contentView); } } - private void processLegacyLargeIcon(Bitmap largeIcon, RemoteViews contentView) { - if (isLegacy()) { - processLegacyLargeIcon( - mLegacyNotificationUtil.isGrayscale(largeIcon), - contentView); + /** + * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is, + * if it's grayscale). + */ + // TODO: also check bounds, transparency, that sort of thing. + private void processLargeIcon(Bitmap largeIcon, RemoteViews contentView) { + if (!isLegacy() || mColorUtil.isGrayscale(largeIcon)) { + applyLargeIconBackground(contentView); } } - private void processLegacyLargeIcon(boolean isGrayscale, RemoteViews contentView) { - if (isLegacy() && isGrayscale) { - contentView.setInt(R.id.icon, "setBackgroundResource", - R.drawable.notification_icon_legacy_bg_inset); - } + /** + * Add a colored circle behind the largeIcon slot. + */ + private void applyLargeIconBackground(RemoteViews contentView) { + contentView.setInt(R.id.icon, "setBackgroundResource", + R.drawable.notification_icon_legacy_bg_inset); + + contentView.setDrawableParameters( + R.id.icon, + true, + -1, + mColor, + PorterDuff.Mode.SRC_ATOP, + -1); } - private void processLegacySmallIcon(int smallIconDrawableId, int smallIconImageViewId, + /** + * Recolor small icons when used in the R.id.right_icon slot. + */ + private void processSmallRightIcon(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); - } + if (!isLegacy() || mColorUtil.isGrayscale(mContext, smallIconDrawableId)) { + contentView.setDrawableParameters(smallIconImageViewId, false, -1, + mContext.getResources().getColor( + R.color.notification_action_legacy_color_filter), + PorterDuff.Mode.MULTIPLY, -1); } } + private int resolveColor() { + if (mColor == COLOR_DEFAULT) { + mColor = mContext.getResources().getColor(R.color.notification_icon_bg_color); + } else { + mColor |= 0xFF000000; // no alpha for custom colors + } + return mColor; + } + /** * Apply the unstyled operations and return a new {@link Notification} object. * @hide @@ -2102,6 +2154,9 @@ public class Notification implements Parcelable n.icon = mSmallIcon; n.iconLevel = mSmallIconLevel; n.number = mNumber; + + n.color = resolveColor(); + n.contentView = makeContentView(); n.contentIntent = mContentIntent; n.deleteIntent = mDeleteIntent; @@ -2207,45 +2262,31 @@ public class Notification implements Parcelable private int getBaseLayoutResource() { - return mQuantumTheme - ? R.layout.notification_template_quantum_base - : R.layout.notification_template_base; + return R.layout.notification_template_quantum_base; } private int getBigBaseLayoutResource() { - return mQuantumTheme - ? R.layout.notification_template_quantum_big_base - : R.layout.notification_template_big_base; + return R.layout.notification_template_quantum_big_base; } private int getBigPictureLayoutResource() { - return mQuantumTheme - ? R.layout.notification_template_quantum_big_picture - : R.layout.notification_template_big_picture; + return R.layout.notification_template_quantum_big_picture; } private int getBigTextLayoutResource() { - return mQuantumTheme - ? R.layout.notification_template_quantum_big_text - : R.layout.notification_template_big_text; + return R.layout.notification_template_quantum_big_text; } private int getInboxLayoutResource() { - return mQuantumTheme - ? R.layout.notification_template_quantum_inbox - : R.layout.notification_template_inbox; + return R.layout.notification_template_quantum_inbox; } private int getActionLayoutResource() { - return mQuantumTheme - ? R.layout.notification_quantum_action - : R.layout.notification_action; + return R.layout.notification_quantum_action; } private int getActionTombstoneLayoutResource() { - return mQuantumTheme - ? R.layout.notification_quantum_action_tombstone - : R.layout.notification_action_tombstone; + return R.layout.notification_quantum_action_tombstone; } } diff --git a/core/java/com/android/internal/util/LegacyNotificationUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java similarity index 93% rename from core/java/com/android/internal/util/LegacyNotificationUtil.java rename to core/java/com/android/internal/util/NotificationColorUtil.java index 0394bbcf96250..f38cbde4730b3 100644 --- a/core/java/com/android/internal/util/LegacyNotificationUtil.java +++ b/core/java/com/android/internal/util/NotificationColorUtil.java @@ -24,6 +24,7 @@ import android.graphics.Color; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.style.TextAppearanceSpan; @@ -38,21 +39,21 @@ import java.util.WeakHashMap; * * @hide */ -public class LegacyNotificationUtil { +public class NotificationColorUtil { - private static final String TAG = "LegacyNotificationUtil"; + private static final String TAG = "NotificationColorUtil"; private static final Object sLock = new Object(); - private static LegacyNotificationUtil sInstance; + private static NotificationColorUtil sInstance; private final ImageUtils mImageUtils = new ImageUtils(); private final WeakHashMap> mGrayscaleBitmapCache = new WeakHashMap>(); - public static LegacyNotificationUtil getInstance() { + public static NotificationColorUtil getInstance() { synchronized (sLock) { if (sInstance == null) { - sInstance = new LegacyNotificationUtil(); + sInstance = new NotificationColorUtil(); } return sInstance; } @@ -107,6 +108,9 @@ public class LegacyNotificationUtil { AnimationDrawable ad = (AnimationDrawable) d; int count = ad.getNumberOfFrames(); return count > 0 && isGrayscale(ad.getFrame(0)); + } else if (d instanceof VectorDrawable) { + // We just assume you're doing the right thing if using vectors + return true; } else { return false; } diff --git a/core/res/res/drawable/notification_icon_legacy_bg.xml b/core/res/res/drawable/notification_icon_legacy_bg.xml index 4ac67c3e47657..cc5755d5618cf 100644 --- a/core/res/res/drawable/notification_icon_legacy_bg.xml +++ b/core/res/res/drawable/notification_icon_legacy_bg.xml @@ -18,5 +18,5 @@ + android:color="@color/notification_icon_bg_color"/> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 761170d854d22..7d3fb444c1da0 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -123,7 +123,7 @@ #3333B5E5 #0cffffff - #ff4285F4 + #ffa3a3a3 #ff555555 diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a8a4b513d7301..2bf72e8adb21a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1664,6 +1664,7 @@ + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 3b536677fde70..ecefc39eb472f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -70,7 +70,7 @@ import android.widget.TextView; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; -import com.android.internal.util.LegacyNotificationUtil; +import com.android.internal.util.NotificationColorUtil; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.SearchPanelView; @@ -143,7 +143,7 @@ public abstract class BaseStatusBar extends SystemUI implements // public mode, private notifications, etc private boolean mLockscreenPublicMode = false; private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray(); - private LegacyNotificationUtil mLegacyNotificationUtil = LegacyNotificationUtil.getInstance(); + private NotificationColorUtil mNotificationColorUtil = NotificationColorUtil.getInstance(); private UserManager mUserManager; @@ -852,7 +852,7 @@ public abstract class BaseStatusBar extends SystemUI implements Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic); icon.setImageDrawable(iconDrawable); - if (mLegacyNotificationUtil.isGrayscale(iconDrawable)) { + if (mNotificationColorUtil.isGrayscale(iconDrawable)) { icon.setBackgroundResource( com.android.internal.R.drawable.notification_icon_legacy_bg_inset); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index fb37491475adc..fce86e88e3f5a 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -471,6 +471,7 @@ public class NotificationManagerService extends SystemService { pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x", notification.defaults, notification.flags)); pw.println(prefix + " sound=" + notification.sound); + pw.println(prefix + String.format(" color=0x%08x", notification.color)); pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d", notification.ledARGB, notification.ledOnMS, notification.ledOffMS));