AndroidNodeFilter
InterfaceBuilder pattern for complex node queries.
AndroidNodeFilter provides a fluent builder interface for constructing complex queries to find nodes in the accessibility tree. All methods are chainable and return the filter instance.
// Find a submit button
const button = screen.findAdvanced(f => f.isButton().hasText("Submit"));
// Find all editable text fields
const inputs = screen.filterAdvanced(f => f.isEditText().isEditable());
// Complex query with multiple conditions
const node = screen.findAdvanced(f =>
f.isClickable()
.hasPackageName("com.example.app")
.anyParent(p => p.hasId("com.example:id/main_container"))
);Class Matchers
Match nodes based on their Android class type.
isButton
isButton(): AndroidNodeFilterMatches nodes with className 'android.widget.Button'.
const buttons = screen.filterAdvanced(f => f.isButton());isText
isText(): AndroidNodeFilterMatches nodes with className 'android.widget.TextView'.
const labels = screen.filterAdvanced(f => f.isText());isImage
isImage(): AndroidNodeFilterMatches nodes with className 'android.widget.ImageView'.
const images = screen.filterAdvanced(f => f.isImage());isEditText
isEditText(): AndroidNodeFilterMatches nodes with className 'android.widget.EditText' (text input fields).
const inputs = screen.filterAdvanced(f => f.isEditText());isCheckBox
isCheckBox(): AndroidNodeFilterMatches nodes with className 'android.widget.CheckBox'.
const checkboxes = screen.filterAdvanced(f => f.isCheckBox());isRadioButton
isRadioButton(): AndroidNodeFilterMatches nodes with className 'android.widget.RadioButton'.
const radios = screen.filterAdvanced(f => f.isRadioButton());isSwitch
isSwitch(): AndroidNodeFilterMatches nodes with className 'android.widget.Switch'.
const switches = screen.filterAdvanced(f => f.isSwitch());isSeekBar
isSeekBar(): AndroidNodeFilterMatches nodes with className 'android.widget.SeekBar'.
const sliders = screen.filterAdvanced(f => f.isSeekBar());isViewGroup
isViewGroup(): AndroidNodeFilterMatches nodes with className 'android.view.ViewGroup' (container elements).
const containers = screen.filterAdvanced(f => f.isViewGroup());is
is(className: string): AndroidNodeFilterMatches nodes with a custom className.
| Parameter | Type | Description |
|---|---|---|
className | string | Full Android class name to match |
const recyclers = screen.filterAdvanced(f => f.is("androidx.recyclerview.widget.RecyclerView"));State Matchers
Match nodes based on their current state.
isClickable
isClickable(): AndroidNodeFilterMatches nodes that are clickable.
const clickables = screen.filterAdvanced(f => f.isClickable());isScrollable
isScrollable(): AndroidNodeFilterMatches nodes that are scrollable.
const scrollView = screen.findAdvanced(f => f.isScrollable());
if (scrollView) {
await scrollView.performAction(agent.constants.ACTION_SCROLL_DOWN);
}isSelected
isSelected(): AndroidNodeFilterMatches nodes that are currently selected.
const selectedItem = screen.findAdvanced(f => f.isSelected());isEditable
isEditable(): AndroidNodeFilterMatches nodes that allow text editing.
const editableInputs = screen.filterAdvanced(f => f.isEditText().isEditable());Content Matchers
Match nodes based on their text content.
hasText
hasText(text: string): AndroidNodeFilterMatches nodes with exact text content.
| Parameter | Type | Description |
|---|---|---|
text | string | Exact text to match |
const submitBtn = screen.findAdvanced(f => f.isButton().hasText("Submit"));text
text(condition: (text: string | undefined) => boolean): AndroidNodeFilterMatches nodes based on a custom text condition.
| Parameter | Type | Description |
|---|---|---|
condition | (text: string | undefined) => boolean | Function to test the text |
const priceNodes = screen.filterAdvanced(f => f.text(t => t?.includes("$") ?? false));const items = screen.filterAdvanced(f => f.text(t => t?.startsWith("Item") ?? false));hasDescription
hasDescription(description: string): AndroidNodeFilterMatches nodes with exact content description (accessibility label).
| Parameter | Type | Description |
|---|---|---|
description | string | Exact description to match |
const closeBtn = screen.findAdvanced(f => f.hasDescription("Close"));descriptionContains
descriptionContains(part: string): AndroidNodeFilterMatches nodes whose description contains the given text.
| Parameter | Type | Description |
|---|---|---|
part | string | Text to search for in description |
const icons = screen.filterAdvanced(f => f.isImage().descriptionContains("icon"));description
description(condition: (desc: string | undefined) => boolean): AndroidNodeFilterMatches nodes based on a custom description condition.
| Parameter | Type | Description |
|---|---|---|
condition | (desc: string | undefined) => boolean | Function to test the description |
const nodes = screen.filterAdvanced(f =>
f.description(d => d?.toLowerCase().includes("button") ?? false)
);hasChildWithText
hasChildWithText(text: string): AndroidNodeFilterMatches nodes that have a direct child with the specified text.
| Parameter | Type | Description |
|---|---|---|
text | string | Text to find in children |
// Find containers that have a "Settings" label inside
const settingsSection = screen.findAdvanced(f =>
f.isViewGroup().hasChildWithText("Settings")
);Identity Matchers
Match nodes based on their identity attributes.
hasId
hasId(id: string): AndroidNodeFilterMatches nodes with the specified viewId.
| Parameter | Type | Description |
|---|---|---|
id | string | View ID to match (e.g., 'com.example:id/button') |
const header = screen.findAdvanced(f => f.hasId("com.example:id/header_title"));hasPackageName
hasPackageName(packageName: string): AndroidNodeFilterMatches nodes belonging to the specified package.
| Parameter | Type | Description |
|---|---|---|
packageName | string | Package name to match |
// Only match nodes from a specific app
const appNodes = screen.filterAdvanced(f =>
f.hasPackageName("com.example.myapp")
);hasChildWithId
hasChildWithId(id: string): AndroidNodeFilterMatches nodes that have a direct child with the specified viewId.
| Parameter | Type | Description |
|---|---|---|
id | string | View ID to find in children |
// Find containers with specific child elements
const container = screen.findAdvanced(f =>
f.hasChildWithId("com.example:id/item_icon")
);Logical Operators
Combine multiple conditions using logical operators.
and
and(filterBuilder: (f: AndroidNodeFilter) => void): AndroidNodeFilterCombines with another filter using AND logic. The node must match both filters.
| Parameter | Type | Description |
|---|---|---|
filterBuilder | (f: AndroidNodeFilter) => void | Function to build the additional filter |
// Find clickable buttons with specific text
const btn = screen.findAdvanced(f =>
f.isButton().and(a => a.isClickable().hasText("Submit"))
);or
or(filterBuilder: (f: AndroidNodeFilter) => void): AndroidNodeFilterCombines with another filter using OR logic. The node must match either filter.
| Parameter | Type | Description |
|---|---|---|
filterBuilder | (f: AndroidNodeFilter) => void | Function to build the alternative filter |
const clickables = screen.filterAdvanced(f =>
f.isButton().or(o => o.isImage().isClickable())
);const node = screen.findAdvanced(f =>
f.hasText("OK").or(o => o.hasText("Confirm")).or(o => o.hasText("Yes"))
);Hierarchy Matchers
Match nodes based on their position in the node hierarchy.
parent
parent(filterBuilder: (f: AndroidNodeFilter) => void): AndroidNodeFilterMatches nodes whose immediate parent matches the given filter.
| Parameter | Type | Description |
|---|---|---|
filterBuilder | (f: AndroidNodeFilter) => void | Function to build the parent filter |
// Find text views inside a specific container
const labels = screen.filterAdvanced(f =>
f.isText().parent(p => p.hasId("com.example:id/header"))
);anyParent
anyParent(filterBuilder: (f: AndroidNodeFilter) => void): AndroidNodeFilterMatches nodes that have any ancestor matching the given filter.
| Parameter | Type | Description |
|---|---|---|
filterBuilder | (f: AndroidNodeFilter) => void | Function to build the ancestor filter |
const listItems = screen.filterAdvanced(f =>
f.isText().anyParent(p => p.hasId("com.example:id/user_list"))
);const dialogButtons = screen.filterAdvanced(f =>
f.isClickable().anyParent(p => p.is("android.app.Dialog"))
);Complex Examples
const loginBtn = screen.findAdvanced(f =>
f.isButton()
.hasText("Login")
.isClickable()
.anyParent(p => p.hasId("com.example:id/login_form"))
);const prices = screen.filterAdvanced(f =>
f.isText()
.text(t => /^\$\d+/.test(t || ""))
.anyParent(p => p.hasId("com.example:id/product_card"))
);
for (const price of prices) {
console.log("Price:", price.text);
}const emptyInput = screen.findAdvanced(f =>
f.isEditText()
.isEditable()
.text(t => !t || t.length === 0)
);
if (emptyInput) {
await emptyInput.performAction(agent.constants.ACTION_CLICK);
await agent.actions.writeText("Hello");
}const confirmBtn = screen.findAdvanced(f =>
f.isButton()
.isClickable()
.or(o => o.hasText("OK"))
.or(o => o.hasText("Confirm"))
.or(o => o.hasText("Yes"))
.or(o => o.hasText("Accept"))
);