Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
- Make `RequestDetailsResolver` public ([#4326](https://github.com/getsentry/sentry-java/pull/4326))
- `RequestDetailsResolver` is now public and has an additional constructor, making it easier to use a custom `TransportFactory`

### Fixes

- Session Replay: Fix masking of non-styled `Text` Composables ([#4361](https://github.com/getsentry/sentry-java/pull/4361))

## 8.10.0

### Features
Expand Down
8 changes: 8 additions & 0 deletions scripts/toggle-codec-logs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ case "$ACTION" in
adb shell setprop log.tag.BufferQueueProducer D
adb shell setprop log.tag.ReflectedParamUpdater D
adb shell setprop log.tag.hw-BpHwBinder D
adb shell setprop log.tag.ACodec D
Copy link
Member Author

@romtsn romtsn Apr 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found these when running on a pixel device, so just added them here, not related to this PR

adb shell setprop log.tag.VideoCapabilities D
adb shell setprop log.tag.OMXUtils D
adb shell setprop log.tag.OMXClient D
echo "✅ Logs ENABLED"
;;
disable)
Expand All @@ -67,6 +71,10 @@ case "$ACTION" in
adb shell setprop log.tag.BufferQueueProducer SILENT
adb shell setprop log.tag.ReflectedParamUpdater SILENT
adb shell setprop log.tag.hw-BpHwBinder SILENT
adb shell setprop log.tag.ACodec SILENT
adb shell setprop log.tag.VideoCapabilities SILENT
adb shell setprop log.tag.OMXUtils SILENT
adb shell setprop log.tag.OMXClient SILENT
echo "🚫 Logs DISABLED"
;;
*)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.ui.semantics.SemanticsActions
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.semantics.getOrNull
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.unit.TextUnit
import io.sentry.SentryLevel
import io.sentry.SentryOptions
import io.sentry.SentryReplayOptions
Expand Down Expand Up @@ -100,14 +101,19 @@ internal object ComposeViewHierarchyNode {
?.invoke(textLayoutResults)

val (color, hasFillModifier) = node.findTextAttributes()
var textColor = textLayoutResults.firstOrNull()?.layoutInput?.style?.color
val textLayoutResult = textLayoutResults.firstOrNull()
var textColor = textLayoutResult?.layoutInput?.style?.color
if (textColor?.isUnspecified == true) {
textColor = color
}
// TODO: support multiple text layouts
val isLaidOut = textLayoutResult?.layoutInput?.style?.fontSize != TextUnit.Unspecified
// TODO: support editable text (currently there's a way to get @Composable's padding only via reflection, and we can't reliably mask input fields based on TextLayout, so we mask the whole view instead)
TextViewHierarchyNode(
layout = if (textLayoutResults.isNotEmpty() && !isEditable) ComposeTextLayout(textLayoutResults.first(), hasFillModifier) else null,
layout = if (textLayoutResult != null && !isEditable && isLaidOut) {
ComposeTextLayout(textLayoutResult, hasFillModifier)
} else {
null
},
dominantColor = textColor?.toArgb()?.toOpaque(),
x = visibleRect.left.toFloat(),
y = visibleRect.top.toFloat(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.test.ext.junit.runners.AndroidJUnit4
import coil.compose.AsyncImage
import io.sentry.SentryOptions
Expand All @@ -41,6 +43,7 @@ import java.io.File
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
Expand All @@ -53,6 +56,7 @@ class ComposeMaskingOptionsTest {
System.setProperty("robolectric.pixelCopyRenderMode", "hardware")
ComposeMaskingOptionsActivity.textModifierApplier = null
ComposeMaskingOptionsActivity.containerModifierApplier = null
ComposeMaskingOptionsActivity.fontSizeApplier = null
}

@Test
Expand All @@ -67,8 +71,23 @@ class ComposeMaskingOptionsTest {
val textNodes = activity.get().collectNodesOfType<TextViewHierarchyNode>(options)
assertEquals(4, textNodes.size) // [TextField, Text, Button, Activity Title]
assertTrue(textNodes.all { it.shouldMask })
// just a sanity check for parsing the tree
assertEquals("Random repo", (textNodes[1].layout as ComposeTextLayout).layout.layoutInput.text.text)
// no fontSize specified - we don't use the text layout
assertNull(textNodes.first().layout)
}

@Test
fun `when text is laid out nodes use it`() {
ComposeMaskingOptionsActivity.fontSizeApplier = { 20.sp }
val activity = buildActivity(ComposeMaskingOptionsActivity::class.java).setup()
shadowOf(Looper.getMainLooper()).idle()

val options = SentryOptions().apply {
sessionReplay.maskAllText = true
}

val textNodes = activity.get().collectNodesOfType<TextViewHierarchyNode>(options)
// the text should be laid out when fontSize is specified
assertEquals("Random repo", (textNodes.first().layout as? ComposeTextLayout)?.layout?.layoutInput?.text?.text)
}

@Test
Expand Down Expand Up @@ -213,6 +232,7 @@ private class ComposeMaskingOptionsActivity : ComponentActivity() {
companion object {
var textModifierApplier: (() -> Modifier)? = null
var containerModifierApplier: (() -> Modifier)? = null
var fontSizeApplier: (() -> TextUnit)? = null
}

override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -232,11 +252,11 @@ private class ComposeMaskingOptionsActivity : ComponentActivity() {
contentDescription = null,
modifier = Modifier.padding(vertical = 16.dp)
)
Text("Random repo", fontSize = fontSizeApplier?.invoke() ?: TextUnit.Unspecified)
TextField(
value = TextFieldValue("Placeholder"),
onValueChange = { _ -> }
)
Text("Random repo")
Button(
onClick = {},
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@ fun Github(
val scope = rememberCoroutineScope()

LaunchedEffect(perPage) {
result = GithubAPI.service.listReposAsync(user.text, perPage).random().full_name
result = try {
GithubAPI.service.listReposAsync(user.text, perPage).random().full_name
} catch (e: Throwable) {
"error"
}
}

SentryTraced("github-$user") {
Expand All @@ -133,12 +137,15 @@ fun Github(
user = newText
}
)
Text("Random repo $result")
Text("Random repo: $result")
Button(
onClick = {
scope.launch {
result =
result = try {
GithubAPI.service.listReposAsync(user.text, perPage).random().full_name
} catch (e: Throwable) {
"error"
}
}
},
modifier = Modifier
Expand Down
Loading