查找节点

FindNode 是核心 Selector 包之上的一个 shell 程序,FindNode 的目的是找到预期的一个或多个 UI 元素(或辅助功能节点)并提取信息或对其执行操作。简而言之,AAI 用查询替换坐标。

FindNode 是 AAI 项目(Accessibility and Automation Integration)的一部分,它由以下组件组成:

  1. 查询语言,简单的一行语法语言来搜索预期的节点。
  2. 每个设备上的 FindNode(和 Selector 核心)。
  3. 一对多同步中的对象模式,将节点(或UI对象)的查询发送给所有设备而不是协调,点击"确定"可以在所有不同分辨率的设备上运行,而不是点击(100,100)。
  4. UI Explorer获取节点信息和帮助程序构建查询,非常适合学习查询语言学习。
  5. AAIS,一种在多个设备上执行自动化的简单语言。
  6. 作为现有 REST 和 JS API 的一部分(例如 devices.click("OK"))。
  7. FindNode + JavaScript 以及“设备”和“设备”构造可以创建自动化脚本。

Selector 位于 Accessibility 和 UI Automator 之上,每个 UI 元素或 UI 元素的容器由节点或节点集标识,每个节点都有其在当前屏幕上唯一的 ID。 Selector 提供了多种方式来搜索一个或多个节点。 一旦获取了节点,就可以完成许多有趣的事情,例如获取文本/图像或执行诸如单击按钮或在文本字段中输入文本等操作,所有这些都无需坐标。 这允许一个自动化脚本在不同的分辨率和大小上运行,而使用坐标是不可能的。 节点的坐标是在运行时获取的。

例如:

JS API:devices.click(“OK”) 而不是 devices.click(100, 200)。找到文本为"OK"的节点,获取坐标并单击它。全部在运行时完成。

