fix(replay): text layouts with center/end alignment return incorrect masking bounding box#5218
fix(replay): text layouts with center/end alignment return incorrect masking bounding box#5218
Conversation
Semver Impact of This PR🟢 Patch (bug fixes) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Bug Fixes 🐛
Internal Changes 🔧
🤖 This preview updates automatically when you update the PR. |
Sentry Build Distribution
|
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| abfcc92 | 337.38 ms | 427.39 ms | 90.00 ms |
| b3d8889 | 371.69 ms | 432.96 ms | 61.26 ms |
| 27d7cf8 | 397.90 ms | 498.65 ms | 100.75 ms |
| b750b96 | 421.25 ms | 444.09 ms | 22.84 ms |
| 0eaac1e | 320.04 ms | 369.52 ms | 49.48 ms |
| f634d01 | 375.06 ms | 420.04 ms | 44.98 ms |
| bbc35bb | 298.53 ms | 372.17 ms | 73.64 ms |
| 1564554 | 323.06 ms | 336.68 ms | 13.62 ms |
| f064536 | 349.86 ms | 417.66 ms | 67.80 ms |
| ee747ae | 554.98 ms | 611.50 ms | 56.52 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| abfcc92 | 1.58 MiB | 2.13 MiB | 557.31 KiB |
| b3d8889 | 1.58 MiB | 2.10 MiB | 535.06 KiB |
| 27d7cf8 | 1.58 MiB | 2.12 MiB | 549.42 KiB |
| b750b96 | 1.58 MiB | 2.10 MiB | 533.20 KiB |
| 0eaac1e | 1.58 MiB | 2.19 MiB | 619.17 KiB |
| f634d01 | 1.58 MiB | 2.10 MiB | 533.40 KiB |
| bbc35bb | 1.58 MiB | 2.12 MiB | 553.01 KiB |
| 1564554 | 1.58 MiB | 2.20 MiB | 635.33 KiB |
| f064536 | 1.58 MiB | 2.20 MiB | 633.90 KiB |
| ee747ae | 1.58 MiB | 2.10 MiB | 530.95 KiB |
Previous results on branch: markushi/fix/jpc-masking-mismatch-weighted-text
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 4af8342 | 390.10 ms | 494.80 ms | 104.69 ms |
| c20e90d | 301.13 ms | 349.74 ms | 48.61 ms |
| 0f34885 | 305.96 ms | 359.02 ms | 53.07 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 4af8342 | 0 B | 0 B | 0 B |
| c20e90d | 0 B | 0 B | 0 B |
| 0f34885 | 0 B | 0 B | 0 B |
… one line of text" This reverts commit cfdcadc.
| layout.getLineLeft(line) | ||
| } | ||
| } | ||
|
|
||
| override fun getLineVisibleEnd(line: Int): Int = layout.getLineVisibleEnd(line) | ||
| override fun getLineRight(line: Int): Float { | ||
| return if (layout.ellipsizedWidth > 0 && layout.ellipsizedWidth < layout.width) { | ||
| layout.ellipsizedWidth.toFloat() | ||
| } else { | ||
| layout.getLineRight(line) | ||
| } | ||
| } |
There was a problem hiding this comment.
Bug: The global ellipsization check in getLineLeft()/getLineRight() breaks masking for non-ellipsized lines in a multiline TextView by incorrectly applying fixed bounds to all lines.
Severity: MEDIUM
Suggested Fix
Modify getLineLeft() and getLineRight() to check for ellipsization on a per-line basis, similar to the previous implementation that used getEllipsisCount(i). This ensures that only the lines that are actually ellipsized receive the fixed bounds, while other lines retain their correct, alignment-based bounds. Add a new test case with a multiline, aligned TextView where only some lines are ellipsized to prevent regressions.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location:
sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt#L198-L212
Potential issue: In `AndroidTextLayout.getLineLeft()` and `getLineRight()`, the code
checks for ellipsization globally using `layout.ellipsizedWidth`. For a multiline
`TextView` with alignment (e.g., center) where only some lines are ellipsized, this new
logic incorrectly applies the fixed ellipsized bounds (e.g., `0f` to
`layout.ellipsizedWidth`) to all lines. This overrides the correct, aligned bounds for
lines that are not ellipsized, leading to incorrect masking rectangles for those lines
during session replay capture. The existing tests do not cover this multiline scenario,
as they only use single-line `TextView`s.
Did we get this right? 👍 / 👎 to inform future reviews.
Description
Fix Compose text masking for layouts where text alignment (
TextAlign.Center,TextAlign.End) or weighted layouts produce incorrect masking bounding boxes.Root Cause
Compose's
MultiParagraphalways usesconstraints.maxWidthas the paragraph width, but the Text composable may size its node to a smaller content width. This causesgetLineLeft/getLineRightto return positions in the paragraph coordinate system that don't match the node's actual bounds, resulting in masking rects placed at wrong positions (e.g. off-screen for end-aligned weighted text).Changes
TextLayoutinterface: replacedgetPrimaryHorizontal,getEllipsisCount,getLineVisibleEnd,getLineStartwithgetLineLeft/getLineRight— directly using the layout's line bounds instead of computing them from character offsetslineWidthstarting from x=0, since text alignment has no visible effect in content-wrapped nodeshasFillModifierworkaround: no longer needed since we usegetLineLeft/getLineRightdirectlyMotivation and Context
The
Fillmodifier detection was a fragile workaround that didn't cover all cases (e.g. weighted text). The new approach correctly handles all text alignment and sizing scenarios by comparing the paragraph width against the actual node width.How did you test it?
Checklist
sendDefaultPIIis enabled.