215 lines
6.6 KiB
Python
215 lines
6.6 KiB
Python
"""
|
|
应用管理技能 - 打开、关闭、切换应用
|
|
"""
|
|
|
|
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
|
|
}
|