feat: 新增 3 个 Operit 安卓 API 示例脚本 (全链路验证通过)

- system_info.py: Environment 存储目录 + Build 构建信息 + Log 日志 + platform (转译层核心 API)
- file_manager.py: Environment 外部存储 + 列目录/写文本/读文本 (真实文件系统操作)
- intent_builder.py: Intent ACTION_VIEW/ACTION_SEND + Uri 解析 + putExtra (组件通信)
- 全链路验证: list 识别 4 脚本 7 工具; run 全部 success (含 Log I/W 输出)
- 配套修复 android_pyapi (Intent.EXTRA_TEXT 挂载, 已推送 Gitee e2f78f3)
This commit is contained in:
shuiyu67 committed 2026-08-07 11:24:34 +08:00
1 parent a71daf32c6
commit f7f7033e57
3 files changed
+268

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
# METADATA
# {
# "name": "file_manager",
# "display_name": {
# "zh": "Android 文件管理器",
# "en": "Android File Manager"
# },
# "description": {
# "zh": "通过 Environment 获取外部存储目录,支持列目录、读文本、写文本(真实文件系统操作)。",
# "en": "Use Environment to get external storage dir; list dir, read/write text files on real filesystem."
# },
# "category": "Files",
# "tools": [
# {
# "name": "list_dir",
# "description": {
# "zh": "列出目录内容",
# "en": "List directory contents"
# },
# "parameters": [
# { "name": "path", "description": { "zh": "目录路径(默认外部存储根)", "en": "Dir path (default external storage root)" }, "type": "string", "required": false }
# ]
# },
# {
# "name": "write_text",
# "description": {
# "zh": "写入文本文件",
# "en": "Write text file"
# },
# "parameters": [
# { "name": "file_path", "description": { "zh": "文件绝对路径", "en": "Absolute file path" }, "type": "string", "required": true },
# { "name": "content", "description": { "zh": "文本内容", "en": "Text content" }, "type": "string", "required": true }
# ]
# },
# {
# "name": "read_text",
# "description": {
# "zh": "读取文本文件",
# "en": "Read text file"
# },
# "parameters": [
# { "name": "file_path", "description": { "zh": "文件绝对路径", "en": "Absolute file path" }, "type": "string", "required": true }
# ]
# }
# ]
# }
TAG = "FileManager"
def _resolve(path):
"""解析路径:默认落到外部存储根"""
if path and path.strip():
return os.path.abspath(path.strip())
return Environment.getExternalStorageDirectory()
async def list_dir(params):
path = _resolve((params or {}).get("path", ""))
if not os.path.isdir(path):
raise Exception(f"目录不存在: {path}")
entries = []
for name in sorted(os.listdir(path)):
full = os.path.join(path, name)
entries.append({
"name": name,
"type": "dir" if os.path.isdir(full) else "file",
"size": os.path.getsize(full) if os.path.isfile(full) else None,
})
Log.i(TAG, f"列出 {path}: {len(entries)} 项")
result = {"path": path, "count": len(entries), "entries": entries[:100]}
complete({"success": True, "message": f"已列出 {len(entries)} 项", "data": result})
return result
async def write_text(params):
file_path = os.path.abspath((params or {}).get("file_path", ""))
content = (params or {}).get("content", "")
if not file_path:
raise Exception("file_path 不能为空")
os.makedirs(os.path.dirname(file_path) or ".", exist_ok=True)
with open(file_path, "w", encoding="utf-8") as fh:
fh.write(content)
size = os.path.getsize(file_path)
Log.w(TAG, f"已写入 {file_path} ({size} 字节)")
result = {"file_path": file_path, "bytes": size}
complete({"success": True, "message": f"写入成功 ({size} 字节)", "data": result})
return result
async def read_text(params):
file_path = os.path.abspath((params or {}).get("file_path", ""))
if not file_path:
raise Exception("file_path 不能为空")
if not os.path.isfile(file_path):
raise Exception(f"文件不存在: {file_path}")
with open(file_path, "r", encoding="utf-8", errors="replace") as fh:
content = fh.read()
result = {"file_path": file_path, "bytes": len(content.encode("utf-8")), "content": content[:20000]}
complete({"success": True, "message": f"读取成功 ({len(content)} 字符)", "data": result})
return result
async def _dispatch(params):
action = (params or {}).get("action", "list_dir")
func = {"list_dir": list_dir, "write_text": write_text, "read_text": read_text}.get(action)
if not func:
raise Exception(f"未知操作: {action}(支持 list_dir/write_text/read_text)")
return await func(params)
exports.list_dir = list_dir
exports.write_text = write_text
exports.read_text = read_text
+76
View File
@@ -0,0 +1,76 @@
# METADATA
# {
# "name": "intent_builder",
# "display_name": {
# "zh": "Intent 意图构建器",
# "en": "Intent Builder"
# },
# "description": {
# "zh": "用 android.content.Intent 构建意图:ACTION_VIEW 打开 URL、ACTION_SEND 分享文本,解析 Uri,并用 Log 输出。",
# "en": "Build intents with android.content.Intent: ACTION_VIEW for URLs, ACTION_SEND for sharing text, parse Uri, log via Log."
# },
# "category": "System",
# "tools": [
# {
# "name": "build_view_intent",
# "description": {
# "zh": "构建 ACTION_VIEW 意图(打开 URL)",
# "en": "Build ACTION_VIEW intent (open URL)"
# },
# "parameters": [
# { "name": "url", "description": { "zh": "要打开的网址", "en": "URL to open" }, "type": "string", "required": true }
# ]
# },
# {
# "name": "build_share_intent",
# "description": {
# "zh": "构建 ACTION_SEND 分享意图",
# "en": "Build ACTION_SEND share intent"
# },
# "parameters": [
# { "name": "text", "description": { "zh": "分享的文本", "en": "Text to share" }, "type": "string", "required": true }
# ]
# }
# ]
# }
TAG = "IntentBuilder"
async def build_view_intent(params):
url = (params or {}).get("url", "")
if not url:
raise Exception("url 不能为空")
intent = Intent(Intent.ACTION_VIEW)
intent.setData(Uri.parse(url))
Log.i(TAG, f"ACTION_VIEW -> {url}")
result = {
"action": intent.getAction(),
"data": intent.getData(),
"flags": intent.getFlags(),
"resolved": intent.toString(),
}
complete({"success": True, "message": "意图构建成功(桌面环境不实际拉起)", "data": result})
return result
async def build_share_intent(params):
text = (params or {}).get("text", "")
if not text:
raise Exception("text 不能为空")
intent = Intent(Intent.ACTION_SEND)
intent.setType("text/plain")
intent.putExtra(Intent.EXTRA_TEXT, text)
Log.i(TAG, f"ACTION_SEND: {text[:40]}")
result = {
"action": intent.getAction(),
"type": intent.getType(),
"extras": intent.getExtras(),
"resolved": intent.toString(),
}
complete({"success": True, "message": "分享意图构建成功", "data": result})
return result
exports.build_view_intent = build_view_intent
exports.build_share_intent = build_share_intent
+78
View File
@@ -0,0 +1,78 @@
# METADATA
# {
# "name": "system_info",
# "display_name": {
# "zh": "系统信息查看器",
# "en": "System Info Viewer"
# },
# "description": {
# "zh": "通过 Android API 转译层获取系统信息:存储目录、构建信息、平台环境,并用 Log 输出日志。",
# "en": "Get system info via Android API translation layer: storage dirs, build info, platform. Log via android.util.Log."
# },
# "category": "System",
# "tools": [
# {
# "name": "get_info",
# "description": {
# "zh": "获取系统信息",
# "en": "Get system info"
# },
# "parameters": []
# }
# ]
# }
import platform
import sys
TAG = "SystemInfo"
def getErrorMessage(error):
return str(error) if error else "unknown"
def getErrorStack(error):
return traceback.format_exc()
async def wrap(func, params):
try:
result = await func(params)
complete({"success": True, "message": "操作成功", "data": result})
except Exception as error:
console.error(f"Error: {getErrorMessage(error)}")
complete({"success": False, "message": getErrorMessage(error), "error_stack": getErrorStack(error)})
async def get_info(params):
"""通过 Android API 转译层采集系统信息"""
try:
info = {
"storage_external": Environment.getExternalStorageDirectory(),
"storage_data": Environment.getDataDirectory(),
"storage_root": Environment.getRootDirectory(),
"storage_public_pictures": Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"build_brand": getattr(Build, "BRAND", "unknown"),
"build_model": getattr(Build, "MODEL", "unknown"),
"build_version": getattr(Build, "VERSION", {}).get("RELEASE", "unknown") if isinstance(getattr(Build, "VERSION", {}), dict) else "unknown",
"os_name": os.name,
"sys_platform": sys.platform,
"python_version": platform.python_version(),
"python_platform": platform.platform(),
"sdcard_mounted": Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED,
}
Log.i(TAG, f"采集完成: external={info['storage_external']}")
complete({"success": True, "message": "系统信息采集完成", "data": info})
return info
except Exception as error:
console.error(f"{TAG} 错误: {getErrorMessage(error)}")
complete({"success": False, "message": f"采集失败: {getErrorMessage(error)}", "error_stack": getErrorStack(error)})
raise
async def get_info_wrapper(params):
await wrap(get_info, params)
exports.get_info = get_info_wrapper