Skip to content

Commit 27e834c

Browse files
committed
Move spans traversal to background thread
1 parent c46fcc0 commit 27e834c

File tree

4 files changed

+81
-20
lines changed

4 files changed

+81
-20
lines changed

sentry-android-replay/src/main/java/io/sentry/android/replay/ScreenshotRecorder.kt

+5-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import io.sentry.SentryLevel.WARNING
2424
import io.sentry.SentryOptions
2525
import io.sentry.SentryReplayOptions
2626
import io.sentry.android.replay.util.MainLooperHandler
27+
import io.sentry.android.replay.util.dominantTextColor
2728
import io.sentry.android.replay.util.getVisibleRects
2829
import io.sentry.android.replay.util.gracefullyShutdown
2930
import io.sentry.android.replay.util.submitSafely
@@ -142,13 +143,14 @@ internal class ScreenshotRecorder(
142143
}
143144

144145
is TextViewHierarchyNode -> {
145-
// TODO: find a way to get the correct text color for RN
146-
// TODO: now it always returns black
146+
val textColor = node.layout.dominantTextColor
147+
?: node.dominantColor
148+
?: Color.BLACK
147149
node.layout.getVisibleRects(
148150
node.visibleRect,
149151
node.paddingLeft,
150152
node.paddingTop
151-
) to (node.dominantColor ?: Color.BLACK)
153+
) to textColor
152154
}
153155

154156
else -> {

sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt

+10-3
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,15 @@ internal val TextView.totalPaddingTopSafe: Int
104104
extendedPaddingTop
105105
}
106106

107-
internal val TextView.dominantTextColor: Int get() {
108-
if (text !is Spanned) return currentTextColor
107+
/**
108+
* Returns the dominant text color of the layout by looking at the [ForegroundColorSpan] spans if
109+
* this text is a [Spanned] text. If the text is not a [Spanned] text or there are no spans, it
110+
* returns null.
111+
*/
112+
internal val Layout?.dominantTextColor: Int? get() {
113+
this ?: return null
114+
115+
if (text !is Spanned) return null
109116

110117
val spans = (text as Spanned).getSpans(0, text.length, ForegroundColorSpan::class.java)
111118

@@ -125,5 +132,5 @@ internal val TextView.dominantTextColor: Int get() {
125132
dominantColor = span.foregroundColor
126133
}
127134
}
128-
return dominantColor ?: currentTextColor
135+
return dominantColor
129136
}

sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ViewHierarchyNode.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ sealed class ViewHierarchyNode(
245245
parent.setImportantForCaptureToAncestors(true)
246246
return TextViewHierarchyNode(
247247
layout = view.layout,
248-
dominantColor = view.dominantTextColor.toOpaque(),
248+
dominantColor = view.currentTextColor.toOpaque(),
249249
paddingLeft = view.totalPaddingLeft,
250250
paddingTop = view.totalPaddingTopSafe,
251251
x = view.x,
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,104 @@
11
package io.sentry.android.replay.util
22

3+
import android.app.Activity
34
import android.graphics.Color
5+
import android.os.Bundle
6+
import android.os.Looper
47
import android.text.SpannableString
58
import android.text.Spanned
69
import android.text.style.ForegroundColorSpan
10+
import android.widget.LinearLayout
11+
import android.widget.LinearLayout.LayoutParams
712
import android.widget.TextView
8-
import androidx.test.core.app.ApplicationProvider
913
import androidx.test.ext.junit.runners.AndroidJUnit4
14+
import io.sentry.SentryOptions
15+
import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode
16+
import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.TextViewHierarchyNode
1017
import org.junit.runner.RunWith
18+
import org.robolectric.Robolectric.buildActivity
19+
import org.robolectric.Shadows.shadowOf
1120
import org.robolectric.annotation.Config
1221
import kotlin.test.Test
1322
import kotlin.test.assertEquals
23+
import kotlin.test.assertNull
24+
import kotlin.test.assertTrue
1425

1526
@RunWith(AndroidJUnit4::class)
1627
@Config(sdk = [30])
1728
class TextViewDominantColorTest {
1829

1930
@Test
2031
fun `when no spans, returns currentTextColor`() {
21-
val textView = TextView(ApplicationProvider.getApplicationContext())
22-
textView.text = "Hello, World!"
23-
textView.setTextColor(Color.WHITE)
32+
val controller = buildActivity(TextViewActivity::class.java, null).setup()
33+
controller.create().start().resume()
2434

25-
assertEquals(Color.WHITE, textView.dominantTextColor)
35+
TextViewActivity.textView?.setTextColor(Color.WHITE)
36+
37+
val node = ViewHierarchyNode.fromView(TextViewActivity.textView!!, null, 0, SentryOptions())
38+
assertTrue(node is TextViewHierarchyNode)
39+
assertNull(node.layout.dominantTextColor)
2640
}
2741

2842
@Test
2943
fun `when has a foreground color span, returns its color`() {
30-
val textView = TextView(ApplicationProvider.getApplicationContext())
44+
val controller = buildActivity(TextViewActivity::class.java, null).setup()
45+
controller.create().start().resume()
46+
3147
val text = "Hello, World!"
32-
textView.text = SpannableString(text).apply {
48+
TextViewActivity.textView?.text = SpannableString(text).apply {
3349
setSpan(ForegroundColorSpan(Color.RED), 0, text.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
3450
}
35-
textView.setTextColor(Color.WHITE)
51+
TextViewActivity.textView?.setTextColor(Color.WHITE)
52+
TextViewActivity.textView?.requestLayout()
3653

37-
assertEquals(Color.RED, textView.dominantTextColor)
54+
shadowOf(Looper.getMainLooper()).idle()
55+
56+
val node = ViewHierarchyNode.fromView(TextViewActivity.textView!!, null, 0, SentryOptions())
57+
assertTrue(node is TextViewHierarchyNode)
58+
assertEquals(Color.RED, node.layout.dominantTextColor)
3859
}
3960

4061
@Test
4162
fun `when has multiple foreground color spans, returns color of the longest span`() {
42-
val textView = TextView(ApplicationProvider.getApplicationContext())
63+
val controller = buildActivity(TextViewActivity::class.java, null).setup()
64+
controller.create().start().resume()
65+
4366
val text = "Hello, World!"
44-
textView.text = SpannableString(text).apply {
67+
TextViewActivity.textView?.text = SpannableString(text).apply {
4568
setSpan(ForegroundColorSpan(Color.RED), 0, 5, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
4669
setSpan(ForegroundColorSpan(Color.BLACK), 6, text.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
4770
}
48-
textView.setTextColor(Color.WHITE)
71+
TextViewActivity.textView?.setTextColor(Color.WHITE)
72+
TextViewActivity.textView?.requestLayout()
73+
74+
shadowOf(Looper.getMainLooper()).idle()
75+
76+
val node = ViewHierarchyNode.fromView(TextViewActivity.textView!!, null, 0, SentryOptions())
77+
assertTrue(node is TextViewHierarchyNode)
78+
assertEquals(Color.BLACK, node.layout.dominantTextColor)
79+
}
80+
}
81+
82+
private class TextViewActivity : Activity() {
83+
84+
companion object {
85+
var textView: TextView? = null
86+
}
87+
88+
override fun onCreate(savedInstanceState: Bundle?) {
89+
super.onCreate(savedInstanceState)
90+
val linearLayout = LinearLayout(this).apply {
91+
setBackgroundColor(android.R.color.white)
92+
orientation = LinearLayout.VERTICAL
93+
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
94+
}
95+
96+
textView = TextView(this).apply {
97+
text = "Hello, World!"
98+
layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
99+
}
100+
linearLayout.addView(textView)
49101

50-
assertEquals(Color.BLACK, textView.dominantTextColor)
102+
setContentView(linearLayout)
51103
}
52104
}

0 commit comments

Comments
 (0)