|
|
AIWROK软件双窗口悬浮日志系统
- /**
- * AIWROK 双窗口悬浮日志系统(极简版)
- * - 窗口 A:主日志面板(大,左侧),滚动显示
- * - 窗口 B:实时统计面板(小,右上),计数/耗时/状态
- * 适用:安卓 Rhino ES5 · AIWROK
- */
- importClass(java.lang.Thread);
- // ======================== 基础 ========================
- function doSleep(ms) { try { Thread.sleep(ms); } catch (e) {} }
- // ======================== 主日志窗口 A ========================
- var winA = {
- floatUI: null,
- tvLog: null,
- tvTitle: null,
- lines: [], // 用数组存所有日志行,setText 时 join 全部输出
- count: 0,
- MAX_LINES: 100, // 最多保留 100 行,超出裁掉旧的
- init: function() {
- this.floatUI = new floatUI();
- var sw = screen.getScreenWidth();
- var sh = screen.getScreenHeight();
- printl("[屏幕] " + sw + "x" + sh);
- // 窗口 A:宽度 40%,高度 22%,贴顶部 8%
- var w = Math.floor(sw * 0.40);
- var h = Math.floor(sh * 0.22);
- var x = 8;
- var y = Math.floor(sh * 0.08);
- var xml = '<LinearLayout orientation="vertical" w="' + w + '" h="' + h +
- '" background="#881e293b">' +
- ' <TextView id="title" text="[ 运行日志 ] 共 0 条" ' +
- 'textColor="#7dd3fc" textSize="13" padding="6" background="#441e40af"/>' +
- ' <ScrollView w="fill_parent" h="fill_parent">' +
- ' <TextView id="logBox" w="fill_parent" h="fill_parent" text="" ' +
- 'textColor="#f1f5f9" textSize="11" padding="6"/>' +
- ' </ScrollView>' +
- '</LinearLayout>';
- try {
- this.floatUI.loadXML(xml);
- this.floatUI.setPosition(x, y);
- this.tvTitle = this.floatUI.findViewById("title");
- this.tvLog = this.floatUI.findViewById("logBox");
- printl("[窗口A] OK pos=(" + x + "," + y + ") size=" + w + "x" + h);
- return true;
- } catch (e) {
- printl("[窗口A] 失败: " + e);
- return false;
- }
- },
- log: function(msg, color) {
- this.count++;
- var colorHex = color || "#E0E0E0";
- if (msg.length > 240) msg = msg.substring(0, 240) + "...";
- var d = new Date();
- var ts = (d.getHours() < 10 ? "0" : "") + d.getHours() + ":" +
- (d.getMinutes() < 10 ? "0" : "") + d.getMinutes() + ":" +
- (d.getSeconds() < 10 ? "0" : "") + d.getSeconds();
- // 前缀标记颜色含义(虽然文本颜色无法区分,但前缀能看懂)
- var prefix = colorHex == "#4ADE80" ? "✓ " :
- colorHex == "#F87171" ? "✗ " :
- colorHex == "#FFD166" ? "⚡ " : "• ";
- this.lines.push(ts + " " + prefix + msg);
- if (this.lines.length > this.MAX_LINES) this.lines.shift();
- try {
- this.tvLog.setText(this.lines.join("\n"));
- this.tvTitle.setText("[ 运行日志 ] 共 " + this.count + " 条");
- } catch (e) {}
- },
- destroy: function() {
- try { if (this.floatUI) { this.floatUI.close(); this.floatUI = null; } } catch (e) {}
- }
- };
- // ======================== 统计窗口 B ========================
- var winB = {
- floatUI: null,
- tvTask: null,
- tvOk: null,
- tvErr: null,
- tvCost: null,
- startTs: 0,
- totalTasks: 0,
- okCount: 0,
- errCount: 0,
- init: function() {
- this.floatUI = new floatUI();
- var sw = screen.getScreenWidth();
- var sh = screen.getScreenHeight();
- // 窗口 B:宽度 25%,高度 10%,和 A 顶部对齐
- var w = Math.floor(sw * 0.25);
- var h = Math.floor(sh * 0.10);
- var x = sw - w - 8;
- var y = Math.floor(sh * 0.08);
- var xml = '<LinearLayout orientation="vertical" w="' + w + '" h="' + h +
- '" background="#88f8fafc" padding="6">' +
- ' <TextView text="📊 实时统计" textColor="#0ea5e9" textSize="13" gravity="center"/>' +
- ' <TextView text="────────────────" w="fill_parent" textColor="#330ea5e9" textSize="8" padding="2"/>' +
- ' <TextView id="tv_task" text="任务: 0" textColor="#334155" textSize="11"/>' +
- ' <TextView id="tv_ok" text="✓ 成功: 0" textColor="#16a34a" textSize="11"/>' +
- ' <TextView id="tv_err" text="✗ 失败: 0" textColor="#dc2626" textSize="11"/>' +
- ' <TextView id="tv_cost" text="⏱ 00:00" textColor="#d97706" textSize="11"/>' +
- '</LinearLayout>';
- try {
- this.floatUI.loadXML(xml);
- this.floatUI.setPosition(x, y);
- this.tvTask = this.floatUI.findViewById("tv_task");
- this.tvOk = this.floatUI.findViewById("tv_ok");
- this.tvErr = this.floatUI.findViewById("tv_err");
- this.tvCost = this.floatUI.findViewById("tv_cost");
- this.startTs = new Date().getTime();
- printl("[窗口B] 统计面板 OK pos=(" + x + "," + y + ") size=" + w + "x" + h);
- return true;
- } catch (e) {
- printl("[窗口B] 失败: " + e);
- return false;
- }
- },
- update: function(taskName, success) {
- this.totalTasks++;
- if (success) this.okCount++; else this.errCount++;
- var elapsed = new Date().getTime() - this.startTs;
- var mm = Math.floor(elapsed / 60000);
- var ss = Math.floor((elapsed % 60000) / 1000);
- var timeStr = (mm < 10 ? "0" : "") + mm + ":" + (ss < 10 ? "0" : "") + ss;
- try {
- this.tvTask.setText("任务: " + this.totalTasks + " · " + taskName);
- this.tvOk.setText("✓ 成功: " + this.okCount);
- this.tvErr.setText("✗ 失败: " + this.errCount);
- this.tvCost.setText("⏱ " + timeStr);
- } catch (e) {}
- },
- tick: function() {
- var elapsed = new Date().getTime() - this.startTs;
- var mm = Math.floor(elapsed / 60000);
- var ss = Math.floor((elapsed % 60000) / 1000);
- var timeStr = (mm < 10 ? "0" : "") + mm + ":" + (ss < 10 ? "0" : "") + ss;
- try { this.tvCost.setText("⏱ " + timeStr); } catch (e) {}
- },
- destroy: function() {
- try { if (this.floatUI) { this.floatUI.close(); this.floatUI = null; } } catch (e) {}
- }
- };
- // ======================== 后台计时线程 ========================
- var stopTick = false;
- new thread().runJsCode(function() {
- while (!stopTick) {
- doSleep(1000);
- if (!stopTick) winB.tick();
- }
- }, "计时线程");
- // ======================== 演示(录像友好版,约 40 秒) ========================
- function runDemo() {
- printl("╔══════════════════════════════════════════════╗");
- printl("║ AIWROK 双窗口悬浮日志系统(录像演示) ║");
- printl("╚══════════════════════════════════════════════╝");
- // ── 第 1 幕:启动(给观众看清两个窗布局,停 3 秒)──
- winA.init();
- doSleep(500);
- winB.init();
- doSleep(1000);
- winA.log("🎬 演示开始,请开始录像...", "#FFD166");
- winB.update("系统启动", true);
- doSleep(2000);
- // ── 第 2 幕:跑任务(间隔 800ms,观众能看清每条)──
- var tasks = [
- ["检测设备权限", true],
- ["连接远程服务器", true],
- ["登录鉴权", true],
- ["拉取配置文件", true],
- ["OCR 图像识别", false], // 故意失败一个
- ["重试 OCR", true],
- ["解析识别结果", true],
- ["写入本地数据库", true],
- ["上传云端同步", true],
- ["校验数据完整性", false], // 再失败一个
- ["自动修复", true],
- ["推送通知到手机", true],
- ["生成运行报告", true]
- ];
- winA.log("═══════ 任务开始 ═══════", "#FFD166");
- winB.update("准备就绪", true);
- doSleep(1200);
- for (var i = 0; i < tasks.length; i++) {
- var name = tasks[i][0];
- var success = tasks[i][1];
- if (success) {
- winA.log("✓ [ " + (i + 1) + "/" + tasks.length + " ] " + name, "#4ADE80");
- } else {
- winA.log("✗ [ " + (i + 1) + "/" + tasks.length + " ] " + name + " → 重试中...", "#F87171");
- }
- winB.update(name, success);
- doSleep(800);
- // 失败后自动重试一条,展示 winB 持续更新
- if (!success) {
- winA.log(" ↳ 重试成功 ✓", "#4ADE80");
- winB.update(name + "(重试)", true);
- doSleep(800);
- }
- }
- // ── 第 3 幕:收尾展示结果(停 5 秒,观众能看清统计面板)──
- winA.log("═══════ 任务全部结束 ═══════", "#FFD166");
- winA.log("成功 " + winB.okCount + " / 失败 " + winB.errCount + " (失败已自动重试)", "#4ADE80");
- doSleep(5000);
- // ── 第 4 幕:展示日志滚动到最新、计时继续跑(停 3 秒)──
- winA.log("窗口保持开启中... 可以继续录统计面板和计时", "#CCCCCC");
- doSleep(3000);
- printl("");
- printl("╔══════════════════════════════════════════════╗");
- printl("║ 演示结束!窗口将在 5 秒后自动关闭 ║");
- printl("║ 如需更长时间录制,注释掉最后的 destroy ║");
- printl("╚══════════════════════════════════════════════╝");
- doSleep(5000);
- stopTick = true;
- winA.destroy();
- winB.destroy();
- printl("🧹 双窗口已关闭");
- }
- runDemo();
复制代码
|
|