diff --git a/app/src/main/res/layout/activity_build.xml b/app/src/main/res/layout/activity_build.xml index 824a7586..58e80db1 100644 --- a/app/src/main/res/layout/activity_build.xml +++ b/app/src/main/res/layout/activity_build.xml @@ -68,7 +68,7 @@ android:layout_weight="1" android:hint="@string/text_source_file_path"> - - - @@ -159,7 +159,7 @@ android:layout_marginTop="12dp" android:hint="@string/text_package_name"> - - - - - - @@ -73,7 +73,7 @@ android:layout_marginTop="12dp" android:hint="@string/text_package_name"> - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_find_or_replace.xml b/app/src/main/res/layout/dialog_find_or_replace.xml index bc922df2..5b45215b 100644 --- a/app/src/main/res/layout/dialog_find_or_replace.xml +++ b/app/src/main/res/layout/dialog_find_or_replace.xml @@ -13,7 +13,7 @@ android:layout_height="wrap_content" android:hint="@string/text_find"> - @@ -30,7 +30,7 @@ android:layout_height="wrap_content" android:hint="@string/text_replace"> - diff --git a/app/src/main/res/layout/dialog_script_loop.xml b/app/src/main/res/layout/dialog_script_loop.xml index 922ae1c3..8a979a7e 100644 --- a/app/src/main/res/layout/dialog_script_loop.xml +++ b/app/src/main/res/layout/dialog_script_loop.xml @@ -13,7 +13,7 @@ android:layout_height="wrap_content" android:hint="@string/text_loop_times"> - - - - 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 2c85d687..cd3444b0 100644 --- a/autojs/src/main/java/com/stardust/autojs/codegeneration/UiSelectorGenerator.java +++ b/autojs/src/main/java/com/stardust/autojs/codegeneration/UiSelectorGenerator.java @@ -4,6 +4,8 @@ import com.stardust.automator.UiGlobalSelector; import com.stardust.automator.UiObject; import com.stardust.util.Consumer; +import androidx.appcompat.widget.AppCompatEditText; + import static com.stardust.autojs.codegeneration.CodeGenerator.FIND_ONE; import static com.stardust.autojs.codegeneration.CodeGenerator.UNTIL_FIND; import static com.stardust.autojs.codegeneration.CodeGenerator.WAIT_FOR; @@ -115,9 +117,9 @@ public class UiSelectorGenerator { private boolean shouldStopGeneration(UiGlobalSelector selector) { if (mSearchMode == UNTIL_FIND) { - return !selector.findAndReturnList(mRoot).isEmpty(); + return !selector.findAndReturnList(mRoot, 1).isEmpty(); } else { - return selector.findAndReturnList(mRoot).size() == 1; + return selector.findAndReturnList(mRoot, 2).size() == 1; } } diff --git a/autojs/src/main/java/com/stardust/autojs/core/accessibility/UiSelector.java b/autojs/src/main/java/com/stardust/autojs/core/accessibility/UiSelector.java index 12aac731..714e1f8d 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/accessibility/UiSelector.java +++ b/autojs/src/main/java/com/stardust/autojs/core/accessibility/UiSelector.java @@ -2,7 +2,9 @@ package com.stardust.autojs.core.accessibility; import android.os.Looper; import android.os.SystemClock; + import androidx.annotation.NonNull; + import android.util.Log; import android.view.accessibility.AccessibilityNodeInfo; @@ -70,22 +72,26 @@ public class UiSelector extends UiGlobalSelector { mAllocator = allocator; } - @NonNull - @ScriptInterface - public UiObjectCollection find() { + protected UiObjectCollection find(int max) { ensureAccessibilityServiceEnabled(); - if((mAccessibilityBridge.getFlags() & AccessibilityBridge.FLAG_FIND_ON_UI_THREAD)!=0 - && Looper.myLooper() != Looper.getMainLooper()){ + if ((mAccessibilityBridge.getFlags() & AccessibilityBridge.FLAG_FIND_ON_UI_THREAD) != 0 + && Looper.myLooper() != Looper.getMainLooper()) { VolatileBox result = new VolatileBox<>(); - mAccessibilityBridge.post(() -> result.setAndNotify(findImpl())); + mAccessibilityBridge.post(() -> result.setAndNotify(findImpl(max))); return result.blockedGet(); } - return findImpl(); + return findImpl(max); } @NonNull @ScriptInterface - protected UiObjectCollection findImpl() { + public UiObjectCollection find() { + return find(Integer.MAX_VALUE); + } + + @NonNull + @ScriptInterface + protected UiObjectCollection findImpl(int max) { AccessibilityNodeInfo root = mAccessibilityBridge.getRootInCurrentWindow(); if (BuildConfig.DEBUG) Log.d(TAG, "find: root = " + root); @@ -96,7 +102,7 @@ public class UiSelector extends UiGlobalSelector { Log.d(TAG, "package in white list, return null"); return UiObjectCollection.Companion.getEMPTY(); } - return findOf(UiObject.Companion.createRoot(root, mAllocator)); + return findOf(UiObject.Companion.createRoot(root, mAllocator), max); } @Override @@ -157,7 +163,7 @@ public class UiSelector extends UiGlobalSelector { } private void ensureNonUiThread() { - if(Looper.myLooper() == Looper.getMainLooper()){ + if (Looper.myLooper() == Looper.getMainLooper()) { // TODO: 2018/11/1 配置字符串 throw new IllegalThreadStateException("不能在ui线程执行阻塞操作, 请在子线程或子脚本执行findOne()或untilFind()"); } @@ -165,16 +171,13 @@ public class UiSelector extends UiGlobalSelector { @ScriptInterface public UiObject findOne(long timeout) { - if (timeout == -1) { - return untilFindOne(); - } - UiObjectCollection uiObjectCollection = find(); + UiObjectCollection uiObjectCollection = find(1); long start = SystemClock.uptimeMillis(); while (uiObjectCollection.empty()) { if (Thread.currentThread().isInterrupted()) { throw new ScriptInterruptedException(); } - if (SystemClock.uptimeMillis() - start > timeout) { + if (timeout > 0 && SystemClock.uptimeMillis() - start > timeout) { return null; } try { @@ -182,7 +185,7 @@ public class UiSelector extends UiGlobalSelector { } catch (InterruptedException e) { throw new ScriptInterruptedException(); } - uiObjectCollection = find(); + uiObjectCollection = find(1); } return uiObjectCollection.get(0); } @@ -192,7 +195,7 @@ public class UiSelector extends UiGlobalSelector { } public UiObject findOnce(int index) { - UiObjectCollection uiObjectCollection = find(); + UiObjectCollection uiObjectCollection = find(index + 1); if (index >= uiObjectCollection.size()) { return null; } @@ -212,8 +215,7 @@ public class UiSelector extends UiGlobalSelector { @NonNull public UiObject untilFindOne() { - UiObjectCollection collection = untilFind(); - return collection.get(0); + return findOne(-1); } @ScriptInterface diff --git a/automator/src/main/java/com/stardust/automator/UiGlobalSelector.kt b/automator/src/main/java/com/stardust/automator/UiGlobalSelector.kt index 25463d10..145d79f6 100644 --- a/automator/src/main/java/com/stardust/automator/UiGlobalSelector.kt +++ b/automator/src/main/java/com/stardust/automator/UiGlobalSelector.kt @@ -186,172 +186,172 @@ open class UiGlobalSelector { //// 第二类筛选条件 -able fun checkable(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.CHECKABLE, b)) + mFilters.add(BooleanFilter[BooleanFilter.CHECKABLE, b]) return this } fun checked(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.CHECKED, b)) + mFilters.add(BooleanFilter[BooleanFilter.CHECKED, b]) return this } fun focusable(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.FOCUSABLE, b)) + mFilters.add(BooleanFilter[BooleanFilter.FOCUSABLE, b]) return this } fun focused(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.FOCUSED, b)) + mFilters.add(BooleanFilter[BooleanFilter.FOCUSED, b]) return this } fun visibleToUser(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.VISIBLE_TO_USER, b)) + mFilters.add(BooleanFilter[BooleanFilter.VISIBLE_TO_USER, b]) return this } fun accessibilityFocused(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.ACCESSIBILITY_FOCUSED, b)) + mFilters.add(BooleanFilter[BooleanFilter.ACCESSIBILITY_FOCUSED, b]) return this } fun selected(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.SELECTED, b)) + mFilters.add(BooleanFilter[BooleanFilter.SELECTED, b]) return this } fun clickable(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.CLICKABLE, b)) + mFilters.add(BooleanFilter[BooleanFilter.CLICKABLE, b]) return this } fun longClickable(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.LONG_CLICKABLE, b)) + mFilters.add(BooleanFilter[BooleanFilter.LONG_CLICKABLE, b]) return this } fun enabled(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.ENABLED, b)) + mFilters.add(BooleanFilter[BooleanFilter.ENABLED, b]) return this } fun password(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.PASSWORD, b)) + mFilters.add(BooleanFilter[BooleanFilter.PASSWORD, b]) return this } fun scrollable(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.SCROLLABLE, b)) + mFilters.add(BooleanFilter[BooleanFilter.SCROLLABLE, b]) return this } fun editable(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.EDITABLE, b)) + mFilters.add(BooleanFilter[BooleanFilter.EDITABLE, b]) return this } fun contentInvalid(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.CONTENT_INVALID, b)) + mFilters.add(BooleanFilter[BooleanFilter.CONTENT_INVALID, b]) return this } fun contextClickable(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.CONTEXT_CLICKABLE, b)) + mFilters.add(BooleanFilter[BooleanFilter.CONTEXT_CLICKABLE, b]) return this } fun multiLine(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.MULTI_LINE, b)) + mFilters.add(BooleanFilter[BooleanFilter.MULTI_LINE, b]) return this } fun dismissable(b: Boolean): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.DISMISSABLE, b)) + mFilters.add(BooleanFilter[BooleanFilter.DISMISSABLE, b]) return this } fun checkable(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.CHECKABLE, true)) + mFilters.add(BooleanFilter[BooleanFilter.CHECKABLE, true]) return this } fun checked(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.CHECKED, true)) + mFilters.add(BooleanFilter[BooleanFilter.CHECKED, true]) return this } fun focusable(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.FOCUSABLE, true)) + mFilters.add(BooleanFilter[BooleanFilter.FOCUSABLE, true]) return this } fun focused(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.FOCUSED, true)) + mFilters.add(BooleanFilter[BooleanFilter.FOCUSED, true]) return this } fun visibleToUser(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.VISIBLE_TO_USER, true)) + mFilters.add(BooleanFilter[BooleanFilter.VISIBLE_TO_USER, true]) return this } fun accessibilityFocused(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.ACCESSIBILITY_FOCUSED, true)) + mFilters.add(BooleanFilter[BooleanFilter.ACCESSIBILITY_FOCUSED, true]) return this } fun selected(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.SELECTED, true)) + mFilters.add(BooleanFilter[BooleanFilter.SELECTED, true]) return this } fun clickable(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.CLICKABLE, true)) + mFilters.add(BooleanFilter[BooleanFilter.CLICKABLE, true]) return this } fun longClickable(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.LONG_CLICKABLE, true)) + mFilters.add(BooleanFilter[BooleanFilter.LONG_CLICKABLE, true]) return this } fun enabled(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.ENABLED, true)) + mFilters.add(BooleanFilter[BooleanFilter.ENABLED, true]) return this } fun password(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.PASSWORD, true)) + mFilters.add(BooleanFilter[BooleanFilter.PASSWORD, true]) return this } fun scrollable(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.SCROLLABLE, true)) + mFilters.add(BooleanFilter[BooleanFilter.SCROLLABLE, true]) return this } fun editable(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.EDITABLE, true)) + mFilters.add(BooleanFilter[BooleanFilter.EDITABLE, true]) return this } fun contentInvalid(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.CONTENT_INVALID, true)) + mFilters.add(BooleanFilter[BooleanFilter.CONTENT_INVALID, true]) return this } fun contextClickable(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.CONTEXT_CLICKABLE, true)) + mFilters.add(BooleanFilter[BooleanFilter.CONTEXT_CLICKABLE, true]) return this } fun multiLine(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.MULTI_LINE, true)) + mFilters.add(BooleanFilter[BooleanFilter.MULTI_LINE, true]) return this } fun dismissable(): UiGlobalSelector { - mFilters.add(BooleanFilter.get(BooleanFilter.DISMISSABLE, true)) + mFilters.add(BooleanFilter[BooleanFilter.DISMISSABLE, true]) return this } @@ -401,30 +401,36 @@ open class UiGlobalSelector { fun filter(filter: BooleanFilter.BooleanSupplier): UiGlobalSelector { mFilters.add(object : DfsFilter() { override fun isIncluded(nodeInfo: UiObject): Boolean { - return filter.get(nodeInfo) + return filter[nodeInfo] } }) return this } - fun findOf(node: UiObject): UiObjectCollection { - return UiObjectCollection.of(findAndReturnList(node)) + fun findOf(node: UiObject, max: Int): UiObjectCollection { + return UiObjectCollection.of(findAndReturnList(node, max)) } - fun findAndReturnList(node: UiObject): List { + fun findOf(node: UiObject): UiObjectCollection { + return findOf(node, Int.MAX_VALUE) + } + + fun findAndReturnList(node: UiObject, max: Int = Int.MAX_VALUE): List { var list: List = listOf(node) for (filter in mFilters) { + val oldMax = filter.maxCount + filter.maxCount = max list = filter.filter(list) + filter.maxCount = oldMax } return list } fun findOneOf(node: UiObject): UiObject? { - // TODO: 2017/3/9 优化 - val collection = findOf(node) + val collection = findOf(node, 1) return if (collection.size() == 0) { null - } else collection.get(0) + } else collection[0] } fun addFilter(filter: ListFilter): UiGlobalSelector { diff --git a/automator/src/main/java/com/stardust/automator/filter/DfsFilter.kt b/automator/src/main/java/com/stardust/automator/filter/DfsFilter.kt index 60ecf8d6..88cf85bd 100644 --- a/automator/src/main/java/com/stardust/automator/filter/DfsFilter.kt +++ b/automator/src/main/java/com/stardust/automator/filter/DfsFilter.kt @@ -16,6 +16,9 @@ abstract class DfsFilter : ListFilter(), Filter { if (isIncluded(node)) { list.add(node) } + if (list.size >= maxCount) { + break + } filterChildren(node, list) } return list @@ -26,6 +29,9 @@ abstract class DfsFilter : ListFilter(), Filter { if (isIncluded(node)) { list.add(node) } + if (list.size >= maxCount) { + return list + } filterChildren(node, list) return list } @@ -37,6 +43,9 @@ abstract class DfsFilter : ListFilter(), Filter { if (included) { list.add(child) } + if (list.size >= maxCount) { + break + } filterChildren(child, list) if (!included) { child.recycle() diff --git a/automator/src/main/java/com/stardust/automator/filter/ListFilter.kt b/automator/src/main/java/com/stardust/automator/filter/ListFilter.kt index 3e8fe541..b634244c 100644 --- a/automator/src/main/java/com/stardust/automator/filter/ListFilter.kt +++ b/automator/src/main/java/com/stardust/automator/filter/ListFilter.kt @@ -20,7 +20,7 @@ abstract class ListFilter { val list = ArrayList() for (node in nodes) { list.addAll(filter(node)) - if (list.size > maxCount) { + if (list.size >= maxCount) { break } }