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:

  • Since it is built on top of FindNode, it inherits properties of FindNode:
    • Object-based, not coordinate based.
    • 100% compatible with FindNode's query language.
    • Support scripts in mixed device environment (different resolution and sized).
    • Use "find" to replace device dependent page down/up. "find" will scroll until the node is found.
  • There is no condition, no loop, no variable (only JS variables), not suitable for complex scripting.
  • Any failure/error will cause the script to stop.
  • It is built to run on multiple devices, users select set of devices (1 to 50), it will starts the script with one thread per device.
  • Support both MDCC (multi-device control center) and WDM (Windows desktop mode).
  • Good integrate with JavaScript, JavaScript code block is enclosed in "{}" at any new line. Will be looped multiple times based on the device count.
  • Any error messages or output from "print", "get" and "sendAai" will be kept in the log file. JavaScript log() can also be saved into log file.
  • "get" and "sendAai" allow JavaScript to obtain the output (getOutput()).
  • AAIS script can perform JavaScript's template literals, allow JS expression or variable substitutions via ${<expression>}.
  • "//" is comment characters, anything after "//" will be ignored.
  • AAIS supports multiple lines per command.

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

  • Make sure the braces and square bracket are matching or receive error.

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:

  • "down": From current position, search downward (default if not specify).
  • "up": From the current position, search upward.
  • "fromTop": From top of the app and start scroll down.
  • "fromBottom": Scroll to bottom of the app and search upward.

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

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:

  • "id": Obtain the id/ids of the query. No argument.
  • "text": Obtain the text information of the query matching nodes. No argument.
  • "description": Obtain the description of the query matching nodes. No argument.
  • "node": Obtain the information of the matching nodes based on the optional argument.

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:

  • AAIS "get" and "sendAai" output can be retrieved in JavaScript function getOutput().
  • All AAIS double quote can include expression in template literals "…${<expression>}…", this allow pass the variable from JS to AAIS.

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":

  • MDCC:
    • Open MDCC window.
    • Select the device by thumbnail device or select a group. Main device will always be selected.
    • Click "Runner".
    • Select one or multiple scripts, when the checkbox is clicked, it will show execution order.
    • Click Run button, it will run scripts on the devices. Error will be shown in the message box next to Runner. Click "Log" to access the log.
  • WDM:
    • In WDM device window, click menu "Runner".
    • Select one or multiple scripts, when the checkbox is clicked, it will show execution order.
    • Click Run button, it will run scripts on the devices.
    • Click the task window on the Runner first button".
    • The Task window will show up, can view the progress and generated log file.