blob: 722ecb3ac88ce8f2980c5ed3158b6c90240e1826 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.media.common;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
/**
* A class that checks to make sure that given colors are within the acceptable contract ratio for
* the car environment.
*/
public class ColorChecker {
private static final String TAG = "ColorChecker";
private static final double MIN_CONTRAST_RATIO = 4.5;
/**
* Calls {@link #getTintColor(int, int...)} with:
* {@code R.color.car_tint_light} and
* {@code R.color.car_tint_dark}
*/
public static int getTintColor(Context context, int backgroundColor) {
int lightTintColor = context.getResources().getColor(R.color.media_tint_light);
int darkTintColor = context.getResources().getColor(R.color.media_tint_dark);
return getTintColor(backgroundColor, lightTintColor, darkTintColor);
}
/**
* Calls {@link #getTintColor(int, int...)} with {@link #MIN_CONTRAST_RATIO}.
*/
public static int getTintColor(int backgroundColor, int... tintColors) {
return getTintColor(MIN_CONTRAST_RATIO, backgroundColor, tintColors);
}
/**
*
* Determines what color to tint icons given the background color that they sit on.
*
* @param minAllowedContrastRatio The minimum contrast ratio
* @param bgColor The background color that the icons sit on.
* @param tintColors A list of potential colors to tint the icons with.
* @return The color that the icons should be tinted. Will be the first tinted color that
* meets the requirements. If none of the tint colors meet the minimum requirements,
* either black or white will be returned, whichever has a higher contrast.
*/
public static int getTintColor(double minAllowedContrastRatio, int bgColor, int... tintColors) {
for (int tc : tintColors) {
double contrastRatio = getContrastRatio(bgColor, tc);
if (contrastRatio >= minAllowedContrastRatio) {
return tc;
}
}
double blackContrastRatio = getContrastRatio(bgColor, Color.BLACK);
double whiteContrastRatio = getContrastRatio(bgColor, Color.WHITE);
if (whiteContrastRatio >= blackContrastRatio) {
Log.w(TAG, "Tint color does not meet contrast requirements. Using white.");
return Color.WHITE;
} else {
Log.w(TAG, "Tint color does not meet contrast requirements. Using black.");
return Color.BLACK;
}
}
/**
* Returns the contrast radio between the two given colors.
*/
public static double getContrastRatio(int color1, int color2) {
return getContrastRatio(getLuminance(color1), getLuminance(color2));
}
/**
* Returns the contrast ratio between the two luminances. Luminances maps to colors and is the
* result returned from {@link #getLuminance(int)}.
*/
public static double getContrastRatio(double luminance1, double luminance2) {
return (Math.max(luminance1, luminance2) + 0.05)
/ (Math.min(luminance1, luminance2) + 0.05);
}
/**
* Calculates the luminance of a color as specified by:
* http://www.w3.org/TR/WCAG20-TECHS/G17.html
*
* @param color The color to calculate the luminance of.
* @return The luminance.
*/
public static double getLuminance(int color) {
// Values are in sRGB
double r = convert8BitToLuminanceComponent(Color.red(color));
double g = convert8BitToLuminanceComponent(Color.green(color));
double b = convert8BitToLuminanceComponent(Color.blue(color));
return r * 0.2126 + g * 0.7152 + b * 0.0722;
}
/**
* Converts am 8 bit color component (0-255) to the luminance component as specified by:
* http://www.w3.org/TR/WCAG20-TECHS/G17.html
*/
private static double convert8BitToLuminanceComponent(double component) {
component /= 255.0;
if (component <= 0.03928) {
return component / 12.92;
} else {
return Math.pow(((component + 0.055) / 1.055), 2.4);
}
}
private ColorChecker() {}
}