optimize: code generation

This commit is contained in:
hyb1996
2017-12-07 22:13:58 +08:00
parent 6ea30c6fcc
commit 8db26f5b11
24 changed files with 687 additions and 177 deletions

View File

@@ -16,7 +16,8 @@ import com.bignerdranch.expandablerecyclerview.ChildViewHolder;
import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter; import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter;
import com.bignerdranch.expandablerecyclerview.ParentViewHolder; import com.bignerdranch.expandablerecyclerview.ParentViewHolder;
import com.bignerdranch.expandablerecyclerview.model.Parent; import com.bignerdranch.expandablerecyclerview.model.Parent;
import com.stardust.autojs.codegeneration.UiSelectorGenerator; import com.stardust.app.DialogUtils;
import com.stardust.autojs.codegeneration.CodeGenerator;
import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.ui.widget.CheckBoxCompat; import com.stardust.scriptdroid.ui.widget.CheckBoxCompat;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
@@ -66,39 +67,39 @@ public class CodeGenerateDialog extends ThemeColorMaterialDialogBuilder {
super(context); super(context);
mRootNode = rootNode; mRootNode = rootNode;
mTargetNode = targetNode; mTargetNode = targetNode;
positiveText(R.string.text_generate_and_copy); positiveText(R.string.text_generate);
neutralText(R.string.text_generate_and_open_editor); negativeText(R.string.text_cancel);
onPositive(((dialog, which) -> generateCodeAndCopy())); onPositive(((dialog, which) -> generateCodeAndShow()));
onNeutral(((dialog, which) -> generateCodeAndOpenEditor()));
setupViews(); setupViews();
} }
private void generateCodeAndCopy() { private void generateCodeAndShow() {
String code = generateCode(); String code = generateCode();
if (code == null) { if (code == null) {
Toast.makeText(getContext(), R.string.text_generate_fail, Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), R.string.text_generate_fail, Toast.LENGTH_SHORT).show();
return; return;
} }
ClipboardUtil.setClip(getContext(), code); DialogUtils.showDialog(new ThemeColorMaterialDialogBuilder(getContext())
Toast.makeText(getContext(), R.string.text_already_copy_to_clip, Toast.LENGTH_SHORT).show(); .title(R.string.text_generated_code)
.content(code)
.positiveText(R.string.text_copy)
.onPositive(((dialog, which) -> ClipboardUtil.setClip(getContext(), code)))
.build());
} }
private void generateCodeAndOpenEditor() {
}
private String generateCode() { private String generateCode() {
UiSelectorGenerator generator = new UiSelectorGenerator(mRootNode, mTargetNode); CodeGenerator generator = new CodeGenerator(mRootNode, mTargetNode);
OptionGroup settings = getOptionGroup(R.string.text_options); OptionGroup settings = getOptionGroup(R.string.text_options);
generator.setUsingId(settings.getOption(R.string.text_using_id_selector).checked); generator.setUsingId(settings.getOption(R.string.text_using_id_selector).checked);
generator.setUsingText(settings.getOption(R.string.text_using_text_selector).checked); generator.setUsingText(settings.getOption(R.string.text_using_text_selector).checked);
generator.setUsingDesc(settings.getOption(R.string.text_using_desc_selector).checked); generator.setUsingDesc(settings.getOption(R.string.text_using_desc_selector).checked);
generator.setSearchMode(getSearchMode()); generator.setSearchMode(getSearchMode());
setAction(generator); setAction(generator);
return generator.generate(); return generator.generateCode();
} }
private void setAction(UiSelectorGenerator generator) { private void setAction(CodeGenerator generator) {
OptionGroup action = getOptionGroup(R.string.text_action); OptionGroup action = getOptionGroup(R.string.text_action);
if (action.getOption(R.string.text_click).checked) { if (action.getOption(R.string.text_click).checked) {
generator.setAction(AccessibilityNodeInfoCompat.ACTION_CLICK); generator.setAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
@@ -117,18 +118,18 @@ public class CodeGenerateDialog extends ThemeColorMaterialDialogBuilder {
private int getSearchMode() { private int getSearchMode() {
OptionGroup selectMode = getOptionGroup(R.string.text_select); OptionGroup selectMode = getOptionGroup(R.string.text_select);
if (selectMode.getOption(R.string.text_find_one).checked) { if (selectMode.getOption(R.string.text_find_one).checked) {
return UiSelectorGenerator.FIND_ONE; return CodeGenerator.FIND_ONE;
} }
if (selectMode.getOption(R.string.text_until_find).checked) { if (selectMode.getOption(R.string.text_until_find).checked) {
return UiSelectorGenerator.UNTIL_FIND; return CodeGenerator.UNTIL_FIND;
} }
if (selectMode.getOption(R.string.text_wait_for).checked) { if (selectMode.getOption(R.string.text_wait_for).checked) {
return UiSelectorGenerator.WAIT_FOR; return CodeGenerator.WAIT_FOR;
} }
if (selectMode.getOption(R.string.text_wait_for).checked) { if (selectMode.getOption(R.string.text_wait_for).checked) {
return UiSelectorGenerator.EXISTS; return CodeGenerator.EXISTS;
} }
return UiSelectorGenerator.FIND_ONE; return CodeGenerator.FIND_ONE;
} }
private void setupViews() { private void setupViews() {

View File

@@ -299,7 +299,7 @@
<string name="text_using_id_selector">使用id选择</string> <string name="text_using_id_selector">使用id选择</string>
<string name="text_using_text_selector">使用文本选择(text)</string> <string name="text_using_text_selector">使用文本选择(text)</string>
<string name="text_using_desc_selector">使用描述选择(desc)</string> <string name="text_using_desc_selector">使用描述选择(desc)</string>
<string name="text_generate_and_copy">生成并复制</string> <string name="text_generate">生成</string>
<string name="text_generate_and_open_editor">生成并打开编辑器</string> <string name="text_generate_and_open_editor">生成并打开编辑器</string>
<string name="text_generate_fail">生成失败了o(╥﹏╥)o</string> <string name="text_generate_fail">生成失败了o(╥﹏╥)o</string>
<string name="key_documentation_source">key_documentation_source</string> <string name="key_documentation_source">key_documentation_source</string>
@@ -326,4 +326,6 @@
<string name="text_apk_builder_plugin_unavailable">打包插件不可用</string> <string name="text_apk_builder_plugin_unavailable">打包插件不可用</string>
<string name="text_detail">详情</string> <string name="text_detail">详情</string>
<string name="text_open">打开</string> <string name="text_open">打开</string>
<string name="text_generated_code">生成结果</string>
<string name="text_copy">复制</string>
</resources> </resources>

View File

@@ -0,0 +1,193 @@
package com.stardust.autojs.codegeneration;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import com.stardust.automator.UiGlobalSelector;
import com.stardust.automator.UiObject;
import com.stardust.view.accessibility.NodeInfo;
/**
* Created by Stardust on 2017/12/7.
*/
public class CodeGenerator {
public static final int UNTIL_FIND = 0;
public static final int FIND_ONE = 1;
public static final int WAIT_FOR = 2;
public static final int EXISTS = 3;
private ReadOnlyUiObject mRoot;
private ReadOnlyUiObject mTarget;
private boolean mUsingId = true;
private boolean mUsingDesc = true;
private boolean mUsingText = true;
private int mSearchMode = FIND_ONE;
private int mAction = -1;
public CodeGenerator(NodeInfo root, NodeInfo target) {
this(new ReadOnlyUiObject(root), new ReadOnlyUiObject(target));
}
public CodeGenerator(ReadOnlyUiObject root, ReadOnlyUiObject target) {
mRoot = root;
mTarget = target;
}
public void setUsingId(boolean usingId) {
mUsingId = usingId;
}
public void setUsingDesc(boolean usingDesc) {
mUsingDesc = usingDesc;
}
public void setUsingText(boolean usingText) {
mUsingText = usingText;
}
public void setSearchMode(int searchMode) {
mSearchMode = searchMode;
}
public void setAction(int action) {
mAction = action;
}
public String generateCode() {
UiObject collection = getCollectionParent(mTarget);
if (collection != null) {
return generateCodeForCollectionChild(collection, mTarget);
}
UiSelectorGenerator generator = new UiSelectorGenerator(mRoot, mTarget);
generator.setSearchMode(mSearchMode);
generator.setUsingDesc(mUsingDesc);
generator.setUsingId(mUsingId);
generator.setUsingText(mUsingText);
String selector = generateCode(generator, mRoot, mTarget, 2, 2);
if (selector == null)
return null;
return generateAction(selector);
}
protected String generateCode(UiSelectorGenerator generator, UiObject root, UiObject target, int maxParentLevel, int maxChildrenLevel) {
String selector = generator.generateSelectorCode();
if (selector != null) {
return selector;
}
if (maxChildrenLevel > 0) {
for (int i = 0; i < target.childCount(); i++) {
UiObject child = target.child(i);
if (child == null)
continue;
String childCode = generateCode(root, child, 0, maxChildrenLevel - 1);
if (childCode != null) {
return childCode + ".parent()";
}
}
}
if (maxParentLevel > 0 && target.parent() != null) {
int index = target.indexInParent();
if (index > 0) {
String parentCode = generateCode(root, target.parent(), maxParentLevel - 1, 0);
if (parentCode != null) {
return parentCode + "child(" + index + ")";
}
}
}
return null;
}
protected String generateCode(UiObject root, UiObject target, int maxParentLevel, int maxChildrenLevel) {
UiSelectorGenerator generator = new UiSelectorGenerator(root, target);
generator.setUsingId(mUsingId);
return generateCode(generator, root, target, maxParentLevel, maxChildrenLevel);
}
private String generateAction(String selector) {
if (selector == null)
return null;
if (mSearchMode == WAIT_FOR) {
return selector + ".waitFor()";
}
if (mSearchMode == EXISTS) {
return "if(" + selector + ".exists()){\n \n}";
}
String action = getAction();
if (action.isEmpty()) {
return selector;
} else {
return selector + "." + action;
}
}
private String getAction() {
switch (mAction) {
case AccessibilityNodeInfoCompat.ACTION_CLICK:
return "click()";
case AccessibilityNodeInfoCompat.ACTION_LONG_CLICK:
return "longClick()";
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
return "scrollBackward()";
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
return "scrollForward()";
case AccessibilityNodeInfoCompat.ACTION_SET_TEXT:
return "setText(\"\")";
}
return "";
}
private String generateCodeForCollectionChild(UiObject collection, UiObject target) {
UiObject parent = target.parent();
if (parent == null)
return null;
UiObject collectionItem = null;
for (int i = 0; i < collection.childCount(); i++) {
if (inherits(collection.child(i), target)) {
collectionItem = collection.child(i);
break;
}
}
if (collectionItem == null)
return null;
String collectionCode = generateCode(mRoot, collection, 2, 0);
if (collectionCode == null)
return null;
String itemCode = generateCode(collectionItem, target, 1, 2);
if (itemCode == null)
return null;
return collectionCode + ".findOne().children().forEach(child => {\n"
+ "var target = child.findOne(" + itemCode + ");\n"
+ "target." + getAction() + ";\n"
+ "});";
}
private boolean inherits(UiObject root, UiObject target) {
for (int i = 0; i < root.childCount(); i++) {
UiObject child = root.child(i);
if (child != null) {
if (child.equals(target) || inherits(child, target)) {
return true;
}
}
}
return false;
}
private UiObject getCollectionParent(UiObject target) {
UiObject parent = target.parent();
while (parent != null) {
if (parent.rowCount() > 0 || parent.columnCount() > 0) {
return parent;
}
parent = parent.parent();
}
return null;
}
}

View File

@@ -6,7 +6,6 @@ import android.support.annotation.Nullable;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import com.stardust.automator.UiObject; import com.stardust.automator.UiObject;
import com.stardust.automator.UiObjectCollection;
import com.stardust.view.accessibility.NodeInfo; import com.stardust.view.accessibility.NodeInfo;
/** /**
@@ -16,19 +15,27 @@ import com.stardust.view.accessibility.NodeInfo;
public class ReadOnlyUiObject extends UiObject { public class ReadOnlyUiObject extends UiObject {
private NodeInfo mNodeInfo; private NodeInfo mNodeInfo;
private boolean mUsingId;
private boolean mUsingText;
private boolean mUsingDesc;
public ReadOnlyUiObject(NodeInfo info) { public ReadOnlyUiObject(NodeInfo info) {
super(info, info.depth); super(info, info.depth, -1);
mNodeInfo = info;
}
public ReadOnlyUiObject(NodeInfo info, int indexInParent) {
super(info, info.depth, indexInParent);
mNodeInfo = info; mNodeInfo = info;
} }
@Nullable @Nullable
@Override @Override
public UiObject child(int i) { public UiObject child(int i) {
return new ReadOnlyUiObject(mNodeInfo.getChildren().get(i)); return new ReadOnlyUiObject(mNodeInfo.getChildren().get(i), i);
}
@Nullable
@Override
public UiObject parent() {
return mNodeInfo.parent == null ? null : new ReadOnlyUiObject(mNodeInfo.parent);
} }
@Override @Override
@@ -189,6 +196,82 @@ public class ReadOnlyUiObject extends UiObject {
return mNodeInfo.scrollable; return mNodeInfo.scrollable;
} }
@Override
public boolean isCheckable() {
return checkable();
}
@Override
public boolean isChecked() {
return checked();
}
@Override
public boolean isFocusable() {
return focusable();
}
@Override
public boolean isFocused() {
return focused();
}
@Override
public boolean isVisibleToUser() {
return visibleToUser();
}
@Override
public boolean isAccessibilityFocused() {
return accessibilityFocused();
}
@Override
public boolean isSelected() {
return selected();
}
@Override
public boolean isClickable() {
return clickable();
}
@Override
public boolean isLongClickable() {
return longClickable();
}
@Override
public boolean isEnabled() {
return enabled();
}
@Override
public boolean isPassword() {
return password();
}
@Override
public boolean isScrollable() {
return scrollable();
}
@Override
public boolean isContextClickable() {
return mNodeInfo.contextClickable;
}
@Override
public boolean isDismissable() {
return mNodeInfo.dismissable;
}
@Override
public boolean isEditable() {
return mNodeInfo.editable;
}
@Override @Override
public int row() { public int row() {
return mNodeInfo.row; return mNodeInfo.row;
@@ -223,4 +306,27 @@ public class ReadOnlyUiObject extends UiObject {
public void recycle() { public void recycle() {
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
ReadOnlyUiObject that = (ReadOnlyUiObject) o;
return mNodeInfo.equals(that.mNodeInfo);
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + mNodeInfo.hashCode();
return result;
}
@Override
public String toString() {
return mNodeInfo.toString();
}
} }

View File

@@ -1,49 +1,81 @@
package com.stardust.autojs.codegeneration; package com.stardust.autojs.codegeneration;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import com.stardust.automator.UiGlobalSelector; import com.stardust.automator.UiGlobalSelector;
import com.stardust.automator.UiObject;
import com.stardust.util.Consumer; import com.stardust.util.Consumer;
import com.stardust.view.accessibility.NodeInfo;
import static com.stardust.autojs.codegeneration.CodeGenerator.FIND_ONE;
import static com.stardust.autojs.codegeneration.CodeGenerator.UNTIL_FIND;
/** /**
* Created by Stardust on 2017/8/4. * Created by Stardust on 2017/12/7.
*/ */
public class UiSelectorGenerator { public class UiSelectorGenerator {
private UiObject mRoot;
public static final int UNTIL_FIND = 0; private UiObject mTarget;
public static final int FIND_ONE = 1;
public static final int WAIT_FOR = 2;
public static final int EXISTS = 3;
private ReadOnlyUiObject mRoot;
private ReadOnlyUiObject mTarget;
private boolean mUsingId = true; private boolean mUsingId = true;
private boolean mUsingDesc = true; private boolean mUsingDesc = true;
private boolean mUsingText = true; private boolean mUsingText = true;
private UiGlobalSelector mUiGlobalSelector;
private int mSearchMode = FIND_ONE; private int mSearchMode = FIND_ONE;
private int mAction = -1;
public UiSelectorGenerator(NodeInfo root, NodeInfo target) { public UiSelectorGenerator(UiObject root, UiObject target) {
this(new ReadOnlyUiObject(root), new ReadOnlyUiObject(target));
}
public UiSelectorGenerator(ReadOnlyUiObject root, ReadOnlyUiObject target) {
mRoot = root; mRoot = root;
mTarget = target; mTarget = target;
} }
public void setSearchMode(int searchMode) { public UiGlobalSelector generateSelector() {
mSearchMode = searchMode; UiGlobalSelector selector = new UiGlobalSelector();
if (mUsingId &&
tryWithStringCondition(selector, mTarget.id(), selector::id)) {
return selector;
}
if (tryWithStringCondition(selector, mTarget.className(), selector::className)) {
return selector;
}
if (mUsingText &&
tryWithStringCondition(selector, mTarget.text(), selector::text)) {
return selector;
}
if (mUsingDesc &&
tryWithStringCondition(selector, mTarget.desc(), selector::desc)) {
return selector;
}
if (mTarget.scrollable() && tryWithBooleanCondition(selector, mTarget.scrollable(), selector::scrollable)) {
return selector;
}
if (mTarget.clickable() && tryWithBooleanCondition(selector, mTarget.clickable(), selector::clickable)) {
return selector;
}
if (mTarget.selected() && tryWithBooleanCondition(selector, mTarget.selected(), selector::selected)) {
return selector;
}
if (mTarget.checkable() && tryWithBooleanCondition(selector, mTarget.checkable(), selector::checkable)) {
return selector;
}
if (mTarget.checked() && tryWithBooleanCondition(selector, mTarget.checked(), selector::checked)) {
return selector;
}
if (mTarget.longClickable() && tryWithBooleanCondition(selector, mTarget.longClickable(), selector::longClickable)) {
return selector;
}
if (tryWithIntCondition(selector, mTarget.depth(), selector::depth)) {
return selector;
}
return null;
} }
public void setAction(int action) { public String generateSelectorCode() {
mAction = action; UiGlobalSelector selector = generateSelector();
if (selector == null) {
return null;
}
return selector + (mSearchMode == FIND_ONE ? ".findOne()" : ".untilFind()");
} }
public void setUsingId(boolean usingId) { public void setUsingId(boolean usingId) {
mUsingId = usingId; mUsingId = usingId;
} }
@@ -56,94 +88,37 @@ public class UiSelectorGenerator {
mUsingText = usingText; mUsingText = usingText;
} }
public void setSearchMode(int searchMode) {
mSearchMode = searchMode;
}
private boolean tryWithStringCondition(String name, String value, Consumer<String> condition, StringBuilder code) { private boolean tryWithBooleanCondition(UiGlobalSelector selector, boolean value, Consumer<Boolean> condition) {
if (value == null || value.isEmpty()) condition.accept(value);
return shouldStopGeneration(selector);
}
private boolean tryWithStringCondition(UiGlobalSelector selector, String value, Consumer<String> condition) {
if (value == null || value.isEmpty()) {
return false; return false;
code.append('.').append(name).append("(\"").append(value).append("\")"); }
condition.accept(value); condition.accept(value);
return isConditionEnough(); return shouldStopGeneration(selector);
} }
private boolean tryWithIntCondition(String name, int value, Consumer<Integer> condition, StringBuilder code) { private boolean shouldStopGeneration(UiGlobalSelector selector) {
code.append('.').append(name).append('(').append(value).append(')');
condition.accept(value);
return isConditionEnough();
}
private boolean isConditionEnough() {
if (mSearchMode == UNTIL_FIND) { if (mSearchMode == UNTIL_FIND) {
return !mUiGlobalSelector.findAndReturnList(mRoot).isEmpty(); return !selector.findAndReturnList(mRoot).isEmpty();
} else { } else {
return mUiGlobalSelector.findAndReturnList(mRoot).size() == 1; return selector.findAndReturnList(mRoot).size() == 1;
} }
} }
public String generate() { private boolean tryWithIntCondition(UiGlobalSelector selector, int value, Consumer<Integer> condition) {
String selector = generateSelector(); condition.accept(value);
if (selector == null) { return shouldStopGeneration(selector);
return null;
}
//remove '.'
selector = selector.substring(1);
if (mSearchMode == WAIT_FOR) {
return selector + ".waitFor()";
}
if (mSearchMode == EXISTS) {
return "if(" + selector + ".exists()){\n \n}";
}
String action = getAction();
if (action == null) {
return selector;
} else {
return selector + action;
}
} }
private String getAction() {
switch (mAction) {
case AccessibilityNodeInfoCompat.ACTION_CLICK:
return ".click()";
case AccessibilityNodeInfoCompat.ACTION_LONG_CLICK:
return ".longClick()";
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
return ".scrollBackward()";
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
return ".scrollForward()";
case AccessibilityNodeInfoCompat.ACTION_SET_TEXT:
return ".setText(\"\")";
}
return null;
}
private String generateSelector() {
mUiGlobalSelector = new UiGlobalSelector();
StringBuilder code = new StringBuilder();
if (mUsingId &&
tryWithStringCondition("id", mTarget.id(), mUiGlobalSelector::id, code)) {
return code.toString();
}
if (tryWithIntCondition("depth", mTarget.depth(), mUiGlobalSelector::depth, code)) {
return code.toString();
}
if (tryWithStringCondition("className", mTarget.className(), mUiGlobalSelector::className, code)) {
return code.toString();
}
if (mUsingText &&
tryWithStringCondition("text", mTarget.text(), mUiGlobalSelector::text, code)) {
return code.toString();
}
if (mUsingDesc &&
tryWithStringCondition("desc", mTarget.desc(), mUiGlobalSelector::desc, code)) {
return code.toString();
}
return null;
}
} }

View File

@@ -178,6 +178,11 @@ public class UiGlobalSelector {
protected boolean isIncluded(UiObject nodeInfo) { protected boolean isIncluded(UiObject nodeInfo) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && nodeInfo.getDrawingOrder() == order; return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && nodeInfo.getDrawingOrder() == order;
} }
@Override
public String toString() {
return "drawingOrder(" + order + ")";
}
}); });
return this; return this;
} }
@@ -430,5 +435,13 @@ public class UiGlobalSelector {
return this; return this;
} }
@Override
public String toString() {
StringBuilder str = new StringBuilder();
for (ListFilter filter : mFilters) {
str.append(filter.toString()).append(".");
}
str.deleteCharAt(str.length() - 1);
return str.toString();
}
} }

