FindNode
FindNode 是核心 Selector 包之上的一个 shell 程序,FindNode 的目的是找到预期的一个或多个 UI 元素(或辅助功能节点)并提取信息或对其执行操作。简而言之,AAI 用查询替换坐标。
FindNode 是 AAI 项目(Accessibility and Automation Integration)的一部分,它由以下组件组成:
Selector 位于 Accessibility 和 UI Automator 之上,每个 UI 元素或 UI 元素的容器由节点或节点集标识,每个节点都有其在当前屏幕上唯一的 ID。选择器提供了多种方式来搜索一个或多个节点。获取节点后,您可以做一些有趣的事情,例如获取文本/图像或执行诸如单击按钮或在文本字段中输入文本等操作,所有这些都无需坐标。这允许一个自动化脚本在不同的分辨率上运行,而使用坐标是不可能的。坐标是在运行时获取的。
例如:
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 对象包含 3 种类型的属性:query、preAction 和 action(s)。提供的命令非常丰富,你可以用它们编写一个简单的自动化。
大概你可以通过使用"device.sendAai()”和"devices.sendAai()”来完成大部分任务,FindNode是Appium的"处理程序”,另一个处理程序"invoke"可以使用Java反射来访问UiDevice,UiObject2中的方法, AccessibilityNodeInfo 和 InteractionController。
限制条件
这是 AAI 的第 2 版,它有一些限制:
查询+动作
使用我们的查询语言在屏幕上查找节点并对找到的节点应用操作。我们将在本手册中介绍查询和操作。
例如:单击带有"OK"标签的按钮:
query:"T:OK", action:"click"
如果名称在"Name"标签旁边,则获取名称
query:"T:Name&&OX:1", action:"getText"
获取所有股票代码:
query:"R:.ticker", action:"getText"
查询
大多数 FindNode 用法与查询有关,这是 FindNode 的核心部分(也是 AAI 的一部分)。它允许用户通过辅助功能节点上不同类型的有用查询来定位预期的 UI 元素。整个屏幕由很多节点组成,一个节点可以是最小的UI元素,也可以是更小节点的容器,很多是不可见的。整个屏幕是从单个根节点开始的树结构。将执行查询以获取符合条件的节点,目的是将大量节点减少到一个或几个预期节点,用户可以获取信息或对节点进行操作。
我们开发了一种小型查询语言,它可以在设备和脚本之间移植,并且功能强大到足以定位大多数节点。格式为 JSON:
{query:"<key>:<value>&&<key>:<value>&&<key>&&<key>…"}
我们接受“||” 和“&&”作为相同的分隔符,以后可以更改。“&&”因为查询是AND关系,这意味着查询节点需要满足所有条件才能成为"匹配节点”。FindNode 不提供 OR 关系,但底层的 Selector 包提供了该功能。“模板"使用该构造。对于某些键,不需要值。
查询字符串分为 3 个部分:模板、基本查询和扩展查询。“模板”(T)将生成节点,“基本查询”(BQ)将从可访问性节点一次执行一个查询,“扩展查询”(EQ)将从 T 和 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'}
>> device.sendAai({query:"TP:all&&T:Main storage&&VG:2&&XT:Audio&&OX:1", action:"getText"})
{retval: '77 MB'}
解析器会将查询分成 TP、BQ 和 EQ 并相应地执行它们:
模板 (TP)
模板查询(由于缺少更好的名称)是查询所必需的键,它生成用于预期目的的节点。缺少模板将默认为"TP:more”,不允许使用多个模板,这里是不同的模板:
基本查询 (BQ)
基本查询是纯粹基于单个节点信息的查询。BQ不是必需的,如果没有定义BQ,它会将模板中的所有节点传递给EQ。
以下是匹配从节点本身获取的信息的基本查询:
由于查询始终在现有屏幕上执行,因此不应使用"P”。“(S)”代表字符串,“I"代表整数,“[x, y]”数组或可选字段。I/S 可以接受整数和字符串。字符串更强大。
键不区分大小写,我们使用大写来区分键和值。
扩展查询 (EQ)
下面是扩展查询的key,一般作用于BQ或者template返回的多个节点。扩展查询从左到右工作,同一个键可以多次应用。情商是可选的。以下是 EQ 的按键:
所有这些查询都是可选的,如果您在屏幕上进行查询,您可以选择任何字段。“(S)”代表字符串,“I"代表整数,“[x, y]”数组。I/S 可以接受整数和字符串。字符串更强大。
键不区分大小写,我们使用大写来区分键和值。
匹配列表 (ML)
查询完成后,将匹配到的节点存入匹配列表(Matching List,ML)中,ML中的节点由节点ID标识。许多动作使用 ML 中的节点,一些动作可以改变 ML。“save"和"load"可以保存和检索ML。使用 action:"getIds" 获取 ML ID。TX/TY/VG 取一个节点生成多个节点。
另一种获取 ML 的方法是使用"元素"数组。
“元素"属性
“elements"接受一个节点ID数组,节点ID必须是屏幕上有效的节点ID,否则会产生错误。当指定"elements"时,将不会执行搜索。在特定节点或调试目的上应用操作很有用。每个带有"元素"的查询都会遍历所有节点来定位对应的节点,尝试将多个 ID 放入数组中,而不是一次一个。
device.sendAai({elements:["1234a", "5678b"], action:"getNodes"})
var ids = device.sendAai({query:"C:.Button"}).ids;
var retval = device.sendAai({elements:ids, action:"getNodes"});
To retrieve one node, can use "ID:<node ID>".
device.sendAai({query:"ID:1234a", action:"getNodes"})
弹出窗口
有两种类型的弹出窗口,它们看起来相同但实现方式不同:
对于第二种弹窗,FindNode可以识别出部分弹窗(很多谷歌应用都使用这种弹窗),如果弹窗打断了FindNode,它会表现出以下行为之一:
发生这种情况时,关闭(从内存中删除)并重新打开应用程序,如果弹出窗口不起作用,请联系支持人员。
测试
通过查询查找节点不是很困难,您需要的是"UI Explorer"和终端:
模板详细介绍
模板(TP)为必填字段,TP根据值返回列表节点,是BQ、EQ或动作的初始节点列表。TP 是 Selector 的子类,将来可能允许用户定义新的模板。TP中的几个规则:
当前支持的模板:
TP:all,more,basic,reduced
这些模板遍历整个屏幕中的节点:
UI Explorer:3个选择:
例子:
第一个不带参数的getCount,将获取"ML"的计数,不带"query”,默认为"TP:more”,因此它与"TP:more"的计数相同。"+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>
返回文本或描述中与指定文本匹配的内容的节点。文本参数是必需的,否则会产生错误。
例子:
>> 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']}
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']}
找到文本字段:
最后一个文本字段:查询:“BP:editable&&IX:-1”
Based on the hint: query:"TP:textInput&&T: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(My Name)"})
{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 作为文本。
>> device.sendAai({query:"TP:line,top,1"})
{count: 6, ids: ['12b9f4','12ccb9','12e33f','1d7d52','12f9c5','1317cd']}
>> device.sendAai({query:"TP:line,bottom,-1"})
{count: 5, ids: ['1ea5e3','1ea9a4','1ead65','1ebc69','1ed2ef']}
TP:scrollable[,<位置>]
此模板返回指示的可滚动项的叶节点。“位置"是从0开始的数字,从上到下。在可滚动节点中使用资源 ID 可以获得更好的结果。
例子:
获取当前屏幕所有可滚动节点:
>> device.sendAai({query:"BP:scrollable"})
{count: 5, ids: ['1e434','5bbba','5bf7b','5d9c2','6f70e']}
>> 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']}]}
例子:
>> device.sendAai({query:"TP:textInput"})
{count: 6, ids: ['15cac','1642e','16f71','176f3','18236','189b8']}
>> device.sendAai({query:"TP:textInput&&IX:-1", action:"setText(John)"})
{retval: true}
Alternatively, you can use hint text to search for node:
>> device.sendAai({query:"T:Person Name", action:"setText(John)"})
{retval: true}
{query:"TP:findText,OK", action:"click"}
// Obtain stock symbols
>> device.sendAai({query:"TP:findText,/^[A-Z]{1,4}$/", action:"getText"})
{retval: ['TGT','INTC','WMT','T','AAPL','SBUX','TSLA']}
基本查询详细介绍
基本查询从 TP 中获取 ML 并执行节点级匹配,一次一个节点并创建一个更短的 ML,丢弃不匹配的节点。BQ 从 Accessibility 中提取数据,它包含单节点信息和动作。在查询中扩展更多功能以实现更强大的匹配(特殊字符)并添加快捷方式使查询更简洁。
查询中的特殊字符
对于 inputType 或 childCount 等数字,它可以接受数字或字符串:
字符串可以是以下之一,“\n"匹配换行符:
“!” 在字符串的开头用作 NOT。"!0" = 非零。“C:!/Layout/”,非布局类。
为了简单起见,{"cmd": "action","action": "findnode", "params":{…}} 的标准前缀将被省略,我们将在 "param" 对象中列出示例并使用 JavaScript 对象符号来减少双引号的数量。
简称
两个快捷方式可用于类名 (C) 和资源 ID (R),前缀为“.”用于替换常用前缀:
这 ”。” 符号只能用于普通匹配和“!” 匹配,其他类型的匹配(例如正则表达式和通配符)需要使用完整形式。
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"坐标的节点不适用于所有设备。
例子:
{query:"BI:[-1, 1000]&&IX:-1", action:"click"}
{query:"BI:[0, 0, 1000, 1000]", action:"getBounds"}
CC:<子计数>, IT:<输入类型>
这些是在节点中找到的属性,很少使用。CC 是孩子的数量,输入类型通常是数字文本字段。
BP:<属性>
布尔属性在节点中很重要,这些属性带有 true 或 false 值。允许多个值,不支持“!” 因为不是在这个时候。以下是属性:
基本查询示例
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个选项可用。目前只有有限的选择,将来会扩展。
这些是选项:
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% 的布局(“setConfig(selector:viewGroupWidthRatio, <percentage>)”)或类中的"ViewGroup”。ML 将包含组内的节点,第一个节点是 ViewGroup/Layout 节点本身。对感兴趣的节点进行逻辑分组并使用"PQ"或"addQuery"搜索组内的节点非常有用。
相关操作:“getViewGroup”。
例子:
选择正确的级别:
>> device.sendAai({query:"T:Stop Charging&&VG", action:"getIds"})
{count: 2, ids: ['4a1fb','4a5bc']}
>> device.sendAai({query:"T:Stop Charging&&VG:2", action:"getIds"})
{count: 20, ids: ['45326','456e7','45aa8','45e69','4622a','465eb','4712e','469ac','46d6d','474ef','478b0','47c71','487b4','48b75','49e3a','4a1fb','4a5bc','4a97d','4ad3e','4b0ff']
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中基本查询的功能,那么就可以进行文本匹配。有两种方法可以做到:
两者都有效:
>> 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'}
动作 Actions
FindNode 提供了许多操作命令,“action"可以接受单个字符串命令或包含多个命令的字符串数组。查询是可选的,如果未指定查询,它将包含默认模板。如果指定查询,查询输出将保存到 ML,命令/操作需要节点将通过查询或可选查询从底层 ML 获取,使用 action:"getIds" 获取 ML 内容。如果未指定操作,则使用操作:“getIds”。“action"和"actions"都可以。
device/devices.sendAai({query:"...", action:<action>})
device/devices.sendAai({query:"...", action:[<action 1>, <action 2>, …]})
device/devices.sendAai({action:[<action 1>, <action 2>, …]})
当 sendAai 遇到来自 FindNode 的错误或超时时,它将返回 null 并带有包含错误消息的"lastError()”。
{action:"openApp('Skype')"}
由于"Skype"是字符串,引号可以省略:
{action:"openApp(Skype)"}
对于需要一个节点的动作(例如"点击”),将选择 ML 中的第一个节点(ON、IX、OX 和 OY 可以更改)。action中有很多命令,新版本会进行改进,可能会引入不兼容,请使用preAction:"version"(11版本)或action:"version"(12+版本)确保版本支持所需的命令。一些命令需要额外的参数(参数将包含在括号中)。目前,支持以下类型的参数:
几个例子:
action:“getNodes('C,R')”
如果要在操作命令中使用变量,请使用以下 4 个选项之一:
var input = "Hello"
>> "setText('" + input + "')"
setText('Hello')
>> 'setText("' + input + '")'
setText("Hello")
>> 'setText(' + input + ')'
setText(Hello)
>> aaix("setText", input)
setText("Hello")
action:[aaix("setText", input), "addQuery('OX:2')", "click"]
如果参数是字符串,以下 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 提供了几种执行此类检查的方法:
可选查询 (OQ)
Actions require nodes将从ML获取,actions require one node,将从ML获取第一个node。但是,用户可以通过在操作中添加选项查询 (OQ) 来更改行为。
例子:
{query:"T:OK", action:"click"} → {action:"click(T:OK)"}
所有 OQ 都是可选的,如果未指定 OQ,则操作将从 ML 中获取节点。如果动作需要一个节点(例如单击),将使用 ML 中的第一个节点,如果动作支持多个节点(例如 getNodes 或 getText),则使用整个 ML。
OQ 是一种常规查询语言,不同的类型由不同的前缀标识,OQ 和其他查询一样可以生成一个或多个节点,一对查询类型会修改 ML。以下是支持的 3 个前缀:
假设计算器中的数字 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
get 命令
获取边界 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(O|M)
“getNodes(T)”或"getNodes(D)”的简单版本,如果您不想知道节点 ID,则更容易解析。它将检测一个或多个节点。getDescription/getText 是相同的,除了一个获取文本,另一个获取描述。
用法:
getDescription [(<查询>)]
getText [(<查询>)]
返回:
如果找到多个节点,则返回 retval 中的文本/描述数组,如果找到一个节点,则返回 retval 中的文本/描述。
>> device.sendAai({query:"LB:-1", postAction:"getText"})
{retval: ['Chats','Calls','Contacts','Notifications']}
>> device.sendAai({query:"LB:2||IX:1", postAction:"getText"})
{retval: 'Calls'}
获取焦点 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 有效的字段标识符是:
返回:
节点信息的计数和数组。
>> 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/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。
加载load/保存save (M)
这些命令存储或检索 ML,这仅对多个命令有用,保存 ML,newQuery + 操作,加载并继续。
用法:
load
save
相交 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>[,
返回:
newQuery 和 addQuery 在"count"中返回结果 ML 的大小。当超时到期时,waitQuery 将返回 null,否则"retval"将包含 true。
动作指令
本节中的命令对节点执行操作。
click、nsClick、click2、longClick
节点上的各种点击,这是同步点击,将确保在返回控件时屏幕开始刷新。
以上所有的点击都会在点击执行后监听屏幕变化的事件,如果屏幕没有变化,这些点击都会失败,最终超时。如果执行点击后屏幕没有改变,使用"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}
刷新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'}]}
滚动到视图 scrollIntoView
接受查询字符串和滚动方向,将通过向上/向下滚动尝试匹配查询,当找到一个或多个节点时,它会返回,如果找不到匹配,则返回 null 并出错。滚动将使用向上/向下翻页,最多 10 页,滚动可能需要一些时间,此命令会自动延长"sendAai"中的超时时间,直到完成。为了使脚本能够在任何分辨率和大小下运行,使用 scrollIntoView 比 PageUp/Down 更好。
用法:
scrollIntoView(<query>) // 默认向下
scrollIntoView(<query>, <方向>)
参数:
<direction>可以是以下之一,如果不指定,默认为"向下”。
“down”:从当前位置开始,向下滚动搜索。
“up”:从当前位置开始向上滚动搜索。
“top”:快速滚动到页面顶部,向下滚动搜索。
"bottom": 快速滚动到页面底部,向上滚动搜索。
返回:
如果找不到查询将返回 null 否则将返回 true,ML 将设置为第一个匹配的节点 ID。
发送键码 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)”更改行为。
返回:
以 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 输入法。
用法:
setText([查询], <输入字符串>)
setText([查询], +<输入字符串>)
参数:
输入字符串:默认情况下,在输入字符串之前会清除文本字段,如果第一个字母是“+”,则会添加到现有字符串上。对于多行文本字段,使用“\n"跳到下一行。
返回:
true 或 false 以指示操作是否成功。
例子:
>> device.sendAai({query:"TP:textInput&&IX:1", action:"setText('Hello')"})
{count: 1, retval: true}
>> device.sendAai({query:"TP:textInput&&IX:0", action: "setText('+ World')"})
{count: 1, 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 表示节点已经在屏幕上完全可见,什么都不做。
休眠 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 | 如果查询中未指定模板,则使用默认模板。 |
命令参考
FindNode 命令参考 |
---|
动作名称 | OQ | 参数 | 输入 | 输出 | ML变化 | 属性 |
---|---|---|---|---|---|---|
获取命令 | ||||||
getBounds | 是的 | - | 所有节点 | ids:[<id>], bounds:[<bounds>] | - | - |
getBoolProp | 是的 | - | 第一个节点 | 重新评估:[<prop>] | - | - |
getBoolPropInAncestors | 是的 | - | 第一个节点 | retval:[<prop>] | - | - |
getChecked | 否 | - | 一个节点 所有节点 |
retval:<boolean> retval:[布尔值|N/A] |
- | - |
getCount | 是 | - | 所有节点 | count:<计数> | - | - |
getDescription | 是 | - | 一个节点 所有节点 |
返回值:<文本> 返回值:[<文本>] |
- | - |
getFocus | 否 | - | 无 | node:<ID> | - | - |
getIds | 是 | - | 所有节点 | ids:[<编号>] | - | - |
getNodes | 是 | [字段列表(S)] | 所有节点 | 列表:[<信息>] | - | - |
getPackageName | 否 | - | 否 | retval:<PackageName> | - | - |
getProgress | 是 | - | 第一个节点 | retval:<范围信息> | - | - |
getQuery | 否 | [igText(B)] [IgDesc(B)] |
第一个节点 | 查询:<查询字符串> | - | - |
getText | 是 | - | 一个节点 所有节点 |
返回值:<文本> 返回值:[<文本>] |
- | - |
getUniqQuery | 否 | [igText(B)] [IgDesc(B)] |
第一个节点 | 查询:<查询字符串> | - | - |
查询命令 | ||||||
save | 否 | - | 所有节点 | retval:<布尔值> | - | - |
load | 否 | - | 没有任何 | 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:<布尔值> | - | - |
longClick | 是的 | - | 第一个节点 | retval:<布尔值> | - | - |
nsClick | 是的 | - | 第一个节点 | retval:<布尔值> | - | - |
refresh | 是的 | - | 所有节点 | retval:<布尔值> | - | - |
scrollIntoView | 是的 | [方向(S)] | 没有任何 | retval:<布尔值> | - | - |
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以获得支持和反馈。