From cfdcadcf866ea5572f154cf2781d90b97fdc0984 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Fri, 20 Mar 2026 12:55:04 +0100 Subject: [PATCH 1/4] Remove fill workaround, shortcut calculations if there's only one line of text --- .../io/sentry/android/replay/util/Nodes.kt | 28 ++++--------------- .../io/sentry/android/replay/util/Views.kt | 3 +- .../viewhierarchy/ComposeViewHierarchyNode.kt | 7 ++--- 3 files changed, 10 insertions(+), 28 deletions(-) diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Nodes.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Nodes.kt index 1d779d389bf..322c40b5b9f 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Nodes.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Nodes.kt @@ -13,28 +13,15 @@ import androidx.compose.ui.node.LayoutNode import androidx.compose.ui.text.TextLayoutResult import kotlin.math.roundToInt -internal class ComposeTextLayout( - internal val layout: TextLayoutResult, - private val hasFillModifier: Boolean, -) : TextLayout { +internal class ComposeTextLayout(internal val layout: TextLayoutResult) : TextLayout { override val lineCount: Int get() = layout.lineCount override val dominantTextColor: Int? get() = null - override fun getPrimaryHorizontal(line: Int, offset: Int): Float { - val horizontalPos = layout.getHorizontalPosition(offset, usePrimaryDirection = true) - // when there's no `fill` modifier on a Text composable, compose still thinks that there's - // one and wrongly calculates horizontal position relative to node's start, not text's start - // for some reason. This is only the case for single-line text (multiline works fien). - // So we subtract line's left to get the correct position - return if (!hasFillModifier && lineCount == 1) { - horizontalPos - layout.getLineLeft(line) - } else { - horizontalPos - } - } + override fun getPrimaryHorizontal(line: Int, offset: Int) = + layout.getHorizontalPosition(offset, usePrimaryDirection = true) override fun getEllipsisCount(line: Int): Int = if (layout.isLineEllipsized(line)) 1 else 0 @@ -92,8 +79,6 @@ internal fun Painter.isMaskable(): Boolean { !className.contains("Brush") } -internal data class TextAttributes(val color: Color?, val hasFillModifier: Boolean) - /** * This method is necessary to mask text in Compose. * @@ -108,10 +93,9 @@ internal data class TextAttributes(val color: Color?, val hasFillModifier: Boole * * We also add special proguard rules to keep the `Text` class names and their `color` member. */ -internal fun LayoutNode.findTextAttributes(): TextAttributes { +internal fun LayoutNode.findColor(): Color? { val modifierInfos = getModifierInfo() var color: Color? = null - var hasFillModifier = false for (index in modifierInfos.indices) { val modifier = modifierInfos[index].modifier val modifierClassName = modifier::class.java.name @@ -127,11 +111,9 @@ internal fun LayoutNode.findTextAttributes(): TextAttributes { } catch (e: Throwable) { null } - } else if (modifierClassName.contains("Fill")) { - hasFillModifier = true } } - return TextAttributes(color, hasFillModifier) + return color } /** diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt index 80ca1beee4e..78ba87efd29 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt @@ -122,7 +122,8 @@ internal fun TextLayout?.getVisibleRects( paddingLeft: Int, paddingTop: Int, ): List { - if (this == null) { + + if (this == null || lineCount <= 1) { return listOf(globalRect) } diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt index f421ff9ad07..f08f08584be 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt @@ -23,8 +23,8 @@ import io.sentry.SentryMaskingOptions import io.sentry.android.replay.SentryReplayModifiers import io.sentry.android.replay.util.ComposeTextLayout import io.sentry.android.replay.util.boundsInWindow +import io.sentry.android.replay.util.findColor import io.sentry.android.replay.util.findPainter -import io.sentry.android.replay.util.findTextAttributes import io.sentry.android.replay.util.isMaskable import io.sentry.android.replay.util.toOpaque import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.GenericViewHierarchyNode @@ -189,11 +189,10 @@ internal object ComposeViewHierarchyNode { ?.action ?.invoke(textLayoutResults) - val (color, hasFillModifier) = node.findTextAttributes() val textLayoutResult = textLayoutResults.firstOrNull() var textColor = textLayoutResult?.layoutInput?.style?.color if (textColor?.isUnspecified == true) { - textColor = color + textColor = node.findColor() } val isLaidOut = textLayoutResult?.layoutInput?.style?.fontSize != TextUnit.Unspecified // TODO: support editable text (currently there's a way to get @Composable's padding only @@ -202,7 +201,7 @@ internal object ComposeViewHierarchyNode { TextViewHierarchyNode( layout = if (textLayoutResult != null && !isEditable && isLaidOut) { - ComposeTextLayout(textLayoutResult, hasFillModifier) + ComposeTextLayout(textLayoutResult) } else { null }, From 781043a589abab9b384a960b291dc9cfc5eea635 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Fri, 20 Mar 2026 13:06:17 +0100 Subject: [PATCH 2/4] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57d640f385a..0cfda7df07b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixes + +- Session Replay: Fix Compose text masking mismatch with weighted text ([#5218](https://github.com/getsentry/sentry-java/pull/5218)) + ### Features - Android: Add `beforeErrorSampling` callback to Session Replay ([#5214](https://github.com/getsentry/sentry-java/pull/5214)) From 1daf0c5dd5a28a34297451fc95a75e904f6878e7 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Fri, 20 Mar 2026 14:04:17 +0100 Subject: [PATCH 3/4] Revert "Remove fill workaround, shortcut calculations if there's only one line of text" This reverts commit cfdcadcf866ea5572f154cf2781d90b97fdc0984. --- .../io/sentry/android/replay/util/Nodes.kt | 28 +++++++++++++++---- .../io/sentry/android/replay/util/Views.kt | 3 +- .../viewhierarchy/ComposeViewHierarchyNode.kt | 7 +++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Nodes.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Nodes.kt index 322c40b5b9f..1d779d389bf 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Nodes.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Nodes.kt @@ -13,15 +13,28 @@ import androidx.compose.ui.node.LayoutNode import androidx.compose.ui.text.TextLayoutResult import kotlin.math.roundToInt -internal class ComposeTextLayout(internal val layout: TextLayoutResult) : TextLayout { +internal class ComposeTextLayout( + internal val layout: TextLayoutResult, + private val hasFillModifier: Boolean, +) : TextLayout { override val lineCount: Int get() = layout.lineCount override val dominantTextColor: Int? get() = null - override fun getPrimaryHorizontal(line: Int, offset: Int) = - layout.getHorizontalPosition(offset, usePrimaryDirection = true) + override fun getPrimaryHorizontal(line: Int, offset: Int): Float { + val horizontalPos = layout.getHorizontalPosition(offset, usePrimaryDirection = true) + // when there's no `fill` modifier on a Text composable, compose still thinks that there's + // one and wrongly calculates horizontal position relative to node's start, not text's start + // for some reason. This is only the case for single-line text (multiline works fien). + // So we subtract line's left to get the correct position + return if (!hasFillModifier && lineCount == 1) { + horizontalPos - layout.getLineLeft(line) + } else { + horizontalPos + } + } override fun getEllipsisCount(line: Int): Int = if (layout.isLineEllipsized(line)) 1 else 0 @@ -79,6 +92,8 @@ internal fun Painter.isMaskable(): Boolean { !className.contains("Brush") } +internal data class TextAttributes(val color: Color?, val hasFillModifier: Boolean) + /** * This method is necessary to mask text in Compose. * @@ -93,9 +108,10 @@ internal fun Painter.isMaskable(): Boolean { * * We also add special proguard rules to keep the `Text` class names and their `color` member. */ -internal fun LayoutNode.findColor(): Color? { +internal fun LayoutNode.findTextAttributes(): TextAttributes { val modifierInfos = getModifierInfo() var color: Color? = null + var hasFillModifier = false for (index in modifierInfos.indices) { val modifier = modifierInfos[index].modifier val modifierClassName = modifier::class.java.name @@ -111,9 +127,11 @@ internal fun LayoutNode.findColor(): Color? { } catch (e: Throwable) { null } + } else if (modifierClassName.contains("Fill")) { + hasFillModifier = true } } - return color + return TextAttributes(color, hasFillModifier) } /** diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt index 78ba87efd29..80ca1beee4e 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt @@ -122,8 +122,7 @@ internal fun TextLayout?.getVisibleRects( paddingLeft: Int, paddingTop: Int, ): List { - - if (this == null || lineCount <= 1) { + if (this == null) { return listOf(globalRect) } diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt index f08f08584be..f421ff9ad07 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt @@ -23,8 +23,8 @@ import io.sentry.SentryMaskingOptions import io.sentry.android.replay.SentryReplayModifiers import io.sentry.android.replay.util.ComposeTextLayout import io.sentry.android.replay.util.boundsInWindow -import io.sentry.android.replay.util.findColor import io.sentry.android.replay.util.findPainter +import io.sentry.android.replay.util.findTextAttributes import io.sentry.android.replay.util.isMaskable import io.sentry.android.replay.util.toOpaque import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.GenericViewHierarchyNode @@ -189,10 +189,11 @@ internal object ComposeViewHierarchyNode { ?.action ?.invoke(textLayoutResults) + val (color, hasFillModifier) = node.findTextAttributes() val textLayoutResult = textLayoutResults.firstOrNull() var textColor = textLayoutResult?.layoutInput?.style?.color if (textColor?.isUnspecified == true) { - textColor = node.findColor() + textColor = color } val isLaidOut = textLayoutResult?.layoutInput?.style?.fontSize != TextUnit.Unspecified // TODO: support editable text (currently there's a way to get @Composable's padding only @@ -201,7 +202,7 @@ internal object ComposeViewHierarchyNode { TextViewHierarchyNode( layout = if (textLayoutResult != null && !isEditable && isLaidOut) { - ComposeTextLayout(textLayoutResult) + ComposeTextLayout(textLayoutResult, hasFillModifier) } else { null }, From 04925570b9c8f8f0d1fd0a241df2a160bde783d1 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Fri, 20 Mar 2026 15:47:38 +0100 Subject: [PATCH 4/4] Work around erroneous paragraph return values --- .../screenshot_mask_all.png | Bin 2673 -> 2608 bytes .../screenshot_mask_text.png | Bin 8428 -> 8366 bytes .../io/sentry/android/replay/util/Nodes.kt | 72 +++++++----------- .../sentry/android/replay/util/TextLayout.kt | 8 +- .../io/sentry/android/replay/util/Views.kt | 26 ++----- .../viewhierarchy/ComposeViewHierarchyNode.kt | 7 +- 6 files changed, 41 insertions(+), 72 deletions(-) diff --git a/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_all.png b/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_all.png index 31ba5f581bcfc891c1c1eef20d9106580c53c0ae..aa1ec41ee06c2a1dbbd7292cf8629935c5c9b634 100644 GIT binary patch literal 2608 zcmeHJSx}Q#6#jqy6oLgwt%ig^g42%_faSY5~U;WG$hA4od`N69_5FfCW*9 zQ4mz>Sgi|$6h;yUBrFm{1gZ#xKnQ_}tOAiG34zc*ee*%9Qy+Zjow;-8+~v&qzVqF4 z@0tDH9_DMT)&Kxt?zxw7002Nb+tM^? zAMY@}n-ZPBz`h-|No&+F6f&zLe>K#m_+G6Hkf)wgi!qjvZA3;t1W3>zq7s36ypC?W z5BSCYtwxy#EpEip5?u`Pss#eULpg<0ALrX>QWlBZI1mA3SqRH(%oO55Xgw|D%jwEZ z#7w{sXZl+of$y2ry?zJXcCrWDse?GDYa(*7w&yo->6ZW_KOAQ@CNg!qp^5V)8 zETc&fqSA34y^0=EFnxD3i%ahj8%3azQH_Dsk4$br>c&&6A@dcc8)qr~OUn4mTqgiAJsuIQW!OWs4{-A!=r9Y%EvFpHS$~q|>tb=FNOfddZ4W z&CeU?Cfe>Pf`p=DItwe>IGM8HE!)oDXCHwMT?TorG&70Ko9Kjg=b9U)B}sJXupxLo z#(R=yI96E~{EX@#55Fy!V@w;E>&~-3NeA8gWY+d$Dq&spzJVB|?MvF-Jmqowm zn)aY+CLh{=_KoV~UiZ9n?_C-0`6|_2&$<_s}VR zUbM2hfIjvPK0j3)u2}*o-f4$dR99pkhXA zz-2OLX7C}7Jy`o-KHOsMTCqx{N?x4mgLl*P8s_qfTHb4wqmkty8`}d@z6f)@${SWs zbl=-L;v~kDQq!&Alh}%&hgQtI*todux4OoYOg3_y!sQL^f!KOWhoC%?zC>FdA3HKX zf1);39Rq`g(XdHmE@AXJ(7~!wX;dSB`r6mI&FL-~0)#QZ6cx3z3iVg8X8dmojgO~H zEGe3@anYKtKYZ@>29544Dw*>^#JqeC;PLo;l2`+~IO|lG_A1UFP`BO$0|quEuDk%4%S`U>}s6nCK|RjIh_$Wgq@L>cOLZyJrqvx=8wo3fBrf{F?w*o=Y4S5()w-vn(!@_Y=3I-InvzV?|^P8HQ4x3r#Haoky z;YLSCiOGP4SPIvO4frhRzu(S9B9WLld{B|bBJzoF-G1k*@WI#l<9drUROitCQ-H+y o6OZ)c;{M;;r>)EP&rjJZSjl6n5Eo>4_}2$`?(t^u+(R<|28Xc1xBvhE literal 2673 zcmeAS@N?(olHy`uVBq!ia0y~yU~~ZDYaDDq5w`UKUw{;2age(c!@6@aFM%AEVkgfK z4j`!ENaAV-MMs$sSUc?7^m;Q1e~AyZv8h7-ayn0^m)krpzqabtItHe-+ie*f zqFDqwR2v+07+I1y6$HE)8Y7rEjtDs@Ok-f$h$8j+>U*0Xcjxw|HQc;*?b@xszh9f@ zw5!$}P`bsHxR_DIEy$D?=v5_vk5$~EEZ%T&Tk-V*RwSr)USX1=g*%`_e#6A!-=<*8T=D+ z4J2x|%Z3&GsB`5>EUhi)(y6omJnz7(S!@^VzueqozG2q2)qj}lIo=Ddkqd7W|M2hc z?``f3Qd{y;KNsZMA7IV6c5ANtcHJYPi4n4XjVOUzz?gr3Uv4$a0>{_uvh41>zP|pp z9Am}=(FaW1wr>Y9FO--|n7f?czIShI{QkP!YMup-mqix@KfKv?c;SzOT)*ClM6lF9 zvu(Wk^>W*a`(C!XVzws?%o`Y1p9xLCj6aN^MUD0K&;CA}f4ko1sto_9@Am(<*)nZ# zs@tjfs+jwJe~UoIp>oEWcMpG06ks|%qmJ#zrAOgkjndiX@q?oIb*O%|>eor>?6*>Z zkrEZ(Dtblh56^+io0$luDFSZ7krD~$dR_njbN+2(om|m6wg)dG8JRX-TOId}>y_$x zzAdML3DK1Ifp?7O-Xk*`pC<#O_s-uR-Sv6@FCNq3SF3Rlu9G~_`CQEDA@eK~a8SAm zkQ|iQoV_>d3+vyy{k!hw<>uywZoj>D)vAjbA>rZSrc%AHK7HCW@!+}h=cO&rt&-qt z&+TW>etxk3=$o%qTlep`@4ct9_~MFxzh1B3wR30X6^BFR_J%EKHX04PckllC?(XhY ztEBpmmlhTVUcY|*@`NYVY2?ufA4&=Toffe_Y%A^bGHVg9jZ$|8D;j{p8WH zr%zLXHiU*={rB&m&|}6wcE-#%-ha}vgEBwRG)NgLw5FH&I8d_Rg`}ak&f1I_Y*nwgr zz^EwOefjMMwco6In}pA7ui5;t?h*Uhhz#c|&CSi#`B`$n z_^zqhbA|VcaMF+ZMeJv*3mDXY{jCGV{jFQGjuqI*UB7xY^zNNIJ039Zk>F+*NC^F* zC4;YYCy=KQN&4EmckjX;tt^-~n_K4;FgKx=A_#RTbqCR=(?Zu)GYOt|x8#@>@B%9k t!u8yMx^?h}=BNv?H;)D;*?^=z1{!- diff --git a/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_text.png b/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_text.png index 952fd6ba634e63272feab38bb17f8ee73e2b2267..6ffbe1e07f5ec41580bf25ba1b5c317b608ae804 100644 GIT binary patch delta 6669 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"@fV&!t>KC|QP>_20z)}GP!thfMqNpD7L6z)42 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 layout.size.width + + override fun getLineLeft(line: Int): Float { + if (paragraphWidthExceedsNode) { + return 0f } + return layout.getLineLeft(line) } - override fun getEllipsisCount(line: Int): Int = if (layout.isLineEllipsized(line)) 1 else 0 - - override fun getLineVisibleEnd(line: Int): Int = layout.getLineEnd(line, visibleEnd = true) + override fun getLineRight(line: Int): Float { + if (paragraphWidthExceedsNode) { + return layout.multiParagraph.getLineWidth(line) + } + return layout.getLineRight(line) + } override fun getLineTop(line: Int): Int = layout.getLineTop(line).roundToInt() override fun getLineBottom(line: Int): Int = layout.getLineBottom(line).roundToInt() - - override fun getLineStart(line: Int): Int = layout.getLineStart(line) } // TODO: probably most of the below we can do via bytecode instrumentation and speed up at runtime @@ -92,8 +93,6 @@ internal fun Painter.isMaskable(): Boolean { !className.contains("Brush") } -internal data class TextAttributes(val color: Color?, val hasFillModifier: Boolean) - /** * This method is necessary to mask text in Compose. * @@ -101,37 +100,24 @@ internal data class TextAttributes(val color: Color?, val hasFillModifier: Boole * string in their name, e.g. TextStringSimpleElement or TextAnnotatedStringElement. We then get the * color from the modifier, to be able to mask it with the correct color. * - * We also look up for classes that have a [Fill] modifier, usually they all have a `Fill` string in - * their name, e.g. FillElement. This is necessary to workaround a Compose bug where single-line - * text composable without a `fill` modifier still thinks that there's one and wrongly calculates - * horizontal position. - * * We also add special proguard rules to keep the `Text` class names and their `color` member. */ -internal fun LayoutNode.findTextAttributes(): TextAttributes { +internal fun LayoutNode.findTextColor(): Color? { val modifierInfos = getModifierInfo() - var color: Color? = null - var hasFillModifier = false for (index in modifierInfos.indices) { val modifier = modifierInfos[index].modifier val modifierClassName = modifier::class.java.name if (modifierClassName.contains("Text")) { - color = - try { - (modifier::class - .java - .getDeclaredField("color") - .apply { isAccessible = true } - .get(modifier) as? ColorProducer) - ?.invoke() - } catch (e: Throwable) { - null - } - } else if (modifierClassName.contains("Fill")) { - hasFillModifier = true + return try { + (modifier::class.java.getDeclaredField("color").apply { isAccessible = true }.get(modifier) + as? ColorProducer) + ?.invoke() + } catch (e: Throwable) { + null + } } } - return TextAttributes(color, hasFillModifier) + return null } /** diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/TextLayout.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/TextLayout.kt index 9b974efceaa..e62584bda06 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/TextLayout.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/TextLayout.kt @@ -13,15 +13,11 @@ internal interface TextLayout { */ val dominantTextColor: Int? - fun getPrimaryHorizontal(line: Int, offset: Int): Float + fun getLineLeft(line: Int): Float - fun getEllipsisCount(line: Int): Int - - fun getLineVisibleEnd(line: Int): Int + fun getLineRight(line: Int): Float fun getLineTop(line: Int): Int fun getLineBottom(line: Int): Int - - fun getLineStart(line: Int): Int } diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt index 80ca1beee4e..b7f09513930 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt @@ -128,21 +128,14 @@ internal fun TextLayout?.getVisibleRects( val rects = mutableListOf() for (i in 0 until lineCount) { - val lineStart = getPrimaryHorizontal(i, getLineStart(i)).toInt() - val ellipsisCount = getEllipsisCount(i) - val lineVisibleEnd = getLineVisibleEnd(i) - var lineEnd = - getPrimaryHorizontal(i, lineVisibleEnd - ellipsisCount + if (ellipsisCount > 0) 1 else 0) - .toInt() - if (lineEnd == 0 && lineVisibleEnd > 0) { - // looks like the case for when emojis are present in text - lineEnd = getPrimaryHorizontal(i, lineVisibleEnd - 1).toInt() + 1 - } + val lineLeft = getLineLeft(i).toInt() + val lineRight = getLineRight(i).toInt() val lineTop = getLineTop(i) val lineBottom = getLineBottom(i) val rect = Rect() - rect.left = globalRect.left + paddingLeft + lineStart - rect.right = rect.left + (lineEnd - lineStart) + + rect.left = globalRect.left + paddingLeft + lineLeft + rect.right = globalRect.left + paddingLeft + lineRight rect.top = globalRect.top + paddingTop + lineTop rect.bottom = rect.top + (lineBottom - lineTop) @@ -197,18 +190,13 @@ internal class AndroidTextLayout(private val layout: Layout) : TextLayout { return dominantColor?.toOpaque() } - override fun getPrimaryHorizontal(line: Int, offset: Int): Float = - layout.getPrimaryHorizontal(offset) + override fun getLineLeft(line: Int): Float = layout.getLineLeft(line) - override fun getEllipsisCount(line: Int): Int = layout.getEllipsisCount(line) - - override fun getLineVisibleEnd(line: Int): Int = layout.getLineVisibleEnd(line) + override fun getLineRight(line: Int): Float = layout.getLineRight(line) override fun getLineTop(line: Int): Int = layout.getLineTop(line) override fun getLineBottom(line: Int): Int = layout.getLineBottom(line) - - override fun getLineStart(line: Int): Int = layout.getLineStart(line) } internal fun View?.addOnDrawListenerSafe(listener: ViewTreeObserver.OnDrawListener) { diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt index f421ff9ad07..ec01d28d4fb 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt @@ -24,7 +24,7 @@ import io.sentry.android.replay.SentryReplayModifiers import io.sentry.android.replay.util.ComposeTextLayout import io.sentry.android.replay.util.boundsInWindow import io.sentry.android.replay.util.findPainter -import io.sentry.android.replay.util.findTextAttributes +import io.sentry.android.replay.util.findTextColor import io.sentry.android.replay.util.isMaskable import io.sentry.android.replay.util.toOpaque import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.GenericViewHierarchyNode @@ -189,11 +189,10 @@ internal object ComposeViewHierarchyNode { ?.action ?.invoke(textLayoutResults) - val (color, hasFillModifier) = node.findTextAttributes() val textLayoutResult = textLayoutResults.firstOrNull() var textColor = textLayoutResult?.layoutInput?.style?.color if (textColor?.isUnspecified == true) { - textColor = color + textColor = node.findTextColor() } val isLaidOut = textLayoutResult?.layoutInput?.style?.fontSize != TextUnit.Unspecified // TODO: support editable text (currently there's a way to get @Composable's padding only @@ -202,7 +201,7 @@ internal object ComposeViewHierarchyNode { TextViewHierarchyNode( layout = if (textLayoutResult != null && !isEditable && isLaidOut) { - ComposeTextLayout(textLayoutResult, hasFillModifier) + ComposeTextLayout(textLayoutResult) } else { null },