View File

@@ -37,37 +37,39 @@ public class UiObject extends AccessibilityNodeInfoCompat {
public static UiObject createRoot(AccessibilityNodeInfo root) { public static UiObject createRoot(AccessibilityNodeInfo root) {
return new UiObject(root, null, 0); return new UiObject(root, null, 0, -1);
} }
public static UiObject createRoot(AccessibilityNodeInfo root, AccessibilityNodeInfoAllocator allocator) { public static UiObject createRoot(AccessibilityNodeInfo root, AccessibilityNodeInfoAllocator allocator) {
return new UiObject(root, allocator, 0); return new UiObject(root, allocator, 0, -1);
} }
private AccessibilityNodeInfoAllocator mAllocator = null; private AccessibilityNodeInfoAllocator mAllocator = null;
private String mStackTrace = ""; private String mStackTrace = "";
private int mDepth = 0; private int mDepth = 0;
private final int mIndexInParent;
public UiObject(Object info) { public UiObject(Object info) {
this(info, 0); this(info, 0, -1);
} }
public UiObject(Object info, AccessibilityNodeInfoAllocator allocator, int depth) { public UiObject(Object info, AccessibilityNodeInfoAllocator allocator, int depth, int indexInParent) {
super(info); super(info);
mDepth = depth; mDepth = depth;
mAllocator = allocator; mAllocator = allocator;
mIndexInParent = indexInParent;
if (DEBUG) if (DEBUG)
mStackTrace = Arrays.toString(Thread.currentThread().getStackTrace()); mStackTrace = Arrays.toString(Thread.currentThread().getStackTrace());
} }
public UiObject(Object info, AccessibilityNodeInfoAllocator allocator) { public UiObject(Object info, AccessibilityNodeInfoAllocator allocator, int indexInParent) {
this(info, allocator, 0); this(info, allocator, 0, indexInParent);
} }
public UiObject(Object info, int depth) { public UiObject(Object info, int depth, int indexInParent) {
this(info, null, depth); this(info, null, depth, indexInParent);
} }
@Nullable @Nullable
@@ -76,7 +78,7 @@ public class UiObject extends AccessibilityNodeInfoCompat {
AccessibilityNodeInfoCompat parent = super.getParent(); AccessibilityNodeInfoCompat parent = super.getParent();
if (parent == null) if (parent == null)
return null; return null;
return new UiObject(parent.getInfo(), mDepth - 1); return new UiObject(parent.getInfo(), mDepth - 1, -1);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
// FIXME: 2017/5/5 // FIXME: 2017/5/5
return null; return null;
@@ -89,13 +91,17 @@ public class UiObject extends AccessibilityNodeInfoCompat {
AccessibilityNodeInfoCompat child = super.getChild(i); AccessibilityNodeInfoCompat child = super.getChild(i);
if (child == null) if (child == null)
return null; return null;
return new UiObject(child.getInfo(), mDepth + 1); return new UiObject(child.getInfo(), mDepth + 1, i);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
// FIXME: 2017/5/5 // FIXME: 2017/5/5
return null; return null;
} }
} }
public int indexInParent() {
return mIndexInParent;
}
public UiObjectCollection find(UiGlobalSelector selector) { public UiObjectCollection find(UiGlobalSelector selector) {
return selector.findOf(this); return selector.findOf(this);
} }
@@ -105,11 +111,11 @@ public class UiObject extends AccessibilityNodeInfoCompat {
} }
public UiObjectCollection children() { public UiObjectCollection children() {
ArrayList<AccessibilityNodeInfoCompat> list = new ArrayList<>(getChildCount()); ArrayList<UiObject> list = new ArrayList<>(getChildCount());
for (int i = 0; i < getChildCount(); i++) { for (int i = 0; i < getChildCount(); i++) {
list.add(getChild(i)); list.add(child(i));
} }
return UiObjectCollection.ofCompat(list); return UiObjectCollection.of(list);
} }
public int childCount() { public int childCount() {
@@ -428,20 +434,6 @@ public class UiObject extends AccessibilityNodeInfoCompat {
return new UiGlobalSelector().id(viewId).findAndReturnList(this); return new UiGlobalSelector().id(viewId).findAndReturnList(this);
} }
public static List<UiObject> compatListToUiObjectList(List<AccessibilityNodeInfoCompat> compats, AccessibilityNodeInfoAllocator allocator) {
// FIXME: 2017/11/5 lost depth info
List<UiObject> uiObjects = new ArrayList<>(compats.size());
for (AccessibilityNodeInfoCompat compat : compats) {
if (compat != null)
uiObjects.add(new UiObject(compat.getInfo(), allocator));
}
return uiObjects;
}
public static List<UiObject> compatListToUiObjectList(List<AccessibilityNodeInfoCompat> compats) {
return compatListToUiObjectList(compats, null);
}
@Override @Override
public void recycle() { public void recycle() {
try { try {
@@ -451,4 +443,5 @@ public class UiObject extends AccessibilityNodeInfoCompat {
} }
} }
} }

