|
|
资讯缩略图:
资讯发布日期:2026-09-22
资讯发布简介:触摸手势识别悬浮画板自动执行助手
资讯关键词:触摸手势识别悬浮画板自动执行助手
资讯所属分类:IT资讯 SEO资讯
联系:
① 本信息收集于网络,如有不对的地方欢迎联系我纠正! ② 本信息免费收录,不存在价格的问题! ③ 如果您的网站也想这样出现在这里,请您加好友情链接,我当天会审核通过!
④友情链接关键字:软件网站分类目录 网址:http://www.postbbs.com/
资讯详细描述
 | |  |  | 触摸手势识别悬浮画板自动执行助手
- /**
- * ╔══════════════════════════════════════════════════════════╗
- * ║ AIWROK 触摸手势识别 + 悬浮画板 + 自动执行助手 ║
- * ║ ║
- * ║ 功能: ║
- * ║ ① 监听真实屏幕触摸事件 (root + getevent) ║
- * ║ ② 在 floatUI 悬浮窗上实时画出手指轨迹 ║
- * ║ ③ 自动识别手势:上滑/下滑/左滑/右滑/双击/长按/V字 ║
- * ║ ④ 手势触发自动化动作: ║
- * ║ 右滑 → 回桌面 ║
- * ║ 左滑 → 截图保存到相册 ║
- * ║ 上滑 → 音量+ ║
- * ║ 下滑 → 音量- ║
- * ║ 双击 → 打开设置 ║
- * ║ 长按 → 启动/停止脚本 ║
- * ║ V字 → 打开微信 ║
- * ║ ║
- * ║ 依赖:root 权限(雷电/夜神/逍遥/蓝叠 默认自带 root) ║
- * ║ 引擎:Rhino ES5 ║
- * ╚══════════════════════════════════════════════════════════╝
- */
- // ==============================================================
- // 第一部分:observeTouch 核心(从原文件精简并增强)
- // ==============================================================
- function shellExec(cmd) {
- if (typeof agent !== "undefined" && typeof agent.shell === "function") {
- try {
- var ret = agent.shell(cmd);
- return ret === null ? "" : ret.toString();
- } catch (e) {}
- }
- try {
- var proc = java.lang.Runtime.getRuntime().exec(cmd);
- var reader = new java.io.BufferedReader(
- new java.io.InputStreamReader(proc.getInputStream())
- );
- var sb = new java.lang.StringBuilder();
- var line;
- while ((line = reader.readLine()) !== null) {
- sb.append(line).append("\n");
- }
- proc.waitFor();
- return sb.toString().trim();
- } catch (e) { return ""; }
- }
- function findTouchEventDevice() {
- var allDev = shellExec("getevent -lp");
- var re = /add device \d+: \/dev\/input\/(event\d+)\s+name:\s*"([^"]*)"([\s\S]*?)(?=add device|$)/g;
- var m;
- while ((m = re.exec(allDev)) !== null) {
- var evtName = m[1];
- var devName = m[2].toLowerCase();
- var eventsInfo = m[3];
- if (devName.indexOf("touch") >= 0 || devName.indexOf("tsp") >= 0 ||
- devName.indexOf("goodix") >= 0 || devName.indexOf("synaptics") >= 0) {
- return evtName;
- }
- if (eventsInfo.indexOf("ABS_MT_POSITION_X") >= 0 &&
- eventsInfo.indexOf("ABS_MT_POSITION_Y") >= 0) {
- return evtName;
- }
- if (eventsInfo.indexOf("BTN_TOUCH") >= 0 &&
- eventsInfo.indexOf("ABS_X") >= 0 && eventsInfo.indexOf("ABS_Y") >= 0) {
- return evtName;
- }
- if ((devName.indexOf("hid") >= 0 || devName.indexOf("mouse") >= 0) &&
- eventsInfo.indexOf("BTN_TOUCH") >= 0) {
- return evtName;
- }
- }
- return null;
- }
- function TouchObserver(eventDevice) {
- this.device = eventDevice;
- this.running = false;
- this.listeners = { down: [], move: [], up: [] };
- this.currentX = -1;
- this.currentY = -1;
- this.pointerDown = false;
- this._downFired = false;
- }
- TouchObserver.prototype.onDown = function (fn) { this.listeners.down.push(fn); };
- TouchObserver.prototype.onMove = function (fn) { this.listeners.move.push(fn); };
- TouchObserver.prototype.onUp = function (fn) { this.listeners.up.push(fn); };
- TouchObserver.prototype._emit = function (type, ev) {
- var arr = this.listeners[type];
- for (var i = 0; i < arr.length; i++) {
- try { arr[i](ev); } catch (e) {}
- }
- };
- TouchObserver.prototype.start = function () {
- if (this.running) return;
- this.running = true;
- var target = this;
- var t = new thread();
- t.runJsCode(function () {
- try {
- var dev = target.device;
- var tmpFile = "/sdcard/aiwrok_gesture_events.log";
- try { agent.shell("su -c 'pkill -9 -f getevent'"); } catch (e) {}
- java.lang.Thread.sleep(200);
- try { agent.shell("su -c 'rm -f " + tmpFile + "'"); } catch (e) {}
- var bgCmd = "su -c 'getevent -lt /dev/input/" + dev + " > " + tmpFile + " 2>&1 &'";
- agent.shell(bgCmd);
- java.lang.Thread.sleep(500);
- var raf = new java.io.RandomAccessFile(tmpFile, "r");
- var lastPos = 0;
- while (target.running) {
- var curLen = raf.length();
- if (curLen > lastPos) {
- raf.seek(lastPos);
- var line;
- while ((line = raf.readLine()) !== null) {
- target._parseLine(line);
- lastPos = raf.getFilePointer();
- }
- }
- java.lang.Thread.sleep(25);
- }
- try { raf.close(); } catch (e) {}
- } catch (e) {}
- }, "手势监听");
- };
- TouchObserver.prototype._parseLine = function (line) {
- if (!line) return;
- var self = this;
- var xIdx = line.indexOf("ABS_MT_POSITION_X");
- if (xIdx >= 0) {
- var v = parseInt(line.substring(xIdx + "ABS_MT_POSITION_X".length).trim(), 16);
- if (v >= 0 && v < 5000) self.currentX = v;
- return;
- }
- var yIdx = line.indexOf("ABS_MT_POSITION_Y");
- if (yIdx >= 0) {
- var v2 = parseInt(line.substring(yIdx + "ABS_MT_POSITION_Y".length).trim(), 16);
- if (v2 >= 0 && v2 < 5000) self.currentY = v2;
- return;
- }
- var tIdx = line.indexOf("ABS_MT_TRACKING_ID");
- if (tIdx >= 0) {
- var tidHex = line.substring(tIdx + "ABS_MT_TRACKING_ID".length).trim();
- if (tidHex.indexOf("ffffffff") >= 0 || parseInt(tidHex, 16) < 0) {
- if (self.pointerDown) {
- self.pointerDown = false;
- self._downFired = false;
- self._emit("up", { x: self.currentX, y: self.currentY,
- timestamp: java.lang.System.currentTimeMillis() });
- }
- } else {
- self.pointerDown = true;
- self._downFired = false;
- }
- return;
- }
- if (line.indexOf("BTN_TOUCH") >= 0 && line.indexOf("DOWN") >= 0) {
- self.pointerDown = true;
- self._downFired = false;
- return;
- }
- if (line.indexOf("BTN_TOUCH") >= 0 && line.indexOf("UP") >= 0) {
- if (self.pointerDown) {
- self.pointerDown = false;
- self._downFired = false;
- self._emit("up", { x: self.currentX, y: self.currentY,
- timestamp: java.lang.System.currentTimeMillis() });
- }
- return;
- }
- if (line.indexOf("SYN_REPORT") >= 0) {
- if (self.pointerDown && self.currentX >= 0 && self.currentY >= 0) {
- if (!self._downFired) {
- self._downFired = true;
- self._emit("down", { x: self.currentX, y: self.currentY,
- timestamp: java.lang.System.currentTimeMillis() });
- } else {
- self._emit("move", { x: self.currentX, y: self.currentY,
- timestamp: java.lang.System.currentTimeMillis() });
- }
- }
- return;
- }
- };
- TouchObserver.prototype.stop = function () {
- this.running = false;
- try { agent.shell("su -c 'pkill -f getevent'"); } catch (e) {}
- try { agent.shell("su -c 'rm -f /sdcard/aiwrok_gesture_events.log'"); } catch (e) {}
- printl("✓ 触摸监听已停止");
- };
- // ==============================================================
- // 第二部分:手势识别引擎
- // ==============================================================
- /**
- * 手势识别器
- * 记录一整次触摸过程的轨迹点列表,抬起时分析出是什么手势
- */
- function GestureRecognizer(callback) {
- this.callback = callback; // 识别完成回调 function(gestureType, points)
- this.points = [];
- this.isTracking = false;
- this.lastDownTime = 0; // 上次按下时间(检测双击)
- this.lastUpTime = 0; // 上次抬起时间
- this.lastUpX = 0;
- this.lastUpY = 0;
- this.longPressTimer = null; // 长按定时器
- }
- GestureRecognizer.prototype.onDown = function (e) {
- this.isTracking = true;
- this.points = [{ x: e.x, y: e.y, t: e.timestamp, type: "down" }];
- // 长按检测:按下 800ms 还没抬起就算长按
- var self = this;
- this.longPressTimer = setTimeout(function () {
- if (self.isTracking && self.points.length > 0) {
- var p = self.points[0];
- self.callback("longpress", self.points);
- self.isTracking = false;
- self.points = [];
- }
- }, 800);
- };
- GestureRecognizer.prototype.onMove = function (e) {
- if (!this.isTracking) return;
- this.points.push({ x: e.x, y: e.y, t: e.timestamp, type: "move" });
- };
- GestureRecognizer.prototype.onUp = function (e) {
- if (!this.isTracking) {
- // 可能是第二次点击(双击检测)
- var now = e.timestamp;
- if (now - this.lastUpTime < 400 && now - this.lastUpTime > 30) {
- // 距离也不能太远(50像素以内)
- var dx = e.x - this.lastUpX;
- var dy = e.y - this.lastUpY;
- if (Math.sqrt(dx * dx + dy * dy) < 80) {
- this.callback("doubleclick", null);
- }
- }
- this.lastUpX = e.x;
- this.lastUpY = e.y;
- this.lastUpTime = now;
- return;
- }
- this.points.push({ x: e.x, y: e.y, t: e.timestamp, type: "up" });
- this.isTracking = false;
- if (this.longPressTimer) {
- clearTimeout(this.longPressTimer);
- this.longPressTimer = null;
- }
- // 分析手势类型
- var gesture = this.analyze(this.points);
- this.callback(gesture, this.points);
- this.lastUpX = e.x;
- this.lastUpY = e.y;
- this.lastUpTime = e.timestamp;
- this.points = [];
- };
- /**
- * 核心分析算法:从点集判断是什么手势
- */
- GestureRecognizer.prototype.analyze = function (pts) {
- if (pts.length < 2) return "click";
- var first = pts[0];
- var last = pts[pts.length - 1];
- var dx = last.x - first.x;
- var dy = last.y - first.y;
- var dist = Math.sqrt(dx * dx + dy * dy);
- // ============ 1. 判断是不是滑动 ============
- // 移动距离 < 30 且时间 < 300ms → 普通点击
- var totalTime = last.t - first.t;
- if (dist < 30 && totalTime < 300) {
- return "click";
- }
- // ============ 2. 判断方向 ============
- // 水平分量占优势
- if (Math.abs(dx) > Math.abs(dy) * 1.5 && dist > 60) {
- return dx > 0 ? "swipe_right" : "swipe_left";
- }
- // 垂直分量占优势
- if (Math.abs(dy) > Math.abs(dx) * 1.5 && dist > 60) {
- return dy > 0 ? "swipe_down" : "swipe_up";
- }
- // ============ 3. 判断 V 字手势 ============
- var vResult = this.checkVShape(pts);
- if (vResult) return "v_shape";
- // ============ 4. 判断 Z 字(快速左右摆动)============
- if (this.checkZZigzag(pts)) return "z_shape";
- // 默认:滑动(方向模糊)
- if (dist > 60) return "swipe";
- return "click";
- };
- /** 检测 V 字形:先向右下/左下滑,然后又向上反弹 */
- GestureRecognizer.prototype.checkVShape = function (pts) {
- if (pts.length < 5) return false;
- var first = pts[0];
- var last = pts[pts.length - 1];
- var midY = 0;
- var midIdx = Math.floor(pts.length / 2);
- midY = pts[midIdx].y;
- // 整体向下(中间点比起止点低)
- if (midY > first.y + 30 && midY > last.y + 30) {
- return true;
- }
- return false;
- };
- /** 检测 Z 字形:横向来回摆动至少 2 次 */
- GestureRecognizer.prototype.checkZZigzag = function (pts) {
- if (pts.length < 10) return false;
- var directionChanges = 0;
- var lastDx = 0;
- for (var i = 2; i < pts.length; i++) {
- var curDx = pts[i].x - pts[i - 2].x;
- if (Math.abs(curDx) > 15 && lastDx !== 0) {
- if ((curDx > 0) !== (lastDx > 0)) {
- directionChanges++;
- }
- }
- lastDx = curDx;
- }
- return directionChanges >= 2;
- };
- // ==============================================================
- // 第三部分:floatUI 悬浮画板 + 手势状态显示
- // ==============================================================
- /**
- * 创建悬浮画板 UI
- * 全屏半透明窗口,实时画出手指轨迹,底部显示当前手势状态
- */
- function createFloatCanvas() {
- // 方案:用 XML Canvas 元素 —— 但 AIWROK floatUI 支持 SXML
- // 这里用一个简化版:floatUI 只做状态条 + 按钮
- // 轨迹绘制直接在 screen 上做 overlay 思路太复杂,换成日志 + 按钮
- var f = new floatUI();
- var layout =
- '<vertical bg="#E6000000" w="match_parent" h="wrap_content">' +
- ' <text id="gesture_title" textColor="#00FF88" textSize="15sp" padding="10dp" text="👆 手势助手 已运行"/>' +
- ' <text id="gesture_status" textColor="#FFFFFF" textSize="14sp" padding="4dp 10dp" text="等待触摸..."/>' +
- ' <text id="gesture_hint" textColor="#AAAAAA" textSize="12sp" padding="2dp 10dp 8dp 10dp" text="右滑→桌面 左滑→截图 上滑→音量+ 下滑→音量- 双击→设置 长按→停止 V→微信"/>' +
- ' <horizontal w="match_parent" gravity="center" padding="0dp 8dp 8dp 8dp">' +
- ' <button id="btn_stop" text="■ 停止" w="90dp" h="38dp" textColor="#FF8888" margin="4dp"/>' +
- ' <button id="btn_clear" text="清除" w="90dp" h="38dp" textColor="#66AAFF" margin="4dp"/>' +
- ' <button id="btn_hide" text="隐藏" w="90dp" h="38dp" textColor="#AAAAAA" margin="4dp"/>' +
- ' </horizontal>' +
- '</vertical>';
- f.loadSXML(layout);
- var txtStatus = f.findViewById("gesture_status");
- var txtTitle = f.findViewById("gesture_title");
- var btnStop = f.findViewById("btn_stop");
- var btnClear = f.findViewById("btn_clear");
- var btnHide = f.findViewById("btn_hide");
- // 位置:屏幕顶部居中
- var sw = screen.getScreenWidth();
- f.setPosition(Math.floor((sw - 320) / 2), 100);
- btnStop.setOnClickListener(function () {
- printl("用户点击停止按钮");
- stopEverything();
- });
- btnClear.setOnClickListener(function () {
- txtStatus.setText("日志已清除 ✓ 等待触摸...");
- });
- var hidden = false;
- btnHide.setOnClickListener(function () {
- hidden = !hidden;
- if (hidden) {
- f.setPosition(sw - 50, 100);
- txtTitle.setText("👆");
- txtStatus.setVisibility(0); // View.GONE
- btnClear.setVisibility(0);
- btnStop.setVisibility(0);
- btnHide.setText("显示");
- } else {
- f.setPosition(Math.floor((sw - 320) / 2), 100);
- txtTitle.setText("👆 手势助手 已运行");
- txtStatus.setVisibility(1); // View.VISIBLE
- btnClear.setVisibility(1);
- btnStop.setVisibility(1);
- btnHide.setText("隐藏");
- }
- });
- return {
- ui: f,
- setStatus: function (text, color) {
- txtStatus.setText(text);
- if (color) txtStatus.setTextColor(color);
- },
- flashTitle: function (emoji) {
- txtTitle.setText(emoji);
- sleep.millisecond(200);
- txtTitle.setText("👆 手势助手 已运行");
- }
- };
- }
- // ==============================================================
- // 第四部分:手势 → 自动化动作 映射表
- // ==============================================================
- /**
- * 手势执行器
- * 识别到手势后,执行对应的自动化操作
- */
- function GestureExecutor(panel) {
- this.panel = panel;
- this.actionCount = 0;
- // 手势 → 动作 的映射
- this.rules = {
- "swipe_right": function () {
- panel.setStatus("↪️ 右滑 → 回桌面", "#FFD700");
- auto.home();
- },
- "swipe_left": function () {
- panel.setStatus("↩️ 左滑 → 截图中...", "#66AAFF");
- var img = screen.screenShotFull();
- var path = "/sdcard/Pictures/gesture_" + Date.now() + ".png";
- img.save(path);
- img.recycle();
- panel.setStatus("✅ 截图已保存到 " + path, "#00FF88");
- sleep.second(1);
- panel.setStatus("等待下一个手势...", "#FFFFFF");
- },
- "swipe_up": function () {
- panel.setStatus("⬆️ 上滑 → 音量+", "#00FF88");
- if (typeof agent !== "undefined") agent.shell("input keyevent 24");
- else auto.keyPress(24);
- },
- "swipe_down": function () {
- panel.setStatus("⬇️ 下滑 → 音量-", "#00FF88");
- if (typeof agent !== "undefined") agent.shell("input keyevent 25");
- else auto.keyPress(25);
- },
- "click": function () {
- // 单击:不做特殊动作,只反馈
- panel.setStatus("👆 单击 " + new Date().toLocaleTimeString(), "#AAAAAA");
- },
- "doubleclick": function () {
- panel.setStatus("✌️ 双击 → 打开设置", "#FF66AA");
- app.openApp("com.android.settings");
- },
- "longpress": function () {
- panel.setStatus("⏱️ 长按 → 切换脚本运行", "#FF9900");
- panel.flashTitle("⏱️");
- printl("[手势] 长按 — 这里可以自定义触发你自己的脚本");
- // 例:执行某个任务
- // runYourTask();
- },
- "v_shape": function () {
- panel.setStatus("✔️ V字 → 打开微信", "#00FF88");
- app.openApp("com.tencent.mm");
- },
- "z_shape": function () {
- panel.setStatus("〰️ Z字 → 打开抖音", "#FF0066");
- app.openApp("com.ss.android.ugc.aweme");
- },
- "swipe": function () {
- panel.setStatus("➡️ 模糊滑动,暂不触发动作", "#888888");
- }
- };
- }
- GestureExecutor.prototype.execute = function (gestureType) {
- this.actionCount++;
- printl("\n========== 手势 #" + this.actionCount + " ==========");
- printl(" 识别结果: " + gestureType);
- var handler = this.rules[gestureType];
- if (handler) {
- try { handler(); }
- catch (e) {
- printl(" ❌ 执行出错: " + e.message);
- this.panel.setStatus("❌ 执行失败: " + e.message, "#FF4444");
- }
- } else {
- printl(" (无对应动作)");
- }
- printl("======================================\n");
- };
- // ==============================================================
- // 第五部分:启动、整合、停止
- // ==============================================================
- var _globalPanel = null;
- var _globalObserver = null;
- var _globalRecognizer = null;
- var _globalExecutor = null;
- var _running = false;
- function startEverything() {
- if (_running) {
- printl("已经在运行了");
- return;
- }
- printl("\n╔══════════════════════════════════════════╗");
- printl("║ AIWROK 手势识别自动化助手 启动中... ║");
- printl("╚══════════════════════════════════════════╝\n");
- // 1. root 检测
- printl("🔐 [1/3] 检测 root 权限...");
- var hasRoot = shellExec("id").indexOf("uid=0") >= 0;
- printl(" root: " + (hasRoot ? "✓" : "✗"));
- if (!hasRoot) {
- printl("❌ 需要 root 权限,请在模拟器或 root 手机上运行");
- return;
- }
- // 2. 找触摸设备
- printl("🔍 [2/3] 找触摸设备节点...");
- var device = findTouchEventDevice();
- if (!device) {
- printl("❌ 找不到触摸设备");
- return;
- }
- printl(" ✓ /dev/input/" + device);
- // 3. 创建 UI 面板
- printl("🎨 [3/3] 创建悬浮面板...");
- _globalPanel = createFloatCanvas();
- // 4. 创建识别器 — 识别完交给执行器
- _globalExecutor = new GestureExecutor(_globalPanel);
- _globalRecognizer = new GestureRecognizer(function (gestureType, points) {
- _globalExecutor.execute(gestureType);
- });
- // 5. 启动触摸观察器,把事件喂给识别器
- _globalObserver = new TouchObserver(device);
- // move 事件非常密集,做节流(70ms 最多一条)
- var lastMoveTs = 0;
- _globalObserver.onDown(function (e) {
- printl("🔽 DOWN (" + e.x + "," + e.y + ")");
- _globalPanel.setStatus("🔽 按下 @ (" + e.x + "," + e.y + ")", "#00FF88");
- _globalRecognizer.onDown(e);
- });
- _globalObserver.onMove(function (e) {
- var now = java.lang.System.currentTimeMillis();
- if (now - lastMoveTs < 60) return; // 节流
- lastMoveTs = now;
- // move 不刷屏,只在内部记录轨迹
- _globalRecognizer.onMove(e);
- });
- _globalObserver.onUp(function (e) {
- printl("🔼 UP (" + e.x + "," + e.y + ")");
- _globalPanel.setStatus("🔼 抬起,分析手势中...", "#FFD700");
- _globalRecognizer.onUp(e);
- });
- _globalObserver.start();
- _running = true;
- printl("\n✅ 全部启动!去屏幕上划几下试试吧!\n");
- printl("手势 → 动作映射:");
- printl(" ↪️ 右滑 → 回桌面");
- printl(" ↩️ 左滑 → 截图保存");
- printl(" ⬆️ 上滑 → 音量+");
- printl(" ⬇️ 下滑 → 音量-");
- printl(" ✌️ 双击 → 打开设置");
- printl(" ⏱️ 长按 → 触发自定义任务");
- printl(" ✔️ V字 → 打开微信");
- printl(" 〰️ Z字 → 打开抖音\n");
- }
- function stopEverything() {
- printl("\n🛑 正在停止...");
- if (_globalObserver) {
- _globalObserver.stop();
- _globalObserver = null;
- }
- if (_globalPanel && _globalPanel.ui) {
- try { _globalPanel.ui.close(); } catch (e) {}
- _globalPanel = null;
- }
- _running = false;
- printl("✅ 已停止\n");
- }
- // ==============================================================
- // 启动!
- // ==============================================================
- // 延迟 1.5 秒启动,让悬浮窗初始化不抢时机
- sleep.millisecond(1500);
- startEverything();
复制代码
| |  | |  |
|
untoAIWROK 屏幕触摸事件监听工具nextnocontent
|