package com.boardgamegeek.util;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.support.v4.util.ArrayMap;
import android.support.v4.view.ViewCompat;
import android.text.TextUtils;
import android.util.Pair;
import android.util.TypedValue;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.github.lzyzsd.randomcolor.RandomColor;
import com.github.lzyzsd.randomcolor.RandomColor.Luminosity;
import com.github.lzyzsd.randomcolor.RandomColor.SaturationType;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Static methods for modifying and applying colors to views.
*/
public class ColorUtils {
public static final int TRANSPARENT = 0;
private static final int BLACK = 0xFF000000;
// private static final int DKGRAY = 0xFF444444;
private static final int GRAY = 0xFF888888;
// private static final int LTGRAY = 0xFFCCCCCC;
private static final int WHITE = 0xFFFFFFFF;
private static final int RED = 0xFFFF0000;
private static final int GREEN = 0xFF008000; // dark green really
private static final int BLUE = 0xFF0000FF;
private static final int YELLOW = 0xFFFFFF00;
// private static final int CYAN = 0xFF00FFFF;
// private static final int MAGENTA = 0xFFFF00FF;
private static final int PURPLE = 0xFF800080;
private static final int ORANGE = 0xFFE59400;
private static final int BROWN = 0xFFA52A2A;
private static final int NATURAL = 0xFFE9C2A6;
private static final int TAN = 0xFFDB9370;
private static final int IVORY = 0xFFFFFFF0;
private static final int ROSE = 0xFFFF007F;
private static final int PINK = 0xFFCD919E;
private static final int TEAL = 0xFF008080;
// private static final int AQUA = 0xFF66CCCC;
private static final int BRONZE = 0xFF8C7853;
private static final int SILVER = 0xFFC0C0C0;
private static final int GOLD = 0xFFFFD700;
private ColorUtils() {
}
/**
* Determine the RGB value of a named color, or a string formatted as "#aarrggbb". Returns a transparent color if
* the color can't be determined from the string.
*/
public static int parseColor(String colorString) {
if (TextUtils.isEmpty(colorString)) {
return TRANSPARENT;
}
if (colorString.charAt(0) == '#') {
if (colorString.length() == 7 || colorString.length() == 9) {
// Use a long to avoid rollovers on #ffXXXXXX
long color = Long.parseLong(colorString.substring(1), 16);
if (colorString.length() == 7) {
// Set the alpha value
color |= 0x00000000ff000000;
}
return (int) color;
} else {
return TRANSPARENT;
}
} else {
Integer color = colorNameMap.get(formatKey(colorString));
if (color != null) {
return color;
}
}
return TRANSPARENT;
}
/**
* Returns a color based on the rating. This maps to the colors used on BGG for integers, using a proportional blend
* for any decimal places.
*/
public static int getRatingColor(double rating) {
int baseRating = MathUtils.constrain((int) rating, 0, 10);
return blendColors(RATING_COLORS[baseRating], RATING_COLORS[baseRating + 1], baseRating + 1 - rating);
}
/**
* Returns a color based that is ratio% of color1 and (1 - ratio)% of color2 (including alpha).
*/
private static int blendColors(int color1, int color2, double ratio) {
double ir = 1.0 - ratio;
int a = (int) (Color.alpha(color1) * ratio + Color.alpha(color2) * ir);
int r = (int) (Color.red(color1) * ratio + Color.red(color2) * ir);
int g = (int) (Color.green(color1) * ratio + Color.green(color2) * ir);
int b = (int) (Color.blue(color1) * ratio + Color.blue(color2) * ir);
return Color.argb(a, r, g, b);
}
/**
* An array of RGBs that match the BGG ratings from 0 to 10.
*/
private static final int[] RATING_COLORS = { 0x00ffffff, 0xffff0000, 0xffff3366, 0xffff6699, 0xffff66cc,
0xffcc99ff, 0xff9999ff, 0xff99ffff, 0xff66ff99, 0xff33cc99, 0xff00cc00, 0x00ffffff };
private static final ArrayMap<String, Integer> colorNameMap;
private static final ArrayList<Pair<String, Integer>> limitedColorNameList;
private static final ArrayList<Pair<String, Integer>> colorNameList;
static {
limitedColorNameList = new ArrayList<>();
limitedColorNameList.add(new Pair<>("Red", RED));
limitedColorNameList.add(new Pair<>("Yellow", YELLOW));
limitedColorNameList.add(new Pair<>("Blue", BLUE));
limitedColorNameList.add(new Pair<>("Green", GREEN));
limitedColorNameList.add(new Pair<>("Purple", PURPLE));
limitedColorNameList.add(new Pair<>("Orange", ORANGE));
limitedColorNameList.add(new Pair<>("White", WHITE));
limitedColorNameList.add(new Pair<>("Black", BLACK));
limitedColorNameList.add(new Pair<>("Natural", NATURAL));
limitedColorNameList.add(new Pair<>("Brown", BROWN));
colorNameList = new ArrayList<>();
colorNameList.addAll(limitedColorNameList);
colorNameList.add(new Pair<>("Tan", TAN));
colorNameList.add(new Pair<>("Gray", GRAY));
colorNameList.add(new Pair<>("Gold", GOLD));
colorNameList.add(new Pair<>("Silver", SILVER));
colorNameList.add(new Pair<>("Bronze", BRONZE));
colorNameList.add(new Pair<>("Ivory", IVORY));
colorNameList.add(new Pair<>("Rose", ROSE));
colorNameList.add(new Pair<>("Pink", PINK));
colorNameList.add(new Pair<>("Teal", TEAL));
// colorNameList.add(new Pair<String, Integer>("Aqua", AQUA));
// colorNameList.add(new Pair<String, Integer>("Cyan", CYAN));
// colorNameList.add(new Pair<String, Integer>("Magenta", MAGENTA));
// colorNameList.add(new Pair<String, Integer>("Light Gray", LTGRAY));
// colorNameList.add(new Pair<String, Integer>("Dark Gray", DKGRAY));
colorNameMap = new ArrayMap<>();
for (Pair<String, Integer> pair : colorNameList) {
colorNameMap.put(formatKey(pair.first), pair.second);
}
}
private static String formatKey(String name) {
return name.toLowerCase(Locale.US);
}
public static List<Pair<String, Integer>> getColorList() {
//noinspection unchecked
return (List<Pair<String, Integer>>) colorNameList.clone();
}
public static List<Pair<String, Integer>> getLimitedColorList() {
//noinspection unchecked
return (List<Pair<String, Integer>>) limitedColorNameList.clone();
}
/**
* Set the background of a {@link android.view.View} to the specified color, with a darker version of the
* color as a border.
*/
public static void setViewBackground(View view, int color) {
Resources r = view.getResources();
Drawable currentDrawable = view.getBackground();
GradientDrawable backgroundDrawable;
if (currentDrawable != null && currentDrawable instanceof GradientDrawable) {
// Reuse drawable
backgroundDrawable = (GradientDrawable) currentDrawable;
} else {
backgroundDrawable = new GradientDrawable();
}
int darkenedColor = darkenColor(color);
backgroundDrawable.setColor(color);
backgroundDrawable.setStroke(getStrokeWidth(r), darkenedColor);
ViewCompat.setBackground(view, backgroundDrawable);
}
private static int getStrokeWidth(Resources r) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, r.getDisplayMetrics());
}
/**
* Set the background of an {@link android.widget.ImageView} to an oval of the specified color, with a darker
* version of the color as a border. For a {@link android.widget.TextView}, changes the text color instead. Doesn't
* do anything for other views. Modified from Roman Nurik's DashClock (https://code.google.com/p/dashclock/).
*/
public static void setColorViewValue(View view, int color) {
if (view instanceof ImageView) {
ImageView imageView = (ImageView) view;
Resources r = imageView.getResources();
Drawable currentDrawable = imageView.getDrawable();
GradientDrawable colorChoiceDrawable;
if (currentDrawable != null && currentDrawable instanceof GradientDrawable) {
// Reuse drawable
colorChoiceDrawable = (GradientDrawable) currentDrawable;
} else {
colorChoiceDrawable = new GradientDrawable();
colorChoiceDrawable.setShape(GradientDrawable.OVAL);
}
// Set stroke to dark version of color
int darkenedColor = darkenColor(color);
colorChoiceDrawable.setColor(color);
final int width = getStrokeWidth(r);
colorChoiceDrawable.setStroke(width, darkenedColor);
imageView.setImageDrawable(colorChoiceDrawable);
} else if (view instanceof TextView) {
if (color != ColorUtils.TRANSPARENT) {
((TextView) view).setTextColor(color);
}
}
}
/**
* Returns a darker version of the specified color. Returns a translucent gray for transparent colors.
*/
private static int darkenColor(int color) {
if (color == TRANSPARENT) {
return Color.argb(127, 127, 127, 127);
}
return Color.rgb(Color.red(color) * 192 / 256, Color.green(color) * 192 / 256, Color.blue(color) * 192 / 256);
}
/**
* Calculate whether a color is light or dark, based on a commonly known brightness formula.
* <p>
* {@literal http://en.wikipedia.org/wiki/HSV_color_space%23Lightness}
*/
public static boolean isColorDark(int color) {
return ((30 * Color.red(color) + 59 * Color.green(color) + 11 * Color.blue(color)) / 100) <= 130;
}
public static final int[] FIVE_STAGE_COLORS = {
GREEN, BLUE, YELLOW, ORANGE, RED
};
/**
* Create an array of random, but light, colors
*/
public static int[] createColors(int count) {
RandomColor r = new RandomColor();
int[] colors = new int[count];
for (int i = 0; i < count; ++i) {
colors[i] = r.randomColor(0, SaturationType.RANDOM, Luminosity.LIGHT);
}
return colors;
}
}