Skip to content

App

device.app(package_name): the app's lifecycle, its permissions and languages, and its elements (only nodes of that package match). Guide: App lifecycle, App or screen.

App

One app on a device: its elements, its lifecycle and the waits on it. Obtain with device.app(package_name), which performs no I/O.

element and wait add the package as one more selector predicate, so only this app's nodes match (in any of its windows); device.screen matches anywhere. Lifecycle calls are executed by the server over ADB and verified against a postcondition; failures are AppLifecycleError.

Example::

app = device.app("com.example.shop")
app.cold_launch()
app.element(res("search")).set_text("socks")
app.wait(text("3 results")).visible()

Timeouts left as None use the device's client-side defaults (Timeouts) where the call has one, else the server's default.

Methods:

Name Description
element

A lazy element: selector restricted to this package's nodes. Nothing is looked

wait

A wait on selector restricted to this package's nodes; timeout (seconds)

is_installed

Whether the package is installed.

install

adb install -r -t of the APK at apk_path, verified. apk_path is a file on

uninstall

pm uninstall, verified.

force_stop

am force-stop plus proof that no process and no activity of the package remain.

clear_data

pm clear: data, cache and runtime permissions are gone; the app is left stopped.

grant_permission

pm grant a runtime permission, e.g. android.permission.CAMERA.

is_permission_granted

Whether permission is granted to the package now, as dumpsys package lists it:

set_locales

The app's own languages (Android's per-app language, API 33+), as BCP-47 tags in

locales

The app's own languages now (canonical BCP-47 tags); empty when it follows the

revoke_permission

pm revoke a runtime permission, proven by dumpsys package. Android kills the

launch

Starts the activity with am start -W and returns when Android reports the launch

await_visible

Waits on the device until this package owns the focused window. Raises

await_toast

Device.await_toast for this package's toasts only: on Android 11+ a text toast is

await_notification

Device.await_notification for this package's notifications only.

await_screen_stable

Waits on the device until this package's focused window has stopped changing for

await_settled

Maestro's waitForAppToSettle, on request only: this package's accessibility

await_animation_end

Maestro's waitForAnimationToEnd, on request only: this package's window pixels

foreground

Bring the app to the front the way the launcher does: its launcher intent with

background

Send the app to the background with the Home key, as a user would; the process keeps

open_link

Open uri (a deep link or app link, e.g. myapp://orders/42 or

cold_launch

Force-stops, launches (with extras, as launch) and returns the verified new

process

The single current process identity; waits briefly for it to exist.

is_running

Whether any process of the package is alive.

await_idle

Waits until the app's own sync contract reports idle for stable_for seconds.

device

device: Device

The device the app runs on.

package_name

package_name: str

The app's package name.

element

element(selector: Selector) -> Element

A lazy element: selector restricted to this package's nodes. Nothing is looked up until an action or query runs.

wait

wait(selector: Selector, timeout: float | None = None) -> ElementWait

A wait on selector restricted to this package's nodes; timeout (seconds) defaults to device.timeouts.wait.

is_installed

is_installed() -> bool

Whether the package is installed.

install

install(apk_path: str | PathLike[str], timeout: float | None = None) -> None

adb install -r -t of the APK at apk_path, verified. apk_path is a file on this machine: its bytes are streamed to the server in 1 MiB chunks, so the server may run elsewhere.

uninstall

uninstall() -> None

pm uninstall, verified.

force_stop

force_stop(timeout: float | None = None) -> None

am force-stop plus proof that no process and no activity of the package remain.

clear_data

clear_data(timeout: float | None = None) -> None

pm clear: data, cache and runtime permissions are gone; the app is left stopped.

grant_permission

grant_permission(permission: str) -> None

pm grant a runtime permission, e.g. android.permission.CAMERA.

is_permission_granted

is_permission_granted(permission: str) -> bool

Whether permission is granted to the package now, as dumpsys package lists it: the read-back for grant_permission, revoke_permission, clear_data and the permission dialog.

set_locales

set_locales(locales: Sequence[str]) -> None

The app's own languages (Android's per-app language, API 33+), as BCP-47 tags in preference order ("fr-FR", "en"); an empty list makes it follow the system again.

The server checks and canonicalizes the tags (INVALID_ARGUMENT for a malformed one) and reads the change back. Like the device conditions, the app's languages before the session's first change come back on Device.detach(). Below API 33: ServerError (reason UNSUPPORTED_API). Android applies it as a configuration change: the app's activities are recreated unless it handles the change itself.