MDCC:用户在主设备上点击一个按钮,按钮的唯一查询(例如"T:OK”)将被发送到其他设备,以搜索节点并点击。目的是提供多种不使用屏幕坐标定位节点的方法,没有页面上/下,而是"scrollToView"来定位节点,这样,同一个脚本可以在不同分辨率和屏幕尺寸的不同手机上运行。

FindNode 使用 Appium 的 JSON 结构来执行 JSON 命令,JSON 格式为:

{"cmd": "action", "action": "findnode", "params": {…}}

每个 FindNode 命令都在"params"对象中执行。

返回值可以有两种类型之一,成功运行:

{"status":0, "value":"{…}"}

或失败:

{"status":13,"value":"{<error message>}"}

TC提供“device.sendAai()”或“devices.sendAai()”与FindNode通信:

  • 发送"params"对象。
  • 如果 FindNode 在特定时间段内未返回,则会产生超时错误。
  • 对于某些需要超过默认超时时间的命令,FindNode 会延长时间以避免超时错误。
  • 自动处理返回值(错误时为空,值时为非空,lastError() 获取错误消息)。
  • 对于多个设备"devices”,将创建线程,将相同的命令发送到设备线程。

大概你可以通过使用“device.sendAai()”和“devices.sendAai()”来完成大部分任务,FindNode是Appium的“处理程序”,另一个处理程序“invoke”可以使用Java反射来访问UiDevice,UiObject2中的方法 , AccessibilityNodeInfo 和 InteractionController。

限制

AAI 有一些限制:

  • 仅支持纵向模式,横向模式将在未来推出。
  • 此时并不是所有的都能够识别,取决于app的设计,可能会漏掉一些节点。
  • 不支持水平滚动或弹出窗口(将获得更少的节点或更多的节点)。
  • 不支持多线程,每次执行都要互斥:MDCC(对象模式)、终端(或脚本)、UI Explorer和部分REST API使用FindNode。

查询+动作

使用我们的查询语言在屏幕上查找节点并对找到的节点应用操作。 我们将在本手册中介绍查询和操作。

例如,要在关于手机屏幕中获取 Android 版本:

>> device.sendAai({query:"TP:basic&&T:Android version&&OY:1", action:"getText"})
{retval: '13'}
  • TP:basic 模板
  • T:Android版本 基础查询
  • OY:1 扩展查询

要单击带有“确定”标签的按钮:

{query:"T:OK", action:"click"}

获取所有股票代码:

{query:"R:.ticker", action:"getText"}


查询

大多数 FindNode 用法与查询有关,这是 FindNode 的核心部分(也是 AAI 的一部分)。它允许用户通过辅助功能节点上不同类型的有用查询来定位预期的 UI 元素。整个屏幕由很多节点组成,一个节点可以是最小的UI元素,也可以是更小节点的容器,很多是不可见的。整个屏幕是从单个根节点开始的树结构。将执行查询以获取符合条件的节点,目的是将大量节点减少到一个或几个预期节点,用户可以获取信息或对节点进行操作。

我们开发了一种小型查询语言,它可以在设备和脚本之间移植,并且功能强大到足以定位大多数节点。格式为 JSON:

{query:"<key>:<value>&&<key>:<value>&&<key>&&<key>…"}

我们接受“||” 和“&&”作为相同的分隔符,以后可以更改。 由于查询是AND关系,查询需要满足所有条件才能成为“匹配节点”,使用“&&”更符合逻辑。 FindNode 不提供 OR 关系,但底层的 Selector 包提供了该功能。 “模板”使用该构造。 对于某些键,不需要值。

查询字符串分为 3 个部分:模板、基本查询和扩展查询。 “模板”(TP)将生成初始节点,“基本查询”(BQ)将一次从节点属性执行查询,“扩展查询”(EQ)将从 TP 和 BQ 获取输出并执行跨多个节点的查询 .

获得主存储剩余空间

可以使用"query”+“action”:

>> device.sendAai({query:"T:Main storage&&OX:1", action:"getText"})
{retval: '402 GB free'}

可以使用"可选查询”(OQ):

>> device.sendAai({action:"getText(T:Main storage&&OX:1)"})
{retval: '402 GB free'}

同样,要返回上一屏幕(向左箭头),可以使用 OQ:

>> device.sendAai({action:"click(T:Storage Analysis&&OX:-1)"})
{retval: true}

要获取音频大小,可以混合不同的查询并仍然获得相同的结果:

>> device.sendAai({query:"TP:all&&T:Main storage&&VG:2&&XT:Audio&&OX:1", action:"getText"})
{retval: '77 MB'}

解析器会将查询分成 TP、BQ 和 EQ,并按以下顺序执行:

  • TP: TP:all
  • BQ: T:Main storage
  • EQ: VG:2&&XT:Audio&&OX:1


模板 (TP)

模板查询(由于缺少更好的名称)是查询所必需的键,它生成用于预期目的的节点。缺少模板将默认为"TP:more”,不允许使用多个模板,这里是不同的模板:

名称 描述
all 返回所有节点。
more “全部”选项,其中删除了布局节点。 如果未定义模板,则这是默认值。
basic 子节点数为零(叶节点)的“更多”选项。
reduced 返回“有趣”的节点(参见 UI Explorer“优化”模式)。
findText,<text> 返回带有特定文本或描述的节点。 findText 需要一个额外的参数来指定要搜索的文本。
anyText[,min][,max] 返回包含文本内容的节点,可选择特定长度。
anyDescription[,min][,max] 返回具有描述内容的节点,可选地具有一定的长度。
textInput 返回从左上角到右下角排序的“可编辑”节点。 与“IX”一起用于多个文本输入。
scrollable,[pos] 返回可滚动节点,对于屏幕有多个可滚动区域,可以通过“位置”来标识匹配哪个可滚动区域。
line,<top|bottom>,<line> 返回可滚动节点顶部或底部的节点。


基本查询 (BQ)

基本查询是纯粹基于单个节点信息的查询。BQ不是必需的,如果没有定义BQ,它会将模板中的所有节点传递给EQ。

以下是匹配从节点本身获取的信息的基本查询:

名称 描述
P:<package name> 包名(S)
C:<class name> 类名(S)
R:<resource ID> 资源编号(S)
D:<text> 内容描述(S)
T:<text> 正文(S)
IT:<number> 文本输入类型(一)
CC:<number> 孩子数(一)
ID:<hex string> 节点 ID (S)
BI:[x, y] 返回节点包含(x,y)
BI:[x1, y1, x2, y2] 返回由矩形包围的节点,x或-1的y将被忽略
BP:<prop name> 返回匹配布尔属性的节点
TD:<text> 匹配文字或描述

由于查询始终在现有屏幕上执行,因此不应使用“P”。 “(S)”代表字符串,“I”整数,“[x, y]”是一个位置,“[x1, y1, x2, y2]”是一个矩形(或边界)。 I/S 可以接受整数和字符串。 字符串更强大。

键不区分大小写,我们使用大写来区分键和值。



扩展查询 (EQ)

下面是扩展查询的key,一般作用于BQ或者template返回的多个节点。扩展查询从左到右工作,同一个键可以多次应用。情商是可选的。以下是 EQ 的按键:

姓名 描述
BQ:<basic query> 对BQ中的多个键执行基本查询。
IX:<number> 根据位置从匹配节点列表中返回一个节点。
OX:<number> 水平偏移到相邻节点(正 - 右,负 - 左)。
OY:<number> 垂直相邻节点的偏移量(正 - 下,负 - 上)。
ON:<type> 从匹配节点列表中选择一个节点的不同方法。
RN 从匹配节点列表中返回优化的节点。
ST:<sort type> 根据节点在屏幕上的位置返回排序的节点。
TX 返回与参考节点水平相交的节点。
TY 返回与参考节点垂直相交的节点。
VG:[level number] 返回同一视图组中的节点。
"X"<BQ key>:<value> 对BQ中的特定键执行基本查询。

所有这些查询都是可选的,如果您在屏幕上进行查询,您可以选择任何字段。 “(S)”代表字符串,“I”代表整数,“[x, y]”数组。 I/S 可以接受整数和字符串。 字符串更强大。

键不区分大小写,我们使用大写来区分键和值。



Matching List (ML)

查询完成后,将匹配到的节点存入匹配列表(Matching List,ML)中,ML中的节点由节点ID标识。 许多动作使用 ML 中的节点,一些动作可以改变 ML。 “save”和“load”以及较新的“push”和“pop”可用于保存和检索已保存的 ML。 使用 action:"getIds" 获取 ML ID。 TX/TY/VG 取一个节点生成多个节点。

另一种获取 ML 的方法是使用"元素"数组。



“elements” property

“elements”接受一个节点ID数组,节点ID必须是屏幕上有效的节点ID,否则会产生错误。 当指定“elements”时,所有元素将存储在ML中,不进行搜索。 按特定顺序对节点应用操作很有用。 每个带有“元素”的查询都会遍历所有节点来定位对应的节点,尝试将多个 ID 放入数组中,而不是一次一个。 元素上可以出现相同的节点 ID。 只有“elements”可以直接组成“ML”,与“forEach”结合起来很有用。

例子:

device.sendAai({elements:["1234a", "5678b"], action:"getNodes"})

这查询数字按钮和构建元素数组并使用“forEach”点击所有按钮并打印结果。

>> ids = device.sendAai({query:"T:/^[1-9]$/"}).ids
a71e,aadf,aea0,b622,b9e3,bda4,c526,c8e7,cca8
>> device.sendAai({elements:ids, action:"forEach(nsClick);getText(+R:.editText_result)"})
{count: 2, list: [{retval: [{retval: true},{retval: true},{retval: true},{retval: true},{retval: true},{retval: true},{retval: true},{retval: true},{retval: true}]},{retval: '789456123'}]}

要检索一个节点,可以使用“ID:”。

device.sendAai({query:"ID:1234a", action:"getNodes(all)"})


Testing

通过查询查找节点不是很困难,您需要的是"UI Explorer"和终端:

  • 节点 ID 很重要,它通常对应用程序是唯一的。
  • 终端:键入:device.sendAai({query:"…"}) 以查找它是否与节点匹配。搜索选项有很多,查找节点的方法不止一种。
  • 用户界面资源管理器:
    • 可以使用查询助手构建查询。
    • 体验不同查询,查看匹配节点。
    • 无限制的撤消/重做以比较不同的查询。
    • 查找节点内的属性。
    • UI Explorer 中的“代码”是定位节点的最大努力,请参阅操作:“getUniqQuery”了解更多信息。
  • 终端:当有疑问时,使用 UI Explorer 查找节点 ID,使用 device.sendAai({elements:["<ID>","<ID>"…], action:"getNodes"}),这将帮助您获取更多信息。
  • 从一个设备开始使用"device.sendAai"到多​​个设备通过"devices.sendAai”,您可以使用Device.searchObject()搜索设备来定位您需要的设备(例如所有设备,一组设备,特定设备)。

详细模板

模板(TP)为必填字段,TP根据值返回列表节点,是BQ、EQ或动作的初始节点列表。TP 是 Selector 的子类,将来可能允许用户定义新的模板。TP中的几个规则:

  • 每个查询一个 TP,一个查询中两个 TP 将导致错误。
  • 如果未定义 TP,将使用默认模板。默认为"TP:more”,可以使用setConfig(selector:defaultTemplate, <template>) 来更改默认模板。默认模板不能包含具有必需参数的模板 (例如 TP:findText)。
  • 如果 TP 返回 null(findText 找不到没有任何文本输入字段的文本或 textInput),它将产生错误。
  • TP 如果包含参数,使用逗号分隔参数。TP:<模板>,<参数>。
  • TP 生成的节点将保存到 ML 中。


TP:all,more,basic,reduced

这些模板遍历整个屏幕中的节点:

  • all 返回所有节点,包括根节点。
  • more删除布局节点的更多"all"选项。如果查询中未定义 TP,则这是默认设置。
  • basic子计数为零的基本"more"选项。
  • reduced使用"reduceNodes"减少"更多"选项,分析屏幕上的节点并返回关键节点。

UI Explorer:3个选择:

  • 默认TP:more
  • 所有TP:all
  • 优化TP:reduced

例如。 第一个不带参数的getCount,将获取“ML”的计数,不带“query”,默认为“TP:more”,因此它与“TP:more”的计数相同。 "+TP:all" 将修改 ML,这就是为什么第三个 "getCount" 将返回 "+TP:all" 构建的 ML 中的节点数。

>> device.sendAai({actions:[ 
 "getCount", 
 "getCount(+TP:all)",
 "getCount",
 "getCount(+TP:more)",
 "getCount(+TP:basic)",
 "getCount(+TP:reduced)"
]})
{count: 6, list: [{count: 237},{count: 242},{count: 242},{count: 237},{count: 76},{count: 58}]}


TP:anyText[,min[,max]],anyDescription[,min[,max]]

此模板将返回 anyText 中的文本或 anyDescription 中具有非空内容的描述。可以选择指定找到的文本的最小和最大长度(含)。如果未指定最大值,则不会强制执行最大长度。不允许最小值/最大值为零。

例子:

>> device.sendAai({query:"TP:anyText", action:"getText"})
{retval: ['Calculator','AC','÷','×','DEL','7','8','9','-' ,'4','5','6','+','1','2','3','%','0','.','±','=']}
>> device.sendAai({query:"TP:anyText,1,1", action:"getText"})
{retval: ['÷','×','7','8','9','-','4','5','6','+','1','2','3','%','0','.','±','=']}


TP:findText,<text>

返回文本或描述中与指定文本匹配的内容的节点。文本参数是必需的,否则会产生错误。它将接受与“T:<…>”类似的参数 在“BQ”中。

例子:

>> device.sendAai({query:"TP:findText,/[0-9]/", action:"getText"})
{retval: ['7','8','9','4','5','6','1','2','3','0']}
>> device.sendAai({query:"TP:findText,/^[A-Z]{1,4}$/", action:"getText"})
{retval: ['TGT','INTC','WMT','T','AAPL','SBUX','TSLA']}
>> device.sendAai({query:"TP:findText,*T", action:"getText"})
{retval: ['TGT','WMT','T']}


TP:textInput

该模板返回"可编辑"的节点。如果没有找到节点,它将产生一个错误。或者,您可以使用"BP:editable"来获得相同的结果。

例子:

获取所有文本字段:

>>  device.sendAai({query:"TP:textInput"})
{count: 6, ids: ['e135','e8b7','f3fa','fb7c','106bf','10e41']}
>> device.sendAai({query:"BP:editable"})
{count: 6, ids: ['e135','e8b7','f3fa','fb7c','106bf','10e41']}
// Show hint text as text value, use "getHintText" to obtain hint text
>> device.sendAai({query:"TP:textInput", action:"getText"})
{retval: ['text','text','Number','Email','text password','Person Name']}

找到文本字段:

最后一个文本字段:查询:“BP:editable&&IX:-1”

基于文本域的提示:query:"TO:text Input &&:name"

使用"setText"更改内容:

>> device.sendAai({query:"BP:editable&&T:Email", action:"setText(john@noname.com)"})
{retval: true}
>> device.sendAai({query:"TP:textInput&&IX:-1", action:"setText(John)"})
{retval: true}
>> device.sendAai({query:"TP:textInput&&T:text ", action:"setText(Hello)"})
{retval: true}


TP:line,<顶部|底部>,<行号>

目前,这仅适用于具有可滚动容器的应用程序,否则 TP:line 将返回 null。FindNode 会将 UI 元素定位到可滚动节点之外,行模式会将 UI 元素分组为一系列行,每行可以有一个或多个节点。许多应用程序在屏幕的顶部和底部都有固定的 UI 元素,无法通过 BQ 或 EQ 定位特定节点。例如"TP:line,top,1&&IX:2"将返回第一行,第二个项目或"TP:line,bottom,-1&&T:Chats"将返回行的底部,Chats 作为文本。

  • 对于计算器等不可滚动的应用程序,将始终返回 null。
  • 状态行底部和可滚动节点顶部之间被认为是顶部区域,行号从1开始。负数行号以相反的顺序返回行,-1是该区域的最后一行。
  • 可滚动节点底部和导航栏顶部之间被视为底部区域。同样,行号从 1 或负行号开始。“TP:line,bottom,-1"返回该区域最后一行的节点(也可能是应用程序的最后一行)。
  • 使用越界行号将得到 null。 (例如底部2行,TP:line,bottom,3会返回null,如果不知道行数,就用负数)。
  • 如果一个节点高度与同一行上的2个垂直节点高度相似,则较低的节点将放置在下一行。
  • 许多应用程序将顶行放在可滚动区域内,TP:line,top,<number> 将返回 null。
  • 不干扰"IX”,节点旁边的小红色计数器或红点将被删除(不在返回列表中)。
  • 线模式将在未来增强以包括不可滚动区域。

例子:

>> device.sendAai({query:"TP:line,top,1"})
{count: 6, ids: ['1902b0','191936','192fbc','1cf0bc','19373e','194a03']}
To go back click the first item "IX:0":
>> device.sendAai({query:"TP:line,top,1", action:"click(IX:0)"})
{retval: true}
>>  device.sendAai({query:"TP:line,bottom,-1"})
{count: 5, ids: ['2be5b8','2c0b42','2c1a46','2c30cc','2c3fd0']}
>> device.sendAai({query:"TP:line,bottom,-1", action:"setText(BP:editable, Hello)"})
{retval: true}


TP:scrollable[,<位置>]

此模板返回由“位置”标识的可滚动屏幕上的项目(叶节点)。 “位置”是从0开始的数字,从上到下。 在可滚动节点中使用资源 ID 可以获得更好的结果。 默认位置为零。 如果在屏幕上找不到可滚动节点,将引发错误。 “TP:scrollable”返回一个可滚动节点上的叶节点,“BP:scrollable”将匹配屏幕上的所有可滚动节点。

对于可滚动的每个项目(或行/列)可能包含多个值,与资源 ID 一起使用以返回项目的特定值。 例如,查询:“TP:scrollable&&R:.tv_name”。

例子:

获取当前屏幕所有可滚动节点:

>> device.sendAai({query:"TP:scrollable"})
{count: 5, ids: ['1e434','5bbba','5bf7b','5d9c2','6f70e']}

"BP:scrollable" 返回屏幕上所有可滚动的节点:

>> device.sendAai({query:"BP:scrollable", action:"getNodes(B)"})
{count: 5, list: [{bounds: '[0,268][1440,2738]', id: '1e434'},{bounds: '[42,457][1440,1303]', id: '5bbba'},{bounds: '[42,457][1440,1303]', id: '5bf7b'},{bounds: '[0,1303][1440,1742]', id: '5d9c2'},{bounds: '[0,1958][1440,2738]', id: '6f70e'}]}

返回设置的所有标题(在三星手机中):

>> device.sendAai({actions:["openAndroidSetting(Settings)", "newQuery(TP:scrollable&&R:android:id/title)", "getText"]})
{count: 3, list: [{retval: true},{count: 10},{retval: ['My Name','Connections','Connected devices','Modes and Routines','Sounds and vibration','Notifications','Display','Wallpaper and style','Themes','Home screen']}]}

详细的基本查询

基本查询从 TP 中获取 ML 并执行节点级匹配,一次一个节点并创建一个更短的 ML,丢弃不匹配的节点。BQ 从 Accessibility 中提取数据,它包含单节点信息和动作。在查询中扩展更多功能以实现更强大的匹配(特殊字符)并添加快捷方式使查询更简洁。



查询中的特殊字符

对于 inputType 或 childCount 等数字,它可以接受数字或字符串:

  • <number>、“<number>”或“=<number>”:匹配精确的数字。
  • "> <number>":匹配大于<number>的数字。
  • "< <number>":匹配小于<number>的数字。

字符串可以是以下之一,“\n"匹配换行符:

  • “<string>”:完全匹配全文,区分大小写。
  • "(?i)<string>":匹配正则表达式中不区分大小写的匹配。
  • "ci:<string>":匹配不区分大小写的字符串。
  • "*<string>":匹配最后的字符串,如"*Group"。
  • "<string>*":匹配开头的字符串。例如"android.widget.*”。
  • “*<string>*”:匹配任何子字符串“*widget*”。
  • "/<regex>/:匹配任何正则表达式。例如"/ImageView|TextView/"

“!” 在字符串的开头用作 NOT。"!0" = 非零。“C:!/Layout/”,非布局类。

为了简单起见,{"cmd": "action","action": "findnode", "params":{…}} 的标准前缀将被省略,我们将在 "param" 对象中列出示例并使用 JavaScript 对象符号来减少双引号的数量。



简称

两个快捷方式可用于类名 (C) 和资源 ID (R),前缀为“.”用于替换常用前缀:

  • 类名 (C):前缀“.”替代"android.widget.”。例如“.Button”=“android.widget.Button”。
  • 资源 ID (R):前缀“.”替代“<package name>:id/”。例如“.text_connect”=“com.sigma_rt.totalcontrol:id/text_connect”。

这 ”。” 符号只能用于普通匹配和“!” 匹配,其他类型的匹配(例如正则表达式和通配符)需要使用完整形式。



P:<包名>, C:<类名>, R:<资源 ID>

所有节点都包含包名和类名以及可选的资源 ID。由于匹配是在运行应用程序的屏幕上进行的,所以"P"将默认为正在运行的应用程序,其他"P"没有意义。所以不需要"P”。



T:<文本>, D:<文本>, TD:<文本>

两个最重要的键,有信息的节点通常带有文本或描述,这比OCR准确和快速得多。“直到"命令可以监视文本或描述是否已更改。经常使用带有特殊字符的 T:<text>。“TD"匹配文字或描述,匹配其中任意一项即匹配成功。

device.sendAai({query:"T:OK", action:"click"})

单击确定按钮。



边界 (BI):

Bound in 采用屏幕坐标或边界,并根据以下标准返回节点。这主要用于屏幕操作,例如 UI Explorer 或调试目的。FindNode 的对象模式试图在独立于分辨率和大小的设备中查找节点,基于"BI"坐标的节点不适用于所有设备。

  • [x, y]:将返回边界包含 (x, y) 的节点。
  • [x, -1] 或 [-1,y] 其中之一为 -1:如果 x = -1,将忽略 x 并返回边界包含 y 的节点,类似地对于 y = -1,它只会匹配X。就像在 y 和 x 位置绘制水平/垂直线一样,将返回接触该线的节点。
  • [x1, y1, x2, y2]:将匹配由 (x1, y1) - (x2, y2) 指定的矩形范围内的节点边界。

例子:

{query:"BI:[-1, 1000]&&IX:-1", action:"click"}
{query:"BI:[0, 0, 1000, 1000]", action:"getBounds"}


CC:<子计数>, IT:<输入类型>

这些是在节点中找到的属性,很少使用。CC 是孩子的数量,输入类型通常是数字文本字段。



BP:<属性>

布尔属性在节点中很重要,这些属性带有 true 或 false 值。 允许多个值,“!” 属性名前面的匹配 false。 以下是属性:

  • 可检查:如果节点包含复选框或开/关开关,通常这带有"可点击”。
  • 选中:如果节点包含复选框/开关,如果选中/打开则为真,如果未选中/关闭则为假。
  • 可点击:如果节点是可点击的(通常是按钮或 UI 元素对点击的响应,例如复选框)。
  • 可编辑:如果节点允许输入或修改文本。
  • 启用:如果节点启用,禁用节点不响应操作。通常以灰色显示。
  • focusable:如果节点可以被聚焦。
  • focused:如果节点被聚焦,通常每个屏幕一个焦点,选择这个将返回 null 或 1 个节点。
  • longClickable:如果节点是可长按的。
  • progressible:如果节点是“progress”节点,比如slider。
  • selected:节点是否被选中。
  • scrollable:如果节点是可滚动节点(容器),则该节点可以水平或垂直滚动。 RecyclerView、ScrollView 和 ListView 等类是可滚动的。

允许多个布尔属性,用逗号分隔,因为逗号对解析器有意义,用单引号/双引号括起来:

Usage:

{query:"BP:<prop name>, !<prop name>, ..."}

例子:

>> device.sendAai({query:"TP:textInput&&BP:clickable"})
{count: 1, ids: ['27ebc0']}
>> device.sendAai({query:"TP:textInput&&BP:'clickable,enabled'"})
{count: 1, ids: ['27ebc0']}
>> device.sendAai({query:'TP:textInput&&BP:"clickable,enabled"'})
{count: 1, ids: ['27ebc0']} 

使用“getNodes(BP)”输出所有布尔属性:

>> device.sendAai({query:"TP:textInput&&BP:'clickable,enabled'", action:"getNodes(BP)"}) 
{count: 1, list: [{booleanProperties: {checkable: false, checked: false, clickable: true, editable: true, enabled: true, focusable: true, focused: false, longClickable: true, multiLine: true, scrollable: false, selected: false, visibleToUser: true}, id: '27ebc0'}]}

从 API 14 开始,支持“不”(!),使用“!”不仅可以找到真值,还可以找到假值。

假设循环通过此以获得值直到不再(箭头显示为灰色):

>> device.sendAai({query:"R:.btn_next_period&&BP:!enabled", action:"getCount"})
{count: 1}

反过来是不正确的:

>> device.sendAai({query:"R:.btn_next_period&&BP:enabled", action:"getCount"})
null
>> lastError()

没有找到匹配

“checkable”通常是“clickable”,从列表中删除checkable:

>> device.sendAai({query:"BP:clickable", action:"getCount"})
{count: 26}
>> device.sendAai({query:"BP:checkable", action:"getCount"})
{count: 10}
>> device.sendAai({query:"BP:'clickable,!checkable'", action:"getCount"})
{count: 16}


基本查询示例

device.sendAai({}) → device.sendAai({query:"TP:more", action:"getIds"})
>> device.sendAai({})
{count: 9, ids: ['95b0','b779','bb3a','9d32','a0f3','a4b4','a875','ac36','aff7']}
>> device.sendAai({query:"TP:more", action:"getIds"})
{count: 9, ids: ['95b0','b779','bb3a','9d32','a0f3','a4b4','a875','ac36','aff7']}
>> device.sendAai({query:"IX:0", action:"getIds"})
{count: 1, ids: ['95b0']}
>> device.sendAai({query:"TP:more&&IX:-1"})
{count: 1, ids: ['aff7']}

可以包含更复杂的查询,例如:

>> device.sendAai({query:"T:/^.$/&&C:.Button&&R:.button_*", action:"getText"})
{retval: ['÷','×','7','8','9','-','4','5','6','+','1','2','3','%','0','.','±','=']}

如果找不到匹配项,它将返回 null,使用 "lastError()" 显示错误信息:

>> device.sendAai({query:"T:X", action:"getText"})
null
>> lastError()
No match found
>> device.sendAai({query:"X:X", action:"getText"})
null
>> lastError()
Error:Cannot find the key for "X"

可以以编程方式检查空值。

详细展开查询

剩下的查询不是 BQ 或 TP。EQ 中的关键通常是涉及多个节点的计算和启发式方法。EQ 从左到右执行键控功能,一路操作,希望最后,ML 包含用户想要的节点。没有动作的 ML 是无趣的,生成的 ML 将传递给动作以应用动作或提取信息。可以多次应用密钥"OX:1&&OX:1"是允许的。



Index(IX) M → O

IX:<号码>。根据从零开始的位置 (IX:0) 从 ML 节点返回一个节点。IX 可以是负值,在这种情况下位置顺序相反(例如 -1 是最后一个节点)。

例子:

>> device.sendAai({})
{count: 9, ids: ['95b0','b779','bb3a','9d32','a0f3','a4b4','a875','ac36','aff7']}
>> device.sendAai({query:"IX:1"})
{count: 1, ids: ['b779']}
>> device.sendAai({query:"IX:-2"})
{count: 1, ids: ['ac36']}


单节点选择(ON)M → O

ON :<option> 从 ML 中的多个节点返回一个节点,目前有 3 个选项可用。 目前只有有限的选项,将来会扩展更多选项。

这些是选项:

  • first:从ML中取出第一个节点(类似于IX:0)。
  • last:从ML中取出最后一个节点(类似于IX:-1)。
  • min:从 ML 中选择最小的节点(面积)。


offset偏移量(OX & OY) O → O

OX :<整数> 和OY :<整数>。使用偏移量以一个参考节点为基准找到相邻节点,OX 找到水平位置,正整数向右移动,负整数向左移动。对于 OY,正整数向下移动,负整数向上移动。

例子:

在关于手机中:

>> device.sendAai({query:"T:Model name&&OX:1", action:"getText"})
{retval: 'Galaxy S22 Ultra'}
>> device.sendAai({query:"T:Model number&&OX:1", action:"getText"})
{retval: 'SM-S908U1'}
>> device.sendAai({query:"T:Android version&&OY:1", action:"getText"})
{retval: '12'}

另一个例子:

>> device.sendAai({query:"T:5&&OX:1", action:"getText"})
{retval: '6'}
>> device.sendAai({query:"T:5&&OY:-1", action:"getText"})
{retval: '8'}

// OX & OY orders may get different results
>> device.sendAai({query:"T:0&&OY:-1&&OX:2", action:"getText"})
{retval: '×'}
>> device.sendAai({query:"T:0&&OX:2&&OY:-1", action:"getText"})
{retval: '−'}


视图组(VG) ○ → ○

视图组:VG 或 VG:<级别>。此命令接受可选级别,如果未指定级别,将使用 1 作为级别。 它接受一个节点(来自查询),将进行父遍历直到找到“组”,级别用于指定何时停止。 “组”是占屏幕宽度 85% 的布局或类中的“ViewGroup”。 ML 将包含组内的节点,第一个节点是包含所有节点的 ViewGroup/Layout 节点本身。 对感兴趣的节点进行逻辑分组并使用“addQuery”或 BQ 搜索组内的节点非常有用。 “setConfig(selector:viewGroupWidthRatio, )”修改85%。

相关操作:“getViewGroup”。

例子:

充电前选择合适的电流,第一级VG没有到达预期节点:

>> device.sendAai({query:"T:Start Charging&&VG"})
{count: 2, ids: ['53b16','53ed7']}

要获得充电组,请使用级别 2。第一个节点将是包含组中所有节点的节点(绿色矩形)。 第一个节点对于“showOnScreen”很有用,以确保整个组在屏幕上可见。

>> device.sendAai({query:"T:Start Charging&&VG:2"})
{count: 24, ids: ['4a8af','4ac70','4b031','4b3f2','4bb74','4bf35','4c2f6','4c6b7','4ca78','4ce39','4d1fa','4d5bb','4d97c','4dd3d','4e0fe','4ec41','4f002','4f3c3','53755','53b16','53ed7','54298','54659','54a1a']}

获取电流安培:

>> device.sendAai({query:"T:Start Charging&&VG:2&&XT:/^[0-9]+ A$/", action:"getText"})
{retval: '32 A'}

要获取和更改,减少 1 安培:

>> device.sendAai({query:"T:Start Charging&&VG:2&&XT:/^[0-9]+ A$/", actions:"getText;click(OX:-1);refresh;getText"})
{count: 4, list: [{retval: '32 A'},{retval: true},{retval: true},{retval: '31 A'}]}

使用“重复”减少 5 安培:

>> device.sendAai({query:"T:Start Charging&&VG:2&&XT:/^[0-9]+ A$/", actions:"getText;repeat(5,nsClick(OX:-1));refresh;getText"})
{count: 4, list: [{retval: '32 A'},{retval: [{retval: true},{retval: true},{retval: true},{retval: true},{retval: true}]},{retval: true},{retval: '27 A'}]}


Intersect相交 (TX & TY) ○ → ○

相交:TX 和 TY。这些命令将一个节点作为参考节点,并将 ML 更改为包含水平相交 (intersectX) 或垂直相交 (intersectY) 的所有节点。匹配器将利用参考节点的边界(intersectX 的上/下,intersectY 的左/右)来搜索相交的节点。

参考节点的大小(宽度或高度)非常重要,如果选择宽参考节点,TY 将匹配屏幕上的大部分节点。明智地选择参考节点。

此命令将获取第一个节点并在 ML 中生成多个节点,您可以使用"addQuery"对它们设置更多约束。

另请参阅: “intersectX"和"intersectY”

例子:

在上面的偏移量计算器示例中:

>> device.sendAai({query:"T:2&&TX"})
{count: 6, ids: ['bb4b','bf0c','c2cd','e0d5','e496','111a2']}
>> device.sendAai({query:"T:2&&TY"})
{count: 6, ids: ['1d115','1d4d6','a886','b3c9','bf0c','ca4f']}

但是,如果您选择结果作为参考节点并在整个屏幕上滚动,则 OX 将获得一个节点(它自己),而 OY 将获得所有节点。

>> device.sendAai({query:"R:.result&&TX"})
{count: 1, ids: ['1d4d6']}
>> device.sendAai({query:"R:.result&&TY"})
{count: 23, ids: ['1d897','1d4d6','d592','f39a','a4c5','a886','ac47','dd14','1029e','b008','b3c9','b78a','e0d5','bb4b','bf0c','c2cd','111a2','e496','c68e','ca4f','ce10','120a6','e857']}


减少节点 (RN) M → M

减少节点:RN。这是 FindNode 的重要特性之一,这个特性接受一个节点 ID 列表,意图减少可见节点的数量。“TL”、“BL”、“OX/OY"和"intersect"都使用了reduceNodes。请注意,如果较大的节点完全包含较小的节点,则 reduceNodes 将选择较小的节点。例如,如果较小的“.Button"节点包含在“.TextView"节点中,“.TextView"节点将被选中而不是“.Button"节点,.TextView 节点上的"单击"仍然有效。请参阅 UI Explorer"优化"模式。

例子:

>> device.sendAai({}).count
242
>> device.sendAai({query:"RN"}).count
55

相关动作:“reduceNodes”。



排序 (ST) M → M

排序:ST:X|Y|YX。根据节点的边界排序,通常方式三是结构和搜索,很可能是从上到下排序的。除非顺序很重要,否则没有理由进行排序。

X – 比较节点的左边界

Y——比较节点的上界

YX——比较节点的上边界和左边界

>> device.sendAai({query:"C:.Button", action:"getText"})
{retval: ['AC','÷','×','DEL','7','8','9','-','4','5','6','+','1','2','3','%','0','.','±','=']}
>> device.sendAai({query:"C:.Button&&ST:Y", action:"getText"})
{retval: ['AC','÷','×','DEL','7','8','9','-','4','5','6','+','1','2','3','%','0','.','±','=']}
>> device.sendAai({query:"C:.Button&&ST:YX", action:"getText"})
{retval: ['AC','÷','×','DEL','7','8','9','-','4','5','6','+','1','2','3','%','0','.','±','=']}
>> device.sendAai({query:"C:.Button&&ST:X", action:"getText"})
{retval: ['AC','7','4','1','0','÷','8','5','2','.','×','9','6','3','±','DEL','-','+','%','=']}

相关操作:排序



基本查询(BQ) M → M

基本查询:BQ:'<基本查询>' 或者 "<基本查询>".。BQ是EQ中的指令。考虑以下查询(从 UI Explorer 自定义查询获得):

查询:T:Overall Cpu Usage&&VG

“VG"是用来获取组的,如果我们要获取"Cpu Cores"的个数,下面的查询是行不通的:

T:Overall Cpu Usage&&VG&&T:Cpu Cores&&OX:1 T:Cpu Cores&&OX:1&&VG

两个"T"命令是基本命令,第二个 T 将覆盖第一个 T,因为 BQ 命令在 EQ 之前应用。如果我们需要EQ中基本查询的功能,那么就可以进行文本匹配。有三种方法可以做到:

  • 使用 BQ:'...' 来包含 BQ keys。BQ:'T:Cpu cores'。
  • 在基本查询中添加了"X"前缀。XT:CPU 内核。
  • 将此标志“selector:allowBqAfterEq”配置为 true,EQ 之后的任何 BQ 命令都将作为 EQ 的一部分按顺序执行。

两者都有效:

  • T:Overall Cpu Usage&&VG&&XT:Cpu Cores&&OX:1
  • T:Overall Cpu Usage&&VG&&BQ:'T:Cpu Cores'&&OX:1
>> device.sendAai({query:"T:Overall Cpu Usage&&VG&&XT:Cpu Cores&&OX:1", action:"getText"})
{retval: '8'}
>> device.sendAai({query:"T:Overall Cpu Usage&&VG&&BQ:'T:Cpu Cores'&&OX:1", action:"getText"})
{retval: '8'}

从版本 14 开始,在 EQ 命令后有一个允许任何 BQ 的标志,在此示例中,“T:Cpu Cores”被视为 EQ 的一部分

>> device.sendAai({action:"setConfig(selector:allowBqAfterEq, true)"})
{retval: true}
>> device.sendAai({query:"T:Overall Cpu Usage&&VG&&T:Cpu Cores&&OX:1", action:"getText"})
{retval: '8'}

动作Actions

FindNode 提供了许多操作命令,“action”可以接受单个字符串命令或包含多个命令的字符串数组。 查询是可选的,如果未指定查询,它将包含默认模板。 如果指定查询,查询输出将保存到 ML,命令/操作需要节点将通过查询或可选查询从底层 ML 获取,使用 action:"getIds" 获取 ML 内容。 如果未指定操作,则使用操作:“getIds”。 “动作”和“动作”的用法是相同的。

使用“device.sendAai()”或“devices.sendAai()”与 FindNode 通信。 对于多个设备,将为每个设备创建一个线程来执行查询和操作。

当 sendAai 遇到来自 FindNode 的错误或超时时,它将返回 null 并带有包含错误消息的“lastError()”。

对于只需要一个节点的动作(例如“点击”),将选择 ML 中的第一个节点(ON、IX、OX 和 OY 可以更改)。 action中有很多命令,新版本会进行改进,可能会引入不兼容,请使用action:"version"确保版本支持所需的命令。 一些命令需要额外的参数(参数将包含在括号中)。 目前支持以下类型的参数:

  • “字符串”或“字符串”:用单引号或双引号括起来的字符串。
  • <no quote>:如果字符串不包含逗号或尾随空格,则不需要引号。
  • 布尔值:真/假。
  • 整数:带或不带小数点的数字。
  • Double:带小数点的数字。


单一操作

在单一动作中,只有一个命令出现在动作中。 在这种情况下,返回结果在“retval”中(如果发现错误则为 null)。

device/devices.sendAai({query:"…", action:<action>]})

