AIWROK软件双窗口悬浮日志系统
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] OKpos=(" + 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] 统计面板 OKpos=(" + 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;
var success = tasks;
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();
页:
[1]