autoglm-gui 0.4.14__py3-none-any.whl → 1.0.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/api/devices.py +49 -0
- AutoGLM_GUI/schemas.py +16 -0
- AutoGLM_GUI/static/assets/{about-29B5FDM8.js → about-BOnRPlKQ.js} +1 -1
- AutoGLM_GUI/static/assets/chat-CGW6uMKB.js +149 -0
- AutoGLM_GUI/static/assets/{index-mVNV0VwM.js → index-CRFVU0eu.js} +1 -1
- AutoGLM_GUI/static/assets/{index-wu8Wjf12.js → index-DH-Dl4tK.js} +5 -5
- AutoGLM_GUI/static/assets/index-DzUQ89YC.css +1 -0
- AutoGLM_GUI/static/index.html +2 -2
- {autoglm_gui-0.4.14.dist-info → autoglm_gui-1.0.1.dist-info}/METADATA +3 -3
- autoglm_gui-1.0.1.dist-info/RECORD +73 -0
- phone_agent/__init__.py +3 -2
- phone_agent/actions/handler.py +124 -31
- phone_agent/actions/handler_ios.py +278 -0
- phone_agent/adb/connection.py +14 -5
- phone_agent/adb/device.py +47 -16
- phone_agent/agent.py +8 -8
- phone_agent/agent_ios.py +277 -0
- phone_agent/config/__init__.py +18 -0
- phone_agent/config/apps.py +1 -1
- phone_agent/config/apps_harmonyos.py +256 -0
- phone_agent/config/apps_ios.py +339 -0
- phone_agent/config/i18n.py +8 -0
- phone_agent/config/timing.py +167 -0
- phone_agent/device_factory.py +166 -0
- phone_agent/hdc/__init__.py +53 -0
- phone_agent/hdc/connection.py +384 -0
- phone_agent/hdc/device.py +269 -0
- phone_agent/hdc/input.py +145 -0
- phone_agent/hdc/screenshot.py +127 -0
- phone_agent/model/client.py +104 -4
- phone_agent/xctest/__init__.py +47 -0
- phone_agent/xctest/connection.py +379 -0
- phone_agent/xctest/device.py +472 -0
- phone_agent/xctest/input.py +311 -0
- phone_agent/xctest/screenshot.py +226 -0
- AutoGLM_GUI/static/assets/chat-DTN2oKtA.js +0 -149
- AutoGLM_GUI/static/assets/index-Dy550Qqg.css +0 -1
- autoglm_gui-0.4.14.dist-info/RECORD +0 -57
- {autoglm_gui-0.4.14.dist-info → autoglm_gui-1.0.1.dist-info}/WHEEL +0 -0
- {autoglm_gui-0.4.14.dist-info → autoglm_gui-1.0.1.dist-info}/entry_points.txt +0 -0
- {autoglm_gui-0.4.14.dist-info → autoglm_gui-1.0.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"""App name to iOS bundle ID mapping for supported applications.
|
|
2
|
+
|
|
3
|
+
Based on iOS app bundle ID conventions and common iOS applications.
|
|
4
|
+
Bundle IDs are in the format: com.company.appName
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
APP_PACKAGES_IOS: dict[str, str] = {
|
|
8
|
+
# Tencent Apps (腾讯系)
|
|
9
|
+
"微信": "com.tencent.xin",
|
|
10
|
+
"企业微信": "com.tencent.ww",
|
|
11
|
+
"微信读书": "com.tencent.weread",
|
|
12
|
+
"微信听书": "com.tencent.wehear",
|
|
13
|
+
"QQ": "com.tencent.mqq",
|
|
14
|
+
"QQ音乐": "com.tencent.QQMusic",
|
|
15
|
+
"QQ阅读": "com.tencent.qqreaderiphone",
|
|
16
|
+
"QQ邮箱": "com.tencent.qqmail",
|
|
17
|
+
"QQ浏览器": "com.tencent.mttlite",
|
|
18
|
+
"TIM": "com.tencent.tim",
|
|
19
|
+
"微视": "com.tencent.microvision",
|
|
20
|
+
"腾讯新闻": "com.tencent.info",
|
|
21
|
+
"腾讯视频": "com.tencent.live4iphone",
|
|
22
|
+
"腾讯动漫": "com.tencent.ied.app.comic",
|
|
23
|
+
"腾讯微云": "com.tencent.weiyun",
|
|
24
|
+
"腾讯体育": "com.tencent.sportskbs",
|
|
25
|
+
"腾讯文档": "com.tencent.txdocs",
|
|
26
|
+
"腾讯翻译君": "com.tencent.qqtranslator",
|
|
27
|
+
"腾讯课堂": "com.tencent.edu",
|
|
28
|
+
"腾讯地图": "com.tencent.sosomap",
|
|
29
|
+
"小鹅拼拼": "com.tencent.dwdcoco",
|
|
30
|
+
"全民k歌": "com.tencent.QQKSong",
|
|
31
|
+
# Alibaba Apps (阿里系)
|
|
32
|
+
"支付宝": "com.alipay.iphoneclient",
|
|
33
|
+
"钉钉": "com.laiwang.DingTalk",
|
|
34
|
+
"闲鱼": "com.taobao.fleamarket",
|
|
35
|
+
"淘宝": "com.taobao.taobao4iphone",
|
|
36
|
+
"斗鱼": "tv.douyu.live",
|
|
37
|
+
"天猫": "com.taobao.tmall",
|
|
38
|
+
"口碑": "com.taobao.kbmeishi",
|
|
39
|
+
"饿了么": "me.ele.ios.eleme",
|
|
40
|
+
"高德地图": "com.autonavi.amap",
|
|
41
|
+
"UC浏览器": "com.ucweb.iphone.lowversion",
|
|
42
|
+
"一淘": "com.taobao.etaocoupon",
|
|
43
|
+
"飞猪": "com.taobao.travel",
|
|
44
|
+
"虾米音乐": "com.xiami.spark",
|
|
45
|
+
"淘票票": "com.taobao.movie.MoviePhoneClient",
|
|
46
|
+
"优酷": "com.youku.YouKu",
|
|
47
|
+
"菜鸟裹裹": "com.cainiao.cnwireless",
|
|
48
|
+
"土豆视频": "com.tudou.tudouiphone",
|
|
49
|
+
# ByteDance Apps (字节系)
|
|
50
|
+
"抖音": "com.ss.iphone.ugc.Aweme",
|
|
51
|
+
"抖音极速版": "com.ss.iphone.ugc.aweme.lite",
|
|
52
|
+
"抖音火山版": "com.ss.iphone.ugc.Live",
|
|
53
|
+
"Tiktok": "com.zhiliaoapp.musically",
|
|
54
|
+
"飞书": "com.bytedance.ee.lark",
|
|
55
|
+
"今日头条": "com.ss.iphone.article.News",
|
|
56
|
+
"西瓜视频": "com.ss.iphone.article.Video",
|
|
57
|
+
"皮皮虾": "com.bd.iphone.super",
|
|
58
|
+
# Meituan Apps (美团系)
|
|
59
|
+
"美团": "com.meituan.imeituan",
|
|
60
|
+
"美团外卖": "com.meituan.itakeaway",
|
|
61
|
+
"大众点评": "com.dianping.dpscope",
|
|
62
|
+
"美团优选": "com.meituan.iyouxuan",
|
|
63
|
+
"美团优选团长": "com.meituan.igrocery.gh",
|
|
64
|
+
"美团骑手": "com.meituan.banma.homebrew",
|
|
65
|
+
"美团开店宝": "com.meituan.imerchantbiz",
|
|
66
|
+
"美团拍店": "com.meituan.pai",
|
|
67
|
+
"美团众包": "com.meituan.banma.crowdsource",
|
|
68
|
+
"美团买菜": "com.baobaoaichi.imaicai",
|
|
69
|
+
# JD Apps (京东系)
|
|
70
|
+
"京东": "com.360buy.jdmobile",
|
|
71
|
+
"京东读书": "com.jd.reader",
|
|
72
|
+
# NetEase Apps (网易系)
|
|
73
|
+
"网易新闻": "com.netease.news",
|
|
74
|
+
"网易云音乐": "com.netease.cloudmusic",
|
|
75
|
+
"网易邮箱大师": "com.netease.macmail",
|
|
76
|
+
"网易严选": "com.netease.yanxuan",
|
|
77
|
+
"网易公开课": "com.netease.videoHD",
|
|
78
|
+
"网易有道词典": "youdaoPro",
|
|
79
|
+
"有道云笔记": "com.youdao.note.YoudaoNoteMac",
|
|
80
|
+
# Baidu Apps (百度系)
|
|
81
|
+
"百度": "com.baidu.BaiduMobile",
|
|
82
|
+
"百度网盘": "com.baidu.netdisk",
|
|
83
|
+
"百度贴吧": "com.baidu.tieba",
|
|
84
|
+
"百度地图": "com.baidu.map",
|
|
85
|
+
"百度阅读": "com.baidu.yuedu",
|
|
86
|
+
"百度翻译": "com.baidu.translate",
|
|
87
|
+
"百度文库": "com.baidu.Wenku",
|
|
88
|
+
"百度视频": "com.baidu.videoiphone",
|
|
89
|
+
"百度输入法": "com.baidu.inputMethod",
|
|
90
|
+
# Kuaishou Apps (快手系)
|
|
91
|
+
"快手": "com.jiangjia.gif",
|
|
92
|
+
"快手极速版": "com.kuaishou.nebula",
|
|
93
|
+
# Other Popular Apps
|
|
94
|
+
"哔哩哔哩": "tv.danmaku.bilianime",
|
|
95
|
+
"芒果TV": "com.hunantv.imgotv",
|
|
96
|
+
"苏宁易购": "SuningEMall",
|
|
97
|
+
"微博": "com.sina.weibo",
|
|
98
|
+
"微博极速版": "com.sina.weibolite",
|
|
99
|
+
"微博国际": "com.weibo.international",
|
|
100
|
+
"墨客": "com.moke.moke.iphone",
|
|
101
|
+
"豆瓣": "com.douban.frodo",
|
|
102
|
+
"知乎": "com.zhihu.ios",
|
|
103
|
+
"小红书": "com.xingin.discover",
|
|
104
|
+
"喜马拉雅": "com.gemd.iting",
|
|
105
|
+
"得到": "com.luojilab.LuoJiFM-IOS",
|
|
106
|
+
"得物": "com.siwuai.duapp",
|
|
107
|
+
"起点读书": "m.qidian.QDReaderAppStore",
|
|
108
|
+
"番茄小说": "com.dragon.read",
|
|
109
|
+
"书旗小说": "com.shuqicenter.reader",
|
|
110
|
+
"拼多多": "com.xunmeng.pinduoduo",
|
|
111
|
+
"多点": "com.dmall.dmall",
|
|
112
|
+
"便利蜂": "com.bianlifeng.customer.ios",
|
|
113
|
+
"亿通行": "com.ruubypay.yitongxing",
|
|
114
|
+
"云闪付": "com.unionpay.chsp",
|
|
115
|
+
"大都会Metro": "com.DDH.SHSubway",
|
|
116
|
+
"爱奇艺视频": "com.qiyi.iphone",
|
|
117
|
+
"搜狐视频": "com.sohu.iPhoneVideo",
|
|
118
|
+
"搜狐新闻": "com.sohu.newspaper",
|
|
119
|
+
"搜狗浏览器": "com.sogou.SogouExplorerMobile",
|
|
120
|
+
"虎牙": "com.yy.kiwi",
|
|
121
|
+
"比心": "com.yitan.bixin",
|
|
122
|
+
"转转": "com.wuba.zhuanzhuan",
|
|
123
|
+
"YY": "yyvoice",
|
|
124
|
+
"绿洲": "com.sina.oasis",
|
|
125
|
+
"陌陌": "com.wemomo.momoappdemo1",
|
|
126
|
+
"什么值得买": "com.smzdm.client.ios",
|
|
127
|
+
"美团秀秀": "com.meitu.mtxx",
|
|
128
|
+
"唯品会": "com.vipshop.iphone",
|
|
129
|
+
"唱吧": "com.changba.ktv",
|
|
130
|
+
"酷狗音乐": "com.kugou.kugou1002",
|
|
131
|
+
"CSDN": "net.csdn.CsdnPlus",
|
|
132
|
+
"多抓鱼": "com.duozhuyu.dejavu",
|
|
133
|
+
"自如": "com.ziroom.ZiroomProject",
|
|
134
|
+
"携程": "ctrip.com",
|
|
135
|
+
"去哪儿旅行": "com.qunar.iphoneclient8",
|
|
136
|
+
"Xmind": "net.xmind.brownieapp",
|
|
137
|
+
"印象笔记": "com.yinxiang.iPhone",
|
|
138
|
+
"欧陆词典": "eusoft.eudic.pro",
|
|
139
|
+
"115": "com.115.personal",
|
|
140
|
+
"名片全能王": "com.intsig.camcard.lite",
|
|
141
|
+
"中国银行": "com.boc.BOCMBCI",
|
|
142
|
+
"58同城": "com.taofang.iphone",
|
|
143
|
+
# International Apps
|
|
144
|
+
"Google Chrome": "com.google.chrome.ios",
|
|
145
|
+
"Gmail": "com.google.Gmail",
|
|
146
|
+
"Facebook": "com.facebook.Facebook",
|
|
147
|
+
"Firefox": "org.mozilla.ios.Firefox",
|
|
148
|
+
"Messenger": "com.facebook.Messenger",
|
|
149
|
+
"Instagram": "com.burbn.instagram",
|
|
150
|
+
"Starbucks": "com.starbucks.mystarbucks",
|
|
151
|
+
"Luckin Coffee": "com.bjlc.luckycoffee",
|
|
152
|
+
"Line": "jp.naver.line",
|
|
153
|
+
"Linkedin": "com.linkedin.LinkedIn",
|
|
154
|
+
"Dcard": "com.dcard.app.Dcard",
|
|
155
|
+
"Youtube": "com.google.ios.youtube",
|
|
156
|
+
"Spotify": "com.spotify.client",
|
|
157
|
+
"Netflix": "com.netflix.Netflix",
|
|
158
|
+
"Twitter": "com.atebits.Tweetie2",
|
|
159
|
+
"WhatsApp": "net.whatsapp.WhatsApp",
|
|
160
|
+
# Apple Native Apps (Apple 原生应用)
|
|
161
|
+
"Safari": "com.apple.mobilesafari",
|
|
162
|
+
"App Store": "com.apple.AppStore",
|
|
163
|
+
"设置": "com.apple.Preferences",
|
|
164
|
+
"相机": "com.apple.camera",
|
|
165
|
+
"照片": "com.apple.mobileslideshow",
|
|
166
|
+
"时钟": "com.apple.mobiletimer",
|
|
167
|
+
"闹钟": "com.apple.mobiletimer",
|
|
168
|
+
"备忘录": "com.apple.mobilenotes",
|
|
169
|
+
"提醒事项": "com.apple.reminders",
|
|
170
|
+
"快捷指令": "com.apple.shortcuts",
|
|
171
|
+
"天气": "com.apple.weather",
|
|
172
|
+
"日历": "com.apple.mobilecal",
|
|
173
|
+
"地图": "com.apple.Maps",
|
|
174
|
+
"电话": "com.apple.mobilephone",
|
|
175
|
+
"通讯录": "com.apple.MobileAddressBook",
|
|
176
|
+
"信息": "com.apple.MobileSMS",
|
|
177
|
+
"Facetime": "com.apple.facetime",
|
|
178
|
+
"FaceTime": "com.apple.facetime",
|
|
179
|
+
"计算器": "com.apple.calculator",
|
|
180
|
+
"家庭": "com.apple.Home",
|
|
181
|
+
"健康": "com.apple.Health",
|
|
182
|
+
"钱包": "com.apple.Passbook",
|
|
183
|
+
"股市": "com.apple.stocks",
|
|
184
|
+
"图书": "com.apple.iBooks",
|
|
185
|
+
"新闻": "com.apple.news",
|
|
186
|
+
"视频": "com.apple.tv",
|
|
187
|
+
"文件": "com.apple.DocumentsApp",
|
|
188
|
+
"邮件": "com.apple.mobilemail",
|
|
189
|
+
"查找": "com.apple.findmy",
|
|
190
|
+
"翻译": "com.apple.Translate",
|
|
191
|
+
"音乐": "com.apple.Music",
|
|
192
|
+
"播客": "com.apple.podcasts",
|
|
193
|
+
"库乐队": "com.apple.mobilegarageband",
|
|
194
|
+
"语音备忘录": "com.apple.VoiceMemos",
|
|
195
|
+
"iMovie": "com.apple.iMovie",
|
|
196
|
+
"Watch": "com.apple.Bridge",
|
|
197
|
+
"Apple Store": "com.apple.store.Jolly",
|
|
198
|
+
"TestFlight": "com.apple.TestFlight",
|
|
199
|
+
"Keynote": "com.apple.Keynote",
|
|
200
|
+
"Keynote 讲演": "com.apple.Keynote",
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def get_bundle_id(app_name: str) -> str | None:
|
|
205
|
+
"""
|
|
206
|
+
Get the iOS bundle ID for an app.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
app_name: The display name of the app.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
The iOS bundle ID, or None if not found.
|
|
213
|
+
"""
|
|
214
|
+
return APP_PACKAGES_IOS.get(app_name)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def get_app_name(bundle_id: str) -> str | None:
|
|
218
|
+
"""
|
|
219
|
+
Get the app name from an iOS bundle ID.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
bundle_id: The iOS bundle ID.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
The display name of the app, or None if not found.
|
|
226
|
+
"""
|
|
227
|
+
for name, bid in APP_PACKAGES_IOS.items():
|
|
228
|
+
if bid == bundle_id:
|
|
229
|
+
return name
|
|
230
|
+
return None
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def list_supported_apps() -> list[str]:
|
|
234
|
+
"""
|
|
235
|
+
Get a list of all supported iOS app names.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
List of app names.
|
|
239
|
+
"""
|
|
240
|
+
return list(APP_PACKAGES_IOS.keys())
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def check_app_installed(app_name: str, wda_url: str = "http://localhost:8100") -> bool:
|
|
244
|
+
"""
|
|
245
|
+
Check if an app is installed on the iOS device.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
app_name: The display name of the app.
|
|
249
|
+
wda_url: WebDriverAgent URL.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
True if app is installed, False otherwise.
|
|
253
|
+
|
|
254
|
+
Note:
|
|
255
|
+
This uses the iTunes API to get app information. For actual
|
|
256
|
+
installation check on device, you would need to use WDA's
|
|
257
|
+
app listing capabilities or URL scheme checking.
|
|
258
|
+
"""
|
|
259
|
+
bundle_id = get_bundle_id(app_name)
|
|
260
|
+
if not bundle_id:
|
|
261
|
+
return False
|
|
262
|
+
|
|
263
|
+
try:
|
|
264
|
+
import requests
|
|
265
|
+
|
|
266
|
+
# Query iTunes API for app info
|
|
267
|
+
url = f"https://itunes.apple.com/lookup?bundleId={bundle_id}"
|
|
268
|
+
response = requests.get(url, timeout=10)
|
|
269
|
+
|
|
270
|
+
if response.status_code == 200:
|
|
271
|
+
data = response.json()
|
|
272
|
+
return data.get("resultCount", 0) > 0
|
|
273
|
+
|
|
274
|
+
except ImportError:
|
|
275
|
+
print("Error: requests library required. Install: pip install requests")
|
|
276
|
+
except Exception as e:
|
|
277
|
+
print(f"Error checking app installation: {e}")
|
|
278
|
+
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_app_info_from_itunes(bundle_id: str) -> dict | None:
|
|
283
|
+
"""
|
|
284
|
+
Get app information from iTunes API using bundle ID.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
bundle_id: The iOS bundle ID.
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Dictionary with app info (name, version, etc.) or None if not found.
|
|
291
|
+
"""
|
|
292
|
+
try:
|
|
293
|
+
import requests
|
|
294
|
+
|
|
295
|
+
url = f"https://itunes.apple.com/lookup?bundleId={bundle_id}"
|
|
296
|
+
response = requests.get(url, timeout=10)
|
|
297
|
+
|
|
298
|
+
if response.status_code == 200:
|
|
299
|
+
data = response.json()
|
|
300
|
+
results = data.get("results", [])
|
|
301
|
+
if results:
|
|
302
|
+
return results[0]
|
|
303
|
+
|
|
304
|
+
except ImportError:
|
|
305
|
+
print("Error: requests library required. Install: pip install requests")
|
|
306
|
+
except Exception as e:
|
|
307
|
+
print(f"Error fetching app info: {e}")
|
|
308
|
+
|
|
309
|
+
return None
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def get_app_info_by_id(app_store_id: str) -> dict | None:
|
|
313
|
+
"""
|
|
314
|
+
Get app information from iTunes API using App Store ID.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
app_store_id: The numeric App Store ID (e.g., "414478124" for WeChat).
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Dictionary with app info or None if not found.
|
|
321
|
+
"""
|
|
322
|
+
try:
|
|
323
|
+
import requests
|
|
324
|
+
|
|
325
|
+
url = f"https://itunes.apple.com/lookup?id={app_store_id}"
|
|
326
|
+
response = requests.get(url, timeout=10)
|
|
327
|
+
|
|
328
|
+
if response.status_code == 200:
|
|
329
|
+
data = response.json()
|
|
330
|
+
results = data.get("results", [])
|
|
331
|
+
if results:
|
|
332
|
+
return results[0]
|
|
333
|
+
|
|
334
|
+
except ImportError:
|
|
335
|
+
print("Error: requests library required. Install: pip install requests")
|
|
336
|
+
except Exception as e:
|
|
337
|
+
print(f"Error fetching app info by ID: {e}")
|
|
338
|
+
|
|
339
|
+
return None
|
phone_agent/config/i18n.py
CHANGED
|
@@ -19,6 +19,10 @@ MESSAGES_ZH = {
|
|
|
19
19
|
"step": "步骤",
|
|
20
20
|
"task": "任务",
|
|
21
21
|
"result": "结果",
|
|
22
|
+
"performance_metrics": "性能指标",
|
|
23
|
+
"time_to_first_token": "首 Token 延迟 (TTFT)",
|
|
24
|
+
"time_to_thinking_end": "思考完成延迟",
|
|
25
|
+
"total_inference_time": "总推理时间",
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
# English messages
|
|
@@ -40,6 +44,10 @@ MESSAGES_EN = {
|
|
|
40
44
|
"step": "Step",
|
|
41
45
|
"task": "Task",
|
|
42
46
|
"result": "Result",
|
|
47
|
+
"performance_metrics": "Performance Metrics",
|
|
48
|
+
"time_to_first_token": "Time to First Token (TTFT)",
|
|
49
|
+
"time_to_thinking_end": "Time to Thinking End",
|
|
50
|
+
"total_inference_time": "Total Inference Time",
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Timing configuration for Phone Agent.
|
|
2
|
+
|
|
3
|
+
This module defines all configurable waiting times used throughout the application.
|
|
4
|
+
Users can customize these values by modifying this file or by setting environment variables.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class ActionTimingConfig:
|
|
13
|
+
"""Configuration for action handler timing delays."""
|
|
14
|
+
|
|
15
|
+
# Text input related delays (in seconds)
|
|
16
|
+
keyboard_switch_delay: float = 1.0 # Delay after switching to ADB keyboard
|
|
17
|
+
text_clear_delay: float = 1.0 # Delay after clearing text
|
|
18
|
+
text_input_delay: float = 1.0 # Delay after typing text
|
|
19
|
+
keyboard_restore_delay: float = 1.0 # Delay after restoring original keyboard
|
|
20
|
+
|
|
21
|
+
def __post_init__(self):
|
|
22
|
+
"""Load values from environment variables if present."""
|
|
23
|
+
self.keyboard_switch_delay = float(
|
|
24
|
+
os.getenv("PHONE_AGENT_KEYBOARD_SWITCH_DELAY", self.keyboard_switch_delay)
|
|
25
|
+
)
|
|
26
|
+
self.text_clear_delay = float(
|
|
27
|
+
os.getenv("PHONE_AGENT_TEXT_CLEAR_DELAY", self.text_clear_delay)
|
|
28
|
+
)
|
|
29
|
+
self.text_input_delay = float(
|
|
30
|
+
os.getenv("PHONE_AGENT_TEXT_INPUT_DELAY", self.text_input_delay)
|
|
31
|
+
)
|
|
32
|
+
self.keyboard_restore_delay = float(
|
|
33
|
+
os.getenv("PHONE_AGENT_KEYBOARD_RESTORE_DELAY", self.keyboard_restore_delay)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class DeviceTimingConfig:
|
|
39
|
+
"""Configuration for device operation timing delays."""
|
|
40
|
+
|
|
41
|
+
# Default delays for various device operations (in seconds)
|
|
42
|
+
default_tap_delay: float = 1.0 # Default delay after tap
|
|
43
|
+
default_double_tap_delay: float = 1.0 # Default delay after double tap
|
|
44
|
+
double_tap_interval: float = 0.1 # Interval between two taps in double tap
|
|
45
|
+
default_long_press_delay: float = 1.0 # Default delay after long press
|
|
46
|
+
default_swipe_delay: float = 1.0 # Default delay after swipe
|
|
47
|
+
default_back_delay: float = 1.0 # Default delay after back button
|
|
48
|
+
default_home_delay: float = 1.0 # Default delay after home button
|
|
49
|
+
default_launch_delay: float = 1.0 # Default delay after launching app
|
|
50
|
+
|
|
51
|
+
def __post_init__(self):
|
|
52
|
+
"""Load values from environment variables if present."""
|
|
53
|
+
self.default_tap_delay = float(
|
|
54
|
+
os.getenv("PHONE_AGENT_TAP_DELAY", self.default_tap_delay)
|
|
55
|
+
)
|
|
56
|
+
self.default_double_tap_delay = float(
|
|
57
|
+
os.getenv("PHONE_AGENT_DOUBLE_TAP_DELAY", self.default_double_tap_delay)
|
|
58
|
+
)
|
|
59
|
+
self.double_tap_interval = float(
|
|
60
|
+
os.getenv("PHONE_AGENT_DOUBLE_TAP_INTERVAL", self.double_tap_interval)
|
|
61
|
+
)
|
|
62
|
+
self.default_long_press_delay = float(
|
|
63
|
+
os.getenv("PHONE_AGENT_LONG_PRESS_DELAY", self.default_long_press_delay)
|
|
64
|
+
)
|
|
65
|
+
self.default_swipe_delay = float(
|
|
66
|
+
os.getenv("PHONE_AGENT_SWIPE_DELAY", self.default_swipe_delay)
|
|
67
|
+
)
|
|
68
|
+
self.default_back_delay = float(
|
|
69
|
+
os.getenv("PHONE_AGENT_BACK_DELAY", self.default_back_delay)
|
|
70
|
+
)
|
|
71
|
+
self.default_home_delay = float(
|
|
72
|
+
os.getenv("PHONE_AGENT_HOME_DELAY", self.default_home_delay)
|
|
73
|
+
)
|
|
74
|
+
self.default_launch_delay = float(
|
|
75
|
+
os.getenv("PHONE_AGENT_LAUNCH_DELAY", self.default_launch_delay)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class ConnectionTimingConfig:
|
|
81
|
+
"""Configuration for ADB connection timing delays."""
|
|
82
|
+
|
|
83
|
+
# ADB server and connection delays (in seconds)
|
|
84
|
+
adb_restart_delay: float = 2.0 # Wait time after enabling TCP/IP mode
|
|
85
|
+
server_restart_delay: float = (
|
|
86
|
+
1.0 # Wait time between killing and starting ADB server
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def __post_init__(self):
|
|
90
|
+
"""Load values from environment variables if present."""
|
|
91
|
+
self.adb_restart_delay = float(
|
|
92
|
+
os.getenv("PHONE_AGENT_ADB_RESTART_DELAY", self.adb_restart_delay)
|
|
93
|
+
)
|
|
94
|
+
self.server_restart_delay = float(
|
|
95
|
+
os.getenv("PHONE_AGENT_SERVER_RESTART_DELAY", self.server_restart_delay)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class TimingConfig:
|
|
101
|
+
"""Master timing configuration combining all timing settings."""
|
|
102
|
+
|
|
103
|
+
action: ActionTimingConfig
|
|
104
|
+
device: DeviceTimingConfig
|
|
105
|
+
connection: ConnectionTimingConfig
|
|
106
|
+
|
|
107
|
+
def __init__(self):
|
|
108
|
+
"""Initialize all timing configurations."""
|
|
109
|
+
self.action = ActionTimingConfig()
|
|
110
|
+
self.device = DeviceTimingConfig()
|
|
111
|
+
self.connection = ConnectionTimingConfig()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# Global timing configuration instance
|
|
115
|
+
# Users can modify these values at runtime or through environment variables
|
|
116
|
+
TIMING_CONFIG = TimingConfig()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_timing_config() -> TimingConfig:
|
|
120
|
+
"""
|
|
121
|
+
Get the global timing configuration.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
The global TimingConfig instance.
|
|
125
|
+
"""
|
|
126
|
+
return TIMING_CONFIG
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def update_timing_config(
|
|
130
|
+
action: ActionTimingConfig | None = None,
|
|
131
|
+
device: DeviceTimingConfig | None = None,
|
|
132
|
+
connection: ConnectionTimingConfig | None = None,
|
|
133
|
+
) -> None:
|
|
134
|
+
"""
|
|
135
|
+
Update the global timing configuration.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
action: New action timing configuration.
|
|
139
|
+
device: New device timing configuration.
|
|
140
|
+
connection: New connection timing configuration.
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
>>> from phone_agent.config.timing import update_timing_config, ActionTimingConfig
|
|
144
|
+
>>> custom_action = ActionTimingConfig(
|
|
145
|
+
... keyboard_switch_delay=0.5,
|
|
146
|
+
... text_input_delay=0.5
|
|
147
|
+
... )
|
|
148
|
+
>>> update_timing_config(action=custom_action)
|
|
149
|
+
"""
|
|
150
|
+
global TIMING_CONFIG
|
|
151
|
+
if action is not None:
|
|
152
|
+
TIMING_CONFIG.action = action
|
|
153
|
+
if device is not None:
|
|
154
|
+
TIMING_CONFIG.device = device
|
|
155
|
+
if connection is not None:
|
|
156
|
+
TIMING_CONFIG.connection = connection
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
__all__ = [
|
|
160
|
+
"ActionTimingConfig",
|
|
161
|
+
"DeviceTimingConfig",
|
|
162
|
+
"ConnectionTimingConfig",
|
|
163
|
+
"TimingConfig",
|
|
164
|
+
"TIMING_CONFIG",
|
|
165
|
+
"get_timing_config",
|
|
166
|
+
"update_timing_config",
|
|
167
|
+
]
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Device factory for selecting ADB or HDC based on device type."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DeviceType(Enum):
|
|
7
|
+
"""Type of device connection tool."""
|
|
8
|
+
|
|
9
|
+
ADB = "adb"
|
|
10
|
+
HDC = "hdc"
|
|
11
|
+
IOS = "ios"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DeviceFactory:
|
|
15
|
+
"""
|
|
16
|
+
Factory class for getting device-specific implementations.
|
|
17
|
+
|
|
18
|
+
This allows the system to work with both Android (ADB) and HarmonyOS (HDC) devices.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, device_type: DeviceType = DeviceType.ADB):
|
|
22
|
+
"""
|
|
23
|
+
Initialize the device factory.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
device_type: The type of device to use (ADB or HDC).
|
|
27
|
+
"""
|
|
28
|
+
self.device_type = device_type
|
|
29
|
+
self._module = None
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def module(self):
|
|
33
|
+
"""Get the appropriate device module (adb or hdc)."""
|
|
34
|
+
if self._module is None:
|
|
35
|
+
if self.device_type == DeviceType.ADB:
|
|
36
|
+
from phone_agent import adb
|
|
37
|
+
|
|
38
|
+
self._module = adb
|
|
39
|
+
elif self.device_type == DeviceType.HDC:
|
|
40
|
+
from phone_agent import hdc
|
|
41
|
+
|
|
42
|
+
self._module = hdc
|
|
43
|
+
else:
|
|
44
|
+
raise ValueError(f"Unknown device type: {self.device_type}")
|
|
45
|
+
return self._module
|
|
46
|
+
|
|
47
|
+
def get_screenshot(self, device_id: str | None = None, timeout: int = 10):
|
|
48
|
+
"""Get screenshot from device."""
|
|
49
|
+
return self.module.get_screenshot(device_id, timeout)
|
|
50
|
+
|
|
51
|
+
def get_current_app(self, device_id: str | None = None) -> str:
|
|
52
|
+
"""Get current app name."""
|
|
53
|
+
return self.module.get_current_app(device_id)
|
|
54
|
+
|
|
55
|
+
def tap(
|
|
56
|
+
self, x: int, y: int, device_id: str | None = None, delay: float | None = None
|
|
57
|
+
):
|
|
58
|
+
"""Tap at coordinates."""
|
|
59
|
+
return self.module.tap(x, y, device_id, delay)
|
|
60
|
+
|
|
61
|
+
def double_tap(
|
|
62
|
+
self, x: int, y: int, device_id: str | None = None, delay: float | None = None
|
|
63
|
+
):
|
|
64
|
+
"""Double tap at coordinates."""
|
|
65
|
+
return self.module.double_tap(x, y, device_id, delay)
|
|
66
|
+
|
|
67
|
+
def long_press(
|
|
68
|
+
self,
|
|
69
|
+
x: int,
|
|
70
|
+
y: int,
|
|
71
|
+
duration_ms: int = 3000,
|
|
72
|
+
device_id: str | None = None,
|
|
73
|
+
delay: float | None = None,
|
|
74
|
+
):
|
|
75
|
+
"""Long press at coordinates."""
|
|
76
|
+
return self.module.long_press(x, y, duration_ms, device_id, delay)
|
|
77
|
+
|
|
78
|
+
def swipe(
|
|
79
|
+
self,
|
|
80
|
+
start_x: int,
|
|
81
|
+
start_y: int,
|
|
82
|
+
end_x: int,
|
|
83
|
+
end_y: int,
|
|
84
|
+
duration_ms: int | None = None,
|
|
85
|
+
device_id: str | None = None,
|
|
86
|
+
delay: float | None = None,
|
|
87
|
+
):
|
|
88
|
+
"""Swipe from start to end."""
|
|
89
|
+
return self.module.swipe(
|
|
90
|
+
start_x, start_y, end_x, end_y, duration_ms, device_id, delay
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def back(self, device_id: str | None = None, delay: float | None = None):
|
|
94
|
+
"""Press back button."""
|
|
95
|
+
return self.module.back(device_id, delay)
|
|
96
|
+
|
|
97
|
+
def home(self, device_id: str | None = None, delay: float | None = None):
|
|
98
|
+
"""Press home button."""
|
|
99
|
+
return self.module.home(device_id, delay)
|
|
100
|
+
|
|
101
|
+
def launch_app(
|
|
102
|
+
self, app_name: str, device_id: str | None = None, delay: float | None = None
|
|
103
|
+
) -> bool:
|
|
104
|
+
"""Launch an app."""
|
|
105
|
+
return self.module.launch_app(app_name, device_id, delay)
|
|
106
|
+
|
|
107
|
+
def type_text(self, text: str, device_id: str | None = None):
|
|
108
|
+
"""Type text."""
|
|
109
|
+
return self.module.type_text(text, device_id)
|
|
110
|
+
|
|
111
|
+
def clear_text(self, device_id: str | None = None):
|
|
112
|
+
"""Clear text."""
|
|
113
|
+
return self.module.clear_text(device_id)
|
|
114
|
+
|
|
115
|
+
def detect_and_set_adb_keyboard(self, device_id: str | None = None) -> str:
|
|
116
|
+
"""Detect and set keyboard."""
|
|
117
|
+
return self.module.detect_and_set_adb_keyboard(device_id)
|
|
118
|
+
|
|
119
|
+
def restore_keyboard(self, ime: str, device_id: str | None = None):
|
|
120
|
+
"""Restore keyboard."""
|
|
121
|
+
return self.module.restore_keyboard(ime, device_id)
|
|
122
|
+
|
|
123
|
+
def list_devices(self):
|
|
124
|
+
"""List connected devices."""
|
|
125
|
+
return self.module.list_devices()
|
|
126
|
+
|
|
127
|
+
def get_connection_class(self):
|
|
128
|
+
"""Get the connection class (ADBConnection or HDCConnection)."""
|
|
129
|
+
if self.device_type == DeviceType.ADB:
|
|
130
|
+
from phone_agent.adb import ADBConnection
|
|
131
|
+
|
|
132
|
+
return ADBConnection
|
|
133
|
+
elif self.device_type == DeviceType.HDC:
|
|
134
|
+
from phone_agent.hdc import HDCConnection
|
|
135
|
+
|
|
136
|
+
return HDCConnection
|
|
137
|
+
else:
|
|
138
|
+
raise ValueError(f"Unknown device type: {self.device_type}")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# Global device factory instance
|
|
142
|
+
_device_factory: DeviceFactory | None = None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def set_device_type(device_type: DeviceType):
|
|
146
|
+
"""
|
|
147
|
+
Set the global device type.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
device_type: The device type to use (ADB or HDC).
|
|
151
|
+
"""
|
|
152
|
+
global _device_factory
|
|
153
|
+
_device_factory = DeviceFactory(device_type)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def get_device_factory() -> DeviceFactory:
|
|
157
|
+
"""
|
|
158
|
+
Get the global device factory instance.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
The device factory instance.
|
|
162
|
+
"""
|
|
163
|
+
global _device_factory
|
|
164
|
+
if _device_factory is None:
|
|
165
|
+
_device_factory = DeviceFactory(DeviceType.ADB) # Default to ADB
|
|
166
|
+
return _device_factory
|