示例:

>> device.sendAai({query:"T:Product name&&OX:1", action:"getText"})
{retval: 'Galaxy S22 Ultra'}
// Absence of query, the default query of "TP:more" will be applied
>> device.sendAai({action:"getText(T:Product name&&OX:1)"})
{retval: 'Galaxy S22 Ultra'}

Error message:
>> device.sendAai({action:"getText(T:Product name&&OX:2)"})
null
>> lastError()
Query Error: Node not found on offset

多个操作

FindNode允许有多个动作,FindNode的很多操作都是在多个动作中完成的,多个动作会从左到右进行,一次一个,结果是一个数组,存放每个动作的输出 ,如果其中一个操作失败,它将返回 null, lastError() 显示失败操作的索引(从零开始)和错误消息。 有两种方法可以定义多个动作:

数组:

actions:["action 1", "action 2", …]

字符串中的分号(版本 14+):

actions:"<action 1>;<action 2>"

device/devices.sendAai({query:"…", actions:[<action 1>, <action 2>, …]})
device/devices.sendAai({query:"…", actions:"<action 1>;<action 2>, …"})

示例:

这里有两种执行多个动作的方法,count 会显示命令的数量,list 会显示动作输出的列表。 List[0]是setText的输出,list[1]是refresh的输出,list[2]是gettext的输出。

