Skip to content

App Model API

pymordialdroid.android_app

Android application model with package lifecycle and ready state detection.

AndroidApp

Bases: PymordialApp

Represents an Android application with lifecycle management.

The controller reference is automatically attached when registered with a PymordialController instance via controller.add_app(...).

Attributes:

Name Type Description
app_name

The display name of the application.

package_name str

The Android package name (e.g. 'com.example.app').

screens str

Dictionary mapping screen names to PymordialScreen instances.

ready_element str

Optional UI element indicating the app has fully loaded.

Source code in .venv/lib/python3.13/site-packages/pymordialdroid/android_app.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
class AndroidApp(PymordialApp):
    """Represents an Android application with lifecycle management.

    The controller reference is automatically attached when registered with
    a PymordialController instance via controller.add_app(...).

    Attributes:
        app_name: The display name of the application.
        package_name: The Android package name (e.g. 'com.example.app').
        screens: Dictionary mapping screen names to PymordialScreen instances.
        ready_element: Optional UI element indicating the app has fully loaded.
    """

    def __init__(
        self,
        app_name: str,
        package_name: str,
        screens: dict[str, PymordialScreen] | None = None,
        ready_element: PymordialElement | None = None,
    ) -> None:
        if not app_name:
            raise ValueError("app_name must be a non-empty string")
        if not package_name:
            raise ValueError("package_name must be a non-empty string")

        super().__init__(
            app_name=app_name,
            screens=screens if screens is not None else {},
            ready_element=ready_element,
        )
        self.package_name: str = package_name
        self.pymordial_controller: PymordialController | None = None

    def check_ready(self, max_tries: int | None = None) -> bool:
        """Checks if the ready_element is visible and transitions to READY state."""
        if not self.ready_element or not self.pymordial_controller:
            return False

        if self.app_state.current_state != AppState.LOADING:
            return False

        try:
            if self.pymordial_controller.is_element_visible(
                self.ready_element, max_tries=max_tries
            ):
                self.app_state.transition_to(AppState.READY)
                return True
        except Exception:
            pass

        return False

    def is_open(self) -> bool:
        """Checks if the app is in the READY state."""
        return self.app_state.current_state == AppState.READY

    def is_loading(self) -> bool:
        """Checks if the app is in the LOADING state."""
        return self.app_state.current_state == AppState.LOADING

    def is_closed(self) -> bool:
        """Checks if the app is in the CLOSED state."""
        return self.app_state.current_state == AppState.CLOSED

    def open(self, timeout: int = 60, wait_time: int = 10) -> bool:
        """Launches the application via the controller."""
        if not self.pymordial_controller:
            raise RuntimeError("Cannot open app: no PymordialController attached.")

        return self.pymordial_controller.open_app(
            app_name=self.app_name,
            package_name=self.package_name,
            timeout=timeout,
            wait_time=wait_time,
        )

    def close(self, timeout: int = 30, wait_time: int = 2) -> bool:
        """Closes the application via the controller."""
        if not self.pymordial_controller:
            raise RuntimeError("Cannot close app: no PymordialController attached.")

        return self.pymordial_controller.close_app(
            package_name=self.package_name,
            timeout=timeout,
            wait_time=wait_time,
        )

    def is_running(self) -> bool:
        """Checks if the application is currently running on the device."""
        if not self.pymordial_controller:
            return False

        bridge = getattr(self.pymordial_controller, "bridge", None)
        if bridge and hasattr(bridge, "is_app_running"):
            return bridge.is_app_running(self.package_name)
        return False

check_ready(max_tries=None)

Checks if the ready_element is visible and transitions to READY state.

Source code in .venv/lib/python3.13/site-packages/pymordialdroid/android_app.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def check_ready(self, max_tries: int | None = None) -> bool:
    """Checks if the ready_element is visible and transitions to READY state."""
    if not self.ready_element or not self.pymordial_controller:
        return False

    if self.app_state.current_state != AppState.LOADING:
        return False

    try:
        if self.pymordial_controller.is_element_visible(
            self.ready_element, max_tries=max_tries
        ):
            self.app_state.transition_to(AppState.READY)
            return True
    except Exception:
        pass

    return False

is_open()

Checks if the app is in the READY state.

Source code in .venv/lib/python3.13/site-packages/pymordialdroid/android_app.py
66
67
68
def is_open(self) -> bool:
    """Checks if the app is in the READY state."""
    return self.app_state.current_state == AppState.READY

is_loading()

Checks if the app is in the LOADING state.

Source code in .venv/lib/python3.13/site-packages/pymordialdroid/android_app.py
70
71
72
def is_loading(self) -> bool:
    """Checks if the app is in the LOADING state."""
    return self.app_state.current_state == AppState.LOADING

is_closed()

Checks if the app is in the CLOSED state.

Source code in .venv/lib/python3.13/site-packages/pymordialdroid/android_app.py
74
75
76
def is_closed(self) -> bool:
    """Checks if the app is in the CLOSED state."""
    return self.app_state.current_state == AppState.CLOSED

open(timeout=60, wait_time=10)

Launches the application via the controller.

Source code in .venv/lib/python3.13/site-packages/pymordialdroid/android_app.py
78
79
80
81
82
83
84
85
86
87
88
def open(self, timeout: int = 60, wait_time: int = 10) -> bool:
    """Launches the application via the controller."""
    if not self.pymordial_controller:
        raise RuntimeError("Cannot open app: no PymordialController attached.")

    return self.pymordial_controller.open_app(
        app_name=self.app_name,
        package_name=self.package_name,
        timeout=timeout,
        wait_time=wait_time,
    )

close(timeout=30, wait_time=2)

Closes the application via the controller.

Source code in .venv/lib/python3.13/site-packages/pymordialdroid/android_app.py
90
91
92
93
94
95
96
97
98
99
def close(self, timeout: int = 30, wait_time: int = 2) -> bool:
    """Closes the application via the controller."""
    if not self.pymordial_controller:
        raise RuntimeError("Cannot close app: no PymordialController attached.")

    return self.pymordial_controller.close_app(
        package_name=self.package_name,
        timeout=timeout,
        wait_time=wait_time,
    )

is_running()

Checks if the application is currently running on the device.

Source code in .venv/lib/python3.13/site-packages/pymordialdroid/android_app.py
101
102
103
104
105
106
107
108
109
def is_running(self) -> bool:
    """Checks if the application is currently running on the device."""
    if not self.pymordial_controller:
        return False

    bridge = getattr(self.pymordial_controller, "bridge", None)
    if bridge and hasattr(bridge, "is_app_running"):
        return bridge.is_app_running(self.package_name)
    return False