View File

@@ -31,10 +31,6 @@ public class UiObjectCollection {
public static final UiObjectCollection EMPTY = UiObjectCollection.of(Collections.<UiObject>emptyList()); public static final UiObjectCollection EMPTY = UiObjectCollection.of(Collections.<UiObject>emptyList());
public static UiObjectCollection ofCompat(List<AccessibilityNodeInfoCompat> list) {
return new UiObjectCollection(UiObject.compatListToUiObjectList(list));
}
public static UiObjectCollection of(List<UiObject> list) { public static UiObjectCollection of(List<UiObject> list) {
return new UiObjectCollection(list); return new UiObjectCollection(list);
} }

View File

@@ -42,6 +42,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isCheckable(); return node.isCheckable();
} }
@Override
public String toString() {
return "checkable";
}
}; };
public static final BooleanSupplier CHECKED = new BooleanSupplier() { public static final BooleanSupplier CHECKED = new BooleanSupplier() {
@@ -50,6 +55,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isChecked(); return node.isChecked();
} }
@Override
public String toString() {
return "checked";
}
}; };
public static final BooleanSupplier FOCUSABLE = new BooleanSupplier() { public static final BooleanSupplier FOCUSABLE = new BooleanSupplier() {
@@ -58,6 +68,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isFocusable(); return node.isFocusable();
} }
@Override
public String toString() {
return "focusable";
}
}; };
public static final BooleanSupplier FOCUSED = new BooleanSupplier() { public static final BooleanSupplier FOCUSED = new BooleanSupplier() {
@@ -66,6 +81,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isFocused(); return node.isFocused();
} }
@Override
public String toString() {
return "focused";
}
}; };
public static final BooleanSupplier VISIBLE_TO_USER = new BooleanSupplier() { public static final BooleanSupplier VISIBLE_TO_USER = new BooleanSupplier() {
@@ -74,6 +94,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isVisibleToUser(); return node.isVisibleToUser();
} }
@Override
public String toString() {
return "visibleToUser";
}
}; };
public static final BooleanSupplier ACCESSIBILITY_FOCUSED = new BooleanSupplier() { public static final BooleanSupplier ACCESSIBILITY_FOCUSED = new BooleanSupplier() {
@@ -82,6 +107,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isAccessibilityFocused(); return node.isAccessibilityFocused();
} }
@Override
public String toString() {
return "accessibilityFocused";
}
}; };
public static final BooleanSupplier SELECTED = new BooleanSupplier() { public static final BooleanSupplier SELECTED = new BooleanSupplier() {
@@ -90,6 +120,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isSelected(); return node.isSelected();
} }
@Override
public String toString() {
return "selected";
}
}; };
public static final BooleanSupplier CLICKABLE = new BooleanSupplier() { public static final BooleanSupplier CLICKABLE = new BooleanSupplier() {
@@ -98,6 +133,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isClickable(); return node.isClickable();
} }
@Override
public String toString() {
return "clickable";
}
}; };
public static final BooleanSupplier LONG_CLICKABLE = new BooleanSupplier() { public static final BooleanSupplier LONG_CLICKABLE = new BooleanSupplier() {
@@ -106,6 +146,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isLongClickable(); return node.isLongClickable();
} }
@Override
public String toString() {
return "longClickable";
}
}; };
public static final BooleanSupplier ENABLED = new BooleanSupplier() { public static final BooleanSupplier ENABLED = new BooleanSupplier() {
@@ -114,6 +159,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isEnabled(); return node.isEnabled();
} }
@Override
public String toString() {
return "enabled";
}
}; };
@@ -123,6 +173,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isPassword(); return node.isPassword();
} }
@Override
public String toString() {
return "password";
}
}; };
public static final BooleanSupplier SCROLLABLE = new BooleanSupplier() { public static final BooleanSupplier SCROLLABLE = new BooleanSupplier() {
@@ -131,6 +186,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isScrollable(); return node.isScrollable();
} }
@Override
public String toString() {
return "scrollable";
}
}; };
public static final BooleanSupplier EDITABLE = new BooleanSupplier() { public static final BooleanSupplier EDITABLE = new BooleanSupplier() {
@@ -139,6 +199,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isEditable(); return node.isEditable();
} }
@Override
public String toString() {
return "editable";
}
}; };
public static final BooleanSupplier CONTENT_INVALID = new BooleanSupplier() { public static final BooleanSupplier CONTENT_INVALID = new BooleanSupplier() {
@@ -146,6 +211,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isContentInvalid(); return node.isContentInvalid();
} }
@Override
public String toString() {
return "contentInvalid";
}
}; };
public static final BooleanSupplier CONTEXT_CLICKABLE = new BooleanSupplier() { public static final BooleanSupplier CONTEXT_CLICKABLE = new BooleanSupplier() {
@@ -155,6 +225,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isContextClickable(); return node.isContextClickable();
} }
@Override
public String toString() {
return "checkable";
}
}; };
public static final BooleanSupplier MULTI_LINE = new BooleanSupplier() { public static final BooleanSupplier MULTI_LINE = new BooleanSupplier() {
@@ -163,6 +238,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isMultiLine(); return node.isMultiLine();
} }
@Override
public String toString() {
return "multiLine";
}
}; };
public static final BooleanSupplier DISMISSABLE = new BooleanSupplier() { public static final BooleanSupplier DISMISSABLE = new BooleanSupplier() {
@@ -171,6 +251,11 @@ public class BooleanFilter extends DfsFilter {
public boolean get(UiObject node) { public boolean get(UiObject node) {
return node.isDismissable(); return node.isDismissable();
} }
@Override
public String toString() {
return "dismissable";
}
}; };
private BooleanSupplier mBooleanSupplier; private BooleanSupplier mBooleanSupplier;
@@ -186,4 +271,8 @@ public class BooleanFilter extends DfsFilter {
return nodeInfo != null && mBooleanSupplier.get(nodeInfo) == mExceptedValue; return nodeInfo != null && mBooleanSupplier.get(nodeInfo) == mExceptedValue;
} }
@Override
public String toString() {
return mBooleanSupplier.toString() + "(" + mExceptedValue + ")";
}
} }

