Loading res/values/colors.xml +2 −0 Original line number Diff line number Diff line Loading @@ -50,4 +50,6 @@ <!-- Used as a fallback since colorSecondary doesn't exist pre-API 25 --> <color name="fallback_secondary_color">#FF37474F</color> <color name="notification_icon_default_color">#757575</color> <!-- Gray 600 --> </resources> src/com/android/launcher3/graphics/IconPalette.java +102 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading src/com/android/launcher3/notification/NotificationFooterLayout.java +4 −2 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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; } Loading Loading @@ -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) { Loading src/com/android/launcher3/notification/NotificationInfo.java +24 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. */ Loading @@ -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; Loading @@ -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; } } src/com/android/launcher3/notification/NotificationItemView.java +4 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } Loading @@ -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. Loading Loading
res/values/colors.xml +2 −0 Original line number Diff line number Diff line Loading @@ -50,4 +50,6 @@ <!-- Used as a fallback since colorSecondary doesn't exist pre-API 25 --> <color name="fallback_secondary_color">#FF37474F</color> <color name="notification_icon_default_color">#757575</color> <!-- Gray 600 --> </resources>
src/com/android/launcher3/graphics/IconPalette.java +102 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading
src/com/android/launcher3/notification/NotificationFooterLayout.java +4 −2 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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; } Loading Loading @@ -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) { Loading
src/com/android/launcher3/notification/NotificationInfo.java +24 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. */ Loading @@ -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; Loading @@ -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; } }
src/com/android/launcher3/notification/NotificationItemView.java +4 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } Loading @@ -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. Loading