12c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer/*
22c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer * Copyright (C) 2015 The Android Open Source Project
32c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer *
42c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer * Licensed under the Apache License, Version 2.0 (the "License");
52c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer * you may not use this file except in compliance with the License.
62c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer * You may obtain a copy of the License at
72c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer *
82c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer *      http://www.apache.org/licenses/LICENSE-2.0
92c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer *
102c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer * Unless required by applicable law or agreed to in writing, software
112c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer * distributed under the License is distributed on an "AS IS" BASIS,
122c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer * See the License for the specific language governing permissions and
142c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer * limitations under the License.
152c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer */
162c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyerpackage com.android.car.apps.common;
172c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
182c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyerimport android.content.Context;
192c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyerimport android.graphics.Color;
202c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyerimport android.util.Log;
212c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
222c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer/**
232c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer * @hide
242c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer */
252c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyerpublic class ColorChecker {
262c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    private static final String TAG = "ColorChecker";
272c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    private static final double MIN_CONTRAST_RATIO = 4.5;
282c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    /**
292c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * Non-critical information doesn't have to meet as stringent contrast requirements.
302c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     */
312c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    private static final double MIN_NON_CRITICAL_CONTRAST_RATIO = 1.5;
322c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
332c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    /**
342c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * Calls {@link #getTintColor(int, int...)} with:
352c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *     {@link R.color#car_tint_light} and
362c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *     {@link R.color#car_tint_dark}
372c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     */
382c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    public static int getTintColor(Context context, int backgroundColor) {
392c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        int lightTintColor = context.getResources().getColor(R.color.car_tint_light);
402c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        int darkTintColor = context.getResources().getColor(R.color.car_tint_dark);
412c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
422c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        return getTintColor(backgroundColor, lightTintColor, darkTintColor);
432c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    }
442c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
452c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    /**
462c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * Calls {@link #getNonCriticalTintColor(int, int...)} with:
472c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *     {@link R.color#car_tint_light} and
482c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *     {@link R.color#car_tint_dark}
492c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     */
502c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    public static int getNonCriticalTintColor(Context context, int backgroundColor) {
512c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        int lightTintColor = context.getResources().getColor(R.color.car_tint_light);
522c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        int darkTintColor = context.getResources().getColor(R.color.car_tint_dark);
532c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
542c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        return getNonCriticalTintColor(backgroundColor, lightTintColor, darkTintColor);
552c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    }
562c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
572c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    /**
582c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * Calls {@link #getTintColor(int, int...)} with {@link #MIN_CONTRAST_RATIO}.
592c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     */
602c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    public static int getTintColor(int backgroundColor, int... tintColors) {
612c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        return getTintColor(MIN_CONTRAST_RATIO, backgroundColor, tintColors);
622c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    }
632c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
642c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    /**
652c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * Calls {@link #getTintColor(int, int...)} with {@link #MIN_NON_CRITICAL_CONTRAST_RATIO}.
662c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     */
672c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    public static int getNonCriticalTintColor(int backgroundColor, int... tintColors) {
682c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        return getTintColor(MIN_NON_CRITICAL_CONTRAST_RATIO, backgroundColor, tintColors);
692c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    }
702c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
712c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    /**
722c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *
732c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * Determines what color to tint icons given the background color that they sit on.
742c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *
752c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * @param minAllowedContrastRatio The minimum contrast ratio
762c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * @param bgColor The background color that the icons sit on.
772c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * @param tintColors A list of potential colors to tint the icons with.
782c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * @return The color that the icons should be tinted. Will be the first tinted color that
792c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *         meets the requirements. If none of the tint colors meet the minimum requirements,
802c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *         either black or white will be returned, whichever has a higher contrast.
812c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     */
822c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    public static int getTintColor(double minAllowedContrastRatio, int bgColor, int... tintColors) {
832c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        for (int tc : tintColors) {
842c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer            double contrastRatio = getContrastRatio(bgColor, tc);
852c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer            if (contrastRatio >= minAllowedContrastRatio) {
862c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer                return tc;
872c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer            }
882c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        }
892c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        double blackContrastRatio = getContrastRatio(bgColor, Color.BLACK);
902c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        double whiteContrastRatio = getContrastRatio(bgColor, Color.WHITE);
912c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        if (whiteContrastRatio >= blackContrastRatio) {
922c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer            Log.w(TAG, "Tint color does not meet contrast requirements. Using white.");
932c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer            return Color.WHITE;
942c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        } else {
952c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer            Log.w(TAG, "Tint color does not meet contrast requirements. Using black.");
962c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer            return Color.BLACK;
972c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        }
982c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    }
992c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
1002c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    public static double getContrastRatio(int color1, int color2) {
1012c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        return getContrastRatio(getLuminance(color1), getLuminance(color2));
1022c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    }
1032c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
1042c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    public static double getContrastRatio(double luminance1, double luminance2) {
1052c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        return (Math.max(luminance1, luminance2) + 0.05) /
1062c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer                (Math.min(luminance1, luminance2) + 0.05);
1072c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    }
1082c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
1092c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    /**
1102c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * Calculates the luminance of a color as specified by:
1112c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *     http://www.w3.org/TR/WCAG20-TECHS/G17.html
1122c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *
1132c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * @param color The color to calculate the luminance of.
1142c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * @return The luminance.
1152c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     */
1162c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    public static double getLuminance(int color) {
1172c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        // Values are in sRGB
1182c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        double r = convert8BitToLuminanceComponent(Color.red(color));
1192c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        double g = convert8BitToLuminanceComponent(Color.green(color));
1202c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        double b = convert8BitToLuminanceComponent(Color.blue(color));
1212c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        return r * 0.2126 + g * 0.7152 + b * 0.0722;
1222c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    }
1232c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer
1242c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    /**
1252c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     * Converts am 8 bit color component (0-255) to the luminance component as specified by:
1262c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     *     http://www.w3.org/TR/WCAG20-TECHS/G17.html
1272c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer     */
1282c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    private static double convert8BitToLuminanceComponent(double component) {
1292c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        component /= 255.0;
1302c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        if (component <= 0.03928) {
1312c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer            return component / 12.92;
1322c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        } else {
1332c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer            return Math.pow(((component + 0.055) / 1.055), 2.4);
1342c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer        }
1352c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer    }
1362c147cd63a518c3b779697d7b5cb53d86bfc2a00Rakesh Iyer}
137