@@ -11,6 +11,7 @@ package com.datadog.android.compose.internal.utils
11
11
import androidx.compose.ui.geometry.Rect
12
12
import androidx.compose.ui.layout.boundsInWindow
13
13
import androidx.compose.ui.node.LayoutNode
14
+ import androidx.compose.ui.semantics.Role
14
15
import androidx.compose.ui.semantics.SemanticsActions
15
16
import androidx.compose.ui.semantics.SemanticsModifier
16
17
import androidx.compose.ui.semantics.SemanticsProperties
@@ -24,12 +25,15 @@ import com.datadog.android.rum.RumAttributes.ACTION_TARGET_SELECTED
24
25
25
26
internal class LayoutNodeUtils {
26
27
27
- @Suppress(" NestedBlockDepth" )
28
+ @Suppress(" NestedBlockDepth" , " CyclomaticComplexMethod " )
28
29
fun resolveLayoutNode (node : LayoutNode ): TargetNode ? {
29
30
return runSafe {
30
31
var isClickable = false
31
32
var isScrollable = false
32
33
var datadogTag: String? = null
34
+ var role: Role ? = null
35
+ var selected: Boolean? = null
36
+ val customAttributes = mutableMapOf<String , Any ?>()
33
37
for (info in node.getModifierInfo()) {
34
38
val modifier = info.modifier
35
39
if (modifier is SemanticsModifier ) {
@@ -43,42 +47,51 @@ internal class LayoutNodeUtils {
43
47
getOrNull(DatadogSemanticsPropertyKey )?.let {
44
48
datadogTag = it
45
49
}
50
+ selected = selected ? : getOrNull(SemanticsProperties .Selected )
51
+ role = role ? : getOrNull(SemanticsProperties .Role )
46
52
}
47
53
} else {
48
- val className = modifier::class .qualifiedName
49
- if (className == CLASS_NAME_CLICKABLE_ELEMENT ||
50
- className == CLASS_NAME_COMBINED_CLICKABLE_ELEMENT ||
51
- className == CLASS_NAME_TOGGLEABLE_ELEMENT
52
- ) {
53
- isClickable = true
54
- } else if (className == CLASS_NAME_SCROLLING_LAYOUT_ELEMENT ||
55
- className == CLASS_NAME_SCROLLABLE_ELEMENT
56
- ) {
57
- isScrollable = true
54
+ when (modifier::class .qualifiedName) {
55
+ CLASS_NAME_CLICKABLE_ELEMENT ,
56
+ CLASS_NAME_COMBINED_CLICKABLE_ELEMENT -> {
57
+ role = role ? : getRole(modifier)
58
+ isClickable = true
59
+ }
60
+
61
+ CLASS_NAME_TOGGLEABLE_ELEMENT -> {
62
+ isClickable = true
63
+ }
64
+
65
+ CLASS_NAME_SCROLLING_LAYOUT_ELEMENT ,
66
+ CLASS_NAME_SCROLLABLE_ELEMENT -> {
67
+ isScrollable = true
68
+ }
58
69
}
59
70
}
60
71
}
61
-
72
+ selected?.let {
73
+ customAttributes[ACTION_TARGET_SELECTED ] = it
74
+ }
75
+ role?.let {
76
+ customAttributes[ACTION_TARGET_ROLE ] = it
77
+ }
62
78
datadogTag?.let {
63
79
TargetNode (
64
80
tag = it,
65
81
isClickable = isClickable,
66
82
isScrollable = isScrollable,
67
- customAttributes = resolveCustomAttributes(node )
83
+ customAttributes = customAttributes.toMap( )
68
84
)
69
85
}
70
86
}
71
87
}
72
88
73
- private fun resolveCustomAttributes (node : LayoutNode ): Map <String , Any ?> {
74
- return node.collapsedSemantics?.let { configuration ->
75
- val selected = configuration.getOrNull(SemanticsProperties .Selected )
76
- val role = configuration.getOrNull(SemanticsProperties .Role )
77
- mapOf (
78
- ACTION_TARGET_SELECTED to selected,
79
- ACTION_TARGET_ROLE to role
80
- )
81
- }.orEmpty()
89
+ @Suppress(" UnsafeThirdPartyFunctionCall" )
90
+ // Function is wrapped with `runSafe` in the call site.
91
+ private fun getRole (obj : Any ): Role {
92
+ val roleField = obj::class .java.getDeclaredField(" role" )
93
+ roleField.isAccessible = true
94
+ return roleField.get(obj) as Role
82
95
}
83
96
84
97
fun getLayoutNodeBoundsInWindow (node : LayoutNode ): Rect ? {
@@ -90,7 +103,7 @@ internal class LayoutNodeUtils {
90
103
private fun <T > runSafe (action : () -> T ): T ? {
91
104
try {
92
105
return action()
93
- } catch (@Suppress(" TooGenericExceptionCaught" ) e: Exception ) {
106
+ } catch (@Suppress(" TooGenericExceptionCaught" ) e: Throwable ) {
94
107
// We rely on visibility suppression to access internal field,
95
108
// any runtime exception must be caught here.
96
109
(Datadog .getInstance() as ? FeatureSdkCore )?.internalLogger?.log(
0 commit comments