应用
打开应用
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("强制停止"));
卸载应用
<em>//输入应用名称
</em><em></em>var appName = rawInput('请输入要卸载的应用名称');
<em>//获取应用包名
</em><em></em>var packageName = getPackageName(appName);
if(!packageName){
toast("应用不存在!");
}else{
<em>//卸载应用
</em><em></em> app.uninstall(packageName);
}
应用工具
var i = dialogs.select("请选择工具", "获取应用包名", "打开应用详情页", "卸载应用");
if(i == -1){
alert("没有选择任何工具!");
}
switch(i){
case 0:
<em>//获取应用包名
</em><em></em> appName = rawInput("请输入应用名称", "QQ");
packageName = getPackageName(appName);
toast(packageName);
setClip(packageName);
toast("已复制到剪贴板");
break;
case 1:
<em>//打开应用详情页
</em><em></em> appName = rawInput("请输入应用名称", "微信");
openAppSetting(getPackageName(appName));
break;
case 2:
<em>//卸载应用
</em><em></em> 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()
<em>// 识别图片中的文字,返回完整识别信息(兼容百度OCR格式)。
</em><em>//可选语言:拉丁 "la" , 中文 "zh" ,梵文 "sa" ,日语 "ja" , 韩语 "ko"
</em><em></em>let result = gmlkit.ocr(img, "zh")
log('OCR识别耗时:' + (new Date() - start) + 'ms')
<em>//排序 sort()会改变原对象,sorted() 不会改变原对象,而是返回新对象
</em><em></em>result.sort()
<em>//或者
</em><em>//let newResult = result.sorted()
</em><em></em>log("识别信息: " + result)
log("-------------------------------------------")
log("文本识别信息: " + result.text)
log("-------------------------------------------")
toastLog("json识别信息: " + JSON.stringify(result))
<em>// 回收图片
</em><em></em>img.recycle()
<em>// project.json
</em><em></em>
{
"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) {
<em>// 强制关闭同名的脚本
</em><em></em> compareEngine.forceStop()
}
})
}
if (!requestScreenCapture()) {
toastLog('请求截图权限失败')
exit()
}
sleep(1000)
<em>// 识别结果和截图信息
</em><em></em>let result = []
let img = null
let running = true
let capturing = true
<em>/**
</em><em> * 截图并识别OCR文本信息
</em><em> */</em>
function captureAndOcr() {
capturing = true
img && img.recycle()
img = captureScreen()
if (!img) {
toastLog('截图失败')
}
let start = new Date()
<em>//结果转数组:层级:3
</em><em></em> result = gmlkit.ocr(img,"zh").toArray(3);
log(result);
toastLog('耗时' + (new Date() - start) + 'ms')
capturing = false
}
captureAndOcr()
<em>// 获取状态栏高度
</em><em></em>let offset = -getStatusBarHeightCompat()
<em>//let offset = 0;
</em><em></em>
<em>// 绘制识别结果
</em><em></em>let window = floaty.rawWindow(
<canvas id="canvas" layout_weight="1" />
);
<em>// 设置悬浮窗位置
</em><em></em>ui.post(() => {
window.setPosition(0, offset)
window.setSize(device.width, device.height)
window.setTouchable(false)
})
<em>// 操作按钮
</em><em></em>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)
})
<em>// 点击识别
</em><em></em>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)
})
<em>// 点击关闭
</em><em></em>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
}
<em>// 清空内容
</em><em></em> 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', () => {
<em>// 标记停止 避免canvas导致闪退
</em><em></em> running = false
<em>// 回收图片
</em><em></em> img && img.recycle()
<em>// 撤销监听
</em><em></em> window.canvas.removeAllListeners()
})
<em>/**
</em><em> * 绘制文本和方框
</em><em> *
</em><em> * @param {*} desc
</em><em> * @param {*} rect
</em><em> * @param {*} colorStr
</em><em> * @param {*} canvas
</em><em> * @param {*} paint
</em><em> */</em>
function drawRectAndText (desc, rect, colorStr, canvas, paint) {
let color = colors.parseColor(colorStr)
paint.setStrokeWidth(1)
paint.setStyle(Paint.Style.STROKE)
<em>// 反色
</em><em></em> 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)
}
<em>/**
</em><em> * 获取状态栏高度
</em><em> *
</em><em> * @returns
</em><em> */</em>
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识别点击
<em>//requestScreenCapture()
</em><em></em>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)
<em>// 回收图片
</em><em></em>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);
}
文件上传
<em>//如果遇到SocketTimeout的异常,重新多运行几次脚本即可
</em><em></em>
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
<em>/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
</em><em> *
</em><em> * This Source Code Form is subject to the terms of the Mozilla Public
</em><em> * License, v. 2.0. If a copy of the MPL was not distributed with this
</em><em> * file, You can obtain one at http://mozilla.org/MPL/2.0/. */</em>
print("----------------------------------------");
<em>// Use the XML constructor to parse an string into an XML object
</em><em></em>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:\n" + employees);
print("----------------------------------------");
<em>// Use an XML literal to create an XML object
</em><em></em>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>
<em>// Construct the full customer name
</em><em></em>var name = order.customer.firstname + " " + order.customer.lastname;
<em>// Calculate the total price
</em><em></em>var total = order.item.price * order.item.quantity;
print("The order XML object constructed using a literal is:\n" + order);
print("The total price of " + name + "'s order is " + total);
print("----------------------------------------");
<em>// construct a new XML object using expando and super-expando properties
</em><em></em>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:\n" + order);
<em>// append a new item to the order
</em><em></em>order.item += <item><description>Catapult</description><price>139.95</price></item>;
print("----------------------------------------");
print("The order after appending a new item is:\n" + order);
print("----------------------------------------");
<em>// dynamically construct an XML element using embedded expressions
</em><em></em>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:\n" + x.toXMLString());
print("----------------------------------------");
<em>// Create a SOAP message
</em><em></em>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>
<em>// declare the SOAP and stocks namespaces
</em><em></em>var soap = new Namespace("http://schemas.xmlsoap.org/soap/envelope/");
var stock = new Namespace ("http://mycompany.com/stocks");
<em>// extract the soap encoding style and body from the soap message
</em><em></em>var encodingStyle = message.@soap::encodingStyle;
print("The encoding style of the soap message is specified by:\n" + encodingStyle);
<em>// change the stock symbol
</em><em></em>message.soap::Body.stock::GetLastTradePrice.symbol = "MYCO";
var body = message.soap::Body;
print("The body of the soap message is:\n" + body);
print("----------------------------------------");
<em>// create an manipulate an XML object using the default xml namespace
</em><em></em>
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:\n" + 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>;
<em>// get the customer element from the orderprint("The customer is:\n" + order.customer);
</em><em></em>
<em>// get the id attribute from the order
</em><em></em>print("The order id is:" + order.@id);
<em>// get all the child elements from the order element
</em><em></em>print("The children of the order are:\n" + order.*);
<em>// get the list of all item descriptions
</em><em></em>print("The order descriptions are:\n" + order.item.description);
<em>// get second item by numeric index
</em><em></em>print("The second item is:\n" + order.item[1]);
<em>// get the list of all child elements in all item elements
</em><em></em>print("The children of the items are:\n" + order.item.*);
<em>// get the second child element from the order by index
</em><em></em>print("The second child of the order is:\n" + order.*[1]);
<em>// calculate the total price of the order
</em><em></em>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>;
<em>// get all the names in e
</em><em></em>print("All the employee names are:\n" + e..name);
<em>// employees with name Joe
</em><em></em>print("The employee named Joe is:\n" + e.employee.(name == "Joe"));
<em>// employees with id's 1 & 2
</em><em></em>print("Employees with ids 1 & 2:\n" + e.employee.(@id == 1 || @id == 2));
<em>// name of employee with id 1
</em><em></em>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")
<em>// PaddleOCR 移动端提供了两种模型:ocr_v2_for_cpu与ocr_v2_for_cpu(slim),此选项用于选择加载的模型,默认true使用v2的slim版(速度更快),false使用v2的普通版(准确率更高)
</em><em></em>let useSlim = true
let start = new Date()
<em>// 识别图片中的文字,返回完整识别信息(兼容百度OCR格式)。
</em><em></em>let result = paddle.ocr(img, useSlim)
log('OCR识别耗时:' + (new Date() - start) + 'ms')
<em>// 可以使用简化的调用命令,默认参数:cpuThreadNum = 4, useSlim = true
</em><em>// const result = paddle.ocr(img)
</em><em></em>toastLog("完整识别信息: " + JSON.stringify(result))
start = new Date()
<em>// 识别图片中的文字,只返回文本识别信息(字符串列表)。当前版本可能存在文字顺序错乱的问题 建议先使用detect后自行排序
</em><em></em>const stringList = paddle.ocrText(img, useSlim)
log('OCR纯文本识别耗时:' + (new Date() - start) + 'ms')
<em>// 可以使用简化的调用命令,默认参数:cpuThreadNum = 4, useSlim = true
</em><em>// const stringList = paddle.ocrText(img)
</em><em></em>toastLog("文本识别信息: " + JSON.stringify(stringList))
<em>// 回收图片
</em><em></em>img.recycle()
<em>// 释放native内存,非必要,供万一出现内存泄露时使用
</em><em>// paddle.release()
</em><em></em>
<em>// project.json
</em><em></em>{
"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")
<em>// 新增:自定义模型路径(必须是绝对路径), files.path() 将相对路径转为绝对路径
</em><em></em>let myModelPath = files.path("./models");
let start = new Date()
<em>// 识别图片中的文字,返回完整识别信息(兼容百度OCR格式)。
</em><em></em>let result = paddle.ocr(img, myModelPath)
log('OCR识别耗时:' + (new Date() - start) + 'ms')
toastLog("完整识别信息: " + JSON.stringify(result))
start = new Date()
<em>// 识别图片中的文字,只返回文本识别信息(字符串列表)。当前版本可能存在文字顺序错乱的问题 建议先使用detect后自行排序
</em><em></em>const stringList = paddle.ocrText(img, myModelPath)
log('OCR纯文本识别耗时:' + (new Date() - start) + 'ms')
toastLog("文本识别信息: " + JSON.stringify(stringList))
<em>// 回收图片
</em><em></em>img.recycle()
<em>// 释放native内存,非必要,供万一出现内存泄露时使用
</em><em>// paddle.release()
</em><em></em>
<em>// project.json
</em><em></em>{
"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) {
<em>// 强制关闭同名的脚本
</em><em></em> compareEngine.forceStop()
}
})
}
if (!requestScreenCapture()) {
toastLog('请求截图权限失败')
exit()
}
sleep(1000)
<em>// 识别结果和截图信息
</em><em></em>let result = []
let img = null
let running = true
let capturing = true
<em>/**
</em><em> * 截图并识别OCR文本信息
</em><em> */</em>
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()
<em>// 获取状态栏高度
</em><em></em>let offset = -getStatusBarHeightCompat()
<em>//let offset = 0;
</em><em></em>
<em>// 绘制识别结果
</em><em></em>let window = floaty.rawWindow(
<canvas id="canvas" layout_weight="1" />
);
<em>// 设置悬浮窗位置
</em><em></em>ui.post(() => {
window.setPosition(0, offset)
window.setSize(device.width, device.height)
window.setTouchable(false)
})
<em>// 操作按钮
</em><em></em>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)
})
<em>// 点击识别
</em><em></em>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)
})
<em>// 点击关闭
</em><em></em>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
}
<em>// 清空内容
</em><em></em> 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', () => {
<em>// 标记停止 避免canvas导致闪退
</em><em></em> running = false
<em>// 回收图片
</em><em></em> img && img.recycle()
<em>// 撤销监听
</em><em></em> window.canvas.removeAllListeners()
})
<em>/**
</em><em> * 绘制文本和方框
</em><em> *
</em><em> * @param {*} desc
</em><em> * @param {*} rect
</em><em> * @param {*} colorStr
</em><em> * @param {*} canvas
</em><em> * @param {*} paint
</em><em> */</em>
function drawRectAndText (desc, rect, colorStr, canvas, paint) {
let color = colors.parseColor(colorStr)
paint.setStrokeWidth(1)
paint.setStyle(Paint.Style.STROKE)
<em>// 反色
</em><em></em> 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)
}
<em>/**
</em><em> * 获取状态栏高度
</em><em> *
</em><em> * @returns
</em><em> */</em>
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");
<em>//或者 KeyCode(26);
</em>
TessractOCR
<em>//此例子仅作为演示,无法运行,因为tessdata目录下没有训练数据,
</em><em>//如需运行,可前往github下载完整例子:https://github.com/wilinz/autoxjs-tessocr
</em><em>//导包
</em><em></em>importClass(com.googlecode.tesseract.android.TessBaseAPI)
<em>//新建OCR实例
</em><em></em>var tessocr = new TessBaseAPI()
<em>//请求截图权限
</em><em></em>requestScreenCapture(false);
<em>//3秒后开始
</em><em></em>toastLog("3秒后截图")
sleep(3000)
toastLog("开始截图")
<em>//截图
</em><em></em>var img = captureScreen();
<em>//tessdata目录所在的文件夹,目录下放置训练数据
</em><em>//训练数据下载地址:https://github.com/tesseract-ocr/tessdata/tree/4.0.0
</em><em></em>var dataPath = files.path("./")
<em>//初始化tessocr
</em><em>//第二个参数是初始化的语言,是数据文件去掉扩展名后的文件名,多个语言用+连接
</em><em>//训练数据文件夹必须命名为tessdata
</em><em>//训练数据下载时是什么名字就是什么名字,不能改
</em><em></em>var ok = tessocr.init(dataPath, "eng+chi_sim")
if (ok) {
toastLog("初始化成功: " + tessocr.getInitLanguagesAsString())
} else {
toastLog("初始化失败")
}
<em>//设置图片
</em><em></em>tessocr.setImage(img.getBitmap())
<em>//打印文本结果
</em><em></em>toastLog(tessocr.getUTF8Text())
<em>//如需获取位置等其他结果请看文档
</em><em></em>
<em>// project.json
</em><em></em>{
"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>
);
<em>//蛇的颜色
</em><em></em>const SNAKE_COLOR = colors.parseColor("#4caf50");
<em>//背景色
</em><em></em>const BG_COLOR = colors.parseColor("#ffffff");
<em>//苹果颜色
</em><em></em>const APPLE_COLOR = colors.parseColor("#f44336");
<em>//墙的颜色
</em><em></em>const WALL_COLOR = colors.parseColor("#607d8b");
<em>//文本颜色
</em><em></em>const TEXT_COLOR = colors.parseColor("#03a9f4");
<em>//蛇自动移动的时间间隔,调小可以增加难度
</em><em></em>const MOVE_INTERVAL = 500;
<em>//方块宽度
</em><em></em>const BLOCK_WIDTH = 40;
<em>//游戏区域宽高
</em><em></em>const GAME_BOARD_HEIGHT = 20;
const GAME_BOARD_WIDTH = 15;
<em>//蛇的四个移动方向
</em><em></em>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};
<em>//蛇,是一个蛇身的坐标的数组
</em><em></em>var snake = [{x: 4, y: 2}, {x: 3, y: 2}, {x: 2, y: 2}];
<em>//苹果的坐标
</em><em></em>var apple = generateApple();
<em>//当前蛇的移动方向
</em><em></em>var direction = DIRECTION_RIGHT;
<em>//标记游戏是否结束
</em><em></em>var isGameOver = false;
<em>//分数
</em><em></em>var score = 0;
var paint = new Paint();
ui.board.on("draw", function(canvas){
<em>//绘制背景色
</em><em></em> canvas.drawColor(BG_COLOR);
<em>//绘制分数
</em><em></em> paint.setColor(TEXT_COLOR);
paint.setTextSize(50);
canvas.drawText("分数: " + score, 30, 70, paint);
<em>//如果游戏结束则绘制游戏结束字样
</em><em></em> if(isGameOver){
canvas.drawText("游戏结束!", canvas.getWidth() - 280, 70, paint);
}
<em>//计算坐标偏移,是的游戏区域绘制在画面的水平居中位置
</em><em></em> var offset = {
x: (canvas.getWidth() - (GAME_BOARD_WIDTH + 2) * BLOCK_WIDTH) / 2,
y: 100
};
<em>//偏移坐标
</em><em></em> canvas.translate(offset.x, offset.y);
<em>//绘制围墙
</em><em></em> paint.setColor(WALL_COLOR);
for(var i = 0; i <= GAME_BOARD_WIDTH + 1; i++){
<em>//上围墙
</em><em></em> drawBlock(canvas, paint, i, 0);
<em>//下围墙
</em><em></em> drawBlock(canvas, paint, i, GAME_BOARD_HEIGHT + 1);
}
for(var i = 0; i <= GAME_BOARD_HEIGHT + 1; i++){
<em>//左围墙
</em><em></em> drawBlock(canvas, paint, 0, i);
<em>//右围墙
</em><em></em> drawBlock(canvas, paint, GAME_BOARD_WIDTH + 1, i);
}
<em>//绘制蛇身
</em><em></em> paint.setColor(SNAKE_COLOR);
for(var i = 0; i < snake.length; i++){
drawBlock(canvas, paint, snake[i].x, snake[i].y);
}
<em>//绘制苹果
</em><em></em> paint.setColor(APPLE_COLOR);
drawBlock(canvas, paint, apple.x, apple.y);
});
<em>//启动游戏线程
</em><em></em>var gameThread = threads.start(game);
<em>//按键点击时改变蛇的移动方向
</em><em></em>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(){
<em>//每隔一段时间让蛇自动前进
</em><em></em> setInterval(()=>{
move(direction.x, direction.y);
}, MOVE_INTERVAL);
}
function move(dx, dy){
log("move: %d, %d", dx, dy);
direction.x = dx;
direction.y = dy;
<em>//蛇前进时把一个新的方块添加到蛇头前面
</em><em></em> var head = snake[0];
snake.splice(0, 0, {
x: head.x + dx,
y: head.y + dy
});
<em>//如果蛇头吃到了苹果
</em><em></em> if(snakeEatsApple()){
<em>//添加分数和重新生成苹果
</em><em></em> score += 5;
apple = generateApple();
}else{
<em>//没有吃到苹果的情况下把蛇尾去掉保持蛇身长度不变
</em><em></em> snake.pop();
}
<em>//碰撞检测
</em><em></em> collisionTest();
}
function snakeEatsApple(){
return snake[0].x == apple.x && snake[0].y == apple.y;
}
function generateApple(){
<em>//循环生成苹果直至苹果不会生成在蛇身上
</em><em></em> 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(){
<em>//检测蛇有没有撞到墙上
</em><em></em> var head = snake[0];
if(head.x < 1 || head.x > GAME_BOARD_WIDTH
|| head.y < 1 || head.y > GAME_BOARD_HEIGHT){
gameOver();
return;
}
<em>//检测蛇有没有撞到自己
</em><em></em> 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>
);
<em>//蛇的颜色
</em><em></em>const SNAKE_COLOR = colors.parseColor("#4caf50");
<em>//背景色
</em><em></em>const BG_COLOR = colors.parseColor("#ffffff");
<em>//苹果颜色
</em><em></em>const APPLE_COLOR = colors.parseColor("#f44336");
<em>//墙的颜色
</em><em></em>const WALL_COLOR = colors.parseColor("#607d8b");
<em>//文本颜色
</em><em></em>const TEXT_COLOR = colors.parseColor("#03a9f4");
<em>//蛇自动移动的时间间隔,调小可以增加难度
</em><em></em>const MOVE_INTERVAL = 500;
<em>//方块宽度
</em><em></em>const BLOCK_WIDTH = 40;
<em>//游戏区域宽高
</em><em></em>const GAME_BOARD_HEIGHT = 20;
const GAME_BOARD_WIDTH = 15;
<em>//蛇的四个移动方向
</em><em></em>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};
<em>//蛇,是一个蛇身的坐标的数组
</em><em></em>var snake = [{x: 4, y: 2}, {x: 3, y: 2}, {x: 2, y: 2}];
<em>//苹果的坐标
</em><em></em>var apple = generateApple();
<em>//当前蛇的移动方向
</em><em></em>var direction = DIRECTION_RIGHT;
<em>//标记游戏是否结束
</em><em></em>var isGameOver = false;
<em>//分数
</em><em></em>var score = 0;
var paint = new Paint();
ui.board.on("draw", function(canvas){
<em>//绘制背景色
</em><em></em> canvas.drawColor(BG_COLOR);
<em>//绘制分数
</em><em></em> paint.setColor(TEXT_COLOR);
paint.setTextSize(50);
canvas.drawText("分数: " + score, 30, 70, paint);
<em>//如果游戏结束则绘制游戏结束字样
</em><em></em> if(isGameOver){
canvas.drawText("游戏结束!", canvas.getWidth() - 280, 70, paint);
}
<em>//计算坐标偏移,是的游戏区域绘制在画面的水平居中位置
</em><em></em> var offset = {
x: (canvas.getWidth() - (GAME_BOARD_WIDTH + 2) * BLOCK_WIDTH) / 2,
y: 100
};
<em>//偏移坐标
</em><em></em> canvas.translate(offset.x, offset.y);
<em>//绘制围墙
</em><em></em> paint.setColor(WALL_COLOR);
for(var i = 0; i <= GAME_BOARD_WIDTH + 1; i++){
<em>//上围墙
</em><em></em> drawBlock(canvas, paint, i, 0);
<em>//下围墙
</em><em></em> drawBlock(canvas, paint, i, GAME_BOARD_HEIGHT + 1);
}
for(var i = 0; i <= GAME_BOARD_HEIGHT + 1; i++){
<em>//左围墙
</em><em></em> drawBlock(canvas, paint, 0, i);
<em>//右围墙
</em><em></em> drawBlock(canvas, paint, GAME_BOARD_WIDTH + 1, i);
}
<em>//绘制蛇身
</em><em></em> paint.setColor(SNAKE_COLOR);
for(var i = 0; i < snake.length; i++){
drawBlock(canvas, paint, snake[i].x, snake[i].y);
}
<em>//绘制苹果
</em><em></em> paint.setColor(APPLE_COLOR);
drawBlock(canvas, paint, apple.x, apple.y);
});
<em>//启动游戏线程
</em><em></em>var gameThread = threads.start(game);
<em>//按键点击时改变蛇的移动方向
</em><em></em>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(){
<em>//每隔一段时间让蛇自动前进
</em><em></em> setInterval(()=>{
move(direction.x, direction.y);
}, MOVE_INTERVAL);
}
function move(dx, dy){
log("move: %d, %d", dx, dy);
direction.x = dx;
direction.y = dy;
<em>//蛇前进时把一个新的方块添加到蛇头前面
</em><em></em> var head = snake[0];
snake.splice(0, 0, {
x: head.x + dx,
y: head.y + dy
});
<em>//如果蛇头吃到了苹果
</em><em></em> if(snakeEatsApple()){
<em>//添加分数和重新生成苹果
</em><em></em> score += 5;
apple = generateApple();
}else{
<em>//没有吃到苹果的情况下把蛇尾去掉保持蛇身长度不变
</em><em></em> snake.pop();
}
<em>//碰撞检测
</em><em></em> collisionTest();
}
function snakeEatsApple(){
return snake[0].x == apple.x && snake[0].y == apple.y;
}
function generateApple(){
<em>//循环生成苹果直至苹果不会生成在蛇身上
</em><em></em> 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(){
<em>//检测蛇有没有撞到墙上
</em><em></em> var head = snake[0];
if(head.x < 1 || head.x > GAME_BOARD_WIDTH
|| head.y < 1 || head.y > GAME_BOARD_HEIGHT){
gameOver();
return;
}
<em>//检测蛇有没有撞到自己
</em><em></em> 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控件为空");
<em>//console.log(script.toString())
</em><em></em> 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')<em>//'toast提示成功';
</em><em></em>}
function webViewExpand_init(webViewWidget) {
webViewWidget.webViewClient = new JavaAdapter(android.webkit.WebViewClient, {
onPageFinished: (webView, curUrl) => {
try {
<em>// 注入 AutoX
</em><em></em> 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();<em>//清理一次
</em><em></em>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示例
<em>// 新建一个WebSocket
</em><em>// 指定web socket的事件回调在当前线程(好处是没有多线程问题要处理,坏处是不能阻塞当前线程,包括死循环)
</em><em>// 不加后面的参数则回调在IO线程
</em><em></em>let ws = web.newWebSocket("wss://demo.piesocket.com/v3/channel_1?notify_self", {
eventThread: 'this'
});
console.show();
<em>// 监听他的各种事件
</em><em></em>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);
});
<em>// 发送文本消息
</em><em></em>log("发送消息: Hello, WebSocket!");
ws.send("h");
setTimeout(() => {
<em>// 两秒后发送二进制消息
</em><em></em> log("发送二进制消息: 5piO5aSp5L2g6IO96ICDMTAw5YiG44CC");
ws.send(web.ByteString.decodeBase64("5piO5aSp5L2g6IO96ICDMTAw5YiG44CC"));
}, 2000);
setTimeout(() => {
<em>// 8秒后断开WebSocket
</em><em></em> log("断开WebSocket");
<em>// 1000表示正常关闭
</em><em></em> ws.close(1000, null);
}, 8000);
setTimeout(() => {
log("退出程序");
}, 12000)