androidctl 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- androidctl/__init__.py +5 -0
- androidctl/__main__.py +4 -0
- androidctl/_version.py +1 -0
- androidctl/app.py +73 -0
- androidctl/cli_options.py +27 -0
- androidctl/command_payloads.py +264 -0
- androidctl/command_views.py +157 -0
- androidctl/commands/__init__.py +1 -0
- androidctl/commands/actions.py +236 -0
- androidctl/commands/adb_wireless.py +157 -0
- androidctl/commands/close.py +30 -0
- androidctl/commands/connect.py +69 -0
- androidctl/commands/execute.py +179 -0
- androidctl/commands/list_apps.py +26 -0
- androidctl/commands/observe.py +26 -0
- androidctl/commands/open.py +41 -0
- androidctl/commands/plumbing.py +58 -0
- androidctl/commands/run_pipeline.py +307 -0
- androidctl/commands/screenshot.py +29 -0
- androidctl/commands/setup.py +301 -0
- androidctl/commands/wait.py +60 -0
- androidctl/daemon/__init__.py +1 -0
- androidctl/daemon/client.py +348 -0
- androidctl/daemon/discovery.py +190 -0
- androidctl/daemon/launcher.py +26 -0
- androidctl/daemon/owner.py +349 -0
- androidctl/errors/__init__.py +1 -0
- androidctl/errors/mapping.py +149 -0
- androidctl/errors/models.py +16 -0
- androidctl/exit_codes.py +8 -0
- androidctl/output.py +147 -0
- androidctl/parsing/__init__.py +1 -0
- androidctl/parsing/duration.py +17 -0
- androidctl/parsing/open_target.py +51 -0
- androidctl/parsing/refs.py +12 -0
- androidctl/parsing/screen_id.py +10 -0
- androidctl/parsing/wait.py +70 -0
- androidctl/renderers/__init__.py +110 -0
- androidctl/renderers/_paths.py +109 -0
- androidctl/renderers/xml.py +234 -0
- androidctl/renderers/xml_projection.py +732 -0
- androidctl/resources/__init__.py +1 -0
- androidctl/resources/androidctl-agent-0.1.0-release.apk +0 -0
- androidctl/setup/__init__.py +1 -0
- androidctl/setup/accessibility.py +159 -0
- androidctl/setup/adb.py +586 -0
- androidctl/setup/apk_resource.py +29 -0
- androidctl/setup/pairing.py +70 -0
- androidctl/setup/verify.py +175 -0
- androidctl/workspace/__init__.py +3 -0
- androidctl/workspace/resolve.py +27 -0
- androidctl-0.1.0.dist-info/METADATA +217 -0
- androidctl-0.1.0.dist-info/RECORD +187 -0
- androidctl-0.1.0.dist-info/WHEEL +5 -0
- androidctl-0.1.0.dist-info/entry_points.txt +3 -0
- androidctl-0.1.0.dist-info/licenses/LICENSE +674 -0
- androidctl-0.1.0.dist-info/top_level.txt +3 -0
- androidctl_contracts/__init__.py +55 -0
- androidctl_contracts/_version.py +1 -0
- androidctl_contracts/_wire_helpers.py +31 -0
- androidctl_contracts/base.py +142 -0
- androidctl_contracts/command_catalog.py +414 -0
- androidctl_contracts/command_results.py +630 -0
- androidctl_contracts/daemon_api.py +335 -0
- androidctl_contracts/errors.py +44 -0
- androidctl_contracts/paths.py +5 -0
- androidctl_contracts/public_screen.py +579 -0
- androidctl_contracts/user_state.py +23 -0
- androidctl_contracts/vocabulary.py +82 -0
- androidctld/__init__.py +5 -0
- androidctld/__main__.py +63 -0
- androidctld/_version.py +1 -0
- androidctld/actions/__init__.py +1 -0
- androidctld/actions/action_target.py +142 -0
- androidctld/actions/capabilities.py +539 -0
- androidctld/actions/executor.py +894 -0
- androidctld/actions/focus_confirmation.py +177 -0
- androidctld/actions/focused_input_admissibility.py +120 -0
- androidctld/actions/fresh_current.py +176 -0
- androidctld/actions/postconditions.py +473 -0
- androidctld/actions/repair.py +101 -0
- androidctld/actions/request_builder.py +204 -0
- androidctld/actions/settle.py +146 -0
- androidctld/actions/submit_confirmation.py +211 -0
- androidctld/actions/submit_routing.py +311 -0
- androidctld/actions/type_confirmation.py +257 -0
- androidctld/app_targets.py +71 -0
- androidctld/artifacts/__init__.py +1 -0
- androidctld/artifacts/models.py +26 -0
- androidctld/artifacts/screen_lookup.py +241 -0
- androidctld/artifacts/screen_payloads.py +109 -0
- androidctld/artifacts/writer.py +286 -0
- androidctld/auth/__init__.py +1 -0
- androidctld/auth/active_registry.py +266 -0
- androidctld/auth/secret_files.py +52 -0
- androidctld/auth/token_store.py +59 -0
- androidctld/commands/__init__.py +1 -0
- androidctld/commands/assembly.py +231 -0
- androidctld/commands/command_models.py +254 -0
- androidctld/commands/dispatch.py +99 -0
- androidctld/commands/executor.py +31 -0
- androidctld/commands/from_boundary.py +175 -0
- androidctld/commands/handlers/__init__.py +15 -0
- androidctld/commands/handlers/action.py +439 -0
- androidctld/commands/handlers/connect.py +94 -0
- androidctld/commands/handlers/list_apps.py +215 -0
- androidctld/commands/handlers/observe.py +121 -0
- androidctld/commands/handlers/screenshot.py +105 -0
- androidctld/commands/handlers/wait.py +286 -0
- androidctld/commands/models.py +65 -0
- androidctld/commands/open_targets.py +56 -0
- androidctld/commands/orchestration.py +353 -0
- androidctld/commands/registry.py +116 -0
- androidctld/commands/result_builders.py +40 -0
- androidctld/commands/result_models.py +555 -0
- androidctld/commands/results.py +108 -0
- androidctld/commands/semantic_command_names.py +17 -0
- androidctld/commands/semantic_error_mapping.py +93 -0
- androidctld/commands/semantic_truth.py +135 -0
- androidctld/commands/service.py +67 -0
- androidctld/config.py +75 -0
- androidctld/daemon/__init__.py +1 -0
- androidctld/daemon/active_slot.py +326 -0
- androidctld/daemon/envelope.py +30 -0
- androidctld/daemon/http_host.py +123 -0
- androidctld/daemon/ingress.py +112 -0
- androidctld/daemon/ownership_probe.py +204 -0
- androidctld/daemon/server.py +286 -0
- androidctld/daemon/service.py +99 -0
- androidctld/device/__init__.py +1 -0
- androidctld/device/action_models.py +154 -0
- androidctld/device/action_serialization.py +121 -0
- androidctld/device/adapters.py +220 -0
- androidctld/device/bootstrap.py +153 -0
- androidctld/device/connectors.py +231 -0
- androidctld/device/errors.py +100 -0
- androidctld/device/interfaces.py +58 -0
- androidctld/device/parsing.py +320 -0
- androidctld/device/rpc.py +483 -0
- androidctld/device/schema.py +114 -0
- androidctld/device/types.py +161 -0
- androidctld/errors/__init__.py +94 -0
- androidctld/logging/__init__.py +22 -0
- androidctld/observation.py +98 -0
- androidctld/protocol.py +53 -0
- androidctld/refs/__init__.py +1 -0
- androidctld/refs/models.py +54 -0
- androidctld/refs/repair.py +284 -0
- androidctld/refs/service.py +422 -0
- androidctld/rendering/__init__.py +1 -0
- androidctld/rendering/screen_xml.py +256 -0
- androidctld/runtime/__init__.py +21 -0
- androidctld/runtime/kernel.py +548 -0
- androidctld/runtime/lifecycle.py +19 -0
- androidctld/runtime/models.py +48 -0
- androidctld/runtime/screen_state.py +117 -0
- androidctld/runtime/state_repo.py +70 -0
- androidctld/runtime/store.py +76 -0
- androidctld/runtime_policy.py +127 -0
- androidctld/schema/__init__.py +5 -0
- androidctld/schema/base.py +132 -0
- androidctld/schema/core.py +35 -0
- androidctld/schema/daemon_api.py +108 -0
- androidctld/schema/persistence.py +161 -0
- androidctld/schema/persistence_io.py +41 -0
- androidctld/schema/validation_errors.py +309 -0
- androidctld/semantics/__init__.py +1 -0
- androidctld/semantics/compiler.py +610 -0
- androidctld/semantics/continuity.py +107 -0
- androidctld/semantics/labels.py +252 -0
- androidctld/semantics/models.py +25 -0
- androidctld/semantics/policy.py +23 -0
- androidctld/semantics/public_models.py +123 -0
- androidctld/semantics/registries.py +13 -0
- androidctld/semantics/submit_refs.py +417 -0
- androidctld/semantics/surface.py +254 -0
- androidctld/semantics/targets.py +167 -0
- androidctld/snapshots/__init__.py +1 -0
- androidctld/snapshots/models.py +219 -0
- androidctld/snapshots/refresh.py +273 -0
- androidctld/snapshots/schema.py +74 -0
- androidctld/snapshots/service.py +138 -0
- androidctld/text_equivalence.py +67 -0
- androidctld/waits/__init__.py +1 -0
- androidctld/waits/evaluators.py +216 -0
- androidctld/waits/loop.py +305 -0
- androidctld/waits/matcher.py +41 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Standalone public screen XML rendering owned by the daemon."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from xml.etree.ElementTree import Element, SubElement, tostring
|
|
7
|
+
|
|
8
|
+
from androidctl_contracts.public_screen import (
|
|
9
|
+
BLOCKING_GROUP_NAMES,
|
|
10
|
+
PUBLIC_GROUP_NAMES,
|
|
11
|
+
PUBLIC_NODE_ROLE_VALUES,
|
|
12
|
+
BlockingGroupName,
|
|
13
|
+
PublicGroupName,
|
|
14
|
+
)
|
|
15
|
+
from androidctld.semantics.public_models import (
|
|
16
|
+
OmittedEntry,
|
|
17
|
+
PublicApp,
|
|
18
|
+
PublicFocus,
|
|
19
|
+
PublicGroup,
|
|
20
|
+
PublicNode,
|
|
21
|
+
PublicScreen,
|
|
22
|
+
PublicSurface,
|
|
23
|
+
TransientItem,
|
|
24
|
+
VisibleWindow,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
XmlAttrs = dict[str, str]
|
|
28
|
+
|
|
29
|
+
_APP_ATTRS = (
|
|
30
|
+
"packageName",
|
|
31
|
+
"activityName",
|
|
32
|
+
"requestedPackageName",
|
|
33
|
+
"resolvedPackageName",
|
|
34
|
+
"matchType",
|
|
35
|
+
)
|
|
36
|
+
_FOCUS_ATTRS = ("inputRef",)
|
|
37
|
+
_VISIBLE_WINDOW_ATTRS = ("windowRef", "role", "focused", "blocking")
|
|
38
|
+
_BLOCKING_GROUPS: set[BlockingGroupName] = set(BLOCKING_GROUP_NAMES)
|
|
39
|
+
_NODE_ROLE_TAGS = frozenset(PUBLIC_NODE_ROLE_VALUES)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def render_screen_xml(screen: PublicScreen) -> str:
|
|
43
|
+
"""Render a standalone public ``<screen>`` artifact."""
|
|
44
|
+
|
|
45
|
+
root = Element("screen", {"screenId": screen.screen_id})
|
|
46
|
+
_append_app(root, screen.app)
|
|
47
|
+
_append_surface(root, screen.surface)
|
|
48
|
+
_append_groups(root, screen.groups, blocking_group=screen.surface.blocking_group)
|
|
49
|
+
_append_omitted(root, screen.omitted)
|
|
50
|
+
_append_visible_windows(root, screen.visible_windows)
|
|
51
|
+
_append_transient(root, screen.transient)
|
|
52
|
+
return tostring(root, encoding="unicode", short_empty_elements=True)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _append_app(parent: Element, app: PublicApp) -> None:
|
|
56
|
+
attrs = _non_empty_attrs(
|
|
57
|
+
{
|
|
58
|
+
"packageName": app.package_name,
|
|
59
|
+
"activityName": app.activity_name,
|
|
60
|
+
"requestedPackageName": app.requested_package_name,
|
|
61
|
+
"resolvedPackageName": app.resolved_package_name,
|
|
62
|
+
"matchType": app.match_type,
|
|
63
|
+
},
|
|
64
|
+
_APP_ATTRS,
|
|
65
|
+
)
|
|
66
|
+
SubElement(parent, "app", attrs)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _append_surface(parent: Element, surface: PublicSurface) -> None:
|
|
70
|
+
attrs = {"keyboardVisible": _bool_string(surface.keyboard_visible)}
|
|
71
|
+
if surface.blocking_group in _BLOCKING_GROUPS:
|
|
72
|
+
attrs["blockingGroup"] = surface.blocking_group
|
|
73
|
+
surface_elem = SubElement(parent, "surface", attrs)
|
|
74
|
+
_append_focus(surface_elem, surface.focus)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _append_focus(parent: Element, focus: PublicFocus) -> None:
|
|
78
|
+
SubElement(
|
|
79
|
+
parent,
|
|
80
|
+
"focus",
|
|
81
|
+
_non_empty_attrs({"inputRef": focus.input_ref}, _FOCUS_ATTRS),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _append_groups(
|
|
86
|
+
parent: Element,
|
|
87
|
+
groups: tuple[PublicGroup, ...],
|
|
88
|
+
*,
|
|
89
|
+
blocking_group: BlockingGroupName | None,
|
|
90
|
+
) -> None:
|
|
91
|
+
groups_elem = SubElement(parent, "groups")
|
|
92
|
+
groups_by_name = {group.name: group for group in groups}
|
|
93
|
+
for group_name in _group_order(blocking_group):
|
|
94
|
+
group_elem = SubElement(groups_elem, group_name)
|
|
95
|
+
group = groups_by_name.get(group_name)
|
|
96
|
+
if group is None:
|
|
97
|
+
continue
|
|
98
|
+
for node in group.nodes:
|
|
99
|
+
_append_group_item(group_elem, node)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _append_group_item(parent: Element, node: PublicNode) -> None:
|
|
103
|
+
if node.kind == "text":
|
|
104
|
+
text_elem = SubElement(parent, "literal", _text_attrs(node))
|
|
105
|
+
text_elem.text = node.text
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
tag = _node_role_tag(node.role)
|
|
109
|
+
node_elem = SubElement(parent, tag, _node_attrs(node))
|
|
110
|
+
for child in node.children:
|
|
111
|
+
_append_group_item(node_elem, child)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _append_omitted(parent: Element, omitted: tuple[OmittedEntry, ...]) -> None:
|
|
115
|
+
omitted_elem = SubElement(parent, "omitted")
|
|
116
|
+
for item in omitted:
|
|
117
|
+
attrs: XmlAttrs = {
|
|
118
|
+
"group": item.group,
|
|
119
|
+
"reason": item.reason,
|
|
120
|
+
}
|
|
121
|
+
if item.count is not None:
|
|
122
|
+
attrs["count"] = str(item.count)
|
|
123
|
+
SubElement(omitted_elem, "entry", attrs)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _append_visible_windows(
|
|
127
|
+
parent: Element,
|
|
128
|
+
visible_windows: tuple[VisibleWindow, ...],
|
|
129
|
+
) -> None:
|
|
130
|
+
windows_elem = SubElement(parent, "visibleWindows")
|
|
131
|
+
for window in visible_windows:
|
|
132
|
+
SubElement(
|
|
133
|
+
windows_elem,
|
|
134
|
+
"window",
|
|
135
|
+
_attrs(
|
|
136
|
+
{
|
|
137
|
+
"windowRef": window.window_ref,
|
|
138
|
+
"role": window.role,
|
|
139
|
+
"focused": window.focused,
|
|
140
|
+
"blocking": window.blocking,
|
|
141
|
+
},
|
|
142
|
+
_VISIBLE_WINDOW_ATTRS,
|
|
143
|
+
),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _append_transient(parent: Element, transient: tuple[TransientItem, ...]) -> None:
|
|
148
|
+
transient_elem = SubElement(parent, "transient")
|
|
149
|
+
for item in transient:
|
|
150
|
+
attrs: XmlAttrs = {}
|
|
151
|
+
if item.kind is not None:
|
|
152
|
+
attrs["kind"] = item.kind
|
|
153
|
+
SubElement(transient_elem, "item", attrs).text = item.text
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _node_attrs(node: PublicNode) -> XmlAttrs:
|
|
157
|
+
attrs: XmlAttrs = {}
|
|
158
|
+
if node.ref is not None:
|
|
159
|
+
attrs["ref"] = node.ref
|
|
160
|
+
if node.ref is not None:
|
|
161
|
+
attrs.update(
|
|
162
|
+
_non_empty_sequence_attrs(
|
|
163
|
+
{
|
|
164
|
+
"actions": node.actions,
|
|
165
|
+
"state": node.state,
|
|
166
|
+
},
|
|
167
|
+
("actions", "state"),
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
if node.scroll_directions:
|
|
171
|
+
attrs["scrollDirections"] = _sequence_attr(node.scroll_directions)
|
|
172
|
+
if node.submit_refs:
|
|
173
|
+
attrs["submitRefs"] = _sequence_attr(node.submit_refs)
|
|
174
|
+
attrs.update(_text_attrs(node))
|
|
175
|
+
return attrs
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _text_attrs(node: PublicNode) -> XmlAttrs:
|
|
179
|
+
attrs: XmlAttrs = {}
|
|
180
|
+
if node.origin is not None:
|
|
181
|
+
attrs["origin"] = node.origin
|
|
182
|
+
if node.window_ref is not None:
|
|
183
|
+
attrs["windowRef"] = node.window_ref
|
|
184
|
+
if node.ambiguity is not None:
|
|
185
|
+
attrs["ambiguity"] = node.ambiguity
|
|
186
|
+
if node.kind != "text" and node.label:
|
|
187
|
+
attrs["label"] = node.label
|
|
188
|
+
if node.within is not None:
|
|
189
|
+
attrs["within"] = node.within
|
|
190
|
+
if node.value is not None:
|
|
191
|
+
attrs["value"] = node.value
|
|
192
|
+
return attrs
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _group_order(
|
|
196
|
+
blocking_group: BlockingGroupName | None,
|
|
197
|
+
) -> tuple[PublicGroupName, ...]:
|
|
198
|
+
if blocking_group not in _BLOCKING_GROUPS:
|
|
199
|
+
return PUBLIC_GROUP_NAMES
|
|
200
|
+
return (
|
|
201
|
+
blocking_group,
|
|
202
|
+
*(
|
|
203
|
+
group_name
|
|
204
|
+
for group_name in PUBLIC_GROUP_NAMES
|
|
205
|
+
if group_name != blocking_group
|
|
206
|
+
),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _attrs(values: dict[str, object], keys: Sequence[str]) -> XmlAttrs:
|
|
211
|
+
attrs: XmlAttrs = {}
|
|
212
|
+
for key in keys:
|
|
213
|
+
value = values.get(key)
|
|
214
|
+
if value is None:
|
|
215
|
+
continue
|
|
216
|
+
if isinstance(value, bool):
|
|
217
|
+
attrs[key] = _bool_string(value)
|
|
218
|
+
elif isinstance(value, Sequence) and not isinstance(value, str):
|
|
219
|
+
attrs[key] = _sequence_attr(value)
|
|
220
|
+
else:
|
|
221
|
+
attrs[key] = str(value)
|
|
222
|
+
return attrs
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _non_empty_attrs(values: dict[str, object], keys: Sequence[str]) -> XmlAttrs:
|
|
226
|
+
return {
|
|
227
|
+
key: str(value)
|
|
228
|
+
for key in keys
|
|
229
|
+
if isinstance((value := values.get(key)), str) and value
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _non_empty_sequence_attrs(
|
|
234
|
+
values: dict[str, object],
|
|
235
|
+
keys: Sequence[str],
|
|
236
|
+
) -> XmlAttrs:
|
|
237
|
+
attrs: XmlAttrs = {}
|
|
238
|
+
for key in keys:
|
|
239
|
+
value = values.get(key)
|
|
240
|
+
if isinstance(value, Sequence) and not isinstance(value, str) and value:
|
|
241
|
+
attrs[key] = _sequence_attr(value)
|
|
242
|
+
return attrs
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _sequence_attr(values: Sequence[object]) -> str:
|
|
246
|
+
return " ".join(str(item) for item in values)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _bool_string(value: bool) -> str:
|
|
250
|
+
return "true" if value else "false"
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _node_role_tag(role: str) -> str:
|
|
254
|
+
if role not in _NODE_ROLE_TAGS:
|
|
255
|
+
raise ValueError(f"unsupported public node role for XML tag: {role}")
|
|
256
|
+
return role
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Workspace-scoped runtime package."""
|
|
2
|
+
|
|
3
|
+
from androidctld.runtime.kernel import RuntimeKernel
|
|
4
|
+
from androidctld.runtime.lifecycle import RuntimeLifecycleLease, capture_lifecycle_lease
|
|
5
|
+
from androidctld.runtime.models import (
|
|
6
|
+
ScreenState,
|
|
7
|
+
WorkspaceRuntime,
|
|
8
|
+
)
|
|
9
|
+
from androidctld.runtime.state_repo import RuntimeStateRepository
|
|
10
|
+
from androidctld.runtime.store import RuntimeSerialCommandBusyError, RuntimeStore
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"RuntimeKernel",
|
|
14
|
+
"RuntimeLifecycleLease",
|
|
15
|
+
"RuntimeSerialCommandBusyError",
|
|
16
|
+
"RuntimeStateRepository",
|
|
17
|
+
"RuntimeStore",
|
|
18
|
+
"ScreenState",
|
|
19
|
+
"WorkspaceRuntime",
|
|
20
|
+
"capture_lifecycle_lease",
|
|
21
|
+
]
|