02_auto.js基础操作1/4

文章目录

  • 02_auto.js基础操作1/4
    • 安卓手势
      • 点击左上角
      • 拉出通知栏
      • 三指捏合
      • 三指下滑
      • 双指捏合
      • 心形手势
    • 解压缩
      • 示例一
      • 示例二
    • 本地存储
      • 保存数组和复杂对象
      • 保存整数登简单数据
      • 随手记
    • 传感器
      • 打印常用传感器信息
      • 显示常用传感器信息
    • 调用JavaAPI
    • 定时器
      • 定时执行
      • 循环执行
    • 对话框
      • 菜单
      • 单选框
      • 多选框
      • 简单计算器
      • 模拟更新下载对话框
      • 确认框
      • 输入框
      • UI模式下使用对话框
    • 多媒体
      • 免root屏幕录制
      • 音乐播放器
    • 多线程
      • 变量可见性实验
      • 多线程按键监听
      • 多线程简单示例
      • 线程启动与关闭
      • 原子变量

安卓手势

点击左上角

"auto";

setScreenMetrics(1080, 1920);   // 屏幕分辨率1080*1920

click(100, 150);

拉出通知栏

"auto";

//表示从位置(500, 10)滑动到位置(500, 1000), 持续两秒
swipe(500, 10, 500, 1000, 2000);

三指捏合

"auto";
setScreenMetrics(1080, 1920);
//如果你使用的是MIUI,此脚本运行后会出现桌面多屏幕编辑
home();
sleep(1500);
gestures([350, [800, 300], [500, 1000]],
         [350, [300, 1500], [500, 1000]],
         [350, [300, 300], [500, 1000]]);

三指下滑

"auto";
/**
 * 同时模拟三个手势:
 * 从(300, 400)到(300, 1400)
 * 从(600, 400)到(600, 1400)
 * 从(900, 400)到(900, 1400)
 * 每一个的时长都为350毫秒
 */
gestures([350, [300, 400], [300, 1400]],
         [350, [600, 400], [600, 1400]],
         [350, [900, 400], [900, 1400]]
);

双指捏合

"auto";

setScreenMetrics(1080, 1920);

//如果你使用的是MIUI,此脚本运行后会出现桌面编辑
home();
sleep(1500);
gestures([500, [800, 300], [500, 1000]],
         [500, [300, 1500], [500, 1000]]);

心形手势

"auto";

toast("开启开发者选项-指针位置或者在画画软件才能查看效果");

setScreenMetrics(1080, 1920);

var points = [10000];
var interval = 0.1;
var x0 = 600;
var y0 = 1000;
var a = 120;

for(var t = 0; t < 2 * Math.PI; t += interval){
    var x = x0 + a * (2 * Math.cos(t) - Math.cos(2 * t));
    var y = y0 + a * (2 * Math.sin(t) - Math.sin(2 * t));
    points.push([parseInt(x), parseInt(y)]);
}

gesture.apply(null, points);

解压缩

示例一

//压缩文件路径(必须是完整路径)
var filePath = "/sdcard/脚本.7z";
//目录路径(必须是完整路径)
var dirPath = "/sdcard/脚本";
//压缩类型
//支持的压缩类型包括:zip 7z bz2 bzip2 tbz2 tbz gz gzip tgz tar wim swm xz txz。
var type = "7z";
//压缩密码
var password = "password"

//7z加密压缩(若文件已存在则跳过)
//zips.A(type, filePath, dirPath, password)

//压缩
switch (zips.A(type, filePath, dirPath)) {
    case 0:
        toastLog("压缩成功!文件已保存为: " + filePath)
        break;
    case 1:
        toastLog("压缩结束,存在非致命错误(例如某些文件正在被使用,没有被压缩)")
        break;
    case 2:
        toastLog("致命错误")
        break;
    case 7:
        toastLog("命令行错误")
        break;
    case 8:
        toastLog("没有足够内存")
        break;
    case 255:
        toastLog("用户中止操作")
        break;
    default: toastLog("未知错误")
}

示例二

// 准备工作,创建文件夹与文件,以便后续用于压缩
// 创建两个文件夹与三个文件
$files.create("/sdcard/脚本/zip_test/");
$files.create("/sdcard/脚本/zip_out/");
$files.write("/sdcard/脚本/zip_test/1.txt", "Hello, World");
$files.write("/sdcard/脚本/zip_test/2.txt", "GoodBye, World");
$files.write("/sdcard/脚本/zip_test/3.txt", "Autox.js");

// 1. 压缩文件夹
// 要压缩的文件夹路径
let dir = '/sdcard/脚本/zip_test/';
// 压缩后的文件路径
let zipFile = '/sdcard/脚本/zip_out/未加密.zip';
$files.remove(zipFile);
$zip.zipDir(dir, zipFile);

// 2.加密压缩文件夹
let encryptedZipFile = '/sdcard/脚本/zip_out/加密.zip';
$files.remove(encryptedZipFile);
$zip.zipDir(dir, encryptedZipFile, {
    password: 'Autox.js'
});

// 3. 压缩单个文件
let zipSingleFie = '/sdcard/脚本/zip_out/单文件.zip'
$files.remove(zipSingleFie);
$zip.zipFile('/sdcard/脚本/zip_test/1.txt', zipSingleFie);

// 4. 压缩多个文件
let zipMultiFile = '/sdcard/脚本/zip_out/多文件.zip';
$files.remove(zipMultiFile);
let fileList = ['/sdcard/脚本/zip_test/1.txt', '/sdcard/脚本/zip_test/2.txt']
$zip.zipFiles(fileList, zipMultiFile);

// 5. 解压文件
$zip.unzip('/sdcard/脚本/zip_out/未加密.zip', '/sdcard/脚本/zip_out/未加密/');

// 6. 解压加密的zip
$zip.unzip('/sdcard/脚本/zip_out/加密.zip', '/sdcard/脚本/zip_out/加密/', {
    password: 'Autox.js'
});

// 7. 从压缩包删除文件
let z = $zip.open('/sdcard/脚本/zip_out/多文件.zip');
z.removeFile('1.txt');

// 8. 为压缩包增加文件
z.addFile('/sdcard/脚本/zip_test/3.txt');

本地存储

保存数组和复杂对象

var storage = storages.create("Auto.js例子:复杂数据");
var arr = [1, 4, 2, 5];
var obj = {
    name: "Auto.js",
    url: "www.autojs.org"
};
//保存
storage.put("arr", arr);
storage.put("obj", obj);

console.show();
//取出
log("arr = ", storage.get("arr"));
log("obj = ", storage.get("obj"));

保存整数登简单数据

var storage = storages.create("Auto.js例子:简单数据");
var a = 1234;
var b = true;
var str = "hello";
//保存
storage.put("a", a);
storage.put("b", b);
storage.put("str", str);

console.show();
//取出
log("a = " + storage.get("a"));
log("b = " + storage.get("b"));
log("str = " + storage.get("str"));

随手记

"ui";
ui.layout(
    <vertical padding="16">
        <horizontal>
            <text textColor="black" textSize="18sp" layout_weight="1">随手记</text>
            <button id="save" text="保存" w="auto" style="Widget.AppCompat.Button.Borderless.Colored"/>
        </horizontal>
        <input id="content" h="*" gravity="top"/>
    </vertical>
);
var storage = storages.create("Auto.js例子:随手记");
var content = storage.get("content");
if(content != null){
    ui.content.setText(content);
}
ui.save.click(()=>{
    storage.put("content", ui.content.text());
});

传感器

打印常用传感器信息

//忽略不支持的传感器,即使有传感器不支持也不抛出异常
sensors.ignoresUnsupportedSensor = true;

sensors.on("unsupported_sensor", function(sensorName, sensorType){
    log("不支持的传感器: %s 类型: %d", sensorName, sensorType);
});

