FindNode
FindNode is a shell program on top of the core Selector package, the purpose of FindNode is to find the intended one or more UI elements (or Accessibility nodes) and extract information or perform actions on them.In short AAI replace coordinates with queries.
FindNode is part of the AAI project (Accessibility and Automation Integration), it consists of the following components:
Selector sits on top of Accessibility and UI Automator, each UI element or container of UI elements is identified by node or sets of nodes, each node has its ID that is unique on the current screen. Selector provide various ways to search for one or multiple nodes. once the nodes are acquired, you can do interesting things such as obtain the texts/images or perform action such as click on button or entering the text on the text field, all without coordinates. This allow one automation script to run on different resolutions which is not possible using coordinates. The coordinates are acquired during the runtime.
For Example:
JS API: devices.click(“OK”) instead of devices.click(100, 200). Find node with text "OK", obtain coordinate and click on it. All done in runtime.
MDCC: Users click a button on the main device, the unique query of the button (e.g. "T:OK") will be sent to other devices, to search for the node and click. The intention is to provide various ways to locate nodes without using screen coordinates, there is no page up/down but "scrollToView" to locate a node, this way, the same script can run on different phones with different resolutions and screen size.
FindNode uses the JSON construct of Appium to carry out the JSON commands, the JSON format is:
{"cmd": "action", "action": "findnode", "params": {…}}
Every FindNode commands are carried out in "params" object.
Return value can have one of two types, successful run:
{"status":0, "value":"{…}"}
Or failure:
{"status":13,"value":"{<error message>}"}
TC provide "device.sendAai()" or "devices.sendAai()" to communicate with FindNode:
The params object contains 3 types of properties: query, preAction and action(s). The commands offered are quite rich, you can write a simple automation with them.
Presumably you can do most of the task by using "device.sendAai()" and "devices.sendAai()", FindNode is a Appium's "handler", another handler "invoke" can use Java Reflection to access methods in UiDevice, UiObject2, AccessibilityNodeInfo and InteractionController.
Limitations
This is the version 2 of AAI, it comes with some limitations:
Query + Actions
Use our query language to find nodes on the screen and apply actions on the nodes found. We will cover Query and Actions in this manual.
For instance: to click a button with "OK" label:
query:"T:OK", action:"click"
To obtain the name if the name is next to the "Name" label
query:"T:Name&&OX:1", action:"getText"
To obtain all the stock symbols:
query:"R:.ticker", action:"getText"
Query
Majority of the FindNode usages are about query, this is center piece of the FindNode (and part of AAI). It allows users to locate intended UI elements via different kind of useful queries on the Accessibility nodes. The entire screen is composed by many nodes, a node can be a smallest UI element or a container of smaller nodes, many are invisible. The entire screen is a tree structure starting from the single root node. Query will be performed to obtain the nodes that meet the criteria, the intent is to reduce the large number of nodes to one or few intended nodes, users can obtain information or apply actions to the nodes.
We developed a small query language that is portable among devices and scripts and powerful enough to locate most of the nodes. The format is JSON:
{query:"<key>:<value>&&<key>:<value>&&<key>&&<key>…"}
We accept "||" and "&&" as identical separator, in the future it can be changed. "&&" since the query is AND relationship, that means the query nodes needs to meet all criterion to become "matching nodes". FindNode does not offer OR relationship but the underlying Selector package provides that capability. “templates" use that construct. For certain key, value is not required.
The query string is divided into 3 sections: template, basic query and extended query. "template" (T) will generate nodes, "basic query" (BQ) will perform query from accessibility nodes one at a time, "extended query" (EQ) will take output from T and BQ and perform query across multiple nodes.
To get Main storage free size
Can use "query" + "action":
>> device.sendAai({query:"T:Main storage&&OX:1", action:"getText"})
{retval: '402 GB free'}
Can use "optional query" (OQ):
>> device.sendAai({action:"getText(T:Main storage&&OX:1)"})
{retval: '402 GB free'}
Similarly to go back to previous screen, can use OQ:
>> device.sendAai({action:"click(T:Storage Analysis&&OX:-1)"})
{retval: true}
To obtain the Audio size, the query can be mixed and still obtain the identical result:
>> 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'}
The parser will separate the query into TP, BQ and EQ and execute them accordingly:
Template (TP)
Template query (for lack of better name) is required key for query, it generate the nodes for intended purpose. Missing template will be defaulted to "TP:more", multiple templates are not allowed, here are different templates:
Basic Query (BQ)
Basic query is queries that is purely based on the individual node information. BQ is not required, if BQ is not defined, it will pass all nodes from templates to EQ.
The following are the basic queries that match information obtained from the nodes itself:
Since the query is always performed on the existing screen, "P" should not be used. The "(S)" stands for String, "I" integer, "[x, y]" array or optional field. I/S can accept both integer and String. String is more powerful.
The keys are case insensitive, we use uppercase to differentiate keys from values.
Expanded Query (EQ)
The following are the keys for expanded query, it usually works on multiple nodes return by BQ or template. The expanded query work from left to right, same key can be applied multiple time. EQ is optional. The following are the keys for EQ:
All these queries are optional, you can pick and choose any field, if you are doing the query on the screen. The "(S)" stands for String, "I" integer, "[x, y]" array. I/S can accept both integer and String. String is more powerful.
The keys are case insensitive, we use uppercase to differentiate keys from values.
Matching List (ML)
After the query is performed, the matched nodes will be stored in Matching List (ML), nodes in ML are identified by node ID. Many actions use the nodes in the ML, some actions can change the ML. "save" and "load" can save and retrieve ML. Use action:"getIds" to obtain the ML IDs. TX/TY/VG take one node and generate multiple nodes.
Another way to obtain the ML is to use "elements" array.
"elements" property
“elements" accept an array of node IDs, the node IDs must be a valid node ID on the screen or it will generate error. When “elements" is specified, the search will not be performed. It is useful to apply actions on specific nodes or debugging purpose. Each query with "elements" will traverse all nodes to locate corresponding node, try to put multiple IDs in the array instead of one at a time.
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"})
Popup window
There are 2 types of popup windows, they appear the same but they are implemented differently:
For second type of popup window, FindNode can identify some of the popup windows (Many Google applications use this type of popup), If the popup window breaks the FindNode, it will exhibit with one the following behaviors:
When this happens, close (remove from memory) and reopen application, if popup window does not work, please contact support.
Testing
To find the nodes via Query is not very difficult, you need is "UI Explorer" and Terminal:
Template in Detail
Template (TP) is a required field, TP returns list nodes based on the values, it is the initial list of nodes for BQ, EQ or actions. TP is a subclass of Selector, maybe able to allow users to define new template in the future. Several rules in TP:
The current supported templates:
TP:all, more, basic, reduced
These templates go thru the nodes in entire screen:
UI Explorer: 3 selections:
Example:
The first getCount without argument, will get the count of "ML", without "query", by default is "TP:more", so it has the same count as "TP:more". "+TP:all" will modify the 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]]
This template will return the text in anyText or description in anyDescription with non-null content. Optionally can specify the minimum and maximum length (inclusive) of the text found. If no max is specified, the maximum length will not be enforced.
>> 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>
Return the nodes with content in text or description that matches specified text. Text argument is required or generate an error.
Example:
>> 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
This template returns the nodes which are "editable". If no nodes are found, it will generate an error. Alternatively, you can use "BP:editable" to obtain the same results.
Example:
Obtain all text field:
>> 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']}
Locate the text field:
Last text field: query:"BP:editable&&IX:-1"
Based on the hint: query:"TP:textInput&&T:name"
Use "setText" to change the content:
>> 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,<top|bottom>,<line number>
Currently, this only applies to app with scrollable container or TP:line will return null. FindNode will locate the UI elements outside of scrollable nodes, line mode will group UI elements into a series of lines, each line can have one or more nodes. Many applications have fixed UI elements on the top and bottom of the screen, specific node cannot located with conjunction of BQ or EQ. For instance "TP:line,top,1&&IX:2" will return the first line, second item or "TP:line,bottom,-1&&T:Chats" will return bottom of the line with Chats as text.
>> 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[,<position>]
This template returns the leaf nodes of the indicated scrollable. "position" is the number starts from 0, top to bottom. Can obtain better result with resource ID in scrollable nodes.
Example:
To obtains all scrollable nodes in the current screen:
>> 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'}]}
Example:
Return all title of the Settings (in Samsung phones):
>> 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']}]}
Examples:
>> 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']}
Basic Query in Detail
Basic query takes ML from TP and perform node-level matching, one node at a time and create a shorter ML, discard nodes that do not match. BQ extracts data from Accessibility, it contains single-node information and actions. Extend more functionalities in the query for more powerful matching (special characters) and added shortcuts to make query more concise.
Special Characters in Query
For number such as inputType or childCount, it can either accept number or string:
String can be one of the following, "\n" to match newline character:
"!" is use at the beginning of string as NOT. "!0" = not zero. "C:!/Layout/", non-layout classes.
For simplicity in examples, the standard prefix of {"cmd": "action","action": "findnode", "params":{…}} will be omitted, we will list the examples in the "param" object and use JavaScript object notations to reduce number of double of quotes.
Shortcuts
Two shortcuts are available for class name (C) and resource ID (R), prefix with “." for substitutions of commonly use prefixes:
The "." notation can only be used for normal matching and "!" matching, other types of matching (e.g. regular expression and wildcard) needs to use full form.
P:<package name>, C:<class name>, R:<resource ID>
All nodes contain package name and class name and optional resource ID. Since the matching is performed on the screen that running the application, "P" will default to the app running, other "P" does not make sense. So "P" is not required.
T:<text>, D:<text>, TD:<text>
Two most important keys, the node with information usually carry text or description, this is much more accurate and faster than OCR. "until" command can monitor if the text or description has been changed. T:<text> with special characters are often used. "TD" match text or description, match is successful if any one of them match.
device.sendAai({query:"T:OK", action:"click"})
Click OK button
Bound In (BI):
Bound in takes screen coordinate or bounds and return the nodes based on the criteria below. This mainly for screen manipulation such as UI Explorer or debugging purposes. FindNode's object mode attempt to find nodes in devices independent of resolutions and sizes, "BI" coordinate-based will not work on all devices.
Example:
{query:"BI:[-1, 1000]&&IX:-1", action:"click"}
{query:"BI:[0, 0, 1000, 1000]", action:"getBounds"}
CC:<child count>, IT:<input type>
These are property found in the node, seldom use. CC is number of children, input type usually a number text field.
BP:<property>
Boolean property is important in the node, these properties carry true or false value. Multiple values are allowed, does not support "!" as not at this time. Here are the properties:
Basic Query Example
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']}
Can include more complex query like:
>> device.sendAai({query:"T:/^.$/&&C:.Button&&R:.button_*", action:"getText"})
{retval: ['÷','×','7','8','9','-','4','5','6','+','1','2','3','%','0','.','±','=']}
If the match cannot be found, it will return null, use "lastError()" to display error message:
>> 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"
Can check for null programmatically.
Expanded Query in Detail
There are the query left that is not BQ or TP. Keys in EQ are often calculations and heuristics that involve multiple nodes. EQ performs the keyed functions from left to right, manipulate along the way, hopefully at the end, the ML contains nodes that users want. The ML is uninteresting without actions, resulting ML will be passed to actions to apply actions or extract information. Keys can be applied multiple times "OX:1&&OX:1" is allowed.
Index (IX) M → O
IX:<number>. Return a node from ML nodes based on the position starting from zero (IX:0). IX can be negative value in which case the position is in reversed order (e.g. -1 is the last node).
Examples:
>> 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']}
Single Node Selection (ON) M → O
ON:<option> Return one node from multiple nodes in ML, currently 3 options are available. Only limited options at this time, will expand in the future.
These are the options:
Offset (OX & OY) O → O
OX:<integer> and OY:<integer>. Use the offset to find neighboring node base on one reference node, OX find horizontal position, positive integer moves right, negative integer moves left. For OY, positive integer moves down, negative integer moves up.
Examples:
In About phones:
>> 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'}
Examples:
>> 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: '−'}
ViewGroup (VG) O → M
ViewGroup: VG or VG:<level>. This command accepts optional level, if level is not specified, 1 will be used as level. It accepts a node (from query), will do parent traversal until a "group" is found, level is used to specify when to stop. A "group" is layout that occupy 85% ("setConfig(selector:viewGroupWidthRatio, <percentage>)" to change that) of the screen width or "ViewGroup" in the class. ML will contain the nodes within the group, with the first node is ViewGroup/Layout node itself. It is very useful to logically group interesting nodes and use "PQ" or "addQuery" to search the nodes within the group.
Related action: "getViewGroup".
Examples:
Pick the right level:
>> 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) O → M
Intersect: TX and TY. These commands take a node as a reference node and change the ML to contain all the nodes that intersect horizontally (intersectX) or vertically (intersectY). The matcher will make use of the bounds (top/bottom for intersectX, left/right for intersectY) of the reference node to search for intersected nodes.
The size (width or height) of the reference node is very important, if a wide reference node is picked, TY will match most of the nodes on screen. Pick reference node wisely.
This command will take first node and generate multiple nodes in ML, you can use "addQuery" to set more constraints on them.
See also: "intersectX" and "intersectY".
Examples:
On the offset calculator example above:
>> 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']}
But if you pick result as reference node with wide scroll entire screen, OX will get one node (itself) and OY will get all nodes.
>> 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']}
Reduce nodes (RN) M → M
Reduce Nodes: RN. This is the one of the important features of FindNode, this feature accepts a list of node IDs, the intent to reduce the number of visible nodes. "TL", "BL", "OX/OY" and "intersect" all use the reduceNodes. Note that the reduceNodes will pick smaller nodes if bigger nodes are fully contain the smaller nodes. For instance, if smaller ".Button" node is enclosed within ".TextView" node, the ".TextView" node will be selected over ".Button" node, the "click" on the .TextView node will still work. See UI Explorer "Optimize" mode.
Example:
>> device.sendAai({}).count
242
>> device.sendAai({query:"RN"}).count
55
Related action:"reduceNodes".
Sort (ST) M → M
Sort: ST:X|Y|YX. Sort based on the bounds of the nodes, usually the way three is structure and the search, quite likely it has been ordered from top to bottom. Unless the order is important, there is little reason to sort.
X – compare the left bounds of the nodes
Y – compare the top bounds of the nodes
YX – compare top bounds and then left bounds of the nodes
>> 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','-','+','%','=']}
Related action: sort
Basic query(BQ) M → M
Basic query: BQ:'<basic query>'. BQ is the command in EQ. Consider the following query (Obtained from UI Explorer Custom Query):
Query: T:Overall Cpu Usage&&VG
"VG" is used to obtain the group, if we want to obtain the number of "Cpu Cores", the following query will not work:
T:Overall Cpu Usage&&VG&&T:Cpu Cores&&OX:1 T:Cpu Cores&&OX:1&&VG
Two "T" commands are basic commands, second T will overwrite first T, since BQ commands applied before EQ. If we need the functionalities of basic query in EQ so the text can be matched. There 2 ways to do it:
Both will work:
>> 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 offers many action commands, "action" can accept a single string command or an array of strings with multiple commands. Query is optional, if query is not specified, it will include the default template. If query is specified, the query output will be saved to ML, the commands/actions require nodes will be obtained from underlying ML either via query or optional query, use action:"getIds" to obtain ML content. If no action is specified, action:"getIds" is used. Either "action" and "actions" are acceptable.
device/devices.sendAai({query:"…", action:<action>})
device/devices.sendAai({query:"…", action:[<action 1>, <action 2>, …]})
device/devices.sendAai({action:[<action 1>, <action 2>, …]})
When sendAai encounter error from FindNode or timeout, it will return null with "lastError()" contain error message.
{action:"openApp('Skype')"}
Since "Skype" is string, the quote can be omitted:
{action:"openApp(Skype)"}
For action that needs one node (e.g. “click”), the first node in the ML will be chosen (ON, IX, OX and OY can change that). There are many commands in actions, improvements will be added on new versions that may introduce incompatibility, please use preAction:"version" (version 11) or action:"version" (version 12+) to ensure the version supports the commands required. Some commands require additional arguments (the arguments will be enclosed in parenthesis). Currently, the following types of arguments are supported:
A few examples:
action:"getNodes('C,R')"
If you want to use variable in the action commands, use one of the following 4 options:
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"]
If the argument is string, below 3 examples are identical:
action:["intersectX(OY:1, IX:2)", "getText"]
action:["intersectX('OY:1', 'IX:2')", "getText"]
action:[aaix("intersectX", "OY:1", "IX:2"), "getText"]
Most of the actions have optional query string in first argument, for instance:
{query:"T:John", action:"click"}
{actions:["newQuery(T:John)", "click"]} → {action:"click(+T:John)"}
{actions:["addQuery(T:Mary)", "click"]} → {action:"click(T:Mary)"}
The added query string will make command more concise. "newQuery" prefixed by "+" will clear ML, make the search and reconstruct the ML. "addQuery" will search based on the existing ML.
Synchronous actions
Great efforts have been put in to ensure the actions are synchronous, when a "click" is called, not only ensure the click is pressed synchronously, it will wait for certain "events" to ensure the rendering "starts" to happen, cannot ensure the rendering is completed. FindNode offers several ways to perform such check:
Optional Query (OQ)
Actions require nodes will obtain from the ML, actions require one node, will obtain the first node from ML. However, users can change the behavior by adding option query (OQ) in actions.
Example:
{query:"T:OK", action:"click"} → {action:"click(T:OK)"}
All OQ is optional, if OQ is not specified, the actions will take the nodes from the ML. If actions require one node (e.g. click), the first node in the ML will be used, if the actions support multiple nodes (e.g. getNodes or getText), the entire ML is used.
OQ is regular query language with different types identified by different prefixes, OQ likes other queries can generate one or multiple nodes, couple of query types will modify ML. Here are the supported 3 prefixes:
Assuming calculator, the numbers 1 to 9 in 3 rows, "7,8,9", "4,5,6" and "1,2,3" (top to bottom):
>> 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'}]}
Here is the difference between regular and "*":
>> 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'}]}
It looks like "*" and "+" is similar, consider the following, the "*" matches the list from "ML" (T:2 is not in ML), "+" search the entire screen content.
>> 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
Since regular query (no prefix) and query with updating ML ("*" prefix) will use ML, use of "TP" is only allowed in "newQuery" or "+" prefix:
>> 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 Commands
getBounds (M)
Return the bounds (format: [left, top, right, bottom]) of the nodes in ML, it returns 2 arrays one with all the IDs the other one with all the bounds.
Usage:
getBounds[(<query>)]
Return:
The array of bounds, each bound is an array of 4-tuples: [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)
Return the Boolean property of a node, it will return "editable", "progressible", "clickable", "checkable", "scrollable" or "longClickable". If no property is found, it will return null.
Usage:
getBoolProp [(<query>)]
Return:
The array of string properties.
>> 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)
Similar to getBoolProp except it traverse parent to its parent until it reaches the top node tree.
Usage:
getBoolPropInAncestors [(<query>)]
Return:
The array of array of property and node ID or null if not found.
>> device.sendAai({query:"T:John", action:"getBoolPropInAncestors"})
{boolProp: [['clickable','3622e'],['scrollable','105cd']], count: 2}
getChecked (O/M)
These commands apply to nodes with "checkable" nodes. Typically, toggle controls, checkboxes or radio buttons are checkable. For instance, class name of ".Switch" or ".Checkbox" are checkable.
Usage:
getChecked [(<query>)]
Return:
If input is one node, it will return true/false in "retval", if multiple nodes, retval will contain the array of true/false, for non-checkable nodes, it will show as "N/A". Return null if none of the checkable node is found. "BP:checkable" to find nodes that is checkable, "BP:checked" to find nodes that have been checked.
See also: setChecked
>> device.sendAai({action:"getChecked(D:Sunday&&TX)"})
{retval: [false,true,true,true,true,true,false]}
If do not wish to use description, can use class to search, no other UI element is having class of ".CheckBox":
>> device.sendAai({action:"getChecked(C:.CheckBox&&IX:0&&TX)"})
{retval: [false,true,true,true,true,true,false]}
To find all the checked description:
>> device.sendAai({action:"getDescription(D:Sunday&&R:.day_button_0&&TX&&XBP:checked)"})
{retval: ['Monday','Tuesday','Wednesday','Thursday','Friday']}
getDescription/getText (O|M)
Simpler version of "getNodes(T)" or "getNodes(D)", much easier to parse if you do not intent to know the node ID. It will detect one or multiple nodes. getDescription/getText are identical except one get the text the other one get the description.
Usage:
getDescription [(<query>)]
getText [(<query>)]
Return:
Return an array of text/description in retval if multiple nodes are found, return text/description in retval if one node is found.
>> device.sendAai({query:"LB:-1", postAction:"getText"})
{retval: ['Chats','Calls','Contacts','Notifications']}
>> device.sendAai({query:"LB:2||IX:1", postAction:"getText"})
{retval: 'Calls'}
getFocus
Return the node receiving focus ("focusable" in getNodes(BP)) (usually text field or slider) or null if no node gains focus. If node is found, it will set the ML to the found node.
Usage:
getFocus
Return:
Return "node" will node ID if found otherwise return null.
Example:
To enter the next line from the existing line
>> device.sendAai({action:["getFocus", aaix("setText", username), "addQuery(OY:1)", aaix("setText", password)]})
{count: 4, list: [{node: '67693'},{retval: true},{count: 1},{retval: true}]}
getIds (M)
This is the default action if action is not defined, this command will return count and array of the ML IDs.
Usage:
getids [(<query>)]
Return:
count and array of IDs.
>> 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)
This is used to retrieve the information of nodes. The fields can determine what to display. "getNodes" will refresh the node before retrieving information.
Usage:
getNodes
getNodes([<query>] [,<fields>])
Arguments:
fields: The optional "fields" is used to define the fields to return, multiple fields in "fields" are separated by comma. Since it has comma inside the "field", you need to use the quote. String The valid field identifiers are:
Return:
Count and array of node information.
>> 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
Return the current running package name.
Usage:
getPackageName
Return:
Package name in "retval".
>> device.sendAai({action:"getPackageName"})
{retval: 'com.google.android.gm'}
getProgress (O)
Return the values of the progress type of UI element such as slider (shorthand of "getNodes(RI)"):
Usage:
getProgress [query]
Return:
the RangeInfo in "retval" of a progressible node, return null if the node is not progressible node:
RangeInfo has one of 3 types: "float" – floating point between min and max, "int" integer between min and max and "percent" between 0 and 1. "current" contains the current value.
>> device.sendAai({action:"getProgress(C:.SeekBar)"})
{retval: {current: 41, max: 100, min: 0, type: 0, typeString: 'int'}}
See also: setProgress
getQuery/getUniqQuery (O)
Accept one node and attempt to generate a query to match the node (or multiple nodes). The query returned from "getQuery" can match multiple nodes, useful to get the text or description from multiple nodes. The query returned from "getUniqQuery" will match only one node, the query obtained will be sent to other devices to locate the nodes. So far, it is rudimentary, we will develop better query string in the future:
Usage:
getUniqQuery/getQuery - Default is to use text and description as part of the query.
getUniqQuery/getQuery(<true/false>) – true to ignore text in search query.
getUniqQuery/getQuery(<true/false>, <true/false>) – true on second argument to ignore description in search query.
Return:
return query string.
To obtain query for each node:
>> 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']}
Query Commands
The following commands with one exception (waitQuery) will involve ML.
load/save (M)
These commands store or retrieve the ML, this is only useful for multiple commands, save the ML, newQuery + actions, load and continue.
Usage:
load
save
intersectX/intersectY (O → M)
If query is specified, the first node of the query will be used for intersect otherwise ML first node will be used. This command will take one node and generate more nodes and store in ML.
Usage:
intersect?[(<query>)]
Return:
true /false: if the operation is successful. If the operation is successful, it will return "count"
If "post query" is specified, the output of post query (see "addQuery") will be stored as ML. If "post query" is not specified, the output of intersect will replace the ML.
See also: "TX" and "TY" queries for more explanation.
Example:
Assuming calculator app
>> device.sendAai({actions:["intersectX(T:3)", "getText"]})
{count: 2, list: [{count: 4},{retval: ['1','2','3','%']}]}
"intersect Y" may include other none visible nodes such as result or viewgroup.
>> device.sendAai({actions:["intersectY(T:3)", "getText"]})
{count: 2, list: [{count: 8},{retval: [{},{},{},'×','9','6','3','±']}]}
Add a regular expression to ensure at least one character is visible:
>> device.sendAai({actions:["intersectY(T:3)", "addQuery(T:/.+/)", "getText"]})
{count: 3, list: [{count: 8},{count: 5},{retval: ['×','9','6','3','±']}]}
This will work too:
>> device.sendAai({actions:["intersectY(T:3)", "getText(T:/.+/)"]})
{count: 2, list: [{count: 8},{retval: ['×','9','6','3','±']}]}
getViewGroup (O → M)
This command is similar to "VG" query, will accept a level and query. Query if defined is used to identify the first node to seach for the group. Similar to "VG", it will change ML.
Usage:
getViewGroup
getViewGroup([<query>,] <level>)
Return:
If ViewGroup is found, it will return the count in the ML, if ViewGroup is not found, it will return null.
See also: Query "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)
It is identical to "RN" query, no argument. Will return number of nodes after results as "count".
Usage:
reduceNodes
Return:
Return the number of resulting nodes in "count".
See also: Query "RN".
>> device.sendAai({query:"TP:reduced"}).count
54
>> device.sendAai({action:"reduceNodes"})
{count: 54}
Sort (M → M)
Sort based on the node bounds. It is identical to "ST" query, same argument as ST.
Usage:
sort(X|Y|YX)
Return:
Return true/false in "retval".
See also: Query "ST".
newQuery/addQuery/waitQuery
All accept query string, "newQuery" will start the new query and recreate ML (similar to "+" in OQ). "addQuery" perform search within ML, place the resulting nodes in ML (similar to "*" in OQ). "waitQuery" will not change the ML, it has a timeout, will make sure the query is fulfilled before timeout expire or it will return null.
For instance: "VG" will usually follow by "addQuery" instead of "newQuery". On the other hand, click to open a new window, will need "newQuery".
newQuery(<query>)
addQuery(<query>)
waitQuey(<query>[,
Return:
newQuery and addQuery return the size of resulting ML in "count". When timeout expire waitQuery will return null otherwise "retval" will contain true.
Action Commands
Commands in this section perform actions to the nodes.
click, nsClick, click2, longClick
Various kinds of clicks on the node, this is synchronous click, will make sure screen is starting to refreshed when the control is returned.
All the above clicks will monitor the events for the changes of the screen after the click is performed, if the screen is not changed, these clicks will fail and eventually timed out. If the screen is not changed after the click is performed, use "nsClick":
Usage:
click/nsClick/click2/longClick [(<query>)]
Return:
true or false to indicate if the operation is successful.
click, nsclick, click2, longClick
click(<query>), nsclick(<query>), click2(<query>), longClick(<query>)
Example:
>> device.sendAai({query:"T:5", action:"click"})
{retval: true}
>> device.sendAai({action:"nsClick(T:5)"})
{retval: true}
refresh (M)
The accessibility nodes information is cached, if the screen has been updated, the cache information may not be accurate, use "refresh" to force to reread the nodes information.
Usage:
refresh [(<query>)]
Return:
Return count and true (always return true)
Example:
>> 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
Accept query string and scrolling direction, will try to match the query by scrolling up/down, when one or multiple nodes are found, it will return, if the match cannot be found, it will return null with an error. The scroll will use page up/down with the maximum of 10 pages, the scroll may take some time, this command will automatically lengthen the timeout in "sendAai" until it is done. In order to make script be able to run in any resolutions and sizes, use scrollIntoView is better than PageUp/Down.
Usage:
scrollIntoView(<query>) // Default down
scrollIntoView(<query>, <direction>)
Arguments:
<direction> can be one of the following, if not specify, the default is "down".
"down": Starting from current location and search by scrolling down.
"up": Starting from current location and search by scrolling up.
"top": Fast scroll to the top of page and search by scrolling down.
"bottom": Fast scroll to the bottom of page and search by scrolling up.
Return:
Will return null if the query cannot be found otherwise will return true, ML will be set to the first matched node ID.
sendKey
sendKey accepts key code or meta state and send the key to the screen (not node), the UI element with the focus will receive the keycode and meta, examples are text field or on screen keyboards. sendKey also offers shortcut, a text string some of the common key codes. Not all key codes will generate characters, several special key codes will bring in new window such as go back, appswitch or go to home screen. The key codes and meta states are displayed in Android KeyEvent class. "sendKey" sends to the screen, not a specific node so ML is not involved.
The shortcuts is nothing but a mapping to different key codes, following are all the shortcuts (case insensitive): "home", "back", "backspace", "enter", "appswitch" (switch to last app), for old applications, "search" and "menu".
Usage:
sendKey(<key code>)
sendKey(<key code>, <meta state>)
sendKey(<shortcut>)
Arguments:
key code and meta state: are in integer, "shortcut" is in string. Regarding the back key, if in Sigma input method input mode, single "back" key will send 2 "back" keys to the window, first back key to dismiss the input method, second back key to dismiss the window. Use "setConfig(sigmaInput:backKeyAutoDismiss, false)" to change the behavior.
Return:
return the status of the operation in true/false.
Example:
tcConst contains key code and meta state information. For example, the following 3 examples will get the same result:
sendKey('back')
sendKey(4)
sendKey(tcConst.keyCodes.BACK)
This will type "A":
>> device.sendAai({preAction:aaix("sendKey", tcConst.keyCodes.A, tcConst.keyCodes.META_SHIFT_ON)})
{retval: true}
setChecked (M)
This command apply to nodes with "checkable" nodes. Typically, toggle controls are checkable, checkboxes or radio buttons, example of class can be ".Switch" or ".Checkbox". For radio button, setChecked(false) will not work since FindNode does not know which radio button to click to disable the existing radio button.
Usage:
setChecked(true|false)
setChecked([query], true|false)
Arguments:
"setChecked(true|false)", since there is no permission to set the checked value, it will click the node to toggle the value if value needs to be changed. It accepts multiple nodes, will ignore non-checkable nodes.
Returns:
setChecked returns "changedCount", how many checkable nodes have been changed.
setProgress (O)
To set the value of the slider bar, the min, max, type, value can be obtained by "getNodes('RI’)",
Usage:
setProgress(<number>)
setProgress(<query>,<number>)
Arguments:
<number>: Integer or decimal to set the value determined by "type", "min" and "max"
Return:
true/false: if the operation is successful.
See also: getProgress
Example:
Set the display brightness to 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)});
}
)
setText (O)
If the node is text field, it will enter "input string" value to the input text field programmatically, no Sigma input method is involved.
Usage:
setText([query], <input string>)
setText([query], +<input string>)
Arguments:
Input string: By default the text field will be cleared before entering the string, if the first letter is "+", it will add on to the existing string. For multiple lines of text field, use "\n" to skip to next line.
Return:
true or false to indicate if the operation is successful.
Example:
>> 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}
Enter Emoji via String.fromCodePoint:
>> device.sendAai({query:"TP:textInput",action:aaix("setText", String.fromCodePoint(0x1f60a))})
{retval: true}
Or mix with normal message:
>> device.sendAai({query:"TP:textInput", action:aaix("setText",
"hello" + String.fromCodePoint(0x1f643, 0x1f644) + "world")})
{retval: true}
showOnScreen (O)
This will ensure the node will appear fully on the screen, if associated UI element is partially displayed, it will scroll until the UI element is fully visible on the screen.
Usage:
showOnScreen [(<query>)]
Return:
true to indicate the screen has been scrolled, false to indicate the node is already fully visible on screen, nothing is done.
sleep
This command will pause execution on the milliseconds specified, it is useful for time related wait before actions.
Usage:
sleep(<time>)
Arguments:
time: Specify time to wait in milliseconds.
Return:
return true
until
until will wait for certain condition based on the option specified, if the condition is met, it will return true, if timeout expired it will return null with error. Currently 2 types are supported:
until([query], gone, <timeout>)
"gone" will wait until the specified node (first element in ML or OQ) disappear.
Example:
The following will wait until reset button is disappeared, both formats are supported:
>> 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}
until([query], changed, T|D, <timeout>)
Will check until timeout or text (T) or description (D) content is changed.
Example:
The following example check if Nasdaq index is changed:
>> 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'}]}
Other Commands
openApp/restartApp /closeApp version 11
3 commands to start, restart close the application on the device. All command accepts one argument, either a package name or the application display name (name show on the launcher), if multiple applications with the same name is matched, the first application will be used. No partial match is allowed, it is case insensitive match for application name. "closeApp" with no argument will close the existing application.
"openApp" will run the application, if the application is resided on the memory, it will resume the execution. "restartApp" will first close the application before start the application, if automations need to start application in a known state. "closeApp" will close the application (force-close). To open an application to reach a "stable known state", use "restartApp" and "waitQuery".
Usage:
openApp(<name>)
restartApp(<name>)
closeApp(<name>)
closeApp
Return:
retval: true for success or null with lastError() on failure.
openAndroidSetting
Will open the android system setting window based the setting name specified, the setting name will be converted to uppercase letter, " " will be converted to "_" and name will add "_SETTINGS" to suffix if it is not already there:
Provide part of the setting in https://developer.android.com/reference/android/provider/Settings, use "listAndroidSetting" for list of settings available in this command. This command will open the setting window. If the setting is not found, it will return null.
Usage:
openAndroidSetting(<Setting name>)
For instance: The following command open wireless setting:
device.sendAai({action:"openAndroidSetting(wireless)"})
The following open Apps setting:
>> device.sendAai({action:"openAndroidSetting(manage applications)"})
{retval: true}
"MANAGE_APPLICATIONS_SETTINGS", "manage applications setting ", "Manage Application" are the same.
listAndroidSettings
This command returns the list of settings as an array.
Usage:
listAndroidSettings
Return:
List of valid settings in array.
>> 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
Change the value of the FindNode (or Selector), various values are identified by name, use "getConfig" to obtain the value, "setConfig" to change the value and "unsetConfig" to revert back to the original value.
getConfig(<name>) → name: <name>, value:<value>
setConfig(<name>, <value>) → retval: true
unsetConfig(<name>) → retval: true
If the name is not found, it will return null.
Name | Type | Default | Description |
---|---|---|---|
text:maxQueryLength | Integer | 30 | Length of text and description limit to be replaced with "*". |
navi:maxPageScroll | Integer | 30 | Maximum number of page swipes to find a node |
navi:scrollVertSteps | Integer | 40 | Number of steps on page swipes, low value is fast and inaccurate, higher value is slow and accurate |
navi:slowScrollSteps | Integer | 100 | Number of swipe steps to make node fully visible |
sigmaInput:backKeyAutoDismiss | Boolean | true | Sigma input method, send extra back key to dismiss invisble keyboard. |
selector:defaultTemplate | String | more | The default template if template is not specified in query. |
Command Reference
FindNode Command Reference |
---|
Action Name | OQ | Arguments | Input | Output | ML Change | Prop |
---|---|---|---|---|---|---|
Get commands | ||||||
getBounds | Yes | - | All Nodes | ids:[<id>], bounds:[<bounds>] | - | - |
getBoolProp | Yes | - | First Node | retval:[<prop>] | - | - |
getBoolPropInAncestors | Yes | - | First Node | retval:[<prop>] | - | - |
getChecked | No | - | One Node All Nodes |
retval:<boolean> retval:[boolean|N/A] |
- | - |
getCount | Yes | - | All Nodes | count:<count> | - | - |
getDescription | Yes | - | One Node All Nodes |
retval:<text> retval:[<text>] |
- | - |
getFocus | No | - | None | node:<ID> | - | - |
getIds | Yes | - | All Nodes | ids:[<ID>] | - | - |
getNodes | Yes | [fieldList(S)] | All Nodes | list:[<info>] | - | - |
getPackageName | No | - | None | retval:<PackageName> | - | - |
getProgress | Yes | - | First Node | retval:<rangeInfo> | - | - |
getQuery | No | [igText(B)] [IgDesc(B)] |
First Node | query:<query String> | - | - |
getText | Yes | - | One Node All Nodes |
retval:<text> retval:[<text>] |
- | - |
getUniqQuery | No | [igText(B)] [IgDesc(B)] |
First Node | query:<query String> | - | - |
Query Commands | ||||||
save | No | - | All Nodes | retval:<boolean> | - | - |
load | No | - | None | retval:<boolean> | Yes | - |
getViewGroup | Yes | [level(I)] | First Node | count:<count> | Yes | VG |
intersectX | Yes | [preQ(S)] [postQ(S)] | First Node | count:<count> | Yes | TX |
intersectY | Yes | [preQ(S)] [postQ(S)] | First Node | count:<count> | Yes | TY |
reduceNodes | No | - | All Nodes | count:<count> | Yes | RN |
sort | No | <type(S)> | All Nodes | retval:<boolean> | Yes | ST |
addQuery | Yes | - | All Nodes | count:<count> | Yes | - |
newQuery | Yes | - | None | count:<count> | Yes | - |
waitQuery | Yes | [duration(I)] | None | retval:<boolean> | - | - |
Action commands | ||||||
click | Yes | - | First Node | retval:<boolean> | - | - |
click2 | Yes | - | First Node | retval:<boolean> | - | - |
longClick | Yes | - | First Node | retval:<boolean> | - | - |
nsClick | Yes | - | First Node | retval:<boolean> | - | - |
refresh | Yes | - | All Nodes | retval:<boolean> | - | - |
scrollIntoView | Yes | [direction(S)] | None | retval:<boolean> | - | - |
sendKey | No | <code(I)> [meta(I)] | None | retval:<boolean> | - | - |
setChecked | Yes | <boolean> | All Nodes | changedCount:<count> | - | - |
setProgress | Yes | <value(F/I)> | First Node | retval:<boolean> | - | - |
setText | Yes | <text(S)> | First Node | retval:<boolean> | - | - |
showOnScreen | Yes | - | First Node | retval:<boolean> | - | - |
sleep | No | <duration in ms(I)> | None | retval:<boolean> | - | - |
until | Yes | <option> <arg> | None | retval:<boolean> | - | - |
Other commands | ||||||
version | No | - | None | retval:<version number> | - | - |
openApp | No | <name>(S)> | None | retval:<boolean> | - | - |
restartApp | No | <name>(S)> | None | retval:<boolean> | - | - |
closeApp | No | [<name>(S)>] | None | retval:<boolean> | - | - |
openAndroidSetting | No | <setting(S)> | None | retval:<boolean> | - | - |
listAndroidSettings | No | - | None | list:[<settings>] | - | - |
getConfig | No | <name(S)> | None | name: <name>, value: <value> | - | - |
setConfig | No | <name(S)> <value(*)> | None | retval:<boolean> | - | - |
unsetConfig | No | <name(S)> | None | retval:<boolean> | - | - |
(S) - String, (I) - Integer, (B) - Boolean, (F) - Float, (*) - All types [Optional Arguments] or <name>:[<return array>] |
||||||
device/devices.sendAai({query:<query>, action:<command>})
device/devices.sendAai({query:<query>, actions:[<command>]}) |
Support
Please send email to support@sigma-rt.com for support and feedbacks.