optimize: code generation
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> condition, StringBuilder code) {
|
||||
if (value == null || value.isEmpty())
|
||||
private boolean tryWithBooleanCondition(UiGlobalSelector selector, boolean value, Consumer<Boolean> condition) {
|
||||
condition.accept(value);
|
||||
return shouldStopGeneration(selector);
|
||||
}
|
||||
|
||||
|
||||
private boolean tryWithStringCondition(UiGlobalSelector selector, String value, Consumer<String> 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<Integer> 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<Integer> 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user