跳到主要内容

作为 Auto.js 插件使用

如果你想脱离不使用auto.js的无障碍部分功能又需要实现自动化操作, 或者说想要在无root环境下使用, 你可以使用Yyds.Auto作为插件去使用
所有的插件操作都不会创建新的进程, 因此速度更快
当然, 同样需要激活引擎~

// ======================== yyds 相关 start ================================
function err_handle(desc, error) {
let IS_DEBUG = false;
let err_msg = desc;
if (error) {
err_msg = desc + "\t" + "错误信息:" + error.message + "\r\n错误堆栈:" + error.stack
}
if (IS_DEBUG) {
toast(err_msg);
}
};


// 以下是插件函数的封装, 可自行根据需要改造
function YydsExtension() {
let yy = null
let yyds = this
// ============ fixed on 2024-01-10 ===================
let lowPower = true
// 标记手机是否Root
var hasRoot = shell('su ifconfig').code == 127 ? false : true
// ====================================================
// ====================================================

// 如果应用已经有root权限, 插件会自动激活
try {
yy = $plugins.load("com.yyds.auto")
} catch (error) {
err_handle("加载插件错误", error)
}
function _api(uri, data) {
for (var i = 0; i < 3; i++) {
try {
let ret = yy.call("/" + uri, data)
return ret;
} catch (error) {
err_handle("_api", uri + "|" + data, error);
sleep(5000);
}
}
}

// yolo 定位
this.key_locate = function () {
if (lowPower) {
return _api("key-locate", { use_gpu: false });
} else {
return _api("key-locate");
}
};
// yolo 定位
this.key_locate_result = function () {
let array = this.key_locate();
if (!array) return null;
array = array.split("\n");
let result = [];
array.forEach(line => {
if (line) {
line = line.replace(/[\'\,\{\}]/g, '=');
let res = line.split('=');
let obj = {};
res = res.filter(i => i);
for (let i = 0; i < res.length; i += 2) {
if (i + 1 < res.length) {
let n = res[i + 1];
if (parseFloat(n)) { obj[res[i].trim()] = parseFloat(n); } else { obj[res[i].trim()] = n; }
}
}
result.push(obj);
}
});
return result;
};
//找图
this.screen_find_images = (ps) => _api("screen-find-images", { "templates": ps.join(";") });

function FindImages(line) {
let line_split = line.split("\t");
this.path = line_split[0].split("/")[line_split[0].split("/").length - 1];
let pos_split = line_split[1].split(" ");
//置信度
this.prob = parseFloat(pos_split[0]);
//图片宽高
this.width = parseInt(pos_split[1].split(",")[0]);
this.height = parseInt(pos_split[1].split(",")[1]);
//坐标
this.x = parseInt(pos_split[2].split(",")[0]);
this.y = parseInt(pos_split[2].split(",")[1]);
}
this.find_images = function (array) {
let res = this.screen_find_images(array);
if (res == null) return null;
let find_line_str = res.split("\n");
let find_result = new Array();
for (let i = 0; i < find_line_str.length; i++) {
if (find_line_str[i].length < 1) continue;
find_result.push(new FindImages(find_line_str[i]));
}
return find_result;
};
/**
*
* @param {Array} array 图片路径数组
* @param {Object} options 筛选设置
* @returns 筛选结果
*/
this.select_images = function (array, options) {
if (array == null) return null;
let res = array;
options = options || {};
let prob = options.prob || 0.9;
let region = options.region || [];
region[0] = region[0] || -1;
region[1] = region[1] || -1;
region[2] = region[2] ? region[0] + region[2] : 10000;
region[3] = region[3] ? region[1] + region[3] : 10000;
let result = new Array();
for (let i = 0; i < res.length; i++) {
if (res[i] === undefined || (res[i].prob < prob) || (region[0] > res[i].x) || (res[i].x > region[2]) || (region[1] > res[i].y) || (res[i].y > region[3])) continue;
result.push(res[i]);
};
if (result[0] == undefined) return null;
return result;
};

this.select_name = function (array, name) {
if (array == null) return null;
for (let i = 0; i < array.length; i++) {
if (array[i].path == name) return { width: array[i].width, height: array[i].height, x: array[i].x, y: array[i].y };
}
return null;
};
// 判断在线
this.is_net_online = () => {
return _api("is-net-online") == "true";
};
// 插件版本
this._version = () => _api("version");
/**
* 点击
* @param {Number} x x坐标
* @param {Number} y y坐标
* @returns Boolean
*/
this.click = function (x, y) {
// ============ fixed on 2024-01-10 判断是否有root权限切换点击方案 ================
if (hasRoot) {
let res = shell(`getprop | grep -i "Note 9 4G" && echo "yes"`, true).result;
if (res.indexOf("yes") != -1) return _api("touch", { x: parseInt(x), y: parseInt(y) }) == "true";
rootA.press(parseInt(x), parseInt(y), random(60, 100));
} else {
return _api("touch", { "x": parseInt(x), "y": parseInt(y) }) == "true";
}
// ===================================================================
// let res = shell(`getprop | grep -i "Note 9 4G" && echo "yes"`, true).result;
// if (res.indexOf("yes") != -1) return _api("touch", { x: parseInt(x), y: parseInt(y) }) == "true";
// rootA.press(parseInt(x), parseInt(y), random(60, 100));
// // return _api("touch", { "x": parseInt(x), "y": parseInt(y) }) == "true";
// ===================================================================

};
/**
* 在指定区域内 随机点击 做到模拟人工操作
* @param {*} x X坐标
* @param {*} y Y坐标
* @param {*} w 在x坐标上, 可以横向偏移多少
* @param {*} h 在y坐标上, 可以纵向偏移多少
* @returns
*/
this.random_click = function (x, y, w, h) {
return yyds.click(random(x - w, x + w), random(y - h, y + h))
}
/**
* 设置引擎 使用内核点击 后面都生效 (需要 root 权限)
* @returns 当前是否使用内核点击
*/
this.ensure_kernel_click = function () {
return _api("/set-no-ra", { "enable": "false" })
}
/**
* 设置引擎 取消内核点击 (需要 root 权限)
* @returns 当前是否使用内核点击
*/
this.cancel_kernel_click = function () {
return _api("/set-no-ra", { "enable": "true" })
}
/**
* 滑动
* @param {Number} x1
* @param {Number} y1
* @param {Number} x2
* @param {Number} y2
* @param {Number} duration
* @returns Boolean
*/
this.swipe = function (x1, y1, x2, y2, duration) {
return _api("swipe", { "x1": parseInt(x1), "y1": parseInt(y1), "x2": parseInt(x2), "y2": parseInt(y2), "duration": parseInt(duration) }) == "true";
};
function Ocr(line) {
let line_split = line.split("\t");
this.prob = line_split[0];
this.text = line_split[1];
let pos_split = line_split[2].split(" ");
// 左上坐标
this.x1 = pos_split[0].split(",")[0];
this.y1 = pos_split[0].split(",")[1];
// 右上坐标
this.x2 = pos_split[1].split(",")[0];
this.y2 = pos_split[1].split(",")[1];
// 左下坐标
this.x3 = pos_split[2].split(",")[0];
this.y3 = pos_split[2].split(",")[1];
// 右下坐标
this.x4 = pos_split[3].split(",")[0];
this.y4 = pos_split[3].split(",")[1];
}
/**
* ocr 文字识别
* @returns 结果集
*/
this.ocr = function () {
let ret;
if (lowPower) {
ret = _api("screen-ocr", { use_gpu: false });
} else {
ret = _api("screen-ocr");
}
if (ret == null) return null;
let ocr_list_str = ret.split("\n");
let ocr_result = new Array();
for (let i = 0; i < ocr_list_str.length; i++) {
if (ocr_list_str[i].length < 1) continue;
ocr_result.push(new Ocr(ocr_list_str[i]));
}
for (let i = 0; i < ocr_result.length; i++) {
if (ocr_result[i].prob == NaN) ocr_result.splice(i, 1);
}
return ocr_result;
};
/**
* 截图并保存到 sdcard
* @returns 存储路径
* adb shell screencap -p /sdcard/screenshot.png
*/
this.screenshot = () => {
return _api("screenshot");
};
/**
* 获取前台运行包名, activity, pid
* @returns [当前包名, 当前activity, pid]
*/
this.foreground = () => {
let ret = _api("foreground");
if (ret == null) return null;
let ret_split = ret.split(" ");
return ret_split;
};

// 执行busybox下的shell命令
this.shell = function (cmd) {
return _api("shell", { cmd: cmd });
};
// 注入 KEY_CODE
this.key_code = function (code) {
return _api("/key-code", { code: code })
}
this.home = function () {
return this.key_code(3);
}
this.menu = function () {
return this.key_code(82);
}
this.back = function () {
return this.key_code(4);
}
this.key_confirm = function () {
return _api('/key-confirm')
}

// 往编辑框输入文字
this.parse = function (text) {
return _api({ paste: text });
}
// ==================== fixed on 2024-01-11 ======================
/**
* 返回节点的中间坐标点, 方便用于点击
* 例: string: '[0,0][1080,2160]' => Array: [540, 1080]
* @param {string} boundsString
* @returns Array
*/
function center_point(boundsString) {
let s = boundsString.split(/\[|\]|,/).filter(i => i !== "");
let x1 = parseInt(s[0]);
let y1 = parseInt(s[1]);
let x2 = parseInt(s[2]);
let y2 = parseInt(s[3]);
return [Math.floor((x1 + x2) / 2), Math.floor((y1 + y2) / 2)];
}
function serializer_ui_control_list(ui_control_list) {
return ui_control_list.map((item) => {
let centerX = center_point(item["boundsString"])[0]
let centerY = center_point(item["boundsString"])[1]
return {
'boundsString': item["boundsString"],
'childCount': item["childCount"],
'parentCount': item["parentCount"],
'cls': item["cls"],
'pkg': item["pkg"],
'text': item["text"],
'desc': item["desc"],
'id': item["id"],
'index': item["index"],
'isCheckable': item["isCheckable"],
'isChecked': item["isChecked"],
'isEnable': item["isEnable"],
'isFocusable': item["isFocusable"],
'isFocused': item["isFocused"],
'isLongClickable': item["isLongClickable"],
'isPassword': item["isPassword"],
'isScrollable': item["isScrollable"],
'isSelected': item["isSelected"],
'isVisible': item["isVisible"],
'centerX': centerX,
'centerY': centerY,
'clickCenter': function (x, y) {
let clickX = x
let clickY = y
return () => {
yyds.click(random(clickX - 5, clickX + 5), random(clickY - 5, clickY + 5))
}
}(centerX, centerY)
}
})


}
/**
* @param {Boolean} match_from_cache 是否从最后一次dump的缓存中匹配 可以加快搜索速度
* @param {object} match_params
* 看文档:https://yydsxx.com/docs/yyds-auto/py-inner/ Python引擎函数
* @returns 识别结果
*/
this.ui_match = function (match_params) {
// 默认不使用缓存
let match_from_cache = false
if (Object.keys(match_params).includes('match_from_cache')) {
match_from_cache = match_params['match_from_cache']
}
let params_ = { "match_from_cache": match_from_cache == true ? "true" : "false" }
for (let k in match_params) {
if (k == "class_") {
params_["class"] = match_params[k]
} else {
params_[k.toString().replace("_", "-")] = match_params[k]
}
}
let ret_str = _api("/uia-match", params_)
let ret_list = JSON.parse(ret_str)
return serializer_ui_control_list(ret_list)
}
/**
* ui是否存在
* @returns boolean ui是否存在ss
*/
this.ui_exist = function (match_params) {
// 默认不使用缓存
let match_from_cache = false
if (Object.keys(match_params).includes('match_from_cache')) {
match_from_cache = match_params['match_from_cache']
}
let params_ = { "match_from_cache": match_from_cache == true ? "true" : "false" }
for (let k in match_params) {
if (k == "class_") {
params_["class"] = match_params[k]
} else {
params_[k.toString().replace("_", "-")] = match_params[k]
}
}
let ret_str = _api("/uia-match", params_)
let ret_list = JSON.parse(ret_str)
return ret_list.length > 0
}
/**
* 还原find
* @param {number} duration 搜索的超时时间,单位毫秒
* @param {object} match_params
* @returns Array
*/
this.ui_match_find = function (duration, match_params) {
// 循环间隔时间
let interval_time = 50
let start_time = new Date().getTime()
let end_time = start_time + duration
let res = []

while (new Date().getTime() <= end_time) {
res = this.ui_match(match_params)
if (res.length > 0) {
return res
}
sleep(interval_time)
}
return res
}
/**
* 还原findOne
* @param {number} duration 搜索的超时时间,单位毫秒
* @param {object} match_params
* @returns
*/
this.ui_match_findOne = function (duration, match_params) {
let res = this.ui_match_find(duration, match_params)
if (res.length > 0) {
return res[0]
}
return null
}
this.CloseAPP = function (PKG_NAME) {
this.shell('am force-stop ' + PKG_NAME)
}
this.RunAPP = function (PKG_NAME) {
return _api("/open-app", { 'pkg': PKG_NAME })
}
/**
* 打开系统url
* 如果 url为 http 链接, 即使用系统默认的浏览器应用
也能打开其它系统链接 比如电话tel:21113336
成功返回:Starting: Intent { act=android.intent.action.VIEW dat=tel:xxxxxxxxxx }
错误返回:Starting: Intent { act=android.intent.action.VIEW dat=asdfs } Error: Activity not started, unable to resolve Intent { act=android.intent.action.VIEW dat=asdfs flg=0x10000000 }
* @param {string} url
* @returns
*/
this.open_url = function (url) {
return _api("/open-url", { "url": url })
}
/**
* 当前活动界面名 比device_foreground 更快
* @returns 当前活动界面名
*/
this.device_foreground_activity = function () {
return _api("/foreground-fast")
}
/**
* 当前前台包名 比device_foreground 更快
* @returns 当前前台包名
*/
this.device_foreground_package = function () {
return _api("/foreground-pkg")
}
/**
* app是否在后台运行
* @returns app是否在后台运行
*/
this.is_app_running = function () {
return _api("/background-is-running", { "pkg": pkg }) == "true"
}
/**
* 将后台运行的应用带回前台
* @returns 是否操作成功
*/
this.bring_app_to_top = function () {
return _api("/background-to-top", { "pkg": pkg }) == "true"
}
/**
* 当前是否在某应用界面内
* @param {string} pkg 应用包名
* @returns
*/
this.is_in_app = function (pkg) {
return pkg == yyds.device_foreground_package()
}
/**
* 注入文本, 受安卓系统限制, *不支持中文
* @param {string} text
* @returns 输入的字符长度
*/
this.input_text = function (text) {
return parseInt(_api("/inject-text", { "text": text.toString() }))
}
/**
* 通过内置 YY 自动输入法输入文本, 需要手动到系统设置启动输入法并切换输入法(或在root下从Yyds.Auto菜单中一键启动)
* @param {string} text
* @returns boolean 仅代表是否发送成功到, 不代表是否执行成功
*/
this.x_input_text = function (text) {
return _api("/xinput-text", { "text": text.toString() }) == "true"
}
/**
* 通过内置 YY 自动输入法清空编辑框文本, 需要手动到系统设置启动输入法并切换输入法(或在root下从Yyds.Auto菜单中一键启动)
* @returns boolean 仅代表是否发送成功到, 不代表是否执行成功
*/
this.x_input_clear = function () {
return _api("/xinput-clear") == "true"
}
/**
* 启用或禁用YY输入法
* 需要ROOT权限, 如果没有ROOT权限请手动到设置->语言与输入法 启用YY输入法
* @param {boolean} enable
*/
this.set_yy_input_enable = function (enable) {
return _api("/enable-yy-input", { "enable": enable ? "true" : "false" }) == "true"
}
this.setText = function(text){
yyds.x_input_clear()
sleep(500)
yyds.x_input_clear()
sleep(500)
yyds.x_input_text(text)
}
/**
* 复制文本到粘贴板, 在高级的安卓版本可能被受到限制, 注意自行测试
* @param {string} text
* @returns
*/
this.paste = function (text) {
return _api("/paste", { text: text })
}

/**
* 获取粘贴板文本, 在安卓9以上被限制, 需要启用YY输入法进行获取
* @returns
*/
this.get_clipboard = function () {
return _api("/clipboard-text", {})
}
// =============================================================
// =============================================================
};


// console.show()
let yyds = new YydsExtension();
// =========================== yyds相关 end =================================

SP_PKG_NAME = 'com.shopee.vn'

let linshi = null

// yyds.x_input_text('fsdf')

// yyds.x_input_clear()
// yyds.setText('fff')

// // id搜索
// linshi = yyds.ui_match({'resource_id':"com.shopee.vn:id/image"})

linshi = yyds.ui_match_findOne(1000,{"class_": "android.view.ViewGroup", "content_desc":".*_title", "top":'0.3'})
log(linshi)

// matchUI = yyds.ui_match_findOne(1000, { "class_": "android.widget.EditText", 'clickable': 'true'})
// log(matchUI)
// if(matchUI){
// matchUI['clickCenter']()
// sleep(2000)
// }
// yyds.key_confirm()
// yyds.home()

// 打开app
// yyds.RunAPP(SP_PKG_NAME)

// yyds.CloseAPP(SP_PKG_NAME)


// res = yyds.ui_match(false, {'text':'.*环球.*'})
// log(res[0]['text'])

// // 坐标点击
// yyds.click(548, 764);

// // 按下返回主页键
// yyds.home();

// // 按下返回键
// yyds.back();

// // 打开网页
// yyds.open_url("https://yydsxx.com");

// // 打开app
// yyds.open_app("com.yyds.auto");

// 坐标滑动
// yyds.swipe(542, 1826, 600,625, 1200);
// yyds.swipe(600,625,300, 300, 1200);

// // 截图, 在auto.js上不需要动态申请任何权限
// yyds.screenshot();

// // 当前屏幕界面文字
// log(yyds.ocr())

// // 前台应用
// yyds.foreground()[0]

// // 前台应用Activity名
// yyds.foreground()[1]

// // 找图, 可以在指定范围内同时识别多张图片 具体用法参数Python语言的文档
// yyds.find_images("/sdcard/1.png", "/sdcard/2.png");

// // 网络是否在线
// log(yyds.is_net_online())

// ui_match(text="我的")