修复 选择器有多个条件时无法找到目标的问题

优化 选择器搜索效率
This commit is contained in:
hyb1996
2018-12-04 13:14:06 +08:00
parent 754819b23f
commit 4b3d4ab84c
26 changed files with 282 additions and 457 deletions

View File

@@ -2,23 +2,7 @@ package com.stardust.automator
import android.graphics.Rect
import android.os.Build
import com.stardust.automator.filter.BooleanFilter
import com.stardust.automator.filter.BoundsFilter
import com.stardust.automator.filter.ClassNameFilter
import com.stardust.automator.filter.DescFilter
import com.stardust.automator.filter.DfsFilter
import com.stardust.automator.filter.IdFilter
import com.stardust.automator.filter.IntFilter
import com.stardust.automator.filter.ListFilter
import com.stardust.automator.filter.PackageNameFilter
import com.stardust.automator.filter.TextFilter
import com.stardust.automator.simple_action.Able
import com.stardust.util.Supplier
import java.util.ArrayList
import java.util.LinkedList
import java.util.Queue
import com.stardust.automator.filter.*
/**
* Created by Stardust on 2017/3/8.
@@ -26,154 +10,154 @@ import java.util.Queue
open class UiGlobalSelector {
private val mFilters = LinkedList<ListFilter>()
private val mSelector = Selector()
//// 第一类筛选条件
open fun id(id: String): UiGlobalSelector {
mFilters.add(IdFilter.equals(id))
mSelector.add(IdFilter.equals(id))
return this
}
fun idContains(str: String): UiGlobalSelector {
mFilters.add(IdFilter.contains(str))
mSelector.add(IdFilter.contains(str))
return this
}
open fun idStartsWith(prefix: String): UiGlobalSelector {
mFilters.add(IdFilter.startsWith(prefix))
mSelector.add(IdFilter.startsWith(prefix))
return this
}
fun idEndsWith(suffix: String): UiGlobalSelector {
mFilters.add(IdFilter.endsWith(suffix))
mSelector.add(IdFilter.endsWith(suffix))
return this
}
open fun idMatches(regex: String): UiGlobalSelector {
mFilters.add(IdFilter.matches(regex))
mSelector.add(IdFilter.matches(regex))
return this
}
fun text(text: String): UiGlobalSelector {
mFilters.add(TextFilter.equals(text))
mSelector.add(TextFilters.equals(text))
return this
}
fun textContains(str: String): UiGlobalSelector {
mFilters.add(TextFilter.contains(str))
mSelector.add(TextFilters.contains(str))
return this
}
fun textStartsWith(prefix: String): UiGlobalSelector {
mFilters.add(TextFilter.startsWith(prefix))
mSelector.add(TextFilters.startsWith(prefix))
return this
}
fun textEndsWith(suffix: String): UiGlobalSelector {
mFilters.add(TextFilter.endsWith(suffix))
mSelector.add(TextFilters.endsWith(suffix))
return this
}
open fun textMatches(regex: String): UiGlobalSelector {
mFilters.add(TextFilter.matches(regex))
mSelector.add(TextFilters.matches(regex))
return this
}
fun desc(desc: String): UiGlobalSelector {
mFilters.add(DescFilter.equals(desc))
mSelector.add(DescFilters.equals(desc))
return this
}
fun descContains(str: String): UiGlobalSelector {
mFilters.add(DescFilter.contains(str))
mSelector.add(DescFilters.contains(str))
return this
}
fun descStartsWith(prefix: String): UiGlobalSelector {
mFilters.add(DescFilter.startsWith(prefix))
mSelector.add(DescFilters.startsWith(prefix))
return this
}
fun descEndsWith(suffix: String): UiGlobalSelector {
mFilters.add(DescFilter.endsWith(suffix))
mSelector.add(DescFilters.endsWith(suffix))
return this
}
open fun descMatches(regex: String): UiGlobalSelector {
mFilters.add(DescFilter.matches(regex))
mSelector.add(DescFilters.matches(regex))
return this
}
fun className(className: String): UiGlobalSelector {
mFilters.add(ClassNameFilter.equals(className))
mSelector.add(ClassNameFilters.equals(className))
return this
}
fun classNameContains(str: String): UiGlobalSelector {
mFilters.add(ClassNameFilter.contains(str))
mSelector.add(ClassNameFilters.contains(str))
return this
}
fun classNameStartsWith(prefix: String): UiGlobalSelector {
mFilters.add(ClassNameFilter.startsWith(prefix))
mSelector.add(ClassNameFilters.startsWith(prefix))
return this
}
fun classNameEndsWith(suffix: String): UiGlobalSelector {
mFilters.add(ClassNameFilter.endsWith(suffix))
mSelector.add(ClassNameFilters.endsWith(suffix))
return this
}
open fun classNameMatches(regex: String): UiGlobalSelector {
mFilters.add(ClassNameFilter.matches(regex))
mSelector.add(ClassNameFilters.matches(regex))
return this
}
fun packageName(packageName: String): UiGlobalSelector {
mFilters.add(PackageNameFilter.equals(packageName))
mSelector.add(PackageNameFilter.equals(packageName))
return this
}
fun packageNameContains(str: String): UiGlobalSelector {
mFilters.add(PackageNameFilter.contains(str))
mSelector.add(PackageNameFilter.contains(str))
return this
}
fun packageNameStartsWith(prefix: String): UiGlobalSelector {
mFilters.add(PackageNameFilter.startsWith(prefix))
mSelector.add(PackageNameFilter.startsWith(prefix))
return this
}
fun packageNameEndsWith(suffix: String): UiGlobalSelector {
mFilters.add(PackageNameFilter.endsWith(suffix))
mSelector.add(PackageNameFilter.endsWith(suffix))
return this
}
open fun packageNameMatches(regex: String): UiGlobalSelector {
mFilters.add(PackageNameFilter.matches(regex))
mSelector.add(PackageNameFilter.matches(regex))
return this
}
fun bounds(l: Int, t: Int, r: Int, b: Int): UiGlobalSelector {
mFilters.add(BoundsFilter(Rect(l, t, r, b), BoundsFilter.TYPE_EQUALS))
mSelector.add(BoundsFilter(Rect(l, t, r, b), BoundsFilter.TYPE_EQUALS))
return this
}
fun boundsInside(l: Int, t: Int, r: Int, b: Int): UiGlobalSelector {
mFilters.add(BoundsFilter(Rect(l, t, r, b), BoundsFilter.TYPE_INSIDE))
mSelector.add(BoundsFilter(Rect(l, t, r, b), BoundsFilter.TYPE_INSIDE))
return this
}
fun boundsContains(l: Int, t: Int, r: Int, b: Int): UiGlobalSelector {
mFilters.add(BoundsFilter(Rect(l, t, r, b), BoundsFilter.TYPE_CONTAINS))
mSelector.add(BoundsFilter(Rect(l, t, r, b), BoundsFilter.TYPE_CONTAINS))
return this
}
fun drawingOrder(order: Int): UiGlobalSelector {
mFilters.add(object : DfsFilter() {
override fun isIncluded(nodeInfo: UiObject): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && nodeInfo.drawingOrder == order
mSelector.add(object : Filter {
override fun filter(node: UiObject): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && node.drawingOrder == order
}
override fun toString(): String {
@@ -186,222 +170,222 @@ open class UiGlobalSelector {
//// 第二类筛选条件 -able
fun checkable(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.CHECKABLE, b])
mSelector.add(BooleanFilter[BooleanFilter.CHECKABLE, b])
return this
}
fun checked(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.CHECKED, b])
mSelector.add(BooleanFilter[BooleanFilter.CHECKED, b])
return this
}
fun focusable(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.FOCUSABLE, b])
mSelector.add(BooleanFilter[BooleanFilter.FOCUSABLE, b])
return this
}
fun focused(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.FOCUSED, b])
mSelector.add(BooleanFilter[BooleanFilter.FOCUSED, b])
return this
}
fun visibleToUser(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.VISIBLE_TO_USER, b])
mSelector.add(BooleanFilter[BooleanFilter.VISIBLE_TO_USER, b])
return this
}
fun accessibilityFocused(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.ACCESSIBILITY_FOCUSED, b])
mSelector.add(BooleanFilter[BooleanFilter.ACCESSIBILITY_FOCUSED, b])
return this
}
fun selected(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.SELECTED, b])
mSelector.add(BooleanFilter[BooleanFilter.SELECTED, b])
return this
}
fun clickable(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.CLICKABLE, b])
mSelector.add(BooleanFilter[BooleanFilter.CLICKABLE, b])
return this
}
fun longClickable(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.LONG_CLICKABLE, b])
mSelector.add(BooleanFilter[BooleanFilter.LONG_CLICKABLE, b])
return this
}
fun enabled(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.ENABLED, b])
mSelector.add(BooleanFilter[BooleanFilter.ENABLED, b])
return this
}
fun password(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.PASSWORD, b])
mSelector.add(BooleanFilter[BooleanFilter.PASSWORD, b])
return this
}
fun scrollable(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.SCROLLABLE, b])
mSelector.add(BooleanFilter[BooleanFilter.SCROLLABLE, b])
return this
}
fun editable(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.EDITABLE, b])
mSelector.add(BooleanFilter[BooleanFilter.EDITABLE, b])
return this
}
fun contentInvalid(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.CONTENT_INVALID, b])
mSelector.add(BooleanFilter[BooleanFilter.CONTENT_INVALID, b])
return this
}
fun contextClickable(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.CONTEXT_CLICKABLE, b])
mSelector.add(BooleanFilter[BooleanFilter.CONTEXT_CLICKABLE, b])
return this
}
fun multiLine(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.MULTI_LINE, b])
mSelector.add(BooleanFilter[BooleanFilter.MULTI_LINE, b])
return this
}
fun dismissable(b: Boolean): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.DISMISSABLE, b])
mSelector.add(BooleanFilter[BooleanFilter.DISMISSABLE, b])
return this
}
fun checkable(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.CHECKABLE, true])
mSelector.add(BooleanFilter[BooleanFilter.CHECKABLE, true])
return this
}
fun checked(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.CHECKED, true])
mSelector.add(BooleanFilter[BooleanFilter.CHECKED, true])
return this
}
fun focusable(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.FOCUSABLE, true])
mSelector.add(BooleanFilter[BooleanFilter.FOCUSABLE, true])
return this
}
fun focused(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.FOCUSED, true])
mSelector.add(BooleanFilter[BooleanFilter.FOCUSED, true])
return this
}
fun visibleToUser(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.VISIBLE_TO_USER, true])
mSelector.add(BooleanFilter[BooleanFilter.VISIBLE_TO_USER, true])
return this
}
fun accessibilityFocused(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.ACCESSIBILITY_FOCUSED, true])
mSelector.add(BooleanFilter[BooleanFilter.ACCESSIBILITY_FOCUSED, true])
return this
}
fun selected(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.SELECTED, true])
mSelector.add(BooleanFilter[BooleanFilter.SELECTED, true])
return this
}
fun clickable(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.CLICKABLE, true])
mSelector.add(BooleanFilter[BooleanFilter.CLICKABLE, true])
return this
}
fun longClickable(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.LONG_CLICKABLE, true])
mSelector.add(BooleanFilter[BooleanFilter.LONG_CLICKABLE, true])
return this
}
fun enabled(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.ENABLED, true])
mSelector.add(BooleanFilter[BooleanFilter.ENABLED, true])
return this
}
fun password(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.PASSWORD, true])
mSelector.add(BooleanFilter[BooleanFilter.PASSWORD, true])
return this
}
fun scrollable(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.SCROLLABLE, true])
mSelector.add(BooleanFilter[BooleanFilter.SCROLLABLE, true])
return this
}
fun editable(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.EDITABLE, true])
mSelector.add(BooleanFilter[BooleanFilter.EDITABLE, true])
return this
}
fun contentInvalid(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.CONTENT_INVALID, true])
mSelector.add(BooleanFilter[BooleanFilter.CONTENT_INVALID, true])
return this
}
fun contextClickable(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.CONTEXT_CLICKABLE, true])
mSelector.add(BooleanFilter[BooleanFilter.CONTEXT_CLICKABLE, true])
return this
}
fun multiLine(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.MULTI_LINE, true])
mSelector.add(BooleanFilter[BooleanFilter.MULTI_LINE, true])
return this
}
fun dismissable(): UiGlobalSelector {
mFilters.add(BooleanFilter[BooleanFilter.DISMISSABLE, true])
mSelector.add(BooleanFilter[BooleanFilter.DISMISSABLE, true])
return this
}
//第三类 int
fun depth(d: Int): UiGlobalSelector {
mFilters.add(IntFilter(IntFilter.DEPTH, d))
mSelector.add(IntFilter(IntFilter.DEPTH, d))
return this
}
fun row(d: Int): UiGlobalSelector {
mFilters.add(IntFilter(IntFilter.ROW, d))
mSelector.add(IntFilter(IntFilter.ROW, d))
return this
}
fun rowCount(d: Int): UiGlobalSelector {
mFilters.add(IntFilter(IntFilter.ROW_COUNT, d))
mSelector.add(IntFilter(IntFilter.ROW_COUNT, d))
return this
}
fun rowSpan(d: Int): UiGlobalSelector {
mFilters.add(IntFilter(IntFilter.ROW_SPAN, d))
mSelector.add(IntFilter(IntFilter.ROW_SPAN, d))
return this
}
fun column(d: Int): UiGlobalSelector {
mFilters.add(IntFilter(IntFilter.COLUMN, d))
mSelector.add(IntFilter(IntFilter.COLUMN, d))
return this
}
fun columnCount(d: Int): UiGlobalSelector {
mFilters.add(IntFilter(IntFilter.COLUMN_COUNT, d))
mSelector.add(IntFilter(IntFilter.COLUMN_COUNT, d))
return this
}
fun columnSpan(d: Int): UiGlobalSelector {
mFilters.add(IntFilter(IntFilter.COLUMN_SPAN, d))
mSelector.add(IntFilter(IntFilter.COLUMN_SPAN, d))
return this
}
fun indexInParent(index: Int): UiGlobalSelector {
mFilters.add(IntFilter(IntFilter.INDEX_IN_PARENT, index))
mSelector.add(IntFilter(IntFilter.INDEX_IN_PARENT, index))
return this
}
fun filter(filter: BooleanFilter.BooleanSupplier): UiGlobalSelector {
mFilters.add(object : DfsFilter() {
override fun isIncluded(nodeInfo: UiObject): Boolean {
return filter[nodeInfo]
mSelector.add(object : Filter {
override fun filter(node: UiObject): Boolean {
return filter[node]
}
})
return this
@@ -416,14 +400,7 @@ open class UiGlobalSelector {
}
fun findAndReturnList(node: UiObject, max: Int = Int.MAX_VALUE): List<UiObject> {
var list: List<UiObject> = listOf(node)
for (filter in mFilters) {
val oldMax = filter.maxCount
filter.maxCount = max
list = filter.filter(list)
filter.maxCount = oldMax
}
return list
return DFS(mSelector, max).search(node)
}
fun findOneOf(node: UiObject): UiObject? {
@@ -433,19 +410,12 @@ open class UiGlobalSelector {
} else collection[0]
}
fun addFilter(filter: ListFilter): UiGlobalSelector {
mFilters.add(filter)
fun addFilter(filter: Filter): UiGlobalSelector {
mSelector.add(filter)
return this
}
override fun toString(): String {
val str = StringBuilder()
for (filter in mFilters) {
str.append(filter.toString()).append(".")
}
if (str.isNotEmpty()) {
str.deleteCharAt(str.length - 1)
}
return str.toString()
return mSelector.toString()
}
}

View File

@@ -0,0 +1,10 @@
package com.stardust.automator.filter
import com.stardust.automator.UiObject
class BFS(val filter: Filter, val limit: Int = Int.MAX_VALUE) {
fun search(node: UiObject) {
}
}

View File

@@ -11,7 +11,7 @@ import java.util.HashMap
* Created by Stardust on 2017/3/9.
*/
class BooleanFilter(private val mBooleanSupplier: BooleanSupplier, private val mExceptedValue: Boolean) : DfsFilter() {
class BooleanFilter(private val mBooleanSupplier: BooleanSupplier, private val mExceptedValue: Boolean) : Filter {
interface BooleanSupplier {
@@ -19,8 +19,8 @@ class BooleanFilter(private val mBooleanSupplier: BooleanSupplier, private val m
}
override fun isIncluded(nodeInfo: UiObject): Boolean {
return mBooleanSupplier[nodeInfo] == mExceptedValue
override fun filter(node: UiObject): Boolean {
return mBooleanSupplier[node] == mExceptedValue
}
override fun toString(): String {

View File

@@ -11,13 +11,13 @@ import java.util.Locale
* Created by Stardust on 2017/3/9.
*/
class BoundsFilter(private val mBounds: Rect, private val mType: Int) : DfsFilter() {
class BoundsFilter(private val mBounds: Rect, private val mType: Int) : Filter {
override fun isIncluded(nodeInfo: UiObject): Boolean {
override fun filter(node: UiObject): Boolean {
if (mType == TYPE_CONTAINS) {
return AccessibilityNodeInfoHelper.getBoundsInScreen(nodeInfo).contains(mBounds)
return AccessibilityNodeInfoHelper.getBoundsInScreen(node).contains(mBounds)
}
val boundsInScreen = AccessibilityNodeInfoHelper.getBoundsInScreen(nodeInfo)
val boundsInScreen = AccessibilityNodeInfoHelper.getBoundsInScreen(node)
return if (mType == TYPE_EQUALS) boundsInScreen == mBounds else mBounds.contains(boundsInScreen)
}

View File

@@ -6,7 +6,7 @@ import com.stardust.automator.UiObject
* Created by Stardust on 2017/3/9.
*/
object ClassNameFilter {
object ClassNameFilters {
private val CLASS_NAME_GETTER = object : KeyGetter {
override fun getKey(nodeInfo: UiObject): String? {
@@ -19,27 +19,27 @@ object ClassNameFilter {
}
}
fun equals(text: String): ListFilter {
var text = text
if (!text.contains(".")) {
text = "android.widget.$text"
fun equals(text: String): Filter {
var className = text
if (!className.contains(".")) {
className = "android.widget.$className"
}
return StringEqualsFilter(text, CLASS_NAME_GETTER)
return StringEqualsFilter(className, CLASS_NAME_GETTER)
}
fun contains(str: String): ListFilter {
fun contains(str: String): Filter {
return StringContainsFilter(str, CLASS_NAME_GETTER)
}
fun startsWith(prefix: String): ListFilter {
fun startsWith(prefix: String): Filter {
return StringStartsWithFilter(prefix, CLASS_NAME_GETTER)
}
fun endsWith(suffix: String): ListFilter {
fun endsWith(suffix: String): Filter {
return StringEndsWithFilter(suffix, CLASS_NAME_GETTER)
}
fun matches(regex: String): ListFilter {
fun matches(regex: String): Filter {
return StringMatchesFilter(regex, CLASS_NAME_GETTER)
}
}

View File

@@ -0,0 +1,42 @@
package com.stardust.automator.filter
import com.stardust.automator.UiObject
import java.util.ArrayList
/**
* Created by Stardust on 2017/3/9.
*/
class DFS(val filter: Filter, val limit: Int = Int.MAX_VALUE) {
fun search(node: UiObject): List<UiObject> {
val list = ArrayList<UiObject>()
if (filter.filter(node)) {
list.add(node)
if (list.size >= limit) {
return list
}
}
filterChildren(node, list)
return list
}
private fun filterChildren(parent: UiObject, list: MutableList<UiObject>) {
for (i in 0 until parent.childCount) {
val child = parent.child(i) ?: continue
val included = filter.filter(child)
if (included) {
list.add(child)
if (list.size >= limit) {
break
}
}
filterChildren(child, list)
if (!included) {
child.recycle()
}
}
}
}

View File

@@ -6,7 +6,7 @@ import com.stardust.automator.UiObject
* Created by Stardust on 2017/3/9.
*/
object DescFilter {
object DescFilters {
private val DESC_GETTER = object : KeyGetter {
override fun getKey(nodeInfo: UiObject): String? {
@@ -19,23 +19,23 @@ object DescFilter {
}
}
fun equals(text: String): ListFilter {
fun equals(text: String): Filter {
return StringEqualsFilter(text, DESC_GETTER)
}
fun contains(str: String): ListFilter {
fun contains(str: String): Filter {
return StringContainsFilter(str, DESC_GETTER)
}
fun startsWith(prefix: String): ListFilter {
fun startsWith(prefix: String): Filter {
return StringStartsWithFilter(prefix, DESC_GETTER)
}
fun endsWith(suffix: String): ListFilter {
fun endsWith(suffix: String): Filter {
return StringEndsWithFilter(suffix, DESC_GETTER)
}
fun matches(regex: String): ListFilter {
fun matches(regex: String): Filter {
return StringMatchesFilter(regex, DESC_GETTER)
}
}

View File

@@ -1,57 +0,0 @@
package com.stardust.automator.filter
import com.stardust.automator.UiObject
import java.util.ArrayList
/**
* Created by Stardust on 2017/3/9.
*/
abstract class DfsFilter : ListFilter(), Filter {
override fun filter(nodes: List<UiObject>): List<UiObject> {
val list = ArrayList<UiObject>()
for (node in nodes) {
if (isIncluded(node)) {
list.add(node)
}
if (list.size >= maxCount) {
break
}
filterChildren(node, list)
}
return list
}
override fun filter(node: UiObject): List<UiObject> {
val list = ArrayList<UiObject>()
if (isIncluded(node)) {
list.add(node)
}
if (list.size >= maxCount) {
return list
}
filterChildren(node, list)
return list
}
private fun filterChildren(parent: UiObject, list: MutableList<UiObject>) {
for (i in 0 until parent.childCount) {
val child = parent.child(i) ?: continue
val included = isIncluded(child)
if (included) {
list.add(child)
}
if (list.size >= maxCount) {
break
}
filterChildren(child, list)
if (!included) {
child.recycle()
}
}
}
protected abstract fun isIncluded(nodeInfo: UiObject): Boolean
}

View File

@@ -8,6 +8,6 @@ import com.stardust.automator.UiObject
interface Filter {
fun filter(node: UiObject): List<UiObject>
fun filter(node: UiObject): Boolean
}

View File

@@ -19,10 +19,6 @@ object IdFilter {
}
}
interface FullIdGetter {
fun getFullId(id: String): String
}
fun equals(id: String): StringEqualsFilter {
return StringEqualsFilter(id, ID_GETTER)
}
@@ -31,7 +27,7 @@ object IdFilter {
return StringStartsWithFilter(prefix, ID_GETTER)
}
fun endsWith(suffix: String): ListFilter {
fun endsWith(suffix: String): StringEndsWithFilter {
return StringEndsWithFilter(suffix, ID_GETTER)
}

View File

@@ -6,14 +6,14 @@ import com.stardust.automator.UiObject
* Created by Stardust on 2017/11/5.
*/
class IntFilter(private val mIntProperty: IntProperty, private val mValue: Int) : DfsFilter() {
class IntFilter(private val mIntProperty: IntProperty, private val mValue: Int) : Filter {
interface IntProperty {
operator fun get(`object`: UiObject): Int
}
override fun isIncluded(nodeInfo: UiObject): Boolean {
return mIntProperty[nodeInfo] == mValue
override fun filter(node: UiObject): Boolean {
return mIntProperty[node] == mValue
}
override fun toString(): String {

View File

@@ -1,31 +0,0 @@
package com.stardust.automator.filter
import com.stardust.automator.UiObject
import java.util.ArrayList
/**
* Created by Stardust on 2017/3/9.
*/
abstract class ListFilter {
var maxCount: Int = Integer.MAX_VALUE
abstract fun filter(nodes: List<UiObject>): List<UiObject>
abstract class Default : Filter, ListFilter() {
override fun filter(nodes: List<UiObject>): List<UiObject> {
val list = ArrayList<UiObject>()
for (node in nodes) {
list.addAll(filter(node))
if (list.size >= maxCount) {
break
}
}
return list
}
}
}

View File

@@ -19,23 +19,23 @@ object PackageNameFilter {
}
}
fun equals(text: String): ListFilter {
fun equals(text: String): Filter {
return StringEqualsFilter(text, PACKAGE_NAME_GETTER)
}
fun contains(str: String): ListFilter {
fun contains(str: String): Filter {
return StringContainsFilter(str, PACKAGE_NAME_GETTER)
}
fun startsWith(prefix: String): ListFilter {
fun startsWith(prefix: String): Filter {
return StringStartsWithFilter(prefix, PACKAGE_NAME_GETTER)
}
fun endsWith(suffix: String): ListFilter {
fun endsWith(suffix: String): Filter {
return StringEndsWithFilter(suffix, PACKAGE_NAME_GETTER)
}
fun matches(regex: String): ListFilter {
fun matches(regex: String): Filter {
return StringMatchesFilter(regex, PACKAGE_NAME_GETTER)
}
}

View File

@@ -0,0 +1,33 @@
package com.stardust.automator.filter
import com.stardust.automator.UiObject
import java.util.*
class Selector : Filter {
private val mFilters = LinkedList<Filter>()
override fun filter(node: UiObject): Boolean {
for (filter in mFilters) {
if (!filter.filter(node)) {
return false
}
}
return true
}
fun add(filter: Filter) {
mFilters.add(filter)
}
override fun toString(): String {
val str = StringBuilder()
for (filter in mFilters) {
str.append(filter.toString()).append(".")
}
if (str.isNotEmpty()) {
str.deleteCharAt(str.length - 1)
}
return str.toString()
}
}

View File

@@ -6,10 +6,10 @@ import com.stardust.automator.UiObject
* Created by Stardust on 2017/3/9.
*/
class StringContainsFilter internal constructor(private val mContains: String, private val mKeyGetter: KeyGetter) : DfsFilter() {
class StringContainsFilter internal constructor(private val mContains: String, private val mKeyGetter: KeyGetter) : Filter {
override fun isIncluded(nodeInfo: UiObject): Boolean {
val key = mKeyGetter.getKey(nodeInfo)
override fun filter(node: UiObject): Boolean {
val key = mKeyGetter.getKey(node)
return key != null && key.contains(mContains)
}

View File

@@ -6,10 +6,10 @@ import com.stardust.automator.UiObject
* Created by Stardust on 2017/3/9.
*/
class StringEndsWithFilter(private val mSuffix: String, private val mKeyGetter: KeyGetter) : DfsFilter() {
class StringEndsWithFilter(private val mSuffix: String, private val mKeyGetter: KeyGetter) : Filter {
override fun isIncluded(nodeInfo: UiObject): Boolean {
val key = mKeyGetter.getKey(nodeInfo)
override fun filter(node: UiObject): Boolean {
val key = mKeyGetter.getKey(node)
return key != null && key.endsWith(mSuffix)
}

View File

@@ -6,10 +6,10 @@ import com.stardust.automator.UiObject
* Created by Stardust on 2017/3/9.
*/
class StringEqualsFilter(private val mValue: String, private val mKeyGetter: KeyGetter) : DfsFilter() {
class StringEqualsFilter(private val mValue: String, private val mKeyGetter: KeyGetter) : Filter {
override fun isIncluded(nodeInfo: UiObject): Boolean {
val key = mKeyGetter.getKey(nodeInfo)
override fun filter(node: UiObject): Boolean {
val key = mKeyGetter.getKey(node)
return if (key != null) {
key == mValue
} else false

View File

@@ -6,10 +6,10 @@ import com.stardust.automator.UiObject
* Created by Stardust on 2017/3/9.
*/
class StringMatchesFilter internal constructor(private val mRegex: String, private val mKeyGetter: KeyGetter) : DfsFilter() {
class StringMatchesFilter internal constructor(private val mRegex: String, private val mKeyGetter: KeyGetter) : Filter {
override fun isIncluded(nodeInfo: UiObject): Boolean {
val key = mKeyGetter.getKey(nodeInfo)
override fun filter(node: UiObject): Boolean {
val key = mKeyGetter.getKey(node)
return key != null && key.matches(mRegex.toRegex())
}

View File

@@ -6,10 +6,10 @@ import com.stardust.automator.UiObject
* Created by Stardust on 2017/3/9.
*/
class StringStartsWithFilter(private val mPrefix: String, private val mKeyGetter: KeyGetter) : DfsFilter() {
class StringStartsWithFilter(private val mPrefix: String, private val mKeyGetter: KeyGetter) : Filter {
override fun isIncluded(nodeInfo: UiObject): Boolean {
val key = mKeyGetter.getKey(nodeInfo)
override fun filter(node: UiObject): Boolean {
val key = mKeyGetter.getKey(node)
return key != null && key.startsWith(mPrefix)
}

View File

@@ -1,48 +0,0 @@
package com.stardust.automator.filter
import com.stardust.automator.UiObject
/**
* Created by Stardust on 2017/3/9.
*/
class TextFilter private constructor(private val mText: String) : ListFilter.Default() {
override fun filter(node: UiObject): List<UiObject> {
return node.findByText(mText)
}
companion object {
private val TEXT_GETTER = object : KeyGetter {
override fun getKey(nodeInfo: UiObject): String? {
val charSequence = nodeInfo.text
return charSequence?.toString()
}
override fun toString(): String {
return "text"
}
}
fun equals(text: String): ListFilter {
return StringEqualsFilter(text, TEXT_GETTER)
}
fun contains(str: String): ListFilter {
return StringContainsFilter(str, TEXT_GETTER)
}
fun startsWith(prefix: String): ListFilter {
return StringStartsWithFilter(prefix, TEXT_GETTER)
}
fun endsWith(suffix: String): ListFilter {
return StringEndsWithFilter(suffix, TEXT_GETTER)
}
fun matches(regex: String): ListFilter {
return StringMatchesFilter(regex, TEXT_GETTER)
}
}
}

View File

@@ -0,0 +1,41 @@
package com.stardust.automator.filter
import com.stardust.automator.UiObject
/**
* Created by Stardust on 2017/3/9.
*/
object TextFilters {
private val TEXT_GETTER = object : KeyGetter {
override fun getKey(nodeInfo: UiObject): String? {
val charSequence = nodeInfo.text
return charSequence?.toString()
}
override fun toString(): String {
return "text"
}
}
fun equals(text: String): Filter {
return StringEqualsFilter(text, TEXT_GETTER)
}
fun contains(str: String): Filter {
return StringContainsFilter(str, TEXT_GETTER)
}
fun startsWith(prefix: String): Filter {
return StringStartsWithFilter(prefix, TEXT_GETTER)
}
fun endsWith(suffix: String): Filter {
return StringEndsWithFilter(suffix, TEXT_GETTER)
}
fun matches(regex: String): Filter {
return StringMatchesFilter(regex, TEXT_GETTER)
}
}

View File

@@ -13,11 +13,11 @@ import org.junit.Assert.*
*/
class DfsFilterTest {
private class RandomDfsFilter : DfsFilter() {
private class RandomFilter : Filter {
private val mRandom = Random()
override fun isIncluded(nodeInfo: UiObject): Boolean {
override fun filter(node: UiObject): Boolean {
return mRandom.nextBoolean()
}
}
@@ -25,9 +25,9 @@ class DfsFilterTest {
@Test
@Throws(Exception::class)
fun filter() {
val filter = RandomDfsFilter()
val filter = RandomFilter()
val root = TestUiObject(10)
val list = filter.filter(root)
val list = DFS(filter).search(root)
for (uiObject in list) {
if (root !== uiObject)
uiObject.recycle()