diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomLetterSpacingSpan.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomLetterSpacingSpan.java index 3b9cf58e33d3a1..d537cd5dccc101 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomLetterSpacingSpan.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomLetterSpacingSpan.java @@ -37,6 +37,10 @@ public void updateMeasureState(TextPaint paint) { apply(paint); } + public float getSpacing() { + return mLetterSpacing; + } + private void apply(TextPaint paint) { if (!Float.isNaN(mLetterSpacing)) { paint.setLetterSpacing(mLetterSpacing); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomStyleSpan.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomStyleSpan.java index b249126cf95750..7866390bfa09bd 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomStyleSpan.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomStyleSpan.java @@ -71,6 +71,10 @@ public int getWeight() { return mFontFamily; } + public @Nullable String getFontFeatureSettings() { + return mFeatureSettings; + } + private static void apply( Paint paint, int style, diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java index f1591a5fe35d2b..82c9f5d75ac517 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.java @@ -31,8 +31,6 @@ public class ReactTextUpdate { private final int mSelectionEnd; private final int mJustificationMode; - public boolean mContainsMultipleFragments; - /** * @deprecated Use a non-deprecated constructor for ReactTextUpdate instead. This one remains * because it's being used by a unit test that isn't currently open source. @@ -142,13 +140,11 @@ public static ReactTextUpdate buildReactTextUpdateFromState( int jsEventCounter, int textAlign, int textBreakStrategy, - int justificationMode, - boolean containsMultipleFragments) { + int justificationMode) { ReactTextUpdate reactTextUpdate = new ReactTextUpdate( text, jsEventCounter, false, textAlign, textBreakStrategy, justificationMode); - reactTextUpdate.mContainsMultipleFragments = containsMultipleFragments; return reactTextUpdate; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index c150abc1500dca..b93aa8eca7d7ec 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -11,6 +11,8 @@ import static com.facebook.react.views.text.TextAttributeProps.UNSET; import android.content.Context; +import android.graphics.Color; +import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; @@ -50,15 +52,19 @@ import com.facebook.react.views.text.CustomLineHeightSpan; import com.facebook.react.views.text.CustomStyleSpan; import com.facebook.react.views.text.ReactAbsoluteSizeSpan; +import com.facebook.react.views.text.ReactBackgroundColorSpan; +import com.facebook.react.views.text.ReactForegroundColorSpan; import com.facebook.react.views.text.ReactSpan; +import com.facebook.react.views.text.ReactStrikethroughSpan; import com.facebook.react.views.text.ReactTextUpdate; import com.facebook.react.views.text.ReactTypefaceUtils; +import com.facebook.react.views.text.ReactUnderlineSpan; import com.facebook.react.views.text.TextAttributes; import com.facebook.react.views.text.TextInlineImageSpan; import com.facebook.react.views.text.TextLayoutManager; import com.facebook.react.views.view.ReactViewBackgroundManager; import java.util.ArrayList; -import java.util.List; +import java.util.Objects; /** * A wrapper around the EditText that lets us better control what happens when an EditText gets @@ -82,7 +88,6 @@ public class ReactEditText extends AppCompatEditText // *TextChanged events should be triggered. This is less expensive than removing the text // listeners and adding them back again after the text change is completed. protected boolean mIsSettingTextFromJS; - protected boolean mIsSettingTextFromCacheUpdate = false; private int mDefaultGravityHorizontal; private int mDefaultGravityVertical; @@ -362,7 +367,7 @@ protected void onSelectionChanged(int selStart, int selEnd) { } super.onSelectionChanged(selStart, selEnd); - if (!mIsSettingTextFromCacheUpdate && mSelectionWatcher != null && hasFocus()) { + if (mSelectionWatcher != null && hasFocus()) { mSelectionWatcher.onSelectionChanged(selStart, selEnd); } } @@ -512,6 +517,14 @@ public void setFontStyle(String fontStyleString) { } } + @Override + public void setFontFeatureSettings(String fontFeatureSettings) { + if (!Objects.equals(fontFeatureSettings, getFontFeatureSettings())) { + super.setFontFeatureSettings(fontFeatureSettings); + mTypefaceDirty = true; + } + } + public void maybeUpdateTypeface() { if (!mTypefaceDirty) { return; @@ -523,6 +536,17 @@ public void maybeUpdateTypeface() { ReactTypefaceUtils.applyStyles( getTypeface(), mFontStyle, mFontWeight, mFontFamily, getContext().getAssets()); setTypeface(newTypeface); + + // Match behavior of CustomStyleSpan and enable SUBPIXEL_TEXT_FLAG when setting anything + // nonstandard + if (mFontStyle != UNSET + || mFontWeight != UNSET + || mFontFamily != null + || getFontFeatureSettings() != null) { + setPaintFlags(getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG); + } else { + setPaintFlags(getPaintFlags() & (~Paint.SUBPIXEL_TEXT_FLAG)); + } } // VisibleForTesting from {@link TextInputEventsTestCase}. @@ -584,10 +608,8 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(reactTextUpdate.getText()); - manageSpans(spannableStringBuilder, reactTextUpdate.mContainsMultipleFragments); - - // Mitigation for https://github.com/facebook/react-native/issues/35936 (S318090) - stripAbsoluteSizeSpans(spannableStringBuilder); + manageSpans(spannableStringBuilder); + stripStyleEquivalentSpans(spannableStringBuilder); mContainsImages = reactTextUpdate.containsImages(); @@ -615,7 +637,7 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) { } // Update cached spans (in Fabric only). - updateCachedSpannable(false); + updateCachedSpannable(); } /** @@ -624,8 +646,7 @@ public void maybeSetText(ReactTextUpdate reactTextUpdate) { * will adapt to the new text, hence why {@link SpannableStringBuilder#replace} never removes * them. */ - private void manageSpans( - SpannableStringBuilder spannableStringBuilder, boolean skipAddSpansForMeasurements) { + private void manageSpans(SpannableStringBuilder spannableStringBuilder) { Object[] spans = getText().getSpans(0, length(), Object.class); for (int spanIdx = 0; spanIdx < spans.length; spanIdx++) { Object span = spans[spanIdx]; @@ -653,33 +674,170 @@ private void manageSpans( spannableStringBuilder.setSpan(span, spanStart, spanEnd, spanFlags); } } + } + + // TODO: Replace with Predicate and lambdas once Java 8 builds in OSS + interface SpanPredicate { + boolean test(T span); + } + + /** + * Remove spans from the SpannableStringBuilder which can be represented by TextAppearance + * attributes on the underlying EditText. This works around instability on Samsung devices with + * the presence of spans https://github.com/facebook/react-native/issues/35936 (S318090) + */ + private void stripStyleEquivalentSpans(SpannableStringBuilder sb) { + stripSpansOfKind( + sb, + ReactAbsoluteSizeSpan.class, + new SpanPredicate() { + @Override + public boolean test(ReactAbsoluteSizeSpan span) { + return span.getSize() == mTextAttributes.getEffectiveFontSize(); + } + }); + + stripSpansOfKind( + sb, + ReactBackgroundColorSpan.class, + new SpanPredicate() { + @Override + public boolean test(ReactBackgroundColorSpan span) { + return span.getBackgroundColor() == mReactBackgroundManager.getBackgroundColor(); + } + }); + + stripSpansOfKind( + sb, + ReactForegroundColorSpan.class, + new SpanPredicate() { + @Override + public boolean test(ReactForegroundColorSpan span) { + return span.getForegroundColor() == getCurrentTextColor(); + } + }); + + stripSpansOfKind( + sb, + ReactStrikethroughSpan.class, + new SpanPredicate() { + @Override + public boolean test(ReactStrikethroughSpan span) { + return (getPaintFlags() & Paint.STRIKE_THRU_TEXT_FLAG) != 0; + } + }); - // In Fabric only, apply necessary styles to entire span - // If the Spannable was constructed from multiple fragments, we don't apply any spans that could - // impact the whole Spannable, because that would override "local" styles per-fragment - if (!skipAddSpansForMeasurements) { - addSpansForMeasurement(getText()); + stripSpansOfKind( + sb, + ReactUnderlineSpan.class, + new SpanPredicate() { + @Override + public boolean test(ReactUnderlineSpan span) { + return (getPaintFlags() & Paint.UNDERLINE_TEXT_FLAG) != 0; + } + }); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + stripSpansOfKind( + sb, + CustomLetterSpacingSpan.class, + new SpanPredicate() { + @Override + public boolean test(CustomLetterSpacingSpan span) { + return span.getSpacing() == mTextAttributes.getEffectiveLetterSpacing(); + } + }); } + + stripSpansOfKind( + sb, + CustomStyleSpan.class, + new SpanPredicate() { + @Override + public boolean test(CustomStyleSpan span) { + return span.getStyle() == mFontStyle + && Objects.equals(span.getFontFamily(), mFontFamily) + && span.getWeight() == mFontWeight + && Objects.equals(span.getFontFeatureSettings(), getFontFeatureSettings()); + } + }); } - private void stripAbsoluteSizeSpans(SpannableStringBuilder sb) { - // We have already set a font size on the EditText itself. We can safely remove sizing spans - // which are the same as the set font size, and not otherwise overlapped. - final int effectiveFontSize = mTextAttributes.getEffectiveFontSize(); - ReactAbsoluteSizeSpan[] spans = sb.getSpans(0, sb.length(), ReactAbsoluteSizeSpan.class); + private void stripSpansOfKind( + SpannableStringBuilder sb, Class clazz, SpanPredicate shouldStrip) { + T[] spans = sb.getSpans(0, sb.length(), clazz); - outerLoop: - for (ReactAbsoluteSizeSpan span : spans) { - ReactAbsoluteSizeSpan[] overlappingSpans = - sb.getSpans(sb.getSpanStart(span), sb.getSpanEnd(span), ReactAbsoluteSizeSpan.class); + for (T span : spans) { + if (shouldStrip.test(span)) { + sb.removeSpan(span); + } + } + } - for (ReactAbsoluteSizeSpan overlappingSpan : overlappingSpans) { - if (span.getSize() != effectiveFontSize) { - continue outerLoop; - } + /** + * Copy styles represented as attributes to the underlying span, for later measurement or other + * usage outside the ReactEditText. + */ + private void addSpansFromStyleAttributes(SpannableStringBuilder workingText) { + int spanFlags = Spannable.SPAN_INCLUSIVE_INCLUSIVE; + + // Set all bits for SPAN_PRIORITY so that this span has the highest possible priority + // (least precedence). This ensures the span is behind any overlapping spans. + spanFlags |= Spannable.SPAN_PRIORITY; + + workingText.setSpan( + new ReactAbsoluteSizeSpan(mTextAttributes.getEffectiveFontSize()), + 0, + workingText.length(), + spanFlags); + + workingText.setSpan( + new ReactForegroundColorSpan(getCurrentTextColor()), 0, workingText.length(), spanFlags); + + int backgroundColor = mReactBackgroundManager.getBackgroundColor(); + if (backgroundColor != Color.TRANSPARENT) { + workingText.setSpan( + new ReactBackgroundColorSpan(backgroundColor), 0, workingText.length(), spanFlags); + } + + if ((getPaintFlags() & Paint.STRIKE_THRU_TEXT_FLAG) != 0) { + workingText.setSpan(new ReactStrikethroughSpan(), 0, workingText.length(), spanFlags); + } + + if ((getPaintFlags() & Paint.UNDERLINE_TEXT_FLAG) != 0) { + workingText.setSpan(new ReactUnderlineSpan(), 0, workingText.length(), spanFlags); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + float effectiveLetterSpacing = mTextAttributes.getEffectiveLetterSpacing(); + if (!Float.isNaN(effectiveLetterSpacing)) { + workingText.setSpan( + new CustomLetterSpacingSpan(effectiveLetterSpacing), + 0, + workingText.length(), + spanFlags); } + } + + if (mFontStyle != UNSET + || mFontWeight != UNSET + || mFontFamily != null + || getFontFeatureSettings() != null) { + workingText.setSpan( + new CustomStyleSpan( + mFontStyle, + mFontWeight, + getFontFeatureSettings(), + mFontFamily, + getContext().getAssets()), + 0, + workingText.length(), + spanFlags); + } - sb.removeSpan(span); + float lineHeight = mTextAttributes.getEffectiveLineHeight(); + if (!Float.isNaN(lineHeight)) { + workingText.setSpan(new CustomLineHeightSpan(lineHeight), 0, workingText.length(), spanFlags); } } @@ -699,73 +857,6 @@ private static boolean sameTextForSpan( return true; } - // This is hacked in for Fabric. When we delete non-Fabric code, we might be able to simplify or - // clean this up a bit. - private void addSpansForMeasurement(Spannable spannable) { - if (!mFabricViewStateManager.hasStateWrapper()) { - return; - } - - boolean originalDisableTextDiffing = mDisableTextDiffing; - mDisableTextDiffing = true; - - int start = 0; - int end = spannable.length(); - - // Remove duplicate spans we might add here - Object[] spans = spannable.getSpans(0, length(), Object.class); - for (Object span : spans) { - int spanFlags = spannable.getSpanFlags(span); - boolean isInclusive = - (spanFlags & Spanned.SPAN_INCLUSIVE_INCLUSIVE) == Spanned.SPAN_INCLUSIVE_INCLUSIVE - || (spanFlags & Spanned.SPAN_INCLUSIVE_EXCLUSIVE) == Spanned.SPAN_INCLUSIVE_EXCLUSIVE; - if (isInclusive - && span instanceof ReactSpan - && spannable.getSpanStart(span) == start - && spannable.getSpanEnd(span) == end) { - spannable.removeSpan(span); - } - } - - List ops = new ArrayList<>(); - - if (!Float.isNaN(mTextAttributes.getLetterSpacing())) { - ops.add( - new TextLayoutManager.SetSpanOperation( - start, end, new CustomLetterSpacingSpan(mTextAttributes.getLetterSpacing()))); - } - ops.add( - new TextLayoutManager.SetSpanOperation( - start, end, new ReactAbsoluteSizeSpan((int) mTextAttributes.getEffectiveFontSize()))); - if (mFontStyle != UNSET || mFontWeight != UNSET || mFontFamily != null) { - ops.add( - new TextLayoutManager.SetSpanOperation( - start, - end, - new CustomStyleSpan( - mFontStyle, - mFontWeight, - null, // TODO: do we need to support FontFeatureSettings / fontVariant? - mFontFamily, - getReactContext(ReactEditText.this).getAssets()))); - } - if (!Float.isNaN(mTextAttributes.getEffectiveLineHeight())) { - ops.add( - new TextLayoutManager.SetSpanOperation( - start, end, new CustomLineHeightSpan(mTextAttributes.getEffectiveLineHeight()))); - } - - int priority = 0; - for (TextLayoutManager.SetSpanOperation op : ops) { - // Actual order of calling {@code execute} does NOT matter, - // but the {@code priority} DOES matter. - op.execute(spannable, priority); - priority++; - } - - mDisableTextDiffing = originalDisableTextDiffing; - } - protected boolean showSoftKeyboard() { return mInputMethodManager.showSoftInput(this, 0); } @@ -1030,7 +1121,9 @@ protected void applyTextAttributes() { float effectiveLetterSpacing = mTextAttributes.getEffectiveLetterSpacing(); if (!Float.isNaN(effectiveLetterSpacing)) { - setLetterSpacing(effectiveLetterSpacing); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + setLetterSpacing(effectiveLetterSpacing); + } } } @@ -1045,7 +1138,7 @@ public FabricViewStateManager getFabricViewStateManager() { * TextLayoutManager.java with some very minor modifications. There's some duplication between * here and TextLayoutManager, so there might be an opportunity for refactor. */ - private void updateCachedSpannable(boolean resetStyles) { + private void updateCachedSpannable() { // Noops in non-Fabric if (mFabricViewStateManager == null || !mFabricViewStateManager.hasStateWrapper()) { return; @@ -1055,12 +1148,6 @@ private void updateCachedSpannable(boolean resetStyles) { return; } - if (resetStyles) { - mIsSettingTextFromCacheUpdate = true; - addSpansForMeasurement(getText()); - mIsSettingTextFromCacheUpdate = false; - } - Editable currentText = getText(); boolean haveText = currentText != null && currentText.length() > 0; @@ -1118,11 +1205,9 @@ private void updateCachedSpannable(boolean resetStyles) { // Measure something so we have correct height, even if there's no string. sb.append("I"); } - - // Make sure that all text styles are applied when we're measurable the hint or "blank" text - addSpansForMeasurement(sb); } + addSpansFromStyleAttributes(sb); TextLayoutManager.setCachedSpannabledForTag(getId(), sb); } @@ -1137,7 +1222,7 @@ void setEventDispatcher(@Nullable EventDispatcher eventDispatcher) { private class TextWatcherDelegator implements TextWatcher { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { - if (!mIsSettingTextFromCacheUpdate && !mIsSettingTextFromJS && mListeners != null) { + if (!mIsSettingTextFromJS && mListeners != null) { for (TextWatcher listener : mListeners) { listener.beforeTextChanged(s, start, count, after); } @@ -1151,23 +1236,20 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { TAG, "onTextChanged[" + getId() + "]: " + s + " " + start + " " + before + " " + count); } - if (!mIsSettingTextFromCacheUpdate) { - if (!mIsSettingTextFromJS && mListeners != null) { - for (TextWatcher listener : mListeners) { - listener.onTextChanged(s, start, before, count); - } + if (!mIsSettingTextFromJS && mListeners != null) { + for (TextWatcher listener : mListeners) { + listener.onTextChanged(s, start, before, count); } - - updateCachedSpannable( - !mIsSettingTextFromJS && !mIsSettingTextFromState && start == 0 && before == 0); } + updateCachedSpannable(); + onContentSizeChange(); } @Override public void afterTextChanged(Editable s) { - if (!mIsSettingTextFromCacheUpdate && !mIsSettingTextFromJS && mListeners != null) { + if (!mIsSettingTextFromJS && mListeners != null) { for (TextWatcher listener : mListeners) { listener.afterTextChanged(s); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index 85fb317a2a9d9d..f4451acede3c32 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -13,6 +13,7 @@ import android.content.res.ColorStateList; import android.graphics.BlendMode; import android.graphics.BlendModeColorFilter; +import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Build; @@ -67,6 +68,7 @@ import com.facebook.react.views.text.ReactBaseTextShadowNode; import com.facebook.react.views.text.ReactTextUpdate; import com.facebook.react.views.text.ReactTextViewManagerCallback; +import com.facebook.react.views.text.ReactTypefaceUtils; import com.facebook.react.views.text.TextAttributeProps; import com.facebook.react.views.text.TextInlineImageSpan; import com.facebook.react.views.text.TextLayoutManager; @@ -404,6 +406,11 @@ public void setFontStyle(ReactEditText view, @Nullable String fontStyle) { view.setFontStyle(fontStyle); } + @ReactProp(name = ViewProps.FONT_VARIANT) + public void setFontVariant(ReactEditText view, @Nullable ReadableArray fontVariant) { + view.setFontFeatureSettings(ReactTypefaceUtils.parseFontVariant(fontVariant)); + } + @ReactProp(name = ViewProps.INCLUDE_FONT_PADDING, defaultBoolean = true) public void setIncludeFontPadding(ReactEditText view, boolean includepad) { view.setIncludeFontPadding(includepad); @@ -925,6 +932,20 @@ public void setAutoFocus(ReactEditText view, boolean autoFocus) { view.setAutoFocus(autoFocus); } + @ReactProp(name = ViewProps.TEXT_DECORATION_LINE) + public void setTextDecorationLine(ReactEditText view, @Nullable String textDecorationLineString) { + view.setPaintFlags( + view.getPaintFlags() & ~(Paint.STRIKE_THRU_TEXT_FLAG | Paint.UNDERLINE_TEXT_FLAG)); + + for (String token : textDecorationLineString.split(" ")) { + if (token.equals("underline")) { + view.setPaintFlags(view.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); + } else if (token.equals("line-through")) { + view.setPaintFlags(view.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + } + } + } + @ReactPropGroup( names = { ViewProps.BORDER_WIDTH, @@ -1322,9 +1343,6 @@ public Object updateState( TextLayoutManager.getOrCreateSpannableForText( view.getContext(), attributedString, mReactTextViewManagerCallback); - boolean containsMultipleFragments = - attributedString.getArray("fragments").toArrayList().size() > 1; - int textBreakStrategy = TextAttributeProps.getTextBreakStrategy(paragraphAttributes.getString("textBreakStrategy")); @@ -1333,8 +1351,7 @@ public Object updateState( state.getInt("mostRecentEventCount"), TextAttributeProps.getTextAlignment(props, TextLayoutManager.isRTL(attributedString)), textBreakStrategy, - TextAttributeProps.getJustificationMode(props), - containsMultipleFragments); + TextAttributeProps.getJustificationMode(props, currentJustificationMode)); } public Object getReactTextUpdate(ReactEditText view, ReactStylesDiffMap props, MapBuffer state) { @@ -1355,9 +1372,6 @@ public Object getReactTextUpdate(ReactEditText view, ReactStylesDiffMap props, M TextLayoutManagerMapBuffer.getOrCreateSpannableForText( view.getContext(), attributedString, mReactTextViewManagerCallback); - boolean containsMultipleFragments = - attributedString.getMapBuffer(TextLayoutManagerMapBuffer.AS_KEY_FRAGMENTS).getCount() > 1; - int textBreakStrategy = TextAttributeProps.getTextBreakStrategy( paragraphAttributes.getString(TextLayoutManagerMapBuffer.PA_KEY_TEXT_BREAK_STRATEGY)); @@ -1368,7 +1382,6 @@ public Object getReactTextUpdate(ReactEditText view, ReactStylesDiffMap props, M TextAttributeProps.getTextAlignment( props, TextLayoutManagerMapBuffer.isRTL(attributedString)), textBreakStrategy, - TextAttributeProps.getJustificationMode(props), - containsMultipleFragments); + TextAttributeProps.getJustificationMode(props, currentJustificationMode)); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundManager.java index 4a5fce52c3a9e5..9b479fa42214be 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundManager.java @@ -19,6 +19,7 @@ public class ReactViewBackgroundManager { private @Nullable ReactViewBackgroundDrawable mReactBackgroundDrawable; private View mView; + private int mColor = Color.TRANSPARENT; public ReactViewBackgroundManager(View view) { this.mView = view; @@ -56,6 +57,10 @@ public void setBackgroundColor(int color) { } } + public int getBackgroundColor() { + return mColor; + } + public void setBorderWidth(int position, float width) { getOrCreateReactViewBackground().setBorderWidth(position, width); }