Skip to content

Commit b7628bd

Browse files
gouravkhungeruffoltzl
authored and
uffoltzl
committed
refactor: FrescoBasedTextInlineImageSpan (facebook#50532)
Summary: I've migrated `FrescoBasedTextInlineImageSpan.java` to kotlin. Reference facebook#50513. ## Changelog: [ANDROID] [CHANGED] - Refactor class `FrescoBasedTextInlineImageSpan` from Java to Kotlin. <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests Pull Request resolved: facebook#50532 Test Plan: Tested the RN tester app with `yarn android` on both new and old architecture. Reviewed By: rshest Differential Revision: D73031024 Pulled By: cortinico fbshipit-source-id: e7208bf1103849f38c3dc26d73b31315b2326275
1 parent f0f8c46 commit b7628bd

File tree

4 files changed

+179
-189
lines changed

4 files changed

+179
-189
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import android.net.Uri
1212
import com.facebook.common.logging.FLog
1313
import com.facebook.common.util.UriUtil
1414
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder
15+
import com.facebook.imagepipeline.request.ImageRequest
1516
import com.facebook.react.bridge.Dynamic
1617
import com.facebook.react.bridge.ReadableArray
1718
import com.facebook.react.bridge.ReadableMap
@@ -28,7 +29,7 @@ import java.util.Locale
2829
/** Shadow node that represents an inline image. Loading is done using Fresco. */
2930
@LegacyArchitecture
3031
internal class FrescoBasedReactTextInlineImageShadowNode(
31-
private val draweeControllerBuilder: AbstractDraweeControllerBuilder<*, *, *, *>,
32+
private val draweeControllerBuilder: AbstractDraweeControllerBuilder<*, ImageRequest, *, *>,
3233
private val callerContext: Any?
3334
) : ReactTextInlineImageShadowNode() {
3435

@@ -121,8 +122,7 @@ internal class FrescoBasedReactTextInlineImageShadowNode(
121122
resizeMode)
122123
}
123124

124-
public fun getDraweeControllerBuilder(): AbstractDraweeControllerBuilder<*, *, *, *> =
125-
draweeControllerBuilder
125+
public fun getDraweeControllerBuilder() = draweeControllerBuilder
126126

127127
public fun getCallerContext(): Any? = callerContext
128128

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageSpan.java

-185
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.views.text.frescosupport
9+
10+
import android.content.res.Resources
11+
import android.graphics.BlendMode
12+
import android.graphics.BlendModeColorFilter
13+
import android.graphics.Canvas
14+
import android.graphics.Paint
15+
import android.graphics.PorterDuff
16+
import android.graphics.PorterDuffColorFilter
17+
import android.graphics.drawable.Drawable
18+
import android.net.Uri
19+
import android.os.Build
20+
import android.widget.TextView
21+
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder
22+
import com.facebook.drawee.generic.GenericDraweeHierarchy
23+
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder
24+
import com.facebook.drawee.view.DraweeHolder
25+
import com.facebook.imagepipeline.request.ImageRequest
26+
import com.facebook.imagepipeline.request.ImageRequestBuilder
27+
import com.facebook.react.bridge.ReadableMap
28+
import com.facebook.react.modules.fresco.ReactNetworkImageRequest
29+
import com.facebook.react.uimanager.PixelUtil
30+
import com.facebook.react.views.image.ImageResizeMode
31+
import com.facebook.react.views.text.internal.span.TextInlineImageSpan
32+
33+
/**
34+
* FrescoBasedTextInlineImageSpan is a span for Images that are inside <Text/>. It computes its size
35+
* based on the input size. When it is time to draw, it will use the Fresco framework to get the
36+
* right Drawable and let that draw.
37+
*
38+
* Since Fresco needs to callback to the TextView that contains this, in the ViewManager, you must
39+
* tell the Span about the TextView
40+
*
41+
* Note: It borrows code from DynamicDrawableSpan and if that code updates how it computes size or
42+
* draws, we need to update this as well.
43+
*/
44+
internal class FrescoBasedReactTextInlineImageSpan(
45+
resources: Resources,
46+
height: Int,
47+
width: Int,
48+
private val tintColor: Int,
49+
uri: Uri?,
50+
private val headers: ReadableMap?,
51+
private val draweeControllerBuilder: AbstractDraweeControllerBuilder<*, ImageRequest, *, *>,
52+
private val callerContext: Any?,
53+
private val resizeMode: String?
54+
) : TextInlineImageSpan() {
55+
56+
private var textView: TextView? = null
57+
private val _uri: Uri = uri ?: Uri.EMPTY
58+
private val _width: Int = PixelUtil.toPixelFromDIP(width.toDouble()).toInt()
59+
private val _height: Int = PixelUtil.toPixelFromDIP(height.toDouble()).toInt()
60+
private val draweeHolder: DraweeHolder<GenericDraweeHierarchy> =
61+
DraweeHolder(GenericDraweeHierarchyBuilder.newInstance(resources).build())
62+
63+
override val width: Int
64+
get() = _width
65+
66+
override val height: Int
67+
get() = _height
68+
69+
override var drawable: Drawable? = null
70+
private set
71+
72+
/**
73+
* The ReactTextView that holds this ImageSpan is responsible for passing these methods on so that
74+
* we can do proper lifetime management for Fresco
75+
*/
76+
public override fun onDetachedFromWindow() {
77+
draweeHolder.onDetach()
78+
}
79+
80+
public override fun onStartTemporaryDetach() {
81+
draweeHolder.onDetach()
82+
}
83+
84+
public override fun onAttachedToWindow() {
85+
draweeHolder.onAttach()
86+
}
87+
88+
public override fun onFinishTemporaryDetach() {
89+
draweeHolder.onAttach()
90+
}
91+
92+
public override fun getSize(
93+
paint: Paint,
94+
text: CharSequence,
95+
start: Int,
96+
end: Int,
97+
fm: Paint.FontMetricsInt?
98+
): Int {
99+
// NOTE: This getSize code is copied from DynamicDrawableSpan and modified
100+
// to not use a Drawable
101+
102+
fm?.let { fm ->
103+
fm.ascent = -_height
104+
fm.descent = 0
105+
106+
fm.top = fm.ascent
107+
fm.bottom = 0
108+
}
109+
110+
return _width
111+
}
112+
113+
public override fun setTextView(textView: TextView?) {
114+
this.textView = textView
115+
}
116+
117+
public override fun draw(
118+
canvas: Canvas,
119+
text: CharSequence,
120+
start: Int,
121+
end: Int,
122+
x: Float,
123+
top: Int,
124+
y: Int,
125+
bottom: Int,
126+
paint: Paint
127+
) {
128+
if (drawable == null) {
129+
val imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(_uri)
130+
val imageRequest: ImageRequest =
131+
ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, headers)
132+
133+
draweeHolder.hierarchy.setActualImageScaleType(ImageResizeMode.toScaleType(resizeMode))
134+
135+
draweeControllerBuilder.reset()
136+
draweeControllerBuilder.oldController = draweeHolder.controller
137+
138+
callerContext?.let { draweeControllerBuilder.setCallerContext(it) }
139+
140+
draweeControllerBuilder.setImageRequest(imageRequest)
141+
142+
val draweeController = draweeControllerBuilder.build()
143+
draweeHolder.controller = draweeController
144+
draweeControllerBuilder.reset()
145+
146+
checkNotNull(draweeHolder.topLevelDrawable).apply {
147+
setBounds(0, 0, _width, _height)
148+
if (tintColor != 0) {
149+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
150+
colorFilter = BlendModeColorFilter(tintColor, BlendMode.SRC_IN)
151+
} else {
152+
colorFilter = PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN)
153+
}
154+
}
155+
callback = textView
156+
drawable = this
157+
}
158+
}
159+
160+
// NOTE: This drawing code is copied from DynamicDrawableSpan
161+
162+
canvas.save()
163+
164+
// Align to center
165+
val _drawable = checkNotNull(drawable)
166+
val fontHeight = (paint.descent() - paint.ascent()).toInt()
167+
val centerY = y + paint.descent().toInt() - fontHeight / 2
168+
val transY = centerY - _drawable.bounds.height() / 2
169+
170+
canvas.translate(x, transY.toFloat())
171+
_drawable.draw(canvas)
172+
canvas.restore()
173+
}
174+
}

0 commit comments

Comments
 (0)