App
One app on a device: its elements, its lifecycle and the waits on it. Obtain with Device.app, which performs no I/O.
element and await 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 host server (ADB plus verified postconditions); the client only names the package and the timeouts, and install uploads the APK from this machine.
val app = device.app("com.example.shop")
app.coldLaunch()
app.element(res("search")).setText("socks")
app.await(text("3 results")).visible()All I/O methods are suspend and require an owning scope (tapScope/tapTest); the constructor (Device.app) only binds values, so it stays non-suspend. Per-call withDeadlineAfter is applied server-side; caller cancellation promptly cancels the RPC. Every call is admitted through the device's command gate, so a started Device.detach or an invalidated connection rejects it locally and an in-flight call delays the session close.
Functions
Maestro's waitForAnimationToEnd, on request only: this package's window pixels have not changed (beyond 0.5 %) for stableFor. Costs one screenshot per 100 ms while waiting.
Waits until the app's TapSynchronization reports no busy work for stableFor. Requires the app's E2E build to ship sync-sdk and be signed like the driver. Experimental: synchronization is still being designed (ExperimentalTapApi).
Device.awaitNotification for this package's notifications only.
Waits on the device until this package's focused window has stopped changing for stableFor according to signal: the accessibility tree (StabilitySignal.TREE), the window pixels (StabilitySignal.PIXELS, 0.5 % tolerance) or both (default). Content-changed events restart the quiet period. Use it explicitly after an action that starts an animation or a transition; no command waits for this implicitly. A screen that keeps changing (indeterminate spinner, ticker, video) times out with SCREEN_CHANGING. awaitSettled and awaitAnimationEnd are the two single-signal shorthands. Only a device WAIT_TIMEOUT becomes WaitTimeoutException; other failures are CommandExceptions.
Maestro's waitForAppToSettle, on request only: this package's accessibility hierarchy has not changed for stableFor. Cheap (no screenshots); sees layout, text and state changes but not pure drawing (a canvas animation, video).
Device.awaitToast 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.
Waits on the device until this package owns the focused window. Throws WaitTimeoutException only when the device reports WAIT_TIMEOUT; any other failure (driver unhealthy, transport lost, ...) is a CommandException.
Sends the app to the background with the Home key, as a user would; the process keeps running. Bring it back with foreground.
Brings 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.
pm grant a runtime permission, e.g. android.permission.CAMERA.
Whether the package is installed.
Whether permission is granted to the package now, as dumpsys package lists it: the read-back for grantPermission, revokePermission, clearData and the permission dialog.
Starts activity (or the launcher 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 (awaitVisible, awaitScreenStable, an element wait).
Opens 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; pass anyApp = true to let 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.
Current single process identity (PID + start token); waits briefly for it to exist.
pm revoke a runtime permission, proven by dumpsys package. Android kills the app's process when one of its runtime permissions is revoked.
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 tags are checked and canonicalized by the server (INVALID_ARGUMENT for a malformed one) and the change is read back. Like the Device conditions, the app's languages before the session's first change come back on Device.detach. Below API 33: ServerException (FailureReason.UNSUPPORTED_API). Android applies it as a configuration change: the app's activities are recreated unless it handles the change itself.