autoglm-gui 1.4.1__py3-none-any.whl → 1.5.1__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.
- AutoGLM_GUI/__init__.py +11 -0
- AutoGLM_GUI/__main__.py +26 -4
- AutoGLM_GUI/actions/__init__.py +6 -0
- phone_agent/actions/handler_ios.py → AutoGLM_GUI/actions/handler.py +30 -112
- AutoGLM_GUI/actions/types.py +15 -0
- {phone_agent → AutoGLM_GUI}/adb/__init__.py +25 -23
- {phone_agent → AutoGLM_GUI}/adb/connection.py +5 -40
- {phone_agent → AutoGLM_GUI}/adb/device.py +12 -94
- {phone_agent → AutoGLM_GUI}/adb/input.py +6 -47
- AutoGLM_GUI/adb/screenshot.py +11 -0
- {phone_agent/config → AutoGLM_GUI/adb}/timing.py +1 -1
- AutoGLM_GUI/adb_plus/keyboard_installer.py +4 -2
- AutoGLM_GUI/adb_plus/screenshot.py +22 -1
- AutoGLM_GUI/adb_plus/serial.py +38 -20
- AutoGLM_GUI/adb_plus/touch.py +4 -9
- AutoGLM_GUI/agents/__init__.py +43 -12
- AutoGLM_GUI/agents/events.py +19 -0
- AutoGLM_GUI/agents/factory.py +31 -38
- AutoGLM_GUI/agents/glm/__init__.py +7 -0
- AutoGLM_GUI/agents/glm/agent.py +297 -0
- AutoGLM_GUI/agents/glm/message_builder.py +81 -0
- AutoGLM_GUI/agents/glm/parser.py +110 -0
- {phone_agent/config → AutoGLM_GUI/agents/glm}/prompts_en.py +7 -9
- {phone_agent/config → AutoGLM_GUI/agents/glm}/prompts_zh.py +18 -25
- AutoGLM_GUI/agents/mai/__init__.py +28 -0
- AutoGLM_GUI/agents/mai/agent.py +408 -0
- AutoGLM_GUI/agents/mai/parser.py +254 -0
- AutoGLM_GUI/agents/mai/prompts.py +103 -0
- AutoGLM_GUI/agents/mai/traj_memory.py +91 -0
- AutoGLM_GUI/agents/protocols.py +12 -8
- AutoGLM_GUI/agents/stream_runner.py +193 -0
- AutoGLM_GUI/api/__init__.py +40 -21
- AutoGLM_GUI/api/agents.py +181 -239
- AutoGLM_GUI/api/control.py +9 -6
- AutoGLM_GUI/api/devices.py +102 -12
- AutoGLM_GUI/api/history.py +104 -0
- AutoGLM_GUI/api/layered_agent.py +67 -15
- AutoGLM_GUI/api/media.py +64 -1
- AutoGLM_GUI/api/scheduled_tasks.py +98 -0
- AutoGLM_GUI/config.py +81 -0
- AutoGLM_GUI/config_manager.py +68 -51
- AutoGLM_GUI/device_manager.py +248 -29
- AutoGLM_GUI/device_protocol.py +1 -1
- AutoGLM_GUI/devices/adb_device.py +5 -10
- AutoGLM_GUI/devices/mock_device.py +4 -2
- AutoGLM_GUI/devices/remote_device.py +8 -3
- AutoGLM_GUI/history_manager.py +164 -0
- AutoGLM_GUI/model/__init__.py +5 -0
- AutoGLM_GUI/model/message_builder.py +69 -0
- AutoGLM_GUI/model/types.py +24 -0
- AutoGLM_GUI/models/__init__.py +10 -0
- AutoGLM_GUI/models/history.py +140 -0
- AutoGLM_GUI/models/scheduled_task.py +71 -0
- AutoGLM_GUI/parsers/__init__.py +22 -0
- AutoGLM_GUI/parsers/base.py +50 -0
- AutoGLM_GUI/parsers/phone_parser.py +58 -0
- AutoGLM_GUI/phone_agent_manager.py +62 -396
- AutoGLM_GUI/platform_utils.py +26 -0
- AutoGLM_GUI/prompt_config.py +15 -0
- AutoGLM_GUI/prompts/__init__.py +32 -0
- AutoGLM_GUI/scheduler_manager.py +350 -0
- AutoGLM_GUI/schemas.py +246 -72
- AutoGLM_GUI/scrcpy_stream.py +142 -24
- AutoGLM_GUI/socketio_server.py +100 -27
- AutoGLM_GUI/static/assets/{about-_XNhzQZX.js → about-CfwX1Cmc.js} +1 -1
- AutoGLM_GUI/static/assets/alert-dialog-CtGlN2IJ.js +1 -0
- AutoGLM_GUI/static/assets/chat-BYa-foUI.js +129 -0
- AutoGLM_GUI/static/assets/circle-alert-t08bEMPO.js +1 -0
- AutoGLM_GUI/static/assets/dialog-FNwZJFwk.js +45 -0
- AutoGLM_GUI/static/assets/eye-D0UPWCWC.js +1 -0
- AutoGLM_GUI/static/assets/history-CRo95B7i.js +1 -0
- AutoGLM_GUI/static/assets/{index-Cy8TmmHV.js → index-BaLMSqd3.js} +1 -1
- AutoGLM_GUI/static/assets/index-CTHbFvKl.js +11 -0
- AutoGLM_GUI/static/assets/index-CV7jGxGm.css +1 -0
- AutoGLM_GUI/static/assets/label-DJFevVmr.js +1 -0
- AutoGLM_GUI/static/assets/logs-RW09DyYY.js +1 -0
- AutoGLM_GUI/static/assets/popover--JTJrE5v.js +1 -0
- AutoGLM_GUI/static/assets/scheduled-tasks-DTRKsQXF.js +1 -0
- AutoGLM_GUI/static/assets/square-pen-CPK_K680.js +1 -0
- AutoGLM_GUI/static/assets/textarea-PRmVnWq5.js +1 -0
- AutoGLM_GUI/static/assets/workflows-CdcsAoaT.js +1 -0
- AutoGLM_GUI/static/index.html +2 -2
- AutoGLM_GUI/types.py +17 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/METADATA +179 -130
- autoglm_gui-1.5.1.dist-info/RECORD +118 -0
- AutoGLM_GUI/agents/mai_adapter.py +0 -627
- AutoGLM_GUI/api/dual_model.py +0 -317
- AutoGLM_GUI/device_adapter.py +0 -263
- AutoGLM_GUI/dual_model/__init__.py +0 -53
- AutoGLM_GUI/dual_model/decision_model.py +0 -664
- AutoGLM_GUI/dual_model/dual_agent.py +0 -917
- AutoGLM_GUI/dual_model/protocols.py +0 -354
- AutoGLM_GUI/dual_model/vision_model.py +0 -442
- AutoGLM_GUI/mai_ui_adapter/agent_wrapper.py +0 -291
- AutoGLM_GUI/phone_agent_patches.py +0 -147
- AutoGLM_GUI/static/assets/chat-DwJpiAWf.js +0 -126
- AutoGLM_GUI/static/assets/dialog-B3uW4T8V.js +0 -45
- AutoGLM_GUI/static/assets/index-Cpv2gSF1.css +0 -1
- AutoGLM_GUI/static/assets/index-UYYauTly.js +0 -12
- AutoGLM_GUI/static/assets/workflows-Du_de-dt.js +0 -1
- autoglm_gui-1.4.1.dist-info/RECORD +0 -117
- mai_agent/base.py +0 -137
- mai_agent/mai_grounding_agent.py +0 -263
- mai_agent/mai_naivigation_agent.py +0 -526
- mai_agent/prompt.py +0 -148
- mai_agent/unified_memory.py +0 -67
- mai_agent/utils.py +0 -73
- phone_agent/__init__.py +0 -12
- phone_agent/actions/__init__.py +0 -5
- phone_agent/actions/handler.py +0 -400
- phone_agent/adb/screenshot.py +0 -108
- phone_agent/agent.py +0 -253
- phone_agent/agent_ios.py +0 -277
- phone_agent/config/__init__.py +0 -53
- phone_agent/config/apps_harmonyos.py +0 -256
- phone_agent/config/apps_ios.py +0 -339
- phone_agent/config/prompts.py +0 -80
- phone_agent/device_factory.py +0 -166
- phone_agent/hdc/__init__.py +0 -53
- phone_agent/hdc/connection.py +0 -384
- phone_agent/hdc/device.py +0 -269
- phone_agent/hdc/input.py +0 -145
- phone_agent/hdc/screenshot.py +0 -127
- phone_agent/model/__init__.py +0 -5
- phone_agent/model/client.py +0 -290
- phone_agent/xctest/__init__.py +0 -47
- phone_agent/xctest/connection.py +0 -379
- phone_agent/xctest/device.py +0 -472
- phone_agent/xctest/input.py +0 -311
- phone_agent/xctest/screenshot.py +0 -226
- {phone_agent/config → AutoGLM_GUI/adb}/apps.py +0 -0
- {phone_agent/config → AutoGLM_GUI}/i18n.py +0 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/WHEEL +0 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/entry_points.txt +0 -0
- {autoglm_gui-1.4.1.dist-info → autoglm_gui-1.5.1.dist-info}/licenses/LICENSE +0 -0
phone_agent/hdc/device.py
DELETED
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
"""Device control utilities for HarmonyOS automation."""
|
|
2
|
-
|
|
3
|
-
import time
|
|
4
|
-
|
|
5
|
-
from phone_agent.config.apps_harmonyos import APP_ABILITIES, APP_PACKAGES
|
|
6
|
-
from phone_agent.config.timing import TIMING_CONFIG
|
|
7
|
-
from phone_agent.hdc.connection import _run_hdc_command
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def get_current_app(device_id: str | None = None) -> str:
|
|
11
|
-
"""
|
|
12
|
-
Get the currently focused app name.
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
device_id: Optional HDC device ID for multi-device setups.
|
|
16
|
-
|
|
17
|
-
Returns:
|
|
18
|
-
The app name if recognized, otherwise "System Home".
|
|
19
|
-
"""
|
|
20
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
21
|
-
|
|
22
|
-
result = _run_hdc_command(
|
|
23
|
-
hdc_prefix + ["shell", "hidumper", "-s", "WindowManagerService", "-a", "-a"],
|
|
24
|
-
capture_output=True,
|
|
25
|
-
text=True,
|
|
26
|
-
encoding="utf-8",
|
|
27
|
-
)
|
|
28
|
-
output = result.stdout
|
|
29
|
-
if not output:
|
|
30
|
-
raise ValueError("No output from hidumper")
|
|
31
|
-
|
|
32
|
-
# Parse window focus info
|
|
33
|
-
for line in output.split("\n"):
|
|
34
|
-
if "focused" in line.lower() or "current" in line.lower():
|
|
35
|
-
for app_name, package in APP_PACKAGES.items():
|
|
36
|
-
if package in line:
|
|
37
|
-
return app_name
|
|
38
|
-
|
|
39
|
-
return "System Home"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def tap(
|
|
43
|
-
x: int, y: int, device_id: str | None = None, delay: float | None = None
|
|
44
|
-
) -> None:
|
|
45
|
-
"""
|
|
46
|
-
Tap at the specified coordinates.
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
x: X coordinate.
|
|
50
|
-
y: Y coordinate.
|
|
51
|
-
device_id: Optional HDC device ID.
|
|
52
|
-
delay: Delay in seconds after tap. If None, uses configured default.
|
|
53
|
-
"""
|
|
54
|
-
if delay is None:
|
|
55
|
-
delay = TIMING_CONFIG.device.default_tap_delay
|
|
56
|
-
|
|
57
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
58
|
-
|
|
59
|
-
# HarmonyOS uses uitest uiInput click
|
|
60
|
-
_run_hdc_command(
|
|
61
|
-
hdc_prefix + ["shell", "uitest", "uiInput", "click", str(x), str(y)],
|
|
62
|
-
capture_output=True,
|
|
63
|
-
)
|
|
64
|
-
time.sleep(delay)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def double_tap(
|
|
68
|
-
x: int, y: int, device_id: str | None = None, delay: float | None = None
|
|
69
|
-
) -> None:
|
|
70
|
-
"""
|
|
71
|
-
Double tap at the specified coordinates.
|
|
72
|
-
|
|
73
|
-
Args:
|
|
74
|
-
x: X coordinate.
|
|
75
|
-
y: Y coordinate.
|
|
76
|
-
device_id: Optional HDC device ID.
|
|
77
|
-
delay: Delay in seconds after double tap. If None, uses configured default.
|
|
78
|
-
"""
|
|
79
|
-
if delay is None:
|
|
80
|
-
delay = TIMING_CONFIG.device.default_double_tap_delay
|
|
81
|
-
|
|
82
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
83
|
-
|
|
84
|
-
# HarmonyOS uses uitest uiInput doubleClick
|
|
85
|
-
_run_hdc_command(
|
|
86
|
-
hdc_prefix + ["shell", "uitest", "uiInput", "doubleClick", str(x), str(y)],
|
|
87
|
-
capture_output=True,
|
|
88
|
-
)
|
|
89
|
-
time.sleep(delay)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def long_press(
|
|
93
|
-
x: int,
|
|
94
|
-
y: int,
|
|
95
|
-
duration_ms: int = 3000,
|
|
96
|
-
device_id: str | None = None,
|
|
97
|
-
delay: float | None = None,
|
|
98
|
-
) -> None:
|
|
99
|
-
"""
|
|
100
|
-
Long press at the specified coordinates.
|
|
101
|
-
|
|
102
|
-
Args:
|
|
103
|
-
x: X coordinate.
|
|
104
|
-
y: Y coordinate.
|
|
105
|
-
duration_ms: Duration of press in milliseconds (note: HarmonyOS longClick may not support duration).
|
|
106
|
-
device_id: Optional HDC device ID.
|
|
107
|
-
delay: Delay in seconds after long press. If None, uses configured default.
|
|
108
|
-
"""
|
|
109
|
-
if delay is None:
|
|
110
|
-
delay = TIMING_CONFIG.device.default_long_press_delay
|
|
111
|
-
|
|
112
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
113
|
-
|
|
114
|
-
# HarmonyOS uses uitest uiInput longClick
|
|
115
|
-
# Note: longClick may have a fixed duration, duration_ms parameter might not be supported
|
|
116
|
-
_run_hdc_command(
|
|
117
|
-
hdc_prefix + ["shell", "uitest", "uiInput", "longClick", str(x), str(y)],
|
|
118
|
-
capture_output=True,
|
|
119
|
-
)
|
|
120
|
-
time.sleep(delay)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def swipe(
|
|
124
|
-
start_x: int,
|
|
125
|
-
start_y: int,
|
|
126
|
-
end_x: int,
|
|
127
|
-
end_y: int,
|
|
128
|
-
duration_ms: int | None = None,
|
|
129
|
-
device_id: str | None = None,
|
|
130
|
-
delay: float | None = None,
|
|
131
|
-
) -> None:
|
|
132
|
-
"""
|
|
133
|
-
Swipe from start to end coordinates.
|
|
134
|
-
|
|
135
|
-
Args:
|
|
136
|
-
start_x: Starting X coordinate.
|
|
137
|
-
start_y: Starting Y coordinate.
|
|
138
|
-
end_x: Ending X coordinate.
|
|
139
|
-
end_y: Ending Y coordinate.
|
|
140
|
-
duration_ms: Duration of swipe in milliseconds (auto-calculated if None).
|
|
141
|
-
device_id: Optional HDC device ID.
|
|
142
|
-
delay: Delay in seconds after swipe. If None, uses configured default.
|
|
143
|
-
"""
|
|
144
|
-
if delay is None:
|
|
145
|
-
delay = TIMING_CONFIG.device.default_swipe_delay
|
|
146
|
-
|
|
147
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
148
|
-
|
|
149
|
-
if duration_ms is None:
|
|
150
|
-
# Calculate duration based on distance
|
|
151
|
-
dist_sq = (start_x - end_x) ** 2 + (start_y - end_y) ** 2
|
|
152
|
-
duration_ms = int(dist_sq / 1000)
|
|
153
|
-
duration_ms = max(500, min(duration_ms, 1000)) # Clamp between 500-1000ms
|
|
154
|
-
|
|
155
|
-
# HarmonyOS uses uitest uiInput swipe
|
|
156
|
-
# Format: swipe startX startY endX endY duration
|
|
157
|
-
_run_hdc_command(
|
|
158
|
-
hdc_prefix
|
|
159
|
-
+ [
|
|
160
|
-
"shell",
|
|
161
|
-
"uitest",
|
|
162
|
-
"uiInput",
|
|
163
|
-
"swipe",
|
|
164
|
-
str(start_x),
|
|
165
|
-
str(start_y),
|
|
166
|
-
str(end_x),
|
|
167
|
-
str(end_y),
|
|
168
|
-
str(duration_ms),
|
|
169
|
-
],
|
|
170
|
-
capture_output=True,
|
|
171
|
-
)
|
|
172
|
-
time.sleep(delay)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def back(device_id: str | None = None, delay: float | None = None) -> None:
|
|
176
|
-
"""
|
|
177
|
-
Press the back button.
|
|
178
|
-
|
|
179
|
-
Args:
|
|
180
|
-
device_id: Optional HDC device ID.
|
|
181
|
-
delay: Delay in seconds after pressing back. If None, uses configured default.
|
|
182
|
-
"""
|
|
183
|
-
if delay is None:
|
|
184
|
-
delay = TIMING_CONFIG.device.default_back_delay
|
|
185
|
-
|
|
186
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
187
|
-
|
|
188
|
-
# HarmonyOS uses uitest uiInput keyEvent Back
|
|
189
|
-
_run_hdc_command(
|
|
190
|
-
hdc_prefix + ["shell", "uitest", "uiInput", "keyEvent", "Back"],
|
|
191
|
-
capture_output=True,
|
|
192
|
-
)
|
|
193
|
-
time.sleep(delay)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def home(device_id: str | None = None, delay: float | None = None) -> None:
|
|
197
|
-
"""
|
|
198
|
-
Press the home button.
|
|
199
|
-
|
|
200
|
-
Args:
|
|
201
|
-
device_id: Optional HDC device ID.
|
|
202
|
-
delay: Delay in seconds after pressing home. If None, uses configured default.
|
|
203
|
-
"""
|
|
204
|
-
if delay is None:
|
|
205
|
-
delay = TIMING_CONFIG.device.default_home_delay
|
|
206
|
-
|
|
207
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
208
|
-
|
|
209
|
-
# HarmonyOS uses uitest uiInput keyEvent Home
|
|
210
|
-
_run_hdc_command(
|
|
211
|
-
hdc_prefix + ["shell", "uitest", "uiInput", "keyEvent", "Home"],
|
|
212
|
-
capture_output=True,
|
|
213
|
-
)
|
|
214
|
-
time.sleep(delay)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def launch_app(
|
|
218
|
-
app_name: str, device_id: str | None = None, delay: float | None = None
|
|
219
|
-
) -> bool:
|
|
220
|
-
"""
|
|
221
|
-
Launch an app by name.
|
|
222
|
-
|
|
223
|
-
Args:
|
|
224
|
-
app_name: The app name (must be in APP_PACKAGES).
|
|
225
|
-
device_id: Optional HDC device ID.
|
|
226
|
-
delay: Delay in seconds after launching. If None, uses configured default.
|
|
227
|
-
|
|
228
|
-
Returns:
|
|
229
|
-
True if app was launched, False if app not found.
|
|
230
|
-
"""
|
|
231
|
-
if delay is None:
|
|
232
|
-
delay = TIMING_CONFIG.device.default_launch_delay
|
|
233
|
-
|
|
234
|
-
if app_name not in APP_PACKAGES:
|
|
235
|
-
print(f"[HDC] App '{app_name}' not found in HarmonyOS app list")
|
|
236
|
-
print(f"[HDC] Available apps: {', '.join(sorted(APP_PACKAGES.keys())[:10])}...")
|
|
237
|
-
return False
|
|
238
|
-
|
|
239
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
240
|
-
bundle = APP_PACKAGES[app_name]
|
|
241
|
-
|
|
242
|
-
# Get the ability name for this bundle
|
|
243
|
-
# Default to "EntryAbility" if not specified in APP_ABILITIES
|
|
244
|
-
ability = APP_ABILITIES.get(bundle, "EntryAbility")
|
|
245
|
-
|
|
246
|
-
# HarmonyOS uses 'aa start' command to launch apps
|
|
247
|
-
# Format: aa start -b {bundle} -a {ability}
|
|
248
|
-
_run_hdc_command(
|
|
249
|
-
hdc_prefix
|
|
250
|
-
+ [
|
|
251
|
-
"shell",
|
|
252
|
-
"aa",
|
|
253
|
-
"start",
|
|
254
|
-
"-b",
|
|
255
|
-
bundle,
|
|
256
|
-
"-a",
|
|
257
|
-
ability,
|
|
258
|
-
],
|
|
259
|
-
capture_output=True,
|
|
260
|
-
)
|
|
261
|
-
time.sleep(delay)
|
|
262
|
-
return True
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
def _get_hdc_prefix(device_id: str | None) -> list:
|
|
266
|
-
"""Get HDC command prefix with optional device specifier."""
|
|
267
|
-
if device_id:
|
|
268
|
-
return ["hdc", "-t", device_id]
|
|
269
|
-
return ["hdc"]
|
phone_agent/hdc/input.py
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
"""Input utilities for HarmonyOS device text input."""
|
|
2
|
-
|
|
3
|
-
from phone_agent.hdc.connection import _run_hdc_command
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def type_text(text: str, device_id: str | None = None) -> None:
|
|
7
|
-
"""
|
|
8
|
-
Type text into the currently focused input field.
|
|
9
|
-
|
|
10
|
-
Args:
|
|
11
|
-
text: The text to type. Supports multi-line text with newline characters.
|
|
12
|
-
device_id: Optional HDC device ID for multi-device setups.
|
|
13
|
-
|
|
14
|
-
Note:
|
|
15
|
-
HarmonyOS uses: hdc shell uitest uiInput text "文本内容"
|
|
16
|
-
This command works without coordinates when input field is focused.
|
|
17
|
-
For multi-line text, the function splits by newlines and sends ENTER keyEvents.
|
|
18
|
-
ENTER key code in HarmonyOS: 2054
|
|
19
|
-
Recommendation: Click on the input field first to focus it, then use this function.
|
|
20
|
-
"""
|
|
21
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
22
|
-
|
|
23
|
-
# Handle multi-line text by splitting on newlines
|
|
24
|
-
if "\n" in text:
|
|
25
|
-
lines = text.split("\n")
|
|
26
|
-
for i, line in enumerate(lines):
|
|
27
|
-
if line: # Only process non-empty lines
|
|
28
|
-
# Escape special characters for shell
|
|
29
|
-
escaped_line = line.replace('"', '\\"').replace("$", "\\$")
|
|
30
|
-
|
|
31
|
-
_run_hdc_command(
|
|
32
|
-
hdc_prefix + ["shell", "uitest", "uiInput", "text", escaped_line],
|
|
33
|
-
capture_output=True,
|
|
34
|
-
text=True,
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
# Send ENTER key event after each line except the last one
|
|
38
|
-
if i < len(lines) - 1:
|
|
39
|
-
try:
|
|
40
|
-
_run_hdc_command(
|
|
41
|
-
hdc_prefix + ["shell", "uitest", "uiInput", "keyEvent", "2054"],
|
|
42
|
-
capture_output=True,
|
|
43
|
-
text=True,
|
|
44
|
-
)
|
|
45
|
-
except Exception as e:
|
|
46
|
-
print(f"[HDC] ENTER keyEvent failed: {e}")
|
|
47
|
-
else:
|
|
48
|
-
# Single line text - original logic
|
|
49
|
-
# Escape special characters for shell (keep quotes for proper text handling)
|
|
50
|
-
# The text will be wrapped in quotes in the command
|
|
51
|
-
escaped_text = text.replace('"', '\\"').replace("$", "\\$")
|
|
52
|
-
|
|
53
|
-
# HarmonyOS uitest uiInput text command
|
|
54
|
-
# Format: hdc shell uitest uiInput text "文本内容"
|
|
55
|
-
_run_hdc_command(
|
|
56
|
-
hdc_prefix + ["shell", "uitest", "uiInput", "text", escaped_text],
|
|
57
|
-
capture_output=True,
|
|
58
|
-
text=True,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def clear_text(device_id: str | None = None) -> None:
|
|
63
|
-
"""
|
|
64
|
-
Clear text in the currently focused input field.
|
|
65
|
-
|
|
66
|
-
Args:
|
|
67
|
-
device_id: Optional HDC device ID for multi-device setups.
|
|
68
|
-
|
|
69
|
-
Note:
|
|
70
|
-
This method uses repeated delete key events to clear text.
|
|
71
|
-
For HarmonyOS, you might also use select all + delete for better efficiency.
|
|
72
|
-
"""
|
|
73
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
74
|
-
# Ctrl+A to select all (key code 2072 for Ctrl, 2017 for A)
|
|
75
|
-
# Then delete
|
|
76
|
-
_run_hdc_command(
|
|
77
|
-
hdc_prefix + ["shell", "uitest", "uiInput", "keyEvent", "2072", "2017"],
|
|
78
|
-
capture_output=True,
|
|
79
|
-
text=True,
|
|
80
|
-
)
|
|
81
|
-
_run_hdc_command(
|
|
82
|
-
hdc_prefix + ["shell", "uitest", "uiInput", "keyEvent", "2055"], # Delete key
|
|
83
|
-
capture_output=True,
|
|
84
|
-
text=True,
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def detect_and_set_adb_keyboard(device_id: str | None = None) -> str:
|
|
89
|
-
"""
|
|
90
|
-
Detect current keyboard and switch to ADB Keyboard if available.
|
|
91
|
-
|
|
92
|
-
Args:
|
|
93
|
-
device_id: Optional HDC device ID for multi-device setups.
|
|
94
|
-
|
|
95
|
-
Returns:
|
|
96
|
-
The original keyboard IME identifier for later restoration.
|
|
97
|
-
|
|
98
|
-
Note:
|
|
99
|
-
This is a placeholder. HarmonyOS may not support ADB Keyboard.
|
|
100
|
-
If there's a similar tool for HarmonyOS, integrate it here.
|
|
101
|
-
"""
|
|
102
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
103
|
-
|
|
104
|
-
# Get current IME (if HarmonyOS supports this)
|
|
105
|
-
try:
|
|
106
|
-
result = _run_hdc_command(
|
|
107
|
-
hdc_prefix + ["shell", "settings", "get", "secure", "default_input_method"],
|
|
108
|
-
capture_output=True,
|
|
109
|
-
text=True,
|
|
110
|
-
)
|
|
111
|
-
current_ime = (result.stdout + result.stderr).strip()
|
|
112
|
-
|
|
113
|
-
# If ADB Keyboard equivalent exists for HarmonyOS, switch to it
|
|
114
|
-
# For now, we'll just return the current IME
|
|
115
|
-
return current_ime
|
|
116
|
-
except Exception:
|
|
117
|
-
return ""
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def restore_keyboard(ime: str, device_id: str | None = None) -> None:
|
|
121
|
-
"""
|
|
122
|
-
Restore the original keyboard IME.
|
|
123
|
-
|
|
124
|
-
Args:
|
|
125
|
-
ime: The IME identifier to restore.
|
|
126
|
-
device_id: Optional HDC device ID for multi-device setups.
|
|
127
|
-
"""
|
|
128
|
-
if not ime:
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
132
|
-
|
|
133
|
-
try:
|
|
134
|
-
_run_hdc_command(
|
|
135
|
-
hdc_prefix + ["shell", "ime", "set", ime], capture_output=True, text=True
|
|
136
|
-
)
|
|
137
|
-
except Exception:
|
|
138
|
-
pass
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def _get_hdc_prefix(device_id: str | None) -> list:
|
|
142
|
-
"""Get HDC command prefix with optional device specifier."""
|
|
143
|
-
if device_id:
|
|
144
|
-
return ["hdc", "-t", device_id]
|
|
145
|
-
return ["hdc"]
|
phone_agent/hdc/screenshot.py
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
"""Screenshot utilities for capturing HarmonyOS device screen."""
|
|
2
|
-
|
|
3
|
-
import base64
|
|
4
|
-
import os
|
|
5
|
-
import tempfile
|
|
6
|
-
import uuid
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from io import BytesIO
|
|
9
|
-
|
|
10
|
-
from PIL import Image
|
|
11
|
-
from phone_agent.hdc.connection import _run_hdc_command
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@dataclass
|
|
15
|
-
class Screenshot:
|
|
16
|
-
"""Represents a captured screenshot."""
|
|
17
|
-
|
|
18
|
-
base64_data: str
|
|
19
|
-
width: int
|
|
20
|
-
height: int
|
|
21
|
-
is_sensitive: bool = False
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def get_screenshot(device_id: str | None = None, timeout: int = 10) -> Screenshot:
|
|
25
|
-
"""
|
|
26
|
-
Capture a screenshot from the connected HarmonyOS device.
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
device_id: Optional HDC device ID for multi-device setups.
|
|
30
|
-
timeout: Timeout in seconds for screenshot operations.
|
|
31
|
-
|
|
32
|
-
Returns:
|
|
33
|
-
Screenshot object containing base64 data and dimensions.
|
|
34
|
-
|
|
35
|
-
Note:
|
|
36
|
-
If the screenshot fails (e.g., on sensitive screens like payment pages),
|
|
37
|
-
a black fallback image is returned with is_sensitive=True.
|
|
38
|
-
"""
|
|
39
|
-
temp_path = os.path.join(tempfile.gettempdir(), f"screenshot_{uuid.uuid4()}.png")
|
|
40
|
-
hdc_prefix = _get_hdc_prefix(device_id)
|
|
41
|
-
|
|
42
|
-
try:
|
|
43
|
-
# Execute screenshot command
|
|
44
|
-
# HarmonyOS HDC only supports JPEG format
|
|
45
|
-
remote_path = "/data/local/tmp/tmp_screenshot.jpeg"
|
|
46
|
-
|
|
47
|
-
# Try method 1: hdc shell screenshot (newer HarmonyOS versions)
|
|
48
|
-
result = _run_hdc_command(
|
|
49
|
-
hdc_prefix + ["shell", "screenshot", remote_path],
|
|
50
|
-
capture_output=True,
|
|
51
|
-
text=True,
|
|
52
|
-
timeout=timeout,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
# Check for screenshot failure (sensitive screen)
|
|
56
|
-
output = result.stdout + result.stderr
|
|
57
|
-
if (
|
|
58
|
-
"fail" in output.lower()
|
|
59
|
-
or "error" in output.lower()
|
|
60
|
-
or "not found" in output.lower()
|
|
61
|
-
):
|
|
62
|
-
# Try method 2: snapshot_display (older versions or different devices)
|
|
63
|
-
result = _run_hdc_command(
|
|
64
|
-
hdc_prefix + ["shell", "snapshot_display", "-f", remote_path],
|
|
65
|
-
capture_output=True,
|
|
66
|
-
text=True,
|
|
67
|
-
timeout=timeout,
|
|
68
|
-
)
|
|
69
|
-
output = result.stdout + result.stderr
|
|
70
|
-
if "fail" in output.lower() or "error" in output.lower():
|
|
71
|
-
return _create_fallback_screenshot(is_sensitive=True)
|
|
72
|
-
|
|
73
|
-
# Pull screenshot to local temp path
|
|
74
|
-
# Note: remote file is JPEG, but PIL can open it regardless of local extension
|
|
75
|
-
_run_hdc_command(
|
|
76
|
-
hdc_prefix + ["file", "recv", remote_path, temp_path],
|
|
77
|
-
capture_output=True,
|
|
78
|
-
text=True,
|
|
79
|
-
timeout=5,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
if not os.path.exists(temp_path):
|
|
83
|
-
return _create_fallback_screenshot(is_sensitive=False)
|
|
84
|
-
|
|
85
|
-
# Read JPEG image and convert to PNG for model inference
|
|
86
|
-
# PIL automatically detects the image format from file content
|
|
87
|
-
img = Image.open(temp_path)
|
|
88
|
-
width, height = img.size
|
|
89
|
-
|
|
90
|
-
buffered = BytesIO()
|
|
91
|
-
img.save(buffered, format="PNG")
|
|
92
|
-
base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8")
|
|
93
|
-
|
|
94
|
-
# Cleanup
|
|
95
|
-
os.remove(temp_path)
|
|
96
|
-
|
|
97
|
-
return Screenshot(
|
|
98
|
-
base64_data=base64_data, width=width, height=height, is_sensitive=False
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
except Exception as e:
|
|
102
|
-
print(f"Screenshot error: {e}")
|
|
103
|
-
return _create_fallback_screenshot(is_sensitive=False)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def _get_hdc_prefix(device_id: str | None) -> list:
|
|
107
|
-
"""Get HDC command prefix with optional device specifier."""
|
|
108
|
-
if device_id:
|
|
109
|
-
return ["hdc", "-t", device_id]
|
|
110
|
-
return ["hdc"]
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def _create_fallback_screenshot(is_sensitive: bool) -> Screenshot:
|
|
114
|
-
"""Create a black fallback image when screenshot fails."""
|
|
115
|
-
default_width, default_height = 1080, 2400
|
|
116
|
-
|
|
117
|
-
black_img = Image.new("RGB", (default_width, default_height), color="black")
|
|
118
|
-
buffered = BytesIO()
|
|
119
|
-
black_img.save(buffered, format="PNG")
|
|
120
|
-
base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8")
|
|
121
|
-
|
|
122
|
-
return Screenshot(
|
|
123
|
-
base64_data=base64_data,
|
|
124
|
-
width=default_width,
|
|
125
|
-
height=default_height,
|
|
126
|
-
is_sensitive=is_sensitive,
|
|
127
|
-
)
|