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) public void onReceiveOnMainThread(SonarObject params, SonarResponder responder)
throws Exception { throws Exception {
final String query = params.getString("query"); 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 results = matchTree == null ? null : matchTree.toSonarObject();
final SonarObject response = final SonarObject response =
new SonarObject.Builder().put("results", results).put("query", query).build(); new SonarObject.Builder().put("results", results).put("query", query).build();
@@ -503,14 +505,19 @@ public class InspectorSonarPlugin implements SonarPlugin {
descriptor.setHighlighted(obj, highlighted, isAlignmentMode); 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); final NodeDescriptor descriptor = descriptorForObject(obj);
List<SearchResultNode> childTrees = null; List<SearchResultNode> childTrees = null;
boolean isMatch = descriptor.matches(query, obj); boolean isMatch = descriptor.matches(query, obj);
for (int i = 0; i < descriptor.getChildCount(obj); i++) { for (int i = 0; i < descriptor.getChildCount(obj); i++) {
Object child = descriptor.getChildAt(obj, i); Object child = descriptor.getChildAt(obj, i);
SearchResultNode childNode = searchTree(query, child); SearchResultNode childNode = searchTree(query, child, axEnabled);
if (childNode != null) { if (childNode != null) {
if (childTrees == null) { if (childTrees == null) {
childTrees = new ArrayList<>(); childTrees = new ArrayList<>();
@@ -521,7 +528,8 @@ public class InspectorSonarPlugin implements SonarPlugin {
if (isMatch || childTrees != null) { if (isMatch || childTrees != null) {
final String id = trackObject(obj); 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; return null;
} }

View File

@@ -18,15 +18,17 @@ public class SearchResultNode {
private final String id; private final String id;
private final boolean isMatch; private final boolean isMatch;
private final SonarObject element; private final SonarObject element;
private final SonarObject axElement;
@Nullable @Nullable
private final List<SearchResultNode> children; private final List<SearchResultNode> children;
SearchResultNode( 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.id = id;
this.isMatch = isMatch; this.isMatch = isMatch;
this.element = element; this.element = element;
this.children = children; this.children = children;
this.axElement = axElement;
} }
SonarObject toSonarObject() { SonarObject toSonarObject() {
@@ -44,6 +46,7 @@ public class SearchResultNode {
return new SonarObject.Builder() return new SonarObject.Builder()
.put("id", this.id) .put("id", this.id)
.put("isMatch", this.isMatch) .put("isMatch", this.isMatch)
.put("axElement", this.axElement)
.put("element", this.element) .put("element", this.element)
.put("children", childArray) .put("children", childArray)
.build(); .build();

View File

@@ -206,6 +206,11 @@ public class ApplicationDescriptor extends NodeDescriptor<ApplicationWrapper> {
return Collections.EMPTY_LIST; return Collections.EMPTY_LIST;
} }
@Override
public SonarObject getExtraInfo(ApplicationWrapper node) {
return new SonarObject.Builder().put("hasAXNode", true).build();
}
@Override @Override
public void setHighlighted(ApplicationWrapper node, boolean selected, boolean isAlignmentMode) throws Exception { public void setHighlighted(ApplicationWrapper node, boolean selected, boolean isAlignmentMode) throws Exception {
final int childCount = getChildCount(node); final int childCount = getChildCount(node);

View File

@@ -463,7 +463,10 @@ public class ViewDescriptor extends NodeDescriptor<View> {
@Override @Override
public SonarObject getExtraInfo(View node) { 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 @Nullable

View File

@@ -86,6 +86,9 @@ public class AccessibilityRoleUtil {
} }
public static AccessibilityRole getRole(View view) { public static AccessibilityRole getRole(View view) {
if (view == null) {
return AccessibilityRole.NONE;
}
AccessibilityNodeInfoCompat nodeInfo = AccessibilityNodeInfoCompat.obtain(); AccessibilityNodeInfoCompat nodeInfo = AccessibilityNodeInfoCompat.obtain();
ViewCompat.onInitializeAccessibilityNodeInfo(view, nodeInfo); ViewCompat.onInitializeAccessibilityNodeInfo(view, nodeInfo);
AccessibilityRole role = getRole(nodeInfo); AccessibilityRole role = getRole(nodeInfo);

View File

@@ -107,6 +107,7 @@ type SearchResultTree = {|
isMatch: Boolean, isMatch: Boolean,
children: ?Array<SearchResultTree>, children: ?Array<SearchResultTree>,
element: Element, element: Element,
axElement: Element,
|}; |};
const LoadingSpinner = LoadingIndicator.extends({ const LoadingSpinner = LoadingIndicator.extends({
@@ -333,15 +334,17 @@ export default class Layout extends SonarPlugin<InspectorState> {
}; };
search(query: string) { search(query: string) {
if (!query) {
return;
}
this.setState({ this.setState({
outstandingSearchQuery: query, outstandingSearchQuery: query,
}); });
this.client
.call('getSearchResults', {query: query}) if (!query) {
.then(response => this.displaySearchResults(response)); this.displaySearchResults({query: '', results: null});
} else {
this.client
.call('getSearchResults', {query: query, axEnabled: this.axEnabled()})
.then(response => this.displaySearchResults(response));
}
} }
executeCommand(command: string) { executeCommand(command: string) {
@@ -391,7 +394,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
results, results,
query, query,
}: { }: {
results: SearchResultTree, results: ?SearchResultTree,
query: string, query: string,
}) { }) {
const elements = this.getElementsFromSearchResultTree(results); const elements = this.getElementsFromSearchResultTree(results);
@@ -405,10 +408,29 @@ export default class Layout extends SonarPlugin<InspectorState> {
elements: elements.map(x => x.element), elements: elements.map(x => x.element),
type: 'UpdateElements', type: 'UpdateElements',
}); });
this.dispatchAction({ this.dispatchAction({
elements: idsToExpand, elements: idsToExpand,
type: 'ExpandElements', 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({ this.setState({
searchResults: { searchResults: {
matches: new Set( matches: new Set(
@@ -422,7 +444,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
}); });
} }
getElementsFromSearchResultTree(tree: SearchResultTree) { getElementsFromSearchResultTree(tree: ?SearchResultTree) {
if (!tree) { if (!tree) {
return []; return [];
} }
@@ -432,6 +454,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
isMatch: tree.isMatch, isMatch: tree.isMatch,
hasChildren: Boolean(tree.children), hasChildren: Boolean(tree.children),
element: tree.element, element: tree.element,
axElement: tree.axElement,
}, },
]; ];
if (tree.children) { if (tree.children) {
@@ -842,12 +865,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
onElementSelected = debounce((selectedKey: ElementID) => { onElementSelected = debounce((selectedKey: ElementID) => {
const {key, AXkey} = this.getKeysFromSelected(selectedKey); const {key, AXkey} = this.getKeysFromSelected(selectedKey);
this.dispatchAction({key, AXkey, type: 'SelectElement'});
this.dispatchAction({
key: key,
AXkey: AXkey,
type: 'SelectElement',
});
this.client.send('setHighlighted', { this.client.send('setHighlighted', {
id: selectedKey, id: selectedKey,