@@ -44,69 +44,57 @@ 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
55- var isClickable = false
56- var isScrollable = false
57-
5855 while (! queue.isEmpty()) {
59- val node = queue.poll() ? : continue
56+ val ( node, parentTag) = queue.poll() ? : continue
6057 if (node.isPlaced && layoutNodeBoundsContain(rootLayoutNode, node, x, y)) {
61-
62- val modifiers = node.getModifierInfo()
63- for (index in modifiers.indices) {
64- val modifierInfo = modifiers[index]
65- val tag = composeHelper!! .extractTag(modifierInfo.modifier)
66- if (tag != null ) {
67- lastKnownTag = tag
68- }
69-
70- if (modifierInfo.modifier is SemanticsModifier ) {
71- val semanticsModifierCore = modifierInfo.modifier as SemanticsModifier
72- val semanticsConfiguration = semanticsModifierCore.semanticsConfiguration
73-
74- for (item in semanticsConfiguration) {
75- val key: String = item.key.name
76- if (" ScrollBy" == key) {
77- isScrollable = true
78- } else if (" OnClick" == key) {
79- 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 )
8091 }
81- }
82- } else {
83- val modifier = modifierInfo.modifier
84- // Newer Jetpack Compose 1.5 uses Node modifiers for clicks/scrolls
85- val type = modifier.javaClass.name
86- if (
87- " androidx.compose.foundation.ClickableElement" == type ||
88- " androidx.compose.foundation.CombinedClickableElement" == type
89- ) {
90- isClickable = true
91- } else if (
92- " androidx.compose.foundation.ScrollingLayoutElement" == type ||
93- " androidx.compose.foundation.ScrollingContainerElement" == type
94- ) {
95- isScrollable = true
9692 }
9793 }
9894 }
99-
100- if (isClickable && targetType == UiElement .Type .CLICKABLE ) {
101- targetTag = lastKnownTag
102- }
103- if (isScrollable && targetType == UiElement .Type .SCROLLABLE ) {
104- targetTag = lastKnownTag
105- // skip any children for scrollable targets
106- break
107- }
10895 }
109- queue.addAll(node.zSortedChildren.asMutableList())
96+
97+ queue.addAll(node.zSortedChildren.asMutableList().map { Pair (it, parentTag) })
11098 }
11199
112100 return if (targetTag == null ) {
@@ -126,6 +114,19 @@ public class ComposeGestureTargetLocator(private val logger: ILogger) : GestureT
126114 return bounds.contains(Offset (x, y))
127115 }
128116
117+ private fun extractTag (composeHelper : SentryComposeHelper , node : LayoutNode ): String? {
118+ var lastKnownTag: String? = null
119+ val modifiers = node.getModifierInfo()
120+ for (index in modifiers.indices) {
121+ val modifierInfo = modifiers[index]
122+ val tag = composeHelper.extractTag(modifierInfo.modifier)
123+ if (tag != null ) {
124+ lastKnownTag = tag
125+ }
126+ }
127+ return lastKnownTag
128+ }
129+
129130 public companion object {
130131 private const val ORIGIN = " jetpack_compose"
131132 }
0 commit comments