Merge "Second pass for AlertDialog tests." into mnc-ub-dev
diff --git a/v4/api/current.txt b/v4/api/current.txt
index ef8da00..3e1f91a 100644
--- a/v4/api/current.txt
+++ b/v4/api/current.txt
@@ -1094,12 +1094,24 @@
public final class ColorUtils {
method public static int HSLToColor(float[]);
+ method public static int LABToColor(double, double, double);
+ method public static void LABToXYZ(double, double, double, double[]);
method public static void RGBToHSL(int, int, int, float[]);
+ method public static void RGBToLAB(int, int, int, double[]);
+ method public static void RGBToXYZ(int, int, int, double[]);
+ method public static int XYZToColor(double, double, double);
+ method public static void XYZToLAB(double, double, double, double[]);
+ method public static int blendARGB(int, int, float);
+ method public static void blendHSL(float[], float[], float[], float);
+ method public static void blendLAB(double[], double[], double[], double);
method public static double calculateContrast(int, int);
method public static double calculateLuminance(int);
method public static int calculateMinimumAlpha(int, int, float);
method public static void colorToHSL(int, float[]);
+ method public static void colorToLAB(int, double[]);
+ method public static void colorToXYZ(int, double[]);
method public static int compositeColors(int, int);
+ method public static double distanceEuclidean(double[], double[]);
method public static int setAlphaComponent(int, int);
}
diff --git a/v4/java/android/support/v4/graphics/ColorUtils.java b/v4/java/android/support/v4/graphics/ColorUtils.java
index 7fef657..2a3074e 100644
--- a/v4/java/android/support/v4/graphics/ColorUtils.java
+++ b/v4/java/android/support/v4/graphics/ColorUtils.java
@@ -21,15 +21,25 @@
import android.support.annotation.FloatRange;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+import android.util.TypedValue;
/**
* A set of color-related utility methods, building upon those available in {@code Color}.
*/
public final class ColorUtils {
+ private static final double XYZ_WHITE_REFERENCE_X = 95.047;
+ private static final double XYZ_WHITE_REFERENCE_Y = 100;
+ private static final double XYZ_WHITE_REFERENCE_Z = 108.883;
+ private static final double XYZ_EPSILON = 0.008856;
+ private static final double XYZ_KAPPA = 903.3;
+
private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10;
private static final int MIN_ALPHA_SEARCH_PRECISION = 1;
+ private static final ThreadLocal<double[]> TEMP_ARRAY = new ThreadLocal<>();
+
private ColorUtils() {}
/**
@@ -60,24 +70,19 @@
}
/**
- * Returns the luminance of a color.
- * <p>
- * Formula defined
- * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef">here</a>.
- * </p>
+ * Returns the luminance of a color as a float between {@code 0.0} and {@code 1.0}.
+ * <p>Defined as the Y component in the XYZ representation of {@code color}.</p>
*/
@FloatRange(from = 0.0, to = 1.0)
public static double calculateLuminance(@ColorInt int color) {
- double red = Color.red(color) / 255d;
- red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4);
-
- double green = Color.green(color) / 255d;
- green = green < 0.03928 ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4);
-
- double blue = Color.blue(color) / 255d;
- blue = blue < 0.03928 ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4);
-
- return (0.2126 * red) + (0.7152 * green) + (0.0722 * blue);
+ double[] result = TEMP_ARRAY.get();
+ if (result == null) {
+ result = new double[3];
+ TEMP_ARRAY.set(result);
+ }
+ colorToXYZ(color, result);
+ // Luminance is the Y component
+ return result[1] / 100;
}
/**
@@ -300,6 +305,219 @@
return (color & 0x00ffffff) | (alpha << 24);
}
+ /**
+ * Convert the ARGB color to its CIE Lab representative components.
+ *
+ * @param color the ARGB color to convert. The alpha component is ignored.
+ * @param result 3 element array which holds the resulting LAB components.
+ */
+ public static void colorToLAB(@ColorInt int color, @NonNull double[] result) {
+ RGBToLAB(Color.red(color), Color.green(color), Color.blue(color), result);
+ }
+
+ /**
+ * Convert RGB components to its CIE Lab representative components.
+ *
+ * <ul>
+ * <li>result[0] is L [0 ...1)</li>
+ * <li>result[1] is a [-128...127)</li>
+ * <li>result[2] is b [-128...127)</li>
+ * </ul>
+ *
+ * @param r red component value [0..255)
+ * @param g green component value [0..255)
+ * @param b blue component value [0..255)
+ * @param result 3 element array which holds the resulting LAB components.
+ */
+ public static void RGBToLAB(@IntRange(from = 0x0, to = 0xFF) int r,
+ @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
+ @NonNull double[] result) {
+ // First we convert RGB to XYZ
+ RGBToXYZ(r, g, b, result);
+ // result now contains XYZ
+ XYZToLAB(result[0], result[1], result[2], result);
+ // result now contains LAB representation
+ }
+
+ /**
+ * Convert the ARGB color to it's CIE XYZ representative components.
+ *
+ * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
+ * 2° Standard Observer (1931).</p>
+ *
+ * <ul>
+ * <li>result[0] is X [0 ...95.047)</li>
+ * <li>result[1] is Y [0...100)</li>
+ * <li>result[2] is Z [0...108.883)</li>
+ * </ul>
+ *
+ * @param color the ARGB color to convert. The alpha component is ignored.
+ * @param result 3 element array which holds the resulting LAB components.
+ */
+ public static void colorToXYZ(@ColorInt int color, @NonNull double[] result) {
+ RGBToXYZ(Color.red(color), Color.green(color), Color.blue(color), result);
+ }
+
+ /**
+ * Convert RGB components to it's CIE XYZ representative components.
+ *
+ * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
+ * 2° Standard Observer (1931).</p>
+ *
+ * <ul>
+ * <li>result[0] is X [0 ...95.047)</li>
+ * <li>result[1] is Y [0...100)</li>
+ * <li>result[2] is Z [0...108.883)</li>
+ * </ul>
+ *
+ * @param r red component value [0..255)
+ * @param g green component value [0..255)
+ * @param b blue component value [0..255)
+ * @param result 3 element array which holds the resulting XYZ components.
+ */
+ public static void RGBToXYZ(@IntRange(from = 0x0, to = 0xFF) int r,
+ @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
+ @NonNull double[] result) {
+ if (result.length != 3) {
+ throw new IllegalArgumentException("result must have a length of 3.");
+ }
+
+ double sr = r / 255.0;
+ sr = sr < 0.04045 ? sr / 12.92 : Math.pow((sr + 0.055) / 1.055, 2.4);
+ double sg = g / 255.0;
+ sg = sg < 0.04045 ? sg / 12.92 : Math.pow((sg + 0.055) / 1.055, 2.4);
+ double sb = b / 255.0;
+ sb = sb < 0.04045 ? sb / 12.92 : Math.pow((sb + 0.055) / 1.055, 2.4);
+
+ result[0] = 100 * (sr * 0.4124 + sg * 0.3576 + sb * 0.1805);
+ result[1] = 100 * (sr * 0.2126 + sg * 0.7152 + sb * 0.0722);
+ result[2] = 100 * (sr * 0.0193 + sg * 0.1192 + sb * 0.9505);
+ }
+
+ /**
+ * Converts a color from CIE XYZ to CIE Lab representation.
+ *
+ * <p>This method expects the XYZ representation to use the D65 illuminant and the CIE
+ * 2° Standard Observer (1931).</p>
+ *
+ * <ul>
+ * <li>result[0] is L [0 ...1)</li>
+ * <li>result[1] is a [-128...127)</li>
+ * <li>result[2] is b [-128...127)</li>
+ * </ul>
+ *
+ * @param x X component value [0...95.047)
+ * @param y Y component value [0...100)
+ * @param z Z component value [0...108.883)
+ * @param result 3 element array which holds the resulting Lab components.
+ */
+ public static void XYZToLAB(@FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_X) double x,
+ @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Y) double y,
+ @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Z) double z,
+ @NonNull double[] result) {
+ if (result.length != 3) {
+ throw new IllegalArgumentException("result must have a length of 3.");
+ }
+ x = pivotXyzComponent(x / XYZ_WHITE_REFERENCE_X);
+ y = pivotXyzComponent(y / XYZ_WHITE_REFERENCE_Y);
+ z = pivotXyzComponent(z / XYZ_WHITE_REFERENCE_Z);
+ result[0] = Math.max(0, 116 * y - 16);
+ result[1] = 500 * (x - y);
+ result[2] = 200 * (y - z);
+ }
+
+ /**
+ * Converts a color from CIE Lab to CIE XYZ representation.
+ *
+ * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
+ * 2° Standard Observer (1931).</p>
+ *
+ * <ul>
+ * <li>result[0] is X [0 ...95.047)</li>
+ * <li>result[1] is Y [0...100)</li>
+ * <li>result[2] is Z [0...108.883)</li>
+ * </ul>
+ *
+ * @param l L component value [0...100)
+ * @param a A component value [-128...127)
+ * @param b B component value [-128...127)
+ * @param result 3 element array which holds the resulting XYZ components.
+ */
+ public static void LABToXYZ(@FloatRange(from = 0f, to = 100) final double l,
+ @FloatRange(from = -128, to = 127) final double a,
+ @FloatRange(from = -128, to = 127) final double b,
+ @NonNull double[] result) {
+ final double fy = (l + 16) / 116;
+ final double fx = a / 500 + fy;
+ final double fz = fy - b / 200;
+
+ double tmp = Math.pow(fx, 3);
+ final double xr = tmp > XYZ_EPSILON ? tmp : (116 * fx - 16) / XYZ_KAPPA;
+ final double yr = l > XYZ_KAPPA * XYZ_EPSILON ? Math.pow(fy, 3) : l / XYZ_KAPPA;
+
+ tmp = Math.pow(fz, 3);
+ final double zr = tmp > XYZ_EPSILON ? tmp : (116 * fz - 16) / XYZ_KAPPA;
+
+ result[0] = xr * XYZ_WHITE_REFERENCE_X;
+ result[1] = yr * XYZ_WHITE_REFERENCE_Y;
+ result[2] = zr * XYZ_WHITE_REFERENCE_Z;
+ }
+
+ /**
+ * Converts a color from CIE XYZ to its RGB representation.
+ *
+ * <p>This method expects the XYZ representation to use the D65 illuminant and the CIE
+ * 2° Standard Observer (1931).</p>
+ *
+ * @param x X component value [0...95.047)
+ * @param y Y component value [0...100)
+ * @param z Z component value [0...108.883)
+ * @return int containing the RGB representation
+ */
+ @ColorInt
+ public static int XYZToColor(@FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_X) double x,
+ @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Y) double y,
+ @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Z) double z) {
+ double r = (x * 3.2406 + y * -1.5372 + z * -0.4986) / 100;
+ double g = (x * -0.9689 + y * 1.8758 + z * 0.0415) / 100;
+ double b = (x * 0.0557 + y * -0.2040 + z * 1.0570) / 100;
+
+ r = r > 0.0031308 ? 1.055 * Math.pow(r, 1 / 2.4) - 0.055 : 12.92 * r;
+ g = g > 0.0031308 ? 1.055 * Math.pow(g, 1 / 2.4) - 0.055 : 12.92 * g;
+ b = b > 0.0031308 ? 1.055 * Math.pow(b, 1 / 2.4) - 0.055 : 12.92 * b;
+
+ return Color.rgb(
+ constrain((int) Math.round(r * 255), 0, 255),
+ constrain((int) Math.round(g * 255), 0, 255),
+ constrain((int) Math.round(b * 255), 0, 255));
+ }
+
+ /**
+ * Converts a color from CIE Lab to its RGB representation.
+ *
+ * @param l L component value [0...100)
+ * @param a A component value [-128...127)
+ * @param b B component value [-128...127)
+ * @return int containing the RGB representation
+ */
+ @ColorInt
+ public static int LABToColor(@FloatRange(from = 0f, to = 100) final double l,
+ @FloatRange(from = -128, to = 127) final double a,
+ @FloatRange(from = -128, to = 127) final double b) {
+ final double[] result = new double[3];
+ LABToXYZ(l, a, b, result);
+ return XYZToColor(result[0], result[1], result[2]);
+ }
+
+ /**
+ * Returns the euclidean distance between two LAB colors.
+ */
+ public static double distanceEuclidean(@NonNull double[] labX, @NonNull double[] labY) {
+ return Math.sqrt(Math.pow(labX[0] - labY[0], 2)
+ + Math.pow(labX[1] - labY[1], 2)
+ + Math.pow(labX[2] - labY[2], 2));
+ }
+
private static float constrain(float amount, float low, float high) {
return amount < low ? low : (amount > high ? high : amount);
}
@@ -308,4 +526,82 @@
return amount < low ? low : (amount > high ? high : amount);
}
+ private static double pivotXyzComponent(double component) {
+ return component > XYZ_EPSILON
+ ? Math.pow(component, 1 / 3.0)
+ : (XYZ_KAPPA * component + 16) / 116;
+ }
+
+ /**
+ * Blend between two ARGB colors using the given ratio.
+ *
+ * @param ratio of which to blend. 0.0 will return {@code color1}, 0.5 will give an even blend,
+ * 1.0 will return {@code color2}.
+ */
+ @ColorInt
+ public static int blendARGB(@ColorInt int color1, @ColorInt int color2,
+ @FloatRange(from = 0.0, to = 1.0) float ratio) {
+ final float inverseRatio = 1 - ratio;
+ float a = Color.alpha(color1) * inverseRatio + Color.alpha(color2) * ratio;
+ float r = Color.red(color1) * inverseRatio + Color.red(color2) * ratio;
+ float g = Color.green(color1) * inverseRatio + Color.green(color2) * ratio;
+ float b = Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio;
+ return Color.argb((int) a, (int) r, (int) g, (int) b);
+ }
+
+ /**
+ * Blend between {@code hsl1} and {@code hsl2} using the given ratio. This will interpolate
+ * the hue using the shortest angle.
+ *
+ * @param hsl1 3 element array which holds the first HSL color.
+ * @param hsl2 3 element array which holds the second HSL color.
+ * @param result 3 element array which holds the resulting HSL components.
+ * @param ratio of which to blend. 0.0 will result in {@code hsl1},
+ * 0.5 will give an even blend, 1.0 will return {@code hsl2}.
+ */
+ public static void blendHSL(@NonNull float[] hsl1, @NonNull float[] hsl2,
+ @NonNull float[] result, @FloatRange(from = 0.0, to = 1.0) float ratio) {
+ if (result.length != 3) {
+ throw new IllegalArgumentException("result must have a length of 3.");
+ }
+ final float inverseRatio = 1 - ratio;
+ // Since hue is circular we will need to interpolate carefully
+ result[0] = circularInterpolate(hsl1[0], hsl2[0], ratio);
+ result[1] = hsl1[1] * inverseRatio + hsl2[1] * ratio;
+ result[2] = hsl1[2] * inverseRatio + hsl2[2] * ratio;
+ }
+
+ /**
+ * Blend between two CIE-LAB colors using the given ratio.
+ *
+ * @param lab1 3 element array which holds the first LAB color.
+ * @param lab2 3 element array which holds the second LAB color.
+ * @param result 3 element array which holds the resulting LAB components.
+ * @param ratio of which to blend. 0.0 will result in {@code lab1}, 0.5 will give an even blend,
+ * 1.0 will return {@code lab2}.
+ */
+ public static void blendLAB(@NonNull double[] lab1,
+ @NonNull double[] lab2, @NonNull double[] result,
+ @FloatRange(from = 0.0, to = 1.0) double ratio) {
+ if (result.length != 3) {
+ throw new IllegalArgumentException("result must have a length of 3.");
+ }
+ final double inverseRatio = 1 - ratio;
+ result[0] = lab1[0] * inverseRatio + lab2[0] * ratio;
+ result[1] = lab1[1] * inverseRatio + lab2[1] * ratio;
+ result[2] = lab1[2] * inverseRatio + lab2[2] * ratio;
+ }
+
+ @VisibleForTesting
+ static float circularInterpolate(float a, float b, float f) {
+ if (Math.abs(b - a) > 180) {
+ if (b > a) {
+ a += 360;
+ } else {
+ b += 360;
+ }
+ }
+ return (a + ((b - a) * f)) % 360;
+ }
+
}
diff --git a/v4/java/android/support/v4/view/ViewPager.java b/v4/java/android/support/v4/view/ViewPager.java
index c6f3648..11acb79 100644
--- a/v4/java/android/support/v4/view/ViewPager.java
+++ b/v4/java/android/support/v4/view/ViewPager.java
@@ -2420,20 +2420,22 @@
throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
}
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
- velocityTracker, mActivePointerId);
- mPopulatePending = true;
- final int width = getClientWidth();
- final int scrollX = getScrollX();
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int currentPage = ii.position;
- final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
- final int totalDelta = (int) (mLastMotionX - mInitialMotionX);
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
- setCurrentItemInternal(nextPage, true, true, initialVelocity);
+ if (mAdapter != null) {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
+ velocityTracker, mActivePointerId);
+ mPopulatePending = true;
+ final int width = getClientWidth();
+ final int scrollX = getScrollX();
+ final ItemInfo ii = infoForCurrentScrollPosition();
+ final int currentPage = ii.position;
+ final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
+ final int totalDelta = (int) (mLastMotionX - mInitialMotionX);
+ int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
+ totalDelta);
+ setCurrentItemInternal(nextPage, true, true, initialVelocity);
+ }
endDrag();
mFakeDragging = false;
@@ -2451,6 +2453,10 @@
throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
}
+ if (mAdapter == null) {
+ return;
+ }
+
mLastMotionX += xOffset;
float oldScrollX = getScrollX();
diff --git a/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java b/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java
index 56cb6fb..6296903 100644
--- a/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java
+++ b/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java
@@ -32,6 +32,8 @@
private static final float ALLOWED_OFFSET_SATURATION = 0.005f;
private static final float ALLOWED_OFFSET_LIGHTNESS = 0.005f;
private static final float ALLOWED_OFFSET_MIN_ALPHA = 0.01f;
+ private static final double ALLOWED_OFFSET_LAB = 0.01;
+ private static final double ALLOWED_OFFSET_XYZ = 0.01;
private static final int ALLOWED_OFFSET_RGB_COMPONENT = 2;
@@ -39,38 +41,55 @@
static {
sEntryList.add(new TestEntry(Color.BLACK).setHsl(0f, 0f, 0f)
+ .setLab(0, 0, 0).setXyz(0, 0, 0)
.setWhiteMinAlpha30(0.35f).setWhiteMinAlpha45(0.46f));
+
sEntryList.add(new TestEntry(Color.WHITE).setHsl(0f, 0f, 1f)
+ .setLab(100, 0.005, -0.01).setXyz(95.05, 100, 108.9)
.setBlackMinAlpha30(0.42f).setBlackMinAlpha45(0.54f));
+
sEntryList.add(new TestEntry(Color.BLUE).setHsl(240f, 1f, 0.5f)
+ .setLab(32.303, 79.197, -107.864).setXyz(18.05, 7.22, 95.05)
.setWhiteMinAlpha30(0.55f).setWhiteMinAlpha45(0.71f));
+
sEntryList.add(new TestEntry(Color.GREEN).setHsl(120f, 1f, 0.5f)
+ .setLab(87.737, -86.185, 83.181).setXyz(35.76, 71.520, 11.920)
.setBlackMinAlpha30(0.43f).setBlackMinAlpha45(0.55f));
+
sEntryList.add(new TestEntry(Color.RED).setHsl(0f, 1f, 0.5f)
+ .setLab(53.233, 80.109, 67.22).setXyz(41.24, 21.26, 1.93)
.setWhiteMinAlpha30(0.84f).setBlackMinAlpha30(0.55f).setBlackMinAlpha45(0.78f));
+
sEntryList.add(new TestEntry(Color.CYAN).setHsl(180f, 1f, 0.5f)
+ .setLab(91.117, -48.08, -14.138).setXyz(53.81, 78.74, 106.97)
.setBlackMinAlpha30(0.43f).setBlackMinAlpha45(0.55f));
+
sEntryList.add(new TestEntry(0xFF2196F3).setHsl(207f, 0.9f, 0.54f)
+ .setLab(60.433, 2.091, -55.116).setXyz(27.711, 28.607, 88.855)
.setBlackMinAlpha30(0.52f).setWhiteMinAlpha30(0.97f).setBlackMinAlpha45(0.7f));
+
sEntryList.add(new TestEntry(0xFFD1C4E9).setHsl(261f, 0.46f, 0.84f)
+ .setLab(81.247, 11.513, -16.677).setXyz(60.742, 58.918, 85.262)
.setBlackMinAlpha30(0.45f).setBlackMinAlpha45(0.58f));
+
sEntryList.add(new TestEntry(0xFF311B92).setHsl(251.09f, 0.687f, 0.339f)
+ .setLab(21.988, 44.301, -60.942).setXyz(6.847, 3.512, 27.511)
.setWhiteMinAlpha30(0.39f).setWhiteMinAlpha45(0.54f));
}
- public void testToHSL() {
+ public void testColorToHSL() {
for (TestEntry entry : sEntryList) {
testColorToHSL(entry.rgb, entry.hsl);
}
}
- public void testFromHSL() {
+ public void testHSLToColor() {
for (TestEntry entry : sEntryList) {
testHSLToColor(entry.hsl, entry.rgb);
}
}
- public void testToHslLimits() {
+ public void testColorToHslLimits() {
final float[] hsl = new float[3];
for (TestEntry entry : sEntryList) {
@@ -82,6 +101,36 @@
}
}
+ public void testColorToXYZ() {
+ for (TestEntry entry : sEntryList) {
+ testColorToXYZ(entry.rgb, entry.xyz);
+ }
+ }
+
+ public void testColorToLAB() {
+ for (TestEntry entry : sEntryList) {
+ testColorToLAB(entry.rgb, entry.lab);
+ }
+ }
+
+ public void testLABToXYZ() {
+ for (TestEntry entry : sEntryList) {
+ testLABToXYZ(entry.lab, entry.xyz);
+ }
+ }
+
+ public void testXYZToColor() {
+ for (TestEntry entry : sEntryList) {
+ testXYZToColor(entry.xyz, entry.rgb);
+ }
+ }
+
+ public void testLABToColor() {
+ for (TestEntry entry : sEntryList) {
+ testLABToColor(entry.lab, entry.rgb);
+ }
+ }
+
public void testMinAlphas() {
for (TestEntry entry : sEntryList) {
testMinAlpha("Black title", entry.rgb, entry.blackMinAlpha30,
@@ -95,57 +144,107 @@
}
}
+ public void testCircularInterpolationForwards() {
+ assertEquals(0f, ColorUtils.circularInterpolate(0, 180, 0f));
+ assertEquals(90f, ColorUtils.circularInterpolate(0, 180, 0.5f));
+ assertEquals(180f, ColorUtils.circularInterpolate(0, 180, 1f));
+ }
+
+ public void testCircularInterpolationBackwards() {
+ assertEquals(180f, ColorUtils.circularInterpolate(180, 0, 0f));
+ assertEquals(90f, ColorUtils.circularInterpolate(180, 0, 0.5f));
+ assertEquals(0f, ColorUtils.circularInterpolate(180, 0, 1f));
+ }
+
+ public void testCircularInterpolationCrossZero() {
+ assertEquals(270f, ColorUtils.circularInterpolate(270, 90, 0f));
+ assertEquals(180f, ColorUtils.circularInterpolate(270, 90, 0.5f));
+ assertEquals(90f, ColorUtils.circularInterpolate(270, 90, 1f));
+ }
+
private static void testMinAlpha(String title, int color, float expected, int actual) {
final String message = title + " text within error for #" + Integer.toHexString(color);
if (expected < 0) {
assertEquals(message, actual, -1);
} else {
- assertClose(message, expected, actual / 255f, ALLOWED_OFFSET_MIN_ALPHA);
+ assertEquals(message, expected, actual / 255f, ALLOWED_OFFSET_MIN_ALPHA);
}
}
- private static void assertClose(String message, float expected, float actual,
- float allowedOffset) {
- StringBuilder sb = new StringBuilder(message);
- sb.append(". Expected: ").append(expected).append(". Actual: ").append(actual);
-
- assertTrue(sb.toString(), Math.abs(expected - actual) <= allowedOffset);
- }
-
- private static void assertClose(String message, int expected, int actual,
- int allowedOffset) {
- StringBuilder sb = new StringBuilder(message);
- sb.append(". Expected: ").append(expected).append(". Actual: ").append(actual);
-
- assertTrue(sb.toString(), Math.abs(expected - actual) <= allowedOffset);
- }
-
- private static void testColorToHSL(int color, float[] expectedHsl) {
+ private static void testColorToHSL(int color, float[] expected) {
float[] actualHSL = new float[3];
ColorUtils.colorToHSL(color, actualHSL);
- assertClose("Hue not within offset", expectedHsl[0], actualHSL[0],
+ assertEquals("Hue not within offset", expected[0], actualHSL[0],
ALLOWED_OFFSET_HUE);
- assertClose("Saturation not within offset", expectedHsl[1], actualHSL[1],
+ assertEquals("Saturation not within offset", expected[1], actualHSL[1],
ALLOWED_OFFSET_SATURATION);
- assertClose("Lightness not within offset", expectedHsl[2], actualHSL[2],
+ assertEquals("Lightness not within offset", expected[2], actualHSL[2],
ALLOWED_OFFSET_LIGHTNESS);
}
- private static void testHSLToColor(float[] hsl, int expectedRgb) {
+ private static void testHSLToColor(float[] hsl, int expected) {
final int actualRgb = ColorUtils.HSLToColor(hsl);
- assertClose("Red not within offset",
- Color.red(expectedRgb), Color.red(actualRgb), ALLOWED_OFFSET_RGB_COMPONENT);
- assertClose("Green not within offset",
- Color.green(expectedRgb), Color.green(actualRgb), ALLOWED_OFFSET_RGB_COMPONENT);
- assertClose("Blue not within offset",
- Color.blue(expectedRgb), Color.blue(actualRgb), ALLOWED_OFFSET_RGB_COMPONENT);
+ assertEquals("Red not within offset", Color.red(expected), Color.red(actualRgb),
+ ALLOWED_OFFSET_RGB_COMPONENT);
+ assertEquals("Green not within offset", Color.green(expected), Color.green(actualRgb),
+ ALLOWED_OFFSET_RGB_COMPONENT);
+ assertEquals("Blue not within offset", Color.blue(expected), Color.blue(actualRgb),
+ ALLOWED_OFFSET_RGB_COMPONENT);
+ }
+
+ private static void testColorToLAB(int color, double[] expected) {
+ double[] result = new double[3];
+ ColorUtils.colorToLAB(color, result);
+
+ assertEquals("L not within offset", expected[0], result[0], ALLOWED_OFFSET_LAB);
+ assertEquals("A not within offset", expected[1], result[1], ALLOWED_OFFSET_LAB);
+ assertEquals("B not within offset", expected[2], result[2], ALLOWED_OFFSET_LAB);
+ }
+
+ private static void testColorToXYZ(int color, double[] expected) {
+ double[] result = new double[3];
+ ColorUtils.colorToXYZ(color, result);
+
+ assertEquals("X not within offset", expected[0], result[0], ALLOWED_OFFSET_XYZ);
+ assertEquals("Y not within offset", expected[1], result[1], ALLOWED_OFFSET_XYZ);
+ assertEquals("Z not within offset", expected[2], result[2], ALLOWED_OFFSET_XYZ);
+ }
+
+ private static void testLABToXYZ(double[] lab, double[] expected) {
+ double[] result = new double[3];
+ ColorUtils.LABToXYZ(lab[0], lab[1], lab[2], result);
+
+ assertEquals("X not within offset", expected[0], result[0], ALLOWED_OFFSET_XYZ);
+ assertEquals("Y not within offset", expected[1], result[1], ALLOWED_OFFSET_XYZ);
+ assertEquals("Z not within offset", expected[2], result[2], ALLOWED_OFFSET_XYZ);
+ }
+
+ private static void testXYZToColor(double[] xyz, int expected) {
+ final int result = ColorUtils.XYZToColor(xyz[0], xyz[1], xyz[2]);
+ assertRGBComponentsClose(expected, result);
+ }
+
+ private static void testLABToColor(double[] lab, int expected) {
+ final int result = ColorUtils.LABToColor(lab[0], lab[1], lab[2]);
+ assertRGBComponentsClose(expected, result);
+ }
+
+ private static void assertRGBComponentsClose(int expected, int actual) {
+ final String message = "Expected: #" + Integer.toHexString(expected)
+ + ", Actual: #" + Integer.toHexString(actual);
+ assertEquals("R not equal: " + message, Color.red(expected), Color.red(actual), 1);
+ assertEquals("G not equal: " + message, Color.green(expected), Color.green(actual), 1);
+ assertEquals("B not equal: " + message, Color.blue(expected), Color.blue(actual), 1);
}
private static class TestEntry {
final int rgb;
final float[] hsl = new float[3];
+ final double[] xyz = new double[3];
+ final double[] lab = new double[3];
+
float blackMinAlpha45 = -1;
float blackMinAlpha30 = -1;
float whiteMinAlpha45 = -1;
@@ -162,6 +261,20 @@
return this;
}
+ TestEntry setXyz(double x, double y, double z) {
+ xyz[0] = x;
+ xyz[1] = y;
+ xyz[2] = z;
+ return this;
+ }
+
+ TestEntry setLab(double l, double a, double b) {
+ lab[0] = l;
+ lab[1] = a;
+ lab[2] = b;
+ return this;
+ }
+
TestEntry setBlackMinAlpha30(float minAlpha) {
blackMinAlpha30 = minAlpha;
return this;
diff --git a/v7/appcompat/res/values/styles_base.xml b/v7/appcompat/res/values/styles_base.xml
index f952530..dad7570 100644
--- a/v7/appcompat/res/values/styles_base.xml
+++ b/v7/appcompat/res/values/styles_base.xml
@@ -301,7 +301,7 @@
<item name="subtitleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Subtitle</item>
<item name="android:minHeight">?attr/actionBarSize</item>
<item name="titleMargins">4dp</item>
- <item name="maxButtonHeight">56dp</item>
+ <item name="maxButtonHeight">@dimen/abc_action_bar_default_height_material</item>
<item name="collapseIcon">?attr/homeAsUpIndicator</item>
<item name="collapseContentDescription">@string/abc_toolbar_collapse_description</item>
<item name="contentInsetStart">16dp</item>