Skip to content

Commit

Permalink
add support to display-p3 backgrounds and borders to android view
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanlntn committed Mar 15, 2024
1 parent b3a8eba commit dbe644f
Show file tree
Hide file tree
Showing 8 changed files with 391 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.PointerEventHelper;
import com.facebook.react.uimanager.util.ReactFindViewUtil;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand All @@ -42,6 +43,7 @@
public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode>
extends ViewManager<T, C> implements BaseViewManagerInterface<T>, View.OnLayoutChangeListener {

private static final String TAG = "BaseViewManager";
private static final int PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX = 2;
private static final float CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER = (float) Math.sqrt(5);

Expand Down Expand Up @@ -164,14 +166,26 @@ public void onLayoutChange(
}

@Override
@ReactProp(
name = ViewProps.BACKGROUND_COLOR,
defaultInt = Color.TRANSPARENT,
customType = "Color")
public void setBackgroundColor(@NonNull T view, int backgroundColor) {
view.setBackgroundColor(backgroundColor);
}

@ReactProp(
name = ViewProps.BACKGROUND_COLOR,
defaultInt = Color.TRANSPARENT,
customType = "Color")
public void setBackgroundColor(@NonNull T view, long backgroundColor) {
try {
Method setBackgroundColorMethod = view.getClass().getMethod("setBackgroundColor", long.class);
setBackgroundColorMethod.invoke(view, backgroundColor);
} catch (NoSuchMethodException e) {
FLog.e(TAG, "setBackgroundColor(long) method not found in view class", e);
view.setBackgroundColor(Color.toArgb(backgroundColor));
} catch (Exception e) {
FLog.e(TAG, "Failed to invoke setBackgroundColor(long)", e);
}
}

@Override
@ReactProp(name = ViewProps.TRANSFORM)
public void setTransform(@NonNull T view, @Nullable ReadableArray matrix) {
Expand Down Expand Up @@ -204,14 +218,21 @@ public void setElevation(@NonNull T view, float elevation) {
}

@Override
@ReactProp(name = ViewProps.SHADOW_COLOR, defaultInt = Color.BLACK, customType = "Color")
public void setShadowColor(@NonNull T view, int shadowColor) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
view.setOutlineAmbientShadowColor(shadowColor);
view.setOutlineSpotShadowColor(shadowColor);
}
}

@ReactProp(name = ViewProps.SHADOW_COLOR, defaultInt = Color.BLACK, customType = "Color")
public void setShadowColor(@NonNull T view, long shadowColor) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
view.setOutlineAmbientShadowColor(Color.toArgb(shadowColor));
view.setOutlineSpotShadowColor(Color.toArgb(shadowColor));
}
}