View File

@@ -5,6 +5,8 @@ import android.graphics.Rect;
import com.stardust.automator.UiObject; import com.stardust.automator.UiObject;
import com.stardust.view.accessibility.AccessibilityNodeInfoHelper; import com.stardust.view.accessibility.AccessibilityNodeInfoHelper;
import java.util.Locale;
/** /**
* Created by Stardust on 2017/3/9. * Created by Stardust on 2017/3/9.
*/ */
@@ -33,4 +35,11 @@ public class BoundsFilter extends DfsFilter {
return boundsInScreen.equals(mBounds); return boundsInScreen.equals(mBounds);
return mBounds.contains(boundsInScreen); return mBounds.contains(boundsInScreen);
} }
@Override
public String toString() {
return String.format(Locale.getDefault(), "bounds%s(%d, %d, %d, %d)", mType == TYPE_EQUALS ? "" :
mType == TYPE_INSIDE ? "Inside" : "Contains",
mBounds.left, mBounds.top, mBounds.right, mBounds.bottom);
}
} }

View File

@@ -14,6 +14,11 @@ public class ClassNameFilter {
CharSequence charSequence = nodeInfo.getClassName(); CharSequence charSequence = nodeInfo.getClassName();
return charSequence == null ? null : charSequence.toString(); return charSequence == null ? null : charSequence.toString();
} }
@Override
public String toString() {
return "className";
}
}; };
public static ListFilter equals(String text) { public static ListFilter equals(String text) {

View File

@@ -14,6 +14,11 @@ public class DescFilter {
CharSequence charSequence = nodeInfo.getContentDescription(); CharSequence charSequence = nodeInfo.getContentDescription();
return charSequence == null ? null : charSequence.toString(); return charSequence == null ? null : charSequence.toString();
} }
@Override
public String toString() {
return "desc";
}
}; };
public static ListFilter equals(String text) { public static ListFilter equals(String text) {

View File

@@ -16,6 +16,11 @@ public class IdFilter extends ListFilter.Default {
public String getKey(UiObject nodeInfo) { public String getKey(UiObject nodeInfo) {
return nodeInfo.getViewIdResourceName(); return nodeInfo.getViewIdResourceName();
} }
@Override
public String toString() {
return "id";
}
}; };
public static StringEqualsFilter equals(String id) { public static StringEqualsFilter equals(String id) {

View File

@@ -17,6 +17,11 @@ public class IntFilter extends DfsFilter {
public int get(UiObject object) { public int get(UiObject object) {
return object.depth(); return object.depth();
} }
@Override
public String toString() {
return "depth";
}
}; };
public static final IntProperty ROW = new IntProperty() { public static final IntProperty ROW = new IntProperty() {
@@ -24,6 +29,11 @@ public class IntFilter extends DfsFilter {
public int get(UiObject object) { public int get(UiObject object) {
return object.row(); return object.row();
} }
@Override
public String toString() {
return "row";
}
}; };
public static final IntProperty ROW_COUNT = new IntProperty() { public static final IntProperty ROW_COUNT = new IntProperty() {
@@ -31,6 +41,11 @@ public class IntFilter extends DfsFilter {
public int get(UiObject object) { public int get(UiObject object) {
return object.rowCount(); return object.rowCount();
} }
@Override
public String toString() {
return "rowCount";
}
}; };
public static final IntProperty ROW_SPAN = new IntProperty() { public static final IntProperty ROW_SPAN = new IntProperty() {
@@ -38,6 +53,11 @@ public class IntFilter extends DfsFilter {
public int get(UiObject object) { public int get(UiObject object) {
return object.rowSpan(); return object.rowSpan();
} }
@Override
public String toString() {
return "rowSpan";
}
}; };
public static final IntProperty COLUMN = new IntProperty() { public static final IntProperty COLUMN = new IntProperty() {
@@ -45,6 +65,11 @@ public class IntFilter extends DfsFilter {
public int get(UiObject object) { public int get(UiObject object) {
return object.column(); return object.column();
} }
@Override
public String toString() {
return "column";
}
}; };
public static final IntProperty COLUMN_COUNT = new IntProperty() { public static final IntProperty COLUMN_COUNT = new IntProperty() {
@@ -52,6 +77,11 @@ public class IntFilter extends DfsFilter {
public int get(UiObject object) { public int get(UiObject object) {
return object.columnCount(); return object.columnCount();
} }
@Override
public String toString() {
return "columnCount";
}
}; };
public static final IntProperty COLUMN_SPAN = new IntProperty() { public static final IntProperty COLUMN_SPAN = new IntProperty() {
@@ -59,6 +89,11 @@ public class IntFilter extends DfsFilter {
public int get(UiObject object) { public int get(UiObject object) {
return object.columnSpan(); return object.columnSpan();
} }
@Override
public String toString() {
return "columnSpan";
}
}; };
private IntProperty mIntProperty; private IntProperty mIntProperty;
@@ -73,4 +108,9 @@ public class IntFilter extends DfsFilter {
protected boolean isIncluded(UiObject nodeInfo) { protected boolean isIncluded(UiObject nodeInfo) {
return mIntProperty.get(nodeInfo) == mValue; return mIntProperty.get(nodeInfo) == mValue;
} }
@Override
public String toString() {
return mIntProperty.toString() + "(" + mValue + ")";
}
} }

View File

@@ -14,6 +14,11 @@ public class PackageNameFilter {
CharSequence charSequence = nodeInfo.getPackageName(); CharSequence charSequence = nodeInfo.getPackageName();
return charSequence == null ? null : charSequence.toString(); return charSequence == null ? null : charSequence.toString();
} }
@Override
public String toString() {
return "packageName";
}
}; };
public static ListFilter equals(String text) { public static ListFilter equals(String text) {

View File

@@ -21,4 +21,8 @@ public class StringContainsFilter extends DfsFilter {
return key != null && key.contains(mContains); return key != null && key.contains(mContains);
} }
@Override
public String toString() {
return mKeyGetter.toString() + "Contains(\"" + mContains + "\")";
}
} }

View File

@@ -22,4 +22,8 @@ public class StringEndsWithFilter extends DfsFilter {
return key != null && key.endsWith(mSuffix); return key != null && key.endsWith(mSuffix);
} }
@Override
public String toString() {
return mKeyGetter.toString() + "EndsWith(\"" + mSuffix + "\")";
}
} }

