FindNode
FindNode 是一个位于核心 Selector 包之上的外壳程序,其目的是找到预期的一个或多个 UI 元素(或可访问性节点),并从中提取信息或执行操作。
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 对象包含 3 种类型的属性:query、preAction 和 postAction(s)。提供的命令非常丰富,您可以使用它们编写简单的自动化脚本。
据推测,您可以通过使用 "device.sendAai()" 和 "devices.sendAai()" 来完成大部分任务,FindNode 是一个 "handler",另一个 "invoke" handler 可以使用 Java Reflection 访问 UiDevice、UiObject2、AccessibilityNodeInfo 和 InteractionController 中的方法。
限制:这是 AAI 的第一版,它具有一些限制:
查询
大多数 FindNode 的用法涉及查询,它允许用户通过对可访问性节点进行不同类型的有用查询来定位预期的 UI 元素。整个屏幕由许多节点组成,一个节点可以是最小的 UI 元素或包含较小节点的容器,其中许多是不可见的。整个屏幕是从单个根节点开始的树状结构。查询将被执行以获取满足条件的节点,目的是将大量节点减少到一个或少数几个预期的节点,用户可以获取信息或对节点应用操作。
我们开发了一种小型的查询语言(为缺乏更好的词而设),它在设备之间具有可移植性,并且足够强大,可以定位大多数节点。格式为 JSON:
{query:"<key>:<value>||<key>:<value>||…"}
出于历史原因,尽管我们使用 "||" 来分隔不同的条件,但 "||" 是 AND 关系,这意味着它需要满足所有条件才能成为 "匹配节点"。FindNode 不提供 OR 关系,但底层的 Selector 包提供了这种能力。"templates" 使用了该结构。
查询字符串
有 14 个不同的键,它们是:
所有这些查询都是可选的,您可以选择任何字段,如果您正在对屏幕进行查询。"(S)" 代表字符串,"I" 代表整数,"[x, y]" 代表数组。I/S 可以接受整数和字符串。字符串更强大。
键不区分大小写,我们使用大写来区分键和值。
查询中的特殊字符
对于像 inputType 或 childCount 这样的数字,它可以接受数字或字符串:
字符串可以是以下之一:
"!" 在字符串开头用作 NOT。"!0" 表示非零。"C:!/Layout/" 表示非布局类。
为了简化示例,标准前缀 {"cmd": "action","action": "findnode", "params":{…}} 将被省略,我们将在 "param" 对象中列出示例,并使用 JavaScript 对象表示法来减少双引号的数量。
快捷方式
有两个快捷方式可用于类名 (C) 和资源 ID (R),使用 "." 作为常用前缀的替代:
Bound In (BI):
Bound In 接受屏幕坐标或边界,并根据以下条件返回节点。主要用于屏幕操作,如 UI Explorer 或调试目的。
最后两种模式在 8.0-u40 中受支持。
示例:
{query:"BI:[-1, 1000]||IX:-1", postAction:"click"}
{query:"BI:[0, 0, 1000, 1000]", postAction:"getBounds"}
Index (IX), Offset (OX/OY) and Line (LT & LB)
这些功能是非常有用的:
Index:IX:<number>。.根据从零开始的位置,从生成的节点列表中返回节点。IX可以是负值,在这种情况下,位置顺序相反(-1是最后一个节点)。
Offset:OX:<integer> 和 OY:<integer>。使用偏移量根据结果节点查找相邻节点,OX查找水平位置,正整数向右移动,负整数向左移动。OY同理,正整数向下移动,负整数向上移动。如果两个选项都存在,则这些选项将根据查询上的位置应用。例如:“OX:1 | | OY:2”将在垂直偏移之前应用水平偏移,其中“OY:2 | | OX:1”将在水平偏移之前应用垂直偏移。顺序对于定位节点的不对称排列非常重要。
Line:LT:<Integer> 和 LB:<Integer>。对于使用行模式的查询,FindNode将分析屏幕,尝试定位可滚动区域之外的UI元素,行模式将UI元素分组为一系列行,每行可以有一个或多个节点。许多应用程序在屏幕的顶部和底部都有固定的UI元素,可以通过使用诸如“LT:1 | | IX:2”(顶行的第二个节点)或“LB:1 | | T:Chats”等查询来轻松定位,并将"Chats"作为text。
这可能导致复杂的查询,例如
{query:"LB:-1||IX:2||OX:1||OY:-1"}
对于行模式,第一个节点的“intersectY”应用于每条直线,通常可以,但在以下情况下,它将返回所有节点:
所以“LT:1”将返回所有 6 个节点。
对于不可滚动的应用程序,“LB”将返回 null,但获取节点并不难:
您可以使用文本作为锚点:
>> device.sendAai({query:"T:Speed||ON:last", postActions:["intersectX", "getText"]}) {count: 2, list: [{count: 4},{retval: ['Speed','Video','VPN','Map']}]} >> device.sendAai({query:"T:Speed||ON:last||OY:-1", postActions:["intersectX", "getIds"]}) {count: 2, list: [{count: 4},{count: 4, ids: ['181ce','19c15','1b65c','1d0a3']}]}
Template (TP)
由于没有更好的名称,我们开发了几个预定义的模板,这些模板是内置的FindNode,默认模板是“more”。
例子:
{query:"TP:findText", text:"OK", postAction:"click"}
单节点选择 (ON)
有些查询需要多个节点(例如检索股票报价),有些时候需要单个节点,例如,单击节点或输入文本,FindNode提供“IX”从特定位置选择节点,ON提供多种方法选择出一个节点。
这里有几种选项:
ON 和 IX 不可以同时使用。
Query 执行的先后顺序
下面是query执行的先后顺序:
“elements”属性
“elements”接受节点ID数组,节点ID必须是屏幕上显示的有效节点ID,否则将生成错误。当指定“elements”时,将不执行搜索,但IX、OX、OY、ON仍能工作。它对于调试目的以及对同一节点应用多个操作的脚本非常有用。每个带有“elements”的查询将遍历所有节点以定位相应的节点,尝试在数组中放置多个ID,而不是一次一个。
device.sendAai({elements:["1234a", "5678b"], postAction:"getNodes"})
var ids = device.sendAai({query:"C:.Button"}).ids;
var retval = device.sendAai({elements:ids, postAction:"getNodes"});
同步动作
为了保证actions的同步已经付出了很大的努力,当一个“click”被调用的时候,不仅保证click被同步按下,还需要等待一些事件来确保按下事件真的执行成功了。 FindNode 提供了几种执行点击的方法:
弹出窗口
有两种类型的弹出窗口,它们看起来相同,但实现方式不同:
对于第二种弹窗,FindNode 可以识别一些弹窗(很多谷歌应用程序都使用这种弹窗),如果弹窗破坏了 FindNode,它将表现出以下行为之一:
发生这种情况时,关闭(从内存中删除)并重新打开应用程序,如果弹出窗口不起作用,请联系支持。
这个版本,水平滚动(左右滑动显示新页面)将不起作用(例如Slack),它将在下一个版本中提供。
测试
通过 Query 找到节点并不是很困难,你需要的是“UI Explorer”和脚本执行终端:
Query 实战1
在TC中,用的最多的query为{}:
var output = device.sendAai({})
这将返回屏幕上的大多数节点,默认返回的是节点ID列表:
{count: 89, ids: ['80006cbe','7440','7bc2','7f83','8344','8705','8ac6','8e87','1f6e7',…]}
你还可以做其他的事情,就像下面的这样:
{query:"CC:!=0"} 或 {query:"CC:>0}:子节点个数大于0的nodes。
{query:"IT:>10000"},inputType大于10000的nodes。
{query:"CC:!=0||IT:>10000"},子节点个数大于0 并且 inputType大于10000的nodes。
{query:"TP:textInput||IX:2", postAction:"setText", input:"Hello"},找到第三个输入框并在输入“Hello”。
{query:"T:Input text here", postAction:"setText", input:"Hello"},找到里面默认值为“Input text here”的输入框:输入框并在输入“Hello”。
{query:“C:.TextView||T: 联系人”} 点击类名为TextView,并且text字段为”Contacts“
{elements:["abcd", "123e", "234f"], query:"IX:1||OY:1", postAction:"click"}
Query 实战2
示例 1:在屏幕底部查找节点:
>> device.sendAai({query:"LB:-1", postAction:"getText"})
{retval: ['Chats','Calls','Contacts','Notifications']}
通过使用偏移量 (OX)、索引 (IX) 或诸如“T:”之类的查询,您几乎可以定位每个节点,例如点击“Contacts”图标,您可以通过下面的 3 个示例都可以达到目的:
>> device.sendAai({query:"LB:1||IX:2", postAction:"click"})
{retval: true}
>> device.sendAai({query:"LB:-1||T:Contacts||OY:-1", postAction:"click"})
{retval: true}
>> device.sendAai({query:"LB:-1||IX:0||OX:2||OY:-1", postAction:"click"})
{retval: true}
行模式的目的是控制顶部/底部的图标。与“Charts”图标相交的红色计数器将被排除在外。所以对于图标行,它是 4 个节点而不是 5 个节点:
>> device.sendAai({query:"LB:1"})
{count: 4, ids: ['14735','17080','18ac7','1a50e']}
如果您想要得到计数器上面的数字,请使用其他查询(IX:-1 是为了防止在屏幕上找到其他“聊天”),底部的第二行将起作用,因为偏移量不会忽略相交的节点:
>> device.sendAai({query:"T:Chats||IX:-1||OY:-1||OX:1", postAction:"getText"})
{retval: '1'}
>> device.sendAai({query:"LB:-1||T:Chats||OY:-1||OX:1", postAction:"getText"})
{retval: '1'}
示例 2:从 关于中获取信息:
获取信息:
>> device.sendAai({query:"T:Model name||OX:1", postAction:"getText"})
{retval: 'Galaxy S10+'}
或者,您可以使用 UiElement 类:
>> var obj = UiElement.findObject(device, "T:Model name||OX:1")
UiElement: 3d012
>> obj.getText()
Galaxy S10+
示例 3:假设您想从 Yahoo Finance 获取股票代码的首页:
device.sendAai({query:"R:.ticker", fields:"T", postAction:"getNodes"}).list.forEach(
function(p) {
print(p.text + ":" +
device.sendAai({query:"OX:1||T:" + p.text, fields:"D",
postAction:"getNodes"}).list[0].description);
})
INTC:53.14
WMT:142.00
XOM:65.93
BIDU:146.53
PEP:173.23
示例 4:在 Skype 文本框中输入文本:
输入文字前:
输入文字后:
这可以通过一条命令来完成所有操作:
var input = "Hello";
device.sendAai({query:"LB:1||TP:textInput", postActions:[aaix("setText", input), "addQuery('OX:2')", "click"]});
aaix("setText", input) 将返回 'setText("Hello")',比 'setText("'+input+'")' 好得多,对多个参数很有用。
下面的函数找人,点击,发消息,返回。 需要两个“返回”键,第一个返回键是关闭键盘,第二个返回键是返回主页面。
function sendAai(device, obj) {
var retval = device.sendAai(obj);
if (retval == null) {
throw "Error on: " + JSON.stringify(obj) + ":" + lastError();
}
print("$ " + JSON.stringify(obj) + "\n" + JSON.stringify(retval));
return retval;
}
function sendSkype(device, name, text) {
sendAai(device, {query:"T:" + name, preAction:"scrollToView", postAction:"click"});
sendAai(device, {query:"TP:textInput", postActions:[aaix("setText", text), "addQuery('OX:2')", "click", "sendKey('back')", "sendKey('back')"]})
}
sendSkype(device, "John", "I will get back to you");
示例 5:在第一个示例中,您可以使用 "T:<text>" 和偏移量来单击图标,某些应用程序不提供文本:
由于这是显示在最后一行,我们可以使用“LB:-1”返回节点,然后使用“IX”从节点列表中定位单个节点,点击搜索图标:
device.sendAai({query:"LB:-1||IX:2", postAction:"click"})
示例 6:在某个聊天窗口中,它显示了姓名(或电话号码)。
>> device.sendAai({query:"LT:1"})
{ids: ['1637ca','163f4c','165d54','166115','1664d6']}
返回:
>> device.sendAai({query:"LT:1||IX:0", postAction:"click"})
{retval: true}
获取名字:
>> device.sendAai({query:"LT:1||IX:1", postAction:"getText"})
{retval: '86278'}
示例 7:轻松找到并单击。
要单击箭头,请使用:
>> device.sendAai({query:"T:Channels||OX:1", postAction:"click"})
{retval: true}
Query 实战3
示例 1:考虑 UI Explorer 中的以下计算器:
使用这个计算器可以很简单:
function calc(device, app, resultQuery, formula) {
device.runAppSync(app);
formula.split("").forEach((n)=>device.clickSync(n));
return device.aaiGetText(resultQuery)[0].text;
}
var result = calc(
Device.getMain(),
"com.dalviksoft.calculator",
"R:*result",
"1230÷567="
);
print(result);
这将为您提供 1230÷567 的结果。
要检索所有数字按钮:
>> device.sendAai({query:"T:/[0-9]/||C:.Button", preAction:"doSort", postAction:"getText"})
{retval: ['7','8','9','4','5','6','1','2','3','0']}
检索数字 5-9,排序基于节点的位置:
>> device.sendAai({query:"T:/[5-9]/||C:.Button", postActions:["sort","getText"]})
{count: 2, list: [{retval: true},{retval: ['7','8','9','5','6']}]}
要检索算术运算 + DEL 按钮等 5 个按钮,有几种方法可以做到,一种方法是使用 x 上的边界:
function getNodes() {
var ret = device.sendAai({query:"T:DEL||C:.Button", postAction:"getNodes", fields:"B"});
if (ret == null) {
print("Error:" + lastError());
return null;
}
var rect = new Rect(ret.list[0].bounds);
var ret = device.sendAai({query:"BI:[" + rect.centerX() + ",-1]||C:.Button"})
if (ret == null) {
print("Error:" + lastError());
return null;
}
return ret.ids;
}
这里可以手动进行2+3的操作,不要每次都搜索节省CPU,使用"elements","="没有字符串表示,所以使用OX:1 from "0"。
var opIds = getNodes();
var numIds = device.sendAai({query:"T:/[0-9]/||C:.Button", preAction: "doSort"}).ids;
device.sendAai({elements:numIds, query:"IX:-2", postAction:"click"});
device.sendAai({elements:opIds, query:"IX:2", postAction:"click"});
device.sendAai({elements:numIds, query:"IX:8", postAction:"click"});
device.sendAai({query:"T:0||C:.Button||OX:1", postAction:"click"});
或者,您可以在文本字段中输入文本。可以使用 UiElement 来做到这一点:
var obj = UiElement.findObject(device, "TP:textInput");
var equal = UiElement.findObject(device, "T:0||C:.Button||OX:1");
obj.setText("2+3");
equal.clickSync();
var result = obj.getText();
print(result);
示例 2:
使用“选中”来操作上述复选框。
将 Sunday 从 on 改为 off,查看前后的值:
>> device.sendAai({query:"D:Sunday||C:.CheckBox", postActions:["getChecked", "setChecked(false)", "getChecked"]})
{count: 3, list: [{retval: true},{checked: false, retval: true},{retval: false}]}
"intersectX" 将返回 Y 坐标与匹配节点相交的所有节点,在这种情况下为所有复选框。使用“setChecked()”设置所有节点为真/假(开/关),它只适用于可检查的节点,没有可检查的节点将被忽略。如果需要更改值,请仅单击复选框。
>> device.sendAai({query:"D:Sunday", postActions:["intersectX","getIds","setChecked(true)"]})
{count: 3, list: [{count: 7},{count: 7, ids: ['9c52d','9c8ee','9ccaf','9d070','9d431','9d7f2','9dbb3']},{changedCount: 7, retval: true}]}
示例 3:OY 和 OX 的顺序很重要,下面是开发者选项:
“Disable adb authorization timeout”右侧没有节点,因此首先在 X 上应用偏移量将变为空:
>> device.sendAai({query:"T:Disable adb authorization*||OX:1||OY:1"})
null
>> lastError()
Offset out of range
如果首先在 Y 上应用偏移量将获得正确的节点:
>> device.sendAai({query:"T:Disable adb authorization*||OY:1||OX:1"})
{count: 1, ids: ['40e9c4']}
>> device.sendAai({query:"T:Disable adb authorization*||OY:1||OX:1", postAction:"click"})
示例 4:您可以使用一个 AAI 命令输入 emoji 符号,这里是 Messenger 的示例:
>> device.sendAai({query:"LB:-1||TP:textInput||OX:1", postActions:["click", "addQuery('OY:1')","intersectX", "addQuery('IX:3')", "click", "addQuery('OY:1')", "intersectX", "addQuery('IX:0')", "addQuery('OX:2||OY:3')", "click", "sendKey('back')"]})
{count: 11, list: [{retval: true},{count: 1},{count: 5},{count: 1},{retval: true},{count: 1},{count: 7},{count: 1},{count: 1},{retval: true},{retval: true}]}
要点击表情符号,需要点击 3 次:
query:"LB:-1||TP:textInput||OX:1" - 选择最后一行文本字段右侧的图标(蓝色图标)。
"click" – 单击节点以打开页面。
"addQuery('OY:1')" – 使用 "OY" 跳到下一行。在这种情况下,它很可能会落入 emoji 图标中,为了安全起见,我们使用以下方式确保找到 emoji 图标。
"intersectX" – 将返回该行的节点:
"addQuery('IX:3')" – 选择该行中的第 4 个节点(表情符号图标)。
"click" – 单击节点以更改页面。
“addQuery('OY:1')”——转到下一行。
"intersectX" – 返回该行的节点:
"addQuery('IX:0')" – 找到表情符号的第一个节点(左上角)作为参考节点。
"addQuery('OX:2||OY:3')" – 第 4 行和第 3 个节点的兴趣。
"click" – 单击节点以输入选定的表情符号。
"sendKey('back')" – 发送返回键。
想知道选择或生成了哪个或哪些节点,打开 UI Explorer 并添加“getIds”以在 UI Explorer 中找出节点 ID。
preAction、postAction 和 postAction
FindNode 开发查询的主要目的是查找节点,“preAction”发生在搜索“postAction”之前,“postActions”发生在搜索或由 preAction 提供的节点之后,执行的操作将在结果节点上。对于需要一个节点的操作(例如“单击”),将使用结果节点中的第一个节点(ON、IX、OX 和 OY 可以更改)。pre 和 post 操作中有许多命令,一些命令需要额外的参数(参数将括在括号中)。目前,支持以下类型的参数:
几个例子:
postAction:"getNodes('C,R')"
如果要在操作命令中使用变量,请使用以下 3 个选项之一:
var input = "Hello"
>> "setText('" + input + "')"
setText('Hello')
>> 'setText("' + input + '")'
setText("Hello")
>> aaix("setText", input)
setText("Hello")
postActions:[aaix("setText", input), "addQuery('OX:2')", "click"]
preAction
“preAction"用于下面的情形之一。如果结果输出是一个或多个节点,然后这些节点将进行 postAction操作, 否则将把输出结构直接给给调用者。
bgQuery:*
这些命令用于后台查询,TC 使用它来支持“addQueryListener()”,当找到匹配时会调用回调。
getFocus
返回接收焦点的节点(通常是文本输入框),如果没有节点获得焦点,则返回 null 并返回错误。如果找到节点,它将继续进行 postAction。
返回:如果没有找到焦点,则返回 null,否则将处理 postAction(s)。
示例:从现有行的下一行进行输入
{preAction:"getFocus", query:"OY:1", postAction:"setText('Hello')"}
getCount
返回匹配节点的数量,而不是节点 ID 的列表。应该在 postAction 中,但是,getCount 比 list 节点 ID 快,这个命令用来确定 count 或者节点搜索。返回:返回匹配节点的数量,不会执行 postAction(s)。
返回:返回匹配节点的数量,不会执行 postAction(s)。
例子:
>> device.sendAai({preAction:"getCount", query:"R:.ticker"})
{count: 6}
scrollToView
接受查询字符串(在“query”中),将通过上下滚动尝试匹配查询,当找到一个或多个节点时,将进行postAction,如果找不到匹配,则返回错误。滚动将使用向上/向下翻页,最多 10 页,滚动可能需要一些时间,该命令会自动延长“sendAai”中的超时时间,直到完成。确定如何执行滚动的几个选项,postAction 中的“showOnScreen”可用于确保节点完全可见。失败时,要返回原始位置,请反向运行 scrollToView。
preAction:"scrollToView(
参数:
<from-direction> 可以是以下之一,如果不指定,默认为“fromCurrentDown”。
“currentDown”:从当前位置开始向下滚动搜索。
“currentUp”:从当前位置开始向上滚动搜索。
“topDown”:快速滚动到页面顶部并通过向下滚动进行搜索。
“bottomUp”:快速滚动到页面底部并通过向上滚动进行搜索。
返回:只有在找不到节点时才会返回 null,否则将继续执行 postAction 或 postActions。
例子:
{query:"T:Bob", preAction:"scrollToView('fromTop')", postActions:["showOnScreen", "getNodes('all')", "click"]}
waitForWindowUpdate
等待窗口内容更新事件发生。如果指定了窗口的包名,但当前窗口没有相同的包名,则函数立即返回。
waitForWindowUpdate(<package name>, <timeout>)
waitForWindowUpdate(<timeout>)
参数:
"packageName: <string>" (可选):如果没有指定包名,则使用当前包名。
"timeout:<integer>" (可选):等待超时,以毫秒为单位。如果未指定超时,则使用 3000 毫秒。最大超时限制为 20 秒。
返回:
如果发生窗口更新,则为 true,如果超时或当前窗口没有指定的包名称,则为 false。
waitSelector
在这种模式下,如果对指定“query”的搜索失败,它将每隔 500 毫秒重试一次,直到找到节点并继续,如果超时,它将返回错误。
waitSelector(<timeout>)
参数:
timeout :超时时间,以毫秒为单位。“timeout”的限制不超过 20 秒。
例子:
device.sendAai({query:"T:Done", preAction:"waitSelector(10000)"})
postAction 和 postActions
搜索后(或来自preAction的节点),如果搜索不到,就会产生错误。如果指定了 preAction:"doCount",则返回匹配数。匹配的节点将可用于“postAction”或“postActions”,这些命令将获取结果节点并对它们执行操作,某些操作只接受一个节点,将应用第一个节点。“postAction”将接受一个命令并将输出作为 JSON 对象返回。“postActions”将接受一个命令数组并以 JSON 对象的形式返回一个输出数组。一些命令可以与多个节点(M)一起工作,一些命令生成更多节点(相交),如果命令只在一个节点(O)中工作,则将使用第一个节点。对于单节点操作,如果不需要第一个节点,请使用带 IX、ON 的 addQuery来改变它。postActions 顺序很重要。
device.sendAai({query:"T:OK||IX:-1", postAction:"click"});
device.sendAai({query:"R:.tickers", postActions:["refresh", "getNodes('T,D')"]});
device.sendAai({query:"R:.tickers", postActions:["getNodes", "sortX", "getNodes"]});
请参阅上面的示例以查看实际的 postAction/postActions。
getIds (M)
如果没有定义 postAction,这是默认的 postAction,该命令将返回匹配节点列表和匹配节点的数量:
返回: 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']}
getNodes (M)
这用于检索节点的信息。这些字段可以确定要显示的内容。“getNodes”将在检索信息之前刷新节点。
getNodes
getNodes(<fields>)
参数:
fields:可选的“fields”用于定义要返回的字段,“fields”中的多个字段用逗号分隔。有效的字段标识符是:
返回:节点信息的计数和数据信息数组。
>> device.sendAai({query:"T:/[0-9]/", postAction:"getNodes('R,T')"})
{count: 10, list: [{id: '98d1', resourceId: '.button_seven', text: '7'},{id: '9c92', resourceId: '.button_eight', text: '8'},{id: 'a053', resourceId: '.button_nine', text: '9'},{id: 'a7d5', resourceId: '.button_four', text: '4'},{id: 'ab96', resourceId: '.button_five', text: '5'},{id: 'af57', resourceId: '.button_six', text: '6'},{id: 'b6d9', resourceId: '.button_one', text: '1'},{id: 'ba9a', resourceId: '.button_two', text: '2'},{id: 'be5b', resourceId: '.button_three', text: '3'},{id: 'c5dd', resourceId: '.button_zero', text: '0'}]}
getBounds(M)
返回节点的边界(格式:[left, top, right, bottom]),它返回 2 个数组,一个具有所有 ID,另一个具有所有边界。
返回: bounds数组,每个bounds是一个4元组数组:[x1, y1, x2。y2]。
>> device.sendAai({query:"T:/[0-9]/", postAction:"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']}
refresh (M)
accessibility节点信息被缓存,如果屏幕已经更新,缓存信息可能不准确,使用“
refresh”强制重新读取节点信息。
返回:返回计数和真(总是返回真)
例子:
>> device.sendAai({query:"T:/[0-9]/", postAction:"refresh"})
{count: 10, retval: true}
click和longClick (O)
节点上的各种点击,这是同步点击,当控件返回时会保证屏幕开始刷新。
返回: true或false表示操作是否成功。
例子:
>> device.sendAai({query:"T:5", postAction:"click"})
{count: 1, retval: true}
clickForNewWindow (O)
通常“click”就足够了,但如果单击会打开一个新窗口并需要一段时间才能达到稳定状态,请使用此命令。 此命令等待比“click”更严格的事件。 如果此命令不起作用,请使用“waitSelector”。 如果单击没有创建新窗口,请不要使用此命令。
clickForNewWindow
返回:true或false表示操作是否成功。
例子:
device.sendAai({query:"T:Scan Now", postAction:"clickForNewWindow"})
showOnScreen (O)
这将确保节点完全出现在屏幕上,如果关联的 UI 元素部分显示,它将滚动直到 UI 元素完全出现在屏幕上。根据 UI 的设计方式,它可能会移动到容器的顶部。
返回: true表示屏幕已经滚动,false表示在屏幕上找到完整节点,什么也不做。
setText(O)
如果节点是文本字段,它将输入所需的字符串值。
setText(<input string>)
参数:
输入字符串:默认情况下,输入字符串之前会清除文本字段,如果第一个字母是“+”,它将添加到现有字符串。对于多行文本字段,使用“\n”跳到下一行。
返回: true或false表示操作是否成功。
例子:
>> device.sendAai({query:"TP:textInput||IX:1", postAction:"setText('Hello')"})
{count: 1, retval: true}
>> device.sendAai({query:"TP:textInput||IX:1", postAction: "setText('+ World')"})
{count: 1, retval: true}
sort/sortX/sortY
根据边界对节点进行排序:
reduceNodes(M)
这是 AAI 的核心功能,reduceNodes 接受节点 ID 列表,旨在将节点数量减少到可见节点。“TL”、“BL”、“OX/OY”和“intersect”都使用reduceNodes。请注意,如果较大的节点完全包含较小的节点,reduceNodes 将选择较小的节点,如果“.Button”节点包含较小的“.TextView”节点,则“.TextView”节点将被选中“.Button”节点, .TextView 节点上的“单击”仍然有效。请参阅 UI Explorer“优化”。
返回:
retval: true/false 表示 reduceNodes 是否减少了任何节点。
count:节点数(如果为false,则保持与原始节点相同的节点)。使用“getIds”检索生成的节点 ID。
例子:
>> device.sendAai({}).count
217
>> device.sendAai({postAction:"reduceNodes"}).count
63
对于上面的屏幕,尝试获取与 相交的节点,reduceNodes 将节点从 29 个减少到 3 个。
>> var rect = new Rect(device.sendAai({query:"T:Schedule", postAction:"getNodes", fields:"B"}).list[0].bounds)
>> device.sendAai({query:'BI:[-1,'+rect.centerY()+']', postActions:["getIds", "reduceNodes", "sortX", "getIds"]})
{count: 4, list: [{count: 29, ids: ['7fbc','873e','8aff','9281','9642','a185','b089','b80b','bf8d','c34e','c70f','45b03','ce91','d613','d9d4','dd95','146b1','e156','e517','e8d8','ec99','28205','285c6','28987','28d48','2a78f','2ab50','2af11','2b2d2']},{count: 3, retval: true},{retval: true},{count: 3, ids: ['28987','2ab50','2af11']}]}
setProgress
设置滑动条的值,可以通过“getNodes('RI')”获取最小值、最大值、类型、值,
setProgress(<number>)
参数:
返回:
true/false:如果操作成功。
示例:将滑块设置为 50%:
>> device.sendAai({query:"C:.SeekBar", postActions:["getNodes('RI')", "setProgress(50)", "getNodes('RI')"]})
{count: 3, list: [{count: 1, list: [{id: '4cf6e', rangeInfo: {current: 17, max: 100, min: 0, type: 0, typeString: 'int'}}]},{retval: true},{count: 1, list: [{id: '4cf6e', rangeInfo: {current: 50, max: 100, min: 0, type: 0, typeString: 'int'}}]}]}
getChecked (O) & setChecked (M)
这些命令适用于具有“可检查”节点的节点。通常,切换控件是可检查的,复选框或单选按钮,类的示例可以是“.Switch”或“.Checkbox”。
setChecked(true|false)
getChecked
参数:
“getChecked”将返回节点检查状态的真或假。如果节点不是一个多选框,则生成错误。
"setChecked(true|false)",由于没有设置checked的权限,如果需要更改值,它会点击节点切换值。它接受多个节点,将忽略不可检查的节点。
返回: getChecked 在可检查节点的状态上返回 true 或 false。setChecked 返回“changedCount”,有多少可检查节点已被更改。
addQuery (O|M)
addQuery 在结果节点上执行子查询,这样您可以从 addQuery 派生节点并对其进行操作。例如,您可以在一个查询中输入文本并单击发送(例如 addQuery('OX:2'))。不支持 LT 和 LB。您可以添加尽可能多的查询。
addQuery(<string>)
参数:
查询字符串:与主查询行完全相同的查询。
返回:匹配节点的数量。
示例:大多数聊天程序需要输入文本并单击发送按钮,使用 addQuery,您可以在一个命令中完成:
device.sendAai({query:"TP:textInput", postActions:["setText('Hello')","addQuery('OX:2')","getIds","click"]})
在 Skype 中,如果您想在不使用“LB”的情况下单击“通话”图标:
>> device.sendAai({query:"TP:anyText||IX:-1", postActions:["intersectX", "addQuery('T:Calls||OY:-1')", "click"]})
{count: 3, list: [{count: 4},{count: 1},{retval: true}]}
intersectX/intersectY (O)
这些命令使查询更有趣,这两个只是生成更多节点的命令。它们以一个节点为参考,返回所有水平(intersectX)和垂直(intersect)相交的节点。该节点可以是相交路径上的任何节点,它将利用参考节点边界(intersectX 为 top/bottom,intersectY 为 left/right)来搜索相交节点。此命令将更改内部匹配列表,因此您可以使用“addQuery”设置约束或操作以对其进行操作。该命令将占用一个节点并生成更多节点,更多示例请参见上面的“Query实战3”。
返回:
true /false:如果操作成功。
例子:
>> device.sendAai({query:"TP:textInput", postActions:["getIds", "intersectX", "getIds"]})
{count: 3, list: [{count: 1, ids: ['379ea6']},{count: 5},{count: 5, ids: ['37845f','379ea6','37a9e9','37c06f','37d6f5']}]}
waitQuery
此命令可用于等待某个节点出现在屏幕上,然后再进行其他发布操作。 此命令等待指定的查询和超时。
waitQuery(<query string>, <timeout>)
waitQuery(<query string>)
参数:
查询字符串:与主查询行完全相同的查询。
timeout:可选,如果没有指定,
返回:如果找到匹配则返回true,如果没有找到则生成错误。
例子:
sleep
此命令将等待指定的毫秒数,这对于简单的等待操作很有用。
sleep(<time>)
参数:
time:指定等待的时间(以毫秒为单位)。
返回:返回真
getText (O|M)
如果您不打算知道节点 ID以及其他信息,那么他会比“getNodes('T')”用起来更简单。它将检测一个或多个节点。
返回:如果找到多个节点,则返回一个文本数组,如果找到一个节点,则返回文本。
例子:
>> device.sendAai({query:"LB:2", postAction:"getText"})
{retval: ['Chats','Calls','Contacts','Notifications']}
>> device.sendAai({query:"LB:2||IX:1", postAction:"getText"})
{retval: 'Calls'}
preAction 和 postAction/postactions 的命令
这些命令适用于 preAction 和 postAction(s)。对于需要节点的 preAction,使用“elements”来提供节点。
sendKey
sendKey 接受键码或元状态并将键发送到屏幕(不是节点),具有焦点的 UI 元素将接收键码和元,例如文本字段或虚拟键盘。sendKey 还提供了快捷方式,一个文本字符串,一些常用的键码。并非所有的键码都会生成字符,一些特殊的键码会带来新的窗口,例如返回、应用程序切换或转到主屏幕。键码和元状态显示在 Android KeyEvent 类中。
快捷键只不过是对不同键码的映射,以下是所有快捷键(不区分大小写):“home”、“back”、“backspace”、“enter”、“appswitch”(切换到最后一个应用程序),对于旧的应用程序、“搜索”和“菜单”。
sendKey(<key code>)
sendKey(<key code>, <meta state>)
sendKey(<shortcut>)
参数:
键码和元状态:为整数,“shortcut”为字符串。关于返回键,有时您需要发送 2 个返回键才能返回上一页,第一个返回键是关闭键盘(Sigma 在屏幕上不可见,但输入框在窗口顶部可见),第二个 返回键是返回上一屏幕。
返回:以真/假返回操作的状态。
例子:
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}
getQuery/getUniqQuery (O)
接受一个节点并尝试生成一个查询以匹配该节点(或多个节点)。从“getQuery”返回的查询可以匹配多个节点,有助于从应用程序中获取管状信息。从“getUniqQuery”返回的查询将只匹配一个节点,这对于点击等操作很有用。目前这是一个初步的,我们将在未来开发更好的查询字符串:
getUniqQuery/getQuery - 默认使用文本和描述作为查询的一部分。
getUniqQuery/getQuery(<true/false> 忽略文本) – true 忽略搜索查询中的文本。
getUniqQuery/getQuery(<true/false> 忽略文本,<true/false> 忽略描述) – 第二个参数为 true 以忽略搜索查询中的描述。
参数:
true or false:忽略查询中的文本。
true or false:忽略查询中的描述。
返回:返回查询字符串。
>> device.sendAai({elements:["123554"], preAction:"getUniqQuery"})
{query: 'T:INTC'}
>> device.sendAai({query:"T:INTC", postAction:"getUniqQuery"})
{query: 'T:INTC'}
>> device.sendAai({query:"T:INTC", postAction:"getUniqQuery(true)"})
{query: 'C:.TextView||R:.ticker||CC:0||IX:0'}
>> var ret = device.sendAai({query:"R:.ticker", postAction:"getQuery(true)"})
{query: 'C:.TextView||R:.ticker||CC:0'}
>> device.sendAai({query:ret.query, postAction:"getNodes('T,D')"})
{count: 5, list: [{id: '411a4', text: 'INTC'},{id: '3ec1a', text: 'WMT'},{id: '3c690', text: 'TGT'},{id: '3a106', text: 'AAPL'},{id: '37b7c', text: 'PEP'}]}
>> device.sendAai({query:ret.query, postAction:"getText"})
{retval: ['INTC','WMT','TGT','AAPL','PEP']}
支持
请发送电子邮件至 support@sigma-rt.com 以获得支持。