FindNode

FindNode 是核心 Selector 包之上的一个 shell 程序,FindNode 的目的是找到预期的一个或多个 UI 元素(或 Accessibility 节点)并提取信息或对它们执行操作。

FindNode 是 AAI 项目(可访问性和自动化集成)的一部分,它由以下组件组成:

  1. 查询语言,简单的一行语法语言来搜索目标节点。
  2. 每个设备上的 FindNode(和 Selector 核心)。
  3. 一对多同步的对象模式,将节点(或UI对象)发送到所有设备而不是协调,单击"确定”可以在不同分辨率的所有设备上运行,而不是单击(100,100)。
  4. UI Explorer 获取节点信息,可以直观的查询语言、学习和探索工具。
  5. AAIS,一种执行自动化的简单语言
  6. 作为现有 REST 和 JS API 的一部分(例如 devices.click("OK"))。

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

例如

JS API:devices.click("OK") 而不是 devices.click(100, 200)。单击确定查询将发送到所有设备。

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 会延长时间以避免超时错误。
  • 自动处理返回值(错误为空,值非空)。
  • 对于多个设备"devices”,将为每个设备创建到 FindNode 的线程。

params 对象包含 3 种类型的属性:query、preAction 和 postAction(s)。提供的命令非常丰富,您可以使用它们编写简单的自动化。

想必你可以通过使用"device.sendAai()”和"devices.sendAai()”来完成大部分任务,FindNode是一个"handler”,另一个handler"invoke”可以使用Java Reflection来访问UiDevice、UiObject2、 AccessibilityNodeInfo 和 InteractionController。

限制

这是 AAI 的第 1 版,它有一些限制:

  • 仅支持纵向模式,未来将引入横向模式。
  • 此时并非所有内容都能够识别,取决于应用程序的设计,可能会错过一些节点。
  • 不支持水平滚动或某些弹出窗口(更少节点或更多节点)。
  • 不支持多线程,每次执行必须互斥:MDCC(对象模式)、终端(或脚本)、UI Explorer和部分REST API。这将在后续版本中解决。

Query

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

我们开发了一种小型查询语言(因为没有更好的词),它可以在设备之间移植,并且功能强大到可以定位大多数节点。格式为 JSON:

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

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

请求参数

有许多类型的查询,一些是基本查询相关的,另一些是计算的衍生物:

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

  • C:<类名> 类名称 (S)
  • R:<资源 ID> 资源 ID (S)
  • D:<文本> 描述 (S)
  • T:<文本> 文字 (S)
  • IT:<数字> 文字输入类型(一)
  • CC:<数字> 子节点数(一)
  • ID:<标识> 十六进制的节点 ID (S)
  • BI:[x, y] 节点包含 (x,y)
  • BI:[x1, y1, x2, y2] 矩形包围的节点,如果x或y为-1,则忽略
  • BP:<属性> 布尔属性 (S)。

