feat: publish workphone SDK deployment and API docs
This commit is contained in:
214
sdk/agent/skills/app_manager.py
Normal file
214
sdk/agent/skills/app_manager.py
Normal file
@@ -0,0 +1,214 @@
|
||||
"""
|
||||
应用管理技能 - 打开、关闭、切换应用
|
||||
"""
|
||||
|
||||
import re
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
from typing import Dict, Any, List
|
||||
|
||||
# 兼容独立运行和包导入
|
||||
_agent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if _agent_dir not in sys.path:
|
||||
sys.path.insert(0, _agent_dir)
|
||||
|
||||
try:
|
||||
from skills.base import BaseSkill
|
||||
except ImportError:
|
||||
from ..base import BaseSkill
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AppManagerSkill(BaseSkill):
|
||||
"""应用管理技能"""
|
||||
|
||||
PACKAGE = "" # 通用技能
|
||||
NAME = "应用管理"
|
||||
|
||||
# 常用应用包名
|
||||
APP_PACKAGES = {
|
||||
"微信": "com.tencent.mm",
|
||||
"抖音": "com.ss.android.ugc.aweme",
|
||||
"支付宝": "com.eg.android.AlipayGphone",
|
||||
"淘宝": "com.taobao.taobao",
|
||||
"微博": "com.sina.weibo",
|
||||
"qq": "com.tencent.mobileqq",
|
||||
"设置": "com.android.settings",
|
||||
"相机": "com.android.camera",
|
||||
"浏览器": "com.android.chrome",
|
||||
"豆包": "com.bytedance.doubao",
|
||||
"小红书": "com.xingin.xhs",
|
||||
"bilibili": "tv.danmaku.bili",
|
||||
"知乎": "com.zhihu.android",
|
||||
}
|
||||
|
||||
def open_app(self, app_name: str) -> Dict[str, Any]:
|
||||
"""
|
||||
打开应用
|
||||
|
||||
Args:
|
||||
app_name: 应用名称(中文或包名)
|
||||
|
||||
Returns:
|
||||
执行结果
|
||||
"""
|
||||
try:
|
||||
# 查找包名
|
||||
package = self.APP_PACKAGES.get(app_name)
|
||||
if not package:
|
||||
# 尝试直接作为包名
|
||||
package = app_name
|
||||
|
||||
# 启动应用
|
||||
self.d.app_start(package)
|
||||
self.sleep(2)
|
||||
|
||||
# 验证是否启动成功
|
||||
current = self.d.app_current()
|
||||
if current.get("package") == package:
|
||||
return {
|
||||
"success": True,
|
||||
"app_name": app_name,
|
||||
"package": package,
|
||||
"message": f"已打开{app_name}"
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"应用启动失败或包名错误: {app_name}",
|
||||
"package": package
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"打开应用失败 {app_name}: {e}")
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"app_name": app_name
|
||||
}
|
||||
|
||||
def close_app(self, app_name: str = None, package: str = None) -> Dict[str, Any]:
|
||||
"""
|
||||
关闭应用
|
||||
|
||||
Args:
|
||||
app_name: 应用名称
|
||||
package: 包名(优先)
|
||||
|
||||
Returns:
|
||||
执行结果
|
||||
"""
|
||||
try:
|
||||
if package:
|
||||
target_package = package
|
||||
elif app_name:
|
||||
target_package = self.APP_PACKAGES.get(app_name, app_name)
|
||||
else:
|
||||
# 关闭当前应用
|
||||
current = self.d.app_current()
|
||||
target_package = current.get("package")
|
||||
|
||||
if target_package:
|
||||
self.d.app_stop(target_package)
|
||||
return {
|
||||
"success": True,
|
||||
"package": target_package,
|
||||
"message": "应用已关闭"
|
||||
}
|
||||
else:
|
||||
return {"success": False, "error": "未指定应用"}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"关闭应用失败: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def get_running_apps(self) -> Dict[str, Any]:
|
||||
"""获取正在运行的应用列表"""
|
||||
try:
|
||||
self.d.press("recent")
|
||||
self.sleep(1)
|
||||
ui_tree = self.d.dump_hierarchy()
|
||||
apps = self._parse_recent_tasks_from_ui(ui_tree)
|
||||
self.home()
|
||||
return {"success": True, "apps": apps}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": str(e), "apps": []}
|
||||
|
||||
@staticmethod
|
||||
def _parse_recent_tasks_from_ui(xml: str) -> List[Dict[str, Any]]:
|
||||
"""从最近任务 UI 解析应用标题占位"""
|
||||
out = []
|
||||
if not xml:
|
||||
return out
|
||||
skip = {"最近", "清除", "关闭", "全部", "清理"}
|
||||
for i, m in enumerate(re.finditer(r'\b(text|content-desc)="([^"]{1,80})"', xml)):
|
||||
if i >= 50:
|
||||
break
|
||||
text = m.group(2).strip()
|
||||
if text and text not in skip and not text.startswith("com."):
|
||||
out.append({"index": len(out) + 1, "title": text})
|
||||
return out
|
||||
|
||||
def get_installed_apps(self, limit: int = 100) -> Dict[str, Any]:
|
||||
"""获取已安装的应用列表"""
|
||||
try:
|
||||
# 通过shell命令获取
|
||||
result = self.d.shell("pm list packages -3") # 第三方应用
|
||||
packages = []
|
||||
|
||||
for line in result.output.split('\n'):
|
||||
if line.startswith('package:'):
|
||||
pkg = line.replace('package:', '').strip()
|
||||
packages.append(pkg)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"packages": packages[:limit],
|
||||
"total": len(packages)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"success": False, "error": str(e), "packages": []}
|
||||
|
||||
def switch_app(self, app_name: str) -> Dict[str, Any]:
|
||||
"""
|
||||
切换应用(如果已打开则切换,否则打开)
|
||||
|
||||
Args:
|
||||
app_name: 应用名称
|
||||
|
||||
Returns:
|
||||
执行结果
|
||||
"""
|
||||
try:
|
||||
package = self.APP_PACKAGES.get(app_name, app_name)
|
||||
|
||||
# 检查是否正在运行
|
||||
current = self.d.app_current()
|
||||
if current.get("package") == package:
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"{app_name}已是当前应用",
|
||||
"package": package
|
||||
}
|
||||
|
||||
# 打开或切换
|
||||
self.d.app_start(package)
|
||||
self.sleep(2)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"app_name": app_name,
|
||||
"package": package,
|
||||
"message": f"已切换到{app_name}"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"切换应用失败 {app_name}: {e}")
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"app_name": app_name
|
||||
}
|
||||
Reference in New Issue
Block a user