Files
auto_control/core/clipboard_helper.py
T

70 lines
3.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.
"""剪贴板注入公共模块(ClipInject APK 通道)。
背景:Android 10+ 禁止后台(atx-agent/shell)写剪贴板——u2.set_clipboard
调用"成功"但内容被系统静默丢弃(Android 12/13 实测)。ClipInject
(com.example.clipinject,用户自研 APK)通过 am start 启动透明 Activity,
窗口聚焦后 setPrimaryClip;shell 身份启动 Activity 不受后台启动限制
(广播方式在 MIUI 会被 Background activity start 拦截,必须用 am start)。
工具页(web/tools_api.py)与任务执行器(tasks/generic/task.py)共用本模块。
"""
import base64
import subprocess
import time
from concurrent.futures import ThreadPoolExecutor
from config import ADB_PATH
from core.adb_helper import adb_connect
from core.logger import get_logger
_log = get_logger("core.clip")
_CLIP_PACKAGE = "com.example.clipinject"
_CLIP_ACTIVITY = "com.example.clipinject/.ClipActivity"
def inject_clipboard(serial, text, d=None):
"""向设备剪贴板注入文字,返回 (ok, msg)。
- d:可选,任务执行器已持有 u2 Device 时传入复用(跳过重复连接),
读回验证直接用它;不传则临时 u2.connect(工具页场景,带超时保护)
- IP:port 设备先 adb connect(已连接自动跳过;绝不 disconnect,红线)
- 未装 ClipInject / 读回不一致都会明确报错(不再静默失败)
"""
import uiautomator2 as u2
if ":" in serial:
try:
adb_connect(serial)
except Exception as e:
return False, f"adb 连接失败: {e}"
b64 = base64.b64encode(text.encode("utf-8")).decode()
# 1. am start 透明 Activity 写剪贴板(shell 身份,不受后台启动限制)
try:
r = subprocess.run(
[ADB_PATH, "-s", serial, "shell", "am", "start",
"-n", _CLIP_ACTIVITY, "--es", "text_b64", b64],
capture_output=True, timeout=30)
out = ((r.stdout or b"") + (r.stderr or b"")).decode(errors="replace")
if "does not exist" in out or "unable to resolve Intent" in out:
return False, f"设备未安装 ClipInject({_CLIP_PACKAGE}),请先安装"
if "Error" in out or ("Starting" not in out and r.returncode != 0):
return False, f"am start 失败: {out.strip()[:120]}"
except subprocess.TimeoutExpired:
return False, "am start 超时(adb 无响应)"
except Exception as e:
return False, f"{type(e).__name__}: {str(e)[:120]}"
# 2. 读回验证(等透明 Activity 完成写入;u2 读剪贴板在 Android 12 实测可用)
time.sleep(0.5)
try:
if d is None:
with ThreadPoolExecutor(max_workers=1) as pool:
d = pool.submit(u2.connect, serial).result(timeout=30)
with ThreadPoolExecutor(max_workers=1) as pool:
got = pool.submit(lambda: d.clipboard).result(timeout=15)
if got == text:
return True, "已注入"
return False, f"剪贴板读回不一致(写入可能被拒): {str(got)[:60]!r}"
except Exception as e:
# 读回不可用(旧版 u2/连接失败)时按写入成功处理,避免误报,但带上具体原因
return True, f"已注入(读回验证不可用: {type(e).__name__}: {str(e)[:80]})"