>> device.sendAai({query:"TP:textInput", actions:["setText(Hello)", "refresh", "getText"]})
{count: 3, list: [{retval: true},{retval: true},{retval: 'Hello'}]}
>> device.sendAai({query:"TP:textInput", actions:"setText(Hello);refresh;getText"})
{count: 3, list: [{retval: true},{retval: true},{retval: 'Hello'}]}

Error message will show the position (zero-based) of the error:
>> device.sendAai({query:"TP:textInput", actions:["setText(Hello)", "refresh", "noAction", "getText"]})
null
>> lastError()
[2 - noAction] Invalid action: noAction

动作中的变量

如果动作的参数在运行时确定,则需要进行变量替换,有 3 种方法可以做到:

  • JavaScript 字符串操作函数。
  • aaix 辅助函数
  • JavaScript 模板文字

如果想在动作命令中使用变量,可以用“+”连接多个项,有几种选择:

var input = "Hello"
>> "setText('" + input + "')"
setText('Hello')
>> 'setText("' + input + '")'
setText("Hello")
>> 'setText(' + input + ')'
setText(Hello)
>> device.sendAai({query:"TP:textInput", action:["setText('" + input + "')", "click(OX:2)"]});
{count: 2, list: [{retval: true},{retval: true}]}

aaix:

实现相同结果的简单辅助函数,仅适用于数组中的单个操作或多个操作:

>> aaix("setText", input)
setText("Hello")
>> aaix("sendKey", tcConst.keyCodes.KEYCODE_HOME, 0)
sendKey(3, 0)

>> device.sendAai({query:"TP:textInput", action:[aaix("setText", input), "click(OX:2)"]})
{count: 2, list: [{retval: true},{retval: true}]}
>> device.sendAai({action:aaix("sendKey", tcConst.keyCodes.KEYCODE_HOME, 0)})
{retval: true}

从版本 14 开始,可以使用 JavaScript 的模板字面值来实现相同的结果:

>> device.sendAai({query:"TP:textInput", action:["setText(${input})", "click(OX:2)"]})
{count: 2, list: [{retval: true},{retval: true}]}
>> device.sendAai({query:"TP:textInput", action:"setText(${input});click(OX:2)"})
{count: 2, list: [{retval: true},{retval: true}]}

如果参数是字符串,以下 3 个示例是相同的:

action:["intersectX(OY:1, IX:2)", "getText"]
action:["intersectX('OY:1', 'IX:2')", "getText"]
action:[aaix("intersectX", "OY:1", "IX:2"), "getText"]

大多数操作在第一个参数中都有可选的查询字符串,例如:

{query:"T:John", action:"click"}
{actions:["newQuery(T:John)", "click"]}	→ {action:"click(+T:John)"}
{actions:["addQuery(T:Mary)", "click"]}	→ {action:"click(T:Mary)"}

添加的查询字符串将使命令更加简洁。以“+”为前缀的"newQuery"将清除ML,进行搜索并重建ML。“addQuery"将根据现有的 ML 进行搜索。



同步动作

已经付出了很大的努力来确保动作是同步的,当调用"点击"时,不仅确保点击被同步按下,它会等待某些"事件"以确保渲染"开始"发生,不能确保渲染完成。FindNode 提供了几种执行此类检查的方法:

  • 诸如"单击"之类的动作命令:它应该在大多数时间都有效。
  • “waitQuery”:如果新窗口需要很长时间才能完成渲染,使用此命令检查新节点是否可用,在 waitQuery 后添加操作命令,这将确保找到节点并准备好接受操作。


可选查询 (OQ)

Actions require nodes将从ML获取,actions require one node,将从ML获取第一个node。但是,用户可以通过在操作中添加选项查询 (OQ) 来更改行为。

E.g.{query:"T:OK", action:"click"} → {action:"click(T:OK)"}

所有 OQ 都是可选的,如果未指定 OQ,则操作将从 ML 中获取节点。如果动作需要一个节点(例如单击),将使用 ML 中的第一个节点,如果动作支持多个节点(例如 getNodes 或 getText),则使用整个 ML。

OQ 是一种常规查询语言,不同的类型由不同的前缀标识,OQ 和其他查询一样可以生成一个或多个节点,一对查询类型会修改 ML。以下是支持的 3 个前缀:

  • 没有前缀,应用来自 ML 的查询。ML 没有变化(类似于"addQuery"操作)。
  • “+”:新查询,新ML(类似于"newQuery"动作)。
  • “*”:与常规查询相同,结果将替换 ML。

假设计算器中的数字 1 到 9 在 3 行中,“7、8、9”、“4、5、6"和"1、2、3”(从上到下):

>> device.sendAai({query:"T:/[3-9]/", actions:["getText(T:5)", "getText"]})
{count: 2, list: [{retval: '5'},{retval: ['7','8','9','4','5','6','3']}]}
>> device.sendAai({query:"T:/[3-9]/", actions:["getText(+T:5)", "getText"]})
{count: 2, list: [{retval: '5'},{retval: '5'}]}
>>  device.sendAai({query:"T:/[3-9]/", actions:["getText(*T:5)", "getText"]})
{count: 2, list: [{retval: '5'},{retval: '5'}]}

这是常规和“*”之间的区别:

>>  device.sendAai({query:"T:/[3-9]/", actions:["getText(OX:1)", "getText(OX:1)", "getText"]})
{count: 3, list: [{retval: '8'},{retval: '8'},{retval: ['7','8','9','4','5','6','3']}]}
>>  device.sendAai({query:"T:/[3-9]/", actions:["getText(*OX:1)", "getText(*OX:1)", "getText"]})
{count: 3, list: [{retval: '8'},{retval: '9'},{retval: '9'}]}

看起来“*”和“+”类似,考虑下面的,“*”匹配来自"ML"的列表(T:2 不在 ML 中),“+”搜索整个屏幕内容。

>>  device.sendAai({query:"T:/[3-9]/", actions:["getText(+T:1)", "getText"]})
{count: 2, list: [{retval: '1'},{retval: '1'}]}
>> device.sendAai({query:"T:/[3-9]/", actions:["getText(*T:1)", "getText"]})
null
>>  lastError()
[0 - getText] Query Error:No match found

由于常规查询(无前缀)和更新 ML 的查询(“*”前缀)将使用 ML,因此仅允许在"newQuery"或“+”前缀中使用"TP”:

>>  device.sendAai({query:"T:/[3-9]/", actions:["getText(TP:all)", "getCount"]})
null
>>  lastError()
[0 - getText] Query Error:Key "TP" is not permitted in this query
>>  device.sendAai({query:"T:/[3-9]/", actions:["getText(+TP:all)", "getCount"]})
{count: 2, list: [{retval: [{},{},{},{},{},{},{},'Calculator',{},{},{},{},'AC','÷','×','DEL','7','8','9','-','4','5','6','+','1','2','3','%','0','.','±','=']},{count: 32}]}
>>  device.sendAai({query:"T:/[3-9]/", actions:["getText(*TP:all)", "getCount"]})
null
>>  lastError()
[0 - getText] Query Error:Key "TP" is not permitted in this query

获取命令


获取边界getBounds(M)

返回 ML 中节点的边界(格式:[left, top, right, bottom]),它返回 2 个数组,一个包含所有 ID,另一个包含所有边界。

用法:

getBounds[(<查询>)]

返回:

bounds 的数组,每个 bound 是一个 4 元组的数组:[x1, y1, x2. y2]。

>> device.sendAai({query:"T:/[0-9]/", action:"getBounds"})
{bounds: [[18,1158,370,1521],[370,1158,722,1521],[722,1158,1074,1521],[18,1521,370,1884],[370,1521,722,1884],[722,1521,1074,1884],[18,1884,370,2247],[370,1884,722,2247],[722,1884,1074,2247],[18,2247,370,2610]], count: 10, ids: ['98d1','9c92','a053','a7d5','ab96','af57','b6d9','ba9a','be5b','c5dd']}


获取节点的布尔属性getBoolProp (O)

返回节点的布尔属性,它将返回"editable”、“progressible”、“clickable”、“checkable”、“scrollable"或"longClickable”。如果没有找到属性,它将返回 null。

用法:

getBoolProp [(<查询>)]

返回:

字符串属性数组。

>> device.sendAai({query:"T:/12:30\\sPM/&&VG", action:"getBoolProp(T:Every day&&OX:1)"})
{retval: ['checkable','clickable']}
>> device.sendAai({query:"T:/12:30\\sPM/&&VG&&PQ:'T:Every day&&OX:1'", action:"getBoolProp"})
{retval: ['checkable','clickable']}
>> device.sendAai({query:"T:ci:calculator", action:"getBoolProp"})
null
>> lastError()
No boolean property is found


获取节点的树布尔属性getBoolPropInAncestors(O)

与 getBoolProp 类似,除了它遍历父节点到其父节点,直到它到达顶部节点树。

用法:

getBoolPropInAncestors [(<查询>)]

返回:

属性数组和节点 ID 的数组,如果找不到则为 null。

>> device.sendAai({query:"T:John", action:"getBoolPropInAncestors"})
{boolProp: [['clickable','3622e'],['scrollable','105cd']], count: 2}


获取是否被选中getChecked(O / M)

这些命令适用于具有"可检查"节点的节点。通常,切换控件、复选框或单选按钮是可检查的。例如,“.Switch"或“.Checkbox"的类名是可检查的。

用法:

getChecked [(<查询>)]

返回:

如果输入是一个节点,则在"retval"中返回true/false,如果是多个节点,retval将包含true/false的数组,对于不可检查的节点,它将显示为"N/A”。如果未找到任何可检查节点,则返回 null。“BP:checkable"查找可检查的节点,“BP:checked"查找已检查的节点。

另见: setChecked

>> device.sendAai({action:"getChecked(D:Sunday&&TX)"})
{retval: [false,true,true,true,true,true,false]}

如果不想用description,可以用class来查找,没有其他UI元素有类“.CheckBox”:

>>  device.sendAai({action:"getChecked(C:.CheckBox&&IX:0&&TX)"})
{retval: [false,true,true,true,true,true,false]}

要查找所有选中的描述:

>> device.sendAai({action:"getDescription(D:Sunday&&R:.day_button_0&&TX&&XBP:checked)"})
{retval: ['Monday','Tuesday','Wednesday','Thursday','Friday']}


getDescription/getText/getHintText (O|M)

“getNodes”的简单版本,更容易返回获取文本、描述和提示文本的内容。

动作将检测一个或多个节点,如果检测到一个节点,它将返回值,否则将返回文本/描述或提示文本的数组。

用法:

getDescription [(<查询>)]

getHintText [(<查询>)]

getText [(<查询>)]

返回:

如果找到多个节点,则在 retval 中返回一组 text/description/hint 文本,如果找到一个节点,则在 retval 中返回 text/description。/p>

>>  device.sendAai({query:"TP:line,bottom,-1", postAction:"getText"})
{retval: ['Chats','Calls','Contacts','Notifications']} 
// One value will not return an array
>> device.sendAai({query:"TP:line,bottom,-1&&IX:1", postAction:"getText"})
{retval: 'Calls'} 

>> device.sendAai({query:"BP:editable", action:"getText"})
{retval: ['text','text','Number','Email','text password','Person Name']}
>> device.sendAai({query:"BP:editable", action:"setText(T:Number, '100');setText(T:text password, secretWord)"})
{count: 2, list: [{retval: true},{retval: true}]}
>> device.sendAai({query:"BP:editable", action:"getText"})
{retval: ['text','text','100','Email','••••••••••','Person Name']}
// getHintText will maintain the value of the original hints, getText does not
>> device.sendAai({query:"BP:editable", action:"getHintText"})
{retval: ['text','text','Number','Email','text password','Person Name']}


获取焦点getFocus