//加速度传感器
sensors.register("accelerometer").on("change", (event, ax, ay, az)=>{
    log("x方向加速度: %d
y方向加速度: %d
z方向加速度: %d", ax, ay, az);
});
//方向传感器
sensors.register("orientation").on("change", (event, dx, dy, dz)=>{
    log("绕x轴转过角度: %d
绕y轴转过角度: %d
绕z轴转过角度: %d", dx, dy, dz);
});
//陀螺仪传感器
sensors.register("gyroscope").on("change", (event, wx, wy, wz)=>{
    log("绕x轴角速度: %d
绕y轴角速度: %d
绕z轴角速度: %d", wx, wy, wz);
});
//磁场传感器
sensors.register("magnetic_field").on("change", (event, bx, by, bz)=>{
    log("x方向磁场强度: %d
y方向磁场强度: %d
z方向磁场强度: %d", bx, by, bz);
});
//重力传感器
sensors.register("magnetic_field").on("change", (event, gx, gy, gz)=>{
    log("x方向重力: %d
y方向重力: %d
z方向重力: %d", gx, gy, gz);
});
//线性加速度传感器
sensors.register("linear_acceleration").on("change", (event, ax, ay, az)=>{
    log("x方向线性加速度: %d
y方向线性加速度: %d
z方向线性加速度: %d", ax, ay, az);
});
//温度传感器
sensors.register("ambient_temperature").on("change", (event, t)=>{
    log("当前温度: %d", t);
});
//光线传感器
sensors.register("light").on("change", (event, l)=>{
    log("当前光的强度: %d", l);
});
//压力传感器
sensors.register("pressure").on("change", (event, p)=>{
    log("当前压力: %d", p);
});
//距离传感器
sensors.register("proximity").on("change", (event, d)=>{
    log("当前距离: %d", d);
});
//湿度传感器
sensors.register("relative_humidity").on("change", (event, rh)=>{
    log("当前相对湿度: %d", rh);
});

//30秒后退出程序
setTimeout(exit, 30 * 1000);

显示常用传感器信息

"ui";

ui.layout(
    <scroll>
        <vertical>
            <text id="accelerometer" margin="12dp" textSize="16sp" textColor="#000000"/>
            <text id="orientation" margin="12dp" textSize="16sp" textColor="#000000"/>
            <text id="gyroscope" margin="12dp" textSize="16sp" textColor="#000000"/>
            <text id="magnetic_field" margin="12dp" textSize="16sp" textColor="#000000"/>
            <text id="gravity" margin="12dp" textSize="16sp" textColor="#000000"/>
            <text id="linear_acceleration" margin="12dp" textSize="16sp" textColor="#000000"/>
            <text id="ambient_temperature" margin="12dp" textSize="16sp" textColor="#000000"/>
            <text id="light" margin="12dp" textSize="16sp" textColor="#000000"/>
            <text id="pressure" margin="12dp" textSize="16sp" textColor="#000000"/>
            <text id="proximity" margin="12dp" textSize="16sp" textColor="#000000"/>
            <text id="relative_humidity" margin="12dp" textSize="16sp" textColor="#000000"/>
        </vertical>
    </scroll>
);

//忽略不支持的传感器,即使有传感器不支持也不抛出异常
sensors.ignoresUnsupportedSensor = true;

sensors.on("unsupported_sensor", function(sensorName, sensorType){
    log(util.format("不支持的传感器: %s 类型: %d", sensorName, sensorType));
});

//加速度传感器
sensors.register("accelerometer", sensors.delay.ui).on("change", (event, ax, ay, az)=>{
    ui.accelerometer.setText(util.format("x方向加速度: %d
y方向加速度: %d
z方向加速度: %d", ax, ay, az));
});
//方向传感器
sensors.register("orientation", sensors.delay.ui).on("change", (event, dx, dy, dz)=>{
    ui.orientation.setText(util.format("绕x轴转过角度: %d
绕y轴转过角度: %d
绕z轴转过角度: %d", dx, dy, dz));
});
//陀螺仪传感器
sensors.register("gyroscope", sensors.delay.ui).on("change", (event, wx, wy, wz)=>{
    ui.gyroscope.setText(util.format("绕x轴角速度: %d
绕y轴角速度: %d
绕z轴角速度: %d", wx, wy, wz));
});
//磁场传感器
sensors.register("magnetic_field", sensors.delay.ui).on("change", (event, bx, by, bz)=>{
    ui.magnetic_field.setText(util.format("x方向磁场强度: %d
y方向磁场强度: %d
z方向磁场强度: %d", bx, by, bz));
});
//重力传感器
sensors.register("gravity", sensors.delay.ui).on("change", (event, gx, gy, gz)=>{
    ui.gravity.setText(util.format("x方向重力: %d
y方向重力: %d
z方向重力: %d", gx, gy, gz));
});
//线性加速度传感器
sensors.register("linear_acceleration", sensors.delay.ui).on("change", (event, ax, ay, az)=>{
    ui.linear_acceleration.setText(util.format("x方向线性加速度: %d
y方向线性加速度: %d
z方向线性加速度: %d", ax, ay, az));
});
//温度传感器
sensors.register("ambient_temperature", sensors.delay.ui).on("change", (event, t)=>{
    ui.ambient_temperature.setText(util.format("当前温度: %d", t));
});
//光线传感器
sensors.register("light", sensors.delay.ui).on("change", (event, l)=>{
    ui.light.setText(util.format("当前光的强度: %d", l));
});
//压力传感器
sensors.register("pressure", sensors.delay.ui).on("change", (event, p)=>{
    ui.pressure.setText(util.format("当前压力: %d", p));
});
//距离传感器
sensors.register("proximity", sensors.delay.ui).on("change", (event, d)=>{
    ui.proximity.setText(util.format("当前距离: %d", d));
});
//湿度传感器
sensors.register("relative_humidity", sensors.delay.ui).on("change", (event, rh)=>{
    ui.relative_humidity.setText(util.format("当前相对湿度: %d", rh));
});

//30秒后退出程序
setTimeout(exit, 30 * 1000);

调用JavaAPI

/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * liveConnect.js: a simple demonstration of javascript-to-Java connectivity
 */

// Create a new StringBuffer. Note that the class name must be fully qualified
// by its package. Packages other than "java" must start with "Packages", i.e.,
// "Packages.javax.servlet...".
var sb = new java.lang.StringBuffer();

// Now add some stuff to the buffer.
sb.append("hi, mom");
sb.append(3); // this will add "3.0" to the buffer since all JS numbers
  // are doubles by default
sb.append(true);

// Now print it out. (The toString() method of sb is automatically called
// to convert the buffer to a string.)
// Should print "hi, mom3.0true".
print(sb);
openConsole();

定时器

定时执行

toast("静等20秒,你会看到想看的...");

var i = 0;

setTimeout(function(){
    app.openUrl("http://music.163.com/#/song?id=109628&autoplay=true&market=baiduhd");
    exit();
}, 20 * 1000);

setInterval(function(){
    i++;
    toast(i * 5 + "秒");
}, 5000);

循环执行

var i = 0;

setInterval(function(){
    i++;
    toast(i * 4 + "秒");
    if(i == 5){
        exit();
    }
}, 4000);

对话框

菜单

while(true){
    var i = dialogs.select("哲学的基本问题是", "社会和自然的关系问题", "思维与存在的关系问题", "政治和经济的关系问题", "实践和理论的关系问题");
    if(i == -1){
        toast("猜一下呗");
        continue;
    }
    if(i == 1){
        toast("答对辣");
        break;
    }else{
        toast("答错辣")
    }
}

单选框

var sex = dialogs.singleChoice("请选择性别", ["男", "女", "基佬", "女装", "其他"], 2);
toast("选择了第" + (sex + 1) + "个选项");

多选框

var i = dialogs.multiChoice("下列作品出自李贽的是", ["《焚书》", "《西湖寻梦》", "《高太史全集》", "《续焚烧书》", "《藏书》"]);
toast("选择了: " + i);
if(i.length == 2 && i.toString() == [0, 4].toString()){
    toast("答对辣");
}else{
    toast("答错辣");
}

简单计算器

var num1 = dialogs.input("请输入第一个数字");
var op = dialogs.singleChoice("请选择运算", ["加", "减", "乘", "除", "幂"]);
var num2 = dialogs.input("请输入第二个数字");
var result = 0;
switch(op){
case 0:
    result = num1 + num2;
    break;
case 1:
    result = num1 - num2;
    break;
case 2:
    result = num1 * num2;
    break;
case 3:
    result = num1 / num2;
    break;
case 4:
    result = Math.pow(num1, num2);
    break;
}
alert("运算结果", result);

模拟更新下载对话框

var releaseNotes = "版本 v7.7.7
"
    + "更新日志:
"
    + "* 新增 若干Bug
";
dialogs.build({
    title: "发现新版本",
    content: releaseNotes,
    positive: "立即下载",
    negative: "取消",
    neutral: "到浏览器下载"
})
    .on("positive", download)
    .on("neutral", () => {
        app.openUrl("https://www.autojs.org");
    })
    .show();

var downloadDialog = null;
var downloadId = -1;

function download(){
    downloadDialog = dialogs.build({
        title: "下载中...",
        positive: "暂停",
        negative: "取消",
        progress: {
            max: 100,
            showMinMax: true
        },
        autoDismiss: false
    })
        .on("positive", ()=>{
            if(downloadDialog.getActionButton("positive") == "暂停"){
                stopDownload();
                downloadDialog.setActionButton("positive", "继续");
            }else{
                startDownload();
                downloadDialog.setActionButton("positive", "暂停");
            }
        })
        .on("negative", ()=>{
            stopDownload();
            downloadDialog.dismiss();
            downloadDialog = null;
        })
        .show();
    startDownload();
}

function startDownload(){
    downloadId = setInterval(()=>{
        var p = downloadDialog.getProgress();
        if(p >= 100){
            stopDownload();
            downloadDialog.dismiss();
            downloadDialog = null;
            toast("下载完成");
        }else{
            downloadDialog.setProgress(p + 1);
        }
    }, 100);
}

function stopDownload(){
    clearInterval(downloadId);
}

确认框

var handsome = confirm("你帅吗?");
if(handsome){
    toast("真不要脸!");
    toast("真不要脸!");
    toast("真不要脸!");
    alert("真不要脸!");
}else{
    toast("嗯");
}

输入框

var name = rawInput("请输入名字");
alert("(•́へ•́╬)", "你好~ " + name);
var expr = dialogs.input("请输入简单的算式", "1+1");
alert("计算结果为 " + expr);

UI模式下使用对话框

"ui";

ui.layout(
    <vertical>
        <button id="callback" align="center">回调形式</button>
        <button id="promise" align="center">Promise形式</button>
        <button id="calc" align="center">简单计算器</button>
    </vertical>
);

ui.callback.click(()=>{
    dialogs.confirm("要弹出输入框吗?", "", function(b){
        if(b){
            dialogs.rawInput("输入", "", function(str){
                alert("您输入的是:" + str);
            });
        }else{
            ui.finish();
        }
    });
});

ui.promise.click(()=>{
    dialogs.confirm("要弹出输入框吗")
        .then(function(b){
            if(b){
               return dialogs.rawInput("输入");
            }else{
                ui.finish();
            }
        }).then(function(str){
            alert("您输入的是:" + str);
        });
});

ui.calc.click(()=>{
    let num1, num2, op;
    dialogs.input("请输入第一个数字")
        .then(n => {
            num1 = n;
            return dialogs.singleChoice("请选择运算", ["加", "减", "乘", "除", "幂"]);
        })
        .then(o => {
            op = o;
            return dialogs.input("请输入第二个数字");
         })
        .then(n => {
            num2 = n;
            var result;
            switch(op){
                case 0:
                    result = num1 + num2;
                    break;
                case 1:
                    result = num1 - num2;
                    break;
                case 2:
                    result = num1 * num2;
                    break;
                case 3:
                    result = num1 / num2;
                    break;
                case 4:
                    result = Math.pow(num1, num2);
                    break;
            }
            alert("运算结果", result);
        });
});

多媒体

免root屏幕录制

"ui";

importClass(android.content.Context);
importClass(android.hardware.display.DisplayManager);
importClass(android.media.MediaRecorder);
importClass(java.io.File);

runtime.requestPermissions(["WRITE_EXTERNAL_STORAGE", "READ_EXTERNAL_STORAGE", "RECORD_AUDIO"]);

mMediaProjectionManager = context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
mMediaRecorder = new MediaRecorder();
mVirtualDisplay = null;
saveDir = "/sdcard";
saveWidth = device.width;
saveHeight = device.height;
saveTime = 10 * 1000; // 单位:毫秒
isRunning = false;

ui.layout(
    <vertical>
        <appbar>
            <toolbar title="免root屏幕录制" />
        </appbar>
        <Switch id="permissions" text="音频录制及存储权限" checked="true" gravity="center"/>
        <frame gravity="center">
            <text text="AutoX" gravity="center" />
        </frame>
        <button text="免root屏幕录制" style="Widget.AppCompat.Button.Colored" id="button" />
    </vertical>
);

ui.button.click(function () {
    if (isRunning) {
        stopRecord();
        ui.button.setText("免root屏幕录制");
    } else {
        activity.startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), 666);
    }
});
// 申请权限
ui.permissions.on("check", function (checked) {
    if (checked) {
        runtime.requestPermissions(["WRITE_EXTERNAL_STORAGE", "READ_EXTERNAL_STORAGE", "RECORD_AUDIO"]);
    } else {
        toastLog("权限不足!");
    }
});
ui.emitter.on("resume", function () {
    ui.permissions.checked = true;
});

// 获取屏幕录制授权
ui.emitter.on("activity_result", (requestCode, resultCode, data) => {
    mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data);
    if (mMediaProjection) {
        startRecord();
        ui.button.setText("视频录制中(点击停止)……");
        setTimeout(function () {
            stopRecord();
            ui.button.setText("免root屏幕录制");
        }, saveTime)
    }
});

events.on("exit", function () {
    stopRecord();
});

function startRecord() {
    if (mMediaProjection == null || isRunning) {
        return false;
    }
    file = new File(saveDir, "screen_record.mp4");
    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mMediaRecorder.setOutputFile(file.getAbsolutePath());
    mMediaRecorder.setVideoSize(saveWidth, saveHeight);
    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mMediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);
    mMediaRecorder.setVideoFrameRate(30);
    try {
        mMediaRecorder.prepare();
    } catch (e) {
        toastLog(e);
    }
    mVirtualDisplay = mMediaProjection.createVirtualDisplay("免root屏幕录制", saveWidth, saveHeight, 1, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mMediaRecorder.getSurface(), null, null);
    mMediaRecorder.start();
    isRunning = true;
    return true;
}

function stopRecord() {
    if (!isRunning) {
        return false;
    }
    mMediaRecorder.stop();
    mMediaRecorder.reset();
    mVirtualDisplay.release();
    mMediaProjection.stop();
    isRunning = false;
    toastLog("录制结束!");
    return true;
}

音乐播放器

"ui";

ui.layout(
  <vertical>
    <text id="name" text="音乐播放器" textSize="22sp" textColor="#fbfbfe" bg="#00afff" w="*" gravity="center"></text>
    <button id="play">播放音乐</button>
    <button id="next">下一曲</button>
    <button id="pause">暂停</button>
  </vertical>
);
var musicDir = '/sdcard/Music';
if (!files.isDir(musicDir)) {
  toastLog(musicDir + "目录不存在!")
}
var musicFiles = files.listDir(musicDir, function (name) {
  return name.endsWith(".mp3") || name.endsWith(".wma") || name.endsWith(".wav")
});
var i = 0;
var musicPath = "";
if (musicFiles.length > 0) {
  musicPath = files.join(musicDir, musicFiles[i]);
  ui.name.setText(files.getNameWithoutExtension(musicPath));
} else {
  toastLog(musicDir + "目录下没有音频文件!")
}
ui.pause.click(function () {
  media.pauseMusic();
});
ui.next.click(function () {
  musicPath = files.join(musicDir, musicFiles[(i + 1) % musicFiles.length]);
  if (files.isFile(musicPath)) {
    ui.name.setText(files.getNameWithoutExtension(musicPath));
    media.playMusic(musicPath, 0.8);
  } else {
    toastLog(musicPath + "音频文件不存在!")
  }
}
);
ui.play.click(function () {
  if (media.isMusicPlaying()) {
    return true;
  } else {
    if (files.isFile(musicPath)) {
      ui.name.setText(files.getNameWithoutExtension(musicPath));
      media.playMusic(musicPath, 0.8);
    } else {
      toastLog(musicPath + "音频文件不存在!")
    }
  }
});
ui.emitter.on("pause", () => {
  if (media.isMusicPlaying()) {
    media.pauseMusic();
  }
});
ui.emitter.on("resume", () => {
  ui.post(function () {
    media.resumeMusic();
  }, 200);
});
events.on("exit", function () {
  media.stopMusic();
});

多线程

变量可见性实验

var running = true;

threads.start(function(){
    while(running){
        log("running = true");
    }
});

sleep(2000);
running = false;
console.info("running = false");

多线程按键监听

auto();

threads.start(function(){
    //在子线程中调用observeKey()从而使按键事件处理在子线程执行
    events.observeKey();
    events.on("key_down", function(keyCode, events){
        //音量键关闭脚本
        if(keyCode == keys.volume_up){
            exit();
        }
    });
});

toast("音量上键关闭脚本");

events.on("exit", function(){
    toast("脚本已结束");
});

while(true){
    log("脚本运行中...");
    sleep(2000);
}

多线程简单示例

//启动一个线程
threads.start(function(){
    //在线程中每隔1秒打印"线程1"
    while(true){
        log("线程1");
        sleep(1000);
    }
});

//启动另一个线程
threads.start(function(){
    //在线程中每隔2秒打印"线程1"
    while(true){
        log("线程2");
        sleep(2000);
    }
});

//在主线程中每隔3秒打印"主线程"
for(var i = 0; i < 10; i++){
    log("主线程");
    sleep(3000);
}
//打印100次后退出所有线程
threads.shutDownAll();

线程启动与关闭

//启动一个无限循环的线程
var thread = threads.start(function(){
    while(true){
        log("子线程运行中...");
        sleep(1000);
    }
});

//5秒后关闭线程
sleep(5000);
thread.interrupt();

原子变量

var i = threads.atomic();

本文转自 https://blog.csdn.net/qq_37952052/article/details/131625821?spm=1001.2014.3001.5502,如有侵权,请联系删除。

02_auto.js基础操作2/4

文章目录

  • 02_auto.js基础操作2/4
    • 复杂界面
      • 待办事项
      • 登录界面
      • 界面模板
      • 用户调查
      • 一个小测试
    • 画布
      • 函数图像简单版
      • 函数图像高级版
    • 脚本引擎
      • 停止所有正在运行的脚本
      • 运行脚本文件
      • 运行录制文件
      • 运行新的脚本任务
    • 界面控件
      • 按钮控件
      • 表格控件-内置图标查看器
      • 复选框单选框控件
      • 进度条控件
      • 卡片布局
      • 列表控件
      • 时间日期选择控件
      • 输入框控件
      • 图片控件
      • 文本控件
      • 下拉菜单
      • 自定义控件-布局模板
      • 自定义控件-带颜色按钮
      • 自定义控件-模块-配置勾选框
      • 自定义控件-使用配置勾选框

复杂界面

待办事项

"ui";

importClass(android.graphics.Paint);

ui.layout(
    <frame>
        <vertical>
            <appbar>
                <toolbar id="toolbar" title="Todo" />
            </appbar>
            <list id="todoList">
                <card w="*" h="70" margin="10 5" cardCornerRadius="2dp"
                    cardElevation="1dp" foreground="?selectableItemBackground">
                    <horizontal gravity="center_vertical">
                        <View bg="{{this.color}}" h="*" w="10" />
                        <vertical padding="10 8" h="auto" w="0" layout_weight="1">
                            <text id="title" text="{{this.title}}" textColor="#222222" textSize="16sp" maxLines="1" />
                            <text text="{{this.summary}}" textColor="#999999" textSize="14sp" maxLines="1" />
                        </vertical>
                        <checkbox id="done" marginLeft="4" marginRight="6" checked="{{this.done}}" />
                    </horizontal>

                </card>
            </list>
        </vertical>
        <fab id="add" w="auto" h="auto" src="@drawable/ic_add_black_48dp"
            margin="16" layout_gravity="bottom|right" tint="#ffffff" />
    </frame>
);

var materialColors = ["#e91e63", "#ab47bc", "#5c6bc0", "#7e57c2", "##2196f3", "#00bcd4",
    "#26a69a", "#4caf50", "#8bc34a", "#ffeb3b", "#ffa726", "#78909c", "#8d6e63"];

var storage = storages.create("todoList");
//从storage获取todo列表
var todoList = storage.get("items", [
    {
        title: "写操作系统作业",
        summary: "明天第1~2节",
        color: "#f44336",
        done: false
    },
    {
        title: "给ui模式增加若干Bug",
        summary: "无限期",
        color: "#ff5722",
        done: false
    },
    {
        title: "发布Auto.js 5.0.0正式版",
        summary: "2019年1月",
        color: "#4caf50",
        done: false
    },
    {
        title: "完成毕业设计和论文",
        summary: "2019年4月",
        color: "#2196f3",
        done: false
    }
]);;

ui.todoList.setDataSource(todoList);

ui.todoList.on("item_bind", function (itemView, itemHolder) {
    //绑定勾选框事件
    itemView.done.on("check", function (checked) {
        let item = itemHolder.item;
        item.done = checked;
        let paint = itemView.title.paint;
        //设置或取消中划线效果
        if (checked) {
            paint.flags |= Paint.STRIKE_THRU_TEXT_FLAG;
        } else {
            paint.flags &= ~Paint.STRIKE_THRU_TEXT_FLAG;
        }
        itemView.title.invalidate();
    });
});

ui.todoList.on("item_click", function (item, i, itemView, listView) {
    itemView.done.checked = !itemView.done.checked;
});

ui.todoList.on("item_long_click", function (e, item, i, itemView, listView) {
    confirm("确定要删除" + item.title + "吗?")
        .then(ok => {
            if (ok) {
                todoList.splice(i, 1);
            }
        });
    e.consumed = true;
});

//当离开本界面时保存todoList
ui.emitter.on("pause", () => {
    storage.put("items", todoList);
});

ui.add.on("click", () => {
    dialogs.rawInput("请输入标题")
        .then(title => {
            if (!title) {
                return;
            }
            dialogs.rawInput("请输入期限", "明天")
                .then(summary => {
                    todoList.push({
                        title: title,
                        summary: summary,
                        color: materialColors[random(0, materialColors.length - 1)]
                    });
                });
        })
});

登录界面

"ui";

showLoginUI();
ui.statusBarColor("#000000")

//显示登录界面
function showLoginUI(){
    ui.layout(
      <frame>
        <vertical h="auto" align="center" margin="0 50">
          <linear>
             <text w="56" gravity="center" color="#111111" size="16">用户名</text>
             <input id="name" w="*" h="40"/>
          </linear>
          <linear>
             <text w="56" gravity="center" color="#111111" size="16">密码</text>
             <input id="password" w="*" h="40" password="true"/>
          </linear>
          <linear gravity="center">
             <button id="login" text="登录"/>
             <button id="register" text="注册"/>
          </linear>
        </vertical>
      </frame>
    );

    ui.login.on("click", () => {
       toast("您输入的用户名为" + ui.name.text() + " 密码为" + ui.password.text());
    });
    ui.register.on("click", () => showRegisterUI());
}

//显示注册界面
function showRegisterUI(){
    ui.layout(
      <frame>
        <vertical h="auto" align="center" margin="0 50">
          <linear>
             <text w="56" gravity="center" color="#111111" size="16">用户名</text>
             <input w="*" h="40"/>
          </linear>
          <linear>
             <text w="56" gravity="center" color="#111111" size="16">密码</text>
             <input w="*" h="40" password="true"/>
          </linear>
          <linear>
             <text w="56" gravity="center" color="#111111" size="16">邮箱</text>
             <input w="*" h="40" inputType="textEmailAddress"/>
          </linear>
          <linear gravity="center">
             <button>确定</button>
             <button id="cancel">取消</button>
          </linear>
        </vertical>
      </frame>
    );
    ui.cancel.on("click", () => showLoginUI());
}

界面模板

"ui";

var color = "#009688";

ui.layout(
    <drawer id="drawer">
        <vertical>
            <appbar>
                <toolbar id="toolbar" title="示例"/>
                <tabs id="tabs"/>
            </appbar>
            <viewpager id="viewpager">
                <frame>
                    <text text="第一页内容" textColor="black" textSize="16sp"/>
                </frame>
                <frame>
                    <text text="第二页内容" textColor="red" textSize="16sp"/>
                </frame>
                <frame>
                    <text text="第三页内容" textColor="green" textSize="16sp"/>
                </frame>
            </viewpager>
        </vertical>
        <vertical layout_gravity="left" bg="#ffffff" w="280">
            <img w="280" h="200" scaleType="fitXY" src="http://images.shejidaren.com/wp-content/uploads/2014/10/023746fki.jpg"/>
            <list id="menu">
                <horizontal bg="?selectableItemBackground" w="*">
                    <img w="50" h="50" padding="16" src="{{this.icon}}" tint="{{color}}"/>
                    <text textColor="black" textSize="15sp" text="{{this.title}}" layout_gravity="center"/>
                </horizontal>
            </list>
        </vertical>
    </drawer>
);

//创建选项菜单(右上角)
ui.emitter.on("create_options_menu", menu=>{
    menu.add("设置");
    menu.add("关于");
});
//监听选项菜单点击
ui.emitter.on("options_item_selected", (e, item)=>{
    switch(item.getTitle()){
        case "设置":
            toast("还没有设置");
            break;
        case "关于":
            alert("关于", "Auto.js界面模板 v1.0.0");
            break;
    }
    e.consumed = true;
});
activity.setSupportActionBar(ui.toolbar);

//设置滑动页面的标题
ui.viewpager.setTitles(["标签一", "标签二", "标签三"]);
//让滑动页面和标签栏联动
ui.tabs.setupWithViewPager(ui.viewpager);

//让工具栏左上角可以打开侧拉菜单
ui.toolbar.setupWithDrawer(ui.drawer);

ui.menu.setDataSource([
  {
      title: "选项一",
      icon: "@drawable/ic_android_black_48dp"
  },
  {
      title: "选项二",
      icon: "@drawable/ic_settings_black_48dp"
  },
  {
      title: "选项三",
      icon: "@drawable/ic_favorite_black_48dp"
  },
  {
      title: "退出",
      icon: "@drawable/ic_exit_to_app_black_48dp"
  }
]);

ui.menu.on("item_click", item => {
    switch(item.title){
        case "退出":
            ui.finish();
            break;
    }
})

用户调查

"ui";

ui.layout(
    <vertical>
        <text textSize="18sp" textColor="#000000" margin="20" textStyle="bold">
            关于Auto.js的用户调查
        </text>
        <ScrollView>
            <vertical>
                <text textSize="16sp" margin="8">1. 您的年龄是?</text>
                <input text="18" inputType="number" margin="0 16"/>
                <text textSize="16sp" margin="8">2. 您用过其他类似软件(脚本精灵,按键精灵等)吗?</text>
                <radiogroup margin="0 16">
                    <radio text="没有用过"/>
                    <radio text="用过"/>
                    <radio text="用过,感觉不好用"/>
                    <radio text="没有Root权限无法使用"/>
                </radiogroup>
                <text textSize="16sp" margin="8">3. 您使用Auto.js通常用于做什么?(多选)</text>
                <checkbox text="游戏辅助" marginLeft="16"/>
                <checkbox text="点赞" marginLeft="16"/>
                <checkbox text="日常生活工作辅助" marginLeft="16"/>
                <checkbox text="练习编程" marginLeft="16"/>
                <checkbox text="自动化测试" marginLeft="16"/>
                <linear>
                    <checkbox text="其他" marginLeft="16"/>
                    <input w="*" margin="0 16"/>
                </linear>
                <text textSize="16sp" margin="8">4. 您更喜欢以下哪个图标?</text>
                <radiogroup margin="0 16">
                    <radio/>
                    <img w="100" h="100" margin="0 16" src="http://www.autojs.org/assets/uploads/profile/3-profileavatar.png"/>
                    <radio/>
                    <img w="100" h="100" margin="0 16" src="http://www.autojs.org/assets/uploads/files/1511945512596-autojs_logo.png"/>
                </radiogroup>
                <text textSize="16sp" margin="8">5. 您是什么时候开始使用Auto.js的呢?</text>
                <datepicker margin="4 16" datePickerMode="spinner"/>
                <text textSize="16sp" margin="8">6. 您用过下面这个Auto.js的论坛吗?</text>
                <webview id="webview" h="300" margin="0 16"/>
                <radiogroup marginLeft="16" marginTop="16">
                    <radio text="没有用过"/>
                    <radio text="用过"/>
                    <radio text="用过,感觉不好用"/>
                </radiogroup>
                <linear gravity="center">
                    <button margin="16">提交</button>
                    <button margin="16">放弃</button>
                </linear>
            </vertical>
        </ScrollView>
    </vertical>
)

ui.webview.loadUrl("http://www.autojs.org");

一个小测试

"ui";

/**
 * By Da Zhang
 * 本脚本仅为娱乐,没有任何破坏性质
 */

ui.statusBarColor("#AA0000");

var Quin = 32552732;

ui.layout(
    <frame background="#AA0000">
     <vertical align="top" paddingTop="5" margin="10">
      <text id="oops" color="#FFFFFF" gravity="center" size="20">Oops, your files have been encrypted!</text>
      <text id="text" bg="#FFFFFF" gravity="left" color="#000000" size="15" marginTop="15" h="425"></text>
      <button id="payment" text="Payment" margin="20 0 0 0"/>
      <button id="decrypt" text="Decrypt"/>
     </vertical>
    </frame>
);
ui.text.text("我的手机出了什么问题?
您的一些重要文件被我加密保存了。
" + 
 "照片、图片、文档、压缩包、音频、视频文件、apk文件等,几乎所有类型的文件都被加密了,因此不能正常打开。
" + 
 "这和一般文件损坏有本质上的区别。您大可在网上找找恢复文件的方法,我敢保证,没有我们的解密服务,就算老天爷来了也不能恢复这些文档。

" + 
 "有没有恢复这些文档的方法?
当然有可恢复的方法。只能通过我们的解密服务才能恢复。我以人格担保,能够提供安全有效的恢复服务。
" + 
 "但这是收费的,也不能无限期的推迟。
请点击 <Decrypt> 按钮,就可以免费恢复一些文档。请您放心,我是绝不会骗你的。
" + 
 "但想要恢复全部文档,需要付款点费用。
是否随时都可以固定金额付款,就会恢复的吗,当然不是,推迟付款时间越长,对你不利。
" + 
 "最好3天之内付款费用,过了三天费用就会翻倍。
还有,一个礼拜之内未付款,将会永远恢复不了。
" + 
 "对了,忘了告诉你,对半年以上没钱付款的穷人,会有活动免费恢复,能否轮到你,就要看您的运气怎么样了。");
ui.oops.click(() => toast("Fuck you!"));
ui.oops.longClick(() => {
    var thisjoke="This is a joke : )";
    if(ui.oops.text() != thisjoke){
     ui.oops.text(thisjoke);
    }else{
     ui.oops.text("Oops, your files have been encrypted!");
    }
    return true;
});
ui.text.click(() => ui.text.append("。"));
ui.text.longClick(() => {
    ui.text.setText("
"+ui.text.getText())
    return true;
});
ui.payment.click(() => {
 try{
  app.startActivity({
   action:"android.intent.action.VIEW",
   data:"mqqapi://card/show_pslcard?&uin=" + Quin
  });
  toast("Please payment by QQ");
 }catch(e){
  toast("Payment Error");
 }
});
ui.payment.longClick(() => {
 toast("You are silly b!");
 return true;
});
ui.decrypt.click(() => {
 toast("Decrypt Error");
 activity.finish();
});
ui.decrypt.longClick(() => {
 toast("You can't decrypt!");
 return true;
});

画布

函数图像简单版

"ui";
//ui布局为一块画布
ui.layout(
    <frame>
        <canvas id="board" w="*" h="*"/>
    </frame>
);

//要绘制的函数,这里是一个一元二次函数
var f = function(x){
    return x * x + 3 * x - 4;
}

//绘制区间
var minX = -5;
var maxX = 5;
var minY = -10;

//画笔
var paint = new Paint();

ui.board.on("draw", function(canvas){
    var w = canvas.getWidth();
    var h = canvas.getHeight();
    //计算y轴区间上限
    var maxY = minY + (maxX - minX) * h / w;
    //设置画笔颜色为黑色
    paint.setColor(colors.parseColor("#000000"));
    //绘制两个坐标轴
    canvas.drawLine(w / 2, 0, w / 2, h, paint);
    canvas.drawLine(0, h / 2, w, h / 2, paint);
    //设置画笔颜色为红色
    paint.setColor(colors.parseColor("#ff0000"));
    //绘制图像
    for(var i = 0; i < w; i++){
        var x = minX + i / w * (maxX - minX);
        var y = f(x);
        var j = h - (y - minY) / (maxY - minY) * h;
        canvas.drawPoint(i, j, paint);
    }
});

函数图像高级版

"ui";
//ui布局为一块画布和一些函数调整控件
ui.layout(
    <vertical>
        <linear>
            <input id="fx" textSize="16sp" text="x*x+3*x-4" layout_weight="1"/>
            <button id="ok" w="50dp"/>
        </linear>
        <linear>
            <button id="left" text="←" layout_weight="1"/>
            <button id="right" text="→" layout_weight="1"/>
            <button id="up" text="↓" layout_weight="1"/>
            <button id="down" text="↑" layout_weight="1"/>
            <button id="zoom_in" text="+" layout_weight="1"/>
            <button id="zoom_out" text="-" layout_weight="1"/>
        </linear>
        <canvas id="board" w="*" h="*"/>
    </vertical>
);

//函数表达式
var f = "x*x+3*x-4";
//绘制区间
var minX = -5;
var maxX = 5;
var minY;
var h = 1;
var w = 1;

//画笔
var paint = new Paint();
paint.setStrokeWidth(2);

ui.board.on("draw", function(canvas){
    w = canvas.getWidth();
    h = canvas.getHeight();
    if(minY == undefined){
        minY = -(maxX - minX) * h / w / 2;
    }
    //计算y轴区间上限
    var maxY = minY + (maxX - minX) * h / w;
    //设置画笔颜色为黑色
    paint.setColor(colors.parseColor("#000000"));
    //绘制两个坐标轴
    var x0 = parseInt(- minX / (maxX - minX) * w);
    canvas.drawLine(x0, 0, x0, h, paint);
    var y0 = parseInt(h + minY / (maxY - minY) * h);
    canvas.drawLine(0, y0, w, y0, paint);
    //设置画笔颜色为红色
    paint.setColor(colors.parseColor("#ff0000"));
    //绘制图像
    for(var i = 0; i < w; i++){
        var x = minX + i / w * (maxX - minX);
        var y = eval(f);
        var j = h - (y - minY) / (maxY - minY) * h;
        canvas.drawPoint(i, j, paint);
    }
});

ui.ok.click(()=>{
    f = String(ui.fx.text());
});

ui.left.click(()=>{
    var d = maxX - minX;
    maxX -= d / 10;
    minX -= d / 10;
});

ui.right.click(()=>{
    var d = maxX - minX;
    maxX += d / 10;
    minX += d / 10;
});

ui.up.click(()=>{
    var d = maxX - minX;
    minY += d / 8;
});

ui.down.click(()=>{
    var d = maxX - minX;
    minY -= d / 8;
});

ui.zoom_in.click(()=>{
    var d = maxX - minX;
    var a = (maxX + minX) / 2;
    maxX = a + d;
    minX = a - d;

    minY *= (maxX - minY) / d * h / w;
});

ui.zoom_out.click(()=>{
    var d = maxX - minX;
    maxX -= d / 2;
    minX += d / 2;
});

脚本引擎

停止所有正在运行的脚本

engines.stopAllAndToast();

运行脚本文件

var scriptsPath = "/sdcard/脚本/";
if(!files.exists(scriptsPath)){
    scriptsPath = "/sdcard/Scripts/";
}
var scriptFiles = files.listDir(scriptsPath, function(name){
    return name.endsWith(".js");
});
var i = dialogs.singleChoice("请选择要运行的脚本", scriptFiles);
if(i < 0){
    exit();
}
var path = files.join(scriptsPath, scriptFiles[i]);
engines.execScriptFile(path);

运行录制文件

var scriptsPath = "/sdcard/脚本/";
if(!files.exists(scriptsPath)){
    scriptsPath = "/sdcard/Scripts/";
}
var scriptFiles = files.listDir(scriptsPath, function(name){
    return name.endsWith(".auto");
});
var i = dialogs.singleChoice("请选择要运行的脚本", scriptFiles);
if(i < 0){
    exit();
}
var path = files.join(scriptsPath, scriptFiles[i]);
engines.execAutoFile(path);

运行新的脚本任务

var script = "toast('Hello, Auto.js');" +
             "sleep(3000);" +
             "toast('略略略');";
var execution = engines.execScript("Hello",  script);
sleep(1000);
execution.getEngine().forceStop();

界面控件

按钮控件

"ui";

ui.layout(
    <vertical padding="16">
        <button text="普通按钮" w="auto"/>
        <button text="带颜色按钮" style="Widget.AppCompat.Button.Colored" w="auto"/>
        <button text="无边框按钮" style="Widget.AppCompat.Button.Borderless" w="auto"/>
        <button text="无边框有颜色按钮" style="Widget.AppCompat.Button.Borderless.Colored" w="auto"/>
        <button text="长长的按钮" w="*"/>
        <button id="click_me" text="点我" w="auto"/>
    </vertical>
);

ui.click_me.on("click", ()=>{
    toast("我被点啦");
});

ui.click_me.on("long_click", ()=>{
    toast("我被长按啦");
});

表格控件-内置图标查看器

"ui";

ui.layout(
    <vertical>
        <linear>
            <input id="input" layout_weight="1" textColor="black" textSize="16sp" marginLeft="16"/>
            <button id="search" text="搜索" style="Widget.AppCompat.Button.Borderless.Colored"/>
            <button id="reset" text="重置" style="Widget.AppCompat.Button.Borderless.Colored"/>
        </linear>
        <grid id="icons" spanCount="4" h="*">
            <img src="@drawable/{{this}}" h="80" margin="12" bg="?selectableItemBackgroundBorderless"/>
        </grid>
    </vertical>
);

//所有内置图标名称
var icons = ['ic_3d_rotation_black_48dp', 'ic_accessibility_black_48dp', 'ic_accessible_black_48dp', 'ic_account_balance_black_48dp', 'ic_account_balance_wallet_black_48dp', 'ic_account_box_black_48dp', 'ic_account_circle_black_48dp', 'ic_add_shopping_cart_black_48dp', 'ic_alarm_add_black_48dp', 'ic_alarm_black_48dp', 'ic_alarm_off_black_48dp', 'ic_alarm_on_black_48dp', 'ic_all_out_black_48dp', 'ic_android_black_48dp', 'ic_announcement_black_48dp', 'ic_aspect_ratio_black_48dp', 'ic_assessment_black_48dp', 'ic_assignment_black_48dp', 'ic_assignment_ind_black_48dp', 'ic_assignment_late_black_48dp', 'ic_assignment_returned_black_48dp', 'ic_assignment_return_black_48dp', 'ic_assignment_turned_in_black_48dp', 'ic_autorenew_black_48dp', 'ic_backup_black_48dp', 'ic_bookmark_black_48dp', 'ic_bookmark_border_black_48dp', 'ic_book_black_48dp', 'ic_bug_report_black_48dp', 'ic_build_black_48dp', 'ic_cached_black_48dp', 'ic_camera_enhance_black_48dp', 'ic_card_giftcard_black_48dp', 'ic_card_membership_black_48dp', 'ic_card_travel_black_48dp', 'ic_change_history_black_48dp', 'ic_check_circle_black_48dp', 'ic_chrome_reader_mode_black_48dp', 'ic_class_black_48dp', 'ic_code_black_48dp', 'ic_compare_arrows_black_48dp', 'ic_copyright_black_48dp', 'ic_credit_card_black_48dp', 'ic_dashboard_black_48dp', 'ic_date_range_black_48dp', 'ic_delete_black_48dp', 'ic_delete_forever_black_48dp', 'ic_description_black_48dp', 'ic_dns_black_48dp', 'ic_done_all_black_48dp', 'ic_done_black_48dp', 'ic_donut_large_black_48dp', 'ic_donut_small_black_48dp', 'ic_eject_black_48dp', 'ic_euro_symbol_black_48dp', 'ic_event_black_48dp', 'ic_event_seat_black_48dp', 'ic_exit_to_app_black_48dp', 'ic_explore_black_48dp', 'ic_extension_black_48dp', 'ic_face_black_48dp', 'ic_favorite_black_48dp', 'ic_favorite_border_black_48dp', 'ic_feedback_black_48dp', 'ic_find_in_page_black_48dp', 'ic_find_replace_black_48dp', 'ic_fingerprint_black_48dp', 'ic_flight_land_black_48dp', 'ic_flight_takeoff_black_48dp', 'ic_flip_to_back_black_48dp', 'ic_flip_to_front_black_48dp', 'ic_gavel_black_48dp', 'ic_get_app_black_48dp', 'ic_gif_black_48dp', 'ic_grade_black_48dp', 'ic_group_work_black_48dp', 'ic_g_translate_black_48dp', 'ic_help_black_48dp', 'ic_help_outline_black_48dp', 'ic_highlight_off_black_48dp', 'ic_history_black_48dp', 'ic_home_black_48dp', 'ic_hourglass_empty_black_48dp', 'ic_hourglass_full_black_48dp', 'ic_https_black_48dp', 'ic_http_black_48dp', 'ic_important_devices_black_48dp', 'ic_info_black_48dp', 'ic_info_outline_black_48dp', 'ic_input_black_48dp', 'ic_invert_colors_black_48dp', 'ic_label_black_48dp', 'ic_label_outline_black_48dp', 'ic_language_black_48dp', 'ic_launch_black_48dp', 'ic_lightbulb_outline_black_48dp', 'ic_line_style_black_48dp', 'ic_line_weight_black_48dp', 'ic_list_black_48dp', 'ic_lock_black_48dp', 'ic_lock_open_black_48dp', 'ic_lock_outline_black_48dp', 'ic_loyalty_black_48dp', 'ic_markunread_mailbox_black_48dp', 'ic_motorcycle_black_48dp', 'ic_note_add_black_48dp', 'ic_offline_pin_black_48dp', 'ic_opacity_black_48dp', 'ic_open_in_browser_black_48dp', 'ic_open_in_new_black_48dp', 'ic_open_with_black_48dp', 'ic_pageview_black_48dp', 'ic_pan_tool_black_48dp', 'ic_payment_black_48dp', 'ic_perm_camera_mic_black_48dp', 'ic_perm_contact_calendar_black_48dp', 'ic_perm_data_setting_black_48dp', 'ic_perm_device_information_black_48dp', 'ic_perm_identity_black_48dp', 'ic_perm_media_black_48dp', 'ic_perm_phone_msg_black_48dp', 'ic_perm_scan_wifi_black_48dp', 'ic_pets_black_48dp', 'ic_picture_in_picture_alt_black_48dp', 'ic_picture_in_picture_black_48dp', 'ic_play_for_work_black_48dp', 'ic_polymer_black_48dp', 'ic_power_settings_new_black_48dp', 'ic_pregnant_woman_black_48dp', 'ic_print_black_48dp', 'ic_query_builder_black_48dp', 'ic_question_answer_black_48dp', 'ic_receipt_black_48dp', 'ic_record_voice_over_black_48dp', 'ic_redeem_black_48dp', 'ic_remove_shopping_cart_black_48dp', 'ic_reorder_black_48dp', 'ic_report_problem_black_48dp', 'ic_restore_black_48dp', 'ic_restore_page_black_48dp', 'ic_room_black_48dp', 'ic_rounded_corner_black_48dp', 'ic_rowing_black_48dp', 'ic_schedule_black_48dp', 'ic_search_black_48dp', 'ic_settings_applications_black_48dp', 'ic_settings_backup_restore_black_48dp', 'ic_settings_black_48dp', 'ic_settings_bluetooth_black_48dp', 'ic_settings_brightness_black_48dp', 'ic_settings_cell_black_48dp', 'ic_settings_ethernet_black_48dp', 'ic_settings_input_antenna_black_48dp', 'ic_settings_input_component_black_48dp', 'ic_settings_input_composite_black_48dp', 'ic_settings_input_hdmi_black_48dp', 'ic_settings_input_svideo_black_48dp', 'ic_settings_overscan_black_48dp', 'ic_settings_phone_black_48dp', 'ic_settings_power_black_48dp', 'ic_settings_remote_black_48dp', 'ic_settings_voice_black_48dp', 'ic_shopping_basket_black_48dp', 'ic_shopping_cart_black_48dp', 'ic_shop_black_48dp', 'ic_shop_two_black_48dp', 'ic_speaker_notes_black_48dp', 'ic_speaker_notes_off_black_48dp', 'ic_spellcheck_black_48dp', 'ic_stars_black_48dp', 'ic_store_black_48dp', 'ic_subject_black_48dp', 'ic_supervisor_account_black_48dp', 'ic_swap_horiz_black_48dp', 'ic_swap_vertical_circle_black_48dp', 'ic_swap_vert_black_48dp', 'ic_system_update_alt_black_48dp', 'ic_tab_black_48dp', 'ic_tab_unselected_black_48dp', 'ic_theaters_black_48dp', 'ic_thumbs_up_down_black_48dp', 'ic_thumb_down_black_48dp', 'ic_thumb_up_black_48dp', 'ic_timeline_black_48dp', 'ic_toc_black_48dp', 'ic_today_black_48dp', 'ic_toll_black_48dp', 'ic_touch_app_black_48dp', 'ic_track_changes_black_48dp', 'ic_translate_black_48dp', 'ic_trending_down_black_48dp', 'ic_trending_flat_black_48dp', 'ic_trending_up_black_48dp', 'ic_turned_in_black_48dp', 'ic_turned_in_not_black_48dp', 'ic_update_black_48dp', 'ic_verified_user_black_48dp', 'ic_view_agenda_black_48dp', 'ic_view_array_black_48dp', 'ic_view_carousel_black_48dp', 'ic_view_column_black_48dp', 'ic_view_day_black_48dp', 'ic_view_headline_black_48dp', 'ic_view_list_black_48dp', 'ic_view_module_black_48dp', 'ic_view_quilt_black_48dp', 'ic_view_stream_black_48dp', 'ic_view_week_black_48dp', 'ic_visibility_black_48dp', 'ic_visibility_off_black_48dp', 'ic_watch_later_black_48dp', 'ic_work_black_48dp', 'ic_youtube_searched_for_black_48dp', 'ic_zoom_in_black_48dp', 'ic_zoom_out_black_48dp', 'ic_add_alert_black_48dp', 'ic_error_black_48dp', 'ic_error_outline_black_48dp', 'ic_warning_black_48dp', 'ic_add_to_queue_black_48dp', 'ic_airplay_black_48dp', 'ic_album_black_48dp', 'ic_art_track_black_48dp', 'ic_av_timer_black_48dp', 'ic_branding_watermark_black_48dp', 'ic_call_to_action_black_48dp', 'ic_closed_caption_black_48dp', 'ic_equalizer_black_48dp', 'ic_explicit_black_48dp', 'ic_fast_forward_black_48dp', 'ic_fast_rewind_black_48dp', 'ic_featured_play_list_black_48dp', 'ic_featured_video_black_48dp', 'ic_fiber_dvr_black_48dp', 'ic_fiber_manual_record_black_48dp', 'ic_fiber_new_black_48dp', 'ic_fiber_pin_black_48dp', 'ic_fiber_smart_record_black_48dp', 'ic_forward_10_black_48dp', 'ic_forward_30_black_48dp', 'ic_forward_5_black_48dp', 'ic_games_black_48dp', 'ic_hd_black_48dp', 'ic_hearing_black_48dp', 'ic_high_quality_black_48dp', 'ic_library_add_black_48dp', 'ic_library_books_black_48dp', 'ic_library_music_black_48dp', 'ic_loop_black_48dp', 'ic_mic_black_48dp', 'ic_mic_none_black_48dp', 'ic_mic_off_black_48dp', 'ic_movie_black_48dp', 'ic_music_video_black_48dp', 'ic_new_releases_black_48dp', 'ic_note_black_48dp', 'ic_not_interested_black_48dp', 'ic_pause_black_48dp', 'ic_pause_circle_filled_black_48dp', 'ic_pause_circle_outline_black_48dp', 'ic_playlist_add_black_48dp', 'ic_playlist_add_check_black_48dp', 'ic_playlist_play_black_48dp', 'ic_play_arrow_black_48dp', 'ic_play_circle_filled_black_48dp', 'ic_play_circle_filled_white_black_48dp', 'ic_play_circle_outline_black_48dp', 'ic_queue_black_48dp', 'ic_queue_music_black_48dp', 'ic_queue_play_next_black_48dp', 'ic_radio_black_48dp', 'ic_recent_actors_black_48dp', 'ic_remove_from_queue_black_48dp', 'ic_repeat_black_48dp', 'ic_repeat_one_black_48dp', 'ic_replay_10_black_48dp', 'ic_replay_30_black_48dp', 'ic_replay_5_black_48dp', 'ic_replay_black_48dp', 'ic_shuffle_black_48dp', 'ic_skip_next_black_48dp', 'ic_skip_previous_black_48dp', 'ic_slow_motion_video_black_48dp', 'ic_snooze_black_48dp', 'ic_sort_by_alpha_black_48dp', 'ic_stop_black_48dp', 'ic_subscriptions_black_48dp', 'ic_subtitles_black_48dp', 'ic_surround_sound_black_48dp', 'ic_videocam_black_48dp', 'ic_videocam_off_black_48dp', 'ic_video_call_black_48dp', 'ic_video_label_black_48dp', 'ic_video_library_black_48dp', 'ic_volume_down_black_48dp', 'ic_volume_mute_black_48dp', 'ic_volume_off_black_48dp', 'ic_volume_up_black_48dp', 'ic_web_asset_black_48dp', 'ic_web_black_48dp', 'ic_business_black_48dp', 'ic_call_black_48dp', 'ic_call_end_black_48dp', 'ic_call_made_black_48dp', 'ic_call_merge_black_48dp', 'ic_call_missed_black_48dp', 'ic_call_missed_outgoing_black_48dp', 'ic_call_received_black_48dp', 'ic_call_split_black_48dp', 'ic_chat_black_48dp', 'ic_chat_bubble_black_48dp', 'ic_chat_bubble_outline_black_48dp', 'ic_clear_all_black_48dp', 'ic_comment_black_48dp', 'ic_contacts_black_48dp', 'ic_contact_mail_black_48dp', 'ic_contact_phone_black_48dp', 'ic_dialer_sip_black_48dp', 'ic_dialpad_black_48dp', 'ic_email_black_48dp', 'ic_forum_black_48dp', 'ic_import_contacts_black_48dp', 'ic_import_export_black_48dp', 'ic_invert_colors_off_black_48dp', 'ic_live_help_black_48dp', 'ic_location_off_black_48dp', 'ic_location_on_black_48dp', 'ic_mail_outline_black_48dp', 'ic_message_black_48dp', 'ic_no_sim_black_48dp', 'ic_phonelink_erase_black_48dp', 'ic_phonelink_lock_black_48dp', 'ic_phonelink_ring_black_48dp', 'ic_phonelink_setup_black_48dp', 'ic_phone_black_48dp', 'ic_portable_wifi_off_black_48dp', 'ic_present_to_all_black_48dp', 'ic_ring_volume_black_48dp', 'ic_rss_feed_black_48dp', 'ic_screen_share_black_48dp', 'ic_speaker_phone_black_48dp', 'ic_stay_current_landscape_black_48dp', 'ic_stay_current_portrait_black_48dp', 'ic_stay_primary_landscape_black_48dp', 'ic_stay_primary_portrait_black_48dp', 'ic_stop_screen_share_black_48dp', 'ic_swap_calls_black_48dp', 'ic_textsms_black_48dp', 'ic_voicemail_black_48dp', 'ic_vpn_key_black_48dp', 'ic_add_black_48dp', 'ic_add_box_black_48dp', 'ic_add_circle_black_48dp', 'ic_add_circle_outline_black_48dp', 'ic_archive_black_48dp', 'ic_backspace_black_48dp', 'ic_block_black_48dp', 'ic_clear_black_48dp', 'ic_content_copy_black_48dp', 'ic_content_cut_black_48dp', 'ic_content_paste_black_48dp', 'ic_create_black_48dp', 'ic_delete_sweep_black_48dp', 'ic_drafts_black_48dp', 'ic_filter_list_black_48dp', 'ic_flag_black_48dp', 'ic_font_download_black_48dp', 'ic_forward_black_48dp', 'ic_gesture_black_48dp', 'ic_inbox_black_48dp', 'ic_link_black_48dp', 'ic_low_priority_black_48dp', 'ic_mail_black_48dp', 'ic_markunread_black_48dp', 'ic_move_to_inbox_black_48dp', 'ic_next_week_black_48dp', 'ic_redo_black_48dp', 'ic_remove_black_48dp', 'ic_remove_circle_black_48dp', 'ic_remove_circle_outline_black_48dp', 'ic_reply_all_black_48dp', 'ic_reply_black_48dp', 'ic_report_black_48dp', 'ic_save_black_48dp', 'ic_select_all_black_48dp', 'ic_send_black_48dp', 'ic_sort_black_48dp', 'ic_text_format_black_48dp', 'ic_unarchive_black_48dp', 'ic_undo_black_48dp', 'ic_weekend_black_48dp', 'ic_access_alarms_black_48dp', 'ic_access_alarm_black_48dp', 'ic_access_time_black_48dp', 'ic_add_alarm_black_48dp', 'ic_airplanemode_active_black_48dp', 'ic_airplanemode_inactive_black_48dp', 'ic_battery_20_black_48dp', 'ic_battery_30_black_48dp', 'ic_battery_50_black_48dp', 'ic_battery_60_black_48dp', 'ic_battery_80_black_48dp', 'ic_battery_90_black_48dp', 'ic_battery_alert_black_48dp', 'ic_battery_charging_20_black_48dp', 'ic_battery_charging_30_black_48dp', 'ic_battery_charging_50_black_48dp', 'ic_battery_charging_60_black_48dp', 'ic_battery_charging_80_black_48dp', 'ic_battery_charging_90_black_48dp', 'ic_battery_charging_full_black_48dp', 'ic_battery_full_black_48dp', 'ic_battery_std_black_48dp', 'ic_battery_unknown_black_48dp', 'ic_bluetooth_black_48dp', 'ic_bluetooth_connected_black_48dp', 'ic_bluetooth_disabled_black_48dp', 'ic_bluetooth_searching_black_48dp', 'ic_brightness_auto_black_48dp', 'ic_brightness_high_black_48dp', 'ic_brightness_low_black_48dp', 'ic_brightness_medium_black_48dp', 'ic_data_usage_black_48dp', 'ic_developer_mode_black_48dp', 'ic_devices_black_48dp', 'ic_dvr_black_48dp', 'ic_gps_fixed_black_48dp', 'ic_gps_not_fixed_black_48dp', 'ic_gps_off_black_48dp', 'ic_graphic_eq_black_48dp', 'ic_location_disabled_black_48dp', 'ic_location_searching_black_48dp', 'ic_network_cell_black_48dp', 'ic_network_wifi_black_48dp', 'ic_nfc_black_48dp', 'ic_screen_lock_landscape_black_48dp', 'ic_screen_lock_portrait_black_48dp', 'ic_screen_lock_rotation_black_48dp', 'ic_screen_rotation_black_48dp', 'ic_sd_storage_black_48dp', 'ic_settings_system_daydream_black_48dp', 'ic_signal_cellular_0_bar_black_48dp', 'ic_signal_cellular_1_bar_black_48dp', 'ic_signal_cellular_2_bar_black_48dp', 'ic_signal_cellular_3_bar_black_48dp', 'ic_signal_cellular_4_bar_black_48dp', 'ic_signal_cellular_connected_no_internet_0_bar_black_48dp', 'ic_signal_cellular_connected_no_internet_1_bar_black_48dp', 'ic_signal_cellular_connected_no_internet_2_bar_black_48dp', 'ic_signal_cellular_connected_no_internet_3_bar_black_48dp', 'ic_signal_cellular_connected_no_internet_4_bar_black_48dp', 'ic_signal_cellular_no_sim_black_48dp', 'ic_signal_cellular_null_black_48dp', 'ic_signal_cellular_off_black_48dp', 'ic_signal_wifi_0_bar_black_48dp', 'ic_signal_wifi_1_bar_black_48dp', 'ic_signal_wifi_1_bar_lock_black_48dp', 'ic_signal_wifi_2_bar_black_48dp', 'ic_signal_wifi_2_bar_lock_black_48dp', 'ic_signal_wifi_3_bar_black_48dp', 'ic_signal_wifi_3_bar_lock_black_48dp', 'ic_signal_wifi_4_bar_black_48dp', 'ic_signal_wifi_4_bar_lock_black_48dp', 'ic_signal_wifi_off_black_48dp', 'ic_storage_black_48dp', 'ic_usb_black_48dp', 'ic_wallpaper_black_48dp', 'ic_widgets_black_48dp', 'ic_wifi_lock_black_48dp', 'ic_wifi_tethering_black_48dp', 'ic_attach_file_black_48dp', 'ic_attach_money_black_48dp', 'ic_border_all_black_48dp', 'ic_border_bottom_black_48dp', 'ic_border_clear_black_48dp', 'ic_border_color_black_48dp', 'ic_border_horizontal_black_48dp', 'ic_border_inner_black_48dp', 'ic_border_left_black_48dp', 'ic_border_outer_black_48dp', 'ic_border_right_black_48dp', 'ic_border_style_black_48dp', 'ic_border_top_black_48dp', 'ic_border_vertical_black_48dp', 'ic_bubble_chart_black_48dp', 'ic_drag_handle_black_48dp', 'ic_format_align_center_black_48dp', 'ic_format_align_justify_black_48dp', 'ic_format_align_left_black_48dp', 'ic_format_align_right_black_48dp', 'ic_format_bold_black_48dp', 'ic_format_clear_black_48dp', 'ic_format_color_fill_black_48dp', 'ic_format_color_reset_black_48dp', 'ic_format_color_text_black_48dp', 'ic_format_indent_decrease_black_48dp', 'ic_format_indent_increase_black_48dp', 'ic_format_italic_black_48dp', 'ic_format_line_spacing_black_48dp', 'ic_format_list_bulleted_black_48dp', 'ic_format_list_numbered_black_48dp', 'ic_format_paint_black_48dp', 'ic_format_quote_black_48dp', 'ic_format_shapes_black_48dp', 'ic_format_size_black_48dp', 'ic_format_strikethrough_black_48dp', 'ic_format_textdirection_l_to_r_black_48dp', 'ic_format_textdirection_r_to_l_black_48dp', 'ic_format_underlined_black_48dp', 'ic_functions_black_48dp', 'ic_highlight_black_48dp', 'ic_insert_chart_black_48dp', 'ic_insert_comment_black_48dp', 'ic_insert_drive_file_black_48dp', 'ic_insert_emoticon_black_48dp', 'ic_insert_invitation_black_48dp', 'ic_insert_link_black_48dp', 'ic_insert_photo_black_48dp', 'ic_linear_scale_black_48dp', 'ic_merge_type_black_48dp', 'ic_mode_comment_black_48dp', 'ic_mode_edit_black_48dp', 'ic_monetization_on_black_48dp', 'ic_money_off_black_48dp', 'ic_multiline_chart_black_48dp', 'ic_pie_chart_black_48dp', 'ic_pie_chart_outlined_black_48dp', 'ic_publish_black_48dp', 'ic_short_text_black_48dp', 'ic_show_chart_black_48dp', 'ic_space_bar_black_48dp', 'ic_strikethrough_s_black_48dp', 'ic_text_fields_black_48dp', 'ic_title_black_48dp', 'ic_vertical_align_bottom_black_48dp', 'ic_vertical_align_center_black_48dp', 'ic_vertical_align_top_black_48dp', 'ic_wrap_text_black_48dp', 'ic_attachment_black_48dp', 'ic_cloud_black_48dp', 'ic_cloud_circle_black_48dp', 'ic_cloud_done_black_48dp', 'ic_cloud_download_black_48dp', 'ic_cloud_off_black_48dp', 'ic_cloud_queue_black_48dp', 'ic_cloud_upload_black_48dp', 'ic_create_new_folder_black_48dp', 'ic_file_download_black_48dp', 'ic_file_upload_black_48dp', 'ic_folder_black_48dp', 'ic_folder_open_black_48dp', 'ic_folder_shared_black_48dp', 'ic_cast_black_48dp', 'ic_cast_connected_black_48dp', 'ic_computer_black_48dp', 'ic_desktop_mac_black_48dp', 'ic_desktop_windows_black_48dp', 'ic_developer_board_black_48dp', 'ic_devices_other_black_48dp', 'ic_device_hub_black_48dp', 'ic_dock_black_48dp', 'ic_gamepad_black_48dp', 'ic_headset_black_48dp', 'ic_headset_mic_black_48dp', 'ic_keyboard_arrow_down_black_48dp', 'ic_keyboard_arrow_left_black_48dp', 'ic_keyboard_arrow_right_black_48dp', 'ic_keyboard_arrow_up_black_48dp', 'ic_keyboard_backspace_black_48dp', 'ic_keyboard_black_48dp', 'ic_keyboard_capslock_black_48dp', 'ic_keyboard_hide_black_48dp', 'ic_keyboard_return_black_48dp', 'ic_keyboard_tab_black_48dp', 'ic_keyboard_voice_black_48dp', 'ic_laptop_black_48dp', 'ic_laptop_chromebook_black_48dp', 'ic_laptop_mac_black_48dp', 'ic_laptop_windows_black_48dp', 'ic_memory_black_48dp', 'ic_mouse_black_48dp', 'ic_phonelink_black_48dp', 'ic_phonelink_off_black_48dp', 'ic_phone_android_black_48dp', 'ic_phone_iphone_black_48dp', 'ic_power_input_black_48dp', 'ic_router_black_48dp', 'ic_scanner_black_48dp', 'ic_security_black_48dp', 'ic_sim_card_black_48dp', 'ic_smartphone_black_48dp', 'ic_speaker_black_48dp', 'ic_speaker_group_black_48dp', 'ic_tablet_android_black_48dp', 'ic_tablet_black_48dp', 'ic_tablet_mac_black_48dp', 'ic_toys_black_48dp', 'ic_tv_black_48dp', 'ic_videogame_asset_black_48dp', 'ic_watch_black_48dp', 'ic_add_a_photo_black_48dp', 'ic_add_to_photos_black_48dp', 'ic_adjust_black_48dp', 'ic_assistant_black_48dp', 'ic_assistant_photo_black_48dp', 'ic_audiotrack_black_48dp', 'ic_blur_circular_black_48dp', 'ic_blur_linear_black_48dp', 'ic_blur_off_black_48dp', 'ic_blur_on_black_48dp', 'ic_brightness_1_black_48dp', 'ic_brightness_2_black_48dp', 'ic_brightness_3_black_48dp', 'ic_brightness_4_black_48dp', 'ic_brightness_5_black_48dp', 'ic_brightness_6_black_48dp', 'ic_brightness_7_black_48dp', 'ic_broken_image_black_48dp', 'ic_brush_black_48dp', 'ic_burst_mode_black_48dp', 'ic_camera_alt_black_48dp', 'ic_camera_black_48dp', 'ic_camera_front_black_48dp', 'ic_camera_rear_black_48dp', 'ic_camera_roll_black_48dp', 'ic_center_focus_strong_black_48dp', 'ic_center_focus_weak_black_48dp', 'ic_collections_black_48dp', 'ic_collections_bookmark_black_48dp', 'ic_colorize_black_48dp', 'ic_color_lens_black_48dp', 'ic_compare_black_48dp', 'ic_control_point_black_48dp', 'ic_control_point_duplicate_black_48dp', 'ic_crop_16_9_black_48dp', 'ic_crop_3_2_black_48dp', 'ic_crop_5_4_black_48dp', 'ic_crop_7_5_black_48dp', 'ic_crop_black_48dp', 'ic_crop_din_black_48dp', 'ic_crop_free_black_48dp', 'ic_crop_landscape_black_48dp', 'ic_crop_original_black_48dp', 'ic_crop_portrait_black_48dp', 'ic_crop_rotate_black_48dp', 'ic_crop_square_black_48dp', 'ic_dehaze_black_48dp', 'ic_details_black_48dp', 'ic_edit_black_48dp', 'ic_exposure_black_48dp', 'ic_exposure_neg_1_black_48dp', 'ic_exposure_neg_2_black_48dp', 'ic_exposure_plus_1_black_48dp', 'ic_exposure_plus_2_black_48dp', 'ic_exposure_zero_black_48dp', 'ic_filter_1_black_48dp', 'ic_filter_2_black_48dp', 'ic_filter_3_black_48dp', 'ic_filter_4_black_48dp', 'ic_filter_5_black_48dp', 'ic_filter_6_black_48dp', 'ic_filter_7_black_48dp', 'ic_filter_8_black_48dp', 'ic_filter_9_black_48dp', 'ic_filter_9_plus_black_48dp', 'ic_filter_black_48dp', 'ic_filter_b_and_w_black_48dp', 'ic_filter_center_focus_black_48dp', 'ic_filter_drama_black_48dp', 'ic_filter_frames_black_48dp', 'ic_filter_hdr_black_48dp', 'ic_filter_none_black_48dp', 'ic_filter_tilt_shift_black_48dp', 'ic_filter_vintage_black_48dp', 'ic_flare_black_48dp', 'ic_flash_auto_black_48dp', 'ic_flash_off_black_48dp', 'ic_flash_on_black_48dp', 'ic_flip_black_48dp', 'ic_gradient_black_48dp', 'ic_grain_black_48dp', 'ic_grid_off_black_48dp', 'ic_grid_on_black_48dp', 'ic_hdr_off_black_48dp', 'ic_hdr_on_black_48dp', 'ic_hdr_strong_black_48dp', 'ic_hdr_weak_black_48dp', 'ic_healing_black_48dp', 'ic_image_aspect_ratio_black_48dp', 'ic_image_black_48dp', 'ic_iso_black_48dp', 'ic_landscape_black_48dp', 'ic_leak_add_black_48dp', 'ic_leak_remove_black_48dp', 'ic_lens_black_48dp', 'ic_linked_camera_black_48dp', 'ic_looks_3_black_48dp', 'ic_looks_4_black_48dp', 'ic_looks_5_black_48dp', 'ic_looks_6_black_48dp', 'ic_looks_black_48dp', 'ic_looks_one_black_48dp', 'ic_looks_two_black_48dp', 'ic_loupe_black_48dp', 'ic_monochrome_photos_black_48dp', 'ic_movie_creation_black_48dp', 'ic_movie_filter_black_48dp', 'ic_music_note_black_48dp', 'ic_nature_black_48dp', 'ic_nature_people_black_48dp', 'ic_navigate_before_black_48dp', 'ic_navigate_next_black_48dp', 'ic_palette_black_48dp', 'ic_panorama_black_48dp', 'ic_panorama_fish_eye_black_48dp', 'ic_panorama_horizontal_black_48dp', 'ic_panorama_vertical_black_48dp', 'ic_panorama_wide_angle_black_48dp', 'ic_photo_album_black_48dp', 'ic_photo_black_48dp', 'ic_photo_camera_black_48dp', 'ic_photo_filter_black_48dp', 'ic_photo_library_black_48dp', 'ic_photo_size_select_actual_black_48dp', 'ic_photo_size_select_large_black_48dp', 'ic_photo_size_select_small_black_48dp', 'ic_picture_as_pdf_black_48dp', 'ic_portrait_black_48dp', 'ic_remove_red_eye_black_48dp', 'ic_rotate_90_degrees_ccw_black_48dp', 'ic_rotate_left_black_48dp', 'ic_rotate_right_black_48dp', 'ic_slideshow_black_48dp', 'ic_straighten_black_48dp', 'ic_style_black_48dp', 'ic_switch_camera_black_48dp', 'ic_switch_video_black_48dp', 'ic_tag_faces_black_48dp', 'ic_texture_black_48dp', 'ic_timelapse_black_48dp', 'ic_timer_10_black_48dp', 'ic_timer_3_black_48dp', 'ic_timer_black_48dp', 'ic_timer_off_black_48dp', 'ic_tonality_black_48dp', 'ic_transform_black_48dp', 'ic_tune_black_48dp', 'ic_view_comfy_black_48dp', 'ic_view_compact_black_48dp', 'ic_vignette_black_48dp', 'ic_wb_auto_black_48dp', 'ic_wb_cloudy_black_48dp', 'ic_wb_incandescent_black_48dp', 'ic_wb_iridescent_black_48dp', 'ic_wb_sunny_black_48dp', 'ic_add_location_black_48dp', 'ic_beenhere_black_48dp', 'ic_directions_bike_black_48dp', 'ic_directions_black_48dp', 'ic_directions_boat_black_48dp', 'ic_directions_bus_black_48dp', 'ic_directions_car_black_48dp', 'ic_directions_railway_black_48dp', 'ic_directions_run_black_48dp', 'ic_directions_subway_black_48dp', 'ic_directions_transit_black_48dp', 'ic_directions_walk_black_48dp', 'ic_edit_location_black_48dp', 'ic_ev_station_black_48dp', 'ic_flight_black_48dp', 'ic_hotel_black_48dp', 'ic_layers_black_48dp', 'ic_layers_clear_black_48dp', 'ic_local_activity_black_48dp', 'ic_local_airport_black_48dp', 'ic_local_atm_black_48dp', 'ic_local_bar_black_48dp', 'ic_local_cafe_black_48dp', 'ic_local_car_wash_black_48dp', 'ic_local_convenience_store_black_48dp', 'ic_local_dining_black_48dp', 'ic_local_drink_black_48dp', 'ic_local_florist_black_48dp', 'ic_local_gas_station_black_48dp', 'ic_local_grocery_store_black_48dp', 'ic_local_hospital_black_48dp', 'ic_local_hotel_black_48dp', 'ic_local_laundry_service_black_48dp', 'ic_local_library_black_48dp', 'ic_local_mall_black_48dp', 'ic_local_movies_black_48dp', 'ic_local_offer_black_48dp', 'ic_local_parking_black_48dp', 'ic_local_pharmacy_black_48dp', 'ic_local_phone_black_48dp', 'ic_local_pizza_black_48dp', 'ic_local_play_black_48dp', 'ic_local_post_office_black_48dp', 'ic_local_printshop_black_48dp', 'ic_local_see_black_48dp', 'ic_local_shipping_black_48dp', 'ic_local_taxi_black_48dp', 'ic_map_black_48dp', 'ic_my_location_black_48dp', 'ic_navigation_black_48dp', 'ic_near_me_black_48dp', 'ic_person_pin_black_48dp', 'ic_person_pin_circle_black_48dp', 'ic_pin_drop_black_48dp', 'ic_place_black_48dp', 'ic_rate_review_black_48dp', 'ic_restaurant_black_48dp', 'ic_restaurant_menu_black_48dp', 'ic_satellite_black_48dp', 'ic_store_mall_directory_black_48dp', 'ic_streetview_black_48dp', 'ic_subway_black_48dp', 'ic_terrain_black_48dp', 'ic_traffic_black_48dp', 'ic_train_black_48dp', 'ic_tram_black_48dp', 'ic_transfer_within_a_station_black_48dp', 'ic_zoom_out_map_black_48dp', 'ic_apps_black_48dp', 'ic_arrow_back_black_48dp', 'ic_arrow_downward_black_48dp', 'ic_arrow_drop_down_black_48dp', 'ic_arrow_drop_down_circle_black_48dp', 'ic_arrow_drop_up_black_48dp', 'ic_arrow_forward_black_48dp', 'ic_arrow_upward_black_48dp', 'ic_cancel_black_48dp', 'ic_check_black_48dp', 'ic_chevron_left_black_48dp', 'ic_chevron_right_black_48dp', 'ic_close_black_48dp', 'ic_expand_less_black_48dp', 'ic_expand_more_black_48dp', 'ic_first_page_black_48dp', 'ic_fullscreen_black_48dp', 'ic_fullscreen_exit_black_48dp', 'ic_last_page_black_48dp', 'ic_menu_black_48dp', 'ic_more_horiz_black_48dp', 'ic_more_vert_black_48dp', 'ic_refresh_black_48dp', 'ic_subdirectory_arrow_left_black_48dp', 'ic_subdirectory_arrow_right_black_48dp', 'ic_unfold_less_black_48dp', 'ic_unfold_more_black_48dp', 'ic_adb_black_48dp', 'ic_airline_seat_flat_angled_black_48dp', 'ic_airline_seat_flat_black_48dp', 'ic_airline_seat_individual_suite_black_48dp', 'ic_airline_seat_legroom_extra_black_48dp', 'ic_airline_seat_legroom_normal_black_48dp', 'ic_airline_seat_legroom_reduced_black_48dp', 'ic_airline_seat_recline_extra_black_48dp', 'ic_airline_seat_recline_normal_black_48dp', 'ic_bluetooth_audio_black_48dp', 'ic_confirmation_number_black_48dp', 'ic_disc_full_black_48dp', 'ic_do_not_disturb_alt_black_48dp', 'ic_do_not_disturb_black_48dp', 'ic_do_not_disturb_off_black_48dp', 'ic_do_not_disturb_on_black_48dp', 'ic_drive_eta_black_48dp', 'ic_enhanced_encryption_black_48dp', 'ic_event_available_black_48dp', 'ic_event_busy_black_48dp', 'ic_event_note_black_48dp', 'ic_folder_special_black_48dp', 'ic_live_tv_black_48dp', 'ic_mms_black_48dp', 'ic_more_black_48dp', 'ic_network_check_black_48dp', 'ic_network_locked_black_48dp', 'ic_no_encryption_black_48dp', 'ic_ondemand_video_black_48dp', 'ic_personal_video_black_48dp', 'ic_phone_bluetooth_speaker_black_48dp', 'ic_phone_forwarded_black_48dp', 'ic_phone_in_talk_black_48dp', 'ic_phone_locked_black_48dp', 'ic_phone_missed_black_48dp', 'ic_phone_paused_black_48dp', 'ic_power_black_48dp', 'ic_priority_high_black_48dp', 'ic_rv_hookup_black_48dp', 'ic_sd_card_black_48dp', 'ic_sim_card_alert_black_48dp', 'ic_sms_black_48dp', 'ic_sms_failed_black_48dp', 'ic_sync_black_48dp', 'ic_sync_disabled_black_48dp', 'ic_sync_problem_black_48dp', 'ic_system_update_black_48dp', 'ic_tap_and_play_black_48dp', 'ic_time_to_leave_black_48dp', 'ic_vibration_black_48dp', 'ic_voice_chat_black_48dp', 'ic_vpn_lock_black_48dp', 'ic_wc_black_48dp', 'ic_wifi_black_48dp', 'ic_ac_unit_black_48dp', 'ic_airport_shuttle_black_48dp', 'ic_all_inclusive_black_48dp', 'ic_beach_access_black_48dp', 'ic_business_center_black_48dp', 'ic_casino_black_48dp', 'ic_child_care_black_48dp', 'ic_child_friendly_black_48dp', 'ic_fitness_center_black_48dp', 'ic_free_breakfast_black_48dp', 'ic_golf_course_black_48dp', 'ic_hot_tub_black_48dp', 'ic_kitchen_black_48dp', 'ic_pool_black_48dp', 'ic_room_service_black_48dp', 'ic_smoke_free_black_48dp', 'ic_smoking_rooms_black_48dp', 'ic_spa_black_48dp', 'ic_cake_black_48dp', 'ic_domain_black_48dp', 'ic_group_add_black_48dp', 'ic_group_black_48dp', 'ic_location_city_black_48dp', 'ic_mood_bad_black_48dp', 'ic_mood_black_48dp', 'ic_notifications_active_black_48dp', 'ic_notifications_black_48dp', 'ic_notifications_none_black_48dp', 'ic_notifications_off_black_48dp', 'ic_notifications_paused_black_48dp', 'ic_pages_black_48dp', 'ic_party_mode_black_48dp', 'ic_people_black_48dp', 'ic_people_outline_black_48dp', 'ic_person_add_black_48dp', 'ic_person_black_48dp', 'ic_person_outline_black_48dp', 'ic_plus_one_black_48dp', 'ic_poll_black_48dp', 'ic_public_black_48dp', 'ic_school_black_48dp', 'ic_sentiment_dissatisfied_black_48dp', 'ic_sentiment_neutral_black_48dp', 'ic_sentiment_satisfied_black_48dp', 'ic_sentiment_very_dissatisfied_black_48dp', 'ic_sentiment_very_satisfied_black_48dp', 'ic_share_black_48dp', 'ic_whatshot_black_48dp', 'ic_star_black_48dp', 'ic_star_border_black_48dp', 'ic_star_half_black_48dp'];

ui.icons.setDataSource(icons);

ui.icons.on("item_click", function(icon){
    var d = "@drawable/" + icon;
    setClip(d);
    toast(d + "已复制到剪贴板");
});

ui.search.on("click", function(){
    var text = ui.input.text();
    if(text.length == 0){
        return;
    }
    search(text);
});

ui.reset.on("click", function(){
    ui.icons.setDataSource(icons);
});

function search(keywords){
    var result = [];
    for(var i = 0; i < icons.length; i++){
        var icon = icons[i];
        if(icon.indexOf(keywords) >= 0){
            result.push(icon);
        }
    }
    ui.icons.setDataSource(result);
}

复选框单选框控件

"ui";

ui.layout(
    <vertical padding="16">
        <checkbox id="cb1" text="复选框"/>
        <checkbox id="cb2" checked="true" text="勾选的复选框"/>
        <radiogroup>
            <radio text="单选框1"/>
            <radio text="单选框2"/>
            <radio text="单选框3"/>
        </radiogroup>
        <radiogroup mariginTop="16">
            <radio text="单选框1"/>
            <radio text="单选框2"/>
            <radio text="勾选的单选框3" checked="true"/>
        </radiogroup>
    </vertical>
);

ui.cb1.on("check", (checked)=>{
    if(checked){
        toast("第一个框被勾选了");
    }else{
        toast("第一个框被取消勾选了");
    }
});

进度条控件

"ui";

ui.layout(
    <vertical padding="16">
        <text text="处理中..." textColor="black" textSize="16sp"/>
        <progressbar />

        <text text="直线无限进度条" textColor="black" textSize="16sp" marginTop="24"/>
        <progressbar indeterminate="true" style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"/>

        <text text="直线进度条" textColor="black" textSize="16sp" marginTop="24"/>
        <progressbar progress="30" style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"/>

        <text text="可调节进度条" textColor="black" textSize="16sp" marginTop="24"/>
        <seekbar progress="20"/>

        <horizontal gravity="center" marginTop="24">
            <text id="progress_value" textColor="black" textSize="16sp" margin="8" text="0"/>
            <progressbar id="progress" w="*" style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"/>
        </horizontal>
        <button id="download">开始下载</button>
    </vertical>
);

var downloadId = null;

ui.download.click(()=>{
    if(downloadId != null){
        stopDownload();
    }else{
        startDownload();
    }
});

function stopDownload(){
    ui.download.text("开始下载");
    clearInterval(downloadId);
    downloadId = null;
}

function startDownload(){
    if(ui.progress.getProgress() == 100){
        ui.progress.setProgress(0);
    }
    ui.download.text("停止下载");
    downloadId = setInterval(()=>{
        var p = ui.progress.getProgress();
        p++;
        if(p > 100){
            stopDownload();
            return;
        }
        ui.progress.setProgress(p);
        ui.progress_value.setText(p.toString());
    }, 200);
}

卡片布局

"ui";

ui.layout(
    <vertical>
        <appbar>
            <toolbar id="toolbar" title="卡片布局"/>
        </appbar>
        <card w="*" h="70" margin="10 5" cardCornerRadius="2dp"
            cardElevation="1dp" gravity="center_vertical">
            <vertical padding="18 8" h="auto">
                <text text="写操作系统作业" textColor="#222222" textSize="16sp"/>
                <text text="明天第1~2节" textColor="#999999" textSize="14sp"/>
            </vertical>
            <View bg="#f44336" h="*" w="10"/>
        </card>
        <card w="*" h="70" margin="10 5" cardCornerRadius="2dp"
            cardElevation="1dp" gravity="center_vertical">
            <vertical padding="18 8" h="auto">
                <text text="修复ui模式的Bug" textColor="#222222" textSize="16sp"/>
                <text text="无限期" textColor="#999999" textSize="14sp"/>
            </vertical>
            <View bg="#ff5722" h="*" w="10"/>
        </card>
        <card w="*" h="70" margin="10 5" cardCornerRadius="2dp"
            cardElevation="1dp" gravity="center_vertical">
            <vertical padding="18 8" h="auto">
                <text text="发布Auto.js 5.0.0正式版" textColor="#222222" textSize="16sp"/>
                <text text="2019年1月" textColor="#999999" textSize="14sp"/>
            </vertical>
            <View bg="#4caf50" h="*" w="10"/>
        </card>
        <card w="*" h="70" margin="10 5" cardCornerRadius="2dp"
            cardElevation="1dp" gravity="center_vertical">
            <vertical padding="18 8" h="auto">
                <text text="完成毕业设计和论文" textColor="#222222" textSize="16sp"/>
                <text text="2019年4月" textColor="#999999" textSize="14sp"/>
            </vertical>
            <View bg="#2196f3" h="*" w="10"/>
        </card>
    </vertical>
);

列表控件

"ui";

ui.layout(
    <frame>
        <list id="list">
            <vertical>
                <text id="name" textSize="16sp" textColor="#000000" text="姓名: {{name}}"/>
                <text id="age" textSize="16sp" textColor="#000000" text="年龄: {{age}}岁"/>
                <button id="deleteItem" text="删除"/>
            </vertical>
        </list>
    </frame>
);

var items = [
    {name: "小明", age: 18}, {name: "小红", age: 30},
    {name: "小东", age: 19}, {name: "小强", age: 31},
    {name: "小满", age: 20}, {name: "小一", age: 32},
    {name: "小和", age: 21}, {name: "小二", age: 1},
    {name: "小贤", age: 22}, {name: "小三", age: 2},
    {name: "小伟", age: 23}, {name: "小四", age: 3},
    {name: "小黄", age: 24}, {name: "小五", age: 4},
    {name: "小健", age: 25}, {name: "小六", age: 5},
    {name: "小啦", age: 26}, {name: "小七", age: 6},
    {name: "小哈", age: 27}, {name: "小八", age: 7},
    {name: "小啊", age: 28}, {name: "小九", age: 8},
    {name: "小啪", age: 29}, {name: "小十", age: 9}
];

ui.list.setDataSource(items);

ui.list.on("item_click", function(item, i, itemView, listView){
    toast("被点击的人名字为: " + item.name + ",年龄为: " + item.age);
});

ui.list.on("item_bind", function(itemView, itemHolder){
    itemView.deleteItem.on("click", function(){
        let item = itemHolder.item;
        toast("被删除的人名字为: " + item.name + ",年龄为: " + item.age);
        items.splice(itemHolder.position, 1);
    });
})

时间日期选择控件

"ui";

ui.layout(
    <scroll>
        <vertical padding="16">
            <text text="日历样式日期选择" textColor="black" textSize="16sp" marginTop="16"/>
            <datepicker />

            <text text="滑动日期选择" textColor="black" textSize="16sp" marginTop="16"/>
            <datepicker datePickerMode="spinner"/>

            <text text="时钟样式时间选择" textColor="black" textSize="16sp" marginTop="16"/>
            <timepicker />

            <text text="滑动时间选择" textColor="black" textSize="16sp" marginTop="16"/>
            <timepicker timePickerMode="spinner"/>

        </vertical>
    </scroll>
)

输入框控件

"ui";

ui.layout(
    <vertical padding="16">
         <text text="输入框" textColor="black" textSize="16sp" marginTop="16"/>
         <input />

         <!-- hint属性用来设置输入框的提示-->
         <text text="带提示的输入框" textColor="black" textSize="16sp" marginTop="16"/>
         <input hint="请输入一些内容"/>

         <!-- inputType属性用来设置输入类型,包括number, email, phone等-->
         <text text="数字输入框" textColor="black" textSize="16sp" marginTop="16"/>
         <input inputType="number" text="123"/>

         <!-- password属性用来设置输入框是否是密码输入框 -->
         <text text="密码输入框" textColor="black" textSize="16sp" marginTop="16"/>
         <input password="true"/>

         <!-- lines属性用来设置输入框的行数 -->
         <text text="多行输入框" textColor="black" textSize="16sp" marginTop="16"/>
         <input lines="3"/>

         <text text="设置输入框错误信息" textColor="black" textSize="16sp" marginTop="16"/>
         <input id="qq" inputType="number" hint="请输入您的QQ号码"/>
         <button id="ok" text="确定" w="auto" style="Widget.AppCompat.Button.Colored"/>
    </vertical>
);

ui.ok.click(()=>{
    var text = ui.qq.text();
    if(text.length == 0){
        ui.qq.setError("输入不能为空");
        return;
    }
    var qq = parseInt(text);
    if(qq < 10000){
        ui.qq.setError("QQ号码格式错误");
        return;
    }
    ui.qq.setError(null);
});

图片控件

"ui";

ui.layout(
<scroll>
    <vertical bg="#707070" padding="16">
        <text text="网络图片" textColor="black" textSize="16sp" marginTop="16"/>
        <img src="http://www.autojs.org/assets/uploads/profile/3-profileavatar.png"
            w="100" h="100"/>

        <text text="带边框的图片" textColor="black" textSize="16sp" marginTop="16"/>
        <img src="http://www.autojs.org/assets/uploads/profile/1-profileavatar.jpeg"
                w="100" h="100" borderWidth="2dp" borderColor="#202020"/>

        <text text="圆形图片" textColor="black" textSize="16sp" marginTop="16"/>
        <img src="http://www.autojs.org/assets/uploads/profile/1-profileavatar.jpeg"
                w="100" h="100" circle="true"/>

        <text text="带边框的圆形图片" textColor="black" textSize="16sp" marginTop="16"/>
        <img src="http://www.autojs.org/assets/uploads/profile/1-profileavatar.jpeg"
                w="100" h="100" circle="true" borderWidth="2dp" borderColor="#202020"/>

        <text text="圆角图片" textColor="black" textSize="16sp" marginTop="16"/>
        <img id="rounded_img" src="http://www.autojs.org/assets/uploads/profile/1-profileavatar.jpeg"
                w="100" h="100" radius="20dp" scaleType="fitXY"/>
        <button id="change_img" text="更改图片"/>
    </vertical>
</scroll>
);

ui.change_img.on("click", ()=>{
    ui.rounded_img.setSource("http://www.autojs.org/assets/uploads/profile/1-profilecover.jpeg");
});

文本控件

"ui";

ui.layout(
    <vertical padding="16">
        <text textSize="40sp">大字</text>
        <text textSize="12sp">小字</text>
        <text textStyle="bold" textColor="black">加粗</text>
        <text textStyle="italic">斜体</text>
        <text textColor="#00ff00">原谅色</text>
        <text margin="8">Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。</text>
        <text maxLines="1" ellipsize="end" margin="8">Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。</text>
        <text maxLines="2" ellipsize="end" margin="8">Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。</text>
        <text w="*" gravity="center" textSize="20sp">居中</text>
        <text autoLink="all">自动超链接网址www.baidu.com, 邮箱 123@qq.com等</text>
    </vertical>
);

下拉菜单

"ui";

ui.layout(
    <vertical padding="16">
        <horizontal>
            <text textSize="16sp">下拉菜单</text>
            <spinner id="sp1" entries="选项1|选项2|选项3"/>
        </horizontal>
        <horizontal>
            <text textSize="16sp">对话框菜单</text>
            <spinner id="sp2" entries="选项4|选项5|选项6" spinnerMode="dialog"/>
        </horizontal>
        <button id="ok">确定</button>
        <button id="select3">选择选项3</button>
    </vertical>
);

ui.ok.on("click", ()=>{
    var i = ui.sp1.getSelectedItemPosition();
    var j = ui.sp2.getSelectedItemPosition();
    toast("您的选择是选项" + (i + 1) + "和选项" + (j + 4));
});

ui.select3.on("click", ()=>{
    ui.sp1.setSelection(2);
});

自定义控件-布局模板

"ui";

var InputLayout = (function() {
    //继承至ui.Widget
    util.extend(InputLayout, ui.Widget);

    function InputLayout() {
        ui.Widget.call(this);
        this.defineAttr("hint", (view, attr, value, defineSetter)=>{
            view._hint.setText(value);
        });
        this.defineAttr("text", (view, attr, value, defineSetter)=>{
             view._input.setText(value);
         });
    }
    InputLayout.prototype.render = function() {
        return (
            <vertical>
                <text id="_hint" textSize="16sp" margin="4" textColor="gray"/>
                <input id="_input" margin="0 16"/>
            </vertical>
        );
    }
    InputLayout.prototype.getInput = function() {
        return this.view._input.getText();
    };
    ui.registerWidget("input-layout", InputLayout);
    return InputLayout;
})();

ui.layout(
    <vertical>
        <input-layout id="name" hint="请输入名字"/>
        <input-layout id="age" hint="请输入年龄" text="18"/>
        <button id="ok" text="确认"/>
    </vertical>
);

ui.ok.on("click", function(){
    toast("名字是:" + ui.name.widget.getInput() + ", 年龄是:" + ui.age.widget.getInput());
});

自定义控件-带颜色按钮

"ui";

var ColoredButton = (function() {
    //继承ui.Widget
    util.extend(ColoredButton, ui.Widget);

    function ColoredButton() {
        //调用父类构造函数
        ui.Widget.call(this);
        //自定义属性color,定义按钮颜色
        this.defineAttr("color", (view, name, defaultGetter) => {
            return this._color;
        }, (view, name, value, defaultSetter) => {
            this._color = value;
            view.attr("backgroundTint", value);
        });
        //自定义属性onClick,定义被点击时执行的代码
        this.defineAttr("onClick", (view, name, defaultGetter) => {
            return this._onClick;
        }, (view, name, value, defaultSetter) => {
            this._onClick = value;
        });
    }
    ColoredButton.prototype.render = function() {
        return (
            <button textSize="16sp" style="Widget.AppCompat.Button.Colored" w="auto"/>
        );
    }
    ColoredButton.prototype.onViewCreated = function(view) {
        view.on("click", () => {
            if (this._onClick) {
                eval(this._onClick);
            }
        });
    }
    ui.registerWidget("colored-button", ColoredButton);
    return ColoredButton;
})();

ui.layout(
    <vertical>
        <colored-button text="第一个按钮" color="#ff5722"/>
        <colored-button text="第二个按钮" onClick="hello()"/>
    </vertical>
);

function hello() {
    alert("Hello ~");

}

自定义控件-模块-配置勾选框

//这个自定义控件是一个勾选框checkbox,能够保存自己的勾选状态,在脚本重新启动时能恢复状态
var PrefCheckBox = (function() {
    //继承至ui.Widget
    util.extend(PrefCheckBox, ui.Widget);

    function PrefCheckBox() {
        //调用父类构造函数
        ui.Widget.call(this);
        //自定义属性key,定义在配置中保存时的key
        this.defineAttr("key");
    }
    PrefCheckBox.prototype.render = function() {
        return (
            <checkbox />
        );
    }
    PrefCheckBox.prototype.onFinishInflation = function(view) {
        view.setChecked(PrefCheckBox.getPref().get(this.getKey(), false));
        view.on("check", (checked) => {
            PrefCheckBox.getPref().put(this.getKey(), checked);
        });
    }
    PrefCheckBox.prototype.getKey = function() {
        if(this.key){
            return this.key;
        }
        let id = this.view.attr("id");
        if(!id){
            throw new Error("should set a id or key to the checkbox");
        }
        return id.replace("@+id/", "");
    }
    PrefCheckBox.setPref = function(pref) {
        PrefCheckBox._pref = pref;
    }
    PrefCheckBox.getPref = function(){
        if(!PrefCheckBox._pref){
            PrefCheckBox._pref = storages.create("pref");
        }
        return PrefCheckBox._pref;
    }
    ui.registerWidget("pref-checkbox", PrefCheckBox);
    return PrefCheckBox;
})();

module.exports = PrefCheckBox;

自定义控件-使用配置勾选框

"ui";

var PrefCheckBox = require('./自定义控件-模块-配置勾选框.js');

ui.layout(
    <vertical>
        <pref-checkbox id="perf1" text="配置1"/>
        <pref-checkbox id="perf2" text="配置2"/>
        <button id="btn" text="获取配置"/>
    </vertical>
);

ui.btn.on("click", function(){
    toast("配置1为" + PrefCheckBox.getPref().get("perf1"));
    toast("配置2为" + PrefCheckBox.getPref().get("perf2"));
});

本文转自 https://blog.csdn.net/qq_37952052/article/details/131625690?spm=1001.2014.3001.5502,如有侵权,请联系删除。

02_auto.js基础操作3/4

文章目录

  • 02_auto.js基础操作3/4
    • 控制台
      • 保存日志
      • 产生随机数
      • 调整控制台大小位置
      • 格式化输出
      • 控制台示例
      • 设置日志显示最大行数
      • 修改控制台界面
      • 终端模拟器
    • 设备与设备信息
      • 调整设备亮度
      • 调整设备音量
      • 获取设备信息
    • 时间与按键、触摸监听
      • 按键监听
      • 触摸监听
      • 通知监听
      • 音量健控制程序
      • 长按返回退出当前程序
      • Toast监听
    • 图片与图色处理
      • 找图
      • 找出所有图
      • 找图并画出位置
      • 获取网络图片并保存
      • 截图并保存
      • 精确找色
      • 模糊找色
      • 区域找色1
      • 区域找色2
      • 实时显示触摸点颜色
      • 图片处理
      • 颜色获取和检测
      • 找到QQ红点位置
    • 文件读写
      • 读取文本文件
      • 读写文本文件
      • 删除所有空文件夹
      • 文件编码转换
      • 文件编码转换(高级)
      • 写入文本文件
    • 消息处理(加密、摘要、编码)
      • 加密解密
      • 消息摘要(MD5,SHA)
      • base64编码
    • 协程
      • 协成HelloWorld
      • ui中使用协成
    • 悬浮窗
      • 动态悬浮文字
      • 护眼模式
      • 悬浮窗输入框
      • 悬浮窗运行脚本按钮简单版
      • 悬浮文字
      • 悬浮运行脚本按钮

控制台

保存日志

console.setGlobalLogConfig({
    file: "/sdcard/log.txt"
});
console.log(1);
console.log(2);
console.error(3);
app.viewFile("/sdcard/log.txt");

产生随机数

console.show();

log("将产生5个1到100的随机数");

for(let i = 0; i < 5; i++){
    print(random(1, 100));
    print("  ");
    sleep(400);
}
print("
");

log("将产生10个1到20的不重复随机数");

var exists = {};

for(let i = 0; i < 10; i++){
    var r;
    do{
        r = random(1, 20);
    }while(exists[r]);
    exists[r] = true;
    print(r + "  ");
    sleep(400);
}

调整控制台大小位置

console.show(true);
console.log("运行结束自动关闭");
console.log("调整大小...");
console.setSize(1000, 1000);
sleep(2000);
console.log("调整位置...");
console.setPosition(0, 500);
sleep(2000);

格式化输出

console.show();

var i = {
    name: "小明",
    age: 18,
    height: 1.72
};

console.log("大家好, 我叫%s, 今年%d岁, 身高%d米", i.name, i.age, i.height);
console.log("实际上我是一个对象啦,长这样子: %j", i);

控制台示例

//显示控制台
console.show();

console.verbose("这是灰色");
console.log("这是黑色");
console.info("这是红色");
console.warn("这是蓝色");
console.error("这是绿色=_=");
hey();

function hey() {
    console.trace("打印日志行数");
}

设置日志显示最大行数

console.show(false);
console.setMaxLines(10);
var i=0;
while(true){
    console.log(i)
    i++;
    sleep(500);
}

修改控制台界面

function myrandom(min,max){
    return Math.floor(Math.random() * (max - min + 1) ) + min;
}

threads.start(function () {
    console.show();
    console.setTitle("中文","#ff11ee00",30);
    console.setCanInput(false);
    var i=0;
    do {
        console.setLogSize(myrandom(4,20) );
        console.setBackgroud("#33ef0000");
        console.setCanInput(i%2==0);
        i++;
        console.log("i----->"+i);
        sleep(3000);
    } while (true);

});

终端模拟器

var sh = new Shell();
sh.setCallback({
    onOutput: function(str){
        print(str);
    }
})
console.show();
do {
    var cmd = console.rawInput();
    sh.exec(cmd);
}while(cmd != "exit");
sh.exit();

设备与设备信息

调整设备亮度

"ui";

ui.layout(
    <vertical padding="16">
        <checkbox id="auto" text="自动亮度"/>
        <text textColor="black" textSize="16sp" margin="8">亮度</text>
        <seekbar id="brightness" max="100"/>
    </vertical>
);

//getBrightnessMode()返回亮度模式,1为自动亮度
ui.auto.setChecked(device.getBrightnessMode() == 1);
ui.auto.setOnCheckedChangeListener(function(v, checked){
    device.setBrightnessMode(checked ? 1: 0);
});

ui.brightness.setProgress(device.getBrightness());
ui.brightness.setOnSeekBarChangeListener({
    onProgressChanged: function(seekbar, p, fromUser){
        if(fromUser){
            device.setBrightness(p);
        }
    }
});

调整设备音量

"ui";

ui.layout(
    <vertical padding="16">
        <text textColor="black" textSize="16sp">媒体音量</text>
        <seekbar id="music"/>

        <text textColor="black" textSize="16sp">通知音量</text>
        <seekbar id="notification"/>

        <text textColor="black" textSize="16sp">闹钟音量</text>
        <seekbar id="alarm"/>
    </vertical>
);

ui.music.setMax(device.getMusicMaxVolume());
ui.music.setProgress(device.getMusicVolume());
ui.music.setOnSeekBarChangeListener({
    onProgressChanged: function(seekbar, p, fromUser){
        if(fromUser){
            device.setMusicVolume(p);
        }
    }
});

ui.notification.setMax(device.getNotificationMaxVolume());
ui.notification.setProgress(device.getAlarmVolume());
ui.notification.setOnSeekBarChangeListener({
    onProgressChanged: function(seekbar, p, fromUser){
        if(fromUser){
            device.setNotificationVolume(p);
        }
    }
});

ui.alarm.setMax(device.getAlarmMaxVolume());
ui.alarm.setProgress(device.getAlarmVolume());
ui.alarm.setOnSeekBarChangeListener({
    onProgressChanged: function(seekbar, p, fromUser){
        if(fromUser){
            device.setAlarmVolume(p);
        }
    }
});

获取设备信息

console.show();

var str = "";
str += "屏幕宽度:" + device.width;
str += "
屏幕高度:" + device.height;
str += "
buildId:" + device.buildId;
str += "
主板:" + device.board;
str += "
制造商:" + device.brand;
str += "
型号:" + device.model;
str += "
产品名称:" + device.product;
str += "
bootloader版本:" + device.bootloader;
str += "
硬件名称:" + device.hardware;
str += "
唯一标识码:" + device.fingerprint;
str += "
IMEI: " + device.getIMEI();
str += "
AndroidId: " + device.getAndroidId();
str += "
Mac: " + device.getMacAddress();
str += "
API: " + device.sdkInt;
str += "
电量: " + device.getBattery();
str += "
是否有虚拟导航: " + device.checkDeviceHasNavigationBar();
str += "
虚拟导航高度: " + device.getVirtualBarHeigh();
log(str);

时间与按键、触摸监听

按键监听

"auto";

events.observeKey();

var keyNames = {
    "KEYCODE_VOLUME_UP": "音量上键",
    "KEYCODE_VOLUME_DOWN": "音量下键",
    "KEYCODE_HOME": "Home键",
    "KEYCODE_BACK": "返回键",
    "KEYCODE_MENU": "菜单键",
    "KEYCODE_POWER": "电源键",
};

events.on("key", function(code, event){
    var keyName = getKeyName(code, event);
    if(event.getAction() == event.ACTION_DOWN){
        toast(keyName + "被按下");
    }else if(event.getAction() == event.ACTION_UP){
        toast(keyName + "弹起");
    }
});

loop();

function getKeyName(code, event){
    var keyCodeStr = event.keyCodeToString(code);
    var keyName = keyNames[keyCodeStr];
    if(!keyName){
        return keyCodeStr;
    }
    return keyName;
}

触摸监听

events.observeTouch();

events.setTouchEventTimeout(30);

toast("请在日志中查看触摸的点的坐标");

events.on("touch", function(point){
    log(point);
});

loop();

通知监听

auto();
events.observeNotification();
events.onNotification(function(notification){
    printNotification(notification);
});
toast("监听中,请在日志中查看记录的通知及其内容");

function printNotification(notification){
    log("应用包名: " + notification.getPackageName());
    log("通知文本: " + notification.getText());
    log("通知优先级: " + notification.priority);
    log("通知目录: " + notification.category);
    log("通知时间: " + new Date(notification.when));
    log("通知数: " + notification.number);
    log("通知摘要: " + notification.tickerText);
}

音量健控制程序

"auto";

events.observeKey();

var interval = 5000;
var task = task1;

events.onKeyDown("volume_up", function(event){
    if(task == task1){
        task = task2;
    }else{
        task = task1;
    }
    toast("任务已切换");
});

events.onKeyDown("volume_down", function(event){
    toast("程序结束");
    exit();
});

task();

loop();

function task1(){
    toast("任务1运行中,音量下键结束,音量上键切换任务");
    setTimeout(task, interval);
}

function task2(){
    toast("任务2运行中,音量下键结束,音量上键切换任务");
    setTimeout(task, interval);
}

长按返回退出当前程序

"auto";

var 长按间隔 = 1500;

var curPackage = null;
var timeoutId = null;

events.observeKey();

events.onKeyDown("back", function(event){
    curPackage = currentPackage();
    timeoutId = setTimeout(function(){
       backBackBackBack();
    }, 长按间隔);
});

events.onKeyUp("back", function(event){
    clearTimeout(timeoutId);
});

loop();

function backBackBackBack(){
    while(curPackage == currentPackage()){
        back();
        sleep(200);
    }
}

Toast监听

auto();
events.observeToast();
events.onToast(function(toast){
    var pkg = toast.getPackageName();
    log("Toast内容: " + toast.getText() +
        " 来自: " + getAppName(pkg) +
        " 包名: " + pkg);
});
toast("监听中,请在日志中查看记录的Toast及其内容");

图片与图色处理

找图

var superMario = images.read("./super_mario.jpg");
var mario = images.read("./mario.png");
var point = findImage(superMario, mario);
toastLog(point);

superMario.recycle();
mario.recycle();

找出所有图

var superMario = images.read("./super_mario.jpg");
var block = images.read("./block.png");

var result = images.matchTemplate(superMario, block, {
    threshold: 0.8
}).matches;
toastLog(result);

superMario.recycle();
block.recycle();

找图并画出位置

var superMario = images.read("./super_mario.jpg");
var block = images.read("./block.png");
var points = images.matchTemplate(superMario, block, {
    threshold: 0.8
}).points;

toastLog(points);

var canvas = new Canvas(superMario);
var paint = new Paint();
paint.setColor(colors.parseColor("#2196F3"));
points.forEach(point => {
    canvas.drawRect(point.x, point.y, point.x + block.width, point.y + block.height, paint);
});
var image = canvas.toImage();
images.save(image, "/sdcard/tmp.png");

app.viewFile("/sdcard/tmp.png");

superMario.recycle();
block.recycle();
image.recycle();

获取网络图片并保存

//这个是Auto.js图标的地址
var url = "https://www.autojs.org/assets/uploads/profile/3-profileavatar.png";
var logo = images.load(url);
//保存到路径/sdcard/auto.js.png
images.save(logo, "/sdcard/auto.js.png");

截图并保存

if(!requestScreenCapture()){
    toast("请求截图失败");
    exit();
}
var img = captureScreen();
images.saveImage(img, "/sdcard/1.png");

精确找色

if(!requestScreenCapture()){
    toast("请求截图失败");
    exit();
}
var img = captureScreen();
//0x9966ff为编辑器紫色字体的颜色
toastLog("开始找色");
var point = findColor(img, 0x9966ff);
if(point){
    toastLog("x = " + point.x + ", y = " + point.y);
}else{
    toastLog("没有找到");
}

模糊找色

if(!requestScreenCapture()){
    toast("请求截图失败");
    exit();
}
var img = captureScreen();
//0x9966ff为编辑器紫色字体的颜色
toastLog("开始找色");
var point = findColor(img, 0x9966ff);
if(point){
    toastLog("x = " + point.x + ", y = " + point.y);
}else{
    toastLog("没有找到");
}

区域找色1

if(!requestScreenCapture()){
    toast("请求截图失败");
    exit();
}
var img = captureScreen();
toastLog("开始找色");
//指定在位置(100, 220)宽高为400*400的区域找色。
//#75438a是编辑器默认主题的棕红色字体(数字)颜色,位置大约在第5行的"2000",坐标大约为(283, 465)
var point = findColorInRegion(img, "#75438a", 90, 220, 900, 1000);
if(point){
    toastLog("x = " + point.x + ", y = " + point.y);
}else{
    toastLog("没有找到");
}

区域找色2

if(!requestScreenCapture()){
    toast("请求截图失败");
    exit();
}
var img = captureScreen();
//0xffffff为白色
toastLog("开始找色");
//指定在位置(90, 220)宽高为900*1000的区域找色。
//0xff00cc是编辑器的深粉红色字体(字符串)颜色
var point = findColor(img, "#ff00cc", {
    region: [90, 220, 900, 1000],
    threads: 8
});
if(point){
    toastLog("x = " + point.x + ", y = " + point.y);
}else{
    toastLog("没有找到");
}

实时显示触摸点颜色

requestScreenCapture();
console.show();
events.observeTouch();
events.setTouchEventTimeout(30);
events.on("touch", function(point){
    var c = colors.toString(images.pixel(captureScreen(), point.x, point.y));
    log("(" + point.x + ", " + point.y + "): " + c);
});

图片处理

"ui";

var url = "https://www.autojs.org/assets/uploads/files/1540386817060-918021-20160416200702191-185324559.jpg";
var logo = null;
var currentImg = null;

events.on("exit", function(){
    if(logo != null){
        logo.recycle();
    }
    if(currentImg != null){
        currentImg.recycle();
    }
});

ui.layout(
    <vertical>
        <img id="img" w="250" h="250" url="{{url}}" />
        <scroll>
            <vertical>
                <button id="rotate" text="旋转" />
                <button id="concat" text="拼接" />
                <button id="grayscale" text="灰度化" />
                <button id="binary" text="二值化" />
                <button id="adaptiveBinary" text="自适应二值化" />
                <button id="hsv" text="RGB转HSV" />
                <button id="blur" text="模糊" />
                <button id="medianBlur" text="中值滤波" />
                <button id="gaussianBlur" text="高斯模糊" />
            </vertical>
        </scroll>
    </vertical>
);

//把一张图片设置到图片控件中
function setImage(img) {
    ui.run(() => {
        ui.img.setImageBitmap(img.bitmap);
        var oldImg = currentImg;
        //不能立即回收currentImg,因为此时img控件还在使用它,应该在下次消息循环再回收它
        ui.post(()=>{
            if(oldImg != null){
                oldImg.recycle();
            }
        });
        currentImg = img;
    });
}

//启动一个处理图片的线程
var imgProcess = threads.start(function () {
    setInterval(() => { }, 1000);
});

//处理图片的函数,把任务交给图片处理线程处理
function processImg(process) {
    imgProcess.setTimeout(() => {
        if (logo == null) {
            logo = images.load(url);
        }
        //处理图片
        var result = process(logo);
        //把处理后的图片设置到图片控件中
        setImage(result);
    }, 0);
}

var degress = 0;

ui.rotate.on("click", () => {
    processImg(img => {
        degress += 90;
        //旋转degress角度
        return images.rotate(img, degress);
    });
});

ui.concat.on("click", () => {
    processImg(img => {
        if(currentImg == null){
            toast("请先点击其他按钮,再点击本按钮");
            return img.clone();
        }
        //把currentImg拼接在img右边
        return images.concat(img, currentImg, "right");
    });
});

ui.grayscale.on("click", () => {
    processImg(img => {
        //灰度化
        return images.grayscale(img);
    });
});

ui.binary.on("click", () => {
    processImg(img => {
        var g = images.grayscale(img);
        //二值化,取灰度为30到200之间的图片
        var result = images.threshold(g, 100, 200);
        g.recycle();
        return result;
    });
});

ui.adaptiveBinary.on("click", () => {
    processImg(img => {
        var g = images.grayscale(img);
        //自适应二值化,最大值为200,块大小为25
        var result = images.adaptiveThreshold(g, 200, "MEAN_C", "BINARY", 25, 10);
        g.recycle();
        return result;
    });
});

ui.hsv.on("click", () => {
    processImg(img => {
        //RGB转HSV
        return images.cvtColor(img, "BGR2HSV");
    });
});

ui.blur.on("click", () => {
    processImg(img => {
        //模糊
        return images.blur(img, [10, 10]);
    });
});

ui.medianBlur.on("click", () => {
    processImg(img => {
        //中值滤波
        return images.medianBlur(img, 5);
    });
});

ui.gaussianBlur.on("click", () => {
    processImg(img => {
        //高斯模糊
        return images.gaussianBlur(img, [5, 5]);
    });
});

颜色获取和检测

if(!requestScreenCapture()){
   toast("请求截图失败");
   exit
}
sleep(2000);
var x = 760;
var y = 180;
//获取在点(x, y)处的颜色
var c = images.pixel(captureScreen(), x, y);
//显示该颜色
var msg = "";
msg += "在位置(" + x + ", " + y + ")处的颜色为" + colors.toString(c);
msg += "
R = " + colors.red(c) + ", G = " + colors.green(c) + ", B = " + colors.blue(c);
//检测在点(x, y)处是否有颜色#73bdb6 (模糊比较)
var isDetected = images.detectsColor(captureScreen(), "#73bdb6", x, y);
msg += "
该位置是否匹配到颜色#73bdb6: " + isDetected;
alert(msg);

找到QQ红点位置

if(!requestScreenCapture()){
    toast("请求截图失败");
    exit();
}
launchApp("QQ");
sleep(2000);
var img = captureScreen();
toastLog("开始找色");
var point = findColor(img, "#f64d30");
if(point){
    toastLog("x = " + point.x + ", y = " + point.y);
}else{
    toastLog("没有找到");
}

文件读写

读取文本文件

//文件路径
var path = "/sdcard/1.txt";
//打开文件
var file = open(path);
//读取文件的所有内容
var text = file.read();
//打印到控制台
print(text);
//关闭文件
file.close();
console.show();

读写文本文件

//以写入模式打开SD卡根目录文件1.txt
var file = open("/sdcard/1.txt", "w")
//写入aaaa
file.write("aaaa");
//写入bbbbb后换行
file.writeline("bbbbb");
//写入ccc与ddd两行
file.writelines(["ccc", "ddd"]);
//关闭文件
file.close();

//以附加模式打开文件
file = open("/sdcard/1.txt", "a");
//附加一行"啦啦啦啦"
file.writeline("啦啦啦啦");
//附加一行"哈哈哈哈"
file.writeline("哈哈哈哈");
//附加两行ccc, ddd
file.writelines(["ccc", "ddd"]);
//输出缓冲区
file.flush();
//关闭文件
file.close();

//以读取模式打开文件
file = open("/sdcard/test.txt", "r")
//读取一行并打印
print(file.readline());
//读取剩余所有行并打印
for each(line in file.readlines()){
  print(line)
}
file.close()

//显示控制台
console.show()

删除所有空文件夹

if(confirm("该操作会删除SD卡目录及其子目录下所有空文件夹,是否继续?")){
    toast("请点击右上角打开日志");
    deleteAllEmptyDirs(files.getSdcardPath());
    toast("全部完成!");
}

function deleteAllEmptyDirs(dir){
    var list = files.listDir(dir);
    var len = list.length;
    if(len == 0){
        log("删除目录 " + dir + " " + (files.remove(dir) ? "成功" : "失败"));
        return;
    }
    for(let i = 0; i < len; i++){
        var child = files.join(dir, list[i]);
        if(files.isDir(child)){
            deleteAllEmptyDirs(child);
        }
    }
}

文件编码转换

//以UTF-8编码打开SD卡上的1.txt文件
var f = open("/sdcard/1.txt", "r", "utf-8");
//读取文件所有内容
var text = f.read();
//关闭文件
f.close();
//以gbk编码打开SD卡上的2.txt文件
var out = open("/sdcard/2.txt", "w", "gbk");
//写入内容
out.write(text);
//关闭文件
out.close();

文件编码转换(高级)

convert("/sdcard/1.txt", "utf-8", "/sdcard/2.txt", "gbk");

/**
 * fromFile: 源文件路径
 * fromEncoding: 源文件编码
 * toFile: 输出文件路径
 * toEncoding: 输出文件编码
 */
function convert(fromFile, fromEncoding, toFile, toEncoding){
    fromFile = open(fromFile, "r", fromEncoding);
    toFile = open(toFile, "w", toEncoding);
    while(true){
        var line = fromFile.readline();
        if(!line)
            break;
        toFile.writeline(line);
    }
}

写入文本文件

//文件路径
var path = "/sdcard/1.txt";
//要写入的文件内容
var text = "Hello, AutoJs";
//以写入模式打开文件
var file = open(path, "w");
//写入文件
file.write(text);
//关闭文件
file.close();

消息处理(加密、摘要、编码)

加密解密

let message = "未加密字符串";
log("明文: ", message);
// 密钥,由于AES等算法要求是16位的倍数,我们这里用一个16位的密钥
let key = new $crypto.Key("password12345678");
log("密钥: ", key);
// AES加密
let aes = $crypto.encrypt(message, key, "AES/ECB/PKCS5padding");
log("AES加密后二进制数据: ", aes);
log("AES解密: ", $crypto.decrypt(aes, key, "AES/ECB/PKCS5padding", {output: 'string'}));

// RSA加密
// 生成RSA密钥
let keyPair = $crypto.generateKeyPair("RSA");
log("密钥对: ", keyPair);
// 使用私钥加密
let rsa = $crypto.encrypt(message, keyPair.privateKey, "RSA/ECB/PKCS1padding");
log("RSA私钥加密后二进制数据: ", rsa);
// 使用公钥解密
log("RSA公钥解密: ", $crypto.decrypt(rsa, keyPair.publicKey, "RSA/ECB/PKCS1padding", {output: 'string'}));

消息摘要(MD5,SHA)

// 字符串消息摘要
let message = "Hello, Autox.js";
// 输出各种消息摘要算法结果的hex值
log("字符串: ", message);
log("MD5: ", $crypto.digest(message, "MD5"));
log("SHA1: ", $crypto.digest(message, "SHA-1"));
log("SHA256: ", $crypto.digest(message, "SHA-256"));

// 输出各种消息摘要算法结果的base64值
log("MD5 [base64]: ", $crypto.digest(message, "MD5", {output: 'base64'}));
log("SHA1 [base64]: ", $crypto.digest(message, "SHA-1", {output: 'base64'}));
log("SHA256 [base64]: ", $crypto.digest(message, "SHA-256", {output: 'base64'}));

// 文件消息摘要
let file = "/sdcard/脚本/_test_for_message_digest.js"
// 写入文件内容,提供为后续计算MD5等
$files.write(file, "Test!");
log("文件: ", file);
log("MD5: ", $crypto.digest(file, "MD5", {input: 'file'}));
log("SHA1: ", $crypto.digest(file, "SHA-1", {input: 'file'}));
log("SHA256: ", $crypto.digest(file, "SHA-256", {input: 'file'}));

base64编码

let message = "autox.js";
log("明文: ", message);

let base64encode = $base64.encode(message);
log("Base64编码: ", base64encode);

let message2 = "YXV0b3guanM=";
log("明文2: ", message2);

let base64decode = $base64.decode(message2);
log("Base64解码: ", base64decode);

协程

协成HelloWorld

// 注意,要使用协程这个特性,必须使用项目功能,并且在project.json配置好features属性

// delay不同于sleep,不会阻塞当前线程
function delay(millis) {
    var cont = continuation.create();
    setTimeout(()=>{
        cont.resume();
    }, millis);
    cont.await();
}

// 异步IO例子,在另一个线程读取文件,读取完成后返回当前线程继续执行
function read(path) {
    var cont = continuation.create();
    threads.start(function(){
        try {
            cont.resume(files.read(path));
        }catch(err){
            cont.resumeError(err);
        }
    });
    return cont.await();
}

// 使用Promise和协程的例子
function add(a, b) {
    return new Promise(function(resolve, reject) {
        var sum = a + b;
        resolve(sum);
    });
}

toastLog("Hello, Continuation!");

//3秒后发出提示
setTimeout(()=>{
    toastLog("3秒后....");
}, 3000);

// 你可以尝试把delay更换成sleep,看会发生什么!
delay(6000);
toastLog("6秒后...");

try {
    toastLog("读取文件hello.txt: " + read("./hello.txt"));
}catch(err){
    console.error(err);
}

var sum = add(1, 2).await();
toastLog("1 + 2 = " + sum);

// project.json
{
  "name": "协程HelloWorld",
  "main": "main.js",
  "ignore": [
    "build"
  ],
  "packageName": "com.example.cont.helloworld",
  "versionName": "1.0.0",
  "versionCode": 1,
  "useFeatures": ["continuation"]
}

ui中使用协成

"ui";

ui.layout(
    <frame bg="#4fc3f7">
        <text textColor="white" textSize="18sp" layout_gravity="center">
            UI中使用协程
        </text>
    </frame>
);

continuation.delay(5000);
if (!requestScreenCapture()) {
    dialogs.alert("请授予软件截图权限").await();
}

// 退出应用对话框
ui.emitter.on("back_pressed", function (e) {
    e.consumed = true;
    let exit = dialogs.confirm("确定要退出程序").await();
    if (exit) {
        ui.finish();
    }
});

// project.json
{
  "name": "协程UI示例",
  "main": "main.js",
  "ignore": [
    "build"
  ],
  "packageName": "com.example.cont.ui",
  "versionName": "1.0.0",
  "versionCode": 1,
  "useFeatures": ["continuation"]
}

悬浮窗

动态悬浮文字

var window = floaty.window(
    <frame gravity="center">
        <text id="text" textSize="16sp" textColor="#f44336"/>
    </frame>
);

window.exitOnClose();

window.text.click(()=>{
    window.setAdjustEnabled(!window.isAdjustEnabled());
});

setInterval(()=>{
    //对控件的操作需要在UI线程中执行
    ui.run(function(){
        window.text.setText(dynamicText());
    });
}, 1000);

function dynamicText(){
    var date = new Date();
    var str = util.format("时间: %d:%d:%d
", date.getHours(), date.getMinutes(), date.getSeconds());
    str += util.format("内存使用量: %d%%
", getMemoryUsage());
    str += "当前活动: " + currentActivity() + "
";
    str += "当前包名: " + currentPackage();
    return str;
}

//获取内存使用率
function getMemoryUsage(){
    var usage = (100 * device.getAvailMem() / device.getTotalMem());
    //保留一位小数
    return Math.round(usage * 10) / 10;
}

护眼模式

var w = floaty.rawWindow(
    <frame gravity="center" bg="#44ffcc00"/>
);

w.setSize(-1, -1);
w.setTouchable(false);

setTimeout(()=>{
    w.close();
}, 60000);

悬浮窗输入框

var window = floaty.window(
    <vertical>
        <input id="input" text="请输入你的名字" textSize="16sp" focusable="true"/>
        <button id="ok" text="确定"/>
    </vertical>
);

window.exitOnClose();

toast("长按确定键可调整位置");

window.input.on("key", function(keyCode, event){
    if(event.getAction() == event.ACTION_DOWN && keyCode == keys.back){
        window.disableFocus();
        event.consumed = true;
    }
});

window.input.on("touch_down", ()=>{
    window.requestFocus();
    window.input.requestFocus();
});

window.ok.on("click", ()=>{
    toast("傻瓜! " + window.input.text());
    window.disableFocus();
});

window.ok.on("long_click", ()=>{
    window.setAdjustEnabled(!window.isAdjustEnabled());
});

setInterval(()=>{}, 1000);

悬浮窗运行脚本按钮简单版

var path = "/sdcard/脚本/test.js";
if(!files.exists(path)){
    toast("脚本文件不存在: " + path);
    exit();
}
var window = floaty.window(
    <frame>
        <button id="action" text="开始运行" w="90" h="40" bg="#77ffffff"/>
    </frame>
);

window.exitOnClose();

var execution = null;

window.action.click(()=>{
    if(window.action.getText() == '开始运行'){
        execution = engines.execScriptFile(path);
        window.action.setText('停止运行');
    }else{
        if(execution){
            execution.getEngine().forceStop();
        }
        window.action.setText('开始运行');
    }
});

window.action.longClick(()=>{
   window.setAdjustEnabled(!window.isAdjustEnabled());
   return true;
});

setInterval(()=>{}, 1000);

悬浮文字

var window = floaty.window(
    <frame gravity="center">
        <text id="text" text="点击可调整位置" textSize="16sp"/>
    </frame>
);

window.exitOnClose();

window.text.click(()=>{
    window.setAdjustEnabled(!window.isAdjustEnabled());
});

setInterval(()=>{}, 1000);

悬浮运行脚本按钮

var path = "/sdcard/脚本/test.js";
if(!files.exists(path)){
    toast("脚本文件不存在: " + path);
    exit();
}
var window = floaty.window(
    <frame>
        <button id="action" text="开始运行" w="90" h="40" bg="#77ffffff"/>
    </frame>
);

setInterval(()=>{}, 1000);

var execution = null;

//记录按键被按下时的触摸坐标
var x = 0, y = 0;
//记录按键被按下时的悬浮窗位置
var windowX, windowY;
//记录按键被按下的时间以便判断长按等动作
var downTime;

window.action.setOnTouchListener(function(view, event){
    switch(event.getAction()){
        case event.ACTION_DOWN:
            x = event.getRawX();
            y = event.getRawY();
            windowX = window.getX();
            windowY = window.getY();
            downTime = new Date().getTime();
            return true;
        case event.ACTION_MOVE:
            //移动手指时调整悬浮窗位置
            window.setPosition(windowX + (event.getRawX() - x),
                windowY + (event.getRawY() - y));
            //如果按下的时间超过1.5秒判断为长按,退出脚本
            if(new Date().getTime() - downTime > 1500){
                exit();
            }
            return true;
        case event.ACTION_UP:
            //手指弹起时如果偏移很小则判断为点击
            if(Math.abs(event.getRawY() - y) < 5 && Math.abs(event.getRawX() - x) < 5){
                onClick();
            }
            return true;
    }
    return true;
});

function onClick(){
    if(window.action.getText() == '开始运行'){
        execution = engines.execScriptFile(path);
        window.action.setText('停止运行');
    }else{
        if(execution){
            execution.getEngine().forceStop();
        }
        window.action.setText('开始运行');
    }
}

本文转自 https://blog.csdn.net/qq_37952052/article/details/131743472?spm=1001.2014.3001.5502,如有侵权,请联系删除。

02_auto.js基础操作4/4

文章目录

  • 02_auto.js基础操作4/4
    • 应用
      • 打开应用
      • 发送意图-文本消息分享
      • 强制停止应用
      • 卸载应用
      • 应用工具
    • GoogleMLkit
      • OCR识别
      • OCR截图识别
      • OCR识别点击
    • HTTP网络请求
      • 获取网页
      • 文件上传
      • 文件下载
    • javascript
      • 数字
      • E4X
      • HelloWorld
    • PaddleOCR
      • PaddleOCR文本识别
      • PaddleOCR文本识别-自定义模型路径
      • PaddleOCR-截图识别
    • Shell命令
      • 冻结网易云音乐
      • 结束所有后台进程
      • 解冻并打开网易云音乐
      • 锁屏
    • TessractOCR
    • Web扩展与游戏编程
      • 贪吃蛇
      • 贪吃蛇重力版
      • AutoX注入webviwe
    • WebSocket
      • 创建客户端
      • WebSocket示例

应用

打开应用

var appName = rawInput("请输入应用名称");
launchApp(appName);

发送意图-文本消息分享

var content = rawInput('请输入要分享的文本');
app.startActivity({
    action: "android.intent.action.SEND",
    type: "text/*",
    extras: {
      "android.intent.extra.TEXT": content
    },
    packageName: "com.tencent.mobileqq",
    className: "com.tencent.mobileqq.activity.JumpActivity"
});

强制停止应用

"auto";

var appName = rawInput("请输入应用名称");
openAppSetting(getPackageName(appName));
while(!click("强制停止"));

卸载应用

//输入应用名称
var appName = rawInput('请输入要卸载的应用名称');
//获取应用包名
var packageName = getPackageName(appName);
if(!packageName){
    toast("应用不存在!");
}else{
    //卸载应用
    app.uninstall(packageName);
}

应用工具

var i = dialogs.select("请选择工具", "获取应用包名", "打开应用详情页", "卸载应用");

if(i == -1){
    alert("没有选择任何工具!");
}

switch(i){
case 0:
    //获取应用包名
    appName = rawInput("请输入应用名称", "QQ");
    packageName = getPackageName(appName);
    toast(packageName);
    setClip(packageName);
    toast("已复制到剪贴板");
    break;
case 1:
    //打开应用详情页
    appName = rawInput("请输入应用名称", "微信");
    openAppSetting(getPackageName(appName));
    break;
case 2:
    //卸载应用
    appName = rawInput("请输入应用名称");
    packageName = getPackageName(appName);
    if(packageName == ""){
        toast("应用不存在");
    }else if(confirm("确定卸载应用" + packageName + "吗?")){
        app.uninstall(packageName);
    }
    break;
}

GoogleMLkit

OCR识别

let img = images.read("./1.png")
let start = new Date()
// 识别图片中的文字,返回完整识别信息(兼容百度OCR格式)。
//可选语言:拉丁 "la" , 中文 "zh" ,梵文 "sa" ,日语 "ja" , 韩语 "ko"
let result = gmlkit.ocr(img, "zh")
log('OCR识别耗时:' + (new Date() - start) + 'ms')
//排序 sort()会改变原对象,sorted() 不会改变原对象,而是返回新对象
result.sort()
//或者
//let newResult = result.sorted()
log("识别信息: " + result)
log("-------------------------------------------")
log("文本识别信息: " + result.text)
log("-------------------------------------------")
toastLog("json识别信息: " + JSON.stringify(result))
// 回收图片
img.recycle()

// project.json

{
  "abis": [
    "arm64-v8a",
    "armeabi-v7a",
    "x86",
    "x86_64"
  ],
  "assets": [
    {
      "form": "file:///android_asset/mlkit-google-ocr-models",
      "to": "/mlkit-google-ocr-models"
    }
  ],
  "buildDir": "build",
  "build": {
    "build_id": null,
    "build_number": 0,
    "build_time": 0
  },
  "useFeatures": [],
  "icon": null,
  "ignoredDirs": [],
  "launchConfig": {
    "displaySplash": false,
    "hideLauncher": false,
    "hideLogs": false,
    "stableMode": false,
    "volumeUpcontrol": false,
    "permissions": [],
    "serviceDesc": "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。",
    "splashIcon": null,
    "splashText": "Powered by Google ML Kit ocr"
  },
  "libs": [
    "libmlkit_google_ocr_pipeline.so",
    "libjackpal-androidterm5.so",
    "libjackpal-termexec2.so"
  ],
  "main": "main.js",
  "name": "Google MlKitOCR",
  "outputPath": null,
  "packageName": "com.script.gmlkitocr",
  "scripts": {},
  "signingConfig": {
    "alias": null,
    "keystore": null
  },
  "sourcePath": null,
  "versionCode": 1,
  "versionName": "1.0.0"
}

OCR截图识别

let currentEngine = engines.myEngine()
let runningEngines = engines.all()
let currentSource = currentEngine.getSource() + ''
if (runningEngines.length > 1) {
  runningEngines.forEach(compareEngine => {
    let compareSource = compareEngine.getSource() + ''
    if (currentEngine.id !== compareEngine.id && compareSource === currentSource) {
      // 强制关闭同名的脚本
      compareEngine.forceStop()
    }
  })
}

if (!requestScreenCapture()) {
  toastLog('请求截图权限失败')
  exit()
}

sleep(1000)

// 识别结果和截图信息
let result = []
let img = null
let running = true
let capturing = true

/**
 * 截图并识别OCR文本信息
 */
function captureAndOcr() {
  capturing = true
  img && img.recycle()
  img = captureScreen()
  if (!img) {
    toastLog('截图失败')
  }
  let start = new Date()
  //结果转数组:层级:3
  result = gmlkit.ocr(img,"zh").toArray(3);
  log(result);
  toastLog('耗时' + (new Date() - start) + 'ms')
  capturing = false
}

captureAndOcr()

// 获取状态栏高度
let offset = -getStatusBarHeightCompat()
//let offset = 0;

// 绘制识别结果
let window = floaty.rawWindow(
  <canvas id="canvas" layout_weight="1" />
);

// 设置悬浮窗位置
ui.post(() => {
  window.setPosition(0, offset)
  window.setSize(device.width, device.height)
  window.setTouchable(false)
})

// 操作按钮
let clickButtonWindow = floaty.rawWindow(
  <vertical>
    <button id="captureAndOcr" text="截图识别" />
    <button id="closeBtn" text="退出" />
  </vertical>
);
ui.run(function () {
  clickButtonWindow.setPosition(device.width / 2 - ~~(clickButtonWindow.getWidth() / 2), device.height * 0.65)
})

// 点击识别
clickButtonWindow.captureAndOcr.click(function () {
  result = []
  ui.run(function () {
    clickButtonWindow.setPosition(device.width, device.height)
  })
  setTimeout(() => {
    threads.start(()=>{
      captureAndOcr()
      ui.run(function () {
        clickButtonWindow.setPosition(device.width / 2 - ~~(clickButtonWindow.getWidth() / 2), device.height * 0.65)
      })
    })
  }, 500)
})

// 点击关闭
clickButtonWindow.closeBtn.click(function () {
  exit()
})

let Typeface = android.graphics.Typeface
let paint = new Paint()
paint.setStrokeWidth(1)
paint.setTypeface(Typeface.DEFAULT_BOLD)
paint.setTextAlign(Paint.Align.LEFT)
paint.setAntiAlias(true)
paint.setStrokeJoin(Paint.Join.ROUND)
paint.setDither(true)
window.canvas.on('draw', function (canvas) {
  if (!running || capturing) {
    return
  }
  // 清空内容
  canvas.drawColor(0xFFFFFF, android.graphics.PorterDuff.Mode.CLEAR)
  if (result && result.length > 0) {
    for (let i = 0; i < result.length; i++) {
      let ocrResult = result[i]
      drawRectAndText(ocrResult.text + ' #信心:' + ocrResult.confidence.toFixed(2), ocrResult.bounds, '#00ff00', canvas, paint);
    }
  }
})

setInterval(() => { }, 10000)
events.on('exit', () => {
  // 标记停止 避免canvas导致闪退
  running = false
  // 回收图片
  img && img.recycle()
  // 撤销监听
  window.canvas.removeAllListeners()

})

/**
 * 绘制文本和方框
 *
 * @param {*} desc
 * @param {*} rect
 * @param {*} colorStr
 * @param {*} canvas
 * @param {*} paint
 */
function drawRectAndText (desc, rect, colorStr, canvas, paint) {
  let color = colors.parseColor(colorStr)

  paint.setStrokeWidth(1)
  paint.setStyle(Paint.Style.STROKE)
  // 反色
  paint.setARGB(255, 255 - (color >> 16 & 0xff), 255 - (color >> 8 & 0xff), 255 - (color & 0xff))
  canvas.drawRect(rect, paint)
  paint.setARGB(255, color >> 16 & 0xff, color >> 8 & 0xff, color & 0xff)
  paint.setStrokeWidth(1)
  paint.setTextSize(20)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawText(desc, rect.left, rect.top, paint)
  paint.setTextSize(10)
  paint.setStrokeWidth(1)
  paint.setARGB(255, 0, 0, 0)
}

/**
 * 获取状态栏高度
 *
 * @returns
 */
function getStatusBarHeightCompat () {
  let result = 0
  let resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android")
  if (resId > 0) {
    result = context.getResources().getDimensionPixelOffset(resId)
  }
  if (result <= 0) {
    result = context.getResources().getDimensionPixelOffset(R.dimen.dimen_25dp)
  }
  return result
}

OCR识别点击

//requestScreenCapture()
var mainActivity = "org.autojs.autojs.ui.main.MainActivity"
if (currentActivity != mainActivity) {
    app.startActivity({
        packageName: "org.autojs.autoxjs.v6",
        className: mainActivity,
    });
    waitForActivity(mainActivity)
}
requestScreenCapture()
sleep(1000)
let img = captureScreen()
let start = new Date()
let result = gmlkit.ocr(img, "zh")
toastLog('OCR识别耗时:' + (new Date() - start) + 'ms')
let managerBtn = result.find(3, e => e.text == "管理")
if (managerBtn) click(managerBtn.bounds)
sleep(500)
let homeBtn = result.find(3, e => e.text == "主页")
if (homeBtn) click(homeBtn.bounds)
sleep(500)
let docBtn = result.find(3, e => e.text == "文档")
if (docBtn) press(docBtn.bounds, 500)
// 回收图片
img.recycle()

HTTP网络请求

获取网页

var url = "www.baidu.com";
var res = http.get(url);
if(res.statusCode == 200){
    toast("请求成功");
    console.show();
    log(res.body.string());
}else{
    toast("请求失败:" + res.statusMessage);
}

文件上传

//如果遇到SocketTimeout的异常,重新多运行几次脚本即可

console.show();
example1();
example2();
example3();
example4();
example5();

function example1(){
    var res = http.postMultipart("http://posttestserver.com/post.php", {
        "file": open("/sdcard/1.txt")
    });
    log("例子1:");
    log(res.body.string());
}

function example2(){
    var res = http.postMultipart("http://posttestserver.com/post.php", {
        "file": ["1.txt", "/sdcard/1.txt"]
    });
    log("例子2:");
    log(res.body.string());
}

function example3(){
    var res = http.postMultipart("http://posttestserver.com/post.php", {
        "file": ["1.txt", "text/plain", "/sdcard/1.txt"]
    });
    log("例子3:");
    log(res.body.string());
}

function example4(){
    var res = http.postMultipart("http://posttestserver.com/post.php", {
        "file": open("/sdcard/1.txt"),
        "aKey": "aValue"
    });
    log("例子4:");
    log(res.body.string());
}

文件下载

var url = "http://www.autojs.org/assets/uploads/profile/3-profileavatar.png";
var res = http.get(url);
if(res.statusCode != 200){
    toast("请求失败");
}
files.writeBytes("/sdcard/1.png", res.body.bytes());
toast("下载成功");
app.viewFile("/sdcard/1.png");

javascript

数字

a = 5;
b = 6;
c = -1;
x = 1.5;
y = a * x * x + b * x * c;
log("y = " + y);
openConsole();

E4X

/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

print("----------------------------------------");

// Use the XML constructor to parse an string into an XML object
var John = "<employee><name>John</name><age>25</age></employee>";
var Sue ="<employee><name>Sue</name><age>32</age></employee>";
var tagName = "employees";
var employees = new XML("<" + tagName +">" + John + Sue + "</" + tagName +">");
print("The employees XML object constructed from a string is:
" + employees);

print("----------------------------------------");

// Use an XML literal to create an XML object
var order = <order>
   <customer>
      <firstname>John</firstname>
      <lastname>Doe</lastname>
   </customer>
   <item>
      <description>Big Screen Television</description>
      <price>1299.99</price>
      <quantity>1</quantity>
   </item>
</order>

// Construct the full customer name
var name = order.customer.firstname + " " + order.customer.lastname;

// Calculate the total price
var total = order.item.price * order.item.quantity;

print("The order XML object constructed using a literal is:
" + order);
print("The total price of " + name + "'s order is " + total);

print("----------------------------------------");

// construct a new XML object using expando and super-expando properties
var order = <order/>;
order.customer.name = "Fred Jones";
order.customer.address.street = "123 Long Lang";
order.customer.address.city = "Underwood";
order.customer.address.state = "CA";
order.item[0] = "";
order.item[0].description = "Small Rodents";
order.item[0].quantity = 10;
order.item[0].price = 6.95;

print("The order custructed using expandos and super-expandos is:
" + order);

// append a new item to the order
order.item += <item><description>Catapult</description><price>139.95</price></item>;

print("----------------------------------------");

print("The order after appending a new item is:
" + order);

print("----------------------------------------");

// dynamically construct an XML element using embedded expressions
var tagname = "name";
var attributename = "id";
var attributevalue = 5;
var content = "Fred";

var x = <{tagname} {attributename}={attributevalue}>{content}</{tagname}>;

print("The dynamically computed element value is:
" + x.toXMLString());

print("----------------------------------------");

// Create a SOAP message
var message = <soap:Envelope
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
      soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
   <soap:Body>
      <m:GetLastTradePrice xmlns:m="http://mycompany.com/stocks">
         <symbol>DIS</symbol>
      </m:GetLastTradePrice>
   </soap:Body>
</soap:Envelope>

// declare the SOAP and stocks namespaces
var soap = new Namespace("http://schemas.xmlsoap.org/soap/envelope/");
var stock = new Namespace ("http://mycompany.com/stocks");

// extract the soap encoding style and body from the soap message
var encodingStyle = message.@soap::encodingStyle;

print("The encoding style of the soap message is specified by:
" + encodingStyle);

// change the stock symbol
message.soap::Body.stock::GetLastTradePrice.symbol = "MYCO";

var body = message.soap::Body;

print("The body of the soap message is:
" + body);

print("----------------------------------------");

// create an manipulate an XML object using the default xml namespace

default xml namespace = "http://default.namespace.com";
var x = <x/>;
x.a = "one";
x.b = "two";
x.c = <c xmlns="http://some.other.namespace.com">three</c>;

print("XML object constructed using the default xml namespace:
" + x);

default xml namespace="";

print("----------------------------------------");

var order = <order id = "123456" timestamp="Mon Mar 10 2003 16:03:25 GMT-0800 (PST)">
   <customer>
      <firstname>John</firstname>
      <lastname>Doe</lastname>
   </customer>
   <item id="3456">
      <description>Big Screen Television</description>
      <price>1299.99</price>
      <quantity>1</quantity>
   </item>
   <item id = "56789">
      <description>DVD Player</description>
      <price>399.99</price>
      <quantity>1</quantity>
   </item>
</order>;

// get the customer element from the orderprint("The customer is:
" + order.customer);

// get the id attribute from the order
print("The order id is:" + order.@id);

// get all the child elements from the order element
print("The children of the order are:
" + order.*); 

// get the list of all item descriptions
print("The order descriptions are:
" + order.item.description); 

// get second item by numeric index
print("The second item is:
" + order.item[1]);

// get the list of all child elements in all item elements
print("The children of the items are:
" + order.item.*);

// get the second child element from the order by index
print("The second child of the order is:
" + order.*[1]);

// calculate the total price of the order
var totalprice = 0;
for each (i in order.item) {
 totalprice += i.price * i.quantity;
}
print("The total price of the order is: " + totalprice);

print("----------------------------------------");

var e = <employees>
   <employee id="1"><name>Joe</name><age>20</age></employee>
   <employee id="2"><name>Sue</name><age>30</age></employee>
</employees>;

// get all the names in e
print("All the employee names are:
" + e..name);

// employees with name Joe
print("The employee named Joe is:
" + e.employee.(name == "Joe"));

// employees with id's 1 & 2
print("Employees with ids 1 & 2:
" + e.employee.(@id == 1 || @id == 2)); 

// name of employee with id 1
print("Name of the the employee with ID=1: " + e.employee.(@id == 1).name);

print("----------------------------------------");

openConsole();

HelloWorld

log("Hello world!!!");
toast("Hello, AutoJs!");
console.show();

PaddleOCR

PaddleOCR文本识别

let img = images.read("./0.jpg")
// PaddleOCR 移动端提供了两种模型:ocr_v2_for_cpu与ocr_v2_for_cpu(slim),此选项用于选择加载的模型,默认true使用v2的slim版(速度更快),false使用v2的普通版(准确率更高)
let useSlim = true
let start = new Date()
// 识别图片中的文字,返回完整识别信息(兼容百度OCR格式)。
let result = paddle.ocr(img, useSlim)
log('OCR识别耗时:' + (new Date() - start) + 'ms')
// 可以使用简化的调用命令,默认参数:cpuThreadNum = 4, useSlim = true
// const result = paddle.ocr(img)
toastLog("完整识别信息: " + JSON.stringify(result))
start = new Date()
// 识别图片中的文字,只返回文本识别信息(字符串列表)。当前版本可能存在文字顺序错乱的问题 建议先使用detect后自行排序
const stringList = paddle.ocrText(img, useSlim)
log('OCR纯文本识别耗时:' + (new Date() - start) + 'ms')
// 可以使用简化的调用命令,默认参数:cpuThreadNum = 4, useSlim = true
// const stringList = paddle.ocrText(img)
toastLog("文本识别信息: " + JSON.stringify(stringList))

// 回收图片
img.recycle()
// 释放native内存,非必要,供万一出现内存泄露时使用
// paddle.release()

// project.json
{
  "abis": [
    "arm64-v8a",
    "armeabi-v7a",
    "x86",
    "x86_64"
  ],
  "assets": [
    {
      "form": "file:///android_asset/models",
      "to": "/models"
    }
  ],
  "buildDir": "build",
  "build": {
    "build_id": null,
    "build_number": 0,
    "build_time": 0
  },
  "useFeatures": [],
  "icon": null,
  "ignoredDirs": [],
  "launchConfig": {
    "displaySplash": false,
    "hideLauncher": false,
    "hideLogs": false,
    "stableMode": false,
    "volumeUpcontrol": false,
    "permissions": [],
    "serviceDesc": "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。",
    "splashIcon": null,
    "splashText": "Powered by paddle ocr"
  },
  "libs": [
    "libc++_shared.so",
    "libpaddle_light_api_shared.so",
    "libhiai.so",
    "libhiai_ir.so",
    "libhiai_ir_build.so",
    "libNative.so",
    "libjackpal-androidterm5.so",
    "libjackpal-termexec2.so"
  ],
  "main": "PaddleOCR.js",
  "name": "PaddleOCR",
  "outputPath": null,
  "packageName": "com.script.paddleocr",
  "scripts": {},
  "signingConfig": {
    "alias": null,
    "keystore": null
  },
  "sourcePath": null,
  "versionCode": 1,
  "versionName": "1.0.0"
}

PaddleOCR文本识别-自定义模型路径

let img = images.read("./0.jpg")
// 新增:自定义模型路径(必须是绝对路径), files.path() 将相对路径转为绝对路径
let myModelPath = files.path("./models");
let start = new Date()
// 识别图片中的文字,返回完整识别信息(兼容百度OCR格式)。
let result = paddle.ocr(img, myModelPath)
log('OCR识别耗时:' + (new Date() - start) + 'ms')
toastLog("完整识别信息: " + JSON.stringify(result))
start = new Date()
// 识别图片中的文字,只返回文本识别信息(字符串列表)。当前版本可能存在文字顺序错乱的问题 建议先使用detect后自行排序
const stringList = paddle.ocrText(img, myModelPath)
log('OCR纯文本识别耗时:' + (new Date() - start) + 'ms')
toastLog("文本识别信息: " + JSON.stringify(stringList))

// 回收图片
img.recycle()
// 释放native内存,非必要,供万一出现内存泄露时使用
// paddle.release()

// project.json
{
  "abis": [
    "arm64-v8a",
    "armeabi-v7a",
    "x86",
    "x86_64"
  ],
  "assets": [
    {
      "form": "models",
      "to": "/models"
    }
  ],
  "buildDir": "build",
  "build": {
    "build_id": null,
    "build_number": 0,
    "build_time": 0
  },
  "useFeatures": [],
  "icon": null,
  "ignoredDirs": [],
  "launchConfig": {
    "displaySplash": false,
    "hideLauncher": false,
    "hideLogs": false,
    "stableMode": false,
    "volumeUpcontrol": false,
    "permissions": [],
    "serviceDesc": "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。",
    "splashIcon": null,
    "splashText": "Powered by paddle ocr"
  },
  "libs": [
    "libc++_shared.so",
    "libpaddle_light_api_shared.so",
    "libhiai.so",
    "libhiai_ir.so",
    "libhiai_ir_build.so",
    "libNative.so",
    "libjackpal-androidterm5.so",
    "libjackpal-termexec2.so"
  ],
  "main": "PaddleOCR(自定义模型路径).js",
  "name": "PaddleOCR(自定义模型路径)",
  "outputPath": null,
  "packageName": "com.script.paddleocr.custommodel",
  "scripts": {},
  "signingConfig": {
    "alias": null,
    "keystore": null
  },
  "sourcePath": null,
  "versionCode": 1,
  "versionName": "1.0.0"
}

PaddleOCR-截图识别

let currentEngine = engines.myEngine()
let runningEngines = engines.all()
let currentSource = currentEngine.getSource() + ''
if (runningEngines.length > 1) {
  runningEngines.forEach(compareEngine => {
    let compareSource = compareEngine.getSource() + ''
    if (currentEngine.id !== compareEngine.id && compareSource === currentSource) {
      // 强制关闭同名的脚本
      compareEngine.forceStop()
    }
  })
}

if (!requestScreenCapture()) {
  toastLog('请求截图权限失败')
  exit()
}

sleep(1000)

// 识别结果和截图信息
let result = []
let img = null
let running = true
let capturing = true

/**
 * 截图并识别OCR文本信息
 */
function captureAndOcr() {
  capturing = true
  img && img.recycle()
  img = captureScreen()
  if (!img) {
    toastLog('截图失败')
  }
  let start = new Date()
  result = paddle.ocr(img);
  log(result);
  toastLog('耗时' + (new Date() - start) + 'ms')
  capturing = false
}

captureAndOcr()

// 获取状态栏高度
let offset = -getStatusBarHeightCompat()
//let offset = 0;

// 绘制识别结果
let window = floaty.rawWindow(
  <canvas id="canvas" layout_weight="1" />
);

// 设置悬浮窗位置
ui.post(() => {
  window.setPosition(0, offset)
  window.setSize(device.width, device.height)
  window.setTouchable(false)
})

// 操作按钮
let clickButtonWindow = floaty.rawWindow(
  <vertical>
    <button id="captureAndOcr" text="截图识别" />
    <button id="closeBtn" text="退出" />
  </vertical>
);
ui.run(function () {
  clickButtonWindow.setPosition(device.width / 2 - ~~(clickButtonWindow.getWidth() / 2), device.height * 0.65)
})

// 点击识别
clickButtonWindow.captureAndOcr.click(function () {
  result = []
  ui.run(function () {
    clickButtonWindow.setPosition(device.width, device.height)
  })
  setTimeout(() => {
    threads.start(()=>{
      captureAndOcr()
      ui.run(function () {
        clickButtonWindow.setPosition(device.width / 2 - ~~(clickButtonWindow.getWidth() / 2), device.height * 0.65)
      })
    })
  }, 500)
})

// 点击关闭
clickButtonWindow.closeBtn.click(function () {
  exit()
})

let Typeface = android.graphics.Typeface
let paint = new Paint()
paint.setStrokeWidth(1)
paint.setTypeface(Typeface.DEFAULT_BOLD)
paint.setTextAlign(Paint.Align.LEFT)
paint.setAntiAlias(true)
paint.setStrokeJoin(Paint.Join.ROUND)
paint.setDither(true)
window.canvas.on('draw', function (canvas) {
  if (!running || capturing) {
    return
  }
  // 清空内容
  canvas.drawColor(0xFFFFFF, android.graphics.PorterDuff.Mode.CLEAR)
  if (result && result.length > 0) {
    for (let i = 0; i < result.length; i++) {
      let ocrResult = result[i]
      drawRectAndText(ocrResult.words + ' #信心:' + ocrResult.confidence.toFixed(2), ocrResult.bounds, '#00ff00', canvas, paint);
    }
  }
})

setInterval(() => { }, 10000)
events.on('exit', () => {
  // 标记停止 避免canvas导致闪退
  running = false
  // 回收图片
  img && img.recycle()
  // 撤销监听
  window.canvas.removeAllListeners()

})

/**
 * 绘制文本和方框
 *
 * @param {*} desc
 * @param {*} rect
 * @param {*} colorStr
 * @param {*} canvas
 * @param {*} paint
 */
function drawRectAndText (desc, rect, colorStr, canvas, paint) {
  let color = colors.parseColor(colorStr)

  paint.setStrokeWidth(1)
  paint.setStyle(Paint.Style.STROKE)
  // 反色
  paint.setARGB(255, 255 - (color >> 16 & 0xff), 255 - (color >> 8 & 0xff), 255 - (color & 0xff))
  canvas.drawRect(rect, paint)
  paint.setARGB(255, color >> 16 & 0xff, color >> 8 & 0xff, color & 0xff)
  paint.setStrokeWidth(1)
  paint.setTextSize(20)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawText(desc, rect.left, rect.top, paint)
  paint.setTextSize(10)
  paint.setStrokeWidth(1)
  paint.setARGB(255, 0, 0, 0)
}

/**
 * 获取状态栏高度
 *
 * @returns
 */
function getStatusBarHeightCompat () {
  let result = 0
  let resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android")
  if (resId > 0) {
    result = context.getResources().getDimensionPixelOffset(resId)
  }
  if (result <= 0) {
    result = context.getResources().getDimensionPixelOffset(R.dimen.dimen_25dp)
  }
  return result
}

Shell命令

冻结网易云音乐

shell("pm disable com.netease.cloudmusic", true);

结束所有后台进程

shell("am kill-all", true);

解冻并打开网易云音乐

shell("pm enable com.netease.cloudmusic", true);
launchApp("网易云音乐");

锁屏

KeyCode("KEYCODE_POWER");
//或者 KeyCode(26);

TessractOCR

//此例子仅作为演示,无法运行,因为tessdata目录下没有训练数据,
//如需运行,可前往github下载完整例子:https://github.com/wilinz/autoxjs-tessocr
//导包
importClass(com.googlecode.tesseract.android.TessBaseAPI)
//新建OCR实例
var tessocr = new TessBaseAPI()
//请求截图权限
requestScreenCapture(false);
//3秒后开始
toastLog("3秒后截图")
sleep(3000)
toastLog("开始截图")
//截图 
var img = captureScreen();
//tessdata目录所在的文件夹,目录下放置训练数据
//训练数据下载地址:https://github.com/tesseract-ocr/tessdata/tree/4.0.0
var dataPath = files.path("./")
//初始化tessocr
//第二个参数是初始化的语言,是数据文件去掉扩展名后的文件名,多个语言用+连接
//训练数据文件夹必须命名为tessdata
//训练数据下载时是什么名字就是什么名字,不能改
var ok = tessocr.init(dataPath, "eng+chi_sim")
if (ok) {
    toastLog("初始化成功: " + tessocr.getInitLanguagesAsString())
} else {
    toastLog("初始化失败")
}
//设置图片
tessocr.setImage(img.getBitmap())
//打印文本结果
toastLog(tessocr.getUTF8Text())
//如需获取位置等其他结果请看文档

// project.json
{
  "abis": [
    "arm64-v8a",
    "armeabi-v7a",
    "x86",
    "x86_64"
  ],
  "assets": [
  ],
  "buildDir": "build",
  "build": {
    "build_id": null,
    "build_number": 0,
    "build_time": 0
  },
  "useFeatures": [],
  "icon": null,
  "ignoredDirs": [
    "build"
  ],
  "launchConfig": {
    "displaySplash": false,
    "hideLauncher": false,
    "hideLogs": false,
    "stableMode": false,
    "volumeUpcontrol": false,
    "permissions": [],
    "serviceDesc": "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。",
    "splashIcon": null,
    "splashText": "Powered by TessOCR"
  },
  "libs": [
    "libtesseract.so",
    "libpng.so",
    "libleptonica.so",
    "libjpeg.so",
    "libjackpal-androidterm5.so",
    "libjackpal-termexec2.so"
  ],
  "main": "main.js",
  "name": "TessOCR",
  "outputPath": null,
  "packageName": "com.script.tessocr",
  "projectDirectory": null,
  "scripts": {},
  "signingConfig": {
    "alias": null,
    "keystore": null
  },
  "sourcePath": null,
  "versionCode": 1,
  "versionName": "1.0.0"
}

Web扩展与游戏编程

贪吃蛇

"ui";

ui.layout(
    <vertical>
        <canvas id="board" layout_weight="1"/>
        <relative h="120">
            <button id="up" text="↑" w="60" h="60" layout_alignParentTop="true" layout_centerHorizontal="true"/>
            <button id="left" text="←" w="60" h="60" layout_alignParentBottom="true" layout_toLeftOf="@id/down"/>
            <button id="down" text="↓" w="60" h="60" layout_alignParentBottom="true" layout_centerHorizontal="true"/>
            <button id="right" text="→" w="60" h="60" layout_alignParentBottom="true" layout_toRightOf="@id/down"/>
        </relative>
    </vertical>
);

//蛇的颜色
const SNAKE_COLOR = colors.parseColor("#4caf50");
//背景色
const BG_COLOR = colors.parseColor("#ffffff");
//苹果颜色
const APPLE_COLOR = colors.parseColor("#f44336");
//墙的颜色
const WALL_COLOR = colors.parseColor("#607d8b");
//文本颜色
const TEXT_COLOR =  colors.parseColor("#03a9f4");

//蛇自动移动的时间间隔,调小可以增加难度
const MOVE_INTERVAL = 500;
//方块宽度
const BLOCK_WIDTH = 40;
//游戏区域宽高
const GAME_BOARD_HEIGHT = 20;
const GAME_BOARD_WIDTH = 15;

//蛇的四个移动方向
const DIRECTION_LEFT = {x: -1, y: 0};
const DIRECTION_RIGHT = {x: 1, y: 0};
const DIRECTION_UP = {x: 0, y: -1};
const DIRECTION_DOWN = {x: 0, y: 1};

//蛇,是一个蛇身的坐标的数组
var snake = [{x: 4, y: 2}, {x: 3, y: 2}, {x: 2, y: 2}];
//苹果的坐标
var apple = generateApple();
//当前蛇的移动方向
var direction = DIRECTION_RIGHT;
//标记游戏是否结束
var isGameOver = false;
//分数
var score = 0;

var paint = new Paint();
ui.board.on("draw", function(canvas){
    //绘制背景色
    canvas.drawColor(BG_COLOR);
    //绘制分数
    paint.setColor(TEXT_COLOR);
    paint.setTextSize(50);
    canvas.drawText("分数: " + score, 30, 70, paint);
    //如果游戏结束则绘制游戏结束字样
    if(isGameOver){
        canvas.drawText("游戏结束!", canvas.getWidth() - 280, 70, paint);
    }
    //计算坐标偏移,是的游戏区域绘制在画面的水平居中位置
    var offset = {
        x: (canvas.getWidth() - (GAME_BOARD_WIDTH + 2) * BLOCK_WIDTH) / 2,
        y: 100
    };
    //偏移坐标
    canvas.translate(offset.x, offset.y);
    //绘制围墙
    paint.setColor(WALL_COLOR);
    for(var i = 0; i <= GAME_BOARD_WIDTH + 1; i++){
        //上围墙
        drawBlock(canvas, paint, i, 0);
        //下围墙
        drawBlock(canvas, paint, i, GAME_BOARD_HEIGHT + 1);
    }
    for(var i = 0; i <= GAME_BOARD_HEIGHT + 1; i++){
        //左围墙
        drawBlock(canvas, paint, 0, i);
        //右围墙
        drawBlock(canvas, paint, GAME_BOARD_WIDTH + 1, i);
    }
    //绘制蛇身
    paint.setColor(SNAKE_COLOR);
    for(var i = 0; i < snake.length; i++){
        drawBlock(canvas, paint, snake[i].x, snake[i].y);
    }
    //绘制苹果
    paint.setColor(APPLE_COLOR);
    drawBlock(canvas, paint, apple.x, apple.y);
});

//启动游戏线程
var gameThread = threads.start(game);

//按键点击时改变蛇的移动方向
ui.left.on("click", ()=> direction = DIRECTION_LEFT);
ui.right.on("click", ()=> direction = DIRECTION_RIGHT);
ui.up.on("click", ()=> direction = DIRECTION_UP);
ui.down.on("click", ()=> direction = DIRECTION_DOWN);

function game(){
    //每隔一段时间让蛇自动前进
    setInterval(()=>{
        move(direction.x, direction.y);
    }, MOVE_INTERVAL);
}

function move(dx, dy){
    log("move: %d, %d", dx, dy);
    direction.x = dx;
    direction.y = dy;
    //蛇前进时把一个新的方块添加到蛇头前面
    var head = snake[0];
    snake.splice(0, 0, {
        x: head.x + dx,
        y: head.y + dy
    });
    //如果蛇头吃到了苹果
    if(snakeEatsApple()){
        //添加分数和重新生成苹果
        score += 5;
        apple = generateApple();
    }else{
        //没有吃到苹果的情况下把蛇尾去掉保持蛇身长度不变
        snake.pop();
    }
    //碰撞检测
    collisionTest();
}

function snakeEatsApple(){
    return snake[0].x == apple.x && snake[0].y == apple.y;
}

function generateApple(){
    //循环生成苹果直至苹果不会生成在蛇身上
    var x, y;
    do{
        x = random(1, GAME_BOARD_WIDTH);
        y = random(1, GAME_BOARD_HEIGHT);
    }while(!isAppleValid(x, y));
    return {x: x, y: y};
}

function isAppleValid(x, y){
    for (var i = 0; i < snake.length; i++) {
        if (snake[i].x == x && snake[i].y == y) {
            return false;
        }
    }
    return true;
}

function collisionTest(){
    //检测蛇有没有撞到墙上
    var head = snake[0];
    if(head.x < 1 || head.x > GAME_BOARD_WIDTH
        || head.y < 1 || head.y > GAME_BOARD_HEIGHT){
            gameOver();
            return;
    }
    //检测蛇有没有撞到自己
    for(var i = 1; i < snake.length; i++){
        if(snake[i].x == head && snake[i].y == head){
            gameOver();
            return;
        }
    }
}

function gameOver(){
    gameThread.interrupt();
    isGameOver = true;
}

function drawBlock(canvas, paint, x, y){
    x *= BLOCK_WIDTH;
    y *= BLOCK_WIDTH;
    canvas.drawRect(x, y, x + BLOCK_WIDTH, y + BLOCK_WIDTH, paint);
}

贪吃蛇重力版

"ui";

ui.layout(
    <vertical>
        <canvas id="board" layout_weight="1"/>
        <relative h="120">
            <button id="up" text="↑" w="60" h="60" layout_alignParentTop="true" layout_centerHorizontal="true"/>
            <button id="left" text="←" w="60" h="60" layout_alignParentBottom="true" layout_toLeftOf="@id/down"/>
            <button id="down" text="↓" w="60" h="60" layout_alignParentBottom="true" layout_centerHorizontal="true"/>
            <button id="right" text="→" w="60" h="60" layout_alignParentBottom="true" layout_toRightOf="@id/down"/>
        </relative>
    </vertical>
);

//蛇的颜色
const SNAKE_COLOR = colors.parseColor("#4caf50");
//背景色
const BG_COLOR = colors.parseColor("#ffffff");
//苹果颜色
const APPLE_COLOR = colors.parseColor("#f44336");
//墙的颜色
const WALL_COLOR = colors.parseColor("#607d8b");
//文本颜色
const TEXT_COLOR =  colors.parseColor("#03a9f4");

//蛇自动移动的时间间隔,调小可以增加难度
const MOVE_INTERVAL = 500;
//方块宽度
const BLOCK_WIDTH = 40;
//游戏区域宽高
const GAME_BOARD_HEIGHT = 20;
const GAME_BOARD_WIDTH = 15;

//蛇的四个移动方向
const DIRECTION_LEFT = {x: -1, y: 0};
const DIRECTION_RIGHT = {x: 1, y: 0};
const DIRECTION_UP = {x: 0, y: -1};
const DIRECTION_DOWN = {x: 0, y: 1};

//蛇,是一个蛇身的坐标的数组
var snake = [{x: 4, y: 2}, {x: 3, y: 2}, {x: 2, y: 2}];
//苹果的坐标
var apple = generateApple();
//当前蛇的移动方向
var direction = DIRECTION_RIGHT;
//标记游戏是否结束
var isGameOver = false;
//分数
var score = 0;

var paint = new Paint();
ui.board.on("draw", function(canvas){
    //绘制背景色
    canvas.drawColor(BG_COLOR);
    //绘制分数
    paint.setColor(TEXT_COLOR);
    paint.setTextSize(50);
    canvas.drawText("分数: " + score, 30, 70, paint);
    //如果游戏结束则绘制游戏结束字样
    if(isGameOver){
        canvas.drawText("游戏结束!", canvas.getWidth() - 280, 70, paint);
    }
    //计算坐标偏移,是的游戏区域绘制在画面的水平居中位置
    var offset = {
        x: (canvas.getWidth() - (GAME_BOARD_WIDTH + 2) * BLOCK_WIDTH) / 2,
        y: 100
    };
    //偏移坐标
    canvas.translate(offset.x, offset.y);
    //绘制围墙
    paint.setColor(WALL_COLOR);
    for(var i = 0; i <= GAME_BOARD_WIDTH + 1; i++){
        //上围墙
        drawBlock(canvas, paint, i, 0);
        //下围墙
        drawBlock(canvas, paint, i, GAME_BOARD_HEIGHT + 1);
    }
    for(var i = 0; i <= GAME_BOARD_HEIGHT + 1; i++){
        //左围墙
        drawBlock(canvas, paint, 0, i);
        //右围墙
        drawBlock(canvas, paint, GAME_BOARD_WIDTH + 1, i);
    }
    //绘制蛇身
    paint.setColor(SNAKE_COLOR);
    for(var i = 0; i < snake.length; i++){
        drawBlock(canvas, paint, snake[i].x, snake[i].y);
    }
    //绘制苹果
    paint.setColor(APPLE_COLOR);
    drawBlock(canvas, paint, apple.x, apple.y);
});

//启动游戏线程
var gameThread = threads.start(game);

//按键点击时改变蛇的移动方向
ui.left.on("click", ()=> direction = DIRECTION_LEFT);
ui.right.on("click", ()=> direction = DIRECTION_RIGHT);
ui.up.on("click", ()=> direction = DIRECTION_UP);
ui.down.on("click", ()=> direction = DIRECTION_DOWN);

function game(){
    //每隔一段时间让蛇自动前进
    setInterval(()=>{
        move(direction.x, direction.y);
    }, MOVE_INTERVAL);
}

function move(dx, dy){
    log("move: %d, %d", dx, dy);
    direction.x = dx;
    direction.y = dy;
    //蛇前进时把一个新的方块添加到蛇头前面
    var head = snake[0];
    snake.splice(0, 0, {
        x: head.x + dx,
        y: head.y + dy
    });
    //如果蛇头吃到了苹果
    if(snakeEatsApple()){
        //添加分数和重新生成苹果
        score += 5;
        apple = generateApple();
    }else{
        //没有吃到苹果的情况下把蛇尾去掉保持蛇身长度不变
        snake.pop();
    }
    //碰撞检测
    collisionTest();
}

function snakeEatsApple(){
    return snake[0].x == apple.x && snake[0].y == apple.y;
}

function generateApple(){
    //循环生成苹果直至苹果不会生成在蛇身上
    var x, y;
    do{
        x = random(1, GAME_BOARD_WIDTH);
        y = random(1, GAME_BOARD_HEIGHT);
    }while(!isAppleValid(x, y));
    return {x: x, y: y};
}

function isAppleValid(x, y){
    for (var i = 0; i < snake.length; i++) {
        if (snake[i].x == x && snake[i].y == y) {
            return false;
        }
    }
    return true;
}

function collisionTest(){
    //检测蛇有没有撞到墙上
    var head = snake[0];
    if(head.x < 1 || head.x > GAME_BOARD_WIDTH
        || head.y < 1 || head.y > GAME_BOARD_HEIGHT){
            gameOver();
            return;
    }
    //检测蛇有没有撞到自己
    for(var i = 1; i < snake.length; i++){
        if(snake[i].x == head && snake[i].y == head){
            gameOver();
            return;
        }
    }
}

function gameOver(){
    gameThread.interrupt();
    isGameOver = true;
}

function drawBlock(canvas, paint, x, y){
    x *= BLOCK_WIDTH;
    y *= BLOCK_WIDTH;
    canvas.drawRect(x, y, x + BLOCK_WIDTH, y + BLOCK_WIDTH, paint);
}

AutoX注入webviwe

"ui";
ui.layout(
    <vertical>
        <horizontal bg="#c7edcc" gravity="center" h="auto">
            <button text="网络冲浪" id="surfInternetBtn" style="Widget.AppCompat.Button.Colored" w="auto" />
            <button text="记忆翻牌" id="loadLocalHtmlBtn" style="Widget.AppCompat.Button.Colored" w="auto" />
            <button text="控制台" id="consoleBtn" style="Widget.AppCompat.Button.Colored" w="auto" />
        </horizontal>
        <vertical h="*" w="*">
            <webview id="webView" layout_below="title" w="*" h="*" />
        </vertical>
    </vertical>
);

function calljavascript(webViewWidget, script, callback) {
    try {
        console.assert(webViewWidget != null, "webView控件为空");
        //console.log(script.toString())
        webViewWidget.evaluatejavascript("javascript:" + script, new JavaAdapter(android.webkit.ValueCallback, {
            onReceiveValue: (val) => {
                if (callback) {
                    callback(val);
                }
            }
        }));
    } catch (e) {
        console.error("执行javascript失败");
        console.trace(e);
    }
}

function AutoX() {
    let getAutoXFrame = () => {
        let bridgeFrame = document.getElementById("AutoXFrame");
        if (!bridgeFrame) {
            bridgeFrame = document.createElement('iframe');
            bridgeFrame.id = "AutoXFrame";
            bridgeFrame.style = "display: none";
            document.body.append(bridgeFrame);
        }
        return bridgeFrame;
    };
    const h5Callbackers = {};
    let h5CallbackIndex = 1;
    let setCallback = (callback) => {
        let callId = h5CallbackIndex++;
        h5Callbackers[callId] = {
            "callback": callback
        };
        return callId;
    };
    let getCallback = (callId) => {
        let callback = h5Callbackers[callId];
        if (callback) {
            delete h5Callbackers[callId];
        }
        return callback;
    };

    function invoke(cmd, params, callback) {
        let callId = null;
        try {
            let paramsStr = JSON.stringify(params);
            let AutoXFrame = getAutoXFrame();
            callId = setCallback(callback);
            AutoXFrame.src = "jsbridge://" + cmd + "/" + callId + "/" + encodeURIComponent(paramsStr);
        } catch (e) {
            if (callId) {
                getCallback(callId);
            }
            console.trace(e);
        }
    };
    let callback = (data) => {
        let callId = data.callId;
        let params = data.params;
        let callbackFun = getCallback(callId);
        if (callbackFun && callbackFun.callback) {
            callbackFun.callback(params);
        }
    };
    return {
        invoke: invoke,
        callback: callback
    };
};
function bridgeHandler_handle(cmd, params) {
    console.log('bridgeHandler处理 cmd=%s, params=%s', cmd, JSON.stringify(params));
    let fun = this[cmd];
    if (!fun) {
        throw new Error("cmd= " + cmd + " 没有定义实现");
    }
    let ret = fun(params)
    return ret;
}
function mFunction(params) {
    toastLog(params.toString());
    device.vibrate(120);
    return files.isDir('/storage/emulated/0/Download')//'toast提示成功';
}
function webViewExpand_init(webViewWidget) {
    webViewWidget.webViewClient = new JavaAdapter(android.webkit.WebViewClient, {
        onPageFinished: (webView, curUrl) => {
            try {
                // 注入 AutoX
                calljavascript(webView, AutoX.toString() + ";var auto0 = AutoX();auto0.invoke('mFunction','This is AutoX!',(data) => {console.log('接收到callback1:' + JSON.stringify(data));});", null);
            } catch (e) {
                console.trace(e)
            }
        },
        shouldOverrideUrlLoading: (webView, request) => {
            let url = '';
            try {
                url = (request.a && request.a.a) || (request.url);
                if (url instanceof android.net.Uri) {
                    url = url.toString();
                }
                if (url.indexOf("jsbridge://") == 0) {
                    let uris = url.split("/");
                    let cmd = uris[2];
                    let callId = uris[3];
                    let params = java.net.URLDecoder.decode(uris[4], "UTF-8");
                    console.log('AutoX处理javascript调用请求: callId=%s, cmd=%s, params=%s', callId, cmd, params);
                    let result = null;
                    try {
                        result = bridgeHandler_handle(cmd, JSON.parse(params));
                    } catch (e) {
                        console.trace(e);
                        result = {
                            message: e.message
                        };
                    }
                    result = result || {};
                    webView.loadUrl("javascript:auto0.callback({'callId':" + callId + ", 'params': " + JSON.stringify(result) + "});");
                } else if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file://") || url.startsWith("ws://") || url.startsWith("wss://")) {
                    webView.loadUrl(url);
                } else {
                }
                return true;
            } catch (e) {
                if (e.javaException instanceof android.content.ActivityNotFoundException) {
                    webView.loadUrl(url);
                } else {
                    toastLog('无法打开URL: ' + url);
                }
                console.trace(e);
            }
        },
        onReceivedError: (webView, webResourceRequest, webResourceError) => {
            let url = webResourceRequest.getUrl();
            let errorCode = webResourceError.getErrorCode();
            let description = webResourceError.getDescription();
            console.trace(errorCode + " " + description + " " + url);
        }
    });
    webViewWidget.webChromeClient = new JavaAdapter(android.webkit.WebChromeClient, {
        onConsoleMessage: (msg) => {
            console.log("[%s:%s]: %s", msg.sourceId(), msg.lineNumber(), msg.message());
        }
    });
}
webViewExpand_init(ui.webView)
ui.webView.loadUrl("https://wht.im");

ui.surfInternetBtn.on("click", () => {
    webViewExpand_init(ui.webView);
    ui.webView.loadUrl("https://wht.im");
});
ui.consoleBtn.on("click", () => {
    app.startActivity("console");
});
ui.loadLocalHtmlBtn.on('click', () => {
    webViewExpand_init(ui.webView);
    let path = "file:" + files.path("game.html");
    ui.webView.loadUrl(path);
});

WebSocket

创建客户端

importPackage(Packages["okhttp3"]);
var client = new OkHttpClient.Builder().retryOnConnectionFailure(true).build();

var request = new Request.Builder().url("ws://192.168.31.164:9317").build();
client.dispatcher().cancelAll();//清理一次
myListener = {
    onOpen: function (webSocket, response) {
        print("onOpen");
        var json = {};
        json.type="hello";
        json.data= {device_name:"模拟设备",client_version:123,app_version:123,app_version_code:"233"};
        var hello=JSON.stringify(json);
        webSocket.send(hello);
    },
    onMessage: function (webSocket, msg) {
        print("msg");
        print(msg);
    },
    onClosing: function (webSocket, code, reason) {
        print("正在关闭");
    },
    onClosed: function (webSocket, code, reason) {
        print("关闭");
    },
    onFailure: function (webSocket, t, response) {
        print("错误");
        print( t);
    }
}
var webSocket= client.newWebSocket(request, new WebSocketListener(myListener));

setInterval(() => {

}, 1000);

WebSocket示例

// 新建一个WebSocket
// 指定web socket的事件回调在当前线程(好处是没有多线程问题要处理,坏处是不能阻塞当前线程,包括死循环)
// 不加后面的参数则回调在IO线程
let ws = web.newWebSocket("wss://demo.piesocket.com/v3/channel_1?notify_self", {
    eventThread: 'this'
});
console.show();
// 监听他的各种事件
ws.on("open", (res, ws) => {
    log("WebSocket已连接");
}).on("failure", (err, res, ws) => {
    log("WebSocket连接失败");
    console.error(err);
}).on("closing", (code, reason, ws) => {
    log("WebSocket关闭中");
}).on("text", (text, ws) => {
    console.info("收到文本消息: ", text);
}).on("binary", (bytes, ws) => {
    console.info("收到二进制消息:");
    console.info("hex: ", bytes.hex());
    console.info("base64: ", bytes.base64());
    console.info("md5: ", bytes.md5());
    console.info("size: ", bytes.size());
    console.info("bytes: ", bytes.toByteArray());
}).on("closed", (code, reason, ws) => {
    log("WebSocket已关闭: code = %d, reason = %s", code, reason);
});

// 发送文本消息
log("发送消息: Hello, WebSocket!");
ws.send("h");
setTimeout(() => {
    // 两秒后发送二进制消息
   log("发送二进制消息: 5piO5aSp5L2g6IO96ICDMTAw5YiG44CC");
   ws.send(web.ByteString.decodeBase64("5piO5aSp5L2g6IO96ICDMTAw5YiG44CC"));
}, 2000);
setTimeout(() => {
    // 8秒后断开WebSocket
    log("断开WebSocket");
    // 1000表示正常关闭
    ws.close(1000, null);
}, 8000);
setTimeout(() => {
    log("退出程序");
}, 12000)

本文转自 https://blog.csdn.net/qq_37952052/article/details/131743625?spm=1001.2014.3001.5502,如有侵权,请联系删除。