diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/DynamicFromObject.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/DynamicFromObject.java
new file mode 100644
index 00000000000..3f7f24bf02b
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/DynamicFromObject.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) Facebook, Inc. and its 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 com.facebook.common.logging.FLog;
+import com.facebook.react.common.ReactConstants;
+import javax.annotation.Nullable;
+
+/**
+ * Implementation of Dynamic wrapping a ReadableArray.
+ */
+public class DynamicFromObject implements Dynamic {
+ private @Nullable Object mObject;
+
+ public DynamicFromObject(@Nullable Object obj) {
+ mObject = obj;
+ }
+
+ @Override
+ public void recycle() {
+ // Noop - nothing to recycle since there is no pooling
+ }
+
+ @Override
+ public boolean isNull() {
+ return mObject == null;
+ }
+
+ @Override
+ public boolean asBoolean() {
+ return (boolean)mObject;
+ }
+
+ @Override
+ public double asDouble() {
+ return (double)mObject;
+ }
+
+ @Override
+ public int asInt() {
+ // Numbers from JS are always Doubles
+ return ((Double)mObject).intValue();
+ }
+
+ @Override
+ public String asString() {
+ return (String)mObject;
+ }
+
+ @Override
+ public ReadableArray asArray() {
+ return (ReadableArray)mObject;
+ }
+
+ @Override
+ public ReadableMap asMap() {
+ return (ReadableMap)mObject;
+ }
+
+ @Override
+ public ReadableType getType() {
+ if (isNull()) {
+ return ReadableType.Null;
+ }
+ if (mObject instanceof Boolean) {
+ return ReadableType.Boolean;
+ }
+ if (mObject instanceof Number) {
+ return ReadableType.Number;
+ }
+ if (mObject instanceof String) {
+ return ReadableType.String;
+ }
+ if (mObject instanceof ReadableMap) {
+ return ReadableType.Map;
+ }
+ if (mObject instanceof ReadableArray) {
+ return ReadableType.Array;
+ }
+ FLog.e(ReactConstants.TAG, "Unmapped object type " + mObject.getClass().getName());
+ return ReadableType.Null;
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java
index f0fd43edd01..119890a2088 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java
@@ -1,10 +1,9 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
+ *
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 java.util.HashMap;
@@ -20,9 +19,9 @@ import javax.annotation.Nullable;
* of {@link WritableNativeMap} created via {@link Arguments#createMap} or just {@link ReadableMap}
* interface if you want your "native" module method to take a map from JS as an argument.
*
- * Main purpose for this class is to be used in java-only unit tests, but could also be used outside
- * of tests in the code that operates only in java and needs to communicate with RN modules via
- * their JS-exposed API.
+ *
Main purpose for this class is to be used in java-only unit tests, but could also be used
+ * outside of tests in the code that operates only in java and needs to communicate with RN modules
+ * via their JS-exposed API.
*/
public class JavaOnlyMap implements ReadableMap, WritableMap {
@@ -62,16 +61,19 @@ public class JavaOnlyMap implements ReadableMap, WritableMap {
return res;
}
- /**
- * @param keysAndValues keys and values, interleaved
- */
+ /** @param keysAndValues keys and values, interleaved */
private JavaOnlyMap(Object... keysAndValues) {
if (keysAndValues.length % 2 != 0) {
throw new IllegalArgumentException("You must provide the same number of keys and values");
}
mBackingMap = new HashMap();
for (int i = 0; i < keysAndValues.length; i += 2) {
- mBackingMap.put(keysAndValues[i], keysAndValues[i + 1]);
+ Object val = keysAndValues[i + 1];
+ if (val instanceof Number) {
+ // all values from JS are doubles, so emulate that here for tests.
+ val = ((Number)val).doubleValue();
+ }
+ mBackingMap.put(keysAndValues[i], val);
}
}
@@ -142,15 +144,20 @@ public class JavaOnlyMap implements ReadableMap, WritableMap {
} else if (value instanceof Dynamic) {
return ((Dynamic) value).getType();
} else {
- throw new IllegalArgumentException("Invalid value " + value.toString() + " for key " + name +
- "contained in JavaOnlyMap");
+ throw new IllegalArgumentException(
+ "Invalid value " + value.toString() + " for key " + name + "contained in JavaOnlyMap");
}
}
+ @Override
+ public @Nonnull Iterator> getEntryIterator() {
+ return mBackingMap.entrySet().iterator();
+ }
+
@Override
public @Nonnull ReadableMapKeySetIterator keySetIterator() {
return new ReadableMapKeySetIterator() {
- Iterator mIterator = mBackingMap.keySet().iterator();
+ Iterator> mIterator = mBackingMap.entrySet().iterator();
@Override
public boolean hasNextKey() {
@@ -159,7 +166,7 @@ public class JavaOnlyMap implements ReadableMap, WritableMap {
@Override
public String nextKey() {
- return mIterator.next();
+ return mIterator.next().getKey();
}
};
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableMap.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableMap.java
index 8a2ae68dbdb..a51e7c89d39 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableMap.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableMap.java
@@ -1,13 +1,14 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
+ * 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 java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -19,16 +20,36 @@ import javax.annotation.Nullable;
public interface ReadableMap {
boolean hasKey(@Nonnull String name);
- boolean isNull(@Nonnull String name);
- boolean getBoolean(@Nonnull String name);
- double getDouble(@Nonnull String name);
- int getInt(@Nonnull String name);
- @Nullable String getString(@Nonnull String name);
- @Nullable ReadableArray getArray(@Nonnull String name);
- @Nullable ReadableMap getMap(@Nonnull String name);
- @Nonnull Dynamic getDynamic(@Nonnull String name);
- @Nonnull ReadableType getType(@Nonnull String name);
- @Nonnull ReadableMapKeySetIterator keySetIterator();
- @Nonnull HashMap toHashMap();
+ boolean isNull(@Nonnull String name);
+
+ boolean getBoolean(@Nonnull String name);
+
+ double getDouble(@Nonnull String name);
+
+ int getInt(@Nonnull String name);
+
+ @Nullable
+ String getString(@Nonnull String name);
+
+ @Nullable
+ ReadableArray getArray(@Nonnull String name);
+
+ @Nullable
+ ReadableMap getMap(@Nonnull String name);
+
+ @Nonnull
+ Dynamic getDynamic(@Nonnull String name);
+
+ @Nonnull
+ ReadableType getType(@Nonnull String name);
+
+ @Nonnull
+ Iterator> getEntryIterator();
+
+ @Nonnull
+ ReadableMapKeySetIterator keySetIterator();
+
+ @Nonnull
+ HashMap toHashMap();
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java
index 38fd926827c..cbd28320503 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java
@@ -1,10 +1,9 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
+ * 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 com.facebook.infer.annotation.Assertions;
@@ -13,6 +12,7 @@ import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.config.ReactFeatureFlags;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -32,17 +32,19 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
}
private @Nullable String[] mKeys;
- private @Nullable HashMap mLocalMap;
- private @Nullable HashMap mLocalTypeMap;
+ private @Nullable HashMap mLocalMap;
+ private @Nullable HashMap mLocalTypeMap;
private static int mJniCallCounter;
+
public static void setUseNativeAccessor(boolean useNativeAccessor) {
ReactFeatureFlags.useMapNativeAccessor = useNativeAccessor;
}
+
public static int getJNIPassCounter() {
return mJniCallCounter;
}
- private HashMap getLocalMap() {
+ private HashMap getLocalMap() {
// Fast return for the common case
if (mLocalMap != null) {
return mLocalMap;
@@ -58,17 +60,19 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
mJniCallCounter++;
int length = mKeys.length;
mLocalMap = new HashMap<>(length);
- for(int i = 0; i< length; i++) {
+ for (int i = 0; i < length; i++) {
mLocalMap.put(mKeys[i], values[i]);
}
}
}
return mLocalMap;
}
+
private native String[] importKeys();
+
private native Object[] importValues();
- private @Nonnull HashMap getLocalTypeMap() {
+ private @Nonnull HashMap getLocalTypeMap() {
// Fast and non-blocking return for common case
if (mLocalTypeMap != null) {
return mLocalTypeMap;
@@ -85,13 +89,14 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
mJniCallCounter++;
int length = mKeys.length;
mLocalTypeMap = new HashMap<>(length);
- for(int i = 0; i< length; i++) {
+ for (int i = 0; i < length; i++) {
mLocalTypeMap.put(mKeys[i], (ReadableType) types[i]);
}
}
}
return mLocalTypeMap;
}
+
private native Object[] importTypes();
@Override
@@ -102,6 +107,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
}
return getLocalMap().containsKey(name);
}
+
private native boolean hasKeyNative(String name);
@Override
@@ -115,6 +121,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
}
throw new NoSuchKeyException(name);
}
+
private native boolean isNullNative(@Nonnull String name);
private @Nonnull Object getValue(@Nonnull String name) {
@@ -146,8 +153,12 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
private void checkInstance(String name, Object value, Class type) {
if (value != null && !type.isInstance(value)) {
throw new ClassCastException(
- "Value for " + name + " cannot be cast from " +
- value.getClass().getSimpleName() + " to " + type.getSimpleName());
+ "Value for "
+ + name
+ + " cannot be cast from "
+ + value.getClass().getSimpleName()
+ + " to "
+ + type.getSimpleName());
}
}
@@ -159,6 +170,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
}
return getValue(name, Boolean.class).booleanValue();
}
+
private native boolean getBooleanNative(String name);
@Override
@@ -169,6 +181,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
}
return getValue(name, Double.class).doubleValue();
}
+
private native double getDoubleNative(String name);
@Override
@@ -181,6 +194,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
// All numbers coming out of native are doubles, so cast here then truncate
return getValue(name, Double.class).intValue();
}
+
private native int getIntNative(String name);
@Override
@@ -191,6 +205,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
}
return getNullableValue(name, String.class);
}
+
private native String getStringNative(String name);
@Override
@@ -201,6 +216,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
}
return getNullableValue(name, ReadableArray.class);
}
+
private native ReadableNativeArray getArrayNative(String name);
@Override
@@ -211,6 +227,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
}
return getNullableValue(name, ReadableNativeMap.class);
}
+
private native ReadableNativeMap getMapNative(String name);
@Override
@@ -224,6 +241,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
}
throw new NoSuchKeyException(name);
}
+
private native ReadableType getTypeNative(String name);
@Override
@@ -231,6 +249,11 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
return DynamicFromMap.create(this, name);
}
+ @Override
+ public @Nonnull Iterator> getEntryIterator() {
+ return getLocalMap().entrySet().iterator();
+ }
+
@Override
public @Nonnull ReadableMapKeySetIterator keySetIterator() {
return new ReadableNativeMapKeySetIterator(this);
@@ -283,8 +306,8 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
break;
default:
throw new IllegalArgumentException("Could not convert object with key: " + key + ".");
- }
}
+ }
return hashMap;
}
@@ -314,17 +337,13 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
return hashMap;
}
- /**
- * Implementation of a {@link ReadableNativeMap} iterator in native memory.
- */
+ /** Implementation of a {@link ReadableNativeMap} iterator in native memory. */
@DoNotStrip
private static class ReadableNativeMapKeySetIterator implements ReadableMapKeySetIterator {
- @DoNotStrip
- private final HybridData mHybridData;
+ @DoNotStrip private final HybridData mHybridData;
// Need to hold a strong ref to the map so that our native references remain valid.
- @DoNotStrip
- private final ReadableNativeMap mMap;
+ @DoNotStrip private final ReadableNativeMap mMap;
public ReadableNativeMapKeySetIterator(ReadableNativeMap readableNativeMap) {
mMap = readableNativeMap;
@@ -333,6 +352,7 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
@Override
public native boolean hasNextKey();
+
@Override
public native String nextKey();
diff --git a/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java b/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java
index f9742ac08d0..4ebe07c820f 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java
@@ -13,6 +13,7 @@ import static javax.tools.Diagnostic.Kind.WARNING;
import com.facebook.infer.annotation.SuppressFieldNotInitialized;
import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.DynamicFromObject;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.annotations.ReactProp;
@@ -55,9 +56,9 @@ import javax.lang.model.util.Types;
/**
* This annotation processor crawls subclasses of ReactShadowNode and ViewManager and finds their
- * exported properties with the @ReactProp or @ReactGroupProp annotation. It generates a class
- * per shadow node/view manager that is named {@code $$PropSetter}. This class contains methods
- * to retrieve the name and type of all methods and a way to set these properties without
+ * exported properties with the @ReactProp or @ReactGroupProp annotation. It generates a class per
+ * shadow node/view manager that is named {@code $$PropSetter}. This class contains
+ * methods to retrieve the name and type of all methods and a way to set these properties without
* reflection.
*/
@SupportedAnnotationTypes("com.facebook.react.uimanager.annotations.ReactPropertyHolder")
@@ -68,10 +69,12 @@ public class ReactPropertyProcessor extends AbstractProcessor {
private static final TypeName PROPS_TYPE =
ClassName.get("com.facebook.react.uimanager", "ReactStylesDiffMap");
+ private static final TypeName OBJECT_TYPE = TypeName.get(Object.class);
private static final TypeName STRING_TYPE = TypeName.get(String.class);
private static final TypeName READABLE_MAP_TYPE = TypeName.get(ReadableMap.class);
private static final TypeName READABLE_ARRAY_TYPE = TypeName.get(ReadableArray.class);
private static final TypeName DYNAMIC_TYPE = TypeName.get(Dynamic.class);
+ private static final TypeName DYNAMIC_FROM_OBJECT_TYPE = TypeName.get(DynamicFromObject.class);
private static final TypeName VIEW_MANAGER_TYPE =
ClassName.get("com.facebook.react.uimanager", "ViewManager");
@@ -80,14 +83,10 @@ public class ReactPropertyProcessor extends AbstractProcessor {
private static final ClassName VIEW_MANAGER_SETTER_TYPE =
ClassName.get(
- "com.facebook.react.uimanager",
- "ViewManagerPropertyUpdater",
- "ViewManagerSetter");
+ "com.facebook.react.uimanager", "ViewManagerPropertyUpdater", "ViewManagerSetter");
private static final ClassName SHADOW_NODE_SETTER_TYPE =
ClassName.get(
- "com.facebook.react.uimanager",
- "ViewManagerPropertyUpdater",
- "ShadowNodeSetter");
+ "com.facebook.react.uimanager", "ViewManagerPropertyUpdater", "ShadowNodeSetter");
private static final TypeName PROPERTY_MAP_TYPE =
ParameterizedTypeName.get(Map.class, String.class, String.class);
@@ -96,14 +95,10 @@ public class ReactPropertyProcessor extends AbstractProcessor {
private final Map mClasses;
- @SuppressFieldNotInitialized
- private Filer mFiler;
- @SuppressFieldNotInitialized
- private Messager mMessager;
- @SuppressFieldNotInitialized
- private Elements mElements;
- @SuppressFieldNotInitialized
- private Types mTypes;
+ @SuppressFieldNotInitialized private Filer mFiler;
+ @SuppressFieldNotInitialized private Messager mMessager;
+ @SuppressFieldNotInitialized private Elements mElements;
+ @SuppressFieldNotInitialized private Types mTypes;
static {
DEFAULT_TYPES = new HashMap<>();
@@ -165,7 +160,8 @@ public class ReactPropertyProcessor extends AbstractProcessor {
if (!shouldIgnoreClass(classInfo)) {
// Sort by name
Collections.sort(
- classInfo.mProperties, new Comparator() {
+ classInfo.mProperties,
+ new Comparator() {
@Override
public int compare(PropertyInfo a, PropertyInfo b) {
return a.mProperty.name().compareTo(b.mProperty.name());
@@ -219,8 +215,8 @@ public class ReactPropertyProcessor extends AbstractProcessor {
classInfo.addProperty(propertyBuilder.build(element, new RegularProperty(prop)));
} else if (propGroup != null) {
for (int i = 0, size = propGroup.names().length; i < size; i++) {
- classInfo
- .addProperty(propertyBuilder.build(element, new GroupProperty(propGroup, i)));
+ classInfo.addProperty(
+ propertyBuilder.build(element, new GroupProperty(propGroup, i)));
}
}
} catch (ReactPropertyException e) {
@@ -251,29 +247,32 @@ public class ReactPropertyProcessor extends AbstractProcessor {
private void generateCode(ClassInfo classInfo, List properties)
throws IOException, ReactPropertyException {
- MethodSpec getMethods = MethodSpec.methodBuilder("getProperties")
- .addModifiers(PUBLIC)
- .addAnnotation(Override.class)
- .addParameter(PROPERTY_MAP_TYPE, "props")
- .returns(TypeName.VOID)
- .addCode(generateGetProperties(properties))
- .build();
+ MethodSpec getMethods =
+ MethodSpec.methodBuilder("getProperties")
+ .addModifiers(PUBLIC)
+ .addAnnotation(Override.class)
+ .addParameter(PROPERTY_MAP_TYPE, "props")
+ .returns(TypeName.VOID)
+ .addCode(generateGetProperties(properties))
+ .build();
TypeName superType = getSuperType(classInfo);
ClassName className = classInfo.mClassName;
String holderClassName =
getClassName((TypeElement) classInfo.mElement, className.packageName()) + "$$PropsSetter";
- TypeSpec holderClass = TypeSpec.classBuilder(holderClassName)
- .addSuperinterface(superType)
- .addModifiers(PUBLIC)
- .addMethod(generateSetPropertySpec(classInfo, properties))
- .addMethod(getMethods)
- .build();
+ TypeSpec holderClass =
+ TypeSpec.classBuilder(holderClassName)
+ .addSuperinterface(superType)
+ .addModifiers(PUBLIC)
+ .addMethod(generateSetPropertySpec(classInfo, properties))
+ .addMethod(getMethods)
+ .build();
- JavaFile javaFile = JavaFile.builder(className.packageName(), holderClass)
- .addFileComment("Generated by " + getClass().getName())
- .build();
+ JavaFile javaFile =
+ JavaFile.builder(className.packageName(), holderClass)
+ .addFileComment("Generated by " + getClass().getName())
+ .build();
javaFile.writeTo(mFiler);
}
@@ -287,9 +286,7 @@ public class ReactPropertyProcessor extends AbstractProcessor {
switch (classInfo.getType()) {
case VIEW_MANAGER:
return ParameterizedTypeName.get(
- VIEW_MANAGER_SETTER_TYPE,
- classInfo.mClassName,
- classInfo.mViewType);
+ VIEW_MANAGER_SETTER_TYPE, classInfo.mClassName, classInfo.mViewType);
case SHADOW_NODE:
return ParameterizedTypeName.get(SHADOW_NODE_SETTER_TYPE, classInfo.mClassName);
default:
@@ -298,12 +295,12 @@ public class ReactPropertyProcessor extends AbstractProcessor {
}
private static MethodSpec generateSetPropertySpec(
- ClassInfo classInfo,
- List properties) {
- MethodSpec.Builder builder = MethodSpec.methodBuilder("setProperty")
- .addModifiers(PUBLIC)
- .addAnnotation(Override.class)
- .returns(TypeName.VOID);
+ ClassInfo classInfo, List properties) {
+ MethodSpec.Builder builder =
+ MethodSpec.methodBuilder("setProperty")
+ .addModifiers(PUBLIC)
+ .addAnnotation(Override.class)
+ .returns(TypeName.VOID);
switch (classInfo.getType()) {
case VIEW_MANAGER:
@@ -312,14 +309,13 @@ public class ReactPropertyProcessor extends AbstractProcessor {
.addParameter(classInfo.mViewType, "view");
break;
case SHADOW_NODE:
- builder
- .addParameter(classInfo.mClassName, "node");
+ builder.addParameter(classInfo.mClassName, "node");
break;
}
return builder
.addParameter(STRING_TYPE, "name")
- .addParameter(PROPS_TYPE, "props")
+ .addParameter(OBJECT_TYPE, "value")
.addCode(generateSetProperty(classInfo, properties))
.build();
}
@@ -334,9 +330,7 @@ public class ReactPropertyProcessor extends AbstractProcessor {
builder.add("switch (name) {\n").indent();
for (int i = 0, size = properties.size(); i < size; i++) {
PropertyInfo propertyInfo = properties.get(i);
- builder
- .add("case \"$L\":\n", propertyInfo.mProperty.name())
- .indent();
+ builder.add("case \"$L\":\n", propertyInfo.mProperty.name()).indent();
switch (info.getType()) {
case VIEW_MANAGER:
@@ -350,14 +344,12 @@ public class ReactPropertyProcessor extends AbstractProcessor {
builder.add("$L, ", ((GroupProperty) propertyInfo.mProperty).mGroupIndex);
}
if (BOXED_PRIMITIVES.contains(propertyInfo.propertyType)) {
- builder.add("props.isNull(name) ? null : ");
+ builder.add("value == null ? null : ");
}
getPropertyExtractor(propertyInfo, builder);
builder.addStatement(")");
- builder
- .addStatement("break")
- .unindent();
+ builder.addStatement("break").unindent();
}
builder.unindent().add("}\n");
@@ -365,17 +357,16 @@ public class ReactPropertyProcessor extends AbstractProcessor {
}
private static CodeBlock.Builder getPropertyExtractor(
- PropertyInfo info,
- CodeBlock.Builder builder) {
+ PropertyInfo info, CodeBlock.Builder builder) {
TypeName propertyType = info.propertyType;
if (propertyType.equals(STRING_TYPE)) {
- return builder.add("props.getString(name)");
+ return builder.add("($L)value", STRING_TYPE);
} else if (propertyType.equals(READABLE_ARRAY_TYPE)) {
- return builder.add("props.getArray(name)");
+ return builder.add("($L)value", READABLE_ARRAY_TYPE); // TODO: use real type but needs import
} else if (propertyType.equals(READABLE_MAP_TYPE)) {
- return builder.add("props.getMap(name)");
+ return builder.add("($L)value", READABLE_MAP_TYPE);
} else if (propertyType.equals(DYNAMIC_TYPE)) {
- return builder.add("props.getDynamic(name)");
+ return builder.add("new $L(value)", DYNAMIC_FROM_OBJECT_TYPE);
}
if (BOXED_PRIMITIVES.contains(propertyType)) {
@@ -383,25 +374,27 @@ public class ReactPropertyProcessor extends AbstractProcessor {
}
if (propertyType.equals(TypeName.BOOLEAN)) {
- return builder.add("props.getBoolean(name, $L)", info.mProperty.defaultBoolean());
- } if (propertyType.equals(TypeName.DOUBLE)) {
+ return builder.add("value == null ? $L : (boolean) value", info.mProperty.defaultBoolean());
+ }
+ if (propertyType.equals(TypeName.DOUBLE)) {
double defaultDouble = info.mProperty.defaultDouble();
if (Double.isNaN(defaultDouble)) {
- return builder.add("props.getDouble(name, $T.NaN)", Double.class);
+ return builder.add("value == null ? $T.NaN : (double) value", Double.class);
} else {
- return builder.add("props.getDouble(name, $Lf)", defaultDouble);
+ return builder.add("value == null ? $Lf : (double) value", defaultDouble);
}
}
if (propertyType.equals(TypeName.FLOAT)) {
float defaultFloat = info.mProperty.defaultFloat();
if (Float.isNaN(defaultFloat)) {
- return builder.add("props.getFloat(name, $T.NaN)", Float.class);
+ return builder.add("value == null ? $T.NaN : ((Double)value).floatValue()", Float.class);
} else {
- return builder.add("props.getFloat(name, $Lf)", defaultFloat);
+ return builder.add("value == null ? $Lf : ((Double)value).floatValue()", defaultFloat);
}
}
if (propertyType.equals(TypeName.INT)) {
- return builder.add("props.getInt(name, $L)", info.mProperty.defaultInt());
+ return builder.add(
+ "value == null ? $L : ((Double)value).intValue()", info.mProperty.defaultInt());
}
throw new IllegalArgumentException();
@@ -424,20 +417,20 @@ public class ReactPropertyProcessor extends AbstractProcessor {
private static String getPropertypTypeName(Property property, TypeName propertyType) {
String defaultType = DEFAULT_TYPES.get(propertyType);
- String useDefaultType = property instanceof RegularProperty ?
- ReactProp.USE_DEFAULT_TYPE : ReactPropGroup.USE_DEFAULT_TYPE;
+ String useDefaultType =
+ property instanceof RegularProperty
+ ? ReactProp.USE_DEFAULT_TYPE
+ : ReactPropGroup.USE_DEFAULT_TYPE;
return useDefaultType.equals(property.customType()) ? defaultType : property.customType();
}
private static void checkElement(Element element) throws ReactPropertyException {
- if (element.getKind() == ElementKind.METHOD
- && element.getModifiers().contains(PUBLIC)) {
+ if (element.getKind() == ElementKind.METHOD && element.getModifiers().contains(PUBLIC)) {
return;
}
throw new ReactPropertyException(
- "@ReactProp and @ReachPropGroup annotation must be on a public method",
- element);
+ "@ReactProp and @ReachPropGroup annotation must be on a public method", element);
}
private static boolean shouldIgnoreClass(ClassInfo classInfo) {
@@ -464,10 +457,15 @@ public class ReactPropertyProcessor extends AbstractProcessor {
private interface Property {
String name();
+
String customType();
+
double defaultDouble();
+
float defaultFloat();
+
int defaultInt();
+
boolean defaultBoolean();
}
@@ -575,9 +573,13 @@ public class ReactPropertyProcessor extends AbstractProcessor {
String name = propertyInfo.mProperty.name();
if (checkPropertyExists(name)) {
throw new ReactPropertyException(
- "Module " + mClassName + " has already registered a property named \"" +
- name + "\". If you want to override a property, don't add" +
- "the @ReactProp annotation to the property in the subclass", propertyInfo);
+ "Module "
+ + mClassName
+ + " has already registered a property named \""
+ + name
+ + "\". If you want to override a property, don't add"
+ + "the @ReactProp annotation to the property in the subclass",
+ propertyInfo);
}
mProperties.add(propertyInfo);
@@ -601,10 +603,7 @@ public class ReactPropertyProcessor extends AbstractProcessor {
public final Property mProperty;
private PropertyInfo(
- String methodName,
- TypeName propertyType,
- Element element,
- Property property) {
+ String methodName, TypeName propertyType, Element element, Property property) {
this.methodName = methodName;
this.propertyType = propertyType;
this.element = element;
@@ -622,8 +621,7 @@ public class ReactPropertyProcessor extends AbstractProcessor {
mClassInfo = classInfo;
}
- public PropertyInfo build(Element element, Property property)
- throws ReactPropertyException {
+ public PropertyInfo build(Element element, Property property) throws ReactPropertyException {
String methodName = element.getSimpleName().toString();
ExecutableElement method = (ExecutableElement) element;
@@ -645,16 +643,14 @@ public class ReactPropertyProcessor extends AbstractProcessor {
TypeName indexType = TypeName.get(parameters.get(index++).asType());
if (!indexType.equals(TypeName.INT)) {
throw new ReactPropertyException(
- "Argument " + index + " must be an int for @ReactPropGroup",
- element);
+ "Argument " + index + " must be an int for @ReactPropGroup", element);
}
}
TypeName propertyType = TypeName.get(parameters.get(index++).asType());
if (!DEFAULT_TYPES.containsKey(propertyType)) {
throw new ReactPropertyException(
- "Argument " + index + " must be of a supported type",
- element);
+ "Argument " + index + " must be of a supported type", element);
}
return new PropertyInfo(methodName, propertyType, element, property);
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerPropertyUpdater.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerPropertyUpdater.java
index 7bea9928a7c..6ab0cdb7a00 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerPropertyUpdater.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerPropertyUpdater.java
@@ -6,25 +6,28 @@
package com.facebook.react.uimanager;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import android.view.View;
import com.facebook.common.logging.FLog;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
public class ViewManagerPropertyUpdater {
public interface Settable {
- void getProperties(Map props);
+ void getProperties(Map props);
}
public interface ViewManagerSetter extends Settable {
- void setProperty(T manager, V view, String name, ReactStylesDiffMap props);
+ void setProperty(T manager, V view, String name, Object value);
}
public interface ShadowNodeSetter extends Settable {
- void setProperty(T node, String name, ReactStylesDiffMap props);
+ void setProperty(T node, String name, Object value);
}
private static final String TAG = "ViewManagerPropertyUpdater";
@@ -40,25 +43,21 @@ public class ViewManagerPropertyUpdater {
}
public static void updateProps(
- T manager,
- V v,
- ReactStylesDiffMap props) {
+ T manager, V v, ReactStylesDiffMap props) {
ViewManagerSetter setter = findManagerSetter(manager.getClass());
- ReadableMap propMap = props.mBackingMap;
- ReadableMapKeySetIterator iterator = propMap.keySetIterator();
- while (iterator.hasNextKey()) {
- String key = iterator.nextKey();
- setter.setProperty(manager, v, key, props);
+ Iterator> iterator = props.mBackingMap.getEntryIterator();
+ while (iterator.hasNext()) {
+ Map.Entry entry = iterator.next();
+ setter.setProperty(manager, v, entry.getKey(), entry.getValue());
}
}
public static void updateProps(T node, ReactStylesDiffMap props) {
ShadowNodeSetter setter = findNodeSetter(node.getClass());
- ReadableMap propMap = props.mBackingMap;
- ReadableMapKeySetIterator iterator = propMap.keySetIterator();
- while (iterator.hasNextKey()) {
- String key = iterator.nextKey();
- setter.setProperty(node, key, props);
+ Iterator> iterator = props.mBackingMap.getEntryIterator();
+ while (iterator.hasNext()) {
+ Map.Entry entry = iterator.next();
+ setter.setProperty(node, entry.getKey(), entry.getValue());
}
}
@@ -126,10 +125,10 @@ public class ViewManagerPropertyUpdater {
}
@Override
- public void setProperty(T manager, V v, String name, ReactStylesDiffMap props) {
+ public void setProperty(T manager, V v, String name, Object value) {
ViewManagersPropertyCache.PropSetter setter = mPropSetters.get(name);
if (setter != null) {
- setter.updateViewProp(manager, v, props);
+ setter.updateViewProp(manager, v, value);
}
}
@@ -151,10 +150,10 @@ public class ViewManagerPropertyUpdater {
}
@Override
- public void setProperty(ReactShadowNode node, String name, ReactStylesDiffMap props) {
+ public void setProperty(ReactShadowNode node, String name, Object value) {
ViewManagersPropertyCache.PropSetter setter = mPropSetters.get(name);
if (setter != null) {
- setter.updateShadowNodeProp(node, props);
+ setter.updateShadowNodeProp(node, value);
}
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java
index 98428f40b9f..f987fc2ef5b 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java
@@ -8,6 +8,7 @@ package com.facebook.react.uimanager;
import android.view.View;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.DynamicFromObject;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
@@ -33,7 +34,7 @@ import javax.annotation.Nullable;
EMPTY_PROPS_MAP.clear();
}
- /*package*/ static abstract class PropSetter {
+ /*package*/ abstract static class PropSetter {
protected final String mPropName;
protected final String mPropType;
@@ -50,16 +51,18 @@ import javax.annotation.Nullable;
private PropSetter(ReactProp prop, String defaultType, Method setter) {
mPropName = prop.name();
- mPropType = ReactProp.USE_DEFAULT_TYPE.equals(prop.customType()) ?
- defaultType : prop.customType();
+ mPropType =
+ ReactProp.USE_DEFAULT_TYPE.equals(prop.customType()) ? defaultType : prop.customType();
mSetter = setter;
mIndex = null;
}
private PropSetter(ReactPropGroup prop, String defaultType, Method setter, int index) {
mPropName = prop.names()[index];
- mPropType = ReactPropGroup.USE_DEFAULT_TYPE.equals(prop.customType()) ?
- defaultType : prop.customType();
+ mPropType =
+ ReactPropGroup.USE_DEFAULT_TYPE.equals(prop.customType())
+ ? defaultType
+ : prop.customType();
mSetter = setter;
mIndex = index;
}
@@ -72,52 +75,55 @@ import javax.annotation.Nullable;
return mPropType;
}
- public void updateViewProp(
- ViewManager viewManager,
- View viewToUpdate,
- ReactStylesDiffMap props) {
+ public void updateViewProp(ViewManager viewManager, View viewToUpdate, Object value) {
try {
if (mIndex == null) {
VIEW_MGR_ARGS[0] = viewToUpdate;
- VIEW_MGR_ARGS[1] = extractProperty(props);
+ VIEW_MGR_ARGS[1] = getValueOrDefault(value);
mSetter.invoke(viewManager, VIEW_MGR_ARGS);
Arrays.fill(VIEW_MGR_ARGS, null);
} else {
VIEW_MGR_GROUP_ARGS[0] = viewToUpdate;
VIEW_MGR_GROUP_ARGS[1] = mIndex;
- VIEW_MGR_GROUP_ARGS[2] = extractProperty(props);
+ VIEW_MGR_GROUP_ARGS[2] = getValueOrDefault(value);
mSetter.invoke(viewManager, VIEW_MGR_GROUP_ARGS);
Arrays.fill(VIEW_MGR_GROUP_ARGS, null);
}
} catch (Throwable t) {
FLog.e(ViewManager.class, "Error while updating prop " + mPropName, t);
- throw new JSApplicationIllegalArgumentException("Error while updating property '" +
- mPropName + "' of a view managed by: " + viewManager.getName(), t);
+ throw new JSApplicationIllegalArgumentException(
+ "Error while updating property '"
+ + mPropName
+ + "' of a view managed by: "
+ + viewManager.getName(),
+ t);
}
}
- public void updateShadowNodeProp(
- ReactShadowNode nodeToUpdate,
- ReactStylesDiffMap props) {
+ public void updateShadowNodeProp(ReactShadowNode nodeToUpdate, Object value) {
try {
if (mIndex == null) {
- SHADOW_ARGS[0] = extractProperty(props);
+ SHADOW_ARGS[0] = getValueOrDefault(value);
mSetter.invoke(nodeToUpdate, SHADOW_ARGS);
Arrays.fill(SHADOW_ARGS, null);
} else {
SHADOW_GROUP_ARGS[0] = mIndex;
- SHADOW_GROUP_ARGS[1] = extractProperty(props);
+ SHADOW_GROUP_ARGS[1] = getValueOrDefault(value);
mSetter.invoke(nodeToUpdate, SHADOW_GROUP_ARGS);
Arrays.fill(SHADOW_GROUP_ARGS, null);
}
} catch (Throwable t) {
FLog.e(ViewManager.class, "Error while updating prop " + mPropName, t);
- throw new JSApplicationIllegalArgumentException("Error while updating property '" +
- mPropName + "' in shadow node of type: " + nodeToUpdate.getViewClass(), t);
+ throw new JSApplicationIllegalArgumentException(
+ "Error while updating property '"
+ + mPropName
+ + "' in shadow node of type: "
+ + nodeToUpdate.getViewClass(),
+ t);
}
}
- protected abstract @Nullable Object extractProperty(ReactStylesDiffMap props);
+ protected abstract @Nullable Object getValueOrDefault(Object value);
}
private static class DynamicPropSetter extends PropSetter {
@@ -131,8 +137,12 @@ import javax.annotation.Nullable;
}
@Override
- protected Object extractProperty(ReactStylesDiffMap props) {
- return props.getDynamic(mPropName);
+ protected Object getValueOrDefault(Object value) {
+ if (value instanceof Dynamic) {
+ return value;
+ } else {
+ return new DynamicFromObject(value);
+ }
}
}
@@ -151,8 +161,9 @@ import javax.annotation.Nullable;
}
@Override
- protected Object extractProperty(ReactStylesDiffMap props) {
- return props.getInt(mPropName, mDefaultValue);
+ protected Object getValueOrDefault(Object value) {
+ // All numbers from JS are Doubles which can't be simply cast to Integer
+ return value == null ? mDefaultValue : (Integer) ((Double)value).intValue();
}
}
@@ -171,8 +182,8 @@ import javax.annotation.Nullable;
}
@Override
- protected Object extractProperty(ReactStylesDiffMap props) {
- return props.getDouble(mPropName, mDefaultValue);
+ protected Object getValueOrDefault(Object value) {
+ return value == null ? mDefaultValue : (Double) value;
}
}
@@ -186,8 +197,9 @@ import javax.annotation.Nullable;
}
@Override
- protected Object extractProperty(ReactStylesDiffMap props) {
- return props.getBoolean(mPropName, mDefaultValue) ? Boolean.TRUE : Boolean.FALSE;
+ protected Object getValueOrDefault(Object value) {
+ boolean val = value == null ? mDefaultValue : (boolean) value;
+ return val ? Boolean.TRUE : Boolean.FALSE;
}
}
@@ -206,8 +218,9 @@ import javax.annotation.Nullable;
}
@Override
- protected Object extractProperty(ReactStylesDiffMap props) {
- return props.getFloat(mPropName, mDefaultValue);
+ protected Object getValueOrDefault(Object value) {
+ // All numbers from JS are Doubles which can't be simply cast to Float
+ return value == null ? mDefaultValue : (Float) ((Double)value).floatValue();
}
}
@@ -218,8 +231,8 @@ import javax.annotation.Nullable;
}
@Override
- protected @Nullable Object extractProperty(ReactStylesDiffMap props) {
- return props.getArray(mPropName);
+ protected @Nullable Object getValueOrDefault(Object value) {
+ return (ReadableArray) value;
}
}
@@ -230,8 +243,8 @@ import javax.annotation.Nullable;
}
@Override
- protected @Nullable Object extractProperty(ReactStylesDiffMap props) {
- return props.getMap(mPropName);
+ protected @Nullable Object getValueOrDefault(Object value) {
+ return (ReadableMap) value;
}
}
@@ -242,8 +255,8 @@ import javax.annotation.Nullable;
}
@Override
- protected @Nullable Object extractProperty(ReactStylesDiffMap props) {
- return props.getString(mPropName);
+ protected @Nullable Object getValueOrDefault(Object value) {
+ return (String) value;
}
}
@@ -254,9 +267,9 @@ import javax.annotation.Nullable;
}
@Override
- protected @Nullable Object extractProperty(ReactStylesDiffMap props) {
- if (!props.isNull(mPropName)) {
- return props.getBoolean(mPropName, /* ignored */ false) ? Boolean.TRUE : Boolean.FALSE;
+ protected @Nullable Object getValueOrDefault(Object value) {
+ if (value != null) {
+ return (boolean) value ? Boolean.TRUE : Boolean.FALSE;
}
return null;
}
@@ -273,9 +286,9 @@ import javax.annotation.Nullable;
}
@Override
- protected @Nullable Object extractProperty(ReactStylesDiffMap props) {
- if (!props.isNull(mPropName)) {
- return props.getInt(mPropName, /* ignored */ 0);
+ protected @Nullable Object getValueOrDefault(Object value) {
+ if (value != null) {
+ return (Integer) value;
}
return null;
}
@@ -317,9 +330,10 @@ import javax.annotation.Nullable;
}
// This is to include all the setters from parent classes. Once calculated the result will be
// stored in CLASS_PROPS_CACHE so that we only scan for @ReactProp annotations once per class.
- props = new HashMap<>(
- getNativePropSettersForViewManagerClass(
- (Class extends ViewManager>) cls.getSuperclass()));
+ props =
+ new HashMap<>(
+ getNativePropSettersForViewManagerClass(
+ (Class extends ViewManager>) cls.getSuperclass()));
extractPropSettersFromViewManagerClassDefinition(cls, props);
CLASS_PROPS_CACHE.put(cls, props);
return props;
@@ -343,18 +357,17 @@ import javax.annotation.Nullable;
return props;
}
// This is to include all the setters from parent classes up to ReactShadowNode class
- props = new HashMap<>(
- getNativePropSettersForShadowNodeClass(
- (Class extends ReactShadowNode>) cls.getSuperclass()));
+ props =
+ new HashMap<>(
+ getNativePropSettersForShadowNodeClass(
+ (Class extends ReactShadowNode>) cls.getSuperclass()));
extractPropSettersFromShadowNodeClassDefinition(cls, props);
CLASS_PROPS_CACHE.put(cls, props);
return props;
}
private static PropSetter createPropSetter(
- ReactProp annotation,
- Method method,
- Class> propTypeClass) {
+ ReactProp annotation, Method method, Class> propTypeClass) {
if (propTypeClass == Dynamic.class) {
return new DynamicPropSetter(annotation, method);
} else if (propTypeClass == boolean.class) {
@@ -376,8 +389,13 @@ import javax.annotation.Nullable;
} else if (propTypeClass == ReadableMap.class) {
return new MapPropSetter(annotation, method);
} else {
- throw new RuntimeException("Unrecognized type: " + propTypeClass + " for method: " +
- method.getDeclaringClass().getName() + "#" + method.getName());
+ throw new RuntimeException(
+ "Unrecognized type: "
+ + propTypeClass
+ + " for method: "
+ + method.getDeclaringClass().getName()
+ + "#"
+ + method.getName());
}
}
@@ -389,43 +407,38 @@ import javax.annotation.Nullable;
String[] names = annotation.names();
if (propTypeClass == Dynamic.class) {
for (int i = 0; i < names.length; i++) {
- props.put(
- names[i],
- new DynamicPropSetter(annotation, method, i));
+ props.put(names[i], new DynamicPropSetter(annotation, method, i));
}
} else if (propTypeClass == int.class) {
for (int i = 0; i < names.length; i++) {
- props.put(
- names[i],
- new IntPropSetter(annotation, method, i, annotation.defaultInt()));
+ props.put(names[i], new IntPropSetter(annotation, method, i, annotation.defaultInt()));
}
} else if (propTypeClass == float.class) {
for (int i = 0; i < names.length; i++) {
- props.put(
- names[i],
- new FloatPropSetter(annotation, method, i, annotation.defaultFloat()));
+ props.put(names[i], new FloatPropSetter(annotation, method, i, annotation.defaultFloat()));
}
} else if (propTypeClass == double.class) {
for (int i = 0; i < names.length; i++) {
props.put(
- names[i],
- new DoublePropSetter(annotation, method, i, annotation.defaultDouble()));
+ names[i], new DoublePropSetter(annotation, method, i, annotation.defaultDouble()));
}
} else if (propTypeClass == Integer.class) {
for (int i = 0; i < names.length; i++) {
- props.put(
- names[i],
- new BoxedIntPropSetter(annotation, method, i));
+ props.put(names[i], new BoxedIntPropSetter(annotation, method, i));
}
} else {
- throw new RuntimeException("Unrecognized type: " + propTypeClass + " for method: " +
- method.getDeclaringClass().getName() + "#" + method.getName());
+ throw new RuntimeException(
+ "Unrecognized type: "
+ + propTypeClass
+ + " for method: "
+ + method.getDeclaringClass().getName()
+ + "#"
+ + method.getName());
}
}
private static void extractPropSettersFromViewManagerClassDefinition(
- Class extends ViewManager> cls,
- Map props) {
+ Class extends ViewManager> cls, Map props) {
Method[] declaredMethods = cls.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++) {
Method method = declaredMethods[i];
@@ -433,30 +446,42 @@ import javax.annotation.Nullable;
if (annotation != null) {
Class>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 2) {
- throw new RuntimeException("Wrong number of args for prop setter: " +
- cls.getName() + "#" + method.getName());
+ throw new RuntimeException(
+ "Wrong number of args for prop setter: " + cls.getName() + "#" + method.getName());
}
if (!View.class.isAssignableFrom(paramTypes[0])) {
- throw new RuntimeException("First param should be a view subclass to be updated: " +
- cls.getName() + "#" + method.getName());
+ throw new RuntimeException(
+ "First param should be a view subclass to be updated: "
+ + cls.getName()
+ + "#"
+ + method.getName());
}
props.put(annotation.name(), createPropSetter(annotation, method, paramTypes[1]));
}
ReactPropGroup groupAnnotation = method.getAnnotation(ReactPropGroup.class);
if (groupAnnotation != null) {
- Class> [] paramTypes = method.getParameterTypes();
+ Class>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 3) {
- throw new RuntimeException("Wrong number of args for group prop setter: " +
- cls.getName() + "#" + method.getName());
+ throw new RuntimeException(
+ "Wrong number of args for group prop setter: "
+ + cls.getName()
+ + "#"
+ + method.getName());
}
if (!View.class.isAssignableFrom(paramTypes[0])) {
- throw new RuntimeException("First param should be a view subclass to be updated: " +
- cls.getName() + "#" + method.getName());
+ throw new RuntimeException(
+ "First param should be a view subclass to be updated: "
+ + cls.getName()
+ + "#"
+ + method.getName());
}
if (paramTypes[1] != int.class) {
- throw new RuntimeException("Second argument should be property index: " +
- cls.getName() + "#" + method.getName());
+ throw new RuntimeException(
+ "Second argument should be property index: "
+ + cls.getName()
+ + "#"
+ + method.getName());
}
createPropSetters(groupAnnotation, method, paramTypes[2], props);
}
@@ -464,29 +489,34 @@ import javax.annotation.Nullable;
}
private static void extractPropSettersFromShadowNodeClassDefinition(
- Class extends ReactShadowNode> cls,
- Map props) {
+ Class extends ReactShadowNode> cls, Map props) {
for (Method method : cls.getDeclaredMethods()) {
ReactProp annotation = method.getAnnotation(ReactProp.class);
if (annotation != null) {
Class>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1) {
- throw new RuntimeException("Wrong number of args for prop setter: " +
- cls.getName() + "#" + method.getName());
+ throw new RuntimeException(
+ "Wrong number of args for prop setter: " + cls.getName() + "#" + method.getName());
}
props.put(annotation.name(), createPropSetter(annotation, method, paramTypes[0]));
}
ReactPropGroup groupAnnotation = method.getAnnotation(ReactPropGroup.class);
if (groupAnnotation != null) {
- Class> [] paramTypes = method.getParameterTypes();
+ Class>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 2) {
- throw new RuntimeException("Wrong number of args for group prop setter: " +
- cls.getName() + "#" + method.getName());
+ throw new RuntimeException(
+ "Wrong number of args for group prop setter: "
+ + cls.getName()
+ + "#"
+ + method.getName());
}
if (paramTypes[0] != int.class) {
- throw new RuntimeException("Second argument should be property index: " +
- cls.getName() + "#" + method.getName());
+ throw new RuntimeException(
+ "Second argument should be property index: "
+ + cls.getName()
+ + "#"
+ + method.getName());
}
createPropSetters(groupAnnotation, method, paramTypes[1], props);
}
diff --git a/ReactAndroid/src/test/java/com/facebook/react/uimanager/SimpleViewPropertyTest.java b/ReactAndroid/src/test/java/com/facebook/react/uimanager/SimpleViewPropertyTest.java
index dc67c8e72ab..95cc4d52767 100644
--- a/ReactAndroid/src/test/java/com/facebook/react/uimanager/SimpleViewPropertyTest.java
+++ b/ReactAndroid/src/test/java/com/facebook/react/uimanager/SimpleViewPropertyTest.java
@@ -9,6 +9,7 @@ package com.facebook.react.uimanager;
import java.util.Map;
+import android.graphics.drawable.ColorDrawable;
import android.view.View;
import com.facebook.react.bridge.ReadableMap;
@@ -94,6 +95,20 @@ public class SimpleViewPropertyTest {
assertThat(view.getAlpha()).isEqualTo(1.0f);
}
+ @Test
+ public void testBackgroundColor() {
+ View view = mManager.createView(mThemedContext, new JSResponderHandler());
+
+ mManager.updateProperties(view, buildStyles());
+ assertThat(view.getBackground()).isEqualTo(null);
+
+ mManager.updateProperties(view, buildStyles("backgroundColor", 12));
+ assertThat(((ColorDrawable)view.getBackground()).getColor()).isEqualTo(12);
+
+ mManager.updateProperties(view, buildStyles("backgroundColor", null));
+ assertThat(((ColorDrawable)view.getBackground()).getColor()).isEqualTo(0);
+ }
+
@Test
public void testGetNativeProps() {
Map nativeProps = mManager.getNativeProps();