Skip to content

Commit 62e7f05

Browse files
authored
Merge a24c545 into 38676c9
2 parents 38676c9 + a24c545 commit 62e7f05

File tree

3 files changed

+28
-2
lines changed

3 files changed

+28
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Fixes
66

77
- Session Replay: Fix masking of non-styled `Text` Composables ([#4361](https://github.com/getsentry/sentry-java/pull/4361))
8+
- Session Replay: Fix masking read-only `TextField` Composables ([#4362](https://github.com/getsentry/sentry-java/pull/4362))
89

910
## 8.9.0
1011

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ internal object ComposeViewHierarchyNode {
4141
return when {
4242
isImage -> SentryReplayOptions.IMAGE_VIEW_CLASS_NAME
4343
collapsedSemantics?.contains(SemanticsProperties.Text) == true ||
44-
collapsedSemantics?.contains(SemanticsActions.SetText) == true -> SentryReplayOptions.TEXT_VIEW_CLASS_NAME
44+
collapsedSemantics?.contains(SemanticsActions.SetText) == true ||
45+
collapsedSemantics?.contains(SemanticsProperties.EditableText) == true -> SentryReplayOptions.TEXT_VIEW_CLASS_NAME
4546
else -> "android.view.View"
4647
}
4748
}
@@ -87,7 +88,8 @@ internal object ComposeViewHierarchyNode {
8788
val isVisible = !node.outerCoordinator.isTransparent() &&
8889
(semantics == null || !semantics.contains(SemanticsProperties.InvisibleToUser)) &&
8990
visibleRect.height() > 0 && visibleRect.width() > 0
90-
val isEditable = semantics?.contains(SemanticsActions.SetText) == true
91+
val isEditable = semantics?.contains(SemanticsActions.SetText) == true ||
92+
semantics?.contains(SemanticsProperties.EditableText) == true
9193
return when {
9294
semantics?.contains(SemanticsProperties.Text) == true || isEditable -> {
9395
val shouldMask = isVisible && node.shouldMask(isImage = false, options)

sentry-android-replay/src/test/java/io/sentry/android/replay/viewhierarchy/ComposeMaskingOptionsTest.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ import androidx.compose.material3.TextField
1616
import androidx.compose.ui.Alignment
1717
import androidx.compose.ui.Modifier
1818
import androidx.compose.ui.platform.testTag
19+
import androidx.compose.ui.semantics.clearAndSetSemantics
20+
import androidx.compose.ui.semantics.editableText
1921
import androidx.compose.ui.semantics.invisibleToUser
2022
import androidx.compose.ui.semantics.semantics
23+
import androidx.compose.ui.text.AnnotatedString
2124
import androidx.compose.ui.text.input.TextFieldValue
2225
import androidx.compose.ui.unit.TextUnit
2326
import androidx.compose.ui.unit.dp
@@ -55,6 +58,7 @@ class ComposeMaskingOptionsTest {
5558
System.setProperty("robolectric.areWindowsMarkedVisible", "true")
5659
System.setProperty("robolectric.pixelCopyRenderMode", "hardware")
5760
ComposeMaskingOptionsActivity.textModifierApplier = null
61+
ComposeMaskingOptionsActivity.textFieldModifierApplier = null
5862
ComposeMaskingOptionsActivity.containerModifierApplier = null
5963
ComposeMaskingOptionsActivity.fontSizeApplier = null
6064
}
@@ -90,6 +94,23 @@ class ComposeMaskingOptionsTest {
9094
assertEquals("Random repo", (textNodes.first().layout as? ComposeTextLayout)?.layout?.layoutInput?.text?.text)
9195
}
9296

97+
@Test
98+
fun `when text input field is readOnly still masks it`() {
99+
ComposeMaskingOptionsActivity.textFieldModifierApplier = {
100+
// newer versions of compose basically do this when a TextField is readOnly
101+
Modifier.clearAndSetSemantics { editableText = AnnotatedString("Placeholder") }
102+
}
103+
val activity = buildActivity(ComposeMaskingOptionsActivity::class.java).setup()
104+
shadowOf(Looper.getMainLooper()).idle()
105+
106+
val options = SentryOptions().apply {
107+
sessionReplay.maskAllText = true
108+
}
109+
110+
val textNodes = activity.get().collectNodesOfType<TextViewHierarchyNode>(options)
111+
assertTrue(textNodes[1].shouldMask)
112+
}
113+
93114
@Test
94115
fun `when maskAllText is set to false all Text nodes are unmasked`() {
95116
val activity = buildActivity(ComposeMaskingOptionsActivity::class.java).setup()
@@ -231,6 +252,7 @@ private class ComposeMaskingOptionsActivity : ComponentActivity() {
231252

232253
companion object {
233254
var textModifierApplier: (() -> Modifier)? = null
255+
var textFieldModifierApplier: (() -> Modifier)? = null
234256
var containerModifierApplier: (() -> Modifier)? = null
235257
var fontSizeApplier: (() -> TextUnit)? = null
236258
}
@@ -254,6 +276,7 @@ private class ComposeMaskingOptionsActivity : ComponentActivity() {
254276
)
255277
Text("Random repo", fontSize = fontSizeApplier?.invoke() ?: TextUnit.Unspecified)
256278
TextField(
279+
modifier = textFieldModifierApplier?.invoke() ?: Modifier,
257280
value = TextFieldValue("Placeholder"),
258281
onValueChange = { _ -> }
259282
)

0 commit comments

Comments
 (0)