View File

@@ -19,9 +19,14 @@ public class StringEqualsFilter extends DfsFilter {
@Override @Override
protected boolean isIncluded(UiObject nodeInfo) { protected boolean isIncluded(UiObject nodeInfo) {
String key = mKeyGetter.getKey(nodeInfo); String key = mKeyGetter.getKey(nodeInfo);
if(key != null){ if (key != null) {
return key.equals(mText); return key.equals(mText);
} }
return false; return false;
} }
@Override
public String toString() {
return mKeyGetter.toString() + "(\"" + mText + "\")";
}
} }

View File

@@ -22,4 +22,8 @@ public class StringMatchesFilter extends DfsFilter {
return key != null && key.matches(mRegex); return key != null && key.matches(mRegex);
} }
@Override
public String toString() {
return mKeyGetter.toString() + "Matches(\"" + mRegex + "\")";
}
} }

View File

@@ -22,4 +22,8 @@ public class StringStartsWithFilter extends DfsFilter {
return key != null && key.startsWith(mPrefix); return key != null && key.startsWith(mPrefix);
} }
@Override
public String toString() {
return mKeyGetter.toString() + "StartsWith(\"" + mPrefix + "\")";
}
} }

View File

@@ -16,6 +16,11 @@ public class TextFilter extends ListFilter.Default {
CharSequence charSequence = nodeInfo.getText(); CharSequence charSequence = nodeInfo.getText();
return charSequence == null ? null : charSequence.toString(); return charSequence == null ? null : charSequence.toString();
} }
@Override
public String toString() {
return "text";
}
}; };
public static ListFilter equals(String text) { public static ListFilter equals(String text) {

View File

@@ -61,10 +61,10 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi
Log.v(TAG, "onAccessibilityEvent: " + event); Log.v(TAG, "onAccessibilityEvent: " + event);
if (!containsAllEventTypes && !eventTypes.contains(event.getEventType())) if (!containsAllEventTypes && !eventTypes.contains(event.getEventType()))
return; return;
int type = event.getEventType(); AccessibilityNodeInfo root = super.getRootInActiveWindow();
if (type == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || type == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER if (root != null) {
|| type == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { mFastRootInActiveWindow = root;
mFastRootInActiveWindow = super.getRootInActiveWindow(); Log.v(TAG, "root not null: " + event);
} }
for (Map.Entry<Integer, AccessibilityDelegate> entry : mDelegates.entrySet()) { for (Map.Entry<Integer, AccessibilityDelegate> entry : mDelegates.entrySet()) {
AccessibilityDelegate delegate = entry.getValue(); AccessibilityDelegate delegate = entry.getValue();
@@ -164,4 +164,5 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi
return stickOnKeyObserver; return stickOnKeyObserver;
} }
} }

View File

@@ -28,23 +28,28 @@ public class LayoutInspector {
Log.d(LOG_TAG, "captureCurrentWindow: service = null"); Log.d(LOG_TAG, "captureCurrentWindow: service = null");
mCapture = null; mCapture = null;
} else { } else {
final AccessibilityNodeInfo root = service.getRootInActiveWindow(); final AccessibilityNodeInfo root = getRootInActiveWindow(service);
if (root == null) { if (root == null) {
Log.d(LOG_TAG, "captureCurrentWindow: root = null"); Log.d(LOG_TAG, "captureCurrentWindow: root = null");
mCapture = null; mCapture = null;
} else { } else {
mExecutor.execute(new Runnable() { mExecutor.execute(() -> {
@Override mDumping = true;
public void run() { mCapture = NodeInfo.capture(root);
mDumping = true; mDumping = false;
mCapture = NodeInfo.capture(root);
mDumping = false;
}
}); });
} }
} }
} }
private AccessibilityNodeInfo getRootInActiveWindow(AccessibilityService service) {
AccessibilityNodeInfo root = service.getRootInActiveWindow();
if (root == null)
return service.fastRootInActiveWindow();
return root;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void refreshChildList(AccessibilityNodeInfo root) { private void refreshChildList(AccessibilityNodeInfo root) {
if (root == null) if (root == null)

View File

@@ -3,6 +3,7 @@ package com.stardust.view.accessibility;
import android.graphics.Rect; import android.graphics.Rect;
import android.support.annotation.Keep; import android.support.annotation.Keep;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo;
@@ -49,9 +50,10 @@ public class NodeInfo {
public boolean checkable; public boolean checkable;
public boolean focused; public boolean focused;
public boolean visibleToUser; public boolean visibleToUser;
public NodeInfo parent;
public NodeInfo(UiObject node) { public NodeInfo(UiObject node, NodeInfo parent) {
id = simplifyId(node.getViewIdResourceName()); id = simplifyId(node.getViewIdResourceName());
desc = node.desc(); desc = node.desc();
className = node.className(); className = node.className();
@@ -86,6 +88,8 @@ public class NodeInfo {
node.getBoundsInParent(mBoundsInParent); node.getBoundsInParent(mBoundsInParent);
bounds = boundsToString(mBoundsInScreen); bounds = boundsToString(mBoundsInScreen);
this.parent = parent;
} }
private String simplifyId(String idResourceName) { private String simplifyId(String idResourceName) {
@@ -109,13 +113,13 @@ public class NodeInfo {
} }
public static NodeInfo capture(@NonNull UiObject parent) { public static NodeInfo capture(@NonNull UiObject uiObject, @Nullable NodeInfo parent) {
NodeInfo nodeInfo = new NodeInfo(parent); NodeInfo nodeInfo = new NodeInfo(uiObject, parent);
int childCount = parent.getChildCount(); int childCount = uiObject.getChildCount();
for (int i = 0; i < childCount; i++) { for (int i = 0; i < childCount; i++) {
UiObject child = parent.child(i); UiObject child = uiObject.child(i);
if (child != null) { if (child != null) {
nodeInfo.children.add(capture(child)); nodeInfo.children.add(capture(child, nodeInfo));
} }
} }
return nodeInfo; return nodeInfo;
@@ -123,11 +127,48 @@ public class NodeInfo {
public static NodeInfo capture(@NonNull AccessibilityNodeInfo root) { public static NodeInfo capture(@NonNull AccessibilityNodeInfo root) {
UiObject r = UiObject.createRoot(root); UiObject r = UiObject.createRoot(root);
return capture(r); return capture(r, null);
} }
@NonNull @NonNull
public List<NodeInfo> getChildren() { public List<NodeInfo> getChildren() {
return children; return children;
} }
@Override
public String toString() {
return className + "{" +
"childCount=" + children.size() +
", mBoundsInScreen=" + mBoundsInScreen +
", mBoundsInParent=" + mBoundsInParent +
", id='" + id + '\'' +
", desc='" + desc + '\'' +
", packageName='" + packageName + '\'' +
", text='" + text + '\'' +
", depth=" + depth +
", drawingOrder=" + drawingOrder +
", accessibilityFocused=" + accessibilityFocused +
", checked=" + checked +
", clickable=" + clickable +
", contextClickable=" + contextClickable +
", dismissable=" + dismissable +
", editable=" + editable +
", enabled=" + enabled +
", focusable=" + focusable +
", longClickable=" + longClickable +
", row=" + row +
", column=" + column +
", rowCount=" + rowCount +
", columnCount=" + columnCount +
", rowSpan=" + rowSpan +
", columnSpan=" + columnSpan +
", selected=" + selected +
", scrollable=" + scrollable +
", bounds='" + bounds + '\'' +
", checkable=" + checkable +
", focused=" + focused +
", visibleToUser=" + visibleToUser +
", parent=" + parent.className +
'}';
}
} }