Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Add DisplayP3 background and border support to View #43056

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole;
import com.facebook.react.uimanager.ReactAccessibilityDelegate.Role;
import com.facebook.react.uimanager.ReactWideGamutView;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.PointerEventHelper;
import com.facebook.react.uimanager.util.ReactFindViewUtil;
import com.facebook.react.views.view.ReactViewGroup;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -164,14 +166,22 @@ 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) {
if (view instanceof ReactWideGamutView) {
((ReactWideGamutView) view).setBackgroundColor(backgroundColor);
} else {
view.setBackgroundColor(Color.toArgb(backgroundColor));
}
}

@Override
@ReactProp(name = ViewProps.TRANSFORM)
public void setTransform(@NonNull T view, @Nullable ReadableArray matrix) {
Expand Down Expand Up @@ -204,14 +214,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
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,116 @@
package com.facebook.react.uimanager

import java.util.Arrays

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

private val mBorder: LongArray = LongArray(12)
private var mValueFlags = 0
private val mDefaultValue: Long
private var mHasAliasesSet = false

public constructor() : this(0L)

public constructor(defaultValue: Long) {
mDefaultValue = defaultValue
}

public constructor(original: BorderColor) {
mDefaultValue = original.mDefaultValue
System.arraycopy(original.mBorder, 0, mBorder, 0, original.mBorder.size)
mValueFlags = original.mValueFlags
mHasAliasesSet = original.mHasAliasesSet
}

/**
* Set a border value.
*
* @param borderType one of [LEFT], [TOP], [RIGHT], [BOTTOM], [VERTICAL], [HORIZONTAL], [ALL]
* @param value the value for this direction
* @return `true` if the spacing has changed, or `false` if the same value was already set
*/
public fun set(borderType: Int, value: Long): Boolean {
if (mBorder[borderType] != value) {
mBorder[borderType] = value
mValueFlags = mValueFlags or sFlagsMap[borderType]
mHasAliasesSet = mValueFlags and (sFlagsMap[ALL] or sFlagsMap[VERTICAL] or sFlagsMap[HORIZONTAL] or sFlagsMap[BLOCK]) != 0
return true
}
return false
}

/**
* Get the border for a direction. This takes into account any default values that have been set.
*
* @param borderType one of [LEFT], [TOP], [RIGHT], [BOTTOM]
*/
public fun get(borderType: Int): Long {
val defaultValue = if (borderType in setOf(START, END, BLOCK, BLOCK_END, BLOCK_START)) 0 else mDefaultValue

if (mValueFlags == 0) return defaultValue

if (mValueFlags and sFlagsMap[borderType] != 0) return mBorder[borderType]

if (mHasAliasesSet) {
val secondType = if (borderType == TOP || borderType == BOTTOM) VERTICAL else HORIZONTAL
if (mValueFlags and sFlagsMap[secondType] != 0) {
return mBorder[secondType]
} else if (mValueFlags and sFlagsMap[ALL] != 0) {
return mBorder[ALL]
}
}

return defaultValue
}

/**
* Get the raw value (that was set using [set]), without taking into account
* any default values.
*
* @param borderType one of [LEFT], [TOP], [RIGHT], [BOTTOM], [VERTICAL], [HORIZONTAL], [ALL]
*/
public fun getRaw(borderType: Int): Long = mBorder[borderType]

/**
* Resets the border instance to its default state. This method is meant to be used when
* recycling Border instances.
*/
public fun reset() {
Arrays.fill(mBorder, 0)
mValueFlags = 0
mHasAliasesSet = false
}

private companion object {
const val LEFT = 0
const val TOP = 1
const val RIGHT = 2
const val BOTTOM = 3
const val START = 4
const val END = 5
const val HORIZONTAL = 6
const val VERTICAL = 7
const val ALL = 8
const val BLOCK = 9
const val BLOCK_END = 10
const val BLOCK_START = 11

private val sFlagsMap = intArrayOf(
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
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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;

/** View that supports wide gamut color. */
public interface ReactWideGamutView {
public fun setBackgroundColor(color: Long)
}
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
Loading