# 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](../../guide/app-lifecycle.md),
[App or screen](../../guide/app-or-screen.md).

## <kbd>class</kbd> `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. 

### <kbd>method</kbd> `App.__init__`

```python
__init__(device: 'Device', package_name: 'str')
```

---

### <kbd>method</kbd> `App.await_animation_end`

```python
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. 

---

### <kbd>method</kbd> `App.await_idle`

```python
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. 

---

### <kbd>method</kbd> `App.await_notification`

```python
await_notification(
    title: 'str | None' = None,
    text: 'str | None' = None,
    mode: 'MatchMode' = <MatchMode.EXACT: 'EXACT'>,
    timeout: 'float | None' = None
) → Notification
```

``Device.await_notification`` for this package's notifications only. 

---

### <kbd>method</kbd> `App.await_screen_stable`

```python
await_screen_stable(
    stable_for: 'float' = 0.5,
    timeout: 'float | None' = None,
    signal: 'StabilitySignal' = <StabilitySignal.ALL: '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``. 

---

### <kbd>method</kbd> `App.await_settled`

```python
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. 

---

### <kbd>method</kbd> `App.await_toast`

```python
await_toast(
    text: 'str | None' = None,
    mode: 'MatchMode' = <MatchMode.EXACT: '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. 

---

### <kbd>method</kbd> `App.await_visible`

```python
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``. 

---

### <kbd>method</kbd> `App.background`

```python
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``. 

---

### <kbd>method</kbd> `App.clear_data`

```python
clear_data(timeout: 'float | None' = None) → None
```

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

---

### <kbd>method</kbd> `App.cold_launch`

```python
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. 

---

### <kbd>method</kbd> `App.element`

```python
element(selector: 'Selector') → Element
```

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

---

### <kbd>method</kbd> `App.force_stop`

```python
force_stop(timeout: 'float | None' = None) → None
```

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

---

### <kbd>method</kbd> `App.foreground`

```python
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. 

---

### <kbd>method</kbd> `App.grant_permission`

```python
grant_permission(permission: 'str') → None
```

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

---

### <kbd>method</kbd> `App.install`

```python
install(
    apk_path: 'str | os.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. 

---

### <kbd>method</kbd> `App.is_installed`

```python
is_installed() → bool
```

Whether the package is installed. 

---

### <kbd>method</kbd> `App.is_permission_granted`

```python
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. 

---

### <kbd>method</kbd> `App.is_running`

```python
is_running() → bool
```

Whether any process of the package is alive. 

---

### <kbd>method</kbd> `App.launch`

```python
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. 

---

### <kbd>method</kbd> `App.locales`

```python
locales() → list[str]
```

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

---

### <kbd>method</kbd> `App.open_link`

```python
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. 

---

### <kbd>method</kbd> `App.process`

```python
process(timeout: 'float | None' = None) → AppProcess
```

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

---

### <kbd>method</kbd> `App.revoke_permission`

```python
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. 

---

### <kbd>method</kbd> `App.set_locales`

```python
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. 

---

### <kbd>method</kbd> `App.uninstall`

```python
uninstall() → None
```

``pm uninstall``, verified. 

---

### <kbd>method</kbd> `App.wait`

```python
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``. 

