diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextView.kt index 38570086f38e1f..afd3cefc09e0f7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextView.kt @@ -27,6 +27,7 @@ import com.facebook.proguard.annotations.DoNotStrip import com.facebook.react.uimanager.BackgroundStyleApplicator import com.facebook.react.uimanager.ReactCompoundView import com.facebook.react.uimanager.style.Overflow +import com.facebook.react.views.text.internal.span.DrawCommandSpan import com.facebook.react.views.text.internal.span.ReactFragmentIndexSpan import kotlin.collections.ArrayList import kotlin.math.roundToInt @@ -119,11 +120,37 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re selectionColor ?: DefaultStyleValuesUtil.getDefaultTextColorHighlight(context) } + val spanned = text as? Spanned + val drawCommandSpans = + spanned?.getSpans(0, spanned.length, DrawCommandSpan::class.java) ?: emptyArray() + + if (spanned != null) { + for (span in drawCommandSpans) { + span.onPreDraw( + spanned.getSpanStart(span), + spanned.getSpanEnd(span), + canvas, + layout, + ) + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { Api34Utils.draw(layout, canvas, selection?.path, selectionPaint) } else { layout.draw(canvas, selection?.path, selectionPaint, 0) } + + if (spanned != null) { + for (span in drawCommandSpans) { + span.onDraw( + spanned.getSpanStart(span), + spanned.getSpanEnd(span), + canvas, + layout, + ) + } + } } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/DrawCommandSpan.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/DrawCommandSpan.kt new file mode 100644 index 00000000000000..20d0f7be33f2b9 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/DrawCommandSpan.kt @@ -0,0 +1,27 @@ +/* + * 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.Canvas +import android.text.Layout +import android.text.style.UpdateAppearance + +/** + * May be overriden to implement charater styles which are applied by [PreparedLayoutTextView] + * during the drawing of text, against the underlying Android canvas + */ +public abstract class DrawCommandSpan : UpdateAppearance, ReactSpan { + /** + * Called before the text is drawn. This happens after the Paragraph component has drawn its + * background, but may be called before text spans with their own background color are drawn. + */ + public open fun onPreDraw(start: Int, end: Int, canvas: Canvas, layout: Layout): Unit = Unit + + /** Called after the text is drawn, including some effects like text shadows */ + public open fun onDraw(start: Int, end: Int, canvas: Canvas, layout: Layout): Unit = Unit +}