From f7f7033e575e884b255fc23efeedb66b90cbb60b Mon Sep 17 00:00:00 2001 From: shuiyu67 Date: Fri, 7 Aug 2026 11:24:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=203=20=E4=B8=AA=20Op?= =?UTF-8?q?erit=20=E5=AE=89=E5=8D=93=20API=20=E7=A4=BA=E4=BE=8B=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=20(=E5=85=A8=E9=93=BE=E8=B7=AF=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E9=80=9A=E8=BF=87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- plugins/operit_scripts/file_manager.py | 114 +++++++++++++++++++++++ plugins/operit_scripts/intent_builder.py | 76 +++++++++++++++ plugins/operit_scripts/system_info.py | 78 ++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 plugins/operit_scripts/file_manager.py create mode 100644 plugins/operit_scripts/intent_builder.py create mode 100644 plugins/operit_scripts/system_info.py diff --git a/plugins/operit_scripts/file_manager.py b/plugins/operit_scripts/file_manager.py new file mode 100644 index 0000000..9085b9f --- /dev/null +++ b/plugins/operit_scripts/file_manager.py @@ -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 diff --git a/plugins/operit_scripts/intent_builder.py b/plugins/operit_scripts/intent_builder.py new file mode 100644 index 0000000..da9bf31 --- /dev/null +++ b/plugins/operit_scripts/intent_builder.py @@ -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 diff --git a/plugins/operit_scripts/system_info.py b/plugins/operit_scripts/system_info.py new file mode 100644 index 0000000..45f1e5b --- /dev/null +++ b/plugins/operit_scripts/system_info.py @@ -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