Skip to main content

Selectors & Nodes

Overview

This chapter covers selector usage and the node info class.

Selector Objects

  • Selector objects support cascading selection. When an element cannot be selected directly, select a parent first, then a child.
  • Selectors partially support regex matching. See Java regex syntax in this tutorial.

XPath Selection

function main() {
// Get selector object
var selector = xpath("//node[@clz='android.widget.TextView']");
let n = selector.getNodeInfo(1000);
logd(JSON.stringify(n))
}

main();

text Attribute Selection

Full Text Match

function main() {
// Get selector object
var selector = text("Settings");
click(selector);
}

main();

Regex Match

function main() {
// Get selector object
var selector = textMatch(".*Settings.*");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

id Attribute Selection

Full Match

function main() {
// Get selector object
var selector = id("com.xx:id/a1");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

Regex Match

function main() {
// Get selector object
var selector = idMatch(".*id8.*");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

clz Attribute Selection

Full Match

function main() {
// Get selector object
var selector = clz("android.widget.TextView");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

Regex Match

function main() {
// Get selector object
var selector = clzMatch(".*TextView.*");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

pkg Attribute Selection

Full Match

function main() {
// Get selector object
var selector = pkg("com.xx");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

Regex Match

function main() {
// Get selector object
var selector = pkgMatch(".*tencent.*");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

desc Text Attribute Selection

Full Match

function main() {
// Get selector object
var selector = desc("My description");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

Regex Match

function main() {
// Get selector object
var selector = descMatch(".*Description.*");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main()

Depth and Drawing Order Matching

drawingOrder

function main() {
// Get selector object
var selector = drawingOrder(1);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

depth

function main() {
// Get selector object
var selector = depth(1);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

other Matching Rules

visible Visible Attribute Match

  • Match on visible attribute
  • @param value string
  • @return {Selector} node selector
function main() {
var node = visible(true).getOneNodeInfo(1000);
logd("node " + node);
}

main();

bounds Bounds Match

  • Match on bounds region
  • @param left Left bound
  • @param top Top bound
  • @param right Right bound
  • @param bottom Bottom bound
  • @return {Selector} node selector
function main() {
// Get selector object controls within bounds 0–800
var selector = bounds(0, 0, 800, 800);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

checkable

function main() {
// Get selector object
var selector = checkable(true);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

checked

function main() {
// Get selector object
var selector = checked(true);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

clickable

function main() {
// Get selector object
var selector = clickable(true);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

longClickable

function main() {
// Get selector object
var selector = longClickable(true);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

scrollable

function main() {
// Get selector object
var selector = scrollable(true);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

focusable

function main() {
// Get selector object
var selector = focusable(true);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

enabled

function main() {
// Get selector object
var selector = enabled(true);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

focused

function main() {
// Get selector object
var selector = focused(true);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

selected

function main() {
// Get selector object
var selector = selected(true);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

index

function main() {
// Get selector object
var selector = index(1);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

childCount

function main() {
// Get selector object
var selector = childCount(1);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

row

function main() {
// Get selector object
var selector = row(1);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

column

function main() {
// Get selector object
var selector = column(1);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

rowSpan

function main() {
// Get selector object
var selector = rowSpan(1);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

columnSpan

function main() {
// Get selector object
var selector = columnSpan(1);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

rowCount

function main() {
// Get selector object
var selector = rowCount(1);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

columnCount

function main() {
// Get selector object
var selector = columnCount(1);
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

Cascading Matching

function main() {
// Get selector object
// Select children under ScrollView parentclz=android.widget.CheckBox all nodes
var selector = clz("android.widget.ScrollView")
.child()
.clz("android.widget.CheckBox");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

Multi-attribute Matching

function main() {
// Get selector object,
// Select CheckBox elements containing "Selector" with checked=true
var selector = textMatch(".*Selector.*")
.checked(true)
.clz("android.widget.CheckBox");
var result = click(selector);
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

Node Info Class

Overview

NodeInfo objects are returned as arrays from getNodeInfo; node fields include:

  • id: string — resource ID
  • clz: string — view class name, e.g. android.widget.TextView
  • pkg: string — package name, e.g. com.xx
  • desc: string — content description
  • text: string — text content
  • checkable: Boolean — whether checkable
  • checked: Boolean — whether checked
  • clickable: Boolean — whether clickable
  • enabled: Boolean — whether enabled
  • focusable: Boolean — whether focusable
  • focused: Boolean — whether focused
  • longClickable: Boolean — whether long-clickable
  • scrollable: Boolean — whether scrollable
  • selected: Boolean — whether selected
  • childCount: int — number of child nodes
  • index: int — node index
  • depth: int — node depth
  • drawingOrder: int — drawing order
  • bounds: Rect — bounds object
  • top: int — top position
  • bottom: int — bottom position
  • left: int — left position
  • right: int — right position
  • visibleBounds: Rect — visible bounds object
  • top: int — top position
  • bottom: int — bottom position
  • left: int — left position
  • right: int — right position

Get one node with selector getOneNodeInfo

  • Get the first node via selector
  • @param timeout Wait time in ms; 0 = no wait
  • @return {NodeInfo} object or null
function main() {
// Get selector object
// Select all CheckBox nodes: clz=android.widget.CheckBox
var node = clz("android.widget.CheckBox").getOneNodeInfo(10000);

if (node) {
var x = node.click();
logd(x);
} else {
toast("No node");
}
}

main();

Get multiple nodes with selector getNodeInfo

  • Get node information
  • @param timeout Wait time in ms; 0 = no wait
  • @return {array} Collection of NodeInfo nodes
function main() {
// Get selector object
// Select all CheckBox nodes: clz=android.widget.CheckBox
var node = clz("android.widget.CheckBox").getNodeInfo(10000);
// This is an array
for (let i = 0; i < node.length; i++) {
logd(JSON.stringify(node[i]))
}
}

main();

Cascade get one child node getOneNodeInfo

  • Get the first node via selector
  • @param timeout Wait time in ms; 0 = no wait
  • @return {NodeInfo} object or null
function main() {
// Get selector object
// Select all ViewGroup nodes: clz=android.widget.ViewGroup
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node) {
// Get child nodes
node = node.getOneNodeInfo(text("Ad"), 10000);
if (!node) {
toast("No child nodes");
return;
}
var x = node.click();
logd(x);
} else {
toast("No node");
}
}

main();

Cascade get multiple child nodes getNodeInfo

  • Get node information
  • @param timeout Wait time in ms; 0 = no wait
  • @return {array} Collection of NodeInfo nodes
function main() {
// Get selector object
// Select all ViewGroup nodes: clz=android.widget.ViewGroup
var node = clz("android.widget.ViewGroup").getNodeInfo(10000);
if (node) {
// Get child nodes
node = node.getNodeInfo(text("Ad").clz("android.widget.TextView"), 10000);
if (!node) {
toast("No child nodes");
return;
}
var x = node.click();
logd(x);
} else {
toast("No node");
}
}

main();

Get parent node

  • Parent node of this node
  • @return {NodeInfo} object or null
function main() {
// Get selector object
// Select all CheckBox nodes: clz=android.widget.CheckBox
var node = clz("android.widget.CheckBox").getOneNodeInfo(10000);
if (node) {
var x = node.parent();
logd(x);
} else {
toast("No node");
}
}

main();

Get child node

  • Get a single child node
  • @param index child node index
  • @return {NodeInfo} object or null
function main() {
// Select all ViewGroup nodes: clz=android.widget.ViewGroup
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node) {
var x = node.child(0);
logd(x);
} else {
toast("No node");
}
}

main();

Get all child nodes

  • Get all child nodes
  • @return {NodeInfo} node collection
function main() {
// Select all ViewGroup nodes: clz=android.widget.ViewGroup
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node) {
var x = node.allChildren();
// This is an array
for (let i = 0; i < x.length; i++) {
logd(x[i])
}
} else {
toast("No node");
}
}

main();

All sibling nodes

  • All sibling nodes of the current node
  • @return {NodeInfo} node collection
function main() {
// Select all ViewGroup nodes: clz=android.widget.ViewGroup
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node) {
var x = node.siblings();
// This is an array
for (let i = 0; i < x.length; i++) {
logd(x[i])
}
} else {
toast("No node");
}
}

main();

Previous sibling nodes previousSiblings

  • Sibling nodes before the current node
  • @return {NodeInfo} node collection
function main() {
// Select all ViewGroup nodes: clz=android.widget.ViewGroup
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node != null) {
let x = node.previousSiblings();
// This is an array
for (let i = 0; i < x.length; i++) {
logd(x[i])
}
} else {
toast("No node");
}
}

main();

Next sibling nodes nextSiblings

  • Sibling nodes after the current node
  • @return {NodeInfo} node collection
function main() {
// Select all ViewGroup nodes: clz=android.widget.ViewGroup
var node = clz("android.widget.ViewGroup").getOneNodeInfo(10000);
if (node) {
let x = node.nextSiblings();
// This is an array
for (let i = 0; i < x.length; i++) {
logd(x[i])
}
} else {
toast("No node");
}
}

main();

Random click in node region

  • Requires accessibility 7.0+ or gestures via agent service
  • Click node
  • @return bool; true on success, false on failure
function main() {
// Get selector object
// Select all CheckBox nodes: clz=android.widget.CheckBox
var node = clz("android.widget.CheckBox").getOneNodeInfo(10000);
if (node) {
node.click()
} else {
toast("No node");
}
}

main();

Pointerless click clickEx

  • Requires accessibility 5.0+ or gestures via agent service
  • Pointerless click on selector; node must be clickable
  • @param selectors Selector object
  • @return {boolean}
function main() {
var node = text("Sample text").getOneNodeInfo(10000);
var result = node.clickEx();
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

Pointerless long click longClickEx

  • Requires accessibility 5.0+ or gestures via agent service
  • Pointerless long click on selector; node must be clickable
  • @param selectors Selector object
  • @return {boolean}
function main() {
var node = text("Sample text").getOneNodeInfo(10000);
var result = node.longClickEx();
if (result) {
toast("Click succeeded");
} else {
toast("Click failed");
}
}

main();

Click node center

  • Requires accessibility 7.0+ or gestures via agent service
  • Click node center point
  • @return bool; true on success, false on failure
function main() {
// Get selector object
// Select all CheckBox nodes: clz=android.widget.CheckBox
var node = clz("android.widget.CheckBox").getOneNodeInfo(10000);
if (node) {
node.clickCenter();
} else {
toast("No node");
}
}

main();

Long click node

  • Requires accessibility 7.0+ or gestures via agent service
  • Long click node
  • @return bool; true on success, false on failure
function main() {
// Get selector object
// Select all CheckBox nodes: clz=android.widget.CheckBox
var node = clz("android.widget.CheckBox").getOneNodeInfo(10000);
if (node) {
node.longClick()
} else {
toast("No node");
}
}

main();

Node input inputText

  • Requires accessibility 5.0+ or gestures via agent service
  • Input data on a node
  • @param content content to enter
  • @return bool; true on success, false on failure
function main() {
// Get selector object
// Select all EditText nodes: clz=android.widget.EditText
var node = clz("android.widget.EditText").getOneNodeInfo(10000);
if (node) {
node.inputText("content")
} else {
toast("No node");
}
}

main();

Node IME input imeInputText

  • Requires accessibility 5.0+ or gestures via agent service
  • IME input on a node; requires this app's IME as default
  • @param content content to enter
  • @return bool; true on success, false on failure
function main() {
// Get selector object
// Select all EditText nodes: clz=android.widget.EditText
var node = clz("android.widget.EditText").getOneNodeInfo(10000);
if (node) {
node.imeInputText("content")
} else {
toast("No node");
}
}

main();

imeInputKeyCode

  • IME input; requires this app's IME as default
  • For scenarios without nodes, e.g. games
  • @param selectors Selector; may be empty if input field is focused
  • @param content See KeyEvent.KEYCODE_* values, e.g. 66 = enter, 67 = del, 84 = SEARCH
  • @return {boolean}
function main() {
var result = clz("android.widget.EditText").getOneNodeInfo(10000);
if (result) {
result.imeInputKeyCode(66);
toast("Yes");
} else {
toast("No");
}
}

main();

Clear node text

  • Requires accessibility 5.0+ or gestures via agent service
  • Clear node text
function main() {
// Get selector object
// Select all EditText nodes: clz=android.widget.EditText
var node = clz("android.widget.EditText").getOneNodeInfo(10000);
if (node) {
var r = node.clearText();
logd("r -=" + r);
} else {
toast("No node");
}
}

main();

Refresh node

  • This method refreshes the node cache
function main() {
// Get selector object
// Select all EditText nodes: clz=android.widget.EditText
var node = clz("android.widget.EditText").getOneNodeInfo(10000);
if (node) {
node.refresh();
} else {
toast("No node");
}
}

main();

Check node valid

  • Whether node info is valid @return bool|Boolean true if present
function main() {
// Get selector object
// Select all EditText nodes: clz=android.widget.EditText
var node = clz("android.widget.EditText").getOneNodeInfo(10000);
if (node) {
var x = node.isValid();
toast("Node valid:" + x);
} else {
toast("No node");
}
}

main();

Scroll forward scrollForward

  • Requires accessibility 5.0+ or gestures via agent service
  • Scroll forward
  • @param selectors Selector object
  • @return {boolean}
function main() {
var node = scrollable(true).getOneNodeInfo(10000);
var result = node.scrollForward();
if (result) {
toast("Scroll succeeded");
} else {
toast("Scroll failed");
}
}

main();

Scroll backward scrollBackward

  • Requires accessibility 5.0+ or gestures via agent service
  • Scroll backward
  • @param selectors Selector object
  • @return {boolean}
function main() {
var node = scrollable(true).getOneNodeInfo(10000);
var result = node.scrollBackward();
if (result) {
toast("Scroll succeeded");
} else {
toast("Scroll failed");
}
}

main();