mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Kotlinify ColorPropConverter (#50626)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50626 As per title. Changelog: [Android][Breaking] Kotlinify ColorPropConverter Reviewed By: javache Differential Revision: D72788490 fbshipit-source-id: 1c81c70566437db36e45e59995093c4c808369d8
This commit is contained in:
committed by
Facebook GitHub Bot
parent
398f2068d1
commit
57768bfbcd
@@ -662,12 +662,12 @@ public class com/facebook/react/bridge/CatalystInstanceImpl$PendingJSCall {
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public class com/facebook/react/bridge/ColorPropConverter {
|
||||
public fun <init> ()V
|
||||
public static fun getColor (Ljava/lang/Object;Landroid/content/Context;)Ljava/lang/Integer;
|
||||
public static fun getColor (Ljava/lang/Object;Landroid/content/Context;I)Ljava/lang/Integer;
|
||||
public static fun getColorInstance (Ljava/lang/Object;Landroid/content/Context;)Landroid/graphics/Color;
|
||||
public static fun resolveResourcePath (Landroid/content/Context;Ljava/lang/String;)Ljava/lang/Integer;
|
||||
public final class com/facebook/react/bridge/ColorPropConverter {
|
||||
public static final field INSTANCE Lcom/facebook/react/bridge/ColorPropConverter;
|
||||
public static final fun getColor (Ljava/lang/Object;Landroid/content/Context;)Ljava/lang/Integer;
|
||||
public static final fun getColor (Ljava/lang/Object;Landroid/content/Context;I)I
|
||||
public static final fun getColorInstance (Ljava/lang/Object;Landroid/content/Context;)Landroid/graphics/Color;
|
||||
public static final fun resolveResourcePath (Landroid/content/Context;Ljava/lang/String;)Ljava/lang/Integer;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/bridge/CxxCallbackImpl : com/facebook/jni/HybridClassBase, com/facebook/react/bridge/Callback {
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ internal class ColorAnimatedNode(
|
||||
return
|
||||
}
|
||||
val context = context ?: return
|
||||
val color = ColorPropConverter.getColor(nativeColor, context)
|
||||
val color = ColorPropConverter.getColor(nativeColor, context) ?: return
|
||||
val rNode = nativeAnimatedNodesManager.getNodeById(rNodeId) as ValueAnimatedNode?
|
||||
val gNode = nativeAnimatedNodesManager.getNodeById(gNodeId) as ValueAnimatedNode?
|
||||
val bNode = nativeAnimatedNodesManager.getNodeById(bNodeId) as ValueAnimatedNode?
|
||||
|
||||
-234
@@ -1,234 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorSpace;
|
||||
import android.os.Build;
|
||||
import android.util.TypedValue;
|
||||
import androidx.annotation.ColorLong;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
|
||||
public class ColorPropConverter {
|
||||
|
||||
private static boolean supportWideGamut() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
}
|
||||
|
||||
private static final String JSON_KEY = "resource_paths";
|
||||
private static final String PREFIX_RESOURCE = "@";
|
||||
private static final String PREFIX_ATTR = "?";
|
||||
private static final String PACKAGE_DELIMITER = ":";
|
||||
private static final String PATH_DELIMITER = "/";
|
||||
private static final String ATTR = "attr";
|
||||
private static final String ATTR_SEGMENT = "attr/";
|
||||
|
||||
@Nullable
|
||||
private static Integer getColorInteger(@Nullable Object value, Context context) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value instanceof Double) {
|
||||
return ((Double) value).intValue();
|
||||
}
|
||||
|
||||
if (context == null) {
|
||||
throw new RuntimeException("Context may not be null.");
|
||||
}
|
||||
|
||||
if (value instanceof ReadableMap) {
|
||||
ReadableMap map = (ReadableMap) value;
|
||||
|
||||
if (map.hasKey("space")) {
|
||||
int r = (int) ((float) map.getDouble("r") * 255);
|
||||
int g = (int) ((float) map.getDouble("g") * 255);
|
||||
int b = (int) ((float) map.getDouble("b") * 255);
|
||||
int a = (int) ((float) map.getDouble("a") * 255);
|
||||
|
||||
return Color.argb(a, r, g, b);
|
||||
}
|
||||
|
||||
ReadableArray resourcePaths = map.getArray(JSON_KEY);
|
||||
|
||||
if (resourcePaths == null) {
|
||||
throw new JSApplicationCausedNativeException(
|
||||
"ColorValue: The `" + JSON_KEY + "` must be an array of color resource path strings.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < resourcePaths.size(); i++) {
|
||||
Integer result = resolveResourcePath(context, resourcePaths.getString(i));
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new JSApplicationCausedNativeException(
|
||||
"ColorValue: None of the paths in the `"
|
||||
+ JSON_KEY
|
||||
+ "` array resolved to a color resource.");
|
||||
}
|
||||
|
||||
throw new JSApplicationCausedNativeException(
|
||||
"ColorValue: the value must be a number or Object.");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Color getColorInstance(@Nullable Object value, Context context) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (supportWideGamut() && value instanceof Double) {
|
||||
return Color.valueOf(((Double) value).intValue());
|
||||
}
|
||||
|
||||
if (context == null) {
|
||||
throw new RuntimeException("Context may not be null.");
|
||||
}
|
||||
|
||||
if (value instanceof ReadableMap) {
|
||||
ReadableMap map = (ReadableMap) value;
|
||||
|
||||
if (supportWideGamut() && map.hasKey("space")) {
|
||||
String rawColorSpace = map.getString("space");
|
||||
boolean isDisplayP3 = rawColorSpace != null && rawColorSpace.equals("display-p3");
|
||||
ColorSpace space =
|
||||
ColorSpace.get(isDisplayP3 ? ColorSpace.Named.DISPLAY_P3 : ColorSpace.Named.SRGB);
|
||||
float r = (float) map.getDouble("r");
|
||||
float g = (float) map.getDouble("g");
|
||||
float b = (float) map.getDouble("b");
|
||||
float a = (float) map.getDouble("a");
|
||||
|
||||
@ColorLong long color = Color.pack(r, g, b, a, space);
|
||||
return Color.valueOf(color);
|
||||
}
|
||||
|
||||
ReadableArray resourcePaths = map.getArray(JSON_KEY);
|
||||
if (resourcePaths == null) {
|
||||
throw new JSApplicationCausedNativeException(
|
||||
"ColorValue: The `" + JSON_KEY + "` must be an array of color resource path strings.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < resourcePaths.size(); i++) {
|
||||
Integer result = resolveResourcePath(context, resourcePaths.getString(i));
|
||||
if (supportWideGamut() && result != null) {
|
||||
return Color.valueOf(result);
|
||||
}
|
||||
}
|
||||
|
||||
throw new JSApplicationCausedNativeException(
|
||||
"ColorValue: None of the paths in the `"
|
||||
+ JSON_KEY
|
||||
+ "` array resolved to a color resource.");
|
||||
}
|
||||
throw new JSApplicationCausedNativeException(
|
||||
"ColorValue: the value must be a number or Object.");
|
||||
}
|
||||
|
||||
public static Integer getColor(@Nullable Object value, Context context) {
|
||||
try {
|
||||
if (supportWideGamut()) {
|
||||
Color color = getColorInstance(value, context);
|
||||
if (color != null) {
|
||||
return color.toArgb();
|
||||
}
|
||||
}
|
||||
} catch (JSApplicationCausedNativeException ex) {
|
||||
FLog.w(ReactConstants.TAG, ex, "Error extracting color from WideGamut");
|
||||
}
|
||||
return getColorInteger(value, context);
|
||||
}
|
||||
|
||||
public static Integer getColor(@Nullable Object value, Context context, int defaultInt) {
|
||||
try {
|
||||
return getColor(value, context);
|
||||
} catch (JSApplicationCausedNativeException e) {
|
||||
FLog.w(ReactConstants.TAG, e, "Error converting ColorValue");
|
||||
return defaultInt;
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer resolveResourcePath(Context context, @Nullable String resourcePath) {
|
||||
if (resourcePath == null || resourcePath.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean isResource = resourcePath.startsWith(PREFIX_RESOURCE);
|
||||
boolean isThemeAttribute = resourcePath.startsWith(PREFIX_ATTR);
|
||||
|
||||
resourcePath = resourcePath.substring(1);
|
||||
|
||||
try {
|
||||
if (isResource) {
|
||||
return resolveResource(context, resourcePath);
|
||||
} else if (isThemeAttribute) {
|
||||
return resolveThemeAttribute(context, resourcePath);
|
||||
}
|
||||
} catch (Resources.NotFoundException exception) {
|
||||
// The resource could not be found so do nothing to allow the for loop to continue and
|
||||
// try the next fallback resource in the array. If none of the fallbacks are
|
||||
// found then the exception immediately after the for loop will be thrown.
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int resolveResource(Context context, String resourcePath) {
|
||||
String[] pathTokens = resourcePath.split(PACKAGE_DELIMITER);
|
||||
|
||||
String packageName = context.getPackageName();
|
||||
String resource = resourcePath;
|
||||
|
||||
if (pathTokens.length > 1) {
|
||||
packageName = pathTokens[0];
|
||||
resource = pathTokens[1];
|
||||
}
|
||||
|
||||
String[] resourceTokens = resource.split(PATH_DELIMITER);
|
||||
String resourceType = resourceTokens[0];
|
||||
String resourceName = resourceTokens[1];
|
||||
|
||||
int resourceId = context.getResources().getIdentifier(resourceName, resourceType, packageName);
|
||||
|
||||
return ResourcesCompat.getColor(context.getResources(), resourceId, context.getTheme());
|
||||
}
|
||||
|
||||
private static int resolveThemeAttribute(Context context, String resourcePath) {
|
||||
String path = resourcePath.replaceAll(ATTR_SEGMENT, "");
|
||||
String[] pathTokens = path.split(PACKAGE_DELIMITER);
|
||||
|
||||
String packageName = context.getPackageName();
|
||||
String resourceName = path;
|
||||
|
||||
if (pathTokens.length > 1) {
|
||||
packageName = pathTokens[0];
|
||||
resourceName = pathTokens[1];
|
||||
}
|
||||
|
||||
int resourceId = context.getResources().getIdentifier(resourceName, ATTR, packageName);
|
||||
|
||||
// If resourceId is 0, try resolving with the android package name
|
||||
if (resourceId == 0) {
|
||||
resourceId = context.getResources().getIdentifier(resourceName, ATTR, "android");
|
||||
}
|
||||
|
||||
TypedValue outValue = new TypedValue();
|
||||
Resources.Theme theme = context.getTheme();
|
||||
|
||||
if (theme.resolveAttribute(resourceId, outValue, true)) {
|
||||
return outValue.data;
|
||||
}
|
||||
|
||||
throw new Resources.NotFoundException();
|
||||
}
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorSpace
|
||||
import android.os.Build
|
||||
import android.util.TypedValue
|
||||
import androidx.annotation.ColorLong
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import com.facebook.common.logging.FLog
|
||||
import com.facebook.react.common.ReactConstants
|
||||
|
||||
public object ColorPropConverter {
|
||||
|
||||
private fun supportWideGamut(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
|
||||
private const val JSON_KEY = "resource_paths"
|
||||
private const val PREFIX_RESOURCE = "@"
|
||||
private const val PREFIX_ATTR = "?"
|
||||
private const val PACKAGE_DELIMITER = ":"
|
||||
private const val PATH_DELIMITER = "/"
|
||||
private const val ATTR = "attr"
|
||||
private const val ATTR_SEGMENT = "attr/"
|
||||
|
||||
private fun getColorInteger(value: Any?, context: Context): Int? {
|
||||
if (value == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (value is Double) {
|
||||
return value.toInt()
|
||||
}
|
||||
|
||||
checkNotNull(context)
|
||||
|
||||
if (value is ReadableMap) {
|
||||
if (value.hasKey("space")) {
|
||||
val r = (value.getDouble("r").toFloat() * 255).toInt()
|
||||
val g = (value.getDouble("g").toFloat() * 255).toInt()
|
||||
val b = (value.getDouble("b").toFloat() * 255).toInt()
|
||||
val a = (value.getDouble("a").toFloat() * 255).toInt()
|
||||
|
||||
return Color.argb(a, r, g, b)
|
||||
}
|
||||
|
||||
val resourcePaths =
|
||||
value.getArray(JSON_KEY)
|
||||
?: throw JSApplicationCausedNativeException(
|
||||
"ColorValue: The `$JSON_KEY` must be an array of color resource path strings.")
|
||||
|
||||
for (i in 0 until resourcePaths.size()) {
|
||||
val result = resolveResourcePath(context, resourcePaths.getString(i))
|
||||
if (result != null) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
throw JSApplicationCausedNativeException(
|
||||
"ColorValue: None of the paths in the `$JSON_KEY` array resolved to a color resource.")
|
||||
}
|
||||
|
||||
throw JSApplicationCausedNativeException("ColorValue: the value must be a number or Object.")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun getColorInstance(value: Any?, context: Context): Color? {
|
||||
if (value == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (supportWideGamut() && value is Double) {
|
||||
return Color.valueOf(value.toInt())
|
||||
}
|
||||
|
||||
checkNotNull(context)
|
||||
|
||||
if (value is ReadableMap) {
|
||||
if (supportWideGamut() && value.hasKey("space")) {
|
||||
val rawColorSpace = value.getString("space")
|
||||
val isDisplayP3 = rawColorSpace == "display-p3"
|
||||
val space =
|
||||
ColorSpace.get(if (isDisplayP3) ColorSpace.Named.DISPLAY_P3 else ColorSpace.Named.SRGB)
|
||||
|
||||
val r = value.getDouble("r").toFloat()
|
||||
val g = value.getDouble("g").toFloat()
|
||||
val b = value.getDouble("b").toFloat()
|
||||
val a = value.getDouble("a").toFloat()
|
||||
|
||||
@ColorLong val color = Color.pack(r, g, b, a, space)
|
||||
return Color.valueOf(color)
|
||||
}
|
||||
|
||||
val resourcePaths =
|
||||
value.getArray(JSON_KEY)
|
||||
?: throw JSApplicationCausedNativeException(
|
||||
"ColorValue: The `$JSON_KEY` must be an array of color resource path strings.")
|
||||
|
||||
for (i in 0 until resourcePaths.size()) {
|
||||
val result = resolveResourcePath(context, resourcePaths.getString(i))
|
||||
if (supportWideGamut() && result != null) {
|
||||
return Color.valueOf(result)
|
||||
}
|
||||
}
|
||||
|
||||
throw JSApplicationCausedNativeException(
|
||||
"ColorValue: None of the paths in the `$JSON_KEY` array resolved to a color resource.")
|
||||
}
|
||||
|
||||
throw JSApplicationCausedNativeException("ColorValue: the value must be a number or Object.")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun getColor(value: Any?, context: Context): Int? {
|
||||
try {
|
||||
if (supportWideGamut()) {
|
||||
val color = getColorInstance(value, context)
|
||||
if (color != null) {
|
||||
return color.toArgb()
|
||||
}
|
||||
}
|
||||
} catch (ex: JSApplicationCausedNativeException) {
|
||||
FLog.w(ReactConstants.TAG, ex, "Error extracting color from WideGamut")
|
||||
}
|
||||
|
||||
return getColorInteger(value, context)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun getColor(value: Any?, context: Context, defaultInt: Int): Int {
|
||||
return try {
|
||||
getColor(value, context) ?: defaultInt
|
||||
} catch (e: JSApplicationCausedNativeException) {
|
||||
FLog.w(ReactConstants.TAG, e, "Error converting ColorValue")
|
||||
defaultInt
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun resolveResourcePath(context: Context, resourcePath: String?): Int? {
|
||||
if (resourcePath.isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val isResource = resourcePath.startsWith(PREFIX_RESOURCE)
|
||||
val isThemeAttribute = resourcePath.startsWith(PREFIX_ATTR)
|
||||
|
||||
val path = resourcePath.substring(1)
|
||||
|
||||
return try {
|
||||
when {
|
||||
isResource -> resolveResource(context, path)
|
||||
isThemeAttribute -> resolveThemeAttribute(context, path)
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Resources.NotFoundException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveResource(context: Context, resourcePath: String): Int {
|
||||
val pathTokens = resourcePath.split(PACKAGE_DELIMITER)
|
||||
var packageName = context.packageName
|
||||
var resource = resourcePath
|
||||
|
||||
if (pathTokens.size > 1) {
|
||||
packageName = pathTokens[0]
|
||||
resource = pathTokens[1]
|
||||
}
|
||||
|
||||
val resourceTokens = resource.split(PATH_DELIMITER)
|
||||
val resourceType = resourceTokens[0]
|
||||
val resourceName = resourceTokens[1]
|
||||
|
||||
val resourceId = context.resources.getIdentifier(resourceName, resourceType, packageName)
|
||||
|
||||
return ResourcesCompat.getColor(context.resources, resourceId, context.theme)
|
||||
}
|
||||
|
||||
private fun resolveThemeAttribute(context: Context, resourcePath: String): Int {
|
||||
val path = resourcePath.replace(ATTR_SEGMENT, "")
|
||||
val pathTokens = path.split(PACKAGE_DELIMITER)
|
||||
|
||||
var packageName = context.packageName
|
||||
var resourceName = path
|
||||
|
||||
if (pathTokens.size > 1) {
|
||||
packageName = pathTokens[0]
|
||||
resourceName = pathTokens[1]
|
||||
}
|
||||
|
||||
var resourceId = context.resources.getIdentifier(resourceName, ATTR, packageName)
|
||||
|
||||
if (resourceId == 0) {
|
||||
resourceId = context.resources.getIdentifier(resourceName, ATTR, "android")
|
||||
}
|
||||
|
||||
val outValue = TypedValue()
|
||||
val theme = context.theme
|
||||
|
||||
if (theme.resolveAttribute(resourceId, outValue, true)) {
|
||||
return outValue.data
|
||||
}
|
||||
|
||||
throw Resources.NotFoundException()
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -46,8 +46,7 @@ public abstract class BaseViewManagerDelegate<
|
||||
mViewManager.setAccessibilityValue(view, value as ReadableMap?)
|
||||
|
||||
ViewProps.BACKGROUND_COLOR ->
|
||||
mViewManager.setBackgroundColor(
|
||||
view, if (value == null) 0 else ColorPropConverter.getColor(value, view.context))
|
||||
mViewManager.setBackgroundColor(view, ColorPropConverter.getColor(value, view.context, 0))
|
||||
|
||||
ViewProps.BORDER_RADIUS ->
|
||||
mViewManager.setBorderRadius(
|
||||
@@ -78,8 +77,7 @@ public abstract class BaseViewManagerDelegate<
|
||||
ViewProps.MIX_BLEND_MODE -> mViewManager.setMixBlendMode(view, value as String?)
|
||||
|
||||
ViewProps.SHADOW_COLOR ->
|
||||
mViewManager.setShadowColor(
|
||||
view, if (value == null) 0 else ColorPropConverter.getColor(value, view.context))
|
||||
mViewManager.setShadowColor(view, ColorPropConverter.getColor(value, view.context, 0))
|
||||
|
||||
ViewProps.IMPORTANT_FOR_ACCESSIBILITY ->
|
||||
mViewManager.setImportantForAccessibility(view, value as String?)
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ internal open class SwipeRefreshLayoutManager :
|
||||
val colorValues = IntArray(colors.size())
|
||||
for (i in 0..<colors.size()) {
|
||||
if (colors.getType(i) == ReadableType.Map) {
|
||||
colorValues[i] = ColorPropConverter.getColor(colors.getMap(i), view.context)
|
||||
colorValues[i] = ColorPropConverter.getColor(colors.getMap(i), view.context, 0)
|
||||
} else {
|
||||
colorValues[i] = colors.getInt(i)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user