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