diff --git a/android/src/main/java/com/facebook/sonar/plugins/inspector/InspectorSonarPlugin.java b/android/src/main/java/com/facebook/sonar/plugins/inspector/InspectorSonarPlugin.java index bc22cd90a..a2e54b357 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/inspector/InspectorSonarPlugin.java +++ b/android/src/main/java/com/facebook/sonar/plugins/inspector/InspectorSonarPlugin.java @@ -372,7 +372,9 @@ public class InspectorSonarPlugin implements SonarPlugin { public void onReceiveOnMainThread(SonarObject params, SonarResponder responder) throws Exception { final String query = params.getString("query"); - final SearchResultNode matchTree = searchTree(query.toLowerCase(), mApplication); + final boolean axEnabled = params.getBoolean("axEnabled"); + + final SearchResultNode matchTree = searchTree(query.toLowerCase(), mApplication, axEnabled); final SonarObject results = matchTree == null ? null : matchTree.toSonarObject(); final SonarObject response = new SonarObject.Builder().put("results", results).put("query", query).build(); @@ -503,14 +505,19 @@ public class InspectorSonarPlugin implements SonarPlugin { descriptor.setHighlighted(obj, highlighted, isAlignmentMode); } - public SearchResultNode searchTree(String query, Object obj) throws Exception { + private boolean hasAXNode(SonarObject node) { + SonarObject extraInfo = node.getObject("extraInfo"); + return extraInfo != null && extraInfo.getBoolean("hasAXNode"); + } + + public SearchResultNode searchTree(String query, Object obj, boolean axEnabled) throws Exception { final NodeDescriptor descriptor = descriptorForObject(obj); List childTrees = null; boolean isMatch = descriptor.matches(query, obj); for (int i = 0; i < descriptor.getChildCount(obj); i++) { Object child = descriptor.getChildAt(obj, i); - SearchResultNode childNode = searchTree(query, child); + SearchResultNode childNode = searchTree(query, child, axEnabled); if (childNode != null) { if (childTrees == null) { childTrees = new ArrayList<>(); @@ -521,7 +528,8 @@ public class InspectorSonarPlugin implements SonarPlugin { if (isMatch || childTrees != null) { final String id = trackObject(obj); - return new SearchResultNode(id, isMatch, getNode(id), childTrees); + SonarObject node = getNode(id); + return new SearchResultNode(id, isMatch, node, childTrees, axEnabled && hasAXNode(node) ? getAXNode(id) : null); } return null; } diff --git a/android/src/main/java/com/facebook/sonar/plugins/inspector/SearchResultNode.java b/android/src/main/java/com/facebook/sonar/plugins/inspector/SearchResultNode.java index 2c6ef5ad4..28829cf21 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/inspector/SearchResultNode.java +++ b/android/src/main/java/com/facebook/sonar/plugins/inspector/SearchResultNode.java @@ -18,15 +18,17 @@ public class SearchResultNode { private final String id; private final boolean isMatch; private final SonarObject element; + private final SonarObject axElement; @Nullable private final List children; SearchResultNode( - String id, boolean isMatch, SonarObject element, @Nullable List children) { + String id, boolean isMatch, SonarObject element, List children, SonarObject axElement) { this.id = id; this.isMatch = isMatch; this.element = element; this.children = children; + this.axElement = axElement; } SonarObject toSonarObject() { @@ -44,6 +46,7 @@ public class SearchResultNode { return new SonarObject.Builder() .put("id", this.id) .put("isMatch", this.isMatch) + .put("axElement", this.axElement) .put("element", this.element) .put("children", childArray) .build(); diff --git a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ApplicationDescriptor.java b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ApplicationDescriptor.java index 4b713c72d..da1b7b14e 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ApplicationDescriptor.java +++ b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ApplicationDescriptor.java @@ -206,6 +206,11 @@ public class ApplicationDescriptor extends NodeDescriptor { return Collections.EMPTY_LIST; } + @Override + public SonarObject getExtraInfo(ApplicationWrapper node) { + return new SonarObject.Builder().put("hasAXNode", true).build(); + } + @Override public void setHighlighted(ApplicationWrapper node, boolean selected, boolean isAlignmentMode) throws Exception { final int childCount = getChildCount(node); diff --git a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewDescriptor.java b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewDescriptor.java index 098a61fc3..321fbed8c 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewDescriptor.java +++ b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/ViewDescriptor.java @@ -463,7 +463,10 @@ public class ViewDescriptor extends NodeDescriptor { @Override public SonarObject getExtraInfo(View node) { - return new SonarObject.Builder().put("focused", AccessibilityUtil.isAXFocused(node)).build(); + return new SonarObject.Builder() + .put("focused", AccessibilityUtil.isAXFocused(node)) + .put("hasAXNode", true) + .build(); } @Nullable diff --git a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/utils/AccessibilityRoleUtil.java b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/utils/AccessibilityRoleUtil.java index 367b7732f..766616f72 100644 --- a/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/utils/AccessibilityRoleUtil.java +++ b/android/src/main/java/com/facebook/sonar/plugins/inspector/descriptors/utils/AccessibilityRoleUtil.java @@ -86,6 +86,9 @@ public class AccessibilityRoleUtil { } public static AccessibilityRole getRole(View view) { + if (view == null) { + return AccessibilityRole.NONE; + } AccessibilityNodeInfoCompat nodeInfo = AccessibilityNodeInfoCompat.obtain(); ViewCompat.onInitializeAccessibilityNodeInfo(view, nodeInfo); AccessibilityRole role = getRole(nodeInfo); diff --git a/src/plugins/layout/index.js b/src/plugins/layout/index.js index 807f34ffb..aba54121d 100644 --- a/src/plugins/layout/index.js +++ b/src/plugins/layout/index.js @@ -107,6 +107,7 @@ type SearchResultTree = {| isMatch: Boolean, children: ?Array, element: Element, + axElement: Element, |}; const LoadingSpinner = LoadingIndicator.extends({ @@ -333,15 +334,17 @@ export default class Layout extends SonarPlugin { }; search(query: string) { - if (!query) { - return; - } this.setState({ outstandingSearchQuery: query, }); - this.client - .call('getSearchResults', {query: query}) - .then(response => this.displaySearchResults(response)); + + if (!query) { + this.displaySearchResults({query: '', results: null}); + } else { + this.client + .call('getSearchResults', {query: query, axEnabled: this.axEnabled()}) + .then(response => this.displaySearchResults(response)); + } } executeCommand(command: string) { @@ -391,7 +394,7 @@ export default class Layout extends SonarPlugin { results, query, }: { - results: SearchResultTree, + results: ?SearchResultTree, query: string, }) { const elements = this.getElementsFromSearchResultTree(results); @@ -405,10 +408,29 @@ export default class Layout extends SonarPlugin { elements: elements.map(x => x.element), type: 'UpdateElements', }); + this.dispatchAction({ elements: idsToExpand, type: 'ExpandElements', }); + + if (this.axEnabled()) { + const AXelements = elements.filter(x => x.axElement); + const AXidsToExpand = AXelements.filter(x => x.hasChildren).map( + x => x.axElement.id, + ); + + this.dispatchAction({ + elements: AXelements.map(x => x.axElement), + type: 'UpdateAXElements', + }); + + this.dispatchAction({ + elements: AXidsToExpand, + type: 'ExpandAXElements', + }); + } + this.setState({ searchResults: { matches: new Set( @@ -422,7 +444,7 @@ export default class Layout extends SonarPlugin { }); } - getElementsFromSearchResultTree(tree: SearchResultTree) { + getElementsFromSearchResultTree(tree: ?SearchResultTree) { if (!tree) { return []; } @@ -432,6 +454,7 @@ export default class Layout extends SonarPlugin { isMatch: tree.isMatch, hasChildren: Boolean(tree.children), element: tree.element, + axElement: tree.axElement, }, ]; if (tree.children) { @@ -842,12 +865,7 @@ export default class Layout extends SonarPlugin { onElementSelected = debounce((selectedKey: ElementID) => { const {key, AXkey} = this.getKeysFromSelected(selectedKey); - - this.dispatchAction({ - key: key, - AXkey: AXkey, - type: 'SelectElement', - }); + this.dispatchAction({key, AXkey, type: 'SelectElement'}); this.client.send('setHighlighted', { id: selectedKey,