Files
workphone-sdk/sdk/agent/skills/search.py

243 lines
8.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
通用搜索技能 - 在任何APP内执行搜索
"""
import re
import logging
import time
import sys
import os
from typing import Dict, Any, Optional, 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 SearchSkill(BaseSkill):
"""通用搜索技能"""
PACKAGE = "" # 通用技能
NAME = "搜索"
def search_in_app(self, keyword: str, app_package: str = None,
search_box_selector: Dict[str, str] = None) -> Dict[str, Any]:
"""
在指定APP内搜索
Args:
keyword: 搜索关键词
app_package: APP包名如果不在当前APP
search_box_selector: 搜索框选择器 {"type": "text", "value": "搜索"}
Returns:
搜索结果
"""
try:
# 如果指定了APP先打开
if app_package:
self.d.app_start(app_package)
self.sleep(2)
# 查找搜索框
search_clicked = False
# 使用自定义选择器
if search_box_selector:
selector_type = search_box_selector.get("type")
selector_value = search_box_selector.get("value")
if selector_type == "text":
if self.d(text=selector_value).exists(timeout=2):
self.d(text=selector_value).click()
search_clicked = True
elif selector_type == "description":
if self.d(description=selector_value).exists(timeout=2):
self.d(description=selector_value).click()
search_clicked = True
elif selector_type == "resourceId":
if self.d(resourceId=selector_value).exists(timeout=2):
self.d(resourceId=selector_value).click()
search_clicked = True
# 默认查找方式
if not search_clicked:
# 尝试多种方式
selectors = [
("text", "搜索"),
("description", "搜索"),
("textContains", "搜索"),
("resourceId", "search"),
("resourceId", "search_box"),
]
for sel_type, sel_value in selectors:
try:
if sel_type == "text":
if self.d(text=sel_value).exists(timeout=1):
self.d(text=sel_value).click()
search_clicked = True
break
elif sel_type == "description":
if self.d(description=sel_value).exists(timeout=1):
self.d(description=sel_value).click()
search_clicked = True
break
elif sel_type == "textContains":
if self.d(textContains=sel_value).exists(timeout=1):
self.d(textContains=sel_value).click()
search_clicked = True
break
elif sel_type == "resourceId":
if self.d(resourceId=sel_value).exists(timeout=1):
self.d(resourceId=sel_value).click()
search_clicked = True
break
except:
continue
# 如果还是没找到,尝试点击屏幕上方(常见搜索框位置)
if not search_clicked:
info = self.d.info
# 点击屏幕上方中间
self.d.click(info['displayWidth'] // 2, info['displayHeight'] // 8)
self.sleep(0.5)
# 输入搜索关键词
self.sleep(0.5)
self.input_text(keyword, clear=True)
self.sleep(1)
# 执行搜索
search_executed = False
# 方法1: 回车
try:
self.d.press("enter")
search_executed = True
except:
pass
# 方法2: 点击搜索按钮
if not search_executed:
if self.click_text("搜索") or self.click_text("确定") or self.click_text("Go"):
search_executed = True
self.sleep(1)
return {
"success": True,
"keyword": keyword,
"app_package": app_package or self.d.app_current().get("package"),
"message": f"已搜索: {keyword}"
}
except Exception as e:
logger.error(f"搜索失败: {e}")
return {
"success": False,
"error": str(e),
"keyword": keyword
}
def search_with_voice(self, keyword: str, app_package: str = None) -> Dict[str, Any]:
"""
使用语音输入搜索(适用于中文输入)
Args:
keyword: 搜索关键词
app_package: APP包名
Returns:
搜索结果
"""
try:
if app_package:
self.d.app_start(app_package)
self.sleep(2)
# 查找搜索框并点击
if not self.click_text("搜索"):
if not self.click_desc("搜索"):
info = self.d.info
self.d.click(info['displayWidth'] // 2, info['displayHeight'] // 8)
self.sleep(0.5)
# 使用ADB输入中文需要设备支持
# 方法1: 尝试直接输入
try:
self.input_text(keyword)
except:
# 方法2: 使用ADB shell输入
self.d.shell(f'am broadcast -a ADB_INPUT_TEXT --es msg "{keyword}"')
self.sleep(1)
# 执行搜索
self.d.press("enter")
self.sleep(1)
return {
"success": True,
"keyword": keyword,
"method": "voice_input",
"message": f"已搜索: {keyword}"
}
except Exception as e:
logger.error(f"语音搜索失败: {e}")
return {
"success": False,
"error": str(e),
"keyword": keyword
}
@staticmethod
def _parse_search_results_from_ui(xml: str, limit: int) -> List[Dict[str, Any]]:
"""从 UI 树解析 text 作为搜索结果占位"""
out = []
if not xml:
return out
skip = {"搜索", "取消", "清除", "发送", "输入"}
for i, m in enumerate(re.finditer(r'\btext="([^"]{1,150})"', xml)):
if i >= limit:
break
text = m.group(1).strip()
if text and text not in skip:
out.append({"index": i + 1, "text": text, "type": "text"})
return out
def get_search_results(self, limit: int = 10) -> Dict[str, Any]:
"""
获取搜索结果列表
Args:
limit: 结果数量限制
Returns:
搜索结果列表
"""
try:
ui_tree = self.d.dump_hierarchy()
results = self._parse_search_results_from_ui(ui_tree, limit)
return {
"success": True,
"results": results,
"total": len(results)
}
except Exception as e:
return {
"success": False,
"error": str(e),
"results": []
}