From cfdcadcf866ea5572f154cf2781d90b97fdc0984 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Fri, 20 Mar 2026 12:55:04 +0100 Subject: [PATCH 1/9] 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/9] 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/9] 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/9] 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 }, From 8cd317c8233dfb470639f745ee92b759a6d3e271 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 23 Mar 2026 14:50:52 +0100 Subject: [PATCH 5/9] Cover some more edge cases --- .../core/ScreenshotEventProcessorTest.kt | 240 ++++++++++++++++++ ...eenshot_mask_ellipsized_compose_masked.png | Bin 0 -> 2917 bytes ...nshot_mask_ellipsized_compose_unmasked.png | Bin 0 -> 20367 bytes ...screenshot_mask_ellipsized_view_masked.png | Bin 0 -> 2331 bytes ...reenshot_mask_ellipsized_view_unmasked.png | Bin 0 -> 16750 bytes .../io/sentry/android/replay/util/Views.kt | 21 +- 6 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_ellipsized_compose_masked.png create mode 100644 sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_ellipsized_compose_unmasked.png create mode 100644 sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_ellipsized_view_masked.png create mode 100644 sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_ellipsized_view_unmasked.png diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt index acc38228e6b..b325f9a3500 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt @@ -9,12 +9,27 @@ import android.graphics.Color import android.graphics.drawable.Drawable import android.os.Bundle import android.os.Looper +import android.text.TextUtils import android.view.View import android.widget.ImageView import android.widget.LinearLayout import android.widget.LinearLayout.LayoutParams import android.widget.RadioButton import android.widget.TextView +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.test.ext.junit.runners.AndroidJUnit4 import com.dropbox.differ.Color as DifferColor import com.dropbox.differ.Image @@ -410,6 +425,48 @@ class ScreenshotEventProcessorTest { assertNotNull(bytes) } + @Test + fun `snapshot - screenshot with ellipsized text no masking`() { + fixture.activity = buildActivity(EllipsizedTextActivity::class.java, null).setup().get() + val bytes = + processEventForSnapshots( + "screenshot_mask_ellipsized_view_unmasked", + isReplayAvailable = false, + ) + assertNotNull(bytes) + } + + @Test + fun `snapshot - screenshot with ellipsized text masking`() { + fixture.activity = buildActivity(EllipsizedTextActivity::class.java, null).setup().get() + val bytes = + processEventForSnapshots("screenshot_mask_ellipsized_view_masked") { + it.screenshot.setMaskAllText(true) + } + assertNotNull(bytes) + } + + @Test + fun `snapshot - compose text no masking`() { + fixture.activity = buildActivity(ComposeTextActivity::class.java, null).setup().get() + val bytes = + processEventForSnapshots( + "screenshot_mask_ellipsized_compose_unmasked", + isReplayAvailable = false, + ) + assertNotNull(bytes) + } + + @Test + fun `snapshot - compose text with masking`() { + fixture.activity = buildActivity(ComposeTextActivity::class.java, null).setup().get() + val bytes = + processEventForSnapshots("screenshot_mask_ellipsized_compose_masked") { + it.screenshot.setMaskAllText(true) + } + assertNotNull(bytes) + } + // endregion private fun getEvent(): SentryEvent = SentryEvent(Throwable("Throwable")) @@ -484,6 +541,189 @@ private class CustomView(context: Context) : View(context) { } } +private class EllipsizedTextActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val longText = "This is a very long text that should be ellipsized when it does not fit" + + val linearLayout = + LinearLayout(this).apply { + setBackgroundColor(Color.WHITE) + orientation = LinearLayout.VERTICAL + layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) + setPadding(10, 10, 10, 10) + } + + // Ellipsize end + linearLayout.addView( + TextView(this).apply { + text = longText + setTextColor(Color.BLACK) + textSize = 16f + maxLines = 1 + ellipsize = TextUtils.TruncateAt.END + setBackgroundColor(Color.LTGRAY) + layoutParams = + LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply { + setMargins(0, 8, 0, 0) + } + } + ) + + // Ellipsize middle + linearLayout.addView( + TextView(this).apply { + text = longText + setTextColor(Color.BLACK) + textSize = 16f + maxLines = 1 + ellipsize = TextUtils.TruncateAt.MIDDLE + setBackgroundColor(Color.LTGRAY) + layoutParams = + LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply { + setMargins(0, 8, 0, 0) + } + } + ) + + // Ellipsize start + linearLayout.addView( + TextView(this).apply { + text = longText + setTextColor(Color.BLACK) + textSize = 16f + maxLines = 1 + ellipsize = TextUtils.TruncateAt.START + setBackgroundColor(Color.LTGRAY) + layoutParams = + LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply { + setMargins(0, 8, 0, 0) + } + } + ) + + // Non-ellipsized text for comparison + linearLayout.addView( + TextView(this).apply { + text = "Short text" + setTextColor(Color.BLACK) + textSize = 16f + setBackgroundColor(Color.LTGRAY) + layoutParams = + LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply { + setMargins(0, 8, 0, 0) + } + } + ) + + setContentView(linearLayout) + } +} + +private class ComposeTextActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val longText = "This is a very long text that should be ellipsized when it does not fit in view" + + setContent { + Column( + modifier = + Modifier.fillMaxWidth() + .background(androidx.compose.ui.graphics.Color.White) + .padding(10.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + // Ellipsis overflow + Text( + longText, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontSize = 16.sp, + modifier = + Modifier.fillMaxWidth().background(androidx.compose.ui.graphics.Color.LightGray), + ) + + // Text with textAlign center + Text( + "Centered text", + textAlign = TextAlign.Center, + fontSize = 16.sp, + modifier = + Modifier.fillMaxWidth().background(androidx.compose.ui.graphics.Color.LightGray), + ) + + // Text with textAlign end + Text( + "End-aligned text", + textAlign = TextAlign.End, + fontSize = 16.sp, + modifier = + Modifier.fillMaxWidth().background(androidx.compose.ui.graphics.Color.LightGray), + ) + + // Ellipsis with textAlign center + Text( + longText, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + textAlign = TextAlign.Center, + fontSize = 16.sp, + modifier = + Modifier.fillMaxWidth().background(androidx.compose.ui.graphics.Color.LightGray), + ) + + // Weighted row with text + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + "Weight 1", + fontSize = 16.sp, + modifier = Modifier.weight(1f).background(androidx.compose.ui.graphics.Color.LightGray), + ) + Text( + "Weight 1", + fontSize = 16.sp, + modifier = Modifier.weight(1f).background(androidx.compose.ui.graphics.Color.LightGray), + ) + } + + // Weighted row with ellipsized text + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + longText, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontSize = 16.sp, + modifier = Modifier.weight(1f).background(androidx.compose.ui.graphics.Color.LightGray), + ) + Text( + longText, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + textAlign = TextAlign.End, + fontSize = 16.sp, + modifier = Modifier.weight(1f).background(androidx.compose.ui.graphics.Color.LightGray), + ) + } + + // Short text (for comparison) + Text( + "Short text", + fontSize = 16.sp, + modifier = Modifier.background(androidx.compose.ui.graphics.Color.LightGray), + ) + } + } + } +} + private class MaskingActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { diff --git a/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_ellipsized_compose_masked.png b/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_ellipsized_compose_masked.png new file mode 100644 index 0000000000000000000000000000000000000000..53e5a236c3e86ca447dd8674c6d046302bd8cda1 GIT binary patch literal 2917 zcmeH}c~p~E7QnwOBmsp41ZAW``)|n-23i*zjyCV zw6ZXklsF;*0DvUroRJLxK*-?RheCie2&R<~{6J_n#s)wcS78<$z-a~)I~4dtpxhGx z-~gIpbk;63i$B8lJ7fxL8yBhB6pX7BrelhHO0)Iz`7O5K& z26iAx%D9{A7bXlb*gh=e$p$P=%igvG)Ou1X_oXr}PJQsQ*#7$WGhK2R8NhYD3hnhy zs&;}vD-hzj_E=ngQO2&4Bgg3u(Hu;HPonEBspHMhcP5ZSq<&hFjToa5f1q; zF8yJ$Pot&;YV#M+#0NhAv0yhG7atG<8b*lgqRPt3pAoRdtj;TfH`KoQ;ODEyq{gVt z$c{^FeO0_(e!sla7F{*eD>(^PJH*eJpZ8vZ$^T2$99+fc`_jU~UTr1N|d2`gShME$a zN}#Yvj(UnrQMik+cGNMZ+^2c<+9osalfGZ%YYir}^_X7S@N&%?R_&8jrFQ^_7WB*- zm&?syN9}O+4)}JLzAVB`%<0n$<4Zo@WL~K{e;QQhXJKJ6r6dKza3m7P78?tK zAB&asZlg1TmKUUDSj$~at9Eo;} z;hehZpT5FlG&%AIpidzaAqpE!mQa-C+R|kFnqoR}fdMoh8f8b3RxvYq-f*$qE4G+W zx)9jV(4cXn1c2JFFOC~7QQryPJC#R4b;m7$acQ>XZ?z*l-eHmh5dFy6#YK+PH{v!X z{?2LR8NqZ{=j^Q4(qt(xP8yI@My z%%CPjWjXi`Rl{zHjm-=|Vf#XUj3^zxOLwqZ9F#@~QW1u;PHZT#m~d&Lo5iN&+;#hN z%&*JWAf_*BDA`bu>lUNrd>fnME2_D%ASuR34argVUV zHCb5pJaF*Cq+(z1Ebpj5W-DO|Z_8mZ#kN2xG+K3ju#l@kmWKaOXqovt&v%>5=OPVz z->L;=<8W1<$zN{mhr)W>KF+e`4OZN}T$fjx*`n~s#jeg%af`!x*Sj}OzCZHi>)5wc zJcZG!M>S(rb55K8+WuE!HPu#d-C9tgcy9s#ajL9qYDx*6lt~FJ>Nr(kr@-N{qqG?e z1~tXQ+Kd~P=4n<$p!<(Deo9m5*18?m_9O|m_S;AUz8zxT0}7vo(UAiWabf}=;+|BB ztaVOQPxBP0OXX_s{+RtDfk}9&PN(eXswvLD_=2@UX|FfRcBG5@J2m4^xHMn?KH!#V zzxM%W!BIzWdzl%$aSL{6Uz1VS2IXpxb=nl|nUVULIS9ok8vbd6R>Ba7vG;^fIQBuL%GBTNv8==u@72TezIy3dCLDxj-dE5RZh`x?;OV^Bj4Rx< zoVljfmU4f+jcV9Yw;LIN3L)qc#Sr^e1N#US;olk}m*=Z;?uXB~KNE%PkDsIGiJD~c zR~GdLNEQtDvo0WuwiByPbAg7Xx=5TpbhaIRdRCT9+S*yxB`Zcq9Berzm1~c-%A6Rw zRO(P%(S9vxm~t>Bb5CMfdHt9`xB*71t`1}exThhN+v7$b3>t(Et-PHds`00xA)k<^ zcVK3B#;#O)y-QvQ9Q~qlO#XK`QEi?B%RixBIW~iNNM66tajA*}R0S%1v}k*aR`?ej zV}R7%Q0}4ey}a|M=)FSpXQ2M4e(=NI=p${@|6R0=?vO>6;d>-!z*ZTcd~0D;X5bd{ EZ)r_j+5i9m literal 0 HcmV?d00001 diff --git a/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_ellipsized_compose_unmasked.png b/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_mask_ellipsized_compose_unmasked.png new file mode 100644 index 0000000000000000000000000000000000000000..efc2304c4f2db20dc4d7d5ee73a741da009a9001 GIT binary patch literal 20367 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 z zd=(xR_WhmKNvy4Ajtwos)=cXm2BM?q%X63!Qs76{FLph=0V(f7VVFovg=0RaJ0Zv@&Sl{Cw?x=3!RA> zZEFa!tAcP7&lbQ3I19s5IZ505#fv{o&ff?)$dw6C3n`?RKHY^78W+y~Na!N{U z+Pkz1AFhAdN@D!K^tF+1<4}{wgRIgbWU*VGZcKsf16k-qaq1IfO~=T{JG(52Ist+5 z)wPz^)+KmGf=|DSm0Sg~vWj;{{-UMV44(B7PY{Up7N?UHaIkx7ZL)Z1GO1`@0n#Bo$6S{{fhBX zDrlJzx=`=O@`sw!;+IC5_-_`XFbE^zSe5?PY8{_0@>)yq_|_euyS7VB4lGAJBzKxz zcZ5IdDHZfXS68)P3T{qI9gSzd+!!!pI16Ha!<(A#>+9uqc?*ykWUge`f_YP)S@7)P ziuxbyYxEJ$96zp_&#Zn0dk4xS+%EL*`VQi8`Ttm!2*1P$P&sZlwu=n+N~qJ)(@PQQr=Y#`=A1k;tB)Nrq1aeEd2m5#e z9=_#%On(OtmW+MbAFPW*OFmPGS|K$-nveT~o$jK`^(s{t^+kjz+-mRF;$)DW1w70M z2)|*G|BuGhq1XG49NB#H=1r0~Lkpt()zR1Y0JyjZ`=s4vktf~kCl`C$-zD`2)DLet zttOXED;bX`BQO+D>6& 0 && layout.ellipsizedWidth < layout.width) { + 0f + } else { + layout.getLineLeft(line) + } + } - override fun getLineRight(line: Int): Float = layout.getLineRight(line) + override fun getLineRight(line: Int): Float { + return if (layout.ellipsizedWidth > 0 && layout.ellipsizedWidth < layout.width) { + layout.ellipsizedWidth.toFloat() + } else { + layout.getLineRight(line) + } + } override fun getLineTop(line: Int): Int = layout.getLineTop(line) From eee1c1da8f406ac283a760001e0443e9a11e2c43 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Mon, 23 Mar 2026 13:54:38 +0000 Subject: [PATCH 6/9] Format code --- .../src/main/java/io/sentry/android/replay/util/Views.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 a8b62eb06d1..d0583cdaa6a 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 @@ -191,9 +191,9 @@ internal class AndroidTextLayout(private val layout: Layout) : TextLayout { } /** - * If text gets ellipsized, we return the left and right bounds of the ellipsized text instead of the width, as it's set to - * some obscure VERY_WIDE value. - * E.g. see https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/widget/TextView.java;l=468?q=VERY_WIDE + * If text gets ellipsized, we return the left and right bounds of the ellipsized text instead of + * the width, as it's set to some obscure VERY_WIDE value. E.g. see + * https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/widget/TextView.java;l=468?q=VERY_WIDE */ override fun getLineLeft(line: Int): Float { return if (layout.ellipsizedWidth > 0 && layout.ellipsizedWidth < layout.width) { From b2e95db477b0934fc29bc904b72516809587442d Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 24 Mar 2026 11:37:20 +0100 Subject: [PATCH 7/9] Trigger Build From 2e7b0832031b7e201343f8835776c350d712e4c3 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 24 Mar 2026 11:57:40 +0100 Subject: [PATCH 8/9] fix dependencies --- sentry-android-core/build.gradle.kts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sentry-android-core/build.gradle.kts b/sentry-android-core/build.gradle.kts index 1134e948226..ffd42c7d4d7 100644 --- a/sentry-android-core/build.gradle.kts +++ b/sentry-android-core/build.gradle.kts @@ -4,6 +4,7 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion plugins { id("com.android.library") alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) jacoco alias(libs.plugins.jacoco.android) alias(libs.plugins.errorprone) @@ -108,7 +109,11 @@ dependencies { testImplementation(projects.sentryCompose) testImplementation(projects.sentryAndroidNdk) testImplementation(libs.dropbox.differ) - testRuntimeOnly(libs.androidx.compose.ui) + testImplementation(libs.androidx.activity.compose) + testImplementation(libs.androidx.compose.ui) + testImplementation(libs.androidx.compose.foundation) + testImplementation(libs.androidx.compose.foundation.layout) + testImplementation(libs.androidx.compose.material3) testRuntimeOnly(libs.androidx.fragment.ktx) testRuntimeOnly(libs.timber) } From 5ddd3871687031f0b48505f075453c84e2df4948 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Wed, 25 Mar 2026 07:19:27 +0100 Subject: [PATCH 9/9] Add snapshot tests for non-ellipsized multi-line text masking Co-Authored-By: Claude Opus 4.6 (1M context) --- .../core/ScreenshotEventProcessorTest.kt | 152 ++++++++++++++++++ .../screenshot_multiline_compose_masked.png | Bin 0 -> 3272 bytes .../screenshot_multiline_compose_unmasked.png | Bin 0 -> 28628 bytes .../screenshot_multiline_view_masked.png | Bin 0 -> 2924 bytes .../screenshot_multiline_view_unmasked.png | Bin 0 -> 20865 bytes 5 files changed, 152 insertions(+) create mode 100644 sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_multiline_compose_masked.png create mode 100644 sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_multiline_compose_unmasked.png create mode 100644 sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_multiline_view_masked.png create mode 100644 sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_multiline_view_unmasked.png diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt index b325f9a3500..300936153f7 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt @@ -467,6 +467,42 @@ class ScreenshotEventProcessorTest { assertNotNull(bytes) } + @Test + fun `snapshot - multiline view text no masking`() { + fixture.activity = buildActivity(MultiLineTextActivity::class.java, null).setup().get() + val bytes = + processEventForSnapshots("screenshot_multiline_view_unmasked", isReplayAvailable = false) + assertNotNull(bytes) + } + + @Test + fun `snapshot - multiline view text with masking`() { + fixture.activity = buildActivity(MultiLineTextActivity::class.java, null).setup().get() + val bytes = + processEventForSnapshots("screenshot_multiline_view_masked") { + it.screenshot.setMaskAllText(true) + } + assertNotNull(bytes) + } + + @Test + fun `snapshot - multiline compose text no masking`() { + fixture.activity = buildActivity(ComposeMultiLineTextActivity::class.java, null).setup().get() + val bytes = + processEventForSnapshots("screenshot_multiline_compose_unmasked", isReplayAvailable = false) + assertNotNull(bytes) + } + + @Test + fun `snapshot - multiline compose text with masking`() { + fixture.activity = buildActivity(ComposeMultiLineTextActivity::class.java, null).setup().get() + val bytes = + processEventForSnapshots("screenshot_multiline_compose_masked") { + it.screenshot.setMaskAllText(true) + } + assertNotNull(bytes) + } + // endregion private fun getEvent(): SentryEvent = SentryEvent(Throwable("Throwable")) @@ -724,6 +760,122 @@ private class ComposeTextActivity : ComponentActivity() { } } +private class MultiLineTextActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val multiLineText = + "This is a long text that will wrap across multiple lines without being ellipsized. " + + "It should continue to flow naturally within the available width of the view." + + val linearLayout = + LinearLayout(this).apply { + setBackgroundColor(Color.WHITE) + orientation = LinearLayout.VERTICAL + layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) + setPadding(10, 10, 10, 10) + } + + // Multi-line wrapping text (no maxLines, no ellipsize) + linearLayout.addView( + TextView(this).apply { + text = multiLineText + setTextColor(Color.BLACK) + textSize = 16f + setBackgroundColor(Color.LTGRAY) + layoutParams = + LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply { + setMargins(0, 8, 0, 0) + } + } + ) + + // Multi-line text with maxLines = 3 (wraps but capped) + linearLayout.addView( + TextView(this).apply { + text = multiLineText + setTextColor(Color.BLACK) + textSize = 16f + maxLines = 3 + setBackgroundColor(Color.LTGRAY) + layoutParams = + LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply { + setMargins(0, 8, 0, 0) + } + } + ) + + // Short single-line text for comparison + linearLayout.addView( + TextView(this).apply { + text = "Short text" + setTextColor(Color.BLACK) + textSize = 16f + setBackgroundColor(Color.LTGRAY) + layoutParams = + LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply { + setMargins(0, 8, 0, 0) + } + } + ) + + setContentView(linearLayout) + } +} + +private class ComposeMultiLineTextActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val multiLineText = + "This is a long text that will wrap across multiple lines without being ellipsized. " + + "It should continue to flow naturally within the available width of the view." + + setContent { + Column( + modifier = + Modifier.fillMaxWidth() + .background(androidx.compose.ui.graphics.Color.White) + .padding(10.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + // Multi-line wrapping text (no maxLines, no overflow) + Text( + multiLineText, + fontSize = 16.sp, + modifier = + Modifier.fillMaxWidth().background(androidx.compose.ui.graphics.Color.LightGray), + ) + + // Multi-line text with maxLines = 3 + Text( + multiLineText, + maxLines = 3, + fontSize = 16.sp, + modifier = + Modifier.fillMaxWidth().background(androidx.compose.ui.graphics.Color.LightGray), + ) + + // Multi-line centered text + Text( + multiLineText, + textAlign = TextAlign.Center, + fontSize = 16.sp, + modifier = + Modifier.fillMaxWidth().background(androidx.compose.ui.graphics.Color.LightGray), + ) + + // Short text for comparison + Text( + "Short text", + fontSize = 16.sp, + modifier = Modifier.background(androidx.compose.ui.graphics.Color.LightGray), + ) + } + } + } +} + private class MaskingActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { diff --git a/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_multiline_compose_masked.png b/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_multiline_compose_masked.png new file mode 100644 index 0000000000000000000000000000000000000000..fb8f816218d0610b70c51cf01fc4a13ada281590 GIT binary patch literal 3272 zcmeHKdpMN&7k}rnGn0%NR^t{k+H7jYxP+2x3Z+8Cij1LM6ru-}8D{8L;(W22k;i65TFE6?lA*CAg8=soB;Oo^>4rz_$enhZxnb@ zP@xO}P>ywTcJw}7FeYkQ{6RNub(dK+P$(rI4CY=X#t#ti8E?L`o^V<1Bpk(r@vp*2J@Djliz59W;l82os

