diff options
author | Tony Wickham <twickham@google.com> | 2017-01-27 23:34:19 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2017-01-27 23:34:19 +0000 |
commit | 72e1688019c4cf650fb533b38296a9442bedf72e (patch) | |
tree | cf7425298ef1c956a514d80a3514979044c73b18 /src | |
parent | dec3a908bfa395095e80e4a532cff98612b624de (diff) | |
parent | f79877c04c071b7ae1618395f0a1dce134fec36e (diff) | |
download | android_packages_apps_Trebuchet-72e1688019c4cf650fb533b38296a9442bedf72e.tar.gz android_packages_apps_Trebuchet-72e1688019c4cf650fb533b38296a9442bedf72e.tar.bz2 android_packages_apps_Trebuchet-72e1688019c4cf650fb533b38296a9442bedf72e.zip |
Merge "Ensure notification icons have enough contrast with background." into ub-launcher3-master
Diffstat (limited to 'src')
5 files changed, 148 insertions, 18 deletions
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java index 58ad449d6..27aeaba34 100644 --- a/src/com/android/launcher3/graphics/IconPalette.java +++ b/src/com/android/launcher3/graphics/IconPalette.java @@ -16,14 +16,22 @@ package com.android.launcher3.graphics; +import android.app.Notification; +import android.content.Context; import android.graphics.Color; import android.support.v4.graphics.ColorUtils; +import android.util.Log; + +import com.android.launcher3.R; /** * Contains colors based on the dominant color of an icon. */ public class IconPalette { + private static final boolean DEBUG = false; + private static final String TAG = "IconPalette"; + public int backgroundColor; public int textColor; public int secondaryColor; @@ -36,6 +44,100 @@ public class IconPalette { return palette; } + /** + * Resolves a color such that it has enough contrast to be used as the + * color of an icon or text on the given background color. + * + * @return a color of the same hue with enough contrast against the background. + * + * This was copied from com.android.internal.util.NotificationColorUtil. + */ + public static int resolveContrastColor(Context context, int color, int background) { + final int resolvedColor = resolveColor(context, color); + + int contrastingColor = ensureTextContrast(resolvedColor, background); + + if (contrastingColor != resolvedColor) { + if (DEBUG){ + Log.w(TAG, String.format( + "Enhanced contrast of notification for %s " + + "%s (over background) by changing #%s to %s", + context.getPackageName(), + contrastChange(resolvedColor, contrastingColor, background), + Integer.toHexString(resolvedColor), Integer.toHexString(contrastingColor))); + } + } + return contrastingColor; + } + + /** + * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT} + * + * This was copied from com.android.internal.util.NotificationColorUtil. + */ + private static int resolveColor(Context context, int color) { + if (color == Notification.COLOR_DEFAULT) { + return context.getColor(R.color.notification_icon_default_color); + } + return color; + } + + /** For debugging. This was copied from com.android.internal.util.NotificationColorUtil. */ + private static String contrastChange(int colorOld, int colorNew, int bg) { + return String.format("from %.2f:1 to %.2f:1", + ColorUtils.calculateContrast(colorOld, bg), + ColorUtils.calculateContrast(colorNew, bg)); + } + + /** + * Finds a text color with sufficient contrast over bg that has the same hue as the original + * color. + * + * This was copied from com.android.internal.util.NotificationColorUtil. + */ + private static int ensureTextContrast(int color, int bg) { + return findContrastColor(color, bg, true, 4.5); + } + /** + * Finds a suitable color such that there's enough contrast. + * + * @param color the color to start searching from. + * @param other the color to ensure contrast against. Assumed to be lighter than {@param color} + * @param findFg if true, we assume {@param color} is a foreground, otherwise a background. + * @param minRatio the minimum contrast ratio required. + * @return a color with the same hue as {@param color}, potentially darkened to meet the + * contrast ratio. + * + * This was copied from com.android.internal.util.NotificationColorUtil. + */ + private static int findContrastColor(int color, int other, boolean findFg, double minRatio) { + int fg = findFg ? color : other; + int bg = findFg ? other : color; + if (ColorUtils.calculateContrast(fg, bg) >= minRatio) { + return color; + } + + double[] lab = new double[3]; + ColorUtils.colorToLAB(findFg ? fg : bg, lab); + + double low = 0, high = lab[0]; + final double a = lab[1], b = lab[2]; + for (int i = 0; i < 15 && high - low > 0.00001; i++) { + final double l = (low + high) / 2; + if (findFg) { + fg = ColorUtils.LABToColor(l, a, b); + } else { + bg = ColorUtils.LABToColor(l, a, b); + } + if (ColorUtils.calculateContrast(fg, bg) > minRatio) { + low = l; + } else { + high = l; + } + } + return ColorUtils.LABToColor(low, a, b); + } + private static int getMutedColor(int color) { int alpha = (int) (255 * 0.15f); return ColorUtils.compositeColors(ColorUtils.setAlphaComponent(color, alpha), Color.WHITE); diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java index cd610bd3b..07178ce0c 100644 --- a/src/com/android/launcher3/notification/NotificationFooterLayout.java +++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java @@ -61,6 +61,7 @@ public class NotificationFooterLayout extends LinearLayout { LinearLayout.LayoutParams mIconLayoutParams; private LinearLayout mIconRow; + private int mBackgroundColor; private int mTextColor; public NotificationFooterLayout(Context context) { @@ -90,7 +91,8 @@ public class NotificationFooterLayout extends LinearLayout { } public void applyColors(IconPalette iconPalette) { - setBackgroundTintList(ColorStateList.valueOf(iconPalette.backgroundColor)); + mBackgroundColor = iconPalette.backgroundColor; + setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor)); findViewById(R.id.divider).setBackgroundColor(iconPalette.secondaryColor); mTextColor = iconPalette.textColor; } @@ -130,7 +132,7 @@ public class NotificationFooterLayout extends LinearLayout { private void addNotificationIconForInfo(NotificationInfo info, boolean fromOverflow) { View icon = new View(getContext()); - icon.setBackground(info.iconDrawable); + icon.setBackground(info.getIconForBackground(getContext(), mBackgroundColor)); icon.setOnClickListener(info); int addIndex = mIconRow.getChildCount(); if (fromOverflow) { diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java index bf57b2aff..af5e817f1 100644 --- a/src/com/android/launcher3/notification/NotificationInfo.java +++ b/src/com/android/launcher3/notification/NotificationInfo.java @@ -25,6 +25,7 @@ import android.service.notification.StatusBarNotification; import android.view.View; import com.android.launcher3.Launcher; +import com.android.launcher3.graphics.IconPalette; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.util.PackageUserKey; @@ -41,11 +42,14 @@ public class NotificationInfo implements View.OnClickListener { public final String notificationKey; public final CharSequence title; public final CharSequence text; - public final Drawable iconDrawable; public final PendingIntent intent; public final boolean autoCancel; public final boolean dismissable; + private final Drawable mIconDrawable; + private boolean mShouldTintIcon; + private int mIconColor; + /** * Extracts the data that we need from the StatusBarNotification. */ @@ -60,10 +64,12 @@ public class NotificationInfo implements View.OnClickListener { Icon icon = notification.getLargeIcon(); if (icon == null) { icon = notification.getSmallIcon(); - iconDrawable = icon.loadDrawable(context); - iconDrawable.setTint(statusBarNotification.getNotification().color); + mIconDrawable = icon.loadDrawable(context); + mIconColor = statusBarNotification.getNotification().color; + mShouldTintIcon = true; } else { - iconDrawable = icon.loadDrawable(context); + mIconDrawable = icon.loadDrawable(context); + mShouldTintIcon = false; } intent = notification.contentIntent; autoCancel = (notification.flags & Notification.FLAG_AUTO_CANCEL) != 0; @@ -83,4 +89,18 @@ public class NotificationInfo implements View.OnClickListener { } PopupContainerWithArrow.getOpen(launcher).close(true); } + + public Drawable getIconForBackground(Context context, int background) { + if (!mShouldTintIcon) { + return mIconDrawable; + } + mIconColor = IconPalette.resolveContrastColor(context, mIconColor, background); + Drawable icon = mIconDrawable.mutate(); + // DrawableContainer ignores the color filter if it's already set, so clear it first to + // get it set and invalidated properly. + icon.setTintList(null); + icon.setTint(mIconColor); + mShouldTintIcon = false; + return icon; + } } diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java index b74cd4e1e..422722d8c 100644 --- a/src/com/android/launcher3/notification/NotificationItemView.java +++ b/src/com/android/launcher3/notification/NotificationItemView.java @@ -35,7 +35,9 @@ import com.android.launcher3.graphics.IconPalette; import com.android.launcher3.popup.PopupItemView; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static com.android.launcher3.LauncherAnimUtils.animateViewHeight; @@ -117,7 +119,7 @@ public class NotificationItemView extends PopupItemView { mHeader.setBackgroundTintList(ColorStateList.valueOf(iconPalette.backgroundColor)); mHeader.setTextColor(ColorStateList.valueOf(iconPalette.textColor)); mDivider.setBackgroundColor(iconPalette.secondaryColor); - mMainView.setBackgroundColor(iconPalette.backgroundColor); + mMainView.applyColors(iconPalette); mFooter.applyColors(iconPalette); } @@ -135,7 +137,7 @@ public class NotificationItemView extends PopupItemView { @Override public void onIconAnimationEnd(NotificationInfo newMainNotification) { if (newMainNotification != null) { - mMainView.applyNotificationInfo(newMainNotification, mIconView, mIconPalette); + mMainView.applyNotificationInfo(newMainNotification, mIconView, true); // Remove the animated notification from the footer by calling trim // TODO: Remove the notification in NotificationFooterLayout directly // instead of relying on this hack. diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java index 2997d4010..76a84b7c6 100644 --- a/src/com/android/launcher3/notification/NotificationMainView.java +++ b/src/com/android/launcher3/notification/NotificationMainView.java @@ -41,6 +41,7 @@ public class NotificationMainView extends LinearLayout implements SwipeHelper.Ca private NotificationInfo mNotificationInfo; private TextView mTitleView; private TextView mTextView; + private IconPalette mIconPalette; public NotificationMainView(Context context) { this(context, null, 0); @@ -62,35 +63,38 @@ public class NotificationMainView extends LinearLayout implements SwipeHelper.Ca mTextView = (TextView) findViewById(R.id.text); } + public void applyColors(IconPalette iconPalette) { + setBackgroundColor(iconPalette.backgroundColor); + mIconPalette = iconPalette; + } + public void applyNotificationInfo(NotificationInfo mainNotification, View iconView) { - applyNotificationInfo(mainNotification, iconView, null); + applyNotificationInfo(mainNotification, iconView, false); } /** - * @param iconPalette if not null, indicates that the new info should be animated in, - * and that part of this animation includes animating the background - * from iconPalette.secondaryColor to iconPalette.backgroundColor. + * Sets the content of this view, animating it after a new icon shifts up if necessary. */ public void applyNotificationInfo(NotificationInfo mainNotification, View iconView, - @Nullable IconPalette iconPalette) { - boolean animate = iconPalette != null; + boolean animate) { if (animate) { mTitleView.setAlpha(0); mTextView.setAlpha(0); - setBackgroundColor(iconPalette.secondaryColor); + setBackgroundColor(mIconPalette.secondaryColor); } mNotificationInfo = mainNotification; mTitleView.setText(mNotificationInfo.title); mTextView.setText(mNotificationInfo.text); - iconView.setBackground(mNotificationInfo.iconDrawable); + iconView.setBackground(mNotificationInfo.getIconForBackground( + getContext(), mIconPalette.backgroundColor)); setOnClickListener(mNotificationInfo); setTranslationX(0); if (animate) { AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); Animator textFade = new LauncherViewPropertyAnimator(mTextView).alpha(1); Animator titleFade = new LauncherViewPropertyAnimator(mTitleView).alpha(1); - ValueAnimator colorChange = ValueAnimator.ofArgb(iconPalette.secondaryColor, - iconPalette.backgroundColor); + ValueAnimator colorChange = ValueAnimator.ofArgb(mIconPalette.secondaryColor, + mIconPalette.backgroundColor); colorChange.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { |