Skip to content

Commit 207af45

Browse files
ryanlntnfacebook-github-bot
authored andcommitted
Update ColorPropConverterto support color function values (facebook#44237)
Summary: This adds support for color function values to ColorPropConverter per the wide gamut color [RFC](react-native-community/discussions-and-proposals#738). It updates the color conversion code so that it returns a Color instance before ultimately being converted to an Integer in preparation for returning long values as needed. ## Changelog: [ANDROID] [ADDED] - Update ColorPropConverter to support color function values Test Plan: Colors should work exactly the same as before. Follow test steps from facebook#42831 to test support for color() function syntax. While colors specified with color() function syntax will not yet render in DisplayP3 color space they will not be misrecognized as resource path colors but will instead fallback to their sRGB color space values. --- After the failure with the tests, I reapplied the changes and test some Jest e2e tests that were failing yesterday: {F1495277376} Differential Revision: D56517579 Pulled By: cipolleschi
1 parent 849da21 commit 207af45

File tree

2 files changed

+100
-12
lines changed

2 files changed

+100
-12
lines changed

packages/react-native/ReactAndroid/api/ReactAndroid.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ public class com/facebook/react/bridge/ColorPropConverter {
649649
public fun <init> ()V
650650
public static fun getColor (Ljava/lang/Object;Landroid/content/Context;)Ljava/lang/Integer;
651651
public static fun getColor (Ljava/lang/Object;Landroid/content/Context;I)Ljava/lang/Integer;
652+
public static fun getColorInstance (Ljava/lang/Object;Landroid/content/Context;)Landroid/graphics/Color;
652653
public static fun resolveResourcePath (Landroid/content/Context;Ljava/lang/String;)Ljava/lang/Integer;
653654
}
654655

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ColorPropConverter.java

Lines changed: 99 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,22 @@
99

1010
import android.content.Context;
1111
import android.content.res.Resources;
12+
import android.graphics.Color;
13+
import android.graphics.ColorSpace;
14+
import android.os.Build;
1215
import android.util.TypedValue;
16+
import androidx.annotation.ColorLong;
1317
import androidx.annotation.Nullable;
1418
import androidx.core.content.res.ResourcesCompat;
1519
import com.facebook.common.logging.FLog;
1620
import com.facebook.react.common.ReactConstants;
1721

1822
public class ColorPropConverter {
23+
24+
private static Boolean supportWideGamut() {
25+
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
26+
}
27+
1928
private static final String JSON_KEY = "resource_paths";
2029
private static final String PREFIX_RESOURCE = "@";
2130
private static final String PREFIX_ATTR = "?";
@@ -24,7 +33,7 @@ public class ColorPropConverter {
2433
private static final String ATTR = "attr";
2534
private static final String ATTR_SEGMENT = "attr/";
2635

27-
public static Integer getColor(Object value, Context context) {
36+
private static Integer getColorInteger(Object value, Context context) {
2837
if (value == null) {
2938
return null;
3039
}
@@ -33,18 +42,13 @@ public static Integer getColor(Object value, Context context) {
3342
return ((Double) value).intValue();
3443
}
3544

36-
if (context == null) {
37-
throw new RuntimeException("Context may not be null.");
38-
}
45+
throwIfNullContext(context);
3946

4047
if (value instanceof ReadableMap) {
4148
ReadableMap map = (ReadableMap) value;
4249
ReadableArray resourcePaths = map.getArray(JSON_KEY);
4350

44-
if (resourcePaths == null) {
45-
throw new JSApplicationCausedNativeException(
46-
"ColorValue: The `" + JSON_KEY + "` must be an array of color resource path strings.");
47-
}
51+
throwIfNullResourcePaths(resourcePaths);
4852

4953
for (int i = 0; i < resourcePaths.size(); i++) {
5054
Integer result = resolveResourcePath(context, resourcePaths.getString(i));
@@ -53,16 +57,62 @@ public static Integer getColor(Object value, Context context) {
5357
}
5458
}
5559

56-
throw new JSApplicationCausedNativeException(
57-
"ColorValue: None of the paths in the `"
58-
+ JSON_KEY
59-
+ "` array resolved to a color resource.");
60+
throwColorResourceNotFound();
61+
}
62+
63+
throw new JSApplicationCausedNativeException(
64+
"ColorValue: the value must be a number or Object.");
65+
}
66+
67+
public static Color getColorInstance(Object value, Context context) {
68+
if (value == null) {
69+
return null;
6070
}
6171

72+
if (supportWideGamut() && value instanceof Double) {
73+
return Color.valueOf(((Double) value).intValue());
74+
}
75+
76+
throwIfNullContext(context);
77+
78+
if (value instanceof ReadableMap) {
79+
ReadableMap map = (ReadableMap) value;
80+
81+
Color wideGamutColor = extractWideGamutColorIfPossible(map);
82+
if (wideGamutColor != null) {
83+
return wideGamutColor;
84+
}
85+
86+
ReadableArray resourcePaths = map.getArray(JSON_KEY);
87+
throwIfNullResourcePaths(resourcePaths);
88+
89+
for (int i = 0; i < resourcePaths.size(); i++) {
90+
Integer result = resolveResourcePath(context, resourcePaths.getString(i));
91+
if (supportWideGamut() && result != null) {
92+
return Color.valueOf(result);
93+
}
94+
}
95+
96+
throwColorResourceNotFound();
97+
}
6298
throw new JSApplicationCausedNativeException(
6399
"ColorValue: the value must be a number or Object.");
64100
}
65101

102+
public static Integer getColor(Object value, Context context) {
103+
try {
104+
if (supportWideGamut()) {
105+
Color color = getColorInstance(value, context);
106+
if (color != null) {
107+
return color.toArgb();
108+
}
109+
}
110+
} catch (JSApplicationCausedNativeException ex) {
111+
FLog.w(ReactConstants.TAG, ex, "Error extracting color from WideGamut");
112+
}
113+
return getColorInteger(value, context);
114+
}
115+
66116
public static Integer getColor(Object value, Context context, int defaultInt) {
67117
try {
68118
return getColor(value, context);
@@ -139,4 +189,41 @@ private static int resolveThemeAttribute(Context context, String resourcePath) {
139189

140190
throw new Resources.NotFoundException();
141191
}
192+
193+
private static void throwIfNullContext(Context context) {
194+
if (context == null) {
195+
throw new RuntimeException("Context may not be null.");
196+
}
197+
}
198+
199+
private static void throwIfNullResourcePaths(ReadableArray resourcePaths) {
200+
if (resourcePaths == null) {
201+
throw new JSApplicationCausedNativeException(
202+
"ColorValue: The `" + JSON_KEY + "` must be an array of color resource path strings.");
203+
}
204+
}
205+
206+
private static void throwColorResourceNotFound() {
207+
throw new JSApplicationCausedNativeException(
208+
"ColorValue: None of the paths in the `"
209+
+ JSON_KEY
210+
+ "` array resolved to a color resource.");
211+
}
212+
213+
private static Color extractWideGamutColorIfPossible(ReadableMap map) {
214+
if (supportWideGamut() && map.hasKey("space")) {
215+
String rawColorSpace = map.getString("space");
216+
boolean isDisplayP3 = rawColorSpace.equals("display-p3");
217+
ColorSpace space =
218+
ColorSpace.get(isDisplayP3 ? ColorSpace.Named.DISPLAY_P3 : ColorSpace.Named.SRGB);
219+
float r = (float) map.getDouble("r");
220+
float g = (float) map.getDouble("g");
221+
float b = (float) map.getDouble("b");
222+
float a = (float) map.getDouble("a");
223+
224+
@ColorLong long color = Color.pack(r, g, b, a, space);
225+
return Color.valueOf(color);
226+
}
227+
return null;
228+
}
142229
}

0 commit comments

Comments
 (0)