diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 94e3879b76a608..924307ea4032db 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -6133,44 +6133,17 @@ public final class com/facebook/react/views/text/ReactTextShadowNode : com/faceb public final fun setShouldNotifyOnTextLayout (Z)V } -public final class com/facebook/react/views/text/ReactTextUpdate { - public static final field Companion Lcom/facebook/react/views/text/ReactTextUpdate$Companion; - public fun (Landroid/text/Spannable;IZFFFFI)V - public fun (Landroid/text/Spannable;IZFFFFIII)V - public fun (Landroid/text/Spannable;IZIII)V - public static final fun buildReactTextUpdateFromState (Landroid/text/Spannable;IIII)Lcom/facebook/react/views/text/ReactTextUpdate; - public final fun containsImages ()Z - public final fun getContainsImages ()Z - public final fun getJsEventCounter ()I - public final fun getJustificationMode ()I - public final fun getPaddingBottom ()F - public final fun getPaddingLeft ()F - public final fun getPaddingRight ()F - public final fun getPaddingTop ()F - public final fun getText ()Landroid/text/Spannable; - public final fun getTextAlign ()I - public final fun getTextBreakStrategy ()I -} - -public final class com/facebook/react/views/text/ReactTextUpdate$Companion { - public final fun buildReactTextUpdateFromState (Landroid/text/Spannable;IIII)Lcom/facebook/react/views/text/ReactTextUpdate; -} - public class com/facebook/react/views/text/ReactTextView : androidx/appcompat/widget/AppCompatTextView, com/facebook/react/uimanager/ReactCompoundView { public fun (Landroid/content/Context;)V protected fun dispatchHoverEvent (Landroid/view/MotionEvent;)Z public fun dispatchKeyEvent (Landroid/view/KeyEvent;)Z public fun getSpanned ()Landroid/text/Spannable; public fun hasOverlappingRendering ()Z - public fun invalidateDrawable (Landroid/graphics/drawable/Drawable;)V public fun onAttachedToWindow ()V - public fun onDetachedFromWindow ()V protected fun onDraw (Landroid/graphics/Canvas;)V - public fun onFinishTemporaryDetach ()V public final fun onFocusChanged (ZILandroid/graphics/Rect;)V protected fun onLayout (ZIIII)V protected fun onMeasure (II)V - public fun onStartTemporaryDetach ()V public fun reactTagForTouch (FF)I public fun setAdjustFontSizeToFit (Z)V public fun setBackgroundColor (I)V @@ -6193,7 +6166,6 @@ public class com/facebook/react/views/text/ReactTextView : androidx/appcompat/wi public fun setText (Lcom/facebook/react/views/text/ReactTextUpdate;)V public fun setTextIsSelectable (Z)V public fun updateView ()V - protected fun verifyDrawable (Landroid/graphics/drawable/Drawable;)Z } public final class com/facebook/react/views/text/ReactTextViewManager : com/facebook/react/views/text/ReactTextAnchorViewManager, com/facebook/react/uimanager/IViewManagerWithChildren, com/facebook/react/views/text/ReactTextViewManagerCallback { @@ -6344,7 +6316,6 @@ public class com/facebook/react/views/textinput/ReactEditText : androidx/appcomp public final fun canUpdateWithEventCount (I)Z protected final fun finalize ()V public final fun getBorderColor (I)I - protected final fun getContainsImages ()Z public final fun getDisableFullscreenUI ()Z public final fun getDragAndDropFilter ()Ljava/util/List; protected final fun getNativeEventCount ()I @@ -6354,27 +6325,21 @@ public class com/facebook/react/views/textinput/ReactEditText : androidx/appcomp public final fun getSubmitBehavior ()Ljava/lang/String; protected final fun hideSoftKeyboard ()V public final fun incrementAndGetEventCounter ()I - public fun invalidateDrawable (Landroid/graphics/drawable/Drawable;)V public fun isLayoutRequested ()Z protected final fun isSettingTextFromJS ()Z protected final fun isSettingTextFromState ()Z public final fun maybeSetSelection (III)V - public final fun maybeSetTextFromJS (Lcom/facebook/react/views/text/ReactTextUpdate;)V - public final fun maybeSetTextFromState (Lcom/facebook/react/views/text/ReactTextUpdate;)V public final fun maybeUpdateTypeface ()V public fun onAttachedToWindow ()V public fun onConfigurationChanged (Landroid/content/res/Configuration;)V public fun onCreateInputConnection (Landroid/view/inputmethod/EditorInfo;)Landroid/view/inputmethod/InputConnection; - public fun onDetachedFromWindow ()V public fun onDragEvent (Landroid/view/DragEvent;)Z public fun onDraw (Landroid/graphics/Canvas;)V - public fun onFinishTemporaryDetach ()V protected fun onFocusChanged (ZILandroid/graphics/Rect;)V public fun onKeyUp (ILandroid/view/KeyEvent;)Z protected fun onLayout (ZIIII)V protected fun onScrollChanged (IIII)V protected fun onSelectionChanged (II)V - public fun onStartTemporaryDetach ()V public fun onTextContextMenuItem (I)Z public fun onTouchEvent (Landroid/view/MotionEvent;)Z public fun removeTextChangedListener (Landroid/text/TextWatcher;)V @@ -6387,7 +6352,6 @@ public class com/facebook/react/views/textinput/ReactEditText : androidx/appcomp public final fun setBorderRadius (FI)V public final fun setBorderStyle (Ljava/lang/String;)V public final fun setBorderWidth (IF)V - protected final fun setContainsImages (Z)V public final fun setContentSizeWatcher (Lcom/facebook/react/views/textinput/ContentSizeWatcher;)V public final fun setContextMenuHidden (Z)V public final fun setDisableFullscreenUI (Z)V @@ -6418,7 +6382,6 @@ public class com/facebook/react/views/textinput/ReactEditText : androidx/appcomp public final fun shouldBlurOnReturn ()Z public final fun shouldSubmitOnReturn ()Z protected final fun showSoftKeyboard ()Z - protected fun verifyDrawable (Landroid/graphics/drawable/Drawable;)Z } public final class com/facebook/react/views/textinput/ReactEditText$Companion { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.kt index 64452e80f3d2c7..9927bf571536e5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.kt @@ -58,7 +58,6 @@ import com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager import com.facebook.react.views.switchview.ReactSwitchManager import com.facebook.react.views.text.PreparedLayoutTextViewManager import com.facebook.react.views.text.ReactTextViewManager -import com.facebook.react.views.text.frescosupport.FrescoBasedReactTextInlineImageViewManager import com.facebook.react.views.textinput.ReactTextInputManager import com.facebook.react.views.unimplementedview.ReactUnimplementedViewManager import com.facebook.react.views.view.ReactViewManager @@ -146,7 +145,6 @@ constructor(private val config: MainPackageConfig? = null) : ReactSafeAreaViewManager(), SwipeRefreshLayoutManager(), // Native equivalents - FrescoBasedReactTextInlineImageViewManager(), ReactImageManager(), ReactModalHostManager(), ReactTextInputManager(), @@ -183,8 +181,6 @@ constructor(private val config: MainPackageConfig? = null) : ReactSwitchManager.REACT_CLASS to ModuleSpec.viewManagerSpec { ReactSwitchManager() }, SwipeRefreshLayoutManager.REACT_CLASS to ModuleSpec.viewManagerSpec { SwipeRefreshLayoutManager() }, - FrescoBasedReactTextInlineImageViewManager.REACT_CLASS to - ModuleSpec.viewManagerSpec { FrescoBasedReactTextInlineImageViewManager() }, ReactImageManager.REACT_CLASS to ModuleSpec.viewManagerSpec { ReactImageManager() }, ReactModalHostManager.REACT_CLASS to ModuleSpec.viewManagerSpec { ReactModalHostManager() }, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.kt index e882fb22e22b87..dad6ce9aa1c547 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextUpdate.kt @@ -7,80 +7,20 @@ package com.facebook.react.views.text -import android.text.Layout -import android.text.Spannable -import com.facebook.react.common.ReactConstants +import android.text.Spanned /** Class that contains the data needed for a text update. Used by both and . */ -public class ReactTextUpdate( - public val text: Spannable, +internal class ReactTextUpdate( + public val text: Spanned, public val jsEventCounter: Int, - public val containsImages: Boolean, - public val paddingLeft: Float, - public val paddingTop: Float, - public val paddingRight: Float, - public val paddingBottom: Float, public val textAlign: Int, public val textBreakStrategy: Int, public val justificationMode: Int, ) { - - /** - * @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. - */ - public constructor( - text: Spannable, - jsEventCounter: Int, - containsImages: Boolean, - paddingStart: Float, - paddingTop: Float, - paddingEnd: Float, - paddingBottom: Float, - textAlign: Int, - ) : this( - text, - jsEventCounter, - containsImages, - paddingStart, - paddingTop, - paddingEnd, - paddingBottom, - textAlign, - Layout.BREAK_STRATEGY_HIGH_QUALITY, - Layout.JUSTIFICATION_MODE_NONE, - ) - - public constructor( - text: Spannable, - jsEventCounter: Int, - containsImages: Boolean, - textAlign: Int, - textBreakStrategy: Int, - justificationMode: Int, - ) : this( - text, - jsEventCounter, - containsImages, - ReactConstants.UNSET.toFloat(), - ReactConstants.UNSET.toFloat(), - ReactConstants.UNSET.toFloat(), - ReactConstants.UNSET.toFloat(), - textAlign, - textBreakStrategy, - justificationMode, - ) - - @Deprecated( - "This is just for backwards compatibility and will be removed some time in the future", - ReplaceWith("containsImages"), - ) - public fun containsImages(): Boolean = containsImages - public companion object { @JvmStatic public fun buildReactTextUpdateFromState( - text: Spannable, + text: Spanned, jsEventCounter: Int, textAlign: Int, textBreakStrategy: Int, @@ -89,7 +29,6 @@ public class ReactTextUpdate( ReactTextUpdate( text, jsEventCounter, - false, textAlign, textBreakStrategy, justificationMode, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java index 906dfbce04905d..5e3cf945fdacdb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java @@ -10,10 +10,10 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Layout; import android.text.Spannable; +import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; import android.text.method.LinkMovementMethod; @@ -53,7 +53,6 @@ import com.facebook.react.uimanager.style.LogicalEdge; import com.facebook.react.uimanager.style.Overflow; import com.facebook.react.views.text.internal.span.ReactTagSpan; -import com.facebook.react.views.text.internal.span.TextInlineImageSpan; import com.facebook.react.views.text.internal.span.TextInlineViewPlaceholderSpan; import com.facebook.yoga.YogaMeasureMode; @@ -66,7 +65,6 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie // https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/widget/TextView.java#L854 private static final int DEFAULT_GRAVITY = Gravity.TOP | Gravity.START; - private boolean mContainsImages; private int mNumberOfLines; private @Nullable TextUtils.TruncateAt mEllipsizeLocation; private boolean mAdjustsFontSizeToFit; @@ -377,38 +375,21 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { public void setText(ReactTextUpdate update) { try (SystraceSection s = new SystraceSection("ReactTextView.setText(ReactTextUpdate)")) { - mContainsImages = update.containsImages(); // Android's TextView crashes when it tries to relayout if LayoutParams are // null; explicitly set the LayoutParams to prevent this crash. See: // https://github.com/facebook/react-native/pull/7011 if (getLayoutParams() == null) { setLayoutParams(EMPTY_LAYOUT_PARAMS); } - Spannable spannable = update.getText(); + Spanned spanned = update.getText(); if (mLinkifyMaskType > 0) { - Linkify.addLinks(spannable, mLinkifyMaskType); + if (!(spanned instanceof Spannable)) { + spanned = new SpannableString(spanned); + } + Linkify.addLinks((Spannable) spanned, mLinkifyMaskType); setMovementMethod(LinkMovementMethod.getInstance()); } - setText(spannable); - float paddingLeft = update.getPaddingLeft(); - float paddingTop = update.getPaddingTop(); - float paddingRight = update.getPaddingRight(); - float paddingBottom = update.getPaddingBottom(); - - // In Fabric padding is set by the update of Layout Metrics and not as part of the "setText" - // operation - // TODO T56559197: remove this condition when we migrate 100% to Fabric - if (paddingLeft != ReactConstants.UNSET - && paddingTop != ReactConstants.UNSET - && paddingRight != ReactConstants.UNSET - && paddingBottom != ReactConstants.UNSET) { - - setPadding( - (int) Math.floor(paddingLeft), - (int) Math.floor(paddingTop), - (int) Math.floor(paddingRight), - (int) Math.floor(paddingBottom)); - } + setText(spanned); int nextTextAlign = update.getTextAlign(); if (nextTextAlign != getGravityHorizontal()) { @@ -481,58 +462,6 @@ public int reactTagForTouch(float touchX, float touchY) { return target; } - @Override - protected boolean verifyDrawable(Drawable drawable) { - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); - TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); - for (TextInlineImageSpan span : spans) { - if (span.getDrawable() == drawable) { - return true; - } - } - } - return super.verifyDrawable(drawable); - } - - @Override - public void invalidateDrawable(Drawable drawable) { - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); - TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); - for (TextInlineImageSpan span : spans) { - if (span.getDrawable() == drawable) { - invalidate(); - } - } - } - super.invalidateDrawable(drawable); - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); - TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); - for (TextInlineImageSpan span : spans) { - span.onDetachedFromWindow(); - } - } - } - - @Override - public void onStartTemporaryDetach() { - super.onStartTemporaryDetach(); - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); - TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); - for (TextInlineImageSpan span : spans) { - span.onStartTemporaryDetach(); - } - } - } - @Override public void setTextIsSelectable(boolean selectable) { mTextIsSelectable = selectable; @@ -551,26 +480,6 @@ public void onAttachedToWindow() { } else { setTextIsSelectable(false); } - - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); - TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); - for (TextInlineImageSpan span : spans) { - span.onAttachedToWindow(); - } - } - } - - @Override - public void onFinishTemporaryDetach() { - super.onFinishTemporaryDetach(); - if (mContainsImages && getText() instanceof Spanned) { - Spanned text = (Spanned) getText(); - TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class); - for (TextInlineImageSpan span : spans) { - span.onFinishTemporaryDetach(); - } - } } @Override diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt index 180f1059463114..26630b47873044 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt @@ -9,6 +9,7 @@ package com.facebook.react.views.text import android.os.Build import android.text.Spannable +import android.text.Spanned import com.facebook.react.R import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.common.mapbuffer.MapBuffer @@ -20,7 +21,6 @@ import com.facebook.react.uimanager.ReactStylesDiffMap import com.facebook.react.uimanager.StateWrapper import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.annotations.ReactProp -import com.facebook.react.views.text.internal.span.TextInlineImageSpan import java.util.HashMap /** @@ -75,17 +75,13 @@ public constructor( override fun updateExtraData(view: ReactTextView, extraData: Any) { SystraceSection("ReactTextViewManager.updateExtraData").use { s -> val update = extraData as ReactTextUpdate - val spannable: Spannable = update.text - @Suppress("DEPRECATION") - if (update.containsImages()) { - TextInlineImageSpan.possiblyUpdateInlineImageSpans(spannable, view) - } + val spanned: Spanned = update.text view.setText(update) // If this text view contains any clickable spans, set a view tag and reset the accessibility // delegate so that these can be picked up by the accessibility system. val accessibilityLinks: ReactTextViewAccessibilityDelegate.AccessibilityLinks = - ReactTextViewAccessibilityDelegate.AccessibilityLinks(spannable) + ReactTextViewAccessibilityDelegate.AccessibilityLinks(spanned) view.setTag( R.id.accessibility_links, if (accessibilityLinks.size() > 0) accessibilityLinks else null, @@ -159,7 +155,6 @@ public constructor( return ReactTextUpdate( spanned, -1, // UNUSED FOR TEXT - false, // TODO add this into local Data TextLayoutManager.getTextGravity(attributedString, spanned), textBreakStrategy, TextAttributeProps.getJustificationMode(props, currentJustificationMode), diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.kt deleted file mode 100644 index cb6db4754349c6..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.kt +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -@file:Suppress("DEPRECATION") - -package com.facebook.react.views.text.frescosupport - -import android.content.Context -import android.net.Uri -import com.facebook.common.logging.FLog -import com.facebook.common.util.UriUtil -import com.facebook.drawee.controller.AbstractDraweeControllerBuilder -import com.facebook.imagepipeline.request.ImageRequest -import com.facebook.react.bridge.Dynamic -import com.facebook.react.bridge.ReadableArray -import com.facebook.react.bridge.ReadableMap -import com.facebook.react.bridge.ReadableType -import com.facebook.react.common.ReactConstants -import com.facebook.react.common.annotations.internal.LegacyArchitecture -import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel -import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger -import com.facebook.react.uimanager.ViewProps -import com.facebook.react.uimanager.annotations.ReactProp -import com.facebook.react.views.text.internal.ReactTextInlineImageShadowNode -import com.facebook.react.views.text.internal.span.TextInlineImageSpan -import com.facebook.yoga.YogaConstants -import java.util.Locale - -/** Shadow node that represents an inline image. Loading is done using Fresco. */ -@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR) -@Deprecated( - message = "This class is part of Legacy Architecture and will be removed in a future release", - level = DeprecationLevel.WARNING, -) -internal class FrescoBasedReactTextInlineImageShadowNode( - private val draweeControllerBuilder: AbstractDraweeControllerBuilder<*, ImageRequest, *, *>, - private val callerContext: Any?, -) : ReactTextInlineImageShadowNode() { - - private var uri: Uri? = null - private var headers: ReadableMap? = null - private var width = YogaConstants.UNDEFINED - private var resizeMode: String? = null - private var height = YogaConstants.UNDEFINED - private var tintColor = 0 - - @ReactProp(name = "src") - fun setSource(sources: ReadableArray?) { - val source = - if (sources == null || sources.size() == 0 || sources.getType(0) != ReadableType.Map) null - else checkNotNull(sources.getMap(0)).getString("uri") - var tempUri: Uri? = null - if (source != null) { - try { - tempUri = Uri.parse(source) - // Verify scheme is set, so that relative uri (used by static resources) are not handled. - if (tempUri.scheme == null) { - tempUri = null - } - } catch (e: Exception) { - // ignore malformed uri, then attempt to extract resource ID. - } - if (tempUri == null) { - tempUri = getResourceDrawableUri(themedContext, source) - } - } - if (tempUri != uri) { - markUpdated() - } - uri = tempUri - } - - @ReactProp(name = "headers") - fun setHeaders(newHeaders: ReadableMap?) { - headers = newHeaders - } - - @ReactProp(name = "tintColor", customType = "Color") - fun setTintColor(newTintColor: Int) { - tintColor = newTintColor - } - - /** Besides width/height, all other layout props on inline images are ignored */ - override fun setWidth(newWidth: Dynamic) { - if (newWidth.type == ReadableType.Number) { - width = newWidth.asDouble().toFloat() - } else { - FLog.w(ReactConstants.TAG, "Inline images must not have percentage based width") - width = YogaConstants.UNDEFINED - } - } - - override fun setHeight(newHeight: Dynamic) { - if (newHeight.type == ReadableType.Number) { - height = newHeight.asDouble().toFloat() - } else { - FLog.w(ReactConstants.TAG, "Inline images must not have percentage based height") - height = YogaConstants.UNDEFINED - } - } - - @ReactProp(name = ViewProps.RESIZE_MODE) - fun setResizeMode(newResizeMode: String?) { - resizeMode = newResizeMode - } - - fun getUri(): Uri? = uri - - fun getHeaders(): ReadableMap? = headers - - override fun isVirtual(): Boolean = true - - override fun buildInlineImageSpan(): TextInlineImageSpan { - val resources = themedContext.resources - val finalWidth = Math.ceil(width.toDouble()).toInt() - val finalHeight = Math.ceil(height.toDouble()).toInt() - return FrescoBasedReactTextInlineImageSpan( - resources, - finalHeight, - finalWidth, - tintColor, - getUri(), - getHeaders(), - getDraweeControllerBuilder(), - getCallerContext(), - resizeMode, - ) - } - - fun getDraweeControllerBuilder() = draweeControllerBuilder - - fun getCallerContext(): Any? = callerContext - - // TODO: t9053573 is tracking that this code should be shared - companion object { - fun getResourceDrawableUri(context: Context, name: String?): Uri? { - if (name == null || name.isEmpty()) { - return null - } - val formattedName = name.lowercase(Locale.getDefault()).replace("-", "_") - val resId = context.resources.getIdentifier(formattedName, "drawable", context.packageName) - return Uri.Builder().scheme(UriUtil.LOCAL_RESOURCE_SCHEME).path(resId.toString()).build() - } - - init { - LegacyArchitectureLogger.assertLegacyArchitecture( - "FrescoBasedReactTextInlineImageShadowNode", - LegacyArchitectureLogLevel.ERROR, - ) - } - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageSpan.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageSpan.kt deleted file mode 100644 index 117a7020322405..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageSpan.kt +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.text.frescosupport - -import android.content.res.Resources -import android.graphics.BlendMode -import android.graphics.BlendModeColorFilter -import android.graphics.Canvas -import android.graphics.Paint -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter -import android.graphics.drawable.Drawable -import android.net.Uri -import android.os.Build -import android.widget.TextView -import com.facebook.drawee.controller.AbstractDraweeControllerBuilder -import com.facebook.drawee.generic.GenericDraweeHierarchy -import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder -import com.facebook.drawee.view.DraweeHolder -import com.facebook.imagepipeline.request.ImageRequest -import com.facebook.imagepipeline.request.ImageRequestBuilder -import com.facebook.react.bridge.ReadableMap -import com.facebook.react.modules.fresco.ReactNetworkImageRequest -import com.facebook.react.uimanager.PixelUtil -import com.facebook.react.views.image.ImageResizeMode -import com.facebook.react.views.text.internal.span.TextInlineImageSpan - -/** - * FrescoBasedTextInlineImageSpan is a span for Images that are inside . It computes its size - * based on the input size. When it is time to draw, it will use the Fresco framework to get the - * right Drawable and let that draw. - * - * Since Fresco needs to callback to the TextView that contains this, in the ViewManager, you must - * tell the Span about the TextView - * - * Note: It borrows code from DynamicDrawableSpan and if that code updates how it computes size or - * draws, we need to update this as well. - */ -internal class FrescoBasedReactTextInlineImageSpan( - resources: Resources, - height: Int, - width: Int, - private val tintColor: Int, - uri: Uri?, - private val headers: ReadableMap?, - private val draweeControllerBuilder: AbstractDraweeControllerBuilder<*, ImageRequest, *, *>, - private val callerContext: Any?, - private val resizeMode: String?, -) : TextInlineImageSpan() { - - private var textView: TextView? = null - private val _uri: Uri = uri ?: Uri.EMPTY - private val _width: Int = PixelUtil.toPixelFromDIP(width.toDouble()).toInt() - private val _height: Int = PixelUtil.toPixelFromDIP(height.toDouble()).toInt() - private val draweeHolder: DraweeHolder = - DraweeHolder(GenericDraweeHierarchyBuilder.newInstance(resources).build()) - - override val width: Int - get() = _width - - override val height: Int - get() = _height - - override var drawable: Drawable? = null - private set - - /** - * The ReactTextView that holds this ImageSpan is responsible for passing these methods on so that - * we can do proper lifetime management for Fresco - */ - override fun onDetachedFromWindow() { - draweeHolder.onDetach() - } - - override fun onStartTemporaryDetach() { - draweeHolder.onDetach() - } - - override fun onAttachedToWindow() { - draweeHolder.onAttach() - } - - override fun onFinishTemporaryDetach() { - draweeHolder.onAttach() - } - - override fun getSize( - paint: Paint, - text: CharSequence, - start: Int, - end: Int, - fm: Paint.FontMetricsInt?, - ): Int { - // NOTE: This getSize code is copied from DynamicDrawableSpan and modified - // to not use a Drawable - - fm?.let { fm -> - fm.ascent = -_height - fm.descent = 0 - - fm.top = fm.ascent - fm.bottom = 0 - } - - return _width - } - - override fun setTextView(textView: TextView?) { - this.textView = textView - } - - override fun draw( - canvas: Canvas, - text: CharSequence, - start: Int, - end: Int, - x: Float, - top: Int, - y: Int, - bottom: Int, - paint: Paint, - ) { - if (drawable == null) { - val imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(_uri) - val imageRequest: ImageRequest = - ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, headers) - - draweeHolder.hierarchy.setActualImageScaleType(ImageResizeMode.toScaleType(resizeMode)) - - draweeControllerBuilder.reset() - draweeControllerBuilder.oldController = draweeHolder.controller - - callerContext?.let { draweeControllerBuilder.setCallerContext(it) } - - draweeControllerBuilder.setImageRequest(imageRequest) - - val draweeController = draweeControllerBuilder.build() - draweeHolder.controller = draweeController - draweeControllerBuilder.reset() - - checkNotNull(draweeHolder.topLevelDrawable).apply { - setBounds(0, 0, _width, _height) - if (tintColor != 0) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - colorFilter = BlendModeColorFilter(tintColor, BlendMode.SRC_IN) - } else { - colorFilter = PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN) - } - } - callback = textView - drawable = this - } - } - - // NOTE: This drawing code is copied from DynamicDrawableSpan - - canvas.save() - - // Align to center - val _drawable = checkNotNull(drawable) - val fontHeight = (paint.descent() - paint.ascent()).toInt() - val centerY = y + paint.descent().toInt() - fontHeight / 2 - val transY = centerY - _drawable.bounds.height() / 2 - - canvas.translate(x, transY.toFloat()) - _drawable.draw(canvas) - canvas.restore() - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageViewManager.kt deleted file mode 100644 index e1ee66a6b1aea2..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageViewManager.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.text.frescosupport - -import android.view.View -import com.facebook.drawee.backends.pipeline.Fresco -import com.facebook.drawee.controller.AbstractDraweeControllerBuilder -import com.facebook.imagepipeline.request.ImageRequest -import com.facebook.react.module.annotations.ReactModule -import com.facebook.react.uimanager.BaseViewManager -import com.facebook.react.uimanager.ThemedReactContext - -/** - * Manages Images embedded in Text nodes using Fresco. Since they are used only as a virtual nodes - * any type of native view operation will throw an [IllegalStateException]. - */ -@Suppress("DEPRECATION") -@ReactModule(name = FrescoBasedReactTextInlineImageViewManager.REACT_CLASS) -internal class FrescoBasedReactTextInlineImageViewManager -@JvmOverloads -constructor( - private val draweeControllerBuilder: - @JvmSuppressWildcards - AbstractDraweeControllerBuilder<*, ImageRequest, *, *>? = - null, - private val callerContext: Any? = null, -) : BaseViewManager() { - - override fun getName(): String = REACT_CLASS - - override fun createViewInstance(context: ThemedReactContext): View { - throw IllegalStateException("RCTTextInlineImage doesn't map into a native view") - } - - override fun createShadowNodeInstance(): FrescoBasedReactTextInlineImageShadowNode = - FrescoBasedReactTextInlineImageShadowNode( - draweeControllerBuilder ?: Fresco.newDraweeControllerBuilder(), - callerContext, - ) - - override fun getShadowNodeClass(): Class = - FrescoBasedReactTextInlineImageShadowNode::class.java - - override fun updateExtraData(root: View, extraData: Any) = Unit - - companion object { - const val REACT_CLASS: String = "RCTTextInlineImage" - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/ReactTextInlineImageShadowNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/ReactTextInlineImageShadowNode.kt deleted file mode 100644 index 84b23a6cbe9273..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/ReactTextInlineImageShadowNode.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -@file:Suppress("DEPRECATION") - -package com.facebook.react.views.text.internal - -import com.facebook.react.common.annotations.internal.LegacyArchitecture -import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel -import com.facebook.react.uimanager.LayoutShadowNode -import com.facebook.react.views.text.internal.span.TextInlineImageSpan -import com.facebook.yoga.YogaNode - -/** Base class for [YogaNode]s that represent inline images. */ -@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR) -@Deprecated( - message = "This class is part of Legacy Architecture and will be removed in a future release", - level = DeprecationLevel.WARNING, -) -internal abstract class ReactTextInlineImageShadowNode : LayoutShadowNode() { - /** - * Build a [TextInlineImageSpan] from this node. This will be added to the TextView in place of - * this node. - */ - abstract fun buildInlineImageSpan(): TextInlineImageSpan -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/TextInlineImageSpan.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/TextInlineImageSpan.kt deleted file mode 100644 index 099ca17595dd33..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/TextInlineImageSpan.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.text.internal.span - -import android.graphics.drawable.Drawable -import android.text.Spannable -import android.text.style.ReplacementSpan -import android.view.View -import android.widget.TextView - -/** Base class for inline image spans. */ -internal abstract class TextInlineImageSpan : ReplacementSpan(), ReactSpan { - /** Get the drawable that is span represents. */ - abstract val drawable: Drawable? - - /** Called by the text view from [View.onDetachedFromWindow], */ - abstract fun onDetachedFromWindow() - - /** Called by the text view from [View.onStartTemporaryDetach]. */ - abstract fun onStartTemporaryDetach() - - /** Called by the text view from [View.onAttachedToWindow]. */ - abstract fun onAttachedToWindow() - - /** Called by the text view from [View.onFinishTemporaryDetach]. */ - abstract fun onFinishTemporaryDetach() - - /** Set the textview that will contain this span. */ - abstract fun setTextView(textView: TextView?) - - /** Get the width of the span. */ - abstract val width: Int - - /** Get the height of the span. */ - abstract val height: Int - - companion object { - /** - * For TextInlineImageSpan we need to update the Span to know that the window is attached and - * the TextView that we will set as the callback on the Drawable. - * - * @param spannable The spannable that may contain TextInlineImageSpans - * @param view The view which will be set as the callback for the Drawable - */ - @JvmStatic - fun possiblyUpdateInlineImageSpans(spannable: Spannable, view: TextView?) { - spannable.getSpans(0, spannable.length, TextInlineImageSpan::class.java).forEach { s -> - s.onAttachedToWindow() - s.setTextView(view) - } - } - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt index fb8f4b1bf79ca6..ff410da5caed84 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt @@ -14,7 +14,6 @@ import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Rect -import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle import android.text.Editable @@ -86,7 +85,6 @@ import com.facebook.react.views.text.internal.span.ReactSpan import com.facebook.react.views.text.internal.span.ReactStrikethroughSpan import com.facebook.react.views.text.internal.span.ReactTextPaintHolderSpan import com.facebook.react.views.text.internal.span.ReactUnderlineSpan -import com.facebook.react.views.text.internal.span.TextInlineImageSpan import java.util.concurrent.CopyOnWriteArrayList import kotlin.math.max import kotlin.math.min @@ -119,7 +117,6 @@ public open class ReactEditText public constructor(context: Context) : AppCompat private var listeners: CopyOnWriteArrayList? public var stagedInputType: Int - protected var containsImages: Boolean = false public var submitBehavior: String? = null public var dragAndDropFilter: List? = null @@ -639,13 +636,13 @@ public open class ReactEditText public constructor(context: Context) : AppCompat public fun incrementAndGetEventCounter(): Int = ++nativeEventCount - public fun maybeSetTextFromJS(reactTextUpdate: ReactTextUpdate) { + internal fun maybeSetTextFromJS(reactTextUpdate: ReactTextUpdate) { isSettingTextFromJS = true maybeSetText(reactTextUpdate) isSettingTextFromJS = false } - public fun maybeSetTextFromState(reactTextUpdate: ReactTextUpdate) { + internal fun maybeSetTextFromState(reactTextUpdate: ReactTextUpdate) { isSettingTextFromState = true maybeSetText(reactTextUpdate) isSettingTextFromState = false @@ -679,9 +676,6 @@ public open class ReactEditText public constructor(context: Context) : AppCompat manageSpans(spannableStringBuilder) stripStyleEquivalentSpans(spannableStringBuilder) - @Suppress("DEPRECATION") - containsImages = reactTextUpdate.containsImages() - // When we update text, we trigger onChangeText code that will // try to update state if the wrapper is available. Temporarily disable // to prevent an (asynchronous) infinite loop. @@ -921,54 +915,6 @@ public open class ReactEditText public constructor(context: Context) : AppCompat } } - override fun verifyDrawable(drawable: Drawable): Boolean { - if (containsImages) { - val text: Spanned? = text - val spans = checkNotNull(text).getSpans(0, text.length, TextInlineImageSpan::class.java) - for (span in spans) { - if (span.drawable === drawable) { - return true - } - } - } - return super.verifyDrawable(drawable) - } - - override fun invalidateDrawable(drawable: Drawable) { - if (containsImages) { - val text: Spanned? = text - val spans = checkNotNull(text).getSpans(0, text.length, TextInlineImageSpan::class.java) - for (span in spans) { - if (span.drawable === drawable) { - invalidate() - } - } - } - super.invalidateDrawable(drawable) - } - - public override fun onDetachedFromWindow() { - super.onDetachedFromWindow() - if (containsImages) { - val text: Spanned? = text - val spans = checkNotNull(text).getSpans(0, text.length, TextInlineImageSpan::class.java) - for (span in spans) { - span.onDetachedFromWindow() - } - } - } - - override fun onStartTemporaryDetach() { - super.onStartTemporaryDetach() - if (containsImages) { - val text: Spanned? = text - val spans = checkNotNull(text).getSpans(0, text.length, TextInlineImageSpan::class.java) - for (span in spans) { - span.onStartTemporaryDetach() - } - } - } - public override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) @@ -994,14 +940,6 @@ public open class ReactEditText public constructor(context: Context) : AppCompat // Restore the selection since `setTextIsSelectable` changed it. maybeSetSelection(selectionStart, selectionEnd) - if (containsImages) { - val text: Spanned? = text - val spans = checkNotNull(text).getSpans(0, text.length, TextInlineImageSpan::class.java) - for (span in spans) { - span.onAttachedToWindow() - } - } - if (autoFocus && !didAttachToWindow) { requestFocusProgrammatically() } @@ -1009,17 +947,6 @@ public open class ReactEditText public constructor(context: Context) : AppCompat didAttachToWindow = true } - override fun onFinishTemporaryDetach() { - super.onFinishTemporaryDetach() - if (containsImages) { - val text: Spanned? = text - val spans = checkNotNull(text).getSpans(0, text.length, TextInlineImageSpan::class.java) - for (span in spans) { - span.onFinishTemporaryDetach() - } - } - } - override fun setBackgroundColor(color: Int) { setBackgroundColor(this, color) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt index 21244cd0b1959a..4c0fb6a4ba84dd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt @@ -73,7 +73,6 @@ import com.facebook.react.views.text.ReactTextViewManagerCallback import com.facebook.react.views.text.ReactTypefaceUtils.parseFontVariant import com.facebook.react.views.text.TextAttributeProps import com.facebook.react.views.text.TextLayoutManager -import com.facebook.react.views.text.internal.span.TextInlineImageSpan.Companion.possiblyUpdateInlineImageSpans import java.util.LinkedList /** Manages instances of TextInput. */ @@ -195,11 +194,6 @@ public open class ReactTextInputManager public constructor() : return ReactTextUpdate( sb, mostRecentEventCount, - false, - 0f, - 0f, - 0f, - 0f, Gravity.NO_GRAVITY, 0, 0, @@ -208,31 +202,6 @@ public open class ReactTextInputManager public constructor() : override fun updateExtraData(view: ReactEditText, extraData: Any) { if (extraData is ReactTextUpdate) { - // TODO T58784068: delete this block of code, these are always unset in Fabric - val paddingLeft = extraData.paddingLeft.toInt() - val paddingTop = extraData.paddingTop.toInt() - val paddingRight = extraData.paddingRight.toInt() - val paddingBottom = extraData.paddingBottom.toInt() - if ( - paddingLeft != UNSET || - paddingTop != UNSET || - paddingRight != UNSET || - paddingBottom != UNSET - ) { - view.setPadding( - if (paddingLeft != UNSET) paddingLeft else view.paddingLeft, - if (paddingTop != UNSET) paddingTop else view.paddingTop, - if (paddingRight != UNSET) paddingRight else view.paddingRight, - if (paddingBottom != UNSET) paddingBottom else view.paddingBottom, - ) - } - - @Suppress("DEPRECATION") - if (extraData.containsImages()) { - val spannable = extraData.text - possiblyUpdateInlineImageSpans(spannable, view) - } - // Ensure that selection is handled correctly on text update val isCurrentSelectionEmpty = view.selectionStart == view.selectionEnd var selectionStart = UNSET