Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72e16880 authored by Tony Wickham's avatar Tony Wickham Committed by Android (Google) Code Review
Browse files

Merge "Ensure notification icons have enough contrast with background." into ub-launcher3-master

parents dec3a908 f79877c0
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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>
+102 −0
Original line number Diff line number Diff line
@@ -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);
+4 −2
Original line number Diff line number Diff line
@@ -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) {
+24 −4
Original line number Diff line number Diff line
@@ -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;
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -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.
Loading