autoglm-gui 1.0.0__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.
Files changed (41) hide show
  1. AutoGLM_GUI/api/devices.py +49 -0
  2. AutoGLM_GUI/schemas.py +16 -0
  3. AutoGLM_GUI/static/assets/{about-29B5FDM8.js → about-BOnRPlKQ.js} +1 -1
  4. AutoGLM_GUI/static/assets/chat-CGW6uMKB.js +149 -0
  5. AutoGLM_GUI/static/assets/{index-mVNV0VwM.js → index-CRFVU0eu.js} +1 -1
  6. AutoGLM_GUI/static/assets/{index-wu8Wjf12.js → index-DH-Dl4tK.js} +5 -5
  7. AutoGLM_GUI/static/assets/index-DzUQ89YC.css +1 -0
  8. AutoGLM_GUI/static/index.html +2 -2
  9. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.1.dist-info}/METADATA +3 -3
  10. autoglm_gui-1.0.1.dist-info/RECORD +73 -0
  11. phone_agent/__init__.py +3 -2
  12. phone_agent/actions/handler.py +124 -31
  13. phone_agent/actions/handler_ios.py +278 -0
  14. phone_agent/adb/connection.py +14 -5
  15. phone_agent/adb/device.py +47 -16
  16. phone_agent/agent.py +8 -8
  17. phone_agent/agent_ios.py +277 -0
  18. phone_agent/config/__init__.py +18 -0
  19. phone_agent/config/apps.py +1 -1
  20. phone_agent/config/apps_harmonyos.py +256 -0
  21. phone_agent/config/apps_ios.py +339 -0
  22. phone_agent/config/i18n.py +8 -0
  23. phone_agent/config/timing.py +167 -0
  24. phone_agent/device_factory.py +166 -0
  25. phone_agent/hdc/__init__.py +53 -0
  26. phone_agent/hdc/connection.py +384 -0
  27. phone_agent/hdc/device.py +269 -0
  28. phone_agent/hdc/input.py +145 -0
  29. phone_agent/hdc/screenshot.py +127 -0
  30. phone_agent/model/client.py +104 -4
  31. phone_agent/xctest/__init__.py +47 -0
  32. phone_agent/xctest/connection.py +379 -0
  33. phone_agent/xctest/device.py +472 -0
  34. phone_agent/xctest/input.py +311 -0
  35. phone_agent/xctest/screenshot.py +226 -0
  36. AutoGLM_GUI/static/assets/chat-DTN2oKtA.js +0 -149
  37. AutoGLM_GUI/static/assets/index-Dy550Qqg.css +0 -1
  38. autoglm_gui-1.0.0.dist-info/RECORD +0 -57
  39. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.1.dist-info}/WHEEL +0 -0
  40. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.1.dist-info}/entry_points.txt +0 -0
  41. {autoglm_gui-1.0.0.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
@@ -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