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