aardio 文档
aardio 范例: 通过 adb 实现安卓手机投屏
//aardio 通过 adb 实现安卓手机投屏
import fonts.fontAwesome;
import win.ui.simpleWindow;
import process.adb;
import wsock.udp.mdnsClient;
import win.ui;
/*DSG{{*/
var winform = win.form(text="安卓投屏助手";right=920;bottom=620;bgcolor=0xFFFFFF;border="none")
winform.add(
bkActionsTitle={cls="bkplus";text='\uF0AD 快捷操作';left=15;top=250;right=265;bottom=275;align="left";color=0x333333;dl=1;dt=1;font=LOGFONT(h=-14;name='FontAwesome';weight=600);z=15};
bkCodePanel={cls="bkplus";left=280;top=46;right=921;bottom=621;bgcolor=0xFFFFFF;db=1;dl=1;dr=1;dt=1;z=26};
bkCodeTitle={cls="bkplus";text='\uF121 自动化代码';left=295;top=60;right=500;bottom=85;align="left";color=0x333333;dl=1;dr=1;dt=1;font=LOGFONT(h=-14;name='FontAwesome';weight=600);z=27};
bkDevicePanel={cls="bkplus";left=0;top=46;right=280;bottom=621;bgcolor=0xF5F5F5;db=1;dl=1;dt=1;z=4};
bkDeviceTitle={cls="bkplus";text='\uF0C9 设备信息';left=15;top=60;right=265;bottom=85;align="left";color=0x333333;dl=1;dt=1;font=LOGFONT(h=-14;name='FontAwesome';weight=600);z=5};
bkHeader={cls="bkplus";left=0;top=0;right=921;bottom=46;bgcolor=0x0078D4;dl=1;dr=1;dt=1;z=1};
bkIpLabel={cls="bkplus";text="IP 地址:";left=15;top=210;right=90;bottom=230;align="left";color=0x666666;dl=1;dt=1;z=13};
bkModelLabel={cls="bkplus";text="设备型号:";left=15;top=135;right=90;bottom=155;align="left";color=0x666666;dl=1;dt=1;z=7};
bkRecordTitle={cls="bkplus";text='\uF111 操作录制';left=15;top=425;right=265;bottom=450;align="left";color=0x333333;dl=1;dt=1;font=LOGFONT(h=-14;name='FontAwesome';weight=600);z=23};
bkScreenLabel={cls="bkplus";text="屏幕分辨率:";left=15;top=185;right=90;bottom=205;align="left";color=0x666666;dl=1;dt=1;z=11};
bkTitle={cls="bkplus";text="安卓投屏助手";left=45;top=10;right=300;bottom=36;align="left";color=0xFFFFFF;dl=1;dt=1;font=LOGFONT(h=-18;weight=600);z=2};
bkTitleIcon={cls="bkplus";text='\uF10B';left=12;top=8;right=42;bottom=40;color=0xFFFFFF;dl=1;dt=1;font=LOGFONT(h=-24;name='FontAwesome');z=3};
bkVersionLabel={cls="bkplus";text="安卓版本:";left=15;top=160;right=90;bottom=180;align="left";color=0x666666;dl=1;dt=1;z=9};
editCode={cls="edit";left=295;top=95;right=900;bottom=600;db=1;dl=1;dr=1;dt=1;edge=1;font=LOGFONT(h=-13;name='Consolas');hscroll=1;multiline=1;vscroll=1;z=30};
lbIpValue={cls="static";text="--";left=95;top=210;right=265;bottom=230;color=0x333333;dl=1;dt=1;z=14};
lbModelValue={cls="static";text="--";left=95;top=135;right=265;bottom=155;color=0x333333;dl=1;dt=1;z=8};
lbScreenValue={cls="static";text="--";left=95;top=185;right=265;bottom=205;color=0x333333;dl=1;dt=1;z=12};
lbVersionValue={cls="static";text="--";left=95;top=160;right=265;bottom=180;color=0x333333;dl=1;dt=1;z=10};
plusBack={cls="plus";text="返回";left=100;top=375;right=175;bottom=405;bgcolor=0xE0E0E0;color=0x333333;dl=1;dt=1;iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF060';notify=1;textPadding={left=24};z=21};
plusClear={cls="plus";text="清空";left=735;top=58;right=795;bottom=83;color=0x666666;dr=1;dt=1;iconStyle={align="left";font=LOGFONT(name='FontAwesome');padding={left=5}};iconText='\uF1F8';notify=1;textPadding={left=20};z=28};
plusConnect={cls="plus";text="连接设备";left=15;top=285;right=135;bottom=320;bgcolor=0x0078D4;color=0xFFFFFF;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-14;name='FontAwesome');padding={left=12}};iconText='\uF0C1';notify=1;textPadding={left=32};z=16};
plusDisconnect={cls="plus";text="断开连接";left=145;top=285;right=265;bottom=320;bgcolor=0xE0E0E0;color=0x333333;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-14;name='FontAwesome');padding={left=12}};iconText='\uF127';notify=1;textPadding={left=32};z=17};
plusHome={cls="plus";text="主页";left=15;top=375;right=90;bottom=405;bgcolor=0xE0E0E0;color=0x333333;dl=1;dt=1;iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF015';notify=1;textPadding={left=24};z=20};
plusMenu={cls="plus";text="菜单";left=185;top=375;right=265;bottom=405;bgcolor=0xE0E0E0;color=0x333333;dl=1;dt=1;iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF0C9';notify=1;textPadding={left=24};z=22};
plusPower={cls="plus";text='\uF011';left=15;top=540;right=55;bottom=570;bgcolor=0xE0E0E0;color=0x333333;db=1;dl=1;font=LOGFONT(h=-16;name='FontAwesome');notify=1;z=34};
plusRecent={cls="plus";text='\uF24D';left=60;top=540;right=100;bottom=570;bgcolor=0xE0E0E0;color=0x333333;db=1;dl=1;font=LOGFONT(h=-16;name='FontAwesome');notify=1;z=35};
plusRotate={cls="plus";text='\uF021';left=105;top=540;right=145;bottom=570;bgcolor=0xE0E0E0;color=0x333333;db=1;dl=1;font=LOGFONT(h=-16;name='FontAwesome');notify=1;z=36};
plusRunCode={cls="plus";text="运行";left=805;top=58;right=900;bottom=83;bgcolor=0x28A745;color=0xFFFFFF;dr=1;dt=1;iconStyle={align="left";font=LOGFONT(name='FontAwesome');padding={left=8}};iconText='\uF04B';notify=1;textPadding={left=24};z=29};
plusSave={cls="plus";text="保存";left=665;top=58;right=725;bottom=83;color=0x666666;dr=1;dt=1;iconStyle={align="left";font=LOGFONT(name='FontAwesome');padding={left=5}};iconText='\uF0C7';notify=1;textPadding={left=20};z=31};
plusScreen={cls="plus";text="投屏";left=15;top=330;right=135;bottom=365;bgcolor=0x28A745;color=0xFFFFFF;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-14;name='FontAwesome');padding={left=12}};iconText='\uF108';notify=1;textPadding={left=32};z=18};
plusScreenshot={cls="plus";text="截图";left=145;top=330;right=265;bottom=365;bgcolor=0xFFC107;color=0x333333;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-14;name='FontAwesome');padding={left=12}};iconText='\uF030';notify=1;textPadding={left=32};z=19};
plusStartRecord={cls="plus";text="开始录制";left=15;top=460;right=135;bottom=495;bgcolor=0xDC3545;color=0xFFFFFF;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-14;name='FontAwesome');padding={left=12}};iconText='\uF111';notify=1;textPadding={left=32};z=24};
plusStatus={cls="plus";text="未连接";left=15;top=95;right=265;bottom=125;align="left";color=0xFF6B6B;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-16;name='FontAwesome');padding={left=8}};iconText='\uF057';z=6};
plusStopRecord={cls="plus";text="停止录制";left=145;top=460;right=265;bottom=495;bgcolor=0xE0E0E0;color=0x333333;dl=1;dt=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-14;name='FontAwesome');padding={left=12}};iconText='\uF04D';notify=1;textPadding={left=32};z=25};
plusVolDown={cls="plus";text='\uF027';left=185;top=540;right=225;bottom=570;bgcolor=0xE0E0E0;color=0x333333;db=1;dl=1;font=LOGFONT(h=-16;name='FontAwesome');notify=1;z=32};
plusVolUp={cls="plus";text='\uF028';left=230;top=540;right=270;bottom=570;bgcolor=0xE0E0E0;color=0x333333;db=1;dl=1;font=LOGFONT(h=-16;name='FontAwesome');notify=1;z=33}
)
/*}}*/
// 应用按钮样式
var applyButtonStyles = function(){
winform.plusConnect.skin({
background={default=0xFF0078D4;hover=0xFF106EBE;active=0xFF005A9E;disabled=0xFFCCCCCC};
color={default=0xFFFFFFFF}
});
winform.plusDisconnect.skin({
background={default=0xFFE0E0E0;hover=0xFFD0D0D0;active=0xFFC0C0C0;disabled=0xFFE8E8E8};
color={default=0xFF333333;disabled=0xFFAAAAAA}
});
winform.plusScreen.skin({
background={default=0xFF28A745;hover=0xFF218838;active=0xFF1E7E34;disabled=0xFFCCCCCC};
color={default=0xFFFFFFFF}
});
winform.plusScreenshot.skin({
background={default=0xFFFFC107;hover=0xFFE0A800;active=0xFFD39E00;disabled=0xFFCCCCCC};
color={default=0xFF333333}
});
var grayBtnSkin = {
background={default=0xFFE0E0E0;hover=0xFFD0D0D0;active=0xFFC0C0C0;disabled=0xFFE8E8E8};
color={default=0xFF333333;disabled=0xFFAAAAAA}
};
winform.plusHome.skin(grayBtnSkin);
winform.plusBack.skin(grayBtnSkin);
winform.plusMenu.skin(grayBtnSkin);
winform.plusVolUp.skin(grayBtnSkin);
winform.plusVolDown.skin(grayBtnSkin);
winform.plusPower.skin(grayBtnSkin);
winform.plusRecent.skin(grayBtnSkin);
winform.plusRotate.skin(grayBtnSkin);
winform.plusStopRecord.skin(grayBtnSkin);
winform.plusStartRecord.skin({
background={default=0xFFDC3545;hover=0xFFC82333;active=0xFFBD2130;disabled=0xFFCCCCCC};
color={default=0xFFFFFFFF}
});
winform.plusClear.skin({
color={default=0xFF666666;hover=0xFFDC3545;active=0xFFC82333}
});
winform.plusSave.skin({
color={default=0xFF666666;hover=0xFF0078D4;active=0xFF005A9E}
});
winform.plusRunCode.skin({
background={default=0xFF28A745;hover=0xFF218838;active=0xFF1E7E34};
color={default=0xFFFFFFFF}
});
};
applyButtonStyles();
// 添加无边框窗口支持
win.ui.simpleWindow(winform);
// 状态变量
var isConnected = false;
var recordProcess = null;
var maxPosX, maxPosY;
var cxScreen, cyScreen;
// 自定义键盘映射
var KEYMAP = {
down = {
["ESC"] = "keyBack";
["HOME"] = "keyHome";
}
};
// 更新连接状态
var updateStatus = function(connected){
isConnected = connected;
var btns = {
winform.plusDisconnect;
winform.plusScreen;
winform.plusScreenshot;
winform.plusHome;
winform.plusBack;
winform.plusMenu;
winform.plusVolUp;
winform.plusVolDown;
winform.plusPower;
winform.plusRecent;
winform.plusRotate;
winform.plusStartRecord;
};
if(connected){
winform.plusStatus.iconText = '\uF058';
winform.plusStatus.iconColor = 0xFF28A745;
winform.plusStatus.argbColor = 0xFF28A745;
winform.plusStatus.text = "已连接";
for i, btn in btns {
btn.disabled = false;
}
} else {
winform.plusStatus.iconText = '\uF057';
winform.plusStatus.iconColor = 0xFFFF6B6B;
winform.plusStatus.argbColor = 0xFFFF6B6B;
winform.plusStatus.text = "未连接";
winform.lbModelValue.text = "--";
winform.lbVersionValue.text = "--";
winform.lbScreenValue.text = "--";
winform.lbIpValue.text = "--";
for i, btn in btns {
btn.disabled = true;
}
}
};
// 获取设备信息
var refreshDeviceInfo = function(){
if(!process.adb.isConnected()) return;
var model = process.adb.getProp("ro.product.model");
if(model) winform.lbModelValue.text = model;
var version = process.adb.version(true);
if(version) winform.lbVersionValue.text = "Android " + version;
cxScreen, cyScreen = process.adb.getScreen();
if(cxScreen && cyScreen){
winform.lbScreenValue.text = cxScreen + " × " + cyScreen;
}
var ip = process.adb.ip();
if(ip) winform.lbIpValue.text = ip;
maxPosX, maxPosY = process.adb.getMaxPos();
return #model || #version || #ip;
};
// 连接设备
winform.plusConnect.oncommand = function(){
owner.disabledText = {'\uF110'};
import process.adb.connecter;
if(process.adb.connecter(winform)){
updateStatus(true);
refreshDeviceInfo();
winform.editCode.print("// 设备连接成功: " + (winform.lbModelValue.text : "未知设备"));
} else {
updateStatus(false);
}
owner.disabledText = null;
};
// 断开连接
winform.plusDisconnect.oncommand = function(){
process.adb.disconnect();
updateStatus(false);
winform.editCode.print("// 已断开设备连接");
};
// 投屏
winform.plusScreen.oncommand = function(){
if(!isConnected){
winform.msgboxErr("请先连接设备");
return;
}
owner.disabledText = {'\uF110'};
import process.scrcpy;
var scrcpy = process.scrcpy();
scrcpy.arguments = {
powerOffOnClose = true;
"--always-on-top";
"--stay-awake";
"--window-title": "投屏 - " + (winform.lbModelValue.text : "设备");
};
var prcs, err = scrcpy.open();
if(prcs){
winform.editCode.print("// 投屏已启动");
// 启动键盘钩子
import key.hook;
if(!winform.keyHook){
winform.keyHook = key.hook();
winform.keyHook.mapKey(KEYMAP, process.adb, scrcpy.hwnd);
}
} else {
winform.editCode.print("// 投屏启动失败: " + (err : "未知错误"));
}
owner.disabledText = null;
};
// 截图
winform.plusScreenshot.oncommand = function(){
if(!isConnected) return;
owner.disabledText = {'\uF110'};
import fsys.dlg;
var path = fsys.dlg.save("*.png|*.png", "screenshot" + tostring(time(), "_%Y%m%d_%H%M%S"));
if(path){
var ok, err = process.adb.screenCap(path);
if(ok){
winform.editCode.print('// 截图已保存: ' + path);
raw.execute(path);
} else {
winform.editCode.print('// 截图失败: ' + (err : "未知错误"));
}
}
owner.disabledText = null;
};
// 快捷按键
winform.plusHome.oncommand = function(){
if(!isConnected) return;
process.adb.keyHome();
winform.editCode.print('process.adb.keyHome()');
};
winform.plusBack.oncommand = function(){
if(!isConnected) return;
process.adb.keyBack();
winform.editCode.print('process.adb.keyBack()');
};
winform.plusMenu.oncommand = function(){
if(!isConnected) return;
process.adb.keyMenu();
winform.editCode.print('process.adb.keyMenu()');
};
winform.plusVolUp.oncommand = function(){
if(!isConnected) return;
process.adb.keyVolumeUp();
winform.editCode.print('process.adb.keyVolumeUp()');
};
winform.plusVolDown.oncommand = function(){
if(!isConnected) return;
process.adb.keyVolumeDown();
winform.editCode.print('process.adb.keyVolumeDown()');
};
winform.plusPower.oncommand = function(){
if(!isConnected) return;
process.adb.keyPower();
winform.editCode.print('process.adb.keyPower()');
};
winform.plusRecent.oncommand = function(){
if(!isConnected) return;
process.adb.keyAppSwitch();
winform.editCode.print('process.adb.keyAppSwitch()');
};
winform.plusRotate.oncommand = function(){
if(!isConnected) return;
// 切换屏幕方向
var orientation = process.adb.shellGet("settings get system accelerometer_rotation");
if(string.trim(orientation) == "1"){
process.adb.shellGet("settings put system accelerometer_rotation 0");
winform.editCode.print('// 已锁定屏幕方向');
} else {
process.adb.shellGet("settings put system accelerometer_rotation 1");
winform.editCode.print('// 已开启自动旋转');
}
};
// 开始录制
var recordingThreadHandle;
winform.plusStartRecord.oncommand = function(){
if(!isConnected){
winform.msgboxErr("请先连接设备");
return;
}
if(recordingThreadHandle) return;
if(!maxPosX || !maxPosY){
winform.msgboxErr("无法获取设备触摸坐标信息");
return;
}
owner.disabled = true;
winform.plusStopRecord.disabled = false;
winform.editCode.print("");
winform.editCode.print("// ========== 开始录制操作 ==========");
winform.editCode.print("import process.adb;");
winform.editCode.print("");
recordingThreadHandle = thread.create(
function(winform, maxPosX, maxPosY, cxScreen, cyScreen){
var xPos, yPos;
var lastTapTime = 0;
import process.adb;
recordProcess = process.adb.getEvent("-l");
for device, eventType, eventCode, value in recordProcess.lines("([^:]+)\:\s+(\S+)\s+(\S+)\s+(\S+)", true){
if(eventType == "EV_SYN"){
if(eventCode == "SYN_REPORT" && !tonumber(value, 16)){
if(xPos !== null && yPos !== null){
var now = time.tick();
if(now - lastTapTime > 100){
winform.editCode.printf("process.adb.tap(%d, %d)", xPos, yPos);
lastTapTime = now;
}
xPos = null;
yPos = null;
}
}
}
elseif(eventType == "EV_ABS"){
if(eventCode == "ABS_MT_POSITION_X"){
xPos = math.round((tonumber(value, 16) / maxPosX) * cxScreen);
}
elseif(eventCode == "ABS_MT_POSITION_Y"){
yPos = math.round((tonumber(value, 16) / maxPosY) * cyScreen);
}
}
elseif(eventType == "EV_KEY"){
if(..string.startsWith(eventCode, "KEY_", true)){
if(value === "DOWN"){
winform.editCode.printf('process.adb.key("%s")', eventCode);
}
}
}
}
}, winform, maxPosX, maxPosY, cxScreen, cyScreen
);
};
// 停止录制
winform.plusStopRecord.oncommand = function(){
if(!recordingThreadHandle) return;
if(recordingThreadHandle){
//有时候做事件真不必那么完美,这样多省事
thread.terminate(recordingThreadHandle,0)
recordingThreadHandle = null;
}
winform.plusStartRecord.disabled = false;
owner.disabled = true;
winform.editCode.print("");
winform.editCode.print("// ========== 录制结束 ==========");
};
// 清空代码
winform.plusClear.oncommand = function(){
winform.editCode.text = "";
};
// 保存代码
winform.plusSave.oncommand = function(){
import fsys.dlg;
var path = fsys.dlg.save("*.aardio|*.aardio", "automation" + tostring(time(), "_%Y%m%d_%H%M%S"));
if(path){
string.save(path, winform.editCode.text);
winform.msgbox("代码已保存到:\n" + path, "保存成功");
}
};
// 运行代码
winform.plusRunCode.oncommand = function(){
if(!isConnected){
winform.msgboxErr("请先连接设备");
return;
}
var code = winform.editCode.text;
if(!#string.trim(code)){
winform.msgboxErr("代码为空");
return;
}
owner.disabledText = {'\uF110'};
var func, err = loadcode(code);
if(func){
var ok, err = call(func);
if(!ok){
winform.editCode.print("// 运行出错: " + err);
} else {
winform.editCode.print("// 运行完成");
}
} else {
winform.editCode.print("// 代码错误: " + err);
}
owner.disabledText = null;
};
// 初始状态
updateStatus(false);
winform.plusStopRecord.disabled = true;
// 检查是否已连接
if(process.adb.isConnected()){
updateStatus(true);
refreshDeviceInfo();
winform.editCode.print("// 检测到已连接的设备");
winform.editCode.print("// 设备: " + (winform.lbModelValue.text : "未知"));
}
var mdns = wsock.udp.mdnsClient();
mdns.onServiceResolved = function(service) {
if( service.serviceType == "_adb-tls-connect._tcp.local"){
if(!isConnected && !process.adb.isConnected()){
var result, err = ..process.adb.connect(service.ip + ":" + service.port);
if(result){
updateStatus(true);
refreshDeviceInfo();
winform.editCode.print("// 设备连接成功: " + (winform.lbModelValue.text : "未知设备"));
}
}
}
}
mdns.discover("_adb-tls-connect._tcp");
// 定时检测设备连接状态
winform.setInterval(
function(){
if(isConnected){
// 轻量级检测:执行一个简单命令
if(!process.adb.isConnected()){
updateStatus(false);
winform.editCode.print("// ⚠ 设备已断开连接");
// 重新启动 mDNS 发现,等待自动重连
mdns.discover("_adb-tls-connect._tcp");
}
}
}, 3000 // 每3秒检查一次,可根据需要调整
);
// 窗口关闭时清理
winform.onDestroy = function(){
mdns.close();
if(winform.keyHook){
winform.keyHook.close();
}
};
winform.show();
win.loopMessage();
Markdown 格式