Skip to content

Wait Conditions

The wait engine lets you pause execution until a condition is met. Built-in conditions cover common scenarios, and combinators let you compose them.

Basic Usage

from adbflow.wait import wait_for, TextVisible, ActivityIs, ScreenOn

async def wait_examples(device):
    # Wait for text to appear on screen
    await wait_for(
        TextVisible(device, "Welcome"),
        timeout=10.0
    )

    # Wait for a specific activity
    await wait_for(
        ActivityIs(device, "com.example.app/.MainActivity"),
        timeout=10.0
    )

    # Wait for screen to turn on
    await wait_for(ScreenOn(device), timeout=5.0)

If the condition isn't met within the timeout, WaitTimeoutError is raised.

Built-in Conditions

Condition True when...
TextVisible Text appears in the UI hierarchy dump
ActivityIs Foreground activity matches the given name
ScreenOn Device screen is on
ScreenOff Device screen is off
PackageRunning A package has a running process
ElementExists A custom async match function returns True

ElementExists

Use ElementExists for custom conditions:

from adbflow.wait import wait_for, ElementExists

async def custom_condition(device):
    # Wait until a specific element is found
    async def check():
        element = await device.ui.find_async(
            Selector().resource_id("com.example:id/loaded")
        )
        return element is not None

    await wait_for(ElementExists(check), timeout=10.0)

Combinators

Compose conditions with AnyOf, AllOf, and Not:

from adbflow.wait import wait_for, TextVisible, ScreenOn, any_of, all_of, not_

async def combinator_examples(device):
    # Wait for ANY condition to be true
    await wait_for(
        any_of(
            TextVisible(device, "Success"),
            TextVisible(device, "Error"),
        ),
        timeout=10.0
    )

    # Wait for ALL conditions to be true
    await wait_for(
        all_of(
            ScreenOn(device),
            TextVisible(device, "Ready"),
        ),
        timeout=10.0
    )

    # Negate a condition
    await wait_for(
        not_(TextVisible(device, "Loading...")),
        timeout=15.0
    )

    # Complex combinations
    await wait_for(
        all_of(
            ScreenOn(device),
            any_of(
                TextVisible(device, "Home"),
                TextVisible(device, "Dashboard"),
            ),
        ),
        timeout=10.0
    )

Device Shortcut Methods

The Device object has convenience methods for common waits:

async def device_shortcuts(device):
    # Wait for text
    found = await device.wait_for_text_async("Welcome", timeout=10.0)

    # Wait for activity
    found = await device.wait_for_activity_async(
        "com.example.app/.MainActivity",
        timeout=10.0
    )

Polling Interval

The interval parameter controls how often the condition is checked:

# Check every 200ms (faster but more CPU)
await wait_for(TextVisible(device, "Done"), timeout=10.0, interval=0.2)

# Check every 2 seconds (slower but lighter)
await wait_for(TextVisible(device, "Done"), timeout=30.0, interval=2.0)

The default interval is 0.5 seconds.

Tips

  • Use any_of when waiting for one of several possible outcomes (e.g., success or error dialog).
  • Use all_of when multiple conditions must hold simultaneously.
  • Use not_ to wait for something to disappear (e.g., a loading spinner).
  • Keep timeouts reasonable — a 60-second wait is usually a sign something is wrong.