AAIS
Overview
AAI Script (AAIS) is very small script built on top of AAI's FindNode, it has a very simple syntax and with a dozen of commands, AAIS is not a replacement of complex JS/REST API, it is useful to write simple tests or throw away scripts. AAIS parser will translate command to JavaScript (and FindNode commands) during execution. It has several properties that is different from JavaScript:
Multiline support
AAIS supports rudimentary multiline supports. There are 2 ways to do multiline support:
"<text1>\
<text2>"
This applies to all double quotes, "\" at the end of the line will allow the concatenation of texts across multiple lines. For example:
{var a = 1, b = 2}
print "The sum of \
${a} and \
${b} is \
${a+b}"
Will display the "The sum of 1 and 2 is 3" in the log file.
{ <query/action>
<query/action> }
This primarily applies to "sendAai", complex commands may take multiple arguments to accomplish the task. Unmatched "{}" or "[]" will receive scripting error.
{var tp = "all"}
// 7 lines command
sendAai {actions:[
"getCount(+TP:\
${tp})",
"getCount(+TP:more)",
"getCount(+TP:basic)",
"getCount(+TP:reduced)"
]}
{
var output = getOutput();
firstNum = output.list[0].count;
lastNum = output.list[output.list.length -1].count;
log("firstNum = " + firstNum);
log("lastNum = " + lastNum);
}
print "From ${tp} template number of ${firstNum} reduced to ${lastNum}"
Log File:
2023-01-13 06:31:30 info system {name: 'samsung-SM-S908U1', object: 'device@1237418919', value: {count: 4, list: [{count: 162},{count: 69},{count: 64},{count: 63}]}, error: {}}
2023-01-13 06:31:30 info system firstNum = 162
2023-01-13 06:31:30 info system lastNum = 63
2023-01-13 06:31:30 info system From all template nodes number of 162 reduced to 63
Notes
AAIS Commands
click/longClick
Click or longclick on query string or text on the screen. If the query or text is not found, the script will stop. See FindNode User Guide on the query syntax. If "click" without argument after the "find" or "wait" commands, it will click the first match of the find command.
Usage:
click/longClick <text|query>
Examples:
click "T:About phone||OX:1"
longClick "Information"
// find and click John
find "John"
click
wait "Click for detail"
click
open/restart
open or restart an application, the restart will force-close the application and start again. If the application is not found, the script will stop.
Usage:
open/restart <package name>
open/restart <app name>
Examples:
open "com.raider.skype"
wait "Favorites"
restart "Skype"
wait
The command waits until the match is found or timeout expires. If the timeout expires, the script will stop. Without the timeout specified, the default is 5000ms. See FindNode User Guide on the query syntax.
Usage:
wait <text|query>
wait <text|query>, <timeout in milliseconds>
Examples:
wait "Finish"
wait "R:.icon_done", 20000
text
Enter text into the text field, the text field is located by position or initial hint string, the script will stop with error if the text field cannot be found. To accurate locate the text field by position, the text field will be sorted based on the text field location from top-left to bottom-right.
Usage:
text <text> // Enter text in the first text field
text <text>, <position> // position starts with 0 – first text field, 1 – second text field, …
text <text>, <hint> // Enter into the text field with the initial hint string
Examples:
text "Hello world"
text "Hello world", "Please input a message"
text "Hello world", 2
press
Press a keycode (and optional meta state) or keycode name, wrong keycode name will stop the script.
Usage:
press <keycode name>
press KeyCode, <keycode>
press KeyCode, <keycode>, <meta state>
Examples:
press Home
press KeyCode, 47, 1
Refer to https://developer.android.com/reference/android/view/KeyEvent for all the keycode names.
The following are the popular keycode name (case insensitive):
Enter
Back
Home
Back_space
Search
Power
Tab
find
It will scroll based on the direction on the screen until the match is found, it not found, the script will stop with error. The default is "down".
Usage:
find <text|query> [<direction>]
There are 4 directions:
Examples:
find "${name}"
find "T:OK||C:.Button"
click
exec
Execute another AAIS or JavaScript, if will stop if the script generates error. The default is on the Documents\scripts directory. If the extension is ".js" it is loaded as JavaScript, if the extension is ".tst", it is loaded as AAIS. "exec" JavaScript is either provide functions/constants to AAIS integration or perform tasks that cannot be done in AAIS.
Usage:
exec <file path and filename>
Examples:
exec "aais\\skype.tst"
exec "C:\\aais\\skype.tst"
exec "nestLib.js"
Print the text string to the log file.
Usage:
print <text>
delay
Temporary pause the execution of the script for specified milliseconds.
Usage:
delay <time in milliseconds>
Example:
delay 10000
check
The check is used to click a checkbox, it accepts true/false. "false" will remove the check and "true" will set check.
Usage:
check <text|query> true|false
Example:
Check "C:.Checkbox&&T:Milk"
progress
Progress is used to set a value on slider such as class name of ".SeekBar". It is shown as "rangeInfo" in FindNode (getNodes(RI)), it has type of integer or float with minimum and maximum value. Make sure the value is within the range.
Usage:
progress <query> <value in integer or float>
Example:
progress "T:Notifications&&OY:1&&OX:1", 1200
get
Obtain information from FindNode. It has 4 types of get (more will be added when required), the argument is optional. The return value will be saved to log file and obtained from JavaScript "getOutput()".
Usage:
get <query>, <type>
Type:
Example:
exec "volumeLib.js"
get "T:Notifications&&OY:1&&OX:1", “node”
{
var rangeInfo = getOutput().list[0].rangeInfo;
var current = rangeInfo.current;
var min = rangeInfo.min;
var max = rangeInfo.max;
var mid = (max-min)/4;
if (current > mid*3) {
tooLoud(getDevice(), current);
} else if (current < mid) {
tooSoft(getDevice(), current);
}
}
sendAai
sendAai use "device.sendAai" to send commands to FindNode, the return value will be saved to log file and obtained from JavaScript "getOutput()". Any failure (receive null from FindNode) will cause the script to stop. AAIS currently does not allow multiple lines per command, next version will support multi-line command.
Usage:
sendAai {<query or action>}
Example:
sendAai {actions:[
"openAndroidSetting(application development settings)",
"scrollIntoView(T:Window animation scale)",
"click",
"click(+T:Animation scale .5x)",
"sendKey(Back)",
"sendKey(Back)"]}
Integration with JavaScript
AAIS is a parser that read the AAIS script with ".tst" extension and generate JavaScript for execution, the integration with JavaScript is simple, enclose JavaScript in "{}". To pass data between AAIS to JS:
E.g.
{ <JavaScript> code }
Or
{
<JavaScript code>
}
Example:
exec "phoneLib.js"
get "T:Android version&&OY:1", "text"
{
setVersion(getDevice(), getOutput().retval);
}
Assuming "phoneLib.js" has "setVersion()" function defined.
Since AAIS script can run on multiple devices, all JS command block will be executed multiple times one at a time, once for each device. So in the above example, if 20 devices are selected, it will execute 20 "setVersion". "getDevice()" will return 20 difference devices.
You can exec (load) a JS library (with constant or functions) first:
exec <library>.js
The JS code block will be in the same context as "exec", so you can access them conveniently.
In addition, AAIS also comes with several functions:
exec "phoneLib.js"
get "T:Model name&&OX:1", "text"
{ saveVar("modelName", getOutput().retval) }
get "T:Model number&&OX:1", "text"
{ saveVar("modelNumber", getOutput().retval) }
get "T:IMEI&&OX:1", "text"
{
saveInfo(getDevice(),
loadVar("modelName"),
loadVar("modelNumber"),
getOutput().retval);
}
saveVar and loadVar is related to getDevice(), so you loadVar will always return the saveVar on the same device without interfering each other.
If you want to do just once instead of each device, use "doOnce()".
exec "runLib.js"
get "T:Run ID", "text"
{
if (doOnce()) {
saveDatabase(getOutput().retval);
}
}
Functions
There are several functions built into AAIS JavaScript:
getDevice() → device
Will return the current device.
getDevices() → Array of devices
Will return all the selected devices.
saveVar(<name>, <value>)
Will store the variable with the name specified, the variable is stored in scope of getDevice().
loadVar(<name>) → <value>
Will load the variable with the name specified and return the value. The value is only visible when store by saveVar() with the same getDevice().
saveGlobalVar(<name>, <value>)
Will store in the global scope identified by name, any device will be able to access it.
loadGlobalVar(<name>) → <value>
Will return the global scope identified by name.
doOnce() → true|false
Will return true on the first run, subsequent run will return false.
getOutput() → output from FindNode
This function only works for "get" and "sendAai", the JavaScript code block needs to immediately follow the "get" or "sendAai" commands, "getOutput" on other commands will generate error and stop the script execution. For the return format, see the content in the log file.
getArg() → array of strings
Runner allows users to pass arguments to JavaScript as an array of strings. This along with variable substitution can create the query/action during runtime. When command line mode is supported, getArg() will return the argument in command mode. There is a button in Runner to set the arguments.
log(<message>)
This function is similar to AAIS "print" function, it append the message into existing Runner's log file.
throw(<message>)
Since AAIS is translated to JavaScript, you can use throw(<message>) to stop the execution. Error message will be logged and display to the users.
"…${<expression>}…"
Expression in template literals allows any double quote to include JavaScript expression enclosed by ${…}. Useful for variable substitution or simple JavaScript expression.
AAIS Examples:
This example opens Tesla app and retrieve the VIN and mileage of the car:
exec "teslaLib.js"
open "Tesla"
find "T:VIN:"
get "T:VIN:&&OX:1", "text"
{ saveVar("vin", getOutput().retval) }
get "T:/[0-9,]+ miles/", "text"
{ saveMileage(new Date(), loadVar("vin"), getOutput().retval) }
print "Done"
Open app based on the index number, can feed in "app" from JavaScript to AAIS:
{
var apps = ["Skype", "Whatsapp", "Telegram"];
var arguments = getArg();
var app = apps[0];
if (arguments.length > 0) {
var appNum;
if(isNaN(appNum = parseInt(arguments[0]))) {
throw "Need a number;"
}
if (appNum < 1 || appNum > apps.length) {
throw "Option out of range";
}
app = apps[appNum-1];
}
}
open "${app}"
print "${app} opened"
Creating Scripts
Save the scripts into a text field with ".tst" extension, e.g. skype.tst, alternatively you can use Record and Replay to record the script in AAIS (you need to select Object mode) file.
Running Scripts
Running AAIS scripts in Runner, the result will be stored in "Task":