@Override
@ReactProp(name = ViewProps.Z_INDEX)
public void setZIndex(@NonNull T view, float zIndex) {
Expand Down Expand Up @@ -856,4 +877,4 @@ public void setTouchEnd(@NonNull T view, boolean value) {
public void setTouchCancel(@NonNull T view, boolean value) {
// no-op, handled by JSResponder
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public interface BaseViewManagerInterface<T extends View> {
void setViewState(T view, @Nullable ReadableMap accessibilityState);

void setBackgroundColor(T view, int backgroundColor);
void setBackgroundColor(T view, long backgroundColor);

void setBorderRadius(T view, float borderRadius);

Expand All @@ -49,6 +50,7 @@ public interface BaseViewManagerInterface<T extends View> {
void setElevation(T view, float elevation);

void setShadowColor(T view, int shadowColor);
void setShadowColor(T view, long shadowColor);

void setImportantForAccessibility(T view, @Nullable String importantForAccessibility);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* 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.uimanager;
import android.graphics.Color;
import java.util.Arrays;

/**
* Class representing CSS spacing border colors. Modified from {@link Spacing} to support long values.
*/
public class BorderColor {

/** Spacing type that represents the left direction. E.g. {@code marginLeft}. */
public static final int LEFT = 0;
/** Spacing type that represents the top direction. E.g. {@code marginTop}. */
public static final int TOP = 1;
/** Spacing type that represents the right direction. E.g. {@code marginRight}. */
public static final int RIGHT = 2;
/** Spacing type that represents the bottom direction. E.g. {@code marginBottom}. */
public static final int BOTTOM = 3;
/**
* Spacing type that represents start direction e.g. left in left-to-right, right in
* right-to-left.
*/
public static final int START = 4;
/**
* Spacing type that represents end direction e.g. right in left-to-right, left in right-to-left.
*/
public static final int END = 5;
/**
* Spacing type that represents horizontal direction (left and right). E.g. {@code
* marginHorizontal}.
*/
public static final int HORIZONTAL = 6;
/**
* Spacing type that represents vertical direction (top and bottom). E.g. {@code marginVertical}.
*/
public static final int VERTICAL = 7;
/**
* Spacing type that represents all directions (left, top, right, bottom). E.g. {@code margin}.
*/
public static final int ALL = 8;
/** Spacing type that represents block directions (top, bottom). E.g. {@code marginBlock}. */
public static final int BLOCK = 9;
/** Spacing type that represents the block end direction (bottom). E.g. {@code marginBlockEnd}. */
public static final int BLOCK_END = 10;
/**
* Spacing type that represents the block start direction (top). E.g. {@code marginBlockStart}.
*/
public static final int BLOCK_START = 11;

private static final int[] sFlagsMap = {
1, /*LEFT*/ 2, /*TOP*/ 4, /*RIGHT*/ 8, /*BOTTOM*/ 16, /*START*/ 32, /*END*/ 64, /*HORIZONTAL*/
128, /*VERTICAL*/ 256, /*ALL*/ 512, /*BLOCK*/ 1024, /*BLOCK_END*/ 2048, /*BLOCK_START*/
};

private final long[] mSpacing;
private int mValueFlags = 0;
private final long mDefaultValue;
private boolean mHasAliasesSet;

public BorderColor() {
this(0);
}

public BorderColor(long defaultValue) {
mDefaultValue = defaultValue;
mSpacing = newFullBorderColorArray();
}

public BorderColor(BorderColor original) {
mDefaultValue = original.mDefaultValue;
mSpacing = Arrays.copyOf(original.mSpacing, original.mSpacing.length);
mValueFlags = original.mValueFlags;
mHasAliasesSet = original.mHasAliasesSet;
}

/**
* Set a spacing value.
*
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}, {@link
* #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL}
* @param value the value for this direction
* @return {@code true} if the spacing has changed, or {@code false} if the same value was already
* set
*/
public boolean set(int spacingType, long value) {
if (mSpacing[spacingType] != value) {
mSpacing[spacingType] = value;

mValueFlags |= sFlagsMap[spacingType];

mHasAliasesSet =
(mValueFlags & sFlagsMap[ALL]) != 0
|| (mValueFlags & sFlagsMap[VERTICAL]) != 0
|| (mValueFlags & sFlagsMap[HORIZONTAL]) != 0
|| (mValueFlags & sFlagsMap[BLOCK]) != 0;

return true;
}

return false;
}

/**
* Get the spacing for a direction. This takes into account any default values that have been set.
*
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}
*/
public long get(int spacingType) {
long defaultValue =
(spacingType == START
|| spacingType == END
|| spacingType == BLOCK
|| spacingType == BLOCK_END
|| spacingType == BLOCK_START
? 0
: mDefaultValue);

if (mValueFlags == 0) {
return defaultValue;
}

if ((mValueFlags & sFlagsMap[spacingType]) != 0) {
return mSpacing[spacingType];
}

if (mHasAliasesSet) {
int secondType = spacingType == TOP || spacingType == BOTTOM ? VERTICAL : HORIZONTAL;
if ((mValueFlags & sFlagsMap[secondType]) != 0) {
return mSpacing[secondType];
} else if ((mValueFlags & sFlagsMap[ALL]) != 0) {
return mSpacing[ALL];
}
}

return defaultValue;
}

/**
* Get the raw value (that was set using {@link #set(int, float)}), without taking into account
* any default values.
*
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}, {@link
* #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL}
*/
public long getRaw(int spacingType) {
return mSpacing[spacingType];
}

/**
* Resets the spacing instance to its default state. This method is meant to be used when
* recycling {@link Spacing} instances.
*/
public void reset() {
Arrays.fill(mSpacing, 0);
mHasAliasesSet = false;
mValueFlags = 0;
}

/**
* Try to get start value and fallback to given type if not defined. This is used privately by the
* layout engine as a more efficient way to fetch direction-aware values by avoid extra method
* invocations.
*/
float getWithFallback(int spacingType, int fallbackType) {
return (mValueFlags & sFlagsMap[spacingType]) != 0 ? mSpacing[spacingType] : get(fallbackType);
}

private static long[] newFullBorderColorArray() {
return new long[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.facebook.react.uimanager;

import android.content.Context;
import android.graphics.Color;
import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.common.logging.FLog;
Expand Down Expand Up @@ -219,6 +220,34 @@ protected Object getValueOrDefault(Object value, Context context) {
}
}

private static class ColorLongPropSetter extends PropSetter {

private final int mDefaultValue;

public ColorLongPropSetter(ReactProp prop, Method setter) {
this(prop, setter, 0);
}

public ColorLongPropSetter(ReactProp prop, Method setter, int defaultValue) {
super(prop, "mixed", setter);
mDefaultValue = defaultValue;
}

public ColorLongPropSetter(ReactPropGroup prop, Method setter, int index, int defaultValue) {
super(prop, "mixed", setter, index);
mDefaultValue = defaultValue;
}

@Override
protected Object getValueOrDefault(Object value, Context context) {
Color color = ColorPropConverter.getColorInstance(value, context);
if (color == null) {
return mDefaultValue;
}
return color.pack();
}
}

private static class BooleanPropSetter extends PropSetter {

private final boolean mDefaultValue;
Expand Down Expand Up @@ -349,6 +378,26 @@ public BoxedColorPropSetter(ReactPropGroup prop, Method setter, int index) {
}
}

private static class BoxedColorLongPropSetter extends PropSetter {

public BoxedColorLongPropSetter(ReactProp prop, Method setter) {
super(prop, "mixed", setter);
}

public BoxedColorLongPropSetter(ReactPropGroup prop, Method setter, int index) {
super(prop, "mixed", setter, index);
}

@Override
protected @Nullable Object getValueOrDefault(Object value, Context context) {
Color color = ColorPropConverter.getColorInstance(value, context);
if (color != null) {
return color.pack();
}
return null;
}
}

/*package*/ static Map<String, String> getNativePropsForView(
Class<? extends ViewManager> viewManagerTopClass,
Class<? extends ReactShadowNode> shadowNodeTopClass) {
Expand Down Expand Up @@ -437,6 +486,8 @@ private static PropSetter createPropSetter(
return new ColorPropSetter(annotation, method, annotation.defaultInt());
}
return new IntPropSetter(annotation, method, annotation.defaultInt());
} else if (propTypeClass == long.class && "Color".equals(annotation.customType())) {
return new ColorLongPropSetter(annotation, method, annotation.defaultInt());
} else if (propTypeClass == float.class) {
return new FloatPropSetter(annotation, method, annotation.defaultFloat());
} else if (propTypeClass == double.class) {
Expand All @@ -450,6 +501,8 @@ private static PropSetter createPropSetter(
return new BoxedColorPropSetter(annotation, method);
}
return new BoxedIntPropSetter(annotation, method);
} else if (propTypeClass == Long.class && "Color".equals(annotation.customType())) {
return new BoxedColorLongPropSetter(annotation, method);
} else if (propTypeClass == ReadableArray.class) {
return new ArrayPropSetter(annotation, method);
} else if (propTypeClass == ReadableMap.class) {
Expand Down Expand Up @@ -483,6 +536,10 @@ private static void createPropSetters(
props.put(names[i], new IntPropSetter(annotation, method, i, annotation.defaultInt()));
}
}
} else if (propTypeClass == long.class && "Color".equals(annotation.customType())) {
for (int i = 0; i < names.length; i++) {
props.put(names[i], new ColorLongPropSetter(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()));
Expand All @@ -500,6 +557,10 @@ private static void createPropSetters(
props.put(names[i], new BoxedIntPropSetter(annotation, method, i));
}
}
} else if (propTypeClass == Long.class && "Color".equals(annotation.customType())) {
for (int i = 0; i < names.length; i++) {
props.put(names[i], new BoxedColorLongPropSetter(annotation, method, i));
}
} else {
throw new RuntimeException(
"Unrecognized type: "
Expand Down
Loading

0 comments on commit dbe644f

Please sign in to comment.