locales

locales() -> list[str]

The app's own languages now (canonical BCP-47 tags); empty when it follows the system. API 33+.

revoke_permission

revoke_permission(permission: str) -> None

pm revoke a runtime permission, proven by dumpsys package. Android kills the app's process when one of its runtime permissions is revoked.

launch

launch(activity: str | None = None, timeout: float | None = None, *, extras: Mapping[str, str | bool | int | float | Long] | None = None) -> None

Starts the activity with am start -W and returns when Android reports the launch complete. Nothing about the UI is assumed: wait for what the test needs (await_visible, await_screen_stable, an element wait).

extras are put on the intent (am start --es/--ez/--ei/--el/--ef): str, bool, int (32-bit, getIntExtra), float (32-bit, getFloatExtra) or tap_e2e.Long(n) for getLongExtra. Anything else raises before the call.

await_visible

await_visible(timeout: float | None = None) -> None

Waits on the device until this package owns the focused window. Raises WaitTimeoutError only when the device reports WAIT_TIMEOUT; any other failure (driver unhealthy, transport lost, ...) is a CommandError.

await_toast

await_toast(text: str | None = None, mode: MatchMode = EXACT, *, timeout: float | None = None) -> Toast

Device.await_toast for this package's toasts only: on Android 11+ a text toast is drawn by SystemUI but still reported under the app that posted it.

await_notification

await_notification(title: str | None = None, text: str | None = None, mode: MatchMode = EXACT, *, timeout: float | None = None) -> Notification

Device.await_notification for this package's notifications only.

await_screen_stable

await_screen_stable(stable_for: float = 0.5, timeout: float | None = None, signal: StabilitySignal = ALL) -> None

Waits on the device until this package's focused window has stopped changing for stable_for seconds according to signal: the accessibility tree (StabilitySignal.TREE), the window pixels (StabilitySignal.PIXELS, 0.5 % tolerance) or both (default). Call it explicitly after an action that starts an animation or transition; nothing waits for this implicitly. A screen that keeps changing times out with detail SCREEN_CHANGING. await_settled / await_animation_end are the shorthands. Only a device WAIT_TIMEOUT becomes WaitTimeoutError; other failures are CommandError.

await_settled

await_settled(stable_for: float = 0.5, timeout: float | None = None) -> None

Maestro's waitForAppToSettle, on request only: this package's accessibility hierarchy has not changed for stable_for seconds. Cheap (no screenshots); misses pure drawing.

await_animation_end

await_animation_end(stable_for: float = 0.5, timeout: float | None = None) -> None

Maestro's waitForAnimationToEnd, on request only: this package's window pixels have not changed (beyond 0.5 %) for stable_for seconds. One screenshot per 100 ms.

foreground

foreground(timeout: float | None = None) -> None

Bring the app to the front the way the launcher does: its launcher intent with NEW_TASK | RESET_TASK_IF_NEEDED, so a running task resumes where it was instead of gaining a second copy of the launcher activity, and a stopped app starts. Returns when am start -W reports the launch complete; wait for the screen you need.

background

background() -> None

Send the app to the background with the Home key, as a user would; the process keeps running. Bring it back with foreground.

open_link(uri: str, any_app: bool = False, timeout: float | None = None) -> str | None

Open uri (a deep link or app link, e.g. myapp://orders/42 or https://example.com/x) with an ACTION_VIEW intent restricted to this package, so no chooser or browser can take it; any_app=True lets Android resolve it like a tap on a link elsewhere would. Returns the activity Android reported starting (package/.Activity) when it reported one. Nothing about the screen is checked: wait for what the link should show.

cold_launch

cold_launch(activity: str | None = None, timeout: float | None = None, *, extras: Mapping[str, str | bool | int | float | Long] | None = None) -> AppProcess

Force-stops, launches (with extras, as launch) and returns the verified new process identity.

process

process(timeout: float | None = None) -> AppProcess

The single current process identity; waits briefly for it to exist.

is_running

is_running() -> bool

Whether any process of the package is alive.

await_idle

await_idle(timeout: float | None = None, stable_for: float = 0.2) -> None

Waits until the app's own sync contract reports idle for stable_for seconds.

Experimental: synchronization is still being designed; this may change in any release.