Expand AX tree when clicking-to-inspect
Summary: Adds expanding functionality to ax tree when click-to-inspect is used. Highlight/selection priority is given to the ax tree if in ax mode and the main tree otherwise. Reviewed By: danielbuechele Differential Revision: D9206028 fbshipit-source-id: 364b3cfc2e68dbce6006c36002353295aa7cdd07
This commit is contained in:
committed by
Facebook Github Bot
parent
a725a8fa68
commit
047b6ed7b7
@@ -427,19 +427,19 @@ public class InspectorSonarPlugin implements SonarPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hitTest(final int touchX, final int touchY) throws Exception {
|
private Touch createTouch(final int touchX, final int touchY, final boolean ax) throws Exception {
|
||||||
final SonarArray.Builder path = new SonarArray.Builder();
|
final SonarArray.Builder path = new SonarArray.Builder();
|
||||||
path.put(trackObject(mApplication));
|
path.put(trackObject(mApplication));
|
||||||
|
|
||||||
final Touch touch =
|
return new Touch() {
|
||||||
new Touch() {
|
|
||||||
int x = touchX;
|
int x = touchX;
|
||||||
int y = touchY;
|
int y = touchY;
|
||||||
Object node = mApplication;
|
Object node = mApplication;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void finish() {
|
public void finish() {
|
||||||
mConnection.send("select", new SonarObject.Builder().put("path", path).build());
|
mConnection.send(ax ? "selectAX" : "select", new SonarObject.Builder().put("path", path).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -453,12 +453,21 @@ public class InspectorSonarPlugin implements SonarPlugin {
|
|||||||
x -= offsetX;
|
x -= offsetX;
|
||||||
y -= offsetY;
|
y -= offsetY;
|
||||||
|
|
||||||
|
if (ax) {
|
||||||
|
node = assertNotNull(descriptorForObject(node).getAXChildAt(node, childIndex));
|
||||||
|
} else {
|
||||||
node = assertNotNull(descriptorForObject(node).getChildAt(node, childIndex));
|
node = assertNotNull(descriptorForObject(node).getChildAt(node, childIndex));
|
||||||
path.put(trackObject(node));
|
}
|
||||||
|
|
||||||
|
path.put(trackObject(node));
|
||||||
final NodeDescriptor<Object> descriptor = descriptorForObject(node);
|
final NodeDescriptor<Object> descriptor = descriptorForObject(node);
|
||||||
|
|
||||||
|
if (ax) {
|
||||||
|
descriptor.axHitTest(node, touch);
|
||||||
|
} else {
|
||||||
descriptor.hitTest(node, touch);
|
descriptor.hitTest(node, touch);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}.run();
|
}.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,9 +476,12 @@ public class InspectorSonarPlugin implements SonarPlugin {
|
|||||||
return x >= l && x <= r && y >= t && y <= b;
|
return x >= l && x <= r && y >= t && y <= b;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void hitTest(final int touchX, final int touchY) throws Exception {
|
||||||
final NodeDescriptor<Object> descriptor = descriptorForObject(mApplication);
|
final NodeDescriptor<Object> descriptor = descriptorForObject(mApplication);
|
||||||
descriptor.hitTest(mApplication, touch);
|
descriptor.hitTest(mApplication, createTouch(touchX, touchY, false));
|
||||||
|
descriptor.axHitTest(mApplication, createTouch(touchX, touchY, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setHighlighted(final String id, final boolean highlighted, final boolean isAlignmentMode) throws Exception {
|
private void setHighlighted(final String id, final boolean highlighted, final boolean isAlignmentMode) throws Exception {
|
||||||
|
|||||||
@@ -177,6 +177,15 @@ public abstract class NodeDescriptor<T> {
|
|||||||
*/
|
*/
|
||||||
public abstract void hitTest(T node, Touch touch) throws Exception;
|
public abstract void hitTest(T node, Touch touch) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform hit testing on the given ax node. Either continue the search in an ax child with {@link
|
||||||
|
* Touch#continueWithOffset(int, int, int, boolean)} or finish the hit testing on this ax node with {@link
|
||||||
|
* Touch#finish()}
|
||||||
|
*/
|
||||||
|
public void axHitTest(T node, Touch touch) throws Exception {
|
||||||
|
touch.finish();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A string indicating how this element should be decorated. Check with the Sonar desktop
|
* @return A string indicating how this element should be decorated. Check with the Sonar desktop
|
||||||
* app to see what values are supported.
|
* app to see what values are supported.
|
||||||
|
|||||||
@@ -210,12 +210,11 @@ public class ApplicationDescriptor extends NodeDescriptor<ApplicationWrapper> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void runHitTest(ApplicationWrapper node, Touch touch, boolean ax) throws Exception {
|
||||||
public void hitTest(ApplicationWrapper node, Touch touch) {
|
|
||||||
final int childCount = getChildCount(node);
|
final int childCount = getChildCount(node);
|
||||||
|
|
||||||
for (int i = childCount - 1; i >= 0; i--) {
|
for (int i = childCount - 1; i >= 0; i--) {
|
||||||
final Object child = getChildAt(node, i);
|
final Object child = ax ? getAXChildAt(node, i) : getChildAt(node, i);
|
||||||
if (child instanceof Activity || child instanceof ViewGroup) {
|
if (child instanceof Activity || child instanceof ViewGroup) {
|
||||||
touch.continueWithOffset(i, 0, 0);
|
touch.continueWithOffset(i, 0, 0);
|
||||||
return;
|
return;
|
||||||
@@ -225,6 +224,16 @@ public class ApplicationDescriptor extends NodeDescriptor<ApplicationWrapper> {
|
|||||||
touch.finish();
|
touch.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hitTest(ApplicationWrapper node, Touch touch) throws Exception{
|
||||||
|
runHitTest(node, touch, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void axHitTest(ApplicationWrapper node, Touch touch) throws Exception {
|
||||||
|
runHitTest(node, touch, true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable String getDecoration(ApplicationWrapper obj) {
|
public @Nullable String getDecoration(ApplicationWrapper obj) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -149,6 +149,12 @@ public class TextViewDescriptor extends NodeDescriptor<TextView> {
|
|||||||
descriptor.hitTest(node, touch);
|
descriptor.hitTest(node, touch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void axHitTest(TextView node, Touch touch) throws Exception {
|
||||||
|
final NodeDescriptor descriptor = descriptorForClass(View.class);
|
||||||
|
descriptor.axHitTest(node, touch);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable String getDecoration(TextView node) throws Exception {
|
public @Nullable String getDecoration(TextView node) throws Exception {
|
||||||
final NodeDescriptor descriptor = descriptorForClass(View.class);
|
final NodeDescriptor descriptor = descriptorForClass(View.class);
|
||||||
|
|||||||
@@ -534,6 +534,11 @@ public class ViewDescriptor extends NodeDescriptor<View> {
|
|||||||
touch.finish();
|
touch.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void axHitTest(View node, Touch touch) {
|
||||||
|
touch.finish();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable String getDecoration(View obj) {
|
public @Nullable String getDecoration(View obj) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -248,8 +248,7 @@ public class ViewGroupDescriptor extends NodeDescriptor<ViewGroup> {
|
|||||||
descriptor.setHighlighted(node, selected, isAlignmentMode);
|
descriptor.setHighlighted(node, selected, isAlignmentMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void runHitTest(ViewGroup node, Touch touch) {
|
||||||
public void hitTest(ViewGroup node, Touch touch) {
|
|
||||||
for (int i = node.getChildCount() - 1; i >= 0; i--) {
|
for (int i = node.getChildCount() - 1; i >= 0; i--) {
|
||||||
final View child = node.getChildAt(i);
|
final View child = node.getChildAt(i);
|
||||||
if (child instanceof HiddenNode
|
if (child instanceof HiddenNode
|
||||||
@@ -277,6 +276,16 @@ public class ViewGroupDescriptor extends NodeDescriptor<ViewGroup> {
|
|||||||
touch.finish();
|
touch.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hitTest(ViewGroup node, Touch touch) {
|
||||||
|
runHitTest(node, touch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void axHitTest(ViewGroup node, Touch touch) {
|
||||||
|
runHitTest(node, touch);
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean shouldSkip(View view) {
|
private static boolean shouldSkip(View view) {
|
||||||
Object tag = view.getTag(R.id.sonar_skip_view_traversal);
|
Object tag = view.getTag(R.id.sonar_skip_view_traversal);
|
||||||
if (!(tag instanceof Boolean)) {
|
if (!(tag instanceof Boolean)) {
|
||||||
|
|||||||
@@ -135,6 +135,12 @@ public class LithoViewDescriptor extends NodeDescriptor<LithoView> {
|
|||||||
touch.continueWithOffset(0, 0, 0);
|
touch.continueWithOffset(0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void axHitTest(LithoView node, Touch touch) throws Exception {
|
||||||
|
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
|
||||||
|
descriptor.axHitTest(node, touch);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDecoration(LithoView node) throws Exception {
|
public String getDecoration(LithoView node) throws Exception {
|
||||||
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
|
final NodeDescriptor descriptor = descriptorForClass(ViewGroup.class);
|
||||||
|
|||||||
@@ -437,6 +437,48 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
return AXToggleButtonEnabled && this.realClient.query.os === 'Android';
|
return AXToggleButtonEnabled && this.realClient.query.os === 'Android';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expand tree and highlight click-to-inspect node that was found
|
||||||
|
onSelectResultsRecieved(path: Array<ElementID>, ax: boolean) {
|
||||||
|
this.getNodesAndDirectChildren(path, ax).then(
|
||||||
|
(elements: Array<Element>) => {
|
||||||
|
const selected = path[path.length - 1];
|
||||||
|
|
||||||
|
this.dispatchAction({
|
||||||
|
elements,
|
||||||
|
type: ax ? 'UpdateAXElements' : 'UpdateElements',
|
||||||
|
});
|
||||||
|
|
||||||
|
// select node from ax tree if in ax mode
|
||||||
|
// select node from main tree if not in ax mode
|
||||||
|
// (also selects corresponding node in other tree if it exists)
|
||||||
|
if ((ax && this.state.inAXMode) || (!ax && !this.state.inAXMode)) {
|
||||||
|
const {key, AXkey} = this.getKeysFromSelected(selected);
|
||||||
|
this.dispatchAction({key, AXkey, type: 'SelectElement'});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatchAction({
|
||||||
|
isSearchActive: false,
|
||||||
|
type: 'SetSearchActive',
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const key of path) {
|
||||||
|
this.dispatchAction({
|
||||||
|
expand: true,
|
||||||
|
key,
|
||||||
|
type: ax ? 'ExpandAXElement' : 'ExpandElement',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client.send('setHighlighted', {
|
||||||
|
id: selected,
|
||||||
|
isAlignmentMode: this.state.isAlignmentMode,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.client.send('setSearchActive', {active: false});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
initAX() {
|
initAX() {
|
||||||
this.client.call('getAXRoot').then((element: Element) => {
|
this.client.call('getAXRoot').then((element: Element) => {
|
||||||
this.dispatchAction({elements: [element], type: 'UpdateAXElements'});
|
this.dispatchAction({elements: [element], type: 'UpdateAXElements'});
|
||||||
@@ -482,6 +524,10 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.client.subscribe('selectAX', ({path}: {path: Array<ElementID>}) => {
|
||||||
|
this.onSelectResultsRecieved(path, true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@@ -513,26 +559,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.client.subscribe('select', ({path}: {path: Array<ElementID>}) => {
|
this.client.subscribe('select', ({path}: {path: Array<ElementID>}) => {
|
||||||
this.getNodesAndDirectChildren(path, false).then(
|
this.onSelectResultsRecieved(path, false);
|
||||||
(elements: Array<Element>) => {
|
|
||||||
const selected = path[path.length - 1];
|
|
||||||
const {key, AXkey} = this.getKeysFromSelected(selected);
|
|
||||||
|
|
||||||
this.dispatchAction({elements, type: 'UpdateElements'});
|
|
||||||
this.dispatchAction({key, AXkey, type: 'SelectElement'});
|
|
||||||
this.dispatchAction({isSearchActive: false, type: 'SetSearchActive'});
|
|
||||||
|
|
||||||
for (const key of path) {
|
|
||||||
this.dispatchAction({expand: true, key, type: 'ExpandElement'});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.client.send('setHighlighted', {
|
|
||||||
id: selected,
|
|
||||||
isAlignmentMode: this.state.isAlignmentMode,
|
|
||||||
});
|
|
||||||
this.client.send('setSearchActive', {active: false});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.axEnabled()) {
|
if (this.axEnabled()) {
|
||||||
@@ -568,7 +595,7 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
|
|
||||||
getNodesAndDirectChildren(
|
getNodesAndDirectChildren(
|
||||||
ids: Array<ElementID>,
|
ids: Array<ElementID>,
|
||||||
ax: boolean, // always false at the moment bc only used for select
|
ax: boolean,
|
||||||
): Promise<Array<Element>> {
|
): Promise<Array<Element>> {
|
||||||
return this.getNodes(ids, {force: false, ax}).then(
|
return this.getNodes(ids, {force: false, ax}).then(
|
||||||
(elements: Array<Element>) => {
|
(elements: Array<Element>) => {
|
||||||
@@ -698,13 +725,22 @@ export default class Layout extends SonarPlugin<InspectorState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onElementExpanded = (key: ElementID, deep: boolean) => {
|
onElementExpanded = (key: ElementID, deep: boolean) => {
|
||||||
|
if (this.state.elements[key]) {
|
||||||
if (deep) {
|
if (deep) {
|
||||||
this.deepExpandElement(key, false);
|
this.deepExpandElement(key, false);
|
||||||
this.deepExpandElement(key, true);
|
|
||||||
} else {
|
} else {
|
||||||
this.expandElement(key, false);
|
this.expandElement(key, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.AXelements[key]) {
|
||||||
|
if (deep) {
|
||||||
|
this.deepExpandElement(key, true);
|
||||||
|
} else {
|
||||||
this.expandElement(key, true);
|
this.expandElement(key, true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.props.logger.track('usage', 'layout:element-expanded', {
|
this.props.logger.track('usage', 'layout:element-expanded', {
|
||||||
id: key,
|
id: key,
|
||||||
deep: deep,
|
deep: deep,
|
||||||
|
|||||||
Reference in New Issue
Block a user