AX tree expands with main tree on search (cannot search ax tree yet)

Summary: AX tree will now stay in sync with the main tree when searching. Also allows user to completely erase search (previously had left one letter highlighted in the tree even if entire query was erased).

Reviewed By: danielbuechele

Differential Revision: D9276721

fbshipit-source-id: 5272bb9cf3400ad3eb9d16bf438b0e5d4b551c6a
This commit is contained in:
Sara Valderrama
2018-08-13 13:21:48 -07:00
committed by Facebook Github Bot
parent 00847365ef
commit 656044ce69
6 changed files with 60 additions and 20 deletions

View File

@@ -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<SearchResultNode> 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;
}

View File

@@ -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<SearchResultNode> children;
SearchResultNode(
String id, boolean isMatch, SonarObject element, @Nullable List<SearchResultNode> children) {
String id, boolean isMatch, SonarObject element, List<SearchResultNode> 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();

View File

@@ -206,6 +206,11 @@ public class ApplicationDescriptor extends NodeDescriptor<ApplicationWrapper> {
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);

View File

@@ -463,7 +463,10 @@ public class ViewDescriptor extends NodeDescriptor<View> {
@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

View File

@@ -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);

View File

@@ -107,6 +107,7 @@ type SearchResultTree = {|
isMatch: Boolean,
children: ?Array<SearchResultTree>,
element: Element,
axElement: Element,
|};
const LoadingSpinner = LoadingIndicator.extends({
@@ -333,15 +334,17 @@ export default class Layout extends SonarPlugin<InspectorState> {
};
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<InspectorState> {
results,
query,
}: {
results: SearchResultTree,
results: ?SearchResultTree,
query: string,
}) {
const elements = this.getElementsFromSearchResultTree(results);
@@ -405,10 +408,29 @@ export default class Layout extends SonarPlugin<InspectorState> {
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<InspectorState> {
});
}
getElementsFromSearchResultTree(tree: SearchResultTree) {
getElementsFromSearchResultTree(tree: ?SearchResultTree) {
if (!tree) {
return [];
}
@@ -432,6 +454,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
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<InspectorState> {
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,