Define a context manager to launch an app
Issue: INFRA-238
Change-Id: I429896a6feaf1db19ac59a6d6bcffaba26b80d81
diff --git a/deploy.py b/deploy.py
index a24ca86..b49967b 100755
--- a/deploy.py
+++ b/deploy.py
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
+import contextlib
import dataclasses
import logging
import pathlib
@@ -39,6 +40,10 @@
super(DeviceCommandError, self).__init__(message)
+class InvalidIntentError(DeviceCommandError):
+ """An intent was rejected by a device."""
+
+
def adb(*args, serial=None, raise_on_error=True):
"""Run ADB command attached to serial.
@@ -133,17 +138,11 @@
This simplifies UI automation. Disabling the feature globally is more robust
than clicking through the the Privacy Impact screen per app.
"""
- print("Disabling the Privacy Impact screen…")
- device.adb(
- "shell",
- (
- "am start -a android.intent.action.MAIN "
- "com.fairphone.privacyimpact/.PrivacyImpactPreferenceActivity"
- ),
- )
- disable_privacy_impact_checkbox = device.ui(className="android.widget.CheckBox")
- if not disable_privacy_impact_checkbox.checked:
- disable_privacy_impact_checkbox.click()
+ _LOG.info("Disable the Privacy Impact screen")
+ with device.launch("com.fairphone.privacyimpact/.PrivacyImpactPreferenceActivity"):
+ disable_privacy_impact_checkbox = device.ui(className="android.widget.CheckBox")
+ if not disable_privacy_impact_checkbox.checked:
+ disable_privacy_impact_checkbox.click()
# Grant the permissions through the UI
@@ -339,6 +338,66 @@
command.append(str(target))
self.adb(*command)
+ @contextlib.contextmanager
+ def launch(self, package_or_activity: str):
+ """Launch an app and eventually goes back to the home screen.
+
+ There are two ways to start an application:
+
+ 1. Start the main activity (advertised in the launcher), only
+ the package name is needed (e.g. `com.android.settings`);
+ 2. Start a specific activity, the qualified activity name is
+ needed (e.g. `com.android.settings/.Settings`).
+
+ Example:
+
+ >>> device = DeviceUnderTest(...)
+ >>> with device.launch("com.android.settings"):
+ ... device.ui(text="Bluetooth").exists
+ True
+ >>> device.ui(text="Bluetooth").exists
+ False
+
+ :param package_or_activity: The package name or the qualified
+ activity name.
+ :raise DeviceCommandError: If the app manager command failed.
+ :raise InvalidIntentError: If the package or activity could not
+ be resolved.
+ """
+ if "/" in package_or_activity:
+ [package, activity] = package_or_activity.split("/", maxsplit=1)
+ else:
+ package, activity = package_or_activity, ""
+
+ if activity:
+ command = [
+ "shell",
+ "am",
+ "start",
+ "-a",
+ "android.intent.action.MAIN",
+ package_or_activity,
+ ]
+ else:
+ # Use the monkey to avoid specifying the exact activity name.
+ command = [
+ "shell",
+ "monkey",
+ "-p",
+ package,
+ "-c",
+ "android.intent.category.LAUNCHER",
+ "1",
+ ]
+
+ try:
+ process = self.adb(*command)
+ if process.stderr:
+ raise InvalidIntentError(self.serial, " ".join(command), process.stderr)
+ yield
+ finally:
+ self.ui.press.home()
+
@dataclasses.dataclass(frozen=True)
class AndroidApp:
@@ -511,20 +570,9 @@
# Push the scenarios, their data, and install the apps
suite.deploy(device)
- # Start the viser app
- device.adb(
- "shell",
- "monkey",
- "-p",
- "com.lunarlabs.panda",
- "-c",
- "android.intent.category.LAUNCHER",
- "1",
- )
-
- configure_perms(device)
-
- configure_settings(device)
+ with device.launch("com.lunarlabs.panda"):
+ configure_perms(device)
+ configure_settings(device)
except (HostCommandError, DeviceCommandError, uiautomator.JsonRPCError):
_LOG.error("Failed to execute deployment", exc_info=True)
finally: