• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.support.car.ui;
17 
18 import android.content.Context;
19 import android.graphics.Color;
20 import android.util.Log;
21 
22 /**
23  * @hide
24  */
25 public class ColorChecker {
26     private static final String TAG = "GH.ColorChecker";
27     private static final double MIN_CONTRAST_RATIO = 4.5;
28     /**
29      * Non-critical information doesn't have to meet as stringent contrast requirements.
30      */
31     private static final double MIN_NON_CRITICAL_CONTRAST_RATIO = 1.5;
32 
33     /**
34      * Calls {@link #getTintColor(int, int...)} with:
35      *     {@link R.color#car_tint_light} and
36      *     {@link R.color#car_tint_dark}
37      */
getTintColor(Context context, int backgroundColor)38     public static int getTintColor(Context context, int backgroundColor) {
39         int lightTintColor = context.getResources().getColor(R.color.car_tint_light);
40         int darkTintColor = context.getResources().getColor(R.color.car_tint_dark);
41 
42         return getTintColor(backgroundColor, lightTintColor, darkTintColor);
43     }
44 
45     /**
46      * Calls {@link #getNonCriticalTintColor(int, int...)} with:
47      *     {@link R.color#car_tint_light} and
48      *     {@link R.color#car_tint_dark}
49      */
getNonCriticalTintColor(Context context, int backgroundColor)50     public static int getNonCriticalTintColor(Context context, int backgroundColor) {
51         int lightTintColor = context.getResources().getColor(R.color.car_tint_light);
52         int darkTintColor = context.getResources().getColor(R.color.car_tint_dark);
53 
54         return getNonCriticalTintColor(backgroundColor, lightTintColor, darkTintColor);
55     }
56 
57     /**
58      * Calls {@link #getTintColor(int, int...)} with {@link #MIN_CONTRAST_RATIO}.
59      */
getTintColor(int backgroundColor, int... tintColors)60     public static int getTintColor(int backgroundColor, int... tintColors) {
61         return getTintColor(MIN_CONTRAST_RATIO, backgroundColor, tintColors);
62     }
63 
64     /**
65      * Calls {@link #getTintColor(int, int...)} with {@link #MIN_NON_CRITICAL_CONTRAST_RATIO}.
66      */
getNonCriticalTintColor(int backgroundColor, int... tintColors)67     public static int getNonCriticalTintColor(int backgroundColor, int... tintColors) {
68         return getTintColor(MIN_NON_CRITICAL_CONTRAST_RATIO, backgroundColor, tintColors);
69     }
70 
71     /**
72      *
73      * Determines what color to tint icons given the background color that they sit on.
74      *
75      * @param minAllowedContrastRatio The minimum contrast ratio
76      * @param bgColor The background color that the icons sit on.
77      * @param tintColors A list of potential colors to tint the icons with.
78      * @return The color that the icons should be tinted. Will be the first tinted color that
79      *         meets the requirements. If none of the tint colors meet the minimum requirements,
80      *         either black or white will be returned, whichever has a higher contrast.
81      */
getTintColor(double minAllowedContrastRatio, int bgColor, int... tintColors)82     public static int getTintColor(double minAllowedContrastRatio, int bgColor, int... tintColors) {
83         for (int tc : tintColors) {
84             double contrastRatio = getContrastRatio(bgColor, tc);
85             if (contrastRatio >= minAllowedContrastRatio) {
86                 return tc;
87             }
88         }
89         double blackContrastRatio = getContrastRatio(bgColor, Color.BLACK);
90         double whiteContrastRatio = getContrastRatio(bgColor, Color.WHITE);
91         if (whiteContrastRatio >= blackContrastRatio) {
92             Log.w(TAG, "Tint color does not meet contrast requirements. Using white.");
93             return Color.WHITE;
94         } else {
95             Log.w(TAG, "Tint color does not meet contrast requirements. Using black.");
96             return Color.BLACK;
97         }
98     }
99 
getContrastRatio(int color1, int color2)100     public static double getContrastRatio(int color1, int color2) {
101         return getContrastRatio(getLuminance(color1), getLuminance(color2));
102     }
103 
getContrastRatio(double luminance1, double luminance2)104     public static double getContrastRatio(double luminance1, double luminance2) {
105         return (Math.max(luminance1, luminance2) + 0.05) /
106                 (Math.min(luminance1, luminance2) + 0.05);
107     }
108 
109     /**
110      * Calculates the luminance of a color as specified by:
111      *     http://www.w3.org/TR/WCAG20-TECHS/G17.html
112      *
113      * @param color The color to calculate the luminance of.
114      * @return The luminance.
115      */
getLuminance(int color)116     public static double getLuminance(int color) {
117         // Values are in sRGB
118         double r = convert8BitToLuminanceComponent(Color.red(color));
119         double g = convert8BitToLuminanceComponent(Color.green(color));
120         double b = convert8BitToLuminanceComponent(Color.blue(color));
121         return r * 0.2126 + g * 0.7152 + b * 0.0722;
122     }
123 
124     /**
125      * Converts am 8 bit color component (0-255) to the luminance component as specified by:
126      *     http://www.w3.org/TR/WCAG20-TECHS/G17.html
127      */
convert8BitToLuminanceComponent(double component)128     private static double convert8BitToLuminanceComponent(double component) {
129         component /= 255.0;
130         if (component <= 0.03928) {
131             return component / 12.92;
132         } else {
133             return Math.pow(((component + 0.055) / 1.055), 2.4);
134         }
135     }
136 }
137