以下是扩展查询:

  • IX:<数字> 根据位置从匹配节点列表中获取一个节点
  • OX:<数字> 水平偏移到相邻节点(正 - 右,负 - 左)
  • OY:<号码> 垂直偏移到相邻节点(正 - 向下,负 - 向上)
  • TP:<模板名称> 复杂搜索的简写形式
  • ON:<类型> 从匹配节点列表中选择一个节点的不同方法
  • LT:<行号> 返回可滚动节点顶部的顶部节点
  • LB:<行号> 返回可滚动节点底部的底部节点
  • ST:<排序类型> 根据屏幕上节点的位置返回排序的节点
  • TX 返回与参考节点水平相交的节点(参见"intersectX”)。
  • TY 返回与参考节点垂直相交的节点(参见"intersectY”)。
  • VG:[级数] 返回与参考节点垂直相交的节点(参见"intersectY”)。
  • RN 从匹配节点列表中返回优化节点(参见“reduceNodes”
  • PQ:<查询> 一切完成后申请。

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

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



查询中的特殊字符

对于 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”。

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



边界(BI):

Bound in 获取屏幕坐标或边界,并根据以下条件返回节点。这主要用于屏幕操作,例如 UI Explorer 或调试目的。

  • [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) 指定的矩形内的节点边界。

8.0-u40 支持最后两种模式。

例子:

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


匹配列表

查询完成后,匹配的节点会存储在匹配列表(ML)中,ML中的节点由节点ID标识。许多动作使用 ML 中的节点,一些动作可以改变 ML。“save”和"load”可以保存和检索ML。使用 action:"getIds" 获取 ML ID。TX/TY/VG 以一个节点生成多个节点。

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



查询优先级

以下是查询优先级:

  1. 行模式 (LT/LB)。
  2. 模板 (TP)。
  3. 基本查询。
  4. 索引(IX)
  5. 单节点选择 (ON)。
  6. 偏移量(OX 和 OY)。
  7. 视图组 (VG)。
  8. 相交 (TX & TY)。
  9. 减少节点(RN)。
  10. 排序 (ST)。
  11. 后查询 (PQ)。


“元素”属性

“元素”接受节点 ID 数组,节点 ID 必须是屏幕上的有效节点 ID,否则会产生错误。当指定"元素”时,将不执行搜索,但是,IX、OX、OY、ON 仍然有效。它对于调试目的和访问无法轻松检索的节点(例如子/父节点)很有用。每个带有"元素”的查询都会遍历所有节点以找到对应的节点,尝试将多个 ID 放入数组中,而不是一次放置一个。

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

var ids = device.sendAai({query:"C:.Button"}).ids;
var retval = device.sendAai({elements:ids, postAction:"getNodes"});

同步动作

已经下了很大的功夫来保证动作的同步,当一个"点击”被调用时,不仅保证点击被同步按下,它会等待某些"事件”来保证渲染"开始”的发生,不能保证渲染完成。FindNode 提供了几种执行方式:

  • “click”:它应该大部分时间都可以工作。
  • “waitQuery”:如果新窗口需要很长时间才能完成渲染,使用该命令检查节点是否可用,添加动作:“click”,当找到节点并准备好时将单击。


有两种类型的弹出窗口,它们看起来相同,但实现方式不同:

  1. 弹出窗口更改节点树的根节点,这将被正确处理(例如 Whatsapp)。
  2. 弹出窗口在节点树中新建了一个分支,生成的节点会是主窗口和弹出窗口的累积节点,可能会找错节点。FindNode 将在发生这种情况时监视事件,并在打开和关闭弹出窗口时正确预测节点树的顶部分支。启动 FindNode 时,请确保应用程序未处于弹出模式(Google Mail、Google 搜索)。

对于第二种弹窗,FindNode 可以识别一些弹窗(很多 Google 应用程序都使用这种弹窗),如果弹窗破坏了 FindNode,它将表现出以下行为之一:

  • 在查询中未找到节点(甚至是"TP:all”)。
  • UI Explorer 中显示的节点很少。
  • 从弹出窗口和主窗口累积节点。UI Explorer 将显示很多没有意义的边界(主窗口中的节点)。

发生这种情况时,关闭(从内存中删除)并重新打开应用程序,如果弹出窗口不起作用,请联系支持。



测试

通过 Query 找到节点并不是很困难,你需要的是"UI Explorer”和终端:

  • 节点 ID 很重要,它通常对应用程序是唯一的。
  • 终端:键入:device.sendAai({query:"…"}) 以查找它是否与节点匹配。有很多搜索选项,找到节点的方法不止一种。
  • 用户界面资源管理器:
    • 从不同节点查找边界。
    • 查找节点内的属性。
    • UI Explorer 中的"代码”是定位节点的最佳方式,有关详细信息,请参阅 postAction"getUniqQuery”。
    • 使用"自定义”查询尝试不同的选项来定位节点。
  • 终端:如有疑问,请使用 UI Explorer 查找节点 ID,使用 device.sendAai({elements:["<ID>","<ID>"...], action:"getNodes"}),这将帮助您获取更多信息。
  • 从使用"device.sendAai”的一个设备开始通过"devices.sendAai”更改为多个设备,您可以使用Device.searchObject()搜索设备以找到您需要的设备(例如所有设备,组中的设备,特定的设备)。


基本查询示例

基本查询在其他查询之前执行(LT 和 LB 除外),匹配节点由节点 ID 标识。如果查询为空,则默认查询为"TP:more”,如果操作为空,则默认操作为"getIds”。

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"

以编程方式,始终检查 null 的返回值。

扩展查询

这些是不特定于节点本身的查询,按优先顺序列出:



顶线和底线 (LT & LB) → 中号

行模式:LT:<Integer> 和 LB:<Integer>。这仅适用于具有可滚动容器或 LT 的应用程序,并且 LB 将返回 null。FindNode 会将 UI 元素定位在可滚动区域之外,行模式会将 UI 元素组合成一系列行,每行可以有一个或多个节点。许多应用程序在屏幕的顶部和底部都有固定的 UI 元素,这可以通过使用"LT:1||IX:2”(顶行的第三个节点)或"LB:-1|”等查询轻松定位。 |T:Chats" 行的底部,以 Chats 为文本。

  • 对于计算器等不可滚动的应用程序,LT 和 LB 将始终返回 null。
  • 在状态行的底部和可滚动区域的顶部之间被认为是顶部区域,LT 从 1 开始,“LT:1”是第一行。LT:-1 是该区域的最后一行。
  • 可滚动区域的底部和导航栏的顶部之间被视为底部区域。同样,LB 从 1 开始,LB:1 是第一行。LB:-1 是该区域的最后一行(也是应用程序的最后一行)。
  • 使用越界行号将变为空。(例如底部的 2 行,LB:3 将得到空值)。
  • 如果一个节点高度与同一行上的两个垂直节点高度相似,则较低的节点将放置在下一行。
  • 许多应用程序将顶行放在可滚动区域内,LT 将始终返回 null。
  • 不干扰"IX”,节点旁边的红色小计数器或红点将被删除(不在返回列表中)。
  • 这个查询不太理想,以后会改。
>> device.sendAai({query:"LT:1"})
{count: 6, ids: ['12b9f4','12ccb9','12e33f','1d7d52','12f9c5','1317cd']}
>> device.sendAai({query:"LB:-1"})
{count: 5, ids: ['1ea5e3','1ea9a4','1ead65','1ebc69','1ed2ef']}

对于不可滚动的应用程序,“LB”将返回 null 但不难获取应用程序底部的节点:


>> device.sendAai({query:"IX:-1&&TX", action:"getText"})
{retval: ['Speed','Video','VPN','Map']}

另一种方法是使用 ViewGroup。每个标签都有一个ViewGroup,所以需要2级。

{query: "IX:-1&&VG:2"}

使用"减少节点”(RN)减少到 8 个节点:

{query: "IX:-1&&VG:2&&RN"}

您现在可以单击例如 Speed(如果已选择 Speed,则使用 nsClick,单击没有可见效果):

>> device.sendAai({query:"IX:-1&&VG:2&&RN&&PQ:'T:Speed'", action:"nsClick"})
{retval: true}
>> device.sendAai({query:"IX:-1&&VG:2&&RN", action:"nsClick(T:Speed)"})
{retval: true}

点击图标:

>> device.sendAai({query:"IX:-1&&VG:2&&RN", action:"nsClick(T:Speed&&OY:-1)"})
{retval: true}

或者,只想点击"速度”:

>> device.sendAai({action:"nsClick(T:Speed&&IX:-1)"})
{retval: true}


模板 (TP)

我们开发了几个内置 FindNode 的预定义模板,这是一个主要由基本查询组成的简短形式:

  • all:返回所有节点。如果没有定义模板,这是默认设置。
  • more:删除布局节点的"全部”选项。如果查询为空,则这是默认设置。
  • baisc:子计数为零的"更多”选项。
  • reduceNodes:带有"RN”(或reduceNodes)的"more”选项。
  • findText:搜索带有文本的节点或带有特定文本的描述。findText 需要一个额外的参数来指定要搜索的文本。
  • anyText:搜索具有文本或描述(非空)内容的节点。
  • textInput:使用输入类型 > 0 或“.EditText”类进行搜索,尝试查找输入文本字段。这也是从左上角到右下角排序的。“IX:0”是第一个文本输入,“IX:1”是第二个文本输入,“IX:-1”是最后一个文本输入。默认是第一个节点。

例子:

 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']}

>> device.sendAai({query:"TP:all"}).count
250
>> device.sendAai({query:"TP:more"}).count
245
>> device.sendAai({query:"TP:basic"}).count
75
>> device.sendAai({query:"TP:reduced"}).count
56

或者:

 device.sendAai({actions:[
 "getCount(TP:all)",
 "getCount(TP:more)",
 "getCount(TP:basic)",
 "getCount(TP:reduced)"
]})
{count: 4, list: [{count: 250},{count: 245},{count: 75},{count: 56}]}


索引(IX)

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 的多个节点中返回一个节点,目前有 4 个选项可用。目前只有有限的选择,将来会扩展。

这些是选项:

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

ON 和 IX 需要互斥。



偏移量(OX & OY) ○→○

OX :<整数> 和OY :<整数>。使用偏移量根据一个参考节点查找相邻节点,OX 查找水平位置,正整数向右移动,负整数向左移动。对于 OY,正整数向下移动,负整数向上移动。如果两个选项都存在,则选项将根据查询中的位置应用。例如:“OX:1||OY:2”将在垂直偏移之前应用水平,而"OY:2||OX:1”将在水平偏移之前应用垂直。顺序对于定位节点的不对称排列很重要。

示例:在关于电话中:

 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,)" 来更改屏幕宽度或类中的 "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']


相交 (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)

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

例子:

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

相关操作:“减少节点”。



排序 (ST)

排序: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','-','+','%','=']}

相关动作:排序



推迟查询(PQ)

发布查询:PQ:'<query>'。考虑以下查询(从 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”和"OX”)将在"VG”之前应用(参见优先级)。我们需要一个辅助查询来使其工作。有两种方法可以做到这一点,查询中的"post query”和实际操作中的"addQuery”。

 device.sendAai({query:"T:Overall Cpu Usage&&VG&&PQ:'T:Cpu Cores&&OX:1'", action:"getText"})
{retval: '8'}
>> device.sendAai({query:"T:Overall Cpu Usage&&VG", actions:["addQuery(T:Cpu Cores&&OX:1)", "getText"]})
{count: 2, list: [{count: 1},{retval: '8'}]}

后查询在所有查询完成后应用查询。Parser 将只接受 PQ 的单引号查询。

相关操作:addQuery


对于线模式,每条线都应用第一个节点的"intersectY”,通常没问题,但在以下情况下,它将返回所有节点:

所以"LT:1”将返回所有 6 个节点。

对于不可滚动的应用程序,“LB”将返回 null,但获取节点并不难:

您可以使用其中一个标签作为锚点(使用"ON:last”或"IX:-1”来确保查询适用于预期节点,以防屏幕内容具有相同的文本:

 device.sendAai({query:"T:Speed&&ON:last", actions:["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']}]}

要单击 VPN 上的图标:

 device.sendAai({query:"T:VPN&&ON:last&&OY:-1", postAction:"click"})
{retval: true}

行动

FindNode 提供了许多动作命令,“action”用于单个命令,“actions”是多个动作命令的数组。查询是可选的,如果不指定查询,则默认为"TP:more”,命令/动作需要从底层ML获取节点,使用action:“getIds”获取ML内容。

device/devices.sendAai({query:"...", action:})

device/devices.sendAai({query:"...", actions:[,, …]})

设备/设备.sendAai({动作:[,, …]})

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

{action:"openApp('Skype')"}

由于 "Skype" 是字符串,因此可以省略引号:

{action:"openApp(Skype)"}

对于需要一个节点的操作(例如"单击”),将选择 ML 中的第一个节点(ON、IX、OX 和 OY 可以更改)。action中有很多命令,不同版本之间存在不兼容,请使用preAction:"version"(版本11)或action:"version"(版本12)确保版本支持所需命令。一些命令需要额外的参数(参数将被括在括号中)。目前,支持以下类型的参数:

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

几个例子:

行动:“getNodes('C,R')”

如果要在操作命令中使用变量,请使用以下 4 个选项之一:

var input = "Hello"
>> "setText('" + input + "')"
setText('Hello')
>> 'setText("' + input + '")'
setText("Hello")
>> 'setText(' + input + ')'
setText(Hello)
>> aaix("setText", input)
setText("Hello")
actions:[aaix("setText", input), "addQuery('OX:2')", "click"]

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

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

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

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

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

获取命令



获取边界 (M)

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

用法:getBounds [查询]

返回: 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']}


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。



获取检查(O/M)

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

得到检查

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

参见:setChecked

 device.sendAai({query:"T:/12:30\\sPM/&&VG&&PQ:'T:Every day&&OX:1'", action:"getChecked"})
{retval: true}
>> device.sendAai({query:"T:/12:30\\sPM/&&VG&&PQ:'R:/.day_button_[0-9]/'", action:"getChecked"})
{retval: [false,true,false,true,false,true,false]}
 >> device.sendAai({query:"T:/12:30\\sPM/&&VG", action:"getChecked"})
{retval: ['N/A','N/A','N/A','N/A','N/A',true,'N/A',false,true,false,true,false,true,false,'N/A','N/A',true,'N/A','N/A']}


获取描述/获取文本 (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'} 


获得焦点

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

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

示例:从现有行输入下一行

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


获取 ID (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']}


获取节点 (M)

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

获取节点

获取节点(<字段>)

论据:

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

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

返回:节点信息的计数和数组。

 device.sendAai({query:"T:/[0-9]/", action:"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'}]}
>> 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'}]}


获取包名称

返回当前运行的包名。

返回: "retval”中的包名。

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


获取进度 (O)

用法:getProgress [查询]

返回可进度节点的"retval”中的 RangeInfo,如果该节点不是可进度节点,则返回 null。

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


getQuery/getUniqQuery (O)

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

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

论据:

true or false:忽略查询中的文本。

true or false:忽略查询中的描述。

返回:返回查询字符串。

要获取每个节点的查询:

 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 + 操作,加载和继续。



intersectX/intersectY (O → M)

不带参数时,此命令与"TX”和"TY”相同。此命令将更改内部匹配列表,因此您可以使用"addQuery”设置约束或操作以对其进行操作。该命令将获取一个节点并生成多个节点,如果内部匹配列表包含多个节点,则将使用第一个节点。"intersect" 和 "addQuery" 通常是连在一起的,我们在 intersect 前后添加了 addQuery。

相交?

intersect?(<post query>) = "intersect?", "addQuery(<post Query>)"

intersect?(<pre Query>, <post query>) = "addQuery(<pre query>)", "intersect?", "addQuery(<post query>"

返回:

true /false:如果操作成功。如果指定了查询,则后查询(参见"addQuery”)的输出将是该命令的输出值。

另请参阅:“TX”和"TY”查询

例子:

>> 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']}]}

获取下一行(OY:1)的第三个节点(IX:2):

>> device,sendAai({query:"T:7", postActions:["intersectX(OY:1, IX:2)", "getText"]})
{count: 2, list: [{count: 1},{retval: '6'}]}

没有 preQuery 没有 postQuery,使用空字符串:

>> device.sendAai({query:"T:Chats||IX:-1", postActions:["intersectX(OY:-1,'')", "getIds"]})
{count: 2, list: [{count: 4},{count: 4, ids: ['140ab','15eb3','178fa','19341']}]}


获取视图组(O → M)

该命令类似于"VG”查询,将接受一个级别和查询。查询是否已定义用于标识要为组搜索的第一个节点。类似于"VG”,它会改变ML。

用法:

获取视图组

getViewGroup(<level>) // 如果参数是整数

getViewGroup(<query>) // 如果参数是字符串

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

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

 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”查询相同,没有参数。将返回"计数”。

用法:reduceNodes

返回:返回"count”中的结果节点数。

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


排序 (M → M)

它与"ST”查询相同,参数与 ST 相同。

用法:排序(X|Y|YX)

返回:在"retval”中返回真/假。



新查询/添加查询/等待查询

所有接受查询字符串,“newQuery”将丢弃 ML,启动新查询并将结果节点放入 ML。“addQuery” 在 ML 中执行搜索,将结果节点放在 ML 中。"waitQuery" 不会改变 ML,它有一个超时,将确保在超时到期之前完成查询,否则它将返回 null。

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

新查询(<查询>)

addQuery(<查询>)

waitQuey(<query>) // 默认超时 = 2000ms

waitQuery(<查询>, <超时>)

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

动作命令

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



点击、nsclick、click2、longClick

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

  • 单击:在节点中心单击。
  • longClick:长按(500ms)模拟长按。
  • click2:点击节点中的随机位置。

以上所有的点击都会监听点击后画面的变化事件,如果画面没有变化,这些点击就会失败,最终超时。如果点击后画面没有变化,使用"nsClick”:

  • nsClick:执行单击但不监视事件。

返回: true或false表示操作是否成功。

点击、nsclick、click2、longClick

点击(<查询>),nsclick(<查询>),点击2(<查询>),长点击(<查询>)

例子:

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


刷新 (M)

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

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

例子:

 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'}]}


滚动查看

接受查询字符串和滚动方向,将通过上下滚动尝试匹配查询,当找到一个或多个节点时返回,如果找不到匹配则返回null并出错。滚动将使用向上/向下翻页,最多 10 页,滚动可能需要一些时间,该命令会自动延长"sendAai”中的超时时间,直到完成。为了使脚本能够以任何分辨率和大小运行,使用 scrollIntoView 比 PageUp/Down 更好。

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

scrollIntoView(<查询>, <方向>)

论据:

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

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

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

“top”:快速滚动到页面顶部并通过向下滚动进行搜索。

“bottom”:快速滚动到页面底部并向上滚动搜索。

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



发送密钥

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

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

sendKey(<键码>)

sendKey(<key code>, <meta state>)

sendKey(<快捷键>)

论据:

键码和元状态:为整数,“快捷方式”为字符串。关于返回键,有时您需要发送 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}


setChecked (M)

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

设置检查(真|假)

论据:

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

返回: setChecked 返回"changedCount”,有多少可检查节点已被更改。



设置进度 (O)

设置滑动条的值,可以通过"getNodes('RI')”获取最小值、最大值、类型、值,

设置进度(<数字>)

论据:

<数字>:整数或小数设置值。

返回:

true/false:如果操作成功。

例子:

将显示亮度设置为 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}

In sound setting, set all 4 sound volumes to 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)});
}
)


设置文本 (O)

如果节点是文本字段,它将在输入文本字段中输入"输入字符串”值。

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 输入 Emoji:

 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}


屏幕显示 (O)

这将确保节点完全显示在屏幕上,如果关联的 UI 元素部分显示,它将滚动直到 UI 元素在屏幕上完全可见。根据 UI 的设计方式,它可能会移动到容器的顶部。所以,请在使用前尝试。

屏幕显示

显示屏幕()

返回: true表示屏幕已经滚动,false表示节点已经在屏幕上完全可见,什么都不做。



睡觉

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

睡眠(<时间>)

论据:

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

返回:返回真

其他命令



打开应用程序/重新启动应用程序/关闭应用程序 版本 11

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

“openApp”将运行应用程序,如果应用程序驻留在内存中,它将恢复执行。

如果自动化需要以已知状态启动应用程序,“restartApp”将在启动应用程序之前先关闭应用程序。“closeApp”将关闭应用程序(强制关闭)。要打开应用程序以达到"稳定的已知状态”,请使用"restartApp”和"waitQuery”。

openApp(<名称>)

重新启动应用程序(<名称>)

closeApp(<名称>)

关闭应用

返回:

retval: true 表示成功或 null 与 lastError() 失败。



打开Android设置

将根据指定的设置名称打开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”、“管理应用程序设置”、“管理应用程序”是一样的。



列表Android设置

此命令将设置列表作为数组返回。

列表Android设置

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


配置:设置,配置:获取,配置:未设置

改变FindNode(或Selector)的值,各种值会通过名字来标识,使用"config:get”获取值,“config:set”改变值,“config:unset”恢复为原值。

配置:获取(<名称>) → 名称:<名称>,值:<值>

config.set(<名称>, <值>) → retval: 真

config.unset(<名称>) → 名称:retval:真

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

命令参考

姓名 选择查询 论据 输入 输出 机器学习改变 支柱
获取命令  
获取边界 是的 所有节点 ids:[<id>], bounds:[<bounds>]
getBoolProp是的 第一个节点 retval:[<prop>]
getBoolPropInAncestors是的 第一个节点retval:[<prop>]
得到检查一个节点
所有节点
retval:<boolean>
retval:[boolean|N/A]
获取描述是的 一个节点
所有节点
retval:<文本>
retval:[<文本>]
获得焦点没有任何节点:<ID>
获取 ID是的所有节点ids:[<ID>]
获取节点[字段列表(S)]所有节点列表:[<信息>]
获取包裹名称没有任何retval:<包名>
获取进度是的第一个节点retval:<范围信息>
获取查询[igText(B)]
[IgDesc(B)]
第一个节点 查询:<查询字符串>
获取文本是的一个节点
所有节点
retval:<文本>
retval:[<文本>]
getUniqQuery[igText(B)]
[IgDesc(B)]
第一个节点查询:<查询字符串>
查询命令  
节省所有节点 retval:<布尔值>
加载没有任何retval:<布尔值>是的
获取视图组是的 [一级(I)] 第一个节点计数:<计数>是的视频网关
相交X是的[preQ(S)] [postQ(S)]第一个节点计数:<计数> 是的德克萨斯州
相交是的 [preQ(S)] [postQ(S)]第一个节点计数:<计数>是的
减少节点所有节点 计数:<计数>是的注册护士
种类<类型(S)> 所有节点retval:<布尔值>是的 英石
添加查询是的所有节点计数:<计数>是的
新查询是的没有任何计数:<计数> 是的
等待查询是的[持续时间(一)] 没有任何retval:<布尔值>
动作命令  
点击是的 第一个节点retval:<布尔值>
点击2是的第一个节点retval:<布尔值>
长按是的第一个节点retval:<布尔值>
ns点击是的 第一个节点retval:<布尔值>
刷新是的所有节点 retval:<布尔值>
滚动查看是的[方向(S)]没有任何retval:<布尔值>
发送密钥<代码(I)> [元(I)]没有任何retval:<布尔值>
设置检查<布尔值>所有节点 改变计数:<计数>
设置进度是的<值(F/I)>第一个节点retval:<布尔值>
设置文本<文本(S)>第一个节点retval:<布尔值>
屏幕显示是的第一个节点retval:<布尔值>
睡觉<持续时间(毫秒)>没有任何retval:<布尔值>
其他命令  
版本没有任何retval:<版本号>
开放应用<姓名(S)>没有任何retval:<布尔值>
重启应用<姓名(S)>没有任何retval:<布尔值>
关闭应用[<姓名(S)>]没有任何 retval:<布尔值>
打开Android设置<设置(S)>没有任何retval:<布尔值>
列表Android设置没有任何列表:[<设置>]
配置:获取<姓名(S)>没有任何名称:<名称>,值:<值>
配置:设置<名称(S)>
<值(*)>
没有任何retval:<布尔值>
配置:未设置<姓名(S)>没有任何retval:<布尔值>
(S) - 字符串,(I) - 整数,(B) - 布尔值,(F) - 浮点数,(*) - 所有类型 [可选参数] 或 <name>:[<eturn array>]
device/devices.sendAai({query:<query>, action:<command>})
device/devices.sendAai({query:<query>, actions:[<command>]})>

支持

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