返回接收焦点的节点(getNodes(BP) 中的"可聚焦”)(通常是文本字段或滑块),如果没有节点获得焦点,则返回 null。如果找到节点,它将 ML 设置为找到的节点。

用法:

获取焦点

返回:

如果找到,则返回"node"将节点 ID,否则返回 null。

例子:

从现有行输入下一行

>> device.sendAai({action:["getFocus", aaix("setText", username), "addQuery(OY:1)", aaix("setText", password)]})
{count: 4, list: [{node: '67693'},{retval: true},{count: 1},{retval: true}]}


获取 ID getIds(M)

如果未定义操作,则这是默认操作,此命令将返回 ML ID 的计数和数组。

用法:

getids [(<查询>)]

返回:

计数和 ID 数组。

>> device.sendAai({})
{count: 26, ids: ['7708','d8a2','e7a6','ef28','824b','860c','89cd','8d8e','914f','9510','98d1','9c92','a053','a414','a7d5','ab96','af57','b318','b6d9','ba9a','be5b','c21c','c5dd','c99e','cd5f','d120']}
>> device.sendAai({action:"getIds(C:.Button)"})
{count: 4, ids: ['8dc5','9547','9908','9cc9']}


获取节点getNodes(M)

这用于检索节点的信息。这些字段可以确定要显示的内容。“getNodes"将在检索信息之前刷新节点。

用法:

获取节点

getNodes([<查询>] [,<字段>])

参数:

fields:可选的"fields"用于定义返回的字段,“fields"中的多个字段用逗号分隔。由于它在"字段"内有逗号,因此您需要使用引号。String 有效的字段标识符是:

  • P:包名(S)
  • C:类名(S)
  • R:资源 ID (S)
  • D:描述(S)
  • T: 文本 (S)
  • IT:输入类型(I)
  • CC: 孩子数 (I)
  • RI: RangeInfo, 提供更多关于控件的类型、最小值、最大值、电流的信息,例如SeekBar(滑块),使用"setProgress"命令更改值。
  • BP:节点中的所有布尔属性。
  • B:返回节点(S)的[左上][右下]边界
  • 全部:一切

返回:

节点信息的计数和数组。

>> device.sendAai({action:"getNodes(T:/[0-9]/,'R,T')"})
{count: 10, list: [{id: 'a773', resourceId: '.button_seven', text: '7'},{id: 'ab34', resourceId: '.button_eight', text: '8'},{id: 'aef5', resourceId: '.button_nine', text: '9'},{id: 'b677', resourceId: '.button_four', text: '4'},{id: 'ba38', resourceId: '.button_five', text: '5'},{id: 'bdf9', resourceId: '.button_six', text: '6'},{id: 'c57b', resourceId: '.button_one', text: '1'},{id: 'c93c', resourceId: '.button_two', text: '2'},{id: 'ccfd', resourceId: '.button_three', text: '3'},{id: 'd47f', resourceId: '.button_zero', text: '0'}]}
>> device.sendAai({query:"C:.SeekBar", action:"getNodes(RI)"})
{count: 1, list: [{id: 'b6f0', rangeInfo: {current: 0, max: 100, min: 0, type: 0, typeString: 'int'}}]}
>> device.sendAai({query:"C:.SeekBar", action:"getNodes(BP)"})
{count: 1, list: [{booleanProperties: {checkable: false, checked: false, clickable: false, editable: false, enabled: true, focusable: true, focused: false, longClickable: false, multiLine: false, scrollable: false, selected: false, visibleToUser: true}, id: 'b6f0'}]}


获取包名getPackageName

返回当前运行的包名。

用法:

获取包名

返回:

“retval"中的包名称。

>> device.sendAai({action:"getPackageName"})
{retval: 'com.google.android.gm'}


获取进度getProgress(O)

返回滑块等 UI 元素的进度类型的值(“getNodes(RI)”的简写):

用法:

getProgress [查询]

返回:

progressible节点的"retval"中的RangeInfo,如果该节点不是progressible节点,则返回null:

RangeInfo 具有以下 3 种类型之一:“float”——最小值和最大值之间的浮点数,最小值和最大值之间的"int"整数以及 0 和 1 之间的"百分比”。“current"包含当前值。

>> device.sendAai({action:"getProgress(C:.SeekBar)"})
{retval: {current: 41, max: 100, min: 0, type: 0, typeString: 'int'}}

另见: setProgress



getQuery/getUniqQuery (O)

接受一个节点并尝试生成一个查询来匹配该节点(或多个节点)。从"getQuery"返回的查询可以匹配多个节点,有助于从多个节点获取文本或描述。“getUniqQuery"返回的query只会匹配一个节点,得到的query会被发送给其他设备定位节点。到目前为止,它是初级的,我们将来会开发更好的查询字符串:

  • 将使用匹配列表(查询或元素)的第一个节点作为引用节点。
  • 输出未完全优化,例如,它不使用模板。
  • 如果找到多个节点,“getUniqQuery"将添加"IX"来标识单个节点,这很容易出错,特别是对于不使用资源 ID 的应用程序,IX 可能很大,UI 的任何变化都可能影响准确性。
  • UI Explorer 的"代码"按钮使用此命令来显示查询字符串。
  • 如果使用偏移量(OX|OY),它将搜索具有文本信息(例如T:<text>)的相邻节点。
  • 如果文本或描述信息是动态的,请使用 ignoreText/ignoreDescription。
  • -对于文本和描述,如果长度超过30个字符,它会列出前30个字符并在末尾添加“*”,使用setConfig(text:maxQueryLength, <length>) 来改变它。例如,如果节点的文本是"The quick brown fox jumps over the lazy dog”,生成的查询将是:“T:The quick brown fox jumps over*”。

用法:

getUniqQuery/getQuery - 默认是使用文本和描述作为查询的一部分。

getUniqQuery/getQuery(<true/false>) – true 忽略搜索查询中的文本。

getUniqQuery/getQuery(<true/false>, <true/false>) – 第二个参数为 true 以忽略搜索查询中的描述。

返回:

返回查询字符串。

要获取每个节点的查询:

>> device.sendAai({query:"T:Controls&&TX"}).ids.forEach(
 x => print(device.sendAai({elements:[x],action:"getUniqQuery"}).query)
)
T:Controls&&OX:-1
T:Controls
T:Controls&&OX:1

>> var query = device.sendAai({query:"T:AAPL", action:"getQuery(true)"}).query
>> query
C:.TextView&&R:.ticker&&CC:0
>> device.sendAai({query:query, action:"getText"})
{retval: ['MSFT','INTC','TGT','T','AAPL','SBUX','TSLA','CSCO']}

查询命令

以下命令有一个例外(waitQuery)将涉及 ML。

推送/弹出和加载/保存 (M)

这些命令存储和检索 ML,这仅对多个命令有用,推送/保存 ML,newQuery + 操作,弹出/加载以检索最后保存的 ML 并继续。 保存/加载是出于兼容性原因,因为它们没有限制存储的 ML 数量。 其实在实现上,“save”和“push”,“load”和“pop”是一样的。 您可以使用多个“保存”和“加载”。

用法:

push

pop

save

load

返回:始终返回“retval”为真,如果堆栈没有可弹出的内容将产生错误。



相交 X intersectX/相交 Y intersectY(O → M)

如果指定查询,则查询的第一个节点将用于相交,否则将使用 ML 第一个节点。此命令将获取一个节点并生成更多节点并存储在 ML 中。

用法:

相交?[(<查询>)]

返回:

true /false:操作是否成功。如果操作成功,将返回"count”

如果指定了"post query”,post query(参见“addQuery”)的输出将被存储为 ML。如果未指定"post query”,intersect 的输出将替换 ML。

另请参阅: “TX"和"TY"查询以获得更多解释

例子:

假设计算器应用程序

>> device.sendAai({actions:["intersectX(T:3)", "getText"]})
{count: 2, list: [{count: 4},{retval: ['1','2','3','%']}]}

“intersect Y"可能包括其他不可见的节点,例如结果或视图组。

>>  device.sendAai({actions:["intersectY(T:3)", "getText"]})
{count: 2, list: [{count: 8},{retval: [{},{},{},'×','9','6','3','±']}]}

添加正则表达式以确保至少有一个字符可见:

>>  device.sendAai({actions:["intersectY(T:3)", "addQuery(T:/.+/)", "getText"]})
{count: 3, list: [{count: 8},{count: 5},{retval: ['×','9','6','3','±']}]}

这也将起作用:

>> device.sendAai({actions:["intersectY(T:3)", "getText(T:/.+/)"]})
{count: 2, list: [{count: 8},{retval: ['×','9','6','3','±']}]}


获取视图组getViewGroup(O → M)

此命令类似于"VG"查询,将接受级别和查询。Query if defined 用于标识要搜索该组的第一个节点。类似于"VG”,它会改变ML。

用法:

获取视图组

getViewGroup([<查询>,] <级别>)

返回:

如果找到ViewGroup,则返回ML中的计数,如果未找到ViewGroup,则返回null。

另请参阅: 查询"VG”

>> device.sendAai({query:"T:Battery&&VG", action:"getText(T:/\\d%/)"})
{retval: '92% available'}
>> device.sendAai({actions:["getViewGroup(T:Battery)","getText(T:/\\d%/)"]})
{count: 2, list: [{count: 6},{retval: '92% available'}]}


减少节点reduceNodes(M → M)

它与"RN"查询相同,没有参数。结果后将返回节点数作为"计数”。

用法:

减少节点

返回:

在"count"中返回结果节点的数量。

另请参阅: 查询"RN”

>> device.sendAai({query:"TP:reduced"}).count
54
>> device.sendAai({action:"reduceNodes"})
{count: 54}


排序Sort (M → M)

根据节点边界排序。它与"ST"查询相同,参数与 ST 相同。

用法:

Sort(X|Y|YX)

返回:

在"retval"中返回真/假。

另请参阅: 查询"ST”



新查询newQuery/添加查询addQuery/等待查询waitQuery

所有接受查询字符串,“newQuery"将开始新的查询并重新创建ML(类似于OQ中的“+”)。"addQuery" 在 ML 中执行搜索,将结果节点放在 ML 中(类似于 OQ 中的 "*")。“waitQuery"不会改变 ML,它有一个超时,将确保在超时到期之前完成查询,否则它将返回 null。

例如:“VG"通常后跟"addQuery"而不是"newQuery”。另一方面,点击打开新窗口,则需要"newQuery”。

newQuery(<query>)

addQuery(<query>)

waitQuey(<query>[,<timeout>]) // 默认超时 = 2000 毫秒

返回:

newQuery 和 addQuery 在"count"中返回结果 ML 的大小。当超时到期时,waitQuery 将返回 null,否则"retval"将包含 true。

动作指令

本节中的命令对节点执行操作。



click、nsClick、click2、longClick

节点上的各种点击,这是同步点击,将确保在返回控件时屏幕开始刷新。

  • click:单击节点的中心。
  • longClick:按住并单击更长时间(500ms)以模拟长按。
  • click2:点击节点中的随机位置。

以上所有的点击都会在点击执行后监听屏幕变化的事件,如果屏幕没有变化,这些点击都会失败,最终超时。如果执行点击后屏幕没有改变,使用"nsClick”:

  • nsClick:执行点击但不监控事件。

用法:

click/nsClick/click2/longClick [(<query>)]

返回:

true 或 false 以指示操作是否成功。

click、nsclick、click2、longClick

click(<query>)、nsclick(<query>)、click2(<query>)、longClick(<query>)

例子:

>> device.sendAai({query:"T:5", action:"click"})
{retval: true}
>> device.sendAai({action:"nsClick(T:5)"})
{retval: true}


forEach (M)

动作循环遍历 ML 中的每个节点并一次执行一个提供的动作。 有几条规则适用:

  • 不能执行可以更改 ML 的操作,否则会产生错误。
  • 所有带有前缀(“+”或“*”)的可选查询 (OQ) 都会产生错误。
  • 所有动作循环只会看到一个节点。
  • 返回值:
    • 对于参数中的单个动作,“retval”数组将用于存储每个节点的输出。
    • 对于多个动作,JSON名称将包含数组的数组,内部数组将是节点的执行结果,输出数组将包含所有节点的数组。

对于多个动作,“forEach”采用JSON对象的名称,值定义数组或字符串中的动作将以分号分隔。 对于单个动作,可以将动作名称放在参数中。

用法:

forEach(<action name>)

forEach(<JSON name>)

返回:

  • 对于参数中的单个动作,“retval”数组将用于存储每个节点的输出。
  • 对于多个动作,JSON名称将包含数组的数组,内部数组将是节点的执行结果,外部数组将包含所有节点的数组。

示例:

第一行运行“getText”一次,第二行运行“getText”4次。

>> device.sendAai({query:"T:1&&TX", action:"getText"})
{retval: ['1','2','3','%']}
>> device.sendAai({query:"T:1&&TX", action:"forEach(getText)"})
{retval: [{retval: '1'},{retval: '2'},{retval: '3'},{retval: '%'}]}

>> device.sendAai({query:"R:.ticker&&T:/TSLA|AAPL|INTC|CSCO/", actions:"forEach(getTicker)", getTicker:"refresh(OX:1);getDescription(OX:1)"})
{getTicker: [[{retval: true},{retval: '160.25'}],[{retval: true},{retval: '190.41'}],[{retval: true},{retval: '50.51'}],[{retval: true},{retval: '29.36'}]]}

示例文档中,对于计算器,将所有操作填入“elements”,并使用“forEach(nsClick)”键入计算。



刷新refresh (M)

缓存可访问节点信息,如果屏幕已经更新,缓存信息可能不准确,使用"刷新"强制重新读取节点信息。

用法:

refresh [(<query>)]

返回:

返回计数和真(总是返回真)

例子:

>> device.sendAai({query:"T:Overall Cpu Usage&&OX:1", actions:["getText", "sleep(2000)", "refresh", "getText"]})
{count: 4, list: [{retval: '29'},{retval: true},{retval: true},{retval: '78'}]}


repeat (M)

此命令对于重复操作很有用,它共享操作输入(JSON 或单个操作)和与“forEach”类似的返回值,它将在给定的运行次数中重复操作/操作执行。 最大重复次数为 10。

用法:

repeat (<count>, <action name>)

repeat (<count>, <JSON name>)

返回:

  • 对于参数中的单个操作,“retval”数组将用于存储每次运行的输出。
  • 对于多个操作,JSON 名称将包含数组的数组,内部数组将是一次运行的执行结果,外部数组将包含所有运行。

例子:

此命令在计算器中点击 5 个“1”:

>> device.sendAai({action:"repeat(5,click(T:1))"})
{retval: [{retval: true},{retval: true},{retval: true},{retval: true},{retval: true}]}

设置EV汽车的充电电流:

减少 1 安培:

>> device.sendAai({action:"click(T:/[0-9]+ A/&&OX:-1)"})
{retval: true}

减少 5 安培:

>> device.sendAai({action:"repeat(5, click(T:/[0-9]+ A/&&OX:-1))"})
{retval: [{retval: true},{retval: true},{retval: true},{retval: true},{retval: true}]}

归约和验证:

>> device.sendAai({query:"T:/[0-9]+ A/", action:"getText;repeat(5,click(OX:-1));refresh;getText"})
{count: 4, list: [{retval: '27 A'},{retval: [{retval: true},{retval: true},{retval: true},{retval: true},{retval: true}]},{retval: true},{retval: '22 A'}]}


滚动到视图scrollIntoView → (M)

接受查询字符串和滚动方向,将根据提供的方向向上/向下滚动尝试匹配查询,当找到一个或多个节点时,它会返回,如果找不到匹配,则返回 null 并出错。 滚动会使用page up/down,最大30页(由setConfig(navi:maxPageScroll, )控制),滚动可能需要一些时间,此命令会自动延长“sendAai”中的超时时间,直到 完毕。 为了使脚本能够在任何分辨率和大小下运行,使用 scrollIntoView 比 Page Up/Down 更好。 这个动作也会使匹配节点中的第一个节点完全可见并修改ML,ML可能包含多个节点。

用法:

scrollIntoView(<query>) // 默认向下

scrollIntoView(<query>, <方向>)

参数:

<direction>可以是以下之一,如果不指定,默认为"向下”。

“down”:从当前位置开始,向下滚动搜索。

“up”:从当前位置开始向上滚动搜索。

“top”:快速滚动到页面顶部,向下滚动搜索。

"bottom": 快速滚动到页面底部,向上滚动搜索。

返回:

如果找不到查询将返回 null 否则将返回 true,ML 将设置为第一个匹配的节点 ID。

例子:

>> device.sendAai({actions:["scrollIntoView(T:John)", "click"]})
{count: 2, list: [{retval: true},{retval: true}]}


发送键码sendKey

sendKey 接受键码或元状态并将键发送到屏幕(不是节点),具有焦点的 UI 元素将接收键码和元状态,例如文本字段或屏幕键盘。sendKey 还提供快捷方式,一个文本字符串,一些常用的键码。并非所有的键码都会生成字符,一些特殊的键码会带来新窗口,例如返回、应用程序切换或转到主屏幕。键代码和元状态显示在 Android KeyEvent 类中。“sendKey"发送到屏幕,而不是特定节点,因此不涉及 ML。

快捷方式只是映射到不同的键码,以下是所有快捷方式(不区分大小写):“home”、“back”、“backspace”、“enter”、“appswitch”(切换到上一个应用程序),对于旧的应用程序、“搜索"和"菜单”。

用法:

sendKey(<keycode>)

sendKey(<键码keycode>, <元状态meta>)

sendKey(<快捷键>)

参数:

关键代码和元状态:是整数,“快捷方式”是单个字符串。 关于返回键,如果在Sigma输入法输入模式下,单个“返回”键会向窗口发送2个“返回”键,第一个返回键关闭输入法,第二个返回键关闭窗口。 使用“setConfig(sigmaInput:backKeyAutoDismiss, false)”更改行为。

以下是快捷方式(其中一些可能无法使用,具体取决于 Android 版本):

appswitch 切换应用

back 发送返回键

backspace 发送退格键

删除发送删除键

enter 发送回车键

home 发送主页键

menu 发送菜单键

search 发送搜索键

其他3个快捷键不是“sendKey”的一部分,但效果类似:

notifications 显示通知屏幕

recentApps 显示最近的应用程序屏幕

quickSettings 显示快速设置屏幕

返回:

以 true/false 返回操作的状态。

例子:

tcConst 包含关键代码和元状态信息。例如,以下 3 个示例将得到相同的结果:

sendKey('back')
sendKey(4)
sendKey(tcConst.keyCodes.BACK)

这将键入"A”:

>> device.sendAai({preAction:aaix("sendKey", tcConst.keyCodes.A, tcConst.keyCodes.META_SHIFT_ON)})
{retval: true}


打开开关setChecked(M)

此命令适用于具有"可检查"节点的节点。通常,切换控件是可检查的,复选框或单选按钮,类的示例可以是“.Switch"或“.Checkbox”。对于单选按钮,setChecked(false) 将不起作用,因为 FindNode 不知道要单击哪个单选按钮来禁用现有的单选按钮。

用法:

setChecked(真|假)

setChecked([查询],真|假)

参数:

“setChecked(true|false)”,由于没有设置选中值的权限,如果需要更改值,它会点击节点来切换值。它接受多个节点,将忽略不可检查的节点。

返回:

setChecked 返回"changedCount”,有多少可检查的节点被改变了。



设置进度setProgress(O)

设置滑动条的值,min,max,type,value可以通过"getNodes('RI')"获取,

用法:

setProgress(<数字>)

setProgress(<查询>,<数字>)

参数:

<number>:整数或小数来设置由"type”、“min"和"max"决定的值

返回:

true/false:操作是否成功。

另见: getProgress

例子:

将显示亮度设置为 50%:

>> device.sendAai({query:"T:Brightness&&VG&&PQ:'C:.SeekBar'", action:"getProgress"})
{retval: {current: 85983232, max: 267386880, min: 0, type: 0, typeString: 'int'}}
>> var max = device.sendAai({query:"T:Brightness&&VG&&PQ:'C:.SeekBar'", action:"getProgress"}).retval.max
267386880
>> device.sendAai({query:"T:Brightness&&VG&&PQ:'C:.SeekBar'", action:aaix("setProgress", max/2)})
{retval: true}

在声音设置中,将 4 个音量都设置为 50%:

device.sendAai({query:"C:.SeekBar"}).ids.forEach(
 x => { 
  var max = device.sendAai({elements:[x], action:"getProgress"}).retval.max;
  device.sendAai({elements:[x], action:aaix("setProgress", max/2)});
}
)


设置文本setText (O)

如果节点是文本字段,它将以编程方式向输入文本字段输入"输入字符串"值,不涉及 Sigma 输入法。

对于Android 11或更高版本,如果消息包含“\n”或“\r\n”作为输入字符串的后缀:

  • “搜索”字段通常不包含执行搜索的按钮。 这将在输入文本后发送“搜索”操作。
  • 对于带有“Enter as Send”的App,无需点击发送按钮即可进入并发送消息。

用法:

setText([查询], <输入字符串>)

setText([查询], +<输入字符串>)

参数:

输入字符串:默认情况下,在输入字符串之前会清除文本字段,如果第一个字母是“+”,则会添加到现有字符串上。对于多行文本字段,使用“\n"跳到下一行。

返回:

true 或 false 以指示操作是否成功。

例子:

>> device.sendAai({query:"BP:editable", action:"getText"})
{retval: ['text','text','Number','Email','text password','Person Name']}
>> device.sendAai({query:"BP:editable", action:"setText(T:Number, '100');setText(T:text password, secretWord)"})
{count: 2, list: [{retval: true},{retval: true}]}
>> device.sendAai({query:"BP:editable", action:"getText"})
{retval: ['text','text','100','Email','••••••••••','Person Name']}
>> device.sendAai({query:"BP:editable", action:"getHintText"})
{retval: ['text','text','Number','Email','text password','Person Name']}

>> device.sendAai({query:"TP:textInput&&IX:1", action:"setText('Hello')"})
{retval: true}
>> device.sendAai({query:"TP:textInput&&IX:0", action: "setText('+ World')"})
{retval: true}
>> device.sendAai({query:"TP:textInput", action: "setText('Hello\n')"})
{retval: true}

通过 String.fromCodePoint 输入表情符号:

>> device.sendAai({query:"TP:textInput",action:aaix("setText", String.fromCodePoint(0x1f60a))})
{retval: true}

或者与普通消息混合:

>> device.sendAai({query:"TP:textInput", action:aaix("setText", 
"hello" + String.fromCodePoint(0x1f643, 0x1f644) + "world")})
{retval: true}


屏幕显示showOnScreen (O)

这将确保第一个节点将完全显示在屏幕上,如果关联的 UI 元素部分显示,它将滚动直到 UI 元素在屏幕上完全可见。 如果节点太大而无法放入可滚动区域,则会产生错误。

用法:

showOnScreen [(<查询>)]

返回:

true 表示屏幕已经滚动,false 表示节点已经在屏幕上完全可见,什么都不做。 如果节点太大而无法放入可滚动区域,则返回 null。



休眠sleep

此命令将在指定的毫秒数上暂停执行,这对于与时间相关的等待操作很有用。

用法:

睡眠(<时间>)

参数:

time:指定等待的时间,以毫秒为单位。

返回:

返回真



直到until

until 会根据指定的选项等待一定的条件,如果满足条件,则返回 true,如果超时,则返回 null 并返回错误。目前支持 2 种类型:

直到([查询],走了,<超时>)

“gone"将等到指定节点(ML 或 OQ 中的第一个元素)消失。

例子:

以下将等到重置按钮消失,两种格式都支持:

>> device.sendAai({query:"R:.btn_img_reset_time", action:"until(gone, 20000)"})
{retval: true}
>> device.sendAai({action:"until(R:.btn_img_reset_time, gone, 20000)"})
{retval: true}

直到([查询],改变,T|D,<超时>)

将检查直到超时或文本 (T) 或描述 (D) 内容更改。

例子:

以下示例检查纳斯达克指数是否已更改:

>> device.sendAai({query:"T:Nasdaq*&&OY:1", actions:["getDescription", "until(changed,D,30000)","getDescription"]})
{count: 3, list: [{retval: '10,989.25'},{retval: true},{retval: '10,988.50'}]}
>> device.sendAai({actions:["until(T:Nasdaq*&&OY:1, changed, D, 30000)","getDescription"]})
{count: 2, list: [{retval: true},{retval: '10,988.50'}]}

其他命令



打开应用openApp/重启应用restartApp/关闭应用closeApp 版本 11

3个命令是启动重启关闭设备上的应用程序。所有命令都接受一个参数,包名称或应用程序显示名称(启动器上显示的名称),如果匹配多个具有相同名称的应用程序,将使用第一个应用程序。不允许部分匹配,它是应用程序名称的不区分大小写的匹配。不带参数的"closeApp"将关闭现有应用程序。

“openApp"将运行应用程序,如果应用程序驻留在内存中,它将恢复执行。如果自动化需要以已知状态启动应用程序,“restartApp"将在启动应用程序之前先关闭应用程序。“closeApp"将关闭应用程序(强制关闭)。要打开应用程序以达到"稳定的已知状态”,请使用"restartApp"和"waitQuery”。

用法:

openApp(<名称>)

restartApp(<名称>)

closeApp(<名称>)

closeApp

返回:

retval:成功时为 true,失败时为 lastError() 为 null。



打开安卓设置页面openAndroidSetting

将根据指定的设置名称打开android系统设置窗口,设置名称将转换为大写字母,“ "将转换为"_”,名称将添加"_SETTINGS"后缀,如果它已经存在:

在https://developer.android.com/reference/android/provider/Settings中提供部分设置,使用"listAndroidSetting"获取此命令中可用的设置列表。此命令将打开设置窗口。如果未找到该设置,它将返回 null。

用法:

openAndroidSetting(<设置名称>)

例如:以下命令打开无线设置:

device.sendAai({action:"openAndroidSetting(wireless)"})

以下打开应用程序设置:

>> device.sendAai({action:"openAndroidSetting(manage applications)"})
{retval: true}

“MANAGE_APPLICATIONS_SETTINGS”、“管理应用程序设置”、“管理应用程序"是相同的。



列出安卓设置listAndroidSettings

此命令以数组形式返回设置列表。

用法:

列出Android设置

返回:

数组中的有效设置列表。

>> device.sendAai({action:"listAndroidSetting"})
{list: ['SETTINGS','ACCESSIBILITY_SETTINGS','ACTION_CONDITION_PROVIDER_SETTINGS', ..., 'ZEN_MODE_PRIORITY_SETTINGS']}
>> device.sendAai({action:"listAndroidSetting"}).list.length
70


setConfig、getConfig、unsetConfig

更改FindNode(或Selector)的值,各种值通过名称来标识,使用"getConfig"获取值,“setConfig"更改值,“unsetConfig"恢复原始值。

getConfig(<名称>) → 名称:<名称>,值:<值>

setConfig(<名称>, <值>) → retval: 真

unsetConfig(<名称>) → retval: 真

如果找不到该名称,它将返回 null。

名称 类型 默认 描述
text:maxQueryLength 整数 30 文本长度和描述限制用“*”代替。
navi:maxPageScroll 整数 30 查找节点的最大页面滑动次数
navi:scrollVertSteps 整数 40 页面滑动步数,低值快速不准确,高值慢而准确
navi:slowScrollSteps 整数 100 使节点完全可见的滑动步数
sigmaInput:backKeyAutoDismiss 布尔值 true Sigma 输入法,发送额外的返回键以关闭隐形键盘。
selector:defaultTemplate 字符串 more 如果查询中未指定模板,则使用默认模板。
selector:allowBqAfterEq 布尔值 false 如果设置为 true,将允许在扩展查询之后定义的基本查询将作为扩展查询的一部分按顺序(从左到右)执行。

命令参考

FindNode 命令参考
版本 14
动作名称 OQ 参数 输入 输出 ML变化 属性
获取命令
getBounds 是的 - 所有节点 ids:[<id>], bounds:[<bounds>] - -
getBoolProp 是的 - 第一个节点 重新评估:[<prop>] - -
getBoolPropInAncestors 是的 - 第一个节点 retval:[<prop>] - -
getChecked - 一个节点
所有节点
retval:<boolean>
retval:[布尔值|N/A]
- -
getCount - 所有节点 count:<计数> - -
getDescription - 一个节点
所有节点
返回值:<文本>
返回值:[<文本>]
- -
getHintText Yes - One Node
All Nodes
retval:<text>
retval:[<text>]
- -
getFocus - node:<ID> - -
getIds - 所有节点 ids:[<编号>] - -
getNodes [字段列表(S)] 所有节点 列表:[<信息>] - -
getPackageName - retval:<PackageName> - -
getProgress - 第一个节点 retval:<范围信息> - -
getQuery [ignoreText(B)]
[ignoreText(B)]
第一个节点 查询:<查询字符串> - -
getText - 一个节点
所有节点
返回值:<文本>
返回值:[<文本>]
- -
getUniqQuery [ignoreText(B)]
[IgnoreDesc(B)]
第一个节点 查询:<查询字符串> - -
查询命令
save/push - 所有节点 retval:<布尔值> - -
load/pop - 没有任何 retval:<布尔值> -
getViewGroup 是的【一级(一)】 第一个节点 计数:<计数> 是的 VG
intersectX 是的 [前Q(S)] [后Q(S)] 第一个节点 计数:<计数> 是的 TX
intersectY是的 [前Q(S)] [后Q(S)]第一个节点计数:<计数>是的 TY
reduceNodes-所有节点 计数:<计数>是的RN
sort<类型(S)>所有节点retval:<布尔值>是的ST
addQuery 是的 - 所有节点 计数:<计数> 是的 -
newQuery是的-没有任何计数:<计数> 是的 -
waitQuery是的[持续时间(I)] 没有任何retval:<布尔值>--
动作指令
click是的 -第一个节点retval:<布尔值>--
click2是的-第一个节点retval:<布尔值>--
forEachNo<action|JSON name>All Nodesretval:<name>:[ [...]]--
longClick是的-第一个节点retval:<布尔值>--
nsClick是的 -第一个节点retval:<布尔值>--
repeatNo<count, action|JSON>All Nodes retval:<name>:[[...]]--
refresh是的-所有节点 retval:<布尔值>--
scrollIntoView是的[方向(S)]没有任何retval:<布尔值>Yes-
sendKey<代码(I)> [元(I)]没有任何retval:<布尔值>--
setChecked是的<布尔值>所有节点 更改计数:<计数>--
setProgress是的<值(F/I)>第一个节点retval:<布尔值>--
setText是的<文本(S)>第一个节点retval:<布尔值>--
showOnScreen是的-第一个节点retval:<布尔值>--
sleep<ms(I) 中的持续时间>没有任何retval:<布尔值>--
until是的<选项> <参数>没有任何retval:<布尔值>--
其他命令
version-没有任何retval:<版本号>--
openApp<名字>(S)>没有任何retval:<布尔值>--
restartApp<名字>(S)>没有任何retval:<布尔值>--
closeApp[<名字>(S)>]没有任何 retval:<布尔值>--
openAndroidSetting<设置(S)>没有任何retval:<布尔值>--
listAndroidSettings-没有任何列表:[<设置>]--
getConfig<姓名>没有任何名称:<名称>,值:<值>--
setConfig<名称(S)> <值(*)>没有任何retval:<布尔值>--
unsetConfig<姓名>没有任何retval:<布尔值>--
(S) - 字符串,(I) - 整数,(B) - 布尔值,(F) - 浮点数,(*) - 所有类型
[可选参数] 或 <name>:[<return array>]
device/devices.sendAai({query:<query>, action:<command>})
device/devices.sendAai({query:<query>, actions:[<command>]})

支持

请发送电子邮件至support@sigma-rt.com以获得支持和反馈。