summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
authorTony Wickham <twickham@google.com>2017-01-27 08:45:49 -0800
committerTony Wickham <twickham@google.com>2017-01-27 12:43:31 -0800
commitf79877c04c071b7ae1618395f0a1dce134fec36e (patch)
tree2bf2e66b101825b4d9e98f2077dc8cfcf1cf48f6 /src/com
parent5cfd1158ec1e4a19689217e9fbddd0fd795b2611 (diff)
downloadandroid_packages_apps_Trebuchet-f79877c04c071b7ae1618395f0a1dce134fec36e.tar.gz
android_packages_apps_Trebuchet-f79877c04c071b7ae1618395f0a1dce134fec36e.tar.bz2
android_packages_apps_Trebuchet-f79877c04c071b7ae1618395f0a1dce134fec36e.zip
Ensure notification icons have enough contrast with background.
This uses the same color calculations as the system, except that we use the extracted notification background instead of assuming it is white. Bug: 32410600 Change-Id: I7be8b9459ca38d01a6780758898541e69ec42576
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/launcher3/graphics/IconPalette.java102
-rw-r--r--src/com/android/launcher3/notification/NotificationFooterLayout.java6
-rw-r--r--src/com/android/launcher3/notification/NotificationInfo.java28
-rw-r--r--src/com/android/launcher3/notification/NotificationItemView.java6
-rw-r--r--src/com/android/launcher3/notification/NotificationMainView.java24
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) {