@@ -44,68 +44,56 @@ public class ComposeGestureTargetLocator(private val logger: ILogger) : GestureT
4444
4545 val rootLayoutNode = root.root
4646
47- val queue: Queue <LayoutNode > = LinkedList ()
48- queue.add(rootLayoutNode)
47+ // Pair<Node, ParentTag>
48+ val queue: Queue <Pair <LayoutNode , String ?>> = LinkedList ()
49+ queue.add(Pair (rootLayoutNode, null ))
4950
50- // the final tag to return
51+ // the final tag to return, only relevant for clicks
52+ // as for scrolls, we return the first matching element
5153 var targetTag: String? = null
5254
53- // the last known tag when iterating the node tree
54- var lastKnownTag: String? = null
5555 while (! queue.isEmpty()) {
56- val node = queue.poll() ? : continue
56+ val ( node, parentTag) = queue.poll() ? : continue
5757 if (node.isPlaced && layoutNodeBoundsContain(rootLayoutNode, node, x, y)) {
58- var isClickable = false
59- var isScrollable = false
60-
61- val modifiers = node.getModifierInfo()
62- for (index in modifiers.indices) {
63- val modifierInfo = modifiers[index]
64- val tag = composeHelper!! .extractTag(modifierInfo.modifier)
65- if (tag != null ) {
66- lastKnownTag = tag
67- }
68-
69- if (modifierInfo.modifier is SemanticsModifier ) {
70- val semanticsModifierCore = modifierInfo.modifier as SemanticsModifier
71- val semanticsConfiguration = semanticsModifierCore.semanticsConfiguration
72-
73- for (item in semanticsConfiguration) {
74- val key: String = item.key.name
75- if (" ScrollBy" == key) {
76- isScrollable = true
77- } else if (" OnClick" == key) {
78- isClickable = true
58+ val tag = extractTag(composeHelper!! , node) ? : parentTag
59+ if (tag != null ) {
60+ val modifiers = node.getModifierInfo()
61+ for (index in modifiers.indices) {
62+ val modifierInfo = modifiers[index]
63+ if (modifierInfo.modifier is SemanticsModifier ) {
64+ val semanticsModifierCore = modifierInfo.modifier as SemanticsModifier
65+ val semanticsConfiguration = semanticsModifierCore.semanticsConfiguration
66+
67+ for (item in semanticsConfiguration) {
68+ val key: String = item.key.name
69+ if (targetType == UiElement .Type .SCROLLABLE && " ScrollBy" == key) {
70+ return UiElement (null , null , null , tag, ORIGIN )
71+ } else if (targetType == UiElement .Type .CLICKABLE && " OnClick" == key) {
72+ targetTag = tag
73+ }
74+ }
75+ } else {
76+ // Jetpack Compose 1.5+: uses Node modifiers elements for clicks/scrolls
77+ val modifier = modifierInfo.modifier
78+ val type = modifier.javaClass.name
79+ if (
80+ targetType == UiElement .Type .CLICKABLE &&
81+ (" androidx.compose.foundation.ClickableElement" == type ||
82+ " androidx.compose.foundation.CombinedClickableElement" == type)
83+ ) {
84+ targetTag = tag
85+ } else if (
86+ targetType == UiElement .Type .SCROLLABLE &&
87+ (" androidx.compose.foundation.ScrollingLayoutElement" == type ||
88+ " androidx.compose.foundation.ScrollingContainerElement" == type)
89+ ) {
90+ return UiElement (null , null , null , tag, ORIGIN )
7991 }
80- }
81- } else {
82- val modifier = modifierInfo.modifier
83- // Newer Jetpack Compose 1.5 uses Node modifiers for clicks/scrolls
84- val type = modifier.javaClass.name
85- if (
86- " androidx.compose.foundation.ClickableElement" == type ||
87- " androidx.compose.foundation.CombinedClickableElement" == type
88- ) {
89- isClickable = true
90- } else if (
91- " androidx.compose.foundation.ScrollingLayoutElement" == type ||
92- " androidx.compose.foundation.ScrollingContainerElement" == type
93- ) {
94- isScrollable = true
9592 }
9693 }
9794 }
98-
99- if (isClickable && targetType == UiElement .Type .CLICKABLE ) {
100- targetTag = lastKnownTag
101- }
102- if (isScrollable && targetType == UiElement .Type .SCROLLABLE ) {
103- targetTag = lastKnownTag
104- // skip any children for scrollable targets
105- break
106- }
95+ queue.addAll(node.zSortedChildren.asMutableList().map { Pair (it, tag) })
10796 }
108- queue.addAll(node.zSortedChildren.asMutableList())
10997 }
11098
11199 return if (targetTag == null ) {
@@ -125,6 +113,19 @@ public class ComposeGestureTargetLocator(private val logger: ILogger) : GestureT
125113 return bounds.contains(Offset (x, y))
126114 }
127115
116+ private fun extractTag (composeHelper : SentryComposeHelper , node : LayoutNode ): String? {
117+ var lastKnownTag: String? = null
118+ val modifiers = node.getModifierInfo()
119+ for (index in modifiers.indices) {
120+ val modifierInfo = modifiers[index]
121+ val tag = composeHelper.extractTag(modifierInfo.modifier)
122+ if (tag != null ) {
123+ lastKnownTag = tag
124+ }
125+ }
126+ return lastKnownTag
127+ }
128+
128129 public companion object {
129130 private const val ORIGIN = " jetpack_compose"
130131 }
0 commit comments