19a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham/*
29a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham * Copyright (C) 2017 The Android Open Source Project
39a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham *
49a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham * Licensed under the Apache License, Version 2.0 (the "License");
59a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham * you may not use this file except in compliance with the License.
69a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham * You may obtain a copy of the License at
79a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham *
89a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham *      http://www.apache.org/licenses/LICENSE-2.0
99a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham *
109a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham * Unless required by applicable law or agreed to in writing, software
119a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham * distributed under the License is distributed on an "AS IS" BASIS,
129a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham * See the License for the specific language governing permissions and
149a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham * limitations under the License.
159a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham */
169a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham
179a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickhampackage com.android.launcher3.graphics;
189a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham
19f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickhamimport android.app.Notification;
20f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickhamimport android.content.Context;
217092db02410562026da17a7b38f48025cc847de2Tony Wickhamimport android.content.res.Resources;
229a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickhamimport android.graphics.Color;
236c82867a7ea3a95a19d6cec7d1fe164d234d2cb6Tony Wickhamimport android.graphics.ColorMatrix;
246c82867a7ea3a95a19d6cec7d1fe164d234d2cb6Tony Wickhamimport android.graphics.ColorMatrixColorFilter;
257092db02410562026da17a7b38f48025cc847de2Tony Wickhamimport android.support.annotation.NonNull;
267092db02410562026da17a7b38f48025cc847de2Tony Wickhamimport android.support.annotation.Nullable;
279a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickhamimport android.support.v4.graphics.ColorUtils;
28f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickhamimport android.util.Log;
29f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham
30f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickhamimport com.android.launcher3.R;
311f3f07d47c29cba3b70bcd15ebb65a077f55a558Sunny Goyalimport com.android.launcher3.util.Themes;
329a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham
339a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham/**
349a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham * Contains colors based on the dominant color of an icon.
359a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham */
369a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickhampublic class IconPalette {
379a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham
38f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    private static final boolean DEBUG = false;
39f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    private static final String TAG = "IconPalette";
40f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham
4196ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal    private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
4296ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal    private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f;
4396ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal
447092db02410562026da17a7b38f48025cc847de2Tony Wickham    private static IconPalette sBadgePalette;
457092db02410562026da17a7b38f48025cc847de2Tony Wickham    private static IconPalette sFolderBadgePalette;
467092db02410562026da17a7b38f48025cc847de2Tony Wickham
4796ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal    public final int dominantColor;
4896ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal    public final int backgroundColor;
496c82867a7ea3a95a19d6cec7d1fe164d234d2cb6Tony Wickham    public final ColorMatrixColorFilter backgroundColorMatrixFilter;
500530e8c60809afa92b1aa907376f0553ad94d58fTony Wickham    public final ColorMatrixColorFilter saturatedBackgroundColorMatrixFilter;
5196ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal    public final int textColor;
5296ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal    public final int secondaryColor;
5396ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal
547092db02410562026da17a7b38f48025cc847de2Tony Wickham    private IconPalette(int color, boolean desaturateBackground) {
5596ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        dominantColor = color;
567092db02410562026da17a7b38f48025cc847de2Tony Wickham        backgroundColor = desaturateBackground ? getMutedColor(dominantColor, 0.87f) : dominantColor;
576c82867a7ea3a95a19d6cec7d1fe164d234d2cb6Tony Wickham        ColorMatrix backgroundColorMatrix = new ColorMatrix();
586c82867a7ea3a95a19d6cec7d1fe164d234d2cb6Tony Wickham        Themes.setColorScaleOnMatrix(backgroundColor, backgroundColorMatrix);
596c82867a7ea3a95a19d6cec7d1fe164d234d2cb6Tony Wickham        backgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
607092db02410562026da17a7b38f48025cc847de2Tony Wickham        if (!desaturateBackground) {
617092db02410562026da17a7b38f48025cc847de2Tony Wickham            saturatedBackgroundColorMatrixFilter = backgroundColorMatrixFilter;
627092db02410562026da17a7b38f48025cc847de2Tony Wickham        } else {
637092db02410562026da17a7b38f48025cc847de2Tony Wickham            // Get slightly more saturated background color.
647092db02410562026da17a7b38f48025cc847de2Tony Wickham            Themes.setColorScaleOnMatrix(getMutedColor(dominantColor, 0.54f), backgroundColorMatrix);
657092db02410562026da17a7b38f48025cc847de2Tony Wickham            saturatedBackgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
667092db02410562026da17a7b38f48025cc847de2Tony Wickham        }
6796ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        textColor = getTextColorForBackground(backgroundColor);
6896ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        secondaryColor = getLowContrastColor(backgroundColor);
6996ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal    }
7096ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal
7196ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal    /**
7296ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal     * Returns a color suitable for the progress bar color of preload icon.
7396ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal     */
741f3f07d47c29cba3b70bcd15ebb65a077f55a558Sunny Goyal    public int getPreloadProgressColor(Context context) {
7596ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        int result = dominantColor;
7696ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal
7796ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        // Make sure that the dominant color has enough saturation to be visible properly.
7896ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        float[] hsv = new float[3];
7996ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        Color.colorToHSV(result, hsv);
8096ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        if (hsv[1] < MIN_PRELOAD_COLOR_SATURATION) {
811f3f07d47c29cba3b70bcd15ebb65a077f55a558Sunny Goyal            result = Themes.getColorAccent(context);
8296ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        } else {
8396ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal            hsv[2] = Math.max(MIN_PRELOAD_COLOR_LIGHTNESS, hsv[2]);
8496ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal            result = Color.HSVToColor(hsv);
8596ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        }
8696ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal        return result;
8796ac68a481ce5b794b5227e09ace7c30d6dd5e7bSunny Goyal    }
889a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham
897092db02410562026da17a7b38f48025cc847de2Tony Wickham    public static IconPalette fromDominantColor(int dominantColor, boolean desaturateBackground) {
907092db02410562026da17a7b38f48025cc847de2Tony Wickham        return new IconPalette(dominantColor, desaturateBackground);
917092db02410562026da17a7b38f48025cc847de2Tony Wickham    }
927092db02410562026da17a7b38f48025cc847de2Tony Wickham
937092db02410562026da17a7b38f48025cc847de2Tony Wickham    /**
947092db02410562026da17a7b38f48025cc847de2Tony Wickham     * Returns an IconPalette based on the badge_color in colors.xml.
957092db02410562026da17a7b38f48025cc847de2Tony Wickham     * If that color is Color.TRANSPARENT, then returns null instead.
967092db02410562026da17a7b38f48025cc847de2Tony Wickham     */
977092db02410562026da17a7b38f48025cc847de2Tony Wickham    public static @Nullable IconPalette getBadgePalette(Resources resources) {
987092db02410562026da17a7b38f48025cc847de2Tony Wickham        int badgeColor = resources.getColor(R.color.badge_color);
997092db02410562026da17a7b38f48025cc847de2Tony Wickham        if (badgeColor == Color.TRANSPARENT) {
1007092db02410562026da17a7b38f48025cc847de2Tony Wickham            // Colors will be extracted per app icon, so a static palette won't work.
1017092db02410562026da17a7b38f48025cc847de2Tony Wickham            return null;
1027092db02410562026da17a7b38f48025cc847de2Tony Wickham        }
1037092db02410562026da17a7b38f48025cc847de2Tony Wickham        if (sBadgePalette == null) {
1047092db02410562026da17a7b38f48025cc847de2Tony Wickham            sBadgePalette = fromDominantColor(badgeColor, false);
1057092db02410562026da17a7b38f48025cc847de2Tony Wickham        }
1067092db02410562026da17a7b38f48025cc847de2Tony Wickham        return sBadgePalette;
1077092db02410562026da17a7b38f48025cc847de2Tony Wickham    }
1087092db02410562026da17a7b38f48025cc847de2Tony Wickham
1097092db02410562026da17a7b38f48025cc847de2Tony Wickham    /**
1107092db02410562026da17a7b38f48025cc847de2Tony Wickham     * Returns an IconPalette based on the folder_badge_color in colors.xml.
1117092db02410562026da17a7b38f48025cc847de2Tony Wickham     */
1127092db02410562026da17a7b38f48025cc847de2Tony Wickham    public static @NonNull IconPalette getFolderBadgePalette(Resources resources) {
1137092db02410562026da17a7b38f48025cc847de2Tony Wickham        if (sFolderBadgePalette == null) {
1147092db02410562026da17a7b38f48025cc847de2Tony Wickham            int badgeColor = resources.getColor(R.color.folder_badge_color);
1157092db02410562026da17a7b38f48025cc847de2Tony Wickham            sFolderBadgePalette = fromDominantColor(badgeColor, false);
1167092db02410562026da17a7b38f48025cc847de2Tony Wickham        }
1177092db02410562026da17a7b38f48025cc847de2Tony Wickham        return sFolderBadgePalette;
1189a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham    }
1199a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham
120f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    /**
121f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * Resolves a color such that it has enough contrast to be used as the
122f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * color of an icon or text on the given background color.
123f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     *
124f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * @return a color of the same hue with enough contrast against the background.
125f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     *
126f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * This was copied from com.android.internal.util.NotificationColorUtil.
127f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     */
128f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    public static int resolveContrastColor(Context context, int color, int background) {
129f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        final int resolvedColor = resolveColor(context, color);
130f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham
131f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        int contrastingColor = ensureTextContrast(resolvedColor, background);
132f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham
133f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        if (contrastingColor != resolvedColor) {
134f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham            if (DEBUG){
135f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham                Log.w(TAG, String.format(
136f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham                        "Enhanced contrast of notification for %s " +
137f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham                                "%s (over background) by changing #%s to %s",
138f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham                        context.getPackageName(),
139f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham                        contrastChange(resolvedColor, contrastingColor, background),
140f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham                        Integer.toHexString(resolvedColor), Integer.toHexString(contrastingColor)));
141f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham            }
142f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        }
143f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        return contrastingColor;
144f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    }
145f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham
146f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    /**
147f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
148f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     *
149f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * This was copied from com.android.internal.util.NotificationColorUtil.
150f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     */
151f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    private static int resolveColor(Context context, int color) {
152f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        if (color == Notification.COLOR_DEFAULT) {
153f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham            return context.getColor(R.color.notification_icon_default_color);
154f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        }
155f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        return color;
156f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    }
157f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham
158f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    /** For debugging. This was copied from com.android.internal.util.NotificationColorUtil. */
159f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    private static String contrastChange(int colorOld, int colorNew, int bg) {
160f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        return String.format("from %.2f:1 to %.2f:1",
161f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham                ColorUtils.calculateContrast(colorOld, bg),
162f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham                ColorUtils.calculateContrast(colorNew, bg));
163f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    }
164f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham
165f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    /**
166f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * Finds a text color with sufficient contrast over bg that has the same hue as the original
167f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * color.
168f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     *
169f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * This was copied from com.android.internal.util.NotificationColorUtil.
170f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     */
171f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    private static int ensureTextContrast(int color, int bg) {
17247ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham        return findContrastColor(color, bg, 4.5);
173f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    }
174f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    /**
175f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * Finds a suitable color such that there's enough contrast.
176f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     *
17747ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham     * @param fg the color to start searching from.
17847ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham     * @param bg the color to ensure contrast against.
179f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * @param minRatio the minimum contrast ratio required.
180f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * @return a color with the same hue as {@param color}, potentially darkened to meet the
181f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     *          contrast ratio.
182f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     *
183f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     * This was copied from com.android.internal.util.NotificationColorUtil.
184f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham     */
18547ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham    private static int findContrastColor(int fg, int bg, double minRatio) {
186f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        if (ColorUtils.calculateContrast(fg, bg) >= minRatio) {
18747ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham            return fg;
188f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        }
189f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham
190f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        double[] lab = new double[3];
19147ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham        ColorUtils.colorToLAB(bg, lab);
19247ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham        double bgL = lab[0];
19347ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham        ColorUtils.colorToLAB(fg, lab);
19447ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham        double fgL = lab[0];
19547ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham        boolean isBgDark = bgL < 50;
196f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham
19747ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham        double low = isBgDark ? fgL : 0, high = isBgDark ? 100 : fgL;
198f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        final double a = lab[1], b = lab[2];
199f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        for (int i = 0; i < 15 && high - low > 0.00001; i++) {
200f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham            final double l = (low + high) / 2;
20147ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham            fg = ColorUtils.LABToColor(l, a, b);
202f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham            if (ColorUtils.calculateContrast(fg, bg) > minRatio) {
20347ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham                if (isBgDark) high = l; else low = l;
204f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham            } else {
20547ab9b1fc5a8625bc714a1def91374d762927877Tony Wickham                if (isBgDark) low = l; else high = l;
206f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham            }
207f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        }
208f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham        return ColorUtils.LABToColor(low, a, b);
209f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham    }
210f79877c04c071b7ae1618395f0a1dce134fec36eTony Wickham
2110530e8c60809afa92b1aa907376f0553ad94d58fTony Wickham    private static int getMutedColor(int color, float whiteScrimAlpha) {
2120530e8c60809afa92b1aa907376f0553ad94d58fTony Wickham        int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * whiteScrimAlpha));
2131237df0a7cb89570b90b30fa30a3c76417ce3b64Tony Wickham        return ColorUtils.compositeColors(whiteScrim, color);
2149a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham    }
2159a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham
2169a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham    private static int getTextColorForBackground(int backgroundColor) {
2171237df0a7cb89570b90b30fa30a3c76417ce3b64Tony Wickham        return getLighterOrDarkerVersionOfColor(backgroundColor, 4.5f);
2189a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham    }
2199a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham
2209a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham    private static int getLowContrastColor(int color) {
2219a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham        return getLighterOrDarkerVersionOfColor(color, 1.5f);
2229a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham    }
2239a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham
2249a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham    private static int getLighterOrDarkerVersionOfColor(int color, float contrastRatio) {
2259a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham        int whiteMinAlpha = ColorUtils.calculateMinimumAlpha(Color.WHITE, color, contrastRatio);
2269a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham        int blackMinAlpha = ColorUtils.calculateMinimumAlpha(Color.BLACK, color, contrastRatio);
2279a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham        int translucentWhiteOrBlack;
2289a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham        if (whiteMinAlpha >= 0) {
2299a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham            translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.WHITE, whiteMinAlpha);
2309a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham        } else if (blackMinAlpha >= 0) {
2319a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham            translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.BLACK, blackMinAlpha);
2329a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham        } else {
2339a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham            translucentWhiteOrBlack = Color.WHITE;
2349a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham        }
2359a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham        return ColorUtils.compositeColors(translucentWhiteOrBlack, color);
2369a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham    }
2379a8d11f930ced4c2706db150b7bbbb21330bd68dTony Wickham}
238