Skip to content

Commit a49b6a7

Browse files
authored
fix: compose autocapture properties update (#261)
1 parent 61eb7ee commit a49b6a7

File tree

2 files changed

+54
-32
lines changed

2 files changed

+54
-32
lines changed

android/src/main/java/com/amplitude/android/internal/ViewHierarchyScanner.kt

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,24 @@ internal object ViewHierarchyScanner {
7272
if (view is ViewGroup) {
7373
queue.addAll(view.children)
7474
}
75-
76-
// Applies the locators until a target is found. If the target type is clickable, check
77-
// the children in case the target is a child which is also clickable.
78-
viewTargetLocators.any { locator ->
79-
with(locator) {
80-
view.locate(targetPosition, targetType)?.let { newTarget ->
81-
if (targetType == ViewTarget.Type.Clickable) {
82-
target = newTarget
83-
return@any true
84-
} else {
85-
return newTarget
75+
try {
76+
// Applies the locators until a target is found. If the target type is clickable, check
77+
// the children in case the target is a child which is also clickable.
78+
viewTargetLocators.any { locator ->
79+
with(locator) {
80+
view.locate(targetPosition, targetType)?.let { newTarget ->
81+
if (targetType == ViewTarget.Type.Clickable) {
82+
target = newTarget
83+
return@any true
84+
} else {
85+
return newTarget
86+
}
8687
}
88+
false
8789
}
88-
false
8990
}
91+
} catch (e: ClassCastException) {
92+
logger.error("Error while locating target in view hierarchy: $e")
9093
}
9194
}
9295

android/src/main/java/com/amplitude/android/internal/locators/ComposeViewTargetLocator.java

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
import androidx.annotation.OptIn;
44
import androidx.compose.ui.InternalComposeUiApi;
5+
import androidx.compose.ui.Modifier;
56
import androidx.compose.ui.geometry.Rect;
67
import androidx.compose.ui.layout.ModifierInfo;
78
import androidx.compose.ui.node.LayoutNode;
89
import androidx.compose.ui.node.Owner;
9-
import androidx.compose.ui.semantics.SemanticsConfiguration;
10-
import androidx.compose.ui.semantics.SemanticsModifier;
11-
import androidx.compose.ui.semantics.SemanticsPropertyKey;
10+
import androidx.compose.ui.platform.InspectableValue;
11+
import androidx.compose.ui.platform.ValueElement;
1212

1313
import com.amplitude.android.internal.ViewTarget;
1414
import com.amplitude.common.Logger;
@@ -17,6 +17,8 @@
1717
import org.jetbrains.annotations.Nullable;
1818

1919
import java.util.ArrayDeque;
20+
import java.util.Iterator;
21+
import java.util.LinkedHashMap;
2022
import java.util.List;
2123
import java.util.Map;
2224
import java.util.Queue;
@@ -72,27 +74,44 @@ public ViewTarget locate(
7274
boolean isClickable = false;
7375
final List<ModifierInfo> modifiers = node.getModifierInfo();
7476
for (ModifierInfo modifierInfo : modifiers) {
75-
if (modifierInfo.getModifier() instanceof SemanticsModifier) {
76-
final SemanticsModifier semanticsModifierCore =
77-
(SemanticsModifier) modifierInfo.getModifier();
78-
final SemanticsConfiguration semanticsConfiguration =
79-
semanticsModifierCore.getSemanticsConfiguration();
80-
for (Map.Entry<? extends SemanticsPropertyKey<?>, ?> entry : semanticsConfiguration) {
81-
final @Nullable String key = entry.getKey().getName();
82-
if ("OnClick".equals(key)) {
83-
isClickable = true;
84-
} else if ("TestTag".equals(key)) {
85-
if (entry.getValue() instanceof String) {
86-
lastKnownTag = (String) entry.getValue();
77+
final Modifier modifier = modifierInfo.getModifier();
78+
if (modifier instanceof InspectableValue) {
79+
final InspectableValue inspectableValue = (InspectableValue) modifier;
80+
if ("testTag".equals(inspectableValue.getNameFallback())) {
81+
Iterator<ValueElement> iterator = inspectableValue.getInspectableElements().iterator();
82+
while (iterator.hasNext()) {
83+
final ValueElement element = iterator.next();
84+
if ("tag".equals(element.getName())) {
85+
lastKnownTag = (String) element.getValue();
86+
break;
87+
}
88+
}
89+
} else if ("semantics".equals(inspectableValue.getNameFallback())) {
90+
Iterator<ValueElement> iterator = inspectableValue.getInspectableElements().iterator();
91+
while (iterator.hasNext()) {
92+
final ValueElement element = iterator.next();
93+
if ("properties".equals(element.getName())) {
94+
final Object elementValue = element.getValue();
95+
if (elementValue instanceof LinkedHashMap) {
96+
final LinkedHashMap<Object, Object> properties = (LinkedHashMap<Object, Object>) elementValue;
97+
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
98+
if ("TestTag".equals(entry.getKey())) {
99+
lastKnownTag = (String) entry.getValue();
100+
break;
101+
}
102+
}
103+
}
87104
}
88105
}
89106
}
90-
} else {
91-
// Newer Jetpack Compose 1.5 uses Node modifiers for clicks/scrolls
92-
final @Nullable String type = modifierInfo.getModifier().getClass().getCanonicalName();
93-
if ("androidx.compose.foundation.ClickableElement".equals(type)
94-
|| "androidx.compose.foundation.CombinedClickableElement".equals(type)) {
107+
if ("clickable".equals(inspectableValue.getNameFallback())) {
95108
isClickable = true;
109+
} else {
110+
final @Nullable String type = modifier.getClass().getCanonicalName();
111+
if ("androidx.compose.foundation.ClickableElement".equals(type)
112+
|| "androidx.compose.foundation.CombinedClickableElement".equals(type)) {
113+
isClickable = true;
114+
}
96115
}
97116
}
98117
}

0 commit comments

Comments
 (0)