81hjI$%-Uus@3pzxUy z=T3G2O8ApU40c)fhyejcbJx?Wax9#&Z7(qPo@Sa8k*-U?jtU_U7GX?}+gsy5)%!0@ za^ryGNw?MzgA97eNK~V?0WF@+&)d6Apn_#=^9RPDbki#*(oFUs^DCxsToxIPfoAeV>N>`F*dGWoVau6PVrVmHxI_i`V z8*BD@1c979{uoJVJ*(r-cy;ka+!~`KKDMlyd=e;Uv$-R!g;vWcw5kVvnU5fp`FF=; zuiJ_@cR`xVlRJr)L7=<)Y7@#AXWkqdJJv^_ zIhaQG*Zsgdrvt|t4v&j!Ik_iO0jQv_ru}eik3X7te4?Mn`mjunjxJA}ed_~$N3^Du z*|#Gfj14wj6t%T^f+7+Ur9SoB)9i7%{NbEgRCk!RyI2;*Kv-4<7}pBOo{3uG?$gNE zUYgy`V%h$m%15i};5bJTLwKZe)}dooWB9(N{PyhYvfdrEAO^l-ydnPuSu?80J|P%91N&J?^ZGgrgsxo3n-c7X!*HnLSF=uLPTe$N1GH>X4OUs1#9 z1_o-E$G;3C1+KNC6c!XHFVvJ3!d2}giNf9&PRhxltbL>@*!eKtx>VTdxVTQ+SLR<^e}O8E;8rTT7rj2u_$8|_^vL=KDLQ-_j+nTmX)4WEW{;H zsevCzgTdQLpSQNM3VK1P=MTlc6}c)x(6n?=;2{) z@WuJ7R0Xzy+5XGYE0pNm0&DJ`?YFKo=@VMh%s1KD*^c(UCmJ&* zA@=wC8D0l3sn@`9_N32C@=e26iftpNdv992yEZ>4SrOO#1ZHj(2d642H|87BjHz@X z$l(3gW6eLU^A=+=adM%wJNvVW+2~Yg{FAn}w$P?37(73oLfQGUaud2JkZ@AxkX7hA z-69mU*Rbuo4Y~Xcs1vun&C}7&fys_HKS$Ib4k`BDNJ~L3xo4%l$VPa`X^~gbZ^igI zw8uwjXUZWV`K6nSkseF5&F*cum71~#T_nO2OIli5JTHEWL`gHnNsEykgP*ozn-FQ! z0Jyi(mw$y_UhZOhQ~aA--}2bLFLfCa%61i5UOIQl_>^u{sC~PP+ICoe09&M0(x9>8o*c5`o*T?C zi67}M!UmTb;YyVC;oS9kK^8#iw3j}4dx;Q^NNdrz5< zG_x?7!OdSayCu+!|3>f4%2(9` z5FYOg9RJRva0m2{JUBwG_0d@L zM>9Z(QPh~2DN%CaBKjwLuKvt#zkM&+ORJ1jZ2YRw}`=LL=i1!@iDCG}Bx ztSnfb9(|?@bLze)z?)wlYJfdUJxHhfrNY9xV9$P$#Q0?h$Zt8)_1TT2>KZs=^_a%r zdd5g4UMvBOb6YldK|J~-02htOyPjoC51dnsPBbjDd2kY3XBkbjO`(bt78e&exsXf} z3*+eZMcx88m|dmE;OeDrx8G!RmKc;;_!%Nk2b?(ma`OAbMO?$uyNpeaG}r%Tg!{Bt pu6E+%F7_Xeq5oI-KMm9Z-q-j1TS2&9B=|Q1xa~gR%yuHB{0{46L=gZ0 literal 0 HcmV?d00001 diff --git a/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_multiline_compose_unmasked.png b/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_multiline_compose_unmasked.png new file mode 100644 index 0000000000000000000000000000000000000000..dc4beee80a68507a82bce68ebd330f48314fabe5 GIT binary patch literal 28628 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 vFaG|&Iz0aK$Ntx8`kxE(|8PO3q&A literal 0 HcmV?d00001 diff --git a/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_multiline_view_masked.png b/sentry-android-core/src/test/resources/snapshots/ScreenshotEventProcessorTest/screenshot_multiline_view_masked.png new file mode 100644 index 0000000000000000000000000000000000000000..373752de30f95ef642b961822be5ddcdbdeb9cba GIT binary patch literal 2924 zcmeHJc~nzZ9)5WV5Y}W6F%4T_1W^!BSu_fSUBqZaR0t5+oU)XovdSKi5u_|~T8ITR zU>OW3OIQSKSOQ3K#7KY;c7dV=jFcq^fwIhtwdb_VbWTtInm>BadFOZT@4b7!d%ySl zzI$(z&N#@+U}XRRkas+3>kI%e0`%~al2AsnlC%s3Sg5muHBj+Vbr$L%LaiNLkWh<7 z`lSMZtg@r6mCKELb7M_sR7TWOCa?RQ%B%e+(Mmp&Kr^`18i(td1{;tMU34XgcZ`%s z1e+X%{b{zc&lpFm{T%kCd@^z*=&NyIt@pf}|J4>Z;rrE+fV==Wdd6l}0{BvkO3;TP ze57$`Cm_)kL_Sp{;Bo=53V}}5{?)4~i|sdkrB+AS+h*8|=W483Z`Irdm}d@hk_^|4 zWk0+nH#uAZOVeN~*wj5GqH%mO=)Wpe)<+^%!4W>?ssxs0AxVjhF9NI!?gxhe7{6$g=i32NyeL2)j+UKJVGqNoKpB`iJ=nmO#yW zA`SlLUUov3Rb^iw9DpYMuRGuQ^?&fiZ}HIwO;5LsM14(Hk~L~1{tibA0-&{Rt}Hw z#Q@Bxp{MqZSy7gKQS$)U7-}geZQ<-63WOQzm6wY~e9XfR>Mr{XLXLx(ysclkWhsM*%J`KE3>UUCQ zU+3NuIqxk@#gcpXQ%&?SH!LB}@~5Y#eL7bUsFHa02<{K4nf9R}x6qgv_t%3FRKht2 z2M5m7lyA1LERHnV+G>?OCG(4NEi4fVg+84tbR6MaWMm|#ukU=e?+6UEQ&U$r-M4R_ zUFKK-_J!Z!MS?NYlt!a*3M>=y=H}+KI?WU{bbU+@Y)^G&arpd;*(UnoGWtpE1Azh0 zdg14$6JU%EwtL|1S110unD~l^X^OJ{4&GFHU2aR9u;LBML!p(&M~Qp=yuVyc0;H$O63FKp=jag5H*pfe$!S@g8LFRoF7L?W~w_$kVIZl)~*ozeRM(b5?*3q zpz?BDIo#_S9TdG63^em$+3&!vKsC08W&8)Yj0b(Qwf(3}`?yL8pC_8tl$e;9^7xlW z^>-BhV}(MN3peZPcykWLTK_mf_uY@H^TjlD*U4DjnWd%bfHhqMb0ZX52Ag3|Ijs9E zGKrvR;q>OwhTb7we56g6gu89n)*?zQ0NV>>6hp#;V@W6TH&r-L`e!G)-{sDl)j6E0T`XVgG8 zb=hIE?1|>sHRot)VqX|?qNBsEKmcGMC(Z2CLuJBB(y~SD!oWNk-@O(_cZb)#p^%DY zK>-3RGV7+xV@}Q;I%%ADhS68NJ=x<=4@yMp#lCi}DUN{rs4G7%KAGA0(#+~9ir@Ze z=?RF+D!c@D+?7eOIDZ9_ZxvaJJMO|o6_kg(gHA>D0}u7vhZNPK(}ZQjO4)kFtOY_BNL} z2G~LE?>z7S9Q7wr hzjYVi`ETUq_Fq$FZc_#i>$KtIrxwFx%2lvO2zn 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

u;MA`Qqc_ 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