diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/codegeneration/CodeGenerateDialog.java b/app/src/main/java/com/stardust/scriptdroid/ui/codegeneration/CodeGenerateDialog.java index 9028fc3d..edc9df42 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/codegeneration/CodeGenerateDialog.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/codegeneration/CodeGenerateDialog.java @@ -16,7 +16,8 @@ import com.bignerdranch.expandablerecyclerview.ChildViewHolder; import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter; import com.bignerdranch.expandablerecyclerview.ParentViewHolder; 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.ui.widget.CheckBoxCompat; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; @@ -66,39 +67,39 @@ public class CodeGenerateDialog extends ThemeColorMaterialDialogBuilder { super(context); mRootNode = rootNode; mTargetNode = targetNode; - positiveText(R.string.text_generate_and_copy); - neutralText(R.string.text_generate_and_open_editor); - onPositive(((dialog, which) -> generateCodeAndCopy())); - onNeutral(((dialog, which) -> generateCodeAndOpenEditor())); + positiveText(R.string.text_generate); + negativeText(R.string.text_cancel); + onPositive(((dialog, which) -> generateCodeAndShow())); setupViews(); } - private void generateCodeAndCopy() { + private void generateCodeAndShow() { String code = generateCode(); if (code == null) { Toast.makeText(getContext(), R.string.text_generate_fail, Toast.LENGTH_SHORT).show(); return; } - ClipboardUtil.setClip(getContext(), code); - Toast.makeText(getContext(), R.string.text_already_copy_to_clip, Toast.LENGTH_SHORT).show(); + DialogUtils.showDialog(new ThemeColorMaterialDialogBuilder(getContext()) + .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() { - UiSelectorGenerator generator = new UiSelectorGenerator(mRootNode, mTargetNode); + CodeGenerator generator = new CodeGenerator(mRootNode, mTargetNode); OptionGroup settings = getOptionGroup(R.string.text_options); generator.setUsingId(settings.getOption(R.string.text_using_id_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.setSearchMode(getSearchMode()); 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); if (action.getOption(R.string.text_click).checked) { generator.setAction(AccessibilityNodeInfoCompat.ACTION_CLICK); @@ -117,18 +118,18 @@ public class CodeGenerateDialog extends ThemeColorMaterialDialogBuilder { private int getSearchMode() { OptionGroup selectMode = getOptionGroup(R.string.text_select); 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) { - return UiSelectorGenerator.UNTIL_FIND; + return CodeGenerator.UNTIL_FIND; } 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) { - return UiSelectorGenerator.EXISTS; + return CodeGenerator.EXISTS; } - return UiSelectorGenerator.FIND_ONE; + return CodeGenerator.FIND_ONE; } private void setupViews() { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0eafa6cb..4834a95f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -299,7 +299,7 @@ 使用id选择 使用文本选择(text) 使用描述选择(desc) - 生成并复制 + 生成 生成并打开编辑器 生成失败了o(╥﹏╥)o key_documentation_source @@ -326,4 +326,6 @@ 打包插件不可用 详情 打开 + 生成结果 + 复制 diff --git a/autojs/src/main/java/com/stardust/autojs/codegeneration/CodeGenerator.java b/autojs/src/main/java/com/stardust/autojs/codegeneration/CodeGenerator.java new file mode 100644 index 00000000..32379af5 --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/codegeneration/CodeGenerator.java @@ -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; + } + +} diff --git a/autojs/src/main/java/com/stardust/autojs/codegeneration/ReadOnlyUiObject.java b/autojs/src/main/java/com/stardust/autojs/codegeneration/ReadOnlyUiObject.java index 119860f8..c2fb2758 100644 --- a/autojs/src/main/java/com/stardust/autojs/codegeneration/ReadOnlyUiObject.java +++ b/autojs/src/main/java/com/stardust/autojs/codegeneration/ReadOnlyUiObject.java @@ -6,7 +6,6 @@ import android.support.annotation.Nullable; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import com.stardust.automator.UiObject; -import com.stardust.automator.UiObjectCollection; import com.stardust.view.accessibility.NodeInfo; /** @@ -16,19 +15,27 @@ import com.stardust.view.accessibility.NodeInfo; public class ReadOnlyUiObject extends UiObject { private NodeInfo mNodeInfo; - private boolean mUsingId; - private boolean mUsingText; - private boolean mUsingDesc; 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; } @Nullable @Override 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 @@ -189,6 +196,82 @@ public class ReadOnlyUiObject extends UiObject { 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 public int row() { return mNodeInfo.row; @@ -223,4 +306,27 @@ public class ReadOnlyUiObject extends UiObject { 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(); + } } diff --git a/autojs/src/main/java/com/stardust/autojs/codegeneration/UiSelectorGenerator.java b/autojs/src/main/java/com/stardust/autojs/codegeneration/UiSelectorGenerator.java index 2161ceca..89f257ed 100644 --- a/autojs/src/main/java/com/stardust/autojs/codegeneration/UiSelectorGenerator.java +++ b/autojs/src/main/java/com/stardust/autojs/codegeneration/UiSelectorGenerator.java @@ -1,49 +1,81 @@ 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.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 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 UiObject mRoot; + private UiObject mTarget; private boolean mUsingId = true; private boolean mUsingDesc = true; private boolean mUsingText = true; - private UiGlobalSelector mUiGlobalSelector; private int mSearchMode = FIND_ONE; - private int mAction = -1; - public UiSelectorGenerator(NodeInfo root, NodeInfo target) { - this(new ReadOnlyUiObject(root), new ReadOnlyUiObject(target)); - } - - public UiSelectorGenerator(ReadOnlyUiObject root, ReadOnlyUiObject target) { + public UiSelectorGenerator(UiObject root, UiObject target) { mRoot = root; mTarget = target; } - public void setSearchMode(int searchMode) { - mSearchMode = searchMode; + public UiGlobalSelector generateSelector() { + 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) { - mAction = action; + public String generateSelectorCode() { + UiGlobalSelector selector = generateSelector(); + if (selector == null) { + return null; + } + return selector + (mSearchMode == FIND_ONE ? ".findOne()" : ".untilFind()"); } + public void setUsingId(boolean usingId) { mUsingId = usingId; } @@ -56,94 +88,37 @@ public class UiSelectorGenerator { mUsingText = usingText; } + public void setSearchMode(int searchMode) { + mSearchMode = searchMode; + } - private boolean tryWithStringCondition(String name, String value, Consumer condition, StringBuilder code) { - if (value == null || value.isEmpty()) + private boolean tryWithBooleanCondition(UiGlobalSelector selector, boolean value, Consumer condition) { + condition.accept(value); + return shouldStopGeneration(selector); + } + + + private boolean tryWithStringCondition(UiGlobalSelector selector, String value, Consumer condition) { + if (value == null || value.isEmpty()) { return false; - code.append('.').append(name).append("(\"").append(value).append("\")"); + } condition.accept(value); - return isConditionEnough(); + return shouldStopGeneration(selector); } - private boolean tryWithIntCondition(String name, int value, Consumer condition, StringBuilder code) { - code.append('.').append(name).append('(').append(value).append(')'); - condition.accept(value); - return isConditionEnough(); - } - - private boolean isConditionEnough() { + private boolean shouldStopGeneration(UiGlobalSelector selector) { if (mSearchMode == UNTIL_FIND) { - return !mUiGlobalSelector.findAndReturnList(mRoot).isEmpty(); + return !selector.findAndReturnList(mRoot).isEmpty(); } else { - return mUiGlobalSelector.findAndReturnList(mRoot).size() == 1; + return selector.findAndReturnList(mRoot).size() == 1; } } - public String generate() { - String selector = generateSelector(); - if (selector == null) { - 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 boolean tryWithIntCondition(UiGlobalSelector selector, int value, Consumer condition) { + condition.accept(value); + return shouldStopGeneration(selector); } - 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; - } } diff --git a/automator/src/main/java/com/stardust/automator/UiGlobalSelector.java b/automator/src/main/java/com/stardust/automator/UiGlobalSelector.java index e2b3c865..e792acab 100644 --- a/automator/src/main/java/com/stardust/automator/UiGlobalSelector.java +++ b/automator/src/main/java/com/stardust/automator/UiGlobalSelector.java @@ -178,6 +178,11 @@ public class UiGlobalSelector { protected boolean isIncluded(UiObject nodeInfo) { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && nodeInfo.getDrawingOrder() == order; } + + @Override + public String toString() { + return "drawingOrder(" + order + ")"; + } }); return this; } @@ -430,5 +435,13 @@ public class UiGlobalSelector { 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(); + } } diff --git a/automator/src/main/java/com/stardust/automator/UiObject.java b/automator/src/main/java/com/stardust/automator/UiObject.java index a8bcff33..2e107b66 100644 --- a/automator/src/main/java/com/stardust/automator/UiObject.java +++ b/automator/src/main/java/com/stardust/automator/UiObject.java @@ -37,37 +37,39 @@ public class UiObject extends AccessibilityNodeInfoCompat { 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) { - return new UiObject(root, allocator, 0); + return new UiObject(root, allocator, 0, -1); } private AccessibilityNodeInfoAllocator mAllocator = null; private String mStackTrace = ""; private int mDepth = 0; + private final int mIndexInParent; 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); mDepth = depth; mAllocator = allocator; + mIndexInParent = indexInParent; if (DEBUG) mStackTrace = Arrays.toString(Thread.currentThread().getStackTrace()); } - public UiObject(Object info, AccessibilityNodeInfoAllocator allocator) { - this(info, allocator, 0); + public UiObject(Object info, AccessibilityNodeInfoAllocator allocator, int indexInParent) { + this(info, allocator, 0, indexInParent); } - public UiObject(Object info, int depth) { - this(info, null, depth); + public UiObject(Object info, int depth, int indexInParent) { + this(info, null, depth, indexInParent); } @Nullable @@ -76,7 +78,7 @@ public class UiObject extends AccessibilityNodeInfoCompat { AccessibilityNodeInfoCompat parent = super.getParent(); if (parent == null) return null; - return new UiObject(parent.getInfo(), mDepth - 1); + return new UiObject(parent.getInfo(), mDepth - 1, -1); } catch (IllegalStateException e) { // FIXME: 2017/5/5 return null; @@ -89,13 +91,17 @@ public class UiObject extends AccessibilityNodeInfoCompat { AccessibilityNodeInfoCompat child = super.getChild(i); if (child == null) return null; - return new UiObject(child.getInfo(), mDepth + 1); + return new UiObject(child.getInfo(), mDepth + 1, i); } catch (IllegalStateException e) { // FIXME: 2017/5/5 return null; } } + public int indexInParent() { + return mIndexInParent; + } + public UiObjectCollection find(UiGlobalSelector selector) { return selector.findOf(this); } @@ -105,11 +111,11 @@ public class UiObject extends AccessibilityNodeInfoCompat { } public UiObjectCollection children() { - ArrayList list = new ArrayList<>(getChildCount()); + ArrayList list = new ArrayList<>(getChildCount()); 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() { @@ -428,20 +434,6 @@ public class UiObject extends AccessibilityNodeInfoCompat { return new UiGlobalSelector().id(viewId).findAndReturnList(this); } - public static List compatListToUiObjectList(List compats, AccessibilityNodeInfoAllocator allocator) { - // FIXME: 2017/11/5 lost depth info - List uiObjects = new ArrayList<>(compats.size()); - for (AccessibilityNodeInfoCompat compat : compats) { - if (compat != null) - uiObjects.add(new UiObject(compat.getInfo(), allocator)); - } - return uiObjects; - } - - public static List compatListToUiObjectList(List compats) { - return compatListToUiObjectList(compats, null); - } - @Override public void recycle() { try { @@ -451,4 +443,5 @@ public class UiObject extends AccessibilityNodeInfoCompat { } } + } diff --git a/automator/src/main/java/com/stardust/automator/UiObjectCollection.java b/automator/src/main/java/com/stardust/automator/UiObjectCollection.java index d4ae681d..1435a356 100644 --- a/automator/src/main/java/com/stardust/automator/UiObjectCollection.java +++ b/automator/src/main/java/com/stardust/automator/UiObjectCollection.java @@ -31,10 +31,6 @@ public class UiObjectCollection { public static final UiObjectCollection EMPTY = UiObjectCollection.of(Collections.emptyList()); - public static UiObjectCollection ofCompat(List list) { - return new UiObjectCollection(UiObject.compatListToUiObjectList(list)); - } - public static UiObjectCollection of(List list) { return new UiObjectCollection(list); } diff --git a/automator/src/main/java/com/stardust/automator/filter/BooleanFilter.java b/automator/src/main/java/com/stardust/automator/filter/BooleanFilter.java index 1f01dd01..627d8a0b 100644 --- a/automator/src/main/java/com/stardust/automator/filter/BooleanFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/BooleanFilter.java @@ -42,6 +42,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isCheckable(); } + + @Override + public String toString() { + return "checkable"; + } }; public static final BooleanSupplier CHECKED = new BooleanSupplier() { @@ -50,6 +55,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isChecked(); } + + @Override + public String toString() { + return "checked"; + } }; public static final BooleanSupplier FOCUSABLE = new BooleanSupplier() { @@ -58,6 +68,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isFocusable(); } + + @Override + public String toString() { + return "focusable"; + } }; public static final BooleanSupplier FOCUSED = new BooleanSupplier() { @@ -66,6 +81,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isFocused(); } + + @Override + public String toString() { + return "focused"; + } }; public static final BooleanSupplier VISIBLE_TO_USER = new BooleanSupplier() { @@ -74,6 +94,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isVisibleToUser(); } + + @Override + public String toString() { + return "visibleToUser"; + } }; public static final BooleanSupplier ACCESSIBILITY_FOCUSED = new BooleanSupplier() { @@ -82,6 +107,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isAccessibilityFocused(); } + + @Override + public String toString() { + return "accessibilityFocused"; + } }; public static final BooleanSupplier SELECTED = new BooleanSupplier() { @@ -90,6 +120,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isSelected(); } + + @Override + public String toString() { + return "selected"; + } }; public static final BooleanSupplier CLICKABLE = new BooleanSupplier() { @@ -98,6 +133,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isClickable(); } + + @Override + public String toString() { + return "clickable"; + } }; public static final BooleanSupplier LONG_CLICKABLE = new BooleanSupplier() { @@ -106,6 +146,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isLongClickable(); } + + @Override + public String toString() { + return "longClickable"; + } }; public static final BooleanSupplier ENABLED = new BooleanSupplier() { @@ -114,6 +159,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isEnabled(); } + + @Override + public String toString() { + return "enabled"; + } }; @@ -123,6 +173,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isPassword(); } + + @Override + public String toString() { + return "password"; + } }; public static final BooleanSupplier SCROLLABLE = new BooleanSupplier() { @@ -131,6 +186,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isScrollable(); } + + @Override + public String toString() { + return "scrollable"; + } }; public static final BooleanSupplier EDITABLE = new BooleanSupplier() { @@ -139,6 +199,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isEditable(); } + + @Override + public String toString() { + return "editable"; + } }; public static final BooleanSupplier CONTENT_INVALID = new BooleanSupplier() { @@ -146,6 +211,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isContentInvalid(); } + + @Override + public String toString() { + return "contentInvalid"; + } }; public static final BooleanSupplier CONTEXT_CLICKABLE = new BooleanSupplier() { @@ -155,6 +225,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isContextClickable(); } + + @Override + public String toString() { + return "checkable"; + } }; public static final BooleanSupplier MULTI_LINE = new BooleanSupplier() { @@ -163,6 +238,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isMultiLine(); } + + @Override + public String toString() { + return "multiLine"; + } }; public static final BooleanSupplier DISMISSABLE = new BooleanSupplier() { @@ -171,6 +251,11 @@ public class BooleanFilter extends DfsFilter { public boolean get(UiObject node) { return node.isDismissable(); } + + @Override + public String toString() { + return "dismissable"; + } }; private BooleanSupplier mBooleanSupplier; @@ -186,4 +271,8 @@ public class BooleanFilter extends DfsFilter { return nodeInfo != null && mBooleanSupplier.get(nodeInfo) == mExceptedValue; } + @Override + public String toString() { + return mBooleanSupplier.toString() + "(" + mExceptedValue + ")"; + } } diff --git a/automator/src/main/java/com/stardust/automator/filter/BoundsFilter.java b/automator/src/main/java/com/stardust/automator/filter/BoundsFilter.java index 37cade4d..1b243fec 100644 --- a/automator/src/main/java/com/stardust/automator/filter/BoundsFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/BoundsFilter.java @@ -5,6 +5,8 @@ import android.graphics.Rect; import com.stardust.automator.UiObject; import com.stardust.view.accessibility.AccessibilityNodeInfoHelper; +import java.util.Locale; + /** * Created by Stardust on 2017/3/9. */ @@ -33,4 +35,11 @@ public class BoundsFilter extends DfsFilter { return boundsInScreen.equals(mBounds); 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); + } } diff --git a/automator/src/main/java/com/stardust/automator/filter/ClassNameFilter.java b/automator/src/main/java/com/stardust/automator/filter/ClassNameFilter.java index 9cb3b6a6..611646ff 100644 --- a/automator/src/main/java/com/stardust/automator/filter/ClassNameFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/ClassNameFilter.java @@ -14,6 +14,11 @@ public class ClassNameFilter { CharSequence charSequence = nodeInfo.getClassName(); return charSequence == null ? null : charSequence.toString(); } + + @Override + public String toString() { + return "className"; + } }; public static ListFilter equals(String text) { diff --git a/automator/src/main/java/com/stardust/automator/filter/DescFilter.java b/automator/src/main/java/com/stardust/automator/filter/DescFilter.java index fb066721..92517644 100644 --- a/automator/src/main/java/com/stardust/automator/filter/DescFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/DescFilter.java @@ -14,6 +14,11 @@ public class DescFilter { CharSequence charSequence = nodeInfo.getContentDescription(); return charSequence == null ? null : charSequence.toString(); } + + @Override + public String toString() { + return "desc"; + } }; public static ListFilter equals(String text) { diff --git a/automator/src/main/java/com/stardust/automator/filter/IdFilter.java b/automator/src/main/java/com/stardust/automator/filter/IdFilter.java index 6aa934dc..917129fa 100644 --- a/automator/src/main/java/com/stardust/automator/filter/IdFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/IdFilter.java @@ -16,6 +16,11 @@ public class IdFilter extends ListFilter.Default { public String getKey(UiObject nodeInfo) { return nodeInfo.getViewIdResourceName(); } + + @Override + public String toString() { + return "id"; + } }; public static StringEqualsFilter equals(String id) { diff --git a/automator/src/main/java/com/stardust/automator/filter/IntFilter.java b/automator/src/main/java/com/stardust/automator/filter/IntFilter.java index fed85da3..d5b356a7 100644 --- a/automator/src/main/java/com/stardust/automator/filter/IntFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/IntFilter.java @@ -17,6 +17,11 @@ public class IntFilter extends DfsFilter { public int get(UiObject object) { return object.depth(); } + + @Override + public String toString() { + return "depth"; + } }; public static final IntProperty ROW = new IntProperty() { @@ -24,6 +29,11 @@ public class IntFilter extends DfsFilter { public int get(UiObject object) { return object.row(); } + + @Override + public String toString() { + return "row"; + } }; public static final IntProperty ROW_COUNT = new IntProperty() { @@ -31,6 +41,11 @@ public class IntFilter extends DfsFilter { public int get(UiObject object) { return object.rowCount(); } + + @Override + public String toString() { + return "rowCount"; + } }; public static final IntProperty ROW_SPAN = new IntProperty() { @@ -38,6 +53,11 @@ public class IntFilter extends DfsFilter { public int get(UiObject object) { return object.rowSpan(); } + + @Override + public String toString() { + return "rowSpan"; + } }; public static final IntProperty COLUMN = new IntProperty() { @@ -45,6 +65,11 @@ public class IntFilter extends DfsFilter { public int get(UiObject object) { return object.column(); } + + @Override + public String toString() { + return "column"; + } }; public static final IntProperty COLUMN_COUNT = new IntProperty() { @@ -52,6 +77,11 @@ public class IntFilter extends DfsFilter { public int get(UiObject object) { return object.columnCount(); } + + @Override + public String toString() { + return "columnCount"; + } }; public static final IntProperty COLUMN_SPAN = new IntProperty() { @@ -59,6 +89,11 @@ public class IntFilter extends DfsFilter { public int get(UiObject object) { return object.columnSpan(); } + + @Override + public String toString() { + return "columnSpan"; + } }; private IntProperty mIntProperty; @@ -73,4 +108,9 @@ public class IntFilter extends DfsFilter { protected boolean isIncluded(UiObject nodeInfo) { return mIntProperty.get(nodeInfo) == mValue; } + + @Override + public String toString() { + return mIntProperty.toString() + "(" + mValue + ")"; + } } diff --git a/automator/src/main/java/com/stardust/automator/filter/PackageNameFilter.java b/automator/src/main/java/com/stardust/automator/filter/PackageNameFilter.java index ab48dc25..17436b03 100644 --- a/automator/src/main/java/com/stardust/automator/filter/PackageNameFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/PackageNameFilter.java @@ -14,6 +14,11 @@ public class PackageNameFilter { CharSequence charSequence = nodeInfo.getPackageName(); return charSequence == null ? null : charSequence.toString(); } + + @Override + public String toString() { + return "packageName"; + } }; public static ListFilter equals(String text) { diff --git a/automator/src/main/java/com/stardust/automator/filter/StringContainsFilter.java b/automator/src/main/java/com/stardust/automator/filter/StringContainsFilter.java index 21a37d74..2647cbb0 100644 --- a/automator/src/main/java/com/stardust/automator/filter/StringContainsFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/StringContainsFilter.java @@ -21,4 +21,8 @@ public class StringContainsFilter extends DfsFilter { return key != null && key.contains(mContains); } + @Override + public String toString() { + return mKeyGetter.toString() + "Contains(\"" + mContains + "\")"; + } } diff --git a/automator/src/main/java/com/stardust/automator/filter/StringEndsWithFilter.java b/automator/src/main/java/com/stardust/automator/filter/StringEndsWithFilter.java index 42914e7c..ee78bebd 100644 --- a/automator/src/main/java/com/stardust/automator/filter/StringEndsWithFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/StringEndsWithFilter.java @@ -22,4 +22,8 @@ public class StringEndsWithFilter extends DfsFilter { return key != null && key.endsWith(mSuffix); } + @Override + public String toString() { + return mKeyGetter.toString() + "EndsWith(\"" + mSuffix + "\")"; + } } diff --git a/automator/src/main/java/com/stardust/automator/filter/StringEqualsFilter.java b/automator/src/main/java/com/stardust/automator/filter/StringEqualsFilter.java index e3b84818..61059ffe 100644 --- a/automator/src/main/java/com/stardust/automator/filter/StringEqualsFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/StringEqualsFilter.java @@ -19,9 +19,14 @@ public class StringEqualsFilter extends DfsFilter { @Override protected boolean isIncluded(UiObject nodeInfo) { String key = mKeyGetter.getKey(nodeInfo); - if(key != null){ + if (key != null) { return key.equals(mText); } return false; } + + @Override + public String toString() { + return mKeyGetter.toString() + "(\"" + mText + "\")"; + } } diff --git a/automator/src/main/java/com/stardust/automator/filter/StringMatchesFilter.java b/automator/src/main/java/com/stardust/automator/filter/StringMatchesFilter.java index 46470eeb..bcab8b4f 100644 --- a/automator/src/main/java/com/stardust/automator/filter/StringMatchesFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/StringMatchesFilter.java @@ -22,4 +22,8 @@ public class StringMatchesFilter extends DfsFilter { return key != null && key.matches(mRegex); } + @Override + public String toString() { + return mKeyGetter.toString() + "Matches(\"" + mRegex + "\")"; + } } diff --git a/automator/src/main/java/com/stardust/automator/filter/StringStartsWithFilter.java b/automator/src/main/java/com/stardust/automator/filter/StringStartsWithFilter.java index e8347003..954f0f73 100644 --- a/automator/src/main/java/com/stardust/automator/filter/StringStartsWithFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/StringStartsWithFilter.java @@ -22,4 +22,8 @@ public class StringStartsWithFilter extends DfsFilter { return key != null && key.startsWith(mPrefix); } + @Override + public String toString() { + return mKeyGetter.toString() + "StartsWith(\"" + mPrefix + "\")"; + } } diff --git a/automator/src/main/java/com/stardust/automator/filter/TextFilter.java b/automator/src/main/java/com/stardust/automator/filter/TextFilter.java index 9cfa6593..fc87dc12 100644 --- a/automator/src/main/java/com/stardust/automator/filter/TextFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/TextFilter.java @@ -16,6 +16,11 @@ public class TextFilter extends ListFilter.Default { CharSequence charSequence = nodeInfo.getText(); return charSequence == null ? null : charSequence.toString(); } + + @Override + public String toString() { + return "text"; + } }; public static ListFilter equals(String text) { diff --git a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java index 6b16b4c8..00680218 100644 --- a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java +++ b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java @@ -61,10 +61,10 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi Log.v(TAG, "onAccessibilityEvent: " + event); if (!containsAllEventTypes && !eventTypes.contains(event.getEventType())) return; - int type = event.getEventType(); - if (type == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || type == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER - || type == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { - mFastRootInActiveWindow = super.getRootInActiveWindow(); + AccessibilityNodeInfo root = super.getRootInActiveWindow(); + if (root != null) { + mFastRootInActiveWindow = root; + Log.v(TAG, "root not null: " + event); } for (Map.Entry entry : mDelegates.entrySet()) { AccessibilityDelegate delegate = entry.getValue(); @@ -164,4 +164,5 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi return stickOnKeyObserver; } + } diff --git a/automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java b/automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java index 9cc30e1b..7d99e080 100644 --- a/automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java +++ b/automator/src/main/java/com/stardust/view/accessibility/LayoutInspector.java @@ -28,23 +28,28 @@ public class LayoutInspector { Log.d(LOG_TAG, "captureCurrentWindow: service = null"); mCapture = null; } else { - final AccessibilityNodeInfo root = service.getRootInActiveWindow(); + final AccessibilityNodeInfo root = getRootInActiveWindow(service); if (root == null) { Log.d(LOG_TAG, "captureCurrentWindow: root = null"); mCapture = null; } else { - mExecutor.execute(new Runnable() { - @Override - public void run() { - mDumping = true; - mCapture = NodeInfo.capture(root); - mDumping = false; - } + mExecutor.execute(() -> { + mDumping = true; + 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) private void refreshChildList(AccessibilityNodeInfo root) { if (root == null) diff --git a/automator/src/main/java/com/stardust/view/accessibility/NodeInfo.java b/automator/src/main/java/com/stardust/view/accessibility/NodeInfo.java index 2b334522..95a09967 100644 --- a/automator/src/main/java/com/stardust/view/accessibility/NodeInfo.java +++ b/automator/src/main/java/com/stardust/view/accessibility/NodeInfo.java @@ -3,6 +3,7 @@ package com.stardust.view.accessibility; import android.graphics.Rect; import android.support.annotation.Keep; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.view.accessibility.AccessibilityNodeInfo; @@ -49,9 +50,10 @@ public class NodeInfo { public boolean checkable; public boolean focused; public boolean visibleToUser; + public NodeInfo parent; - public NodeInfo(UiObject node) { + public NodeInfo(UiObject node, NodeInfo parent) { id = simplifyId(node.getViewIdResourceName()); desc = node.desc(); className = node.className(); @@ -86,6 +88,8 @@ public class NodeInfo { node.getBoundsInParent(mBoundsInParent); bounds = boundsToString(mBoundsInScreen); + this.parent = parent; + } private String simplifyId(String idResourceName) { @@ -109,13 +113,13 @@ public class NodeInfo { } - public static NodeInfo capture(@NonNull UiObject parent) { - NodeInfo nodeInfo = new NodeInfo(parent); - int childCount = parent.getChildCount(); + public static NodeInfo capture(@NonNull UiObject uiObject, @Nullable NodeInfo parent) { + NodeInfo nodeInfo = new NodeInfo(uiObject, parent); + int childCount = uiObject.getChildCount(); for (int i = 0; i < childCount; i++) { - UiObject child = parent.child(i); + UiObject child = uiObject.child(i); if (child != null) { - nodeInfo.children.add(capture(child)); + nodeInfo.children.add(capture(child, nodeInfo)); } } return nodeInfo; @@ -123,11 +127,48 @@ public class NodeInfo { public static NodeInfo capture(@NonNull AccessibilityNodeInfo root) { UiObject r = UiObject.createRoot(root); - return capture(r); + return capture(r, null); } @NonNull public List getChildren() { 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 + + '}'; + } }