Home | History | Annotate | Download | only in ui
      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 public class ColorChecker {
     23     private static final String TAG = "GH.ColorChecker";
     24     private static final double MIN_CONTRAST_RATIO = 4.5;
     25     /**
     26      * Non-critical information doesn't have to meet as stringent contrast requirements.
     27      */
     28     private static final double MIN_NON_CRITICAL_CONTRAST_RATIO = 1.5;
     29 
     30     /**
     31      * Calls {@link #getTintColor(int, int...)} with:
     32      *     {@link R.color#car_tint_light} and
     33      *     {@link R.color#car_tint_dark}
     34      */
     35     public static int getTintColor(Context context, int backgroundColor) {
     36         int lightTintColor = context.getResources().getColor(R.color.car_tint_light);
     37         int darkTintColor = context.getResources().getColor(R.color.car_tint_dark);
     38 
     39         return getTintColor(backgroundColor, lightTintColor, darkTintColor);
     40     }
     41 
     42     /**
     43      * Calls {@link #getNonCriticalTintColor(int, int...)} with:
     44      *     {@link R.color#car_tint_light} and
     45      *     {@link R.color#car_tint_dark}
     46      */
     47     public static int getNonCriticalTintColor(Context context, int backgroundColor) {
     48         int lightTintColor = context.getResources().getColor(R.color.car_tint_light);
     49         int darkTintColor = context.getResources().getColor(R.color.car_tint_dark);
     50 
     51         return getNonCriticalTintColor(backgroundColor, lightTintColor, darkTintColor);
     52     }
     53 
     54     /**
     55      * Calls {@link #getTintColor(int, int...)} with {@link #MIN_CONTRAST_RATIO}.
     56      */
     57     public static int getTintColor(int backgroundColor, int... tintColors) {
     58         return getTintColor(MIN_CONTRAST_RATIO, backgroundColor, tintColors);
     59     }
     60 
     61     /**
     62      * Calls {@link #getTintColor(int, int...)} with {@link #MIN_NON_CRITICAL_CONTRAST_RATIO}.
     63      */
     64     public static int getNonCriticalTintColor(int backgroundColor, int... tintColors) {
     65         return getTintColor(MIN_NON_CRITICAL_CONTRAST_RATIO, backgroundColor, tintColors);
     66     }
     67 
     68     /**
     69      *
     70      * Determines what color to tint icons given the background color that they sit on.
     71      *
     72      * @param minAllowedContrastRatio The minimum contrast ratio
     73      * @param bgColor The background color that the icons sit on.
     74      * @param tintColors A list of potential colors to tint the icons with.
     75      * @return The color that the icons should be tinted. Will be the first tinted color that
     76      *         meets the requirements. If none of the tint colors meet the minimum requirements,
     77      *         either black or white will be returned, whichever has a higher contrast.
     78      */
     79     public static int getTintColor(double minAllowedContrastRatio, int bgColor, int... tintColors) {
     80         for (int tc : tintColors) {
     81             double contrastRatio = getContrastRatio(bgColor, tc);
     82             if (contrastRatio >= minAllowedContrastRatio) {
     83                 return tc;
     84             }
     85         }
     86         double blackContrastRatio = getContrastRatio(bgColor, Color.BLACK);
     87         double whiteContrastRatio = getContrastRatio(bgColor, Color.WHITE);
     88         if (whiteContrastRatio >= blackContrastRatio) {
     89             Log.w(TAG, "Tint color does not meet contrast requirements. Using white.");
     90             return Color.WHITE;
     91         } else {
     92             Log.w(TAG, "Tint color does not meet contrast requirements. Using black.");
     93             return Color.BLACK;
     94         }
     95     }
     96 
     97     public static double getContrastRatio(int color1, int color2) {
     98         return getContrastRatio(getLuminance(color1), getLuminance(color2));
     99     }
    100 
    101     public static double getContrastRatio(double luminance1, double luminance2) {
    102         return (Math.max(luminance1, luminance2) + 0.05) /
    103                 (Math.min(luminance1, luminance2) + 0.05);
    104     }
    105 
    106     /**
    107      * Calculates the luminance of a color as specified by:
    108      *     http://www.w3.org/TR/WCAG20-TECHS/G17.html
    109      *
    110      * @param color The color to calculate the luminance of.
    111      * @return The luminance.
    112      */
    113     public static double getLuminance(int color) {
    114         // Values are in sRGB
    115         double r = convert8BitToLuminanceComponent(Color.red(color));
    116         double g = convert8BitToLuminanceComponent(Color.green(color));
    117         double b = convert8BitToLuminanceComponent(Color.blue(color));
    118         return r * 0.2126 + g * 0.7152 + b * 0.0722;
    119     }
    120 
    121     /**
    122      * Converts am 8 bit color component (0-255) to the luminance component as specified by:
    123      *     http://www.w3.org/TR/WCAG20-TECHS/G17.html
    124      */
    125     private static double convert8BitToLuminanceComponent(double component) {
    126         component /= 255.0;
    127         if (component <= 0.03928) {
    128             return component / 12.92;
    129         } else {
    130             return Math.pow(((component + 0.055) / 1.055), 2.4);
    131         }
    132     }
    133 }
    134