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
- XPath selector; see : https://www.jianshu.com/p/c205334122b3 and https://www.runoob.com/xpath/xpath-syntax.html
- Use the IDE node panel to inspect xpath attributes and test xpath
- Requires EC 10.12.0+
- @param value e.g. //node[@text=‘Sample’] selects nodes with that text
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();