From f8bf14d3e401f2787891ed260bcb92a4b66333ab Mon Sep 17 00:00:00 2001 From: SuperMonster003 Date: Thu, 1 Dec 2022 16:21:28 +0800 Subject: [PATCH] =?UTF-8?q?1.0.0=20-=20=E9=87=8D=E6=96=B0=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E6=96=87=E6=A1=A3=E7=BB=93=E6=9E=84/=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E9=83=A8=E5=88=86=E7=AB=A0=E8=8A=82/=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/.gitignore | 3 + api/{_404.md => 404.md} | 0 api/_sidebar.md | 30 - api/_toc.md | 40 - api/activity.md | 10 + api/all.md | 45 +- api/apiLevel.md | 72 + api/app.md | 176 +- api/appType.md | 259 + api/arrayx.md | 407 ++ api/autojs.md | 8 + api/automator.md | 534 ++ api/base64.md | 10 + api/canvas.md | 77 +- api/changelog.md | 13 + api/color.md | 152 + api/console.md | 81 +- api/context.md | 14 + api/continuation.md | 10 + api/coordinatesBasedAutomation.md | 260 - api/{_coverpage.md => coverpage.md} | 6 +- api/crypto.md | 21 +- api/dataTypes.md | 1472 ++++ api/device.md | 135 +- api/dialogs.md | 157 +- api/documentation.md | 443 +- api/e4x.md | 31 + api/engines.md | 93 +- api/events.md | 209 +- api/exceptions.md | 1071 +++ api/files.md | 131 +- api/floaty.md | 87 +- api/global.md | 1023 +++ api/globals.md | 223 - api/glossaries.md | 566 ++ api/http.md | 105 +- api/i18n.md | 10 + api/image.md | 798 +++ api/images.md | 941 --- api/index.html | 21 +- api/intentType.md | 52 + api/keys.md | 95 +- api/mathx.md | 673 ++ api/media.md | 45 +- api/modules.md | 25 +- api/numberx.md | 301 + api/overview.md | 73 +- api/plugins.md | 10 + api/polyfill.md | 30 + api/progress.md | 69 + api/qa.md | 89 +- api/recorder.md | 66 + api/runtime.md | 52 + api/scriptingJava.md | 262 + api/sensors.md | 159 +- api/shell.md | 347 +- api/sidebar.md | 76 + api/static/docsify-config.js | 17 + api/static/docsify.js | 9719 +++++++++++++++++++++++++++ api/static/docsify.min.js | 1 - api/static/search.js | 555 ++ api/static/search.min.js | 1 - api/static/vue.css | 1251 +++- api/storages.md | 43 +- api/tasks.md | 10 + api/threads.md | 101 +- api/timers.md | 61 +- api/toc.md | 84 + api/ui.md | 439 +- api/uiObjectActionsType.md | 1246 ++++ api/uiObjectCollectionType.md | 791 +++ api/uiObjectType.md | 2189 ++++++ api/uiSelectorType.md | 6106 +++++++++++++++++ api/util.md | 48 - api/web.md | 10 + api/widgetsBasedAutomation.md | 1226 ---- 76 files changed, 31901 insertions(+), 4165 deletions(-) create mode 100644 api/.gitignore rename api/{_404.md => 404.md} (100%) delete mode 100644 api/_sidebar.md delete mode 100644 api/_toc.md create mode 100644 api/activity.md create mode 100644 api/apiLevel.md create mode 100644 api/appType.md create mode 100644 api/arrayx.md create mode 100644 api/autojs.md create mode 100644 api/automator.md create mode 100644 api/base64.md create mode 100644 api/changelog.md create mode 100644 api/color.md create mode 100644 api/context.md create mode 100644 api/continuation.md delete mode 100644 api/coordinatesBasedAutomation.md rename api/{_coverpage.md => coverpage.md} (60%) create mode 100644 api/dataTypes.md create mode 100644 api/e4x.md create mode 100644 api/exceptions.md create mode 100644 api/global.md delete mode 100644 api/globals.md create mode 100644 api/glossaries.md create mode 100644 api/i18n.md create mode 100644 api/image.md delete mode 100644 api/images.md create mode 100644 api/intentType.md create mode 100644 api/mathx.md create mode 100644 api/numberx.md create mode 100644 api/plugins.md create mode 100644 api/polyfill.md create mode 100644 api/progress.md create mode 100644 api/recorder.md create mode 100644 api/runtime.md create mode 100644 api/scriptingJava.md create mode 100644 api/sidebar.md create mode 100644 api/static/docsify-config.js create mode 100644 api/static/docsify.js delete mode 100644 api/static/docsify.min.js create mode 100644 api/static/search.js delete mode 100644 api/static/search.min.js create mode 100644 api/tasks.md create mode 100644 api/toc.md create mode 100644 api/uiObjectActionsType.md create mode 100644 api/uiObjectCollectionType.md create mode 100644 api/uiObjectType.md create mode 100644 api/uiSelectorType.md create mode 100644 api/web.md delete mode 100644 api/widgetsBasedAutomation.md diff --git a/api/.gitignore b/api/.gitignore new file mode 100644 index 0000000..de1a26b --- /dev/null +++ b/api/.gitignore @@ -0,0 +1,3 @@ +.idea +*.bak +declarations \ No newline at end of file diff --git a/api/_404.md b/api/404.md similarity index 100% rename from api/_404.md rename to api/404.md diff --git a/api/_sidebar.md b/api/_sidebar.md deleted file mode 100644 index 8ba6ba6..0000000 --- a/api/_sidebar.md +++ /dev/null @@ -1,30 +0,0 @@ -* [About - 关于文档](/documentation) -* ------------- -* %MARKER% -* ------------- -* [Q&A](/qa) -* [Global - 全局对象](/globals) -* [应用 - App](/app) -* [控制台 - Console](/console) -* [基于坐标的操作 - CoordinatesBasedAutomation](/coordinatesBasedAutomation) -* [加解密与消息摘要 - Crypto](/crypto) -* [设备 - Device](/device) -* [对话框 - Dialogs](/dialogs) -* [脚本引擎 - Engines](/engines) -* [事件与监听 - Events](/events) -* [悬浮窗 - Floaty](/floaty) -* [文件系统 - Files](/files) -* [HTTP - Http](/http) -* [图片与颜色 - Images](/images) -* [画布 - Canvas](/canvas) -* [按键模拟 - Keys](/keys) -* [多媒体 - Media](/media) -* [模块 - Modules](/modules) -* [基于控件的操作 - WidgetsBaseAutomation](/widgetsBasedAutomation) -* [传感器 - Sensor](/sensors) -* [Shell - Shell](/shell) -* [本地储存 - Storages](/storages) -* [多线程 - Threads](/threads) -* [定时器 - Timers](/timers) -* [用户界面 - UI](/ui) -* [调用Java - Work with Java](https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/Rhino/Scripting_Java) diff --git a/api/_toc.md b/api/_toc.md deleted file mode 100644 index 273e093..0000000 --- a/api/_toc.md +++ /dev/null @@ -1,40 +0,0 @@ -@// NB(chrisdickinson): if you move this file, be sure to update tools/doc/html.js to -@// point at the new location. - -* [关于本文档](documentation.html) -* [W3CSchool - ECMAScript教程](http://www.w3school.com.cn/js/pro_js_syntax.asp) -* [阮一峰 - ECMAScript 6教程](http://es6.ruanyifeng.com/#README) - -
- -* [Overview - 综述](overview.html) -* [Q&A - 常见问题](qa.html) -* [App - 应用](app.html) -* [Canvas - 画布](canvas.html) -* [Console - 控制台](console.html) -* [CoordinatesBasedAutomation - 基于坐标的操作](coordinatesBasedAutomation.html) -* [Crypto - 加解密与消息摘要](crypto.html) -* [Device - 设备](device.html) -* [Dialogs - 对话框](dialogs.html) -* [Engines - 脚本引擎](engines.html) -* [Events - 事件与监听](events.html) -* [Floaty - 悬浮窗](floaty.html) -* [Files - 文件系统](files.html) -* [Globals - 一般全局函数](globals.html) -* [Http - HTTP](http.html) -* [Images - 图片与图色处理](images.html) -* [Keys - 按键模拟](keys.html) -* [Media - 多媒体](media.html) -* [Modules - 模块](modules.html) -* [Sensors - 传感器](sensors.html) -* [Shell - Shell命令](shell.html) -* [Storages - 本地存储](storages.html) -* [Threads - 多线程](threads.html) -* [Timers - 定时器](timers.html) -* [UI - 用户界面](ui.html) -* [WidgetsBasedAutomation - 基于控件的操作](widgetsBasedAutomation.html) -* [Work with Java - 调用Java API](https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/Rhino/Scripting_Java) - -
- -* [GitHub项目 & Issue提交](https://github.com/SuperMonster003/AutoJs6) diff --git a/api/activity.md b/api/activity.md new file mode 100644 index 0000000..082c663 --- /dev/null +++ b/api/activity.md @@ -0,0 +1,10 @@ +# 活动 (Activity) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + + diff --git a/api/all.md b/api/all.md index 7d25d7a..f1d7795 100644 --- a/api/all.md +++ b/api/all.md @@ -1,26 +1,37 @@ @include overview +@include documentation @include qa + +@include global +@include automator +@include autojs @include app -@include canvas -@include console -@include coordinatesBasedAutomation -@include crypto +@include color +@include image +@include keys @include device -@include dialogs -@include engines -@include events -@include floaty +@include storages @include files -@include globals -@include http -@include images -@include keys -@include media +@include engines +@include tasks @include modules -@include sensors +@include plugins +@include continuation +@include console @include shell -@include storages -@include threads +@include media +@include sensors +@include recorder @include timers +@include threads +@include events +@include dialogs +@include floaty +@include canvas @include ui -@include widgetsBasedAutomation \ No newline at end of file +@include web +@include http +@include base64 +@include crypto +@include i18n +@include e4x \ No newline at end of file diff --git a/api/apiLevel.md b/api/apiLevel.md new file mode 100644 index 0000000..b0ae9d1 --- /dev/null +++ b/api/apiLevel.md @@ -0,0 +1,72 @@ +# 安卓 API 级别 (Android API Level) + +API 级别 (API Level) 是对 Android 平台版本 (SDK Platforms) 提供的框架 API 修订版进行唯一标识的整数值 (SDK INT). + +Android 平台提供一种框架 API, 应用可利用它与底层 Android 系统进行交互. +每个 Android 平台版本恰好支持一个 API 级别, 但隐含对所有早期 API 级别的支持. +Android 平台初始版本提供的是 API 级别 1, 后续版本的 API 级别则依次增加. + +下表列出了各 Android 平台版本所支持的 API 级别: + +| API 级别 | 版本名称 (Version Name) | 版本代号 (Version Code) | 版本号 (Version Number) | 内部代号 (Internal Codename) | 发行日期 | +|:-------|:---------------------------|:-----------------------|:---------------------|:-------------------------|:-------------------| +| 34 | Android 14 | ? | 14 | Upside Down Cake | Q3 2023 | +| 33 | Android 13 | TIRAMISU | 13 | Tiramisu | August 15, 2022 | +| 32 | Android 12L | S_V2 | 12.1 | Snow Cone v2 | March 7, 2022 | +| 31 | Android 12 | S | 12 | Snow Cone | October 4, 2021 | +| 30 | Android 11 | R | 11 | Red Velvet Cake | September 8, 2020 | +| 29 | Android 10 | Q | 10 | Quince Tart | September 3, 2019 | +| 28 | Android Pie | P | 9 | Pistachio Ice Cream | August 6, 2018 | +| 27 | Android Oreo | O_MR1 | 8.1 | Oatmeal Cookie | December 5, 2017 | +| 26 | Android Oreo | O | 8.0 | Oatmeal Cookie | August 21, 2017 | +| 25 | Android Nougat | N_MR1 | 7.1-7.1.2 | New York Cheesecake | October 4, 2016 | +| 24 | Android Nougat | N | 7.0 | New York Cheesecake | August 22, 2016 | +| 23 | Android Marshmallow | M | 6.0-6.0.1 | Macadamia Nut Cookie | October 2, 2015 | +| 22 | Android Lollipop | LOLLIPOP_MR1 | 5.1-5.1.1 | Lemon Meringue Pie | March 2, 2015 | +| 21 | Android Lollipop | LOLLIPOP | 5.0-5.0.2 | Lemon Meringue Pie | November 4, 2014 | +| 20 | Android KitKat | KITKAT_WATCH | 4.4W-4.4W.2 | Key Lime Pie | June 25, 2014 | +| 19 | Android KitKat | KITKAT | 4.4-4.4.4 | Key Lime Pie | October 31, 2013 | +| 18 | Android Jelly Bean | JELLY_BEAN_MR2 | 4.3-4.3.1 | Jelly Bean | July 24, 2013 | +| 17 | Android Jelly Bean | JELLY_BEAN_MR1 | 4.2-4.2.2 | Jelly Bean | November 13, 2012 | +| 16 | Android Jelly Bean | JELLY_BEAN | 4.1-4.1.2 | Jelly Bean | July 9, 2012 | +| 15 | Android Ice Cream Sandwich | ICE_CREAM_SANDWICH_MR1 | 4.0.3-4.0.4 | Ice Cream Sandwich | December 16, 2011 | +| 14 | Android Ice Cream Sandwich | ICE_CREAM_SANDWICH | 4.0-4.0.2 | Ice Cream Sandwich | October 18, 2011 | +| 13 | Android Honeycomb | HONEYCOMB_MR2 | 3.2-3.2.6 | Honeycomb | July 15, 2011 | +| 12 | Android Honeycomb | HONEYCOMB_MR1 | 3.1 | Honeycomb | May 10, 2011 | +| 11 | Android Honeycomb | HONEYCOMB | 3.0 | Honeycomb | February 22, 2011 | +| 10 | Android Gingerbread | GINGERBREAD_MR1 | 2.3.3-2.3.7 | Gingerbread | February 9, 2011 | +| 9 | Android Gingerbread | GINGERBREAD | 2.3-2.3.2 | Gingerbread | December 6, 2010 | +| 8 | Android Froyo | FROYO | 2.2-2.2.3 | Froyo | May 20, 2010 | +| 7 | Android Eclair | ECLAIR_MR1 | 2.1 | Eclair | January 11, 2010 | +| 6 | Android Eclair | ECLAIR_0_1 | 2.0.1 | Eclair | December 3, 2009 | +| 5 | Android Eclair | ECLAIR | 2.0 | Eclair | October 27, 2009 | +| 4 | Android Donut | DONUT | 1.6 | Donut | September 15, 2009 | +| 3 | Android Cupcake | CUPCAKE | 1.5 | Cupcake | April 27, 2009 | +| 2 | Android 1.1 | BASE_1_1 | 1.1 | Petit Four | February 9, 2009 | +| 1 | Android 1.0 | BASE | 1.0 | - | September 23, 2008 | + +文档通常使用以下格式之一表示 API 级别的信息: + +- 30 (11) [R] +- API 30 (11) [R] +- Android API 30 (11) [R] + +上述示例中, +`30` 表示 `API 级别`, +`11` 表示 `版本号 (Version Number)`, +`R` 表示 `版本代号 (Version Code)`. + +查询当前设备的 API 级别: + +```js +console.log(device.sdkInt); /* e.g. 30 */ +``` + +要求设备 API 级别不低于指定值: + +```js +/* 在 API 级别低于 30 的设备上将抛出异常. */ +runtime.requiresApi(30); +``` + +> 注: AutoJs6 安装及使用需满足的最低 API 级别为 24. \ No newline at end of file diff --git a/api/app.md b/api/app.md index 749cc1a..7e8e79a 100644 --- a/api/app.md +++ b/api/app.md @@ -1,16 +1,23 @@ -# App +# 通用应用 (App) -app模块提供一系列函数,用于使用其他应用、与其他应用交互。例如发送意图、打开文件、发送邮件等。 +--- -同时提供了方便的进阶函数startActivity和sendBroadcast,用他们可完成app模块没有内置的和其他应用的交互。 +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +app模块提供一系列函数, 用于使用其他应用、与其他应用交互. 例如发送意图、打开文件、发送邮件等. + +同时提供了方便的进阶函数startActivity和sendBroadcast, 用他们可完成app模块没有内置的和其他应用的交互. ## app.versionCode * {number} -当前软件版本号,整数值。例如160, 256等。 +当前软件版本号, 整数值. 例如160, 256等. -如果在Auto.js中运行则为Auto.js的版本号;在打包的软件中则为打包软件的版本号。 +如果在Auto.js中运行则为Auto.js的版本号;在打包的软件中则为打包软件的版本号. ``` toastLog(app.versionCode); @@ -20,9 +27,9 @@ toastLog(app.versionCode); * {string} -当前软件的版本名称,例如"3.0.0 Beta"。 +当前软件的版本名称, 例如"3.0.0 Beta". -如果在Auto.js中运行则为Auto.js的版本名称;在打包的软件中则为打包软件的版本名称。 +如果在Auto.js中运行则为Auto.js的版本名称;在打包的软件中则为打包软件的版本名称. ``` toastLog(app.verionName); @@ -32,21 +39,21 @@ toastLog(app.verionName); * {number} -Auto.js版本号,整数值。例如160, 256等。 +Auto.js版本号, 整数值. 例如160, 256等. ## app.autojs.versionName * {string} -Auto.js版本名称,例如"3.0.0 Beta"。 +Auto.js版本名称, 例如"3.0.0 Beta". ## app.launchApp(appName) * `appName` {string} 应用名称 -通过应用名称启动应用。如果该名称对应的应用不存在,则返回false; 否则返回true。如果该名称对应多个应用,则只启动其中某一个。 +通过应用名称启动应用. 如果该名称对应的应用不存在, 则返回false; 否则返回true. 如果该名称对应多个应用, 则只启动其中某一个. -该函数也可以作为全局函数使用。 +该函数也可以作为全局函数使用. ``` launchApp("Auto.js"); @@ -56,9 +63,9 @@ launchApp("Auto.js"); * `packageName` {string} 应用包名 -通过应用包名启动应用。如果该包名对应的应用不存在,则返回false;否则返回true。 +通过应用包名启动应用. 如果该包名对应的应用不存在, 则返回false;否则返回true. -该函数也可以作为全局函数使用。 +该函数也可以作为全局函数使用. ``` //启动微信 @@ -69,15 +76,15 @@ launch("com.tencent.mm"); * `packageName` {string} 应用包名 -相当于`app.launch(packageName)`。 +相当于`app.launch(packageName)`. ## app.getPackageName(appName) * `appName` {string} 应用名称 -获取应用名称对应的已安装的应用的包名。如果该找不到该应用,返回null;如果该名称对应多个应用,则只返回其中某一个的包名。 +获取应用名称对应的已安装的应用的包名. 如果该找不到该应用, 返回null;如果该名称对应多个应用, 则只返回其中某一个的包名. -该函数也可以作为全局函数使用。 +该函数也可以作为全局函数使用. ``` var name = getPackageName("QQ"); //返回"com.tencent.mobileqq" @@ -87,9 +94,9 @@ var name = getPackageName("QQ"); //返回"com.tencent.mobileqq" * `packageName` {string} 应用包名 -获取应用包名对应的已安装的应用的名称。如果该找不到该应用,返回null。 +获取应用包名对应的已安装的应用的名称. 如果该找不到该应用, 返回null. -该函数也可以作为全局函数使用。 +该函数也可以作为全局函数使用. ``` var name = getAppName("com.tencent.mobileqq"); //返回"QQ" @@ -99,17 +106,17 @@ var name = getAppName("com.tencent.mobileqq"); //返回"QQ" * `packageName` {string} 应用包名 -打开应用的详情页(设置页)。如果找不到该应用,返回false; 否则返回true。 +打开应用的详情页(设置页). 如果找不到该应用, 返回false; 否则返回true. -该函数也可以作为全局函数使用。 +该函数也可以作为全局函数使用. ## app.viewFile(path) * `path` {string} 文件路径 -用其他应用查看文件。文件不存在的情况由查看文件的应用处理。 +用其他应用查看文件. 文件不存在的情况由查看文件的应用处理. -如果找不出可以查看该文件的应用,则抛出`ActivityNotException`。 +如果找不出可以查看该文件的应用, 则抛出`ActivityNotException`. ``` //查看文本文件 @@ -120,9 +127,9 @@ app.viewFile("/sdcard/1.txt"); * `path` {string} 文件路径 -用其他应用编辑文件。文件不存在的情况由编辑文件的应用处理。 +用其他应用编辑文件. 文件不存在的情况由编辑文件的应用处理. -如果找不出可以编辑该文件的应用,则抛出`ActivityNotException`。 +如果找不出可以编辑该文件的应用, 则抛出`ActivityNotException`. ``` //编辑文本文件 @@ -133,7 +140,7 @@ app.editFile("/sdcard/1.txt/); * `packageName` {string} 应用包名 -卸载应用。执行后会会弹出卸载应用的提示框。如果该包名的应用未安装,由应用卸载程序处理,可能弹出"未找到应用"的提示。 +卸载应用. 执行后会会弹出卸载应用的提示框. 如果该包名的应用未安装, 由应用卸载程序处理, 可能弹出"未找到应用"的提示. ``` //卸载QQ @@ -142,28 +149,28 @@ app.uninstall("com.tencent.mobileqq"); ## app.openUrl(url) -* `url` {string} 网站的Url,如果不以"http://"或"https://"开头则默认是"http://"。 +* `url` {string} 网站的Url, 如果不以"http://"或"https://"开头则默认是"http://". -用浏览器打开网站url。 +用浏览器打开网站url. -如果没有安装浏览器应用,则抛出`ActivityNotException`。 +如果没有安装浏览器应用, 则抛出`ActivityNotException`. ## app.sendEmail(options) -* `options` {Object} 发送邮件的参数。包括: -* `email` {string} | {Array} 收件人的邮件地址。如果有多个收件人,则用字符串数组表示 -* `cc` {string} | {Array} 抄送收件人的邮件地址。如果有多个抄送收件人,则用字符串数组表示 -* `bcc` {string} | {Array} 密送收件人的邮件地址。如果有多个密送收件人,则用字符串数组表示 +* `options` {Object} 发送邮件的参数. 包括: +* `email` {string} | {Array} 收件人的邮件地址. 如果有多个收件人, 则用字符串数组表示 +* `cc` {string} | {Array} 抄送收件人的邮件地址. 如果有多个抄送收件人, 则用字符串数组表示 +* `bcc` {string} | {Array} 密送收件人的邮件地址. 如果有多个密送收件人, 则用字符串数组表示 * `subject` {string} 邮件主题(标题) * `text` {string} 邮件正文 -* `attachment` {string} 附件的路径。 +* `attachment` {string} 附件的路径. -根据选项options调用邮箱应用发送邮件。这些选项均是可选的。 +根据选项options调用邮箱应用发送邮件. 这些选项均是可选的. -如果没有安装邮箱应用,则抛出`ActivityNotException`。 +如果没有安装邮箱应用, 则抛出`ActivityNotException`. ``` -//发送邮件给10086@qq.com和10001@qq.com。 +//发送邮件给10086@qq.com和10001@qq.com. app.sendEmail({ email: ["10086@qq.com", "10001@qq.com"], subject: "这是一个邮件标题", @@ -173,69 +180,42 @@ app.sendEmail({ ## app.startActivity(name) -* `name` {string} 活动名称,可选的值为: +* `name` {string} 活动名称, 可选的值为: * `console` 日志界面 * `settings` 设置界面 -启动Auto.js的特定界面。该函数在Auto.js内运行则会打开Auto.js内的界面,在打包应用中运行则会打开打包应用的相应界面。 +启动Auto.js的特定界面. 该函数在Auto.js内运行则会打开Auto.js内的界面, 在打包应用中运行则会打开打包应用的相应界面. ``` app.startActivity("console"); ``` -# 进阶: 意图Intent - -Intent(意图) 是一个消息传递对象,您可以使用它从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个: - -* 启动活动(Activity): - Activity 表示应用中的一个"屏幕"。例如应用主入口都是一个Activity,应用的功能通常也以Activity的形式独立,例如微信的主界面、朋友圈、聊天窗口都是不同的Activity。通过将 Intent 传递给 startActivity(),您可以启动新的 Activity 实例。Intent 描述了要启动的 Activity,并携带了任何必要的数据。 - -* 启动服务(Service): - Service 是一个不使用用户界面而在后台执行操作的组件。通过将 Intent 传递给 startService(),您可以启动服务执行一次性操作(例如,下载文件)。Intent 描述了要启动的服务,并携带了任何必要的数据。 - -* 传递广播: - 广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将 Intent 传递给 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast(),您可以将广播传递给其他应用。 - -本模块提供了构建Intent的函数(`app.intent()`), 启动Activity的函数`app.startActivity()`, 发送广播的函数`app.sendBroadcast()`。 - -使用这些方法可以用来方便的调用其他应用。例如直接打开某个QQ号的个人卡片页,打开某个QQ号的聊天窗口等。 - -``` -var qq = "2732014414"; -app.startActivity({  -    action: "android.intent.action.VIEW",  -    data:"mqq://im/chat?chat_type=wpa&version=1&src_type=web&uin=" + qq,  -    packageName: "com.tencent.mobileqq",  -}); - -``` - ## app.intent(options) -* `options` {Object} 选项,包括: - * `action` {string} 意图的Action,指意图要完成的动作,是一个字符串常量,比如"android.intent.action.SEND"。当action以"android.intent.action"开头时,可以省略前缀,直接用"SEND"代替。参见[Actions](https://developer.android.com/reference/android/content/Intent.html#standard-activity-actions)。 +* `options` {Object} 选项, 包括: + * `action` {string} 意图的Action, 指意图要完成的动作, 是一个字符串常量, 比如"android.intent.action.SEND". 当action以"android.intent.action"开头时, 可以省略前缀, 直接用"SEND"代替. 参见[Actions](https://developer.android.com/reference/android/content/Intent.html#standard-activity-actions/). - * `type` {string} 意图的MimeType,表示和该意图直接相关的数据的类型,表示比如"text/plain"为纯文本类型。 + * `type` {string} 意图的MimeType, 表示和该意图直接相关的数据的类型, 表示比如"text/plain"为纯文本类型. - * `data` {string} 意图的Data,表示和该意图直接相关的数据,是一个Uri, 可以是文件路径或者Url等。例如要打开一个文件, action为"android.intent.action.VIEW", data为"file:///sdcard/1.txt"。 + * `data` {string} 意图的Data, 表示和该意图直接相关的数据, 是一个Uri, 可以是文件路径或者Url等. 例如要打开一个文件, action为"android.intent.action.VIEW", data为"file:///sdcard/1.txt". - * `category` {Array} 意图的类别。比较少用。参见[Categories](https://developer.android.com/reference/android/content/Intent.html#standard-categories)。 + * `category` {Array} 意图的类别. 比较少用. 参见[Categories](https://developer.android.com/reference/android/content/Intent.html#standard-categories/). * `packageName` {string} 目标包名 * `className` {string} 目标Activity或Service等组件的名称 - * `extras` {Object} 以键值对构成的这个Intent的Extras(额外信息)。提供该意图的其他信息,例如发送邮件时的邮件标题、邮件正文。参见[Extras](https://developer.android.com/reference/android/content/Intent.html#standard-extra-data)。 + * `extras` {Object} 以键值对构成的这个Intent的Extras(额外信息). 提供该意图的其他信息, 例如发送邮件时的邮件标题、邮件正文. 参见[Extras](https://developer.android.com/reference/android/content/Intent.html#standard-extra-data/). - * `flags` {Array} intent的标识,字符串数组,例如`["activity_new_task", "grant_read_uri_permission"]`。参见[Flags](https://developer.android.com/reference/android/content/Intent.html#setFlags%28int%29)。 + * `flags` {Array} intent的标识, 字符串数组, 例如`["activity_new_task", "grant_read_uri_permission"]`. 参见[Flags](https://developer.android.com/reference/android/content/Intent.html#setFlags%28int%29/). **[v4.1.0新增]** - * `root` {Boolea} 是否以root权限启动、发送该intent。使用该参数后,不能使用`context.startActivity()`等方法,而应该直接使用诸如`app.startActivity({...})`的方法。 + * `root` {Boolea} 是否以root权限启动、发送该intent. 使用该参数后, 不能使用`context.startActivity()`等方法, 而应该直接使用诸如`app.startActivity({...})`的方法. **[v4.1.0新增]** -根据选项,构造一个意图Intent对象。 +根据选项, 构造一个意图Intent对象. 例如: @@ -249,9 +229,9 @@ var i = app.intent({ context.startActivity(i); ``` -需要注意的是,除非应用专门暴露Activity出来,否则在没有root权限的情况下使用intent是无法跳转到特定Activity、应用的特定界面的。例如我们能通过Intent跳转到QQ的分享界面,是因为QQ对外暴露了分享的Activity;而在没有root权限的情况下,我们无法通过intent跳转到QQ的设置界面,因为QQ并没有暴露这个Activity。 +需要注意的是, 除非应用专门暴露Activity出来, 否则在没有root权限的情况下使用intent是无法跳转到特定Activity、应用的特定界面的. 例如我们能通过Intent跳转到QQ的分享界面, 是因为QQ对外暴露了分享的Activity;而在没有root权限的情况下, 我们无法通过intent跳转到QQ的设置界面, 因为QQ并没有暴露这个Activity. -但如果有root权限,则在intent的参数加上`"root": true`即可。例如使用root权限跳转到Auto.js的设置界面为: +但如果有root权限, 则在intent的参数加上`"root": true`即可. 例如使用root权限跳转到Auto.js的设置界面为: ``` app.startActivity({ @@ -261,15 +241,15 @@ app.startActivity({ }); ``` -另外,关于intent的参数如何获取的问题,一些intent是意外发现并且在网络中传播的(例如跳转QQ聊天窗口是因为QQ给网页提供了跳转到客服QQ的方法),如果要自己获取活动的intent的参数,可以通过例如"intent记录","隐式启动"等应用拦截内部intent或者查询暴露的intent。其中拦截内部intent需要XPosed框架,或者可以通过反编译等手段获取参数。总之,没有简单直接的方法。 +另外, 关于intent的参数如何获取的问题, 一些intent是意外发现并且在网络中传播的(例如跳转QQ聊天窗口是因为QQ给网页提供了跳转到客服QQ的方法), 如果要自己获取活动的intent的参数, 可以通过例如"intent记录", "隐式启动"等应用拦截内部intent或者查询暴露的intent. 其中拦截内部intent需要XPosed框架, 或者可以通过反编译等手段获取参数. 总之, 没有简单直接的方法. -更多信息,请百度[安卓Intent](https://www.baidu.com/s?wd=android%20Intent)或参考[Android指南: Intent](https://developer.android.com/guide/components/intents-filters.html#Types)。 +更多信息, 请百度[安卓Intent](https://www.baidu.com/s?wd=android%20Intent)或参考[Android指南: Intent](https://developer.android.com/guide/components/intents-filters.html#Types). ## app.startActivity(options) * `options` {Object} 选项 -根据选项构造一个Intent,并启动该Activity。 +根据选项构造一个Intent, 并启动该Activity. ``` app.startActivity({ @@ -283,23 +263,23 @@ app.startActivity({ * `options` {Object} 选项 -根据选项构造一个Intent,并发送该广播。 +根据选项构造一个Intent, 并发送该广播. ## app.startService(options) * `options` {Object} 选项 -根据选项构造一个Intent,并启动该服务。 +根据选项构造一个Intent, 并启动该服务. ## app.sendBroadcast(name) **[v4.1.0新增]** -* `name` {string} 特定的广播名称,包括: +* `name` {string} 特定的广播名称, 包括: * `inspect_layout_hierarchy` 布局层次分析 * `inspect_layout_bounds` 布局范围 -发送以上特定名称的广播可以触发Auto.js的布局分析,方便脚本调试。这些广播在Auto.js发送才有效,在打包的脚本上运行将没有任何效果。 +发送以上特定名称的广播可以触发Auto.js的布局分析, 方便脚本调试. 这些广播在Auto.js发送才有效, 在打包的脚本上运行将没有任何效果. ``` app.sendBroadcast("inspect_layout_bounds"); @@ -311,7 +291,7 @@ app.sendBroadcast("inspect_layout_bounds"); * `options` {Object} 选项 -根据选项构造一个Intent,转换为对应的shell的intent命令的参数。 +根据选项构造一个Intent, 转换为对应的shell的intent命令的参数. 例如: @@ -322,33 +302,33 @@ shell("am start " + app.intentToShell({ }), true); ``` -参见[intent参数的规范](https://developer.android.com/studio/command-line/adb#IntentSpec)。 +参见[intent参数的规范](https://developer.android.com/studio/command-line/adb#IntentSpec/). ## app.parseUri(uri) **[v4.1.0新增]** -* `uri` {string} 一个代表Uri的字符串,例如"file:///sdcard/1.txt", "https://www.autojs.org" -* 返回 {Uri} 一个代表Uri的对象,参见[android.net.Uri](https://developer.android.com/reference/android/net/Uri)。 +* `uri` {string} 一个代表Uri的字符串, 例如"file:///sdcard/1.txt", "https://www.autojs.org" +* 返回 {Uri} 一个代表Uri的对象, 参见[android.net.Uri](https://developer.android.com/reference/android/net/Uri/). -解析uri字符串并返回相应的Uri对象。即使Uri格式错误,该函数也会返回一个Uri对象,但之后如果访问该对象的scheme, path等值可能因解析失败而返回`null`。 +解析uri字符串并返回相应的Uri对象. 即使Uri格式错误, 该函数也会返回一个Uri对象, 但之后如果访问该对象的scheme, path等值可能因解析失败而返回`null`. -需要注意的是,在高版本Android上,由于系统限制直接在Uri暴露文件的绝对路径,因此如果uri字符串是文件`file://...`,返回的Uri会是诸如`content://...`的形式。 +需要注意的是, 在高版本Android上, 由于系统限制直接在Uri暴露文件的绝对路径, 因此如果uri字符串是文件`file://...`, 返回的Uri会是诸如`content://...`的形式. ## app.getUriForFile(path) **[v4.1.0新增]** -* `path` {string} 文件路径,例如"/sdcard/1.txt" -* 返回 {Uri} 一个指向该文件的Uri的对象,参见[android.net.Uri](https://developer.android.com/reference/android/net/Uri)。 +* `path` {string} 文件路径, 例如"/sdcard/1.txt" +* 返回 {Uri} 一个指向该文件的Uri的对象, 参见[android.net.Uri](https://developer.android.com/reference/android/net/Uri/). -从一个文件路径创建一个uri对象。需要注意的是,在高版本Android上,由于系统限制直接在Uri暴露文件的绝对路径,因此返回的Uri会是诸如`content://...`的形式。 +从一个文件路径创建一个uri对象. 需要注意的是, 在高版本Android上, 由于系统限制直接在Uri暴露文件的绝对路径, 因此返回的Uri会是诸如`content://...`的形式. ## app.getInstalledApps([options]) -** [[Pro 8.0.0新增](https://pro.autojs.org/)] ** +** [[Pro 8.0.0新增](https://pro.autojs.org//)] ** -* `options` {Object} 选项,包括: +* `options` {Object} 选项, 包括: * `get`: 指定返回的应用信息中包含的信息 * `"activities"` 应用的Activity组件信息 * `"configurations"` 应用的硬件配置 @@ -376,12 +356,12 @@ shell("am start " + app.intentToShell({ * `"apex"` APEX应用 * 返回 {Array\} -返回为当前用户安装的所有应用程序包的列表。如果设置了match选项 `uninstalled_packages`,则包括被删除但保留了数据的应用程序。 -获取安装的应用列表。 +返回为当前用户安装的所有应用程序包的列表. 如果设置了match选项 `uninstalled_packages`, 则包括被删除但保留了数据的应用程序. +获取安装的应用列表. -返回值是ApplicationInfo对象的数组。 如果没有安装任何应用,则返回一个空数组。 +返回值是ApplicationInfo对象的数组. 如果没有安装任何应用, 则返回一个空数组. -选项options的match选项用于指定要返回哪些应用程序,get选项用于指定返回的应用程序携带哪些信息。 +选项options的match选项用于指定要返回哪些应用程序, get选项用于指定返回的应用程序携带哪些信息. ``` let apps = $app.getInstalledApps({ diff --git a/api/appType.md b/api/appType.md new file mode 100644 index 0000000..d447ca8 --- /dev/null +++ b/api/appType.md @@ -0,0 +1,259 @@ +# 应用枚举类 (App) + +为便于与其他应用交互, AutoJs6 内置了部分常见应用的信息, 如下表: + +| 枚举实例名 | 中文名 | 英文名 | 包名 | 别名 | +|:-----------------|:---------------|:------------------|:-----------------------------------|:-----------------| +| ACCUWEATHER | AccuWeather | ~ | com.accuweather.android | accuweather | +| ADM | ADM | ~ | com.dv.adm | adm | +| ALIPAY | 支付宝 | Alipay | com.eg.android.AlipayGphone | alipay | +| AMAP | 高德地图 | Amap | com.autonavi.minimap | amap | +| APPOPS | App Ops | ~ | rikka.appops | appops | +| AQUAMAIL | Aqua Mail | ~ | org.kman.AquaMail | aquamail | +| AUTOJS | Auto.js | ~ | org.autojs.autojs | autojs | +| AUTOJS6 | AutoJs6 | ~ | org.autojs.autojs6 | autojs6 | +| AUTOJSPRO | AutoJsPro | ~ | org.autojs.autojspro | autojspro | +| BAIDUMAP | 百度地图 | BaiduMap | com.baidu.BaiduMap | baidumap | +| BILIBILI | 哔哩哔哩 | bilibili | tv.danmaku.bili | bilibili | +| BREVENT | 黑阈 | Brevent | mie.piebridge.brevent | brevent | +| CALENDAR | 日历 | Calendar | com.google.android.calendar | calendar | +| CHROME | Chrome | ~ | com.android.chrome | chrome | +| COOLAPK | 酷安 | CoolApk | com.coolapk.market | coolapk | +| DIANPING | 大众点评 | Dianping | com.dianping.v1 | dianping | +| DIGICAL | DigiCal | ~ | com.digibites.calendar | digical | +| DRIVE | 云端硬盘 | Drive | com.google.android.apps.docs | drive | +| ES | ES文件浏览器 | ES File Explorer | com.estrongs.android.pop | es | +| EUDIC | 欧路词典 | Eudic | com.qianyan.eudic | eudic | +| EXCEL | Excel | ~ | com.microsoft.office.excel | excel | +| FIREFOX | Firefox | ~ | org.mozilla.firefox | firefox | +| FX | FX | ~ | nextapp.fx | fx | +| GEOMETRICWEATHER | 几何天气 | Geometric Weather | wangdaye.com.geometricweather | geometricweather | +| HTTPCANARY | HttpCanary | ~ | com.guoshi.httpcanary.premium | httpcanary | +| IDLEFISH | 闲鱼 | ~ | com.taobao.idlefish | idlefish | +| IDMPLUS | IDM+ | ~ | idm.internet.download.manager.plus | idm+ | +| JD | 京东 | ~ | com.jingdong.app.mall | jd | +| KEEP | Keep | ~ | com.gotokeep.keep | keep | +| KEEPNOTES | Keep 记事 | Keep Notes | com.google.android.keep | keepnotes | +| MAGISK | Magisk | ~ | com.topjohnwu.magisk | magisk | +| MEITUAN | 美团 | Meituan | com.sankuai.meituan | meituan | +| MT | MT管理器 | MT Manager | bin.mt.plus | mt | +| MXPRO | MX 播放器专业版 | MX Player Pro | com.mxtech.videoplayer.pro | mxpro | +| ONEDRIVE | OneDrive | ~ | com.microsoft.skydrive | onedrive | +| PACKETCAPTURE | Packet Capture | ~ | app.greyshirts.sslcapture | packetcapture | +| PARALLELSPACE | 平行空间(原双开大师) | Parallel Space | com.lbe.parallel.intl | parallelspace | +| POWERPOINT | PowerPoint | ~ | com.microsoft.office.powerpoint | powerpoint | +| PULSARPLUS | Pulsar+ | ~ | com.rhmsoft.pulsar.pro | pulsarplus | +| PUREWEATHER | Pure天气 | ~ | hanjie.app.pureweather | pureweather | +| QQ | QQ | ~ | com.tencent.mobileqq | qq | +| QQMUSIC | QQ音乐 | QQMusic | com.tencent.qqmusic | qqmusic | +| SDMAID | SD Maid | ~ | eu.thedarken.sdm | sdmaid | +| SHIZUKU | Shizuku | ~ | moe.shizuku.privileged.api | shizuku | +| STOPAPP | 小黑屋 | ~ | web1n.stopapp | stopapp | +| TAOBAO | 淘宝 | ~ | com.taobao.taobao | taobao | +| TRAINNOTE | 训记 | ~ | com.trainnote.rn | trainnote | +| TWITTER | Twitter | ~ | com.twitter.android | twitter | +| UNIONPAY | 云闪付 | ~ | com.unionpay | unionpay | +| VIA | Via | ~ | mark.via.gp | via | +| VYSOR | Vysor | ~ | com.koushikdutta.vysor | vysor | +| WECHAT | 微信 | WeChat | com.tencent.mm | wechat | +| WORD | Word | ~ | com.microsoft.office.word | word | +| ZHIHU | 知乎 | ~ | com.zhihu.android | zhihu | + +通常 "别名" 字段取自 "枚举实例名" 字段的名称小写形式. +表列 "英文名" 中波浪符号表示与 "中文名" 对应字段名称相同. + +> 注: 上述信息可能发生变更. +> 例如一些应用在某个时间点开始去除了 "英文名" 并统一使用 "中文名" 字段, 甚至部分应用会在每个版本均变更其应用名. +> 如果用户编写的脚本对应用名十分敏感, 建议使用 App#getAppName 或 app.getAppName 等方式获取设备中已安装应用的真实应用名. + +--- + +

App

+ +--- + +## [@] App + +**`6.2.0`** **`Global`** **`Enum`** + +App 为枚举类, 因此可使用 Java 通用的枚举类方法: + +```js +/* 打印所有枚举实例名. */ +console.log(App.values().map(o => o.name())); + +/* 获取一个枚举实例. */ +const tt = App.FIREFOX; + +/* 调用实例方法. */ +console.log(tt.getAppName()); +console.log(tt.getPackageName()); +console.log(tt.getAlias()); + +``` + +## [m#] getAppName + +获取枚举实例的应用名. + +### getAppName() + +- **returns** { [string](dataTypes#string) } + +优先获取设备中已安装应用的应用名, 若应用未安装, 则获取 App 枚举实例中预置的应用名. + +```js +// "Firefox" +console.log(App.FIREFOX.getAppName()); +``` + +## [m#] getAppNameZh + +获取枚举实例中预置的中文应用名. + +### getAppNameZh() + +- **returns** { [string](dataTypes#string) } + +```js +// "支付宝" +console.log(App.ALIPAY.getAppNameZh()); +``` + +## [m#] getAppNameEn + +获取枚举实例中预置的英文应用名. + +### getAppNameEn() + +- **returns** { [string](dataTypes#string) } + +```js +// "Alipay" +console.log(App.ALIPAY.getAppNameEn()); +``` + +## [m#] getPackageName + +获取枚举实例中预置的应用包名. + +### getPackageName() + +- **returns** { [string](dataTypes#string) } + +```js +// "com.eg.android.AlipayGphone" +console.log(App.ALIPAY.getPackageName()); +``` + +## [m#] getAlias + +获取枚举实例中预置的应用别名. + +### getAlias() + +- **returns** { [string](dataTypes#string) } + +```js +// "alipay" +console.log(App.ALIPAY.getAlias()); +``` + +## [m#] isInstalled + +检查枚举实例是否在设备安装. + +### isInstalled() + +- **returns** { [boolean](dataTypes#boolean) } + +```js +/* e.g. true */ +console.log(App.ALIPAY.isInstalled()); +``` + +## [m#] ensureInstalled + +确保枚举实例在设备安装, 否则抛出异常. + +### ensureInstalled() + +- **returns** { [void](dataTypes#void) } + +```js +App.FIREFOX.ensureInstalled(); +``` + +## [m#] uninstall + +卸载设备中存在的枚举实例应用. + +### uninstall() + +- **returns** { [void](dataTypes#void) } + +```js +App.FIREFOX.uninstall(); +``` + +## [m#] launch + +启动设备中的枚举实例应用. + +### launch() + +- **returns** { [boolean](dataTypes#boolean) } + +若应用未安装或启动过程中出错, 将返回 false (而非抛出异常). + +```js +/* e.g. true */ +console.log(App.FIREFOX.launch()); +``` + +## [m#] openSettings + +跳转至枚举实例应用的应用详情页面. + +### openSettings() + +- **returns** { [boolean](dataTypes#boolean) } + +若应用未安装或跳转页面过程中出错, 将返回 false (而非抛出异常). + +```js +/* e.g. true */ +console.log(App.FIREFOX.openSettings()); +``` + +## [m#] toString + +获取枚举实例自定义的实例信息字符串. + +### toString() + +- **returns** { [string](dataTypes#string) } + +```js +/* e.g. {appName: "Firefox", packageName: "org.mozilla.firefox", alias: "firefox"} */ +console.log(App.FIREFOX.toString()); +console.log(App.FIREFOX); /* 同上. */ +``` + +## [m] getAppByAlias + +通过应用别名获取对应的 App 实例. + +### getAppByAlias(alias) + +- **alias** { [string](dataTypes#string) } - 应用别名 +- **returns** { [App](appType) | [null](dataTypes#null) } + +应用别名对应的枚举实例不存在时将返回 null: + +```js +let tt = App.getAppByAlias('twitter'); +if (tt !== null) { + console.log(tt.getPackageName()); +} +``` \ No newline at end of file diff --git a/api/arrayx.md b/api/arrayx.md new file mode 100644 index 0000000..5abc97a --- /dev/null +++ b/api/arrayx.md @@ -0,0 +1,407 @@ +# Arrayx (Array 扩展) + +Arrayx 用于扩展 JavaScript 标准内置对象 Array 的功能 (参阅 [内置对象扩展](glossaries#内置对象扩展)). + +Arrayx 全局可用: + +```js +console.log(typeof Arrayx); // "object" +console.log(typeof Arrayx.union); // "function" +``` + +当启用内置扩展后, Arrayx 将被应用在 Array 及其原型上: + +```js +console.log(typeof Array.prototype.union); // "function" +console.log(typeof [].union); // "function" +``` + +## 启用内置扩展 + +内置扩展默认被禁用, 以下任一方式可启用内置扩展: + +- 在脚本中加入代码片段: `plugins.extendAll();` 或 `plugins.extend('Array');` +- AutoJs6 应用设置 - 扩展性 - JavaScript 内置对象扩展 - [ 启用 ] + +当上述应用设置启用时, 所有脚本均默认启用内置扩展. +当上述应用设置禁用时, 只有加入上述代码片段的脚本才会启用内置扩展. +内置扩展往往是不安全的, 除非明确了解内置扩展的原理及风险, 否则不建议启用. + +## 排序稳定性 + +Arrayx 的诸多排序方法, 如 [ sortBy / sortDescending / sortByDescending / sorted / sortedBy / sortedDescending / sortedByDescending ] 等, 其内部实现均调用了 JavaScript 的原生方法 [Array.prototype.sort](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort). + +自 ES10(ECMAScript 2019)起,[规范](https://tc39.es/ecma262/#sec-array.prototype.sort) 要求 Array.prototype.sort 为稳定排序, 因此 Arrayx 的排序方法也是稳定的. + +> 参阅: [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#%E6%8E%92%E5%BA%8F%E7%A8%B3%E5%AE%9A%E6%80%A7) + +## 排序方式 + +Arrayx 排序方法中的 selector (条件选择器) 使用的 compareFn (比较函数) 与默认的稍有不同. +默认的比较函数按照转换为字符串的诸个字符的 Unicode 位点进行 **升序** 排序, 例如 80 会被排列到 9 之前. +而 Arrayx 排序方法的 selector 则采用简单的直接比较方法: + +```js +/* Arrayx 使用的 compareFn. */ +(a, b) => a === b ? 0 : a > b ? 1 : -1; +``` + +因此对于 number 数组的排序会出现不同的结果: + +```js +console.log([ 80, 70, 9 ].sort()); // [ 70, 80, 9 ] +console.log(Arrayx.sorted([ 80, 70, 9 ])); // [ 9, 70, 80 ] +``` + +--- + +

Arrayx

+ +--- + +## [m] ensureArray + +### ensureArray(...o) + +**`6.2.0`** **`xObject`** + +- **o** { [...](documentation#可变参数)[any](dataTypes#any)[[]](documentation#可变参数) } - 任意对象 +- **returns** { [void](dataTypes#void) } + +相当于严格类型检查, 当任意一个 o 不满足 `Array.isArray(o)` 时抛出异常. + +```js +/* 确保每一个对象都是 Array. */ + +console.log(Arrayx.ensureArray([])); /* 无异常. */ +console.log(Arrayx.ensureArray([], 9)); /* 抛出异常. */ +console.log(Arrayx.ensureArray([ 5 ], [ 2, 3 ])); /* 无异常. */ + +/* 启用内置对象扩展后. */ + +console.log(Array.ensureArray([])); /* 无异常. */ +console.log(Array.ensureArray([], 9)); /* 抛出异常. */ +console.log(Array.ensureArray([ 5 ], [ 2, 3 ])); /* 无异常. */ +``` + +## [m] intersect + +### intersect(o, ...others) + +**`6.2.0`** **`xProto`** + +- **o** { [T](dataTypes#generic)[[]](dataTypes#array) } - 数组 +- **others** { [...](documentation#可变参数)[U](dataTypes#generic)[[]](dataTypes#array)[[]](documentation#可变参数) } - 待处理数组 +- **returns** { ([T](dataTypes#generic) & [U](dataTypes#generic))[[]](dataTypes#array) } + +返回多个数组的交集. + +```js +let arrA = [ 1, 2, 3 ]; +let arrB = [ 2, 3, 4 ]; +console.log(Arrayx.intersect(arrA, arrB)); // [ 2, 3 ] + +/* 启用内置对象扩展后. */ +console.log(arrA.intersect(arrB)); /* 同上. */ + +let arrC = [ 1, 1, 2 ]; +let arrD = [ 1, 1, 3 ]; +/* 返回的交集结果中不包含重复元素. */ +console.log(Arrayx.intersect(arrC, arrD)); // [ 1 ] + +/* 启用内置对象扩展后. */ +console.log(arrC.intersect(arrD)); /* 同上. */ +``` + +## [m] union + +### union(o, ...others) + +**`6.2.0`** **`xProto`** + +- **o** { [T](dataTypes#generic)[[]](dataTypes#array) } - 数组 +- **others** { [...](documentation#可变参数)[U](dataTypes#generic)[[]](dataTypes#array)[[]](documentation#可变参数) } - 待处理数组 +- **returns** { ([T](dataTypes#generic) | [U](dataTypes#generic))[[]](dataTypes#array) } + +返回多个数组的并集. + +```js +let arrA = [ 1, 2 ]; +let arrB = [ 3, 4 ]; +console.log(Arrayx.union(arrA, arrB)); // [ 1, 2, 3, 4 ] + +/* 启用内置对象扩展后. */ +console.log(arrA.union(arrB)); /* 同上. */ + +let arrC = [ 7, 8, 9 ]; +let arrD = [ 7, 10 ]; +/* 返回的并集结果中不包含重复元素. */ +console.log(Arrayx.union(arrC, arrD)); // [ 7, 8, 9, 10 ] + +/* 启用内置对象扩展后. */ +console.log(arrC.union(arrD)); /* 同上. */ +``` + +## [m] distinct + +### distinct(arr) + +**`6.2.0`** **`xProto`** + +- **o** { [T](dataTypes#generic)[[]](dataTypes#array) } - 待处理数组 +- **returns** { [T](dataTypes#generic)[[]](dataTypes#array) } + +返回去除重复元素后的新数组. + +```js +let arr = [ 1, 2, 1, 3, 2, 2 ]; +console.log(Arrayx.distinct(arr)); // [ 1, 2, 3 ] + +/* 启用内置对象扩展后. */ +console.log(arr.distinct()); /* 同上. */ +``` + +## [m] distinctBy + +### distinctBy(arr, selector) + +**`6.2.0`** **`xProto`** + +- **arr** { [T](dataTypes#generic)[[]](dataTypes#array) } - 待处理数组 +- **selector** { [(](dataTypes#function)e: [T](dataTypes#generic)[)](dataTypes#function) [=>](dataTypes#function) [U](dataTypes#generic) } - 条件选择器 +- **returns** { [T](dataTypes#generic)[[]](dataTypes#array) } + +返回按一定条件去除重复元素后的新数组. + +```js +/* 按奇偶条件去重. */ +/* 即结果数组中的所有元素保证相互之间奇偶互异. */ +let arrA = [ 1, 2, 1, 3, 2, 2 ]; +console.log(Arrayx.distinctBy(arrA, e => e % 2)); // [ 1, 2 ] + +/* 启用内置对象扩展后. */ +console.log(arrA.distinctBy(e => e % 2)); // /* 同上. */ + +/* 按字符串长度条件去重. */ +/* 即结果数组中的所有元素保证相互之间长度不同. */ +let arrB = [ 'a', 'ab', 'c', 'bc', 'abc', '123' ]; +console.log(Arrayx.distinctBy(arrB, e => e.length)); // [ 'a', 'ab', 'abc' ] + +/* 启用内置对象扩展后. */ +console.log(arrB.distinctBy(e => e.length)); /* 同上. */ + +/* 按对象属性条件去重. */ +let arrC = [ + { num: 1, count: 10 }, + { num: 2, count: 10 }, + { num: 3, count: 20 }, + { num: 4, count: 10 }, + { num: 5, count: 20 }, +]; +console.log(Arrayx.distinctBy(arrC, e => e.count)); // [ { num: 1, count: 10 }, { num: 3, count: 20 } ] + +/* 启用内置对象扩展后. */ +console.log(arrC.distinctBy(e => e.count)); /* 同上. */ +``` + +## [m] sortBy + +### sortBy(arr, selector) + +**`6.2.0`** **`xProto`** + +- **arr** { [T](dataTypes#generic)[[]](dataTypes#array) } - 待处理数组 +- **selector** { [(](dataTypes#function)e: [T](dataTypes#generic)[)](dataTypes#function) [=>](dataTypes#function) [U](dataTypes#generic) } - 条件选择器 +- **returns** { [T](dataTypes#generic)[[]](dataTypes#array) } + +按一定条件原地排序, 并返回排序后的新数组. +方法调用后, 原数组将发生改变. + +```js +/* 将字符串按 "数字化" 顺序排序. */ +let arrA = [ '10', '2', '30', '4', '50', '0x6' ]; +console.log(Arrayx.sortBy(arrA, Number)); // [ '2', '4', '0x6', '10', '30', '50' ] + +/* arrA 将发生改变. */ +console.log(arrA); // [ '2', '4', '0x6', '10', '30', '50' ] + +/* 启用内置对象扩展后的使用方式. */ +console.log(arrA.sortBy(Number)); /* 结果同上. */ + +/* 按字符串长度条件排序. */ +let arrB = [ 'a', 'ab', 'c', 'bc', 'abc', '123' ]; +console.log(Arrayx.sortBy(arrB, e => e.length)); // [ 'a', 'c', 'ab', 'bc', 'abc', '123' ] + +/* 启用内置对象扩展后的使用方式. */ +console.log(arrB.sortBy(e => e.length)); /* 结果同上. */ + +/* 按对象属性条件排序. */ +let arrC = [ + { num: 1, count: 10 }, + { num: 2, count: 30 }, + { num: 3, count: 20 }, + { num: 4, count: 10 }, + { num: 5, count: 0 }, +]; +console.log(Arrayx.sortBy(arrC, e => e.count)); /* num 按照 5 - 1 - 4 - 3 - 2 排序. */ + +/* 启用内置对象扩展后的使用方式. */ +console.log(arrC.sortBy(e => e.count)); /* 结果同上. */ +``` + +## [m] sortDescending + +### sortDescending(arr) + +**`6.2.0`** **`xProto`** + +- **arr** { [T](dataTypes#generic)[[]](dataTypes#array) } - 待处理数组 +- **returns** { [T](dataTypes#generic)[[]](dataTypes#array) } + +按一定条件原地 **降序** 排序, 并返回排序后的新数组. +方法调用后, 原数组将发生改变. + +```js +/* 将字符串按 "数字化" 顺序排序. */ +let arrA = [ '10', '2', '30', '4', '50', '0x6' ]; + +// [ '2', '4', '0x6', '10', '30', '50' ] +console.log(Arrayx.sortDescending(arrA, Number)); + +/* arrA 将发生改变. */ +console.log(arrA[0]); // '2' + +/* 启用内置对象扩展后的使用方式. */ +console.log(arrA.sortDescending(Number)); /* 结果同上. */ + +/* 按字符串长度条件排序. */ +let arrB = [ 'a', 'ab', 'c', 'bc', 'abc', '123' ]; + +// [ 'a', 'c', 'ab', 'bc', 'abc', '123' ] +console.log(Arrayx.sortDescending(arrB, e => e.length)); + +/* 启用内置对象扩展后的使用方式. */ +console.log(arrB.sortDescending(e => e.length)); /* 结果同上. */ + +/* 按对象属性条件排序. */ +let arrC = [ + { num: 1, count: 10 }, + { num: 2, count: 30 }, + { num: 3, count: 20 }, + { num: 4, count: 10 }, + { num: 5, count: 0 }, +]; + +/* 元素将按照 num 属性值以 5 - 1 - 4 - 3 - 2 排序. */ +console.log(Arrayx.sortDescending(arrC, e => e.count)); + +/* 启用内置对象扩展后的使用方式. */ +console.log(arrC.sortDescending(e => e.count)); /* 结果同上. */ +``` + +## [m] sortByDescending + +### sortByDescending(arr, selector) + +**`6.2.0`** **`xProto`** + +- **arr** { [T](dataTypes#generic)[[]](dataTypes#array) } - 待处理数组 +- **selector** { [(](dataTypes#function)e: [T](dataTypes#generic)[)](dataTypes#function) [=>](dataTypes#function) [U](dataTypes#generic) } - 条件选择器 +- **returns** { [T](dataTypes#generic)[[]](dataTypes#array) } + +按一定条件原地 **降序** 排序, 并返回排序后的新数组. +方法调用后, 原数组将发生改变. + +> 参阅: [sortBy](#m-sortby) + +## [m] sorted + +### sorted(arr) + +**`6.2.0`** **`xProto`** + +- **arr** { [T](dataTypes#generic)[[]](dataTypes#array) } - 待处理数组 +- **returns** { [T](dataTypes#generic)[[]](dataTypes#array) } + +按简单比较方式原地排序, 并返回排序后的新数组. +方法调用后, 原数组将 **不** 发生改变. + +```js +let arr = [ 2, 3, 1, 20, 10 ]; + +console.log(Arrayx.sorted(arr)); // [ 1, 2, 3, 10, 20 ] + +/* 启用内置对象扩展后. */ +console.log(arr.sorted()); /* 同上. */ + +/* arr 不发生改变. */ +console.log(arr); // [ 2, 3, 1, 20, 10 ] +``` + +## [m] sortedBy + +### sortedBy(arr, selector) + +**`6.2.0`** **`xProto`** + +- **arr** { [T](dataTypes#generic)[[]](dataTypes#array) } - 待处理数组 +- **selector** { [(](dataTypes#function)e: [T](dataTypes#generic)[)](dataTypes#function) [=>](dataTypes#function) [U](dataTypes#generic) } - 条件选择器 +- **returns** { [T](dataTypes#generic)[[]](dataTypes#array) } + +按一定条件原地排序, 并返回排序后的新数组. +方法调用后, 原数组将 **不** 发生改变. + +> 参阅: [sortBy](#m-sortby) + +## [m] sortedDescending + +### sortedDescending(arr) + +**`6.2.0`** **`xProto`** + +- **arr** { [T](dataTypes#generic)[[]](dataTypes#array) } - 待处理数组 +- **returns** { [T](dataTypes#generic)[[]](dataTypes#array) } + +按一定条件原地 **降序** 排序, 并返回排序后的新数组. +方法调用后, 原数组将 **不** 发生改变. + +> 参阅: [sortDescending](#m-sortdescending) + +## [m] sortedByDescending + +### sortedByDescending(arr, selector) + +**`6.2.0`** **`xProto`** + +- **arr** { [T](dataTypes#generic)[[]](dataTypes#array) } - 待处理数组 +- **selector** { [(](dataTypes#function)e: [T](dataTypes#generic)[)](dataTypes#function) [=>](dataTypes#function) [U](dataTypes#generic) } - 条件选择器 +- **returns** { [T](dataTypes#generic)[[]](dataTypes#array) } + +按一定条件原地 **降序** 排序, 并返回排序后的新数组. +方法调用后, 原数组将 **不** 发生改变. + +> 参阅: [sortByDescending](#m-sortbydescending) + +## [m] shuffle + +### shuffle(arr) + +**`6.2.0`** **`xProto`** + +- **arr** { [T](dataTypes#generic)[[]](dataTypes#array) } - 待处理数组 +- **returns** { [T](dataTypes#generic)[[]](dataTypes#array) } + +按随机乱序方式原地排序, 并返回排序后的新数组. +方法调用后, 原数组将发生改变. + +```js +/* 将元素随机乱序排序. */ +let arrA = [ 1, 2, 3, 4, 5, 6 ]; +console.log(Arrayx.shuffle(arrA)); /* e.g. [ 2, 4, 5, 1, 6, 3 ] */ + +/* arrA 将发生改变. */ +console.log(arrA); /* 很可能不再是 [ 1, 2, 3, 4, 5, 6 ]. */ + +/* 启用内置对象扩展后的使用方式. */ +console.log(arrA.shuffle()); /* 另一个随机的结果. */ +``` \ No newline at end of file diff --git a/api/autojs.md b/api/autojs.md new file mode 100644 index 0000000..4cf88e8 --- /dev/null +++ b/api/autojs.md @@ -0,0 +1,8 @@ +# AutoJs6 本体应用 + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- \ No newline at end of file diff --git a/api/automator.md b/api/automator.md new file mode 100644 index 0000000..f949b29 --- /dev/null +++ b/api/automator.md @@ -0,0 +1,534 @@ +# 自动化 (Automator) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +## 简易自动化 (SimpleActionAutomator) + +待补充... + +## 高权限自动化 (RootAutomator) + +待补充... + +## 自动化配置 (AutomatorConfiguration) + +待补充... + +## 选择器 (UiSelector) + +UiSelector (选择器), 亦可看作是 [控件节点](uiObjectType) 的条件筛选器, 用于通过附加不同的条件, 筛选出一个或一组活动窗口中的 `控件节点`, 并做进一步处理, 如 [ 执行 [控件行为](uiObjectActionsType) (点击, 长按, 设置文本等) / 判断位置 / 获取文本内容 / 获取控件特定状态 / 在 [控件层级](glossaries#控件层级) 中进行 [罗盘](uiObjectType.md#m-compass) 导航 ] 等. + +详情参阅 [选择器 (UiSelector)](uiSelectorType) 章节. + +## 控件节点 (UiObject) + +UiObject 通常被称为 [ 控件 / 节点 / 控件节点 ], 可看做是一个通过安卓无障碍服务包装的 [AccessibilityNodeInfo](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo) 对象, 代表一个当前活动窗口中的节点, 通过此节点可收集控件信息或执行控件行为, 进而实现一系列自动化操作. + +详情参阅 [控件节点 (UiObject)](uiObjectType) 章节. + +## 控件集合 (UiObjectCollection) + +UiObjectCollection 代表 [控件节点 (UiObject)](uiObjectType) 的对象集合. + +详情参阅 [控件集合 (UiObjectCollection)](uiObjectCollectionType) 章节. + +## 控件节点行为 (UiObjectActions) + +UiObjectActions 是一个 Java 接口, 代表 [控件节点 (UiObject)](uiObjectType) 的行为集合. + +详情参阅 [控件节点行为 (UiObjectActions)](uiObjectActionsType) 章节. + +--- + +# 基于坐标的触摸模拟 + +本章节介绍了一些使用坐标进行点击、滑动的函数. 这些函数有的需要安卓7.0以上, 有的需要root权限. + +要获取要点击的位置的坐标, 可以在开发者选项中开启"指针位置". + +基于坐标的脚本通常会有分辨率的问题, 这时可以通过`setScreenMetrics()`函数来进行自动坐标放缩. 这个函数会影响本章节的所有点击、长按、滑动等函数. 通过设定脚本设计时的分辨率, 使得脚本在其他分辨率下自动放缩坐标. + +控件和坐标也可以相互结合. 一些控件是无法点击的(clickable为false), 无法通过`.click()`函数来点击, 这时如果安卓版本在7.0以上或者有root权限, 就可以通过以下方式来点击: + +``` +//获取这个控件 +var widget = id("xxx").findOne(); +//获取其中心位置并点击 +click(widget.bounds().centerX(), widget.bounds().centerY()); +//如果用root权限则用Tap +``` + +## setScreenMetrics(width, height) + +* width {number} 屏幕宽度, 单位像素 +* height {number} 屏幕高度, 单位像素 + +设置脚本坐标点击所适合的屏幕宽高. 如果脚本运行时, 屏幕宽度不一致会自动放缩坐标. + +例如在1920*1080的设备中, 某个操作的代码为 + +``` +setScreenMetrics(1080, 1920); +click(800, 200); +longClick(300, 500); +``` + +那么在其他设备上AutoJs会自动放缩坐标以便脚本仍然有效. 例如在540 * 960的屏幕中`click(800, 200)`实际上会点击位置(400, 100). + +# 安卓7.0以上的触摸和手势模拟 + +**注意以下命令只有Android7.0及以上才有效** + +## click(x, y) + +* `x` {number} 要点击的坐标的x值 +* `y` {number} 要点击的坐标的y值 + +模拟点击坐标(x, y), 并返回是否点击成功. 只有在点击执行完成后脚本才继续执行. + +一般而言, 只有点击过程(大约150毫秒)中被其他事件中断(例如用户自行点击)才会点击失败. + +使用该函数模拟连续点击时可能有点击速度过慢的问题, 这时可以用`press()`函数代替. + +## longClick(x, y) + +* `x` {number} 要长按的坐标的x值 +* `y` {number} 要长按的坐标的y值 + +模拟长按坐标(x, y), 并返回是否成功. 只有在长按执行完成(大约600毫秒)时脚本才会继续执行. + +一般而言, 只有长按过程中被其他事件中断(例如用户自行点击)才会长按失败. + +## press(x, y, duration) + +* `x` {number} 要按住的坐标的x值 +* `y` {number} 要按住的坐标的y值 +* `duration` {number} 按住时长, 单位毫秒 + +模拟按住坐标(x, y), 并返回是否成功. 只有按住操作执行完成时脚本才会继续执行. + +如果按住时间过短, 那么会被系统认为是点击;如果时长超过500毫秒, 则认为是长按. + +一般而言, 只有按住过程中被其他事件中断才会操作失败. + +一个连点器的例子如下: + +``` +//循环100次 +for(var i = 0; i < 100; i++){ + //点击位置(500, 1000), 每次用时1毫秒 + press(500, 1000, 1); +} +``` + +## swipe(x1, y1, x2, y2, duration) + +* `x1` {number} 滑动的起始坐标的x值 +* `y1` {number} 滑动的起始坐标的y值 +* `x2` {number} 滑动的结束坐标的x值 +* `y2` {number} 滑动的结束坐标的y值 +* `duration` {number} 滑动时长, 单位毫秒 + +模拟从坐标(x1, y1)滑动到坐标(x2, y2), 并返回是否成功. 只有滑动操作执行完成时脚本才会继续执行. + +一般而言, 只有滑动过程中被其他事件中断才会滑动失败. + +## gesture(duration, [x1, y1], [x2, y2], ...) + +* `duration` {number} 手势的时长 +* [x, y] ... 手势滑动路径的一系列坐标 + +模拟手势操作. 例如`gesture(1000, [0, 0], [500, 500], [500, 1000])`为模拟一个从(0, 0)到(500, 500)到(500, 100)的手势操作, 时长为2秒. + +## gestures([delay1, duration1, [x1, y1], [x2, y2], ...], [delay2, duration2, [x3, y3], [x4, y4], ...], ...) + +同时模拟多个手势. 每个手势的参数为\[delay, duration, 坐标\], delay为延迟多久(毫秒)才执行该手势;duration为手势执行时长;坐标为手势经过的点的坐标. 其中delay参数可以省略, 默认为0. + +例如手指捏合: + +``` +gestures([0, 500, [800, 300], [500, 1000]], + [0, 500, [300, 1500], [500, 1000]]); +``` + +# RootAutomator + +RootAutomator是一个使用root权限来模拟触摸的对象, 用它可以完成触摸与多点触摸, 并且这些动作的执行没有延迟. + +一个脚本中最好只存在一个RootAutomator, 并且保证脚本结束退出他. 可以在exit事件中退出RootAutomator, 例如: + +``` +var ra = new RootAutomator(); +events.on('exit', function(){ + ra.exit(); +}); +//执行一些点击操作 +... + +``` + +**注意以下命令需要root权限** + +## RootAutomator.tap(x, y[, id]) + +* `x` {number} 横坐标 +* `y` {number} 纵坐标 +* `id` {number} 多点触摸id, 可选, 默认为1, 可以通过setDefaultId指定. + +点击位置(x, y). 其中id是一个整数值, 用于区分多点触摸, 不同的id表示不同的"手指", 例如: + +``` +var ra = new RootAutomator(); +//让"手指1"点击位置(100, 100) +ra.tap(100, 100, 1); +//让"手指2"点击位置(200, 200); +ra.tap(200, 200, 2); +ra.exit(); +``` + +如果不需要多点触摸, 则不需要id这个参数. +多点触摸通常用于手势或游戏操作, 例如模拟双指捏合、双指上滑等. + +某些情况下可能存在tap点击无反应的情况, 这时可以用`RootAutomator.press()`函数代替. + +## RootAutomator.swipe(x1, x2, y1, y2[, duration, id]) + +* `x1` {number} 滑动起点横坐标 +* `y1` {number} 滑动起点纵坐标 +* `x2` {number} 滑动终点横坐标 +* `y2` {number} 滑动终点纵坐标 +* `duration` {number} 滑动时长, 单位毫秒, 默认值为300 +* `id` {number} 多点触摸id, 可选, 默认为1 + +模拟一次从(x1, y1)到(x2, y2)的时间为duration毫秒的滑动. + +## RootAutomator.press(x, y, duration[, id]) + +* `x` {number} 横坐标 +* `y` {number} 纵坐标 +* `duration` {number} 按下时长 +* `id` {number} 多点触摸id, 可选, 默认为1 + +模拟按下位置(x, y), 时长为duration毫秒. + +## RootAutomator.longPress(x, y[\, id\]) + +* `x` {number} 横坐标 +* `y` {number} 纵坐标 +* `duration` {number} 按下时长 +* `id` {number} 多点触摸id, 可选, 默认为1 + +模拟长按位置(x, y). + +以上为简单模拟触摸操作的函数. 如果要模拟一些复杂的手势, 需要更底层的函数. + +## RootAutomator.touchDown(x, y[, id]) + +* `x` {number} 横坐标 +* `y` {number} 纵坐标 +* `id` {number} 多点触摸id, 可选, 默认为1 + +模拟手指按下位置(x, y). + +## RootAutomator.touchMove(x, y[, id]) + +* `x` {number} 横坐标 +* `y` {number} 纵坐标 +* `id` {number} 多点触摸id, 可选, 默认为1 + +模拟移动手指到位置(x, y). + +## RootAutomator.touchUp([id]) + +* `id` {number} 多点触摸id, 可选, 默认为1 + +模拟手指弹起. + +# 使用root权限点击和滑动的简单命令 + +注意:本章节的函数在后续版本很可能有改动!请勿过分依赖本章节函数的副作用. 推荐使用`RootAutomator`代替本章节的触摸函数. + +以下函数均需要root权限, 可以实现任意位置的点击、滑动等. + +* 这些函数通常首字母大写以表示其特殊的权限. +* 这些函数均不返回任何值. +* 并且, 这些函数的执行是异步的、非阻塞的, 在不同机型上所用的时间不同. 脚本不会等待动作执行完成才继续执行. 因此最好在每个函数之后加上适当的sleep来达到期望的效果. + +例如: + +``` +Tap(100, 100); +sleep(500); +``` + +注意, 动作的执行可能无法被停止, 例如: + +``` +for(var i = 0; i < 100; i++){ + Tap(100, 100); +} +``` + +这段代码执行后可能会出现在任务管理中停止脚本后点击仍然继续的情况. +因此, 强烈建议在每个动作后加上延时: + +``` +for(var i = 0; i < 100; i++){ + Tap(100, 100); + sleep(500); +} +``` + +## Tap(x, y) + +* x, y {number} 要点击的坐标. + +点击位置(x, y), 您可以通过"开发者选项"开启指针位置来确定点击坐标. + +## Swipe(x1, y1, x2, y2, \[duration\]) + +* x1, y1 {number} 滑动起点的坐标 +* x2, y2 {number} 滑动终点的坐标 +* duration {number} 滑动动作所用的时间 + +滑动. 从(x1, y1)位置滑动到(x2, y2)位置. + +# 基于控件的操作 + +基于控件的操作指的是选择屏幕上的控件, 获取其信息或对其进行操作. 对于一般软件而言, 基于控件的操作对不同机型有很好的兼容性;但是对于游戏而言, 由于游戏界面并不是由控件构成, 无法采用本章节的方法, 也无法使用本章节的函数. 有关游戏脚本的编写, 请参考《基于坐标的操作》. + +基于控件的操作依赖于无障碍服务, 因此最好在脚本开头使用`auto()`函数来确保无障碍服务已经启用. 如果运行到某个需要权限的语句无障碍服务并没启动, 则会抛出异常并跳转到无障碍服务界面. 这样的用户体验并不好, 因为需要重新运行脚本, 后续会加入等待无障碍服务启动并让脚本继续运行的函数. + +您也可以在脚本开头使用`"auto";`表示这个脚本需要无障碍服务, 但是不推荐这种做法, 因为这个标记必须在脚本的最开头(前面不能有注释或其他语句、空格等), 我们推荐使用`auto()`函数来确保无障碍服务已启用. + +## auto([mode]) + +* `mode` {string} 模式 + +检查无障碍服务是否已经启用, 如果没有启用则抛出异常并跳转到无障碍服务启用界面;同时设置无障碍模式为mode. mode的可选值为: + +* `fast` 快速模式. 该模式下会启用控件缓存, 从而选择器获取屏幕控件更快. 对于需要快速的控件操作的脚本可以使用该模式, 一般脚本则没有必要使用该函数. +* `normal` 正常模式, 默认. + +如果不加mode参数, 则为正常模式. + +建议使用`auto.waitFor()`和`auto.setMode()`代替该函数, 因为`auto()`函数如果无障碍服务未启动会停止脚本;而`auto.waitFor()`则会在在无障碍服务启动后继续运行. + +示例: + +``` +auto("fast"); +``` + +示例2: + +``` +auto(); +``` + +## auto.waitFor() + +检查无障碍服务是否已经启用, 如果没有启用则跳转到无障碍服务启用界面, 并等待无障碍服务启动;当无障碍服务启动后脚本会继续运行. + +因为该函数是阻塞的, 因此除非是有协程特性, 否则不能在ui模式下运行该函数, 建议在ui模式下使用`auto()`函数. + +## auto.setMode(mode) + +* `mode` {string} 模式 + +设置无障碍模式为mode. mode的可选值为: + +* `fast` 快速模式. 该模式下会启用控件缓存, 从而选择器获取屏幕控件更快. 对于需要快速的控件查看和操作的脚本可以使用该模式, 一般脚本则没有必要使用该函数. +* `normal` 正常模式, 默认. + +## auto.setFlags(flags) + +**[v4.1.0新增]** + +* `flags` {string} | {Array} 一些标志, 来启用和禁用某些特性, 包括: + * `findOnUiThread` 使用该特性后, 选择器搜索时会在主进程进行. 该特性用于解决线程安全问题导致的次生问题, 不过目前貌似已知问题并不是线程安全问题. + * `useUsageStats` 使用该特性后, 将会以"使用情况统计"服务的结果来检测当前正在运行的应用包名(需要授予"查看使用情况统计"权限). 如果觉得currentPackage()返回的结果不太准确, 可以尝试该特性. + * `useShell` 使用该特性后, 将使用shell命令获取当前正在运行的应用的包名、活动名称, 但是需要root权限. + +启用有关automator的一些特性. 例如: + +``` +auto.setFlags(["findOnUiThread", "useShell"]); +``` + +## auto.service + +**[v4.1.0新增]** + +* [AccessibilityService](https://developer.android.com/reference/android/accessibilityservice/AccessibilityService/) + +获取无障碍服务. 如果无障碍服务没有启动, 则返回`null`. + +参见[AccessibilityService](https://developer.android.com/reference/android/accessibilityservice/AccessibilityService/). + +## auto.windows + +**[v4.1.0新增]** + +* {Array} + +当前所有窗口([AccessibilityWindowInfo](https://developer.android.com/reference/android/view/accessibility/AccessibilityWindowInfo/))的数组, 可能包括状态栏、输入法、当前应用窗口, 弹出窗口、悬浮窗、分屏应用窗口等. 可以分别获取每个窗口的布局信息. + +该函数需要Android 5.0以上才能运行. + +## auto.root + +**[v4.1.0新增]** + +* {UiObject} + +当前窗口的布局根元素. 如果无障碍服务未启动或者WindowFilter均返回false, 则会返回`null`. + +如果不设置windowFilter, 则当前窗口即为活跃的窗口(获取到焦点、正在触摸的窗口);如果设置了windowFilter, 则获取的是过滤的窗口中的第一个窗口. + +如果系统是Android5.0以下, 则始终返回当前活跃的窗口的布局根元素. + +## auto.rootInActiveWindow + +**[v4.1.0新增]** + +* {UiObject} + +当前活跃的窗口(获取到焦点、正在触摸的窗口)的布局根元素. 如果无障碍服务未启动则为`null`. + +## auto.setWindowFilter(filter) + +**[v4.1.0新增]** + +* `filter` {Function} 参数为窗口([AccessibilityWindowInfo](https://developer.android.com/reference/android/view/accessibility/AccessibilityWindowInfo/)), 返回值为Boolean的函数. + +设置窗口过滤器. 这个过滤器可以决定哪些窗口是目标窗口, 并影响选择器的搜索. 例如, 如果想要选择器在所有窗口(包括状态栏、输入法等)中搜索, 只需要使用以下代码: + +``` +auto.setWindowFilter(function(window){ + //不管是如何窗口, 都返回true, 表示在该窗口中搜索 + return true; +}); +``` + +又例如, 当前使用了分屏功能, 屏幕上有Auto.js和QQ两个应用, 但我们只想选择器对QQ界面进行搜索, 则: + +``` +auto.setWindowFilter(function(window){ + // 对于应用窗口, 他的title属性就是应用的名称, 因此可以通过title属性来判断一个应用 + return window.title == "QQ"; +}); +``` + +选择器默认是在当前活跃的窗口中搜索, 不会搜索诸如悬浮窗、状态栏之类的, 使用WindowFilter则可以控制搜索的窗口. + +需要注意的是, 如果WindowFilter返回的结果均为false, 则选择器的搜索结果将为空. + +另外setWindowFilter函数也会影响`auto.windowRoots`的结果. + +该函数需要Android 5.0以上才有效. + +## auto.windowRoots + +**[v4.1.0新增]** + +* {Array} + +返回当前被WindowFilter过滤的窗口的布局根元素组成的数组. + +如果系统是Android5.0以下, 则始终返回当前活跃的窗口的布局根元素的数组. + +# SimpleActionAutomator + +SimpleActionAutomator提供了一些模拟简单操作的函数, 例如点击文字、模拟按键等. 这些函数可以直接作为全局函数使用. + +## click(text[, i]) + +* `text` {string} 要点击的文本 +* `i` {number} 如果相同的文本在屏幕中出现多次, 则i表示要点击第几个文本, i从0开始计算 + +返回是否点击成功. 当屏幕中并未包含该文本, 或者该文本所在区域不能点击时返回false, 否则返回true. + +该函数可以点击大部分包含文字的按钮. 例如微信主界面下方的"微信", "联系人", "发现", "我"的按钮. +通常与while同时使用以便点击按钮直至成功. 例如: + +``` +while(!click("扫一扫")); +``` + +当不指定参数i时则会尝试点击屏幕上出现的所有文字text并返回是否全部点击成功. + +i是从0开始计算的, 也就是, `click("啦啦啦", 0)`表示点击屏幕上第一个"啦啦啦", `click("啦啦啦", 1)`表示点击屏幕上第二个"啦啦啦". + +> 文本所在区域指的是, 从文本处向其父视图寻找, 直至发现一个可点击的部件为止. + +## click(left, top, bottom, right) + +* `left` {number} 要点击的长方形区域左边与屏幕左边的像素距离 +* `top` {number} 要点击的长方形区域上边与屏幕上边的像素距离 +* `bottom` {number} 要点击的长方形区域下边与屏幕下边的像素距离 +* `right` {number} 要点击的长方形区域右边与屏幕右边的像素距离 + +**注意, 该函数一般只用于录制的脚本中使用, 在自己写的代码中使用该函数一般不要使用该函数. ** + +点击在指定区域的控件. 当屏幕中并未包含与该区域严格匹配的区域, 或者该区域不能点击时返回false, 否则返回true. + +有些按钮或者部件是图标而不是文字(例如发送朋友圈的照相机图标以及QQ下方的消息、联系人、动态图标), 这时不能通过`click(text, i)`来点击, 可以通过描述图标所在的区域来点击. left, bottom, top, right描述的就是点击的区域. + +至于要定位点击的区域, 可以在悬浮窗使用布局分析工具查看控件的bounds属性. + +通过无障碍服务录制脚本会生成该语句. + +## longClick(text[, i])) + +* `text` {string} 要长按的文本 +* `i` {number} 如果相同的文本在屏幕中出现多次, 则i表示要长按第几个文本, i从0开始计算 + +返回是否点击成功. 当屏幕中并未包含该文本, 或者该文本所在区域不能点击时返回false, 否则返回true. + +当不指定参数i时则会尝试点击屏幕上出现的所有文字text并返回是否全部长按成功. + +## scrollUp([i]) + +* `i` {number} 要滑动的控件序号 + +找到第i+1个可滑动控件上滑或**左滑**. 返回是否操作成功. 屏幕上没有可滑动的控件时返回false. + +另外不加参数时`scrollUp()`会寻找面积最大的可滑动的控件上滑或左滑, 例如微信消息列表等. + +参数为一个整数i时会找到第i + 1个可滑动控件滑动. 例如`scrollUp(0)`为滑动第一个可滑动控件. + +## scrollDown([i]) + +* `i` {number} 要滑动的控件序号 + +找到第i+1个可滑动控件下滑或**右滑**. 返回是否操作成功. 屏幕上没有可滑动的控件时返回false. + +另外不加参数时`scrollUp()`会寻找面积最大的可滑动的控件下滑或右滑. + +参数为一个整数i时会找到第i + 1个可滑动控件滑动. 例如`scrollUp(0)`为滑动第一个可滑动控件. + +## setText([i, ]text) + +* i {number} 表示要输入的为第i + 1个输入框 +* text {string} 要输入的文本 + +返回是否输入成功. 当找不到对应的文本框时返回false. + +不加参数i则会把所有输入框的文本都置为text. 例如`setText("测试")`. + +这里的输入文本的意思是, 把输入框的文本置为text, 而不是在原来的文本上追加. + +## input([i, ]text) + +* i {number} 表示要输入的为第i + 1个输入框 +* text {string} 要输入的文本 + +返回是否输入成功. 当找不到对应的文本框时返回false. + +不加参数i则会把所有输入框的文本追加内容text. 例如`input("测试")`. \ No newline at end of file diff --git a/api/base64.md b/api/base64.md new file mode 100644 index 0000000..ab9fd6f --- /dev/null +++ b/api/base64.md @@ -0,0 +1,10 @@ +# Base64 + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + + diff --git a/api/canvas.md b/api/canvas.md index 443a54b..601f4b8 100644 --- a/api/canvas.md +++ b/api/canvas.md @@ -1,18 +1,25 @@ -# Canvas +# 画布 (Canvas) -canvas提供了使用画布进行2D画图的支持,可用于简单的小游戏开发或者图片编辑。使用canvas可以轻松地在一张图片或一个界面上绘制各种线与图形。 +--- -canvas的坐标系为平面直角坐标系,以控件左上角为原点,控件上边沿为x轴正方向,控件左边沿为y轴正方向。例如分辨率为1920*1080的屏幕上,canvas控件覆盖全屏,画一条从屏幕左上角到屏幕右下角的线段为: +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +canvas提供了使用画布进行2D画图的支持, 可用于简单的小游戏开发或者图片编辑. 使用canvas可以轻松地在一张图片或一个界面上绘制各种线与图形. + +canvas的坐标系为平面直角坐标系, 以控件左上角为原点, 控件上边沿为x轴正方向, 控件左边沿为y轴正方向. 例如分辨率为1920*1080的屏幕上, canvas控件覆盖全屏, 画一条从屏幕左上角到屏幕右下角的线段为: ``` canvas.drawLine(0, 0, 1080, 1920, paint); ``` -canvas的绘制依赖于画笔Paint, 通过设置画笔的粗细、颜色、填充等可以改变绘制出来的图形。例如绘制一个红色实心正方形为: +canvas的绘制依赖于画笔Paint, 通过设置画笔的粗细、颜色、填充等可以改变绘制出来的图形. 例如绘制一个红色实心正方形为: ``` var paint = new Paint(); -//设置画笔为填充,则绘制出来的图形都是实心的 +//设置画笔为填充, 则绘制出来的图形都是实心的 paint.setStyle(Paint.STYLE.FILL); //设置画笔颜色为红色 paint.setColor(colors.RED); @@ -20,11 +27,11 @@ paint.setColor(colors.RED); canvas.drawRect(0, 0, 100, 100, paint); ``` -如果要绘制正方形的边框,则通过设置画笔的Style来实现: +如果要绘制正方形的边框, 则通过设置画笔的Style来实现: ``` var paint = new Paint(); -//设置画笔为描边,则绘制出来的图形都是轮廓 +//设置画笔为描边, 则绘制出来的图形都是轮廓 paint.setStyle(Paint.STYLE.STROKE); //设置画笔颜色为红色 paint.setColor(colors.RED); @@ -32,19 +39,19 @@ paint.setColor(colors.RED); canvas.drawRect(0, 0, 100, 100, paint); ``` -结合画笔,canvas可以绘制基本图形、图片等。 +结合画笔, canvas可以绘制基本图形、图片等. ## canvas.getWidth() * 返回 {number} -返回画布当前图层的宽度。 +返回画布当前图层的宽度. ## canvas.getHeight() * 返回 {number} -返回画布当前图层的高度。 +返回画布当前图层的高度. ## canvas.drawRGB(r, int g, int b) @@ -52,7 +59,7 @@ canvas.drawRect(0, 0, 100, 100, paint); * `g` {number} 绿色通道值 * `b` {number} 蓝色通道值 -将整个可绘制区域填充为r、g、b指定的颜色。相当于 `canvas.drawColor(colors.rgb(r, g, b))`。 +将整个可绘制区域填充为r、g、b指定的颜色. 相当于 `canvas.drawColor(colors.rgb(r, g, b))`. ## canvas.drawARGB(a, r, g, b) @@ -61,27 +68,27 @@ canvas.drawRect(0, 0, 100, 100, paint); * `g` {number} 绿色通道值 * `b` {number} 蓝色通道值 -将整个可绘制区域填充为a、r、g、b指定的颜色。相当于 `canvas.drawColor(colors.argb(a, r, g, b))`。 +将整个可绘制区域填充为a、r、g、b指定的颜色. 相当于 `canvas.drawColor(colors.argb(a, r, g, b))`. ## canvas.drawColor(color) * `color` {number} 颜色值 -将整个可绘制区域填充为color指定的颜色。 +将整个可绘制区域填充为color指定的颜色. ## canvas.drawColor(color, mode) * `color` {number} 颜色值 * `mode` {PorterDuff.Mode} Porter-Duff操作 -将整个可绘制区域填充为color指定的颜色。 +将整个可绘制区域填充为color指定的颜色. ## canvas.drawPaint(paint) * `paint` {Paint} 画笔 -将整个可绘制区域用paint指定的画笔填充。相当于绘制一个无限大的矩形,但是更快。 -通过该方法可以绘制一个指定的着色器的图案。 +将整个可绘制区域用paint指定的画笔填充. 相当于绘制一个无限大的矩形, 但是更快. +通过该方法可以绘制一个指定的着色器的图案. ## canvas.drawPoint(x, y, paint) @@ -89,19 +96,19 @@ canvas.drawRect(0, 0, 100, 100, paint); * `y` {number} y坐标 * `paint` {Paint} 画笔 -在可绘制区域绘制由坐标(x, y)指定的点。 -点的形状由画笔的线帽决定(参见paint.setStrokeCap(cap))。 -点的大小由画笔的宽度决定(参见paint.setStrokeWidth(width))。 -> 如果画笔宽度为0,则也会绘制1个像素至4个(若抗锯齿启用)。 +在可绘制区域绘制由坐标(x, y)指定的点. +点的形状由画笔的线帽决定(参见paint.setStrokeCap(cap)). +点的大小由画笔的宽度决定(参见paint.setStrokeWidth(width)). +> 如果画笔宽度为0, 则也会绘制1个像素至4个(若抗锯齿启用). -相当于 `canvas.drawPoints([x, y], paint)`。 +相当于 `canvas.drawPoints([x, y], paint)`. ## canvas.drawPoints(pts, paint) * `pts` {Array} 点坐标数组 [x0, y0, x1, y1, x2, y2, ...] * `paint` {Paint} 画笔 -在可绘制区域绘制由坐标数组指定的多个点。 +在可绘制区域绘制由坐标数组指定的多个点. ## canvas.drawLine(startX, startY, stopX, stopY, paint) @@ -111,17 +118,17 @@ canvas.drawRect(0, 0, 100, 100, paint); * `endY` {number} 终点y坐标 * `paint` {Paint} 画笔 -在可绘制区域绘制由起点坐标(startX, startY)和终点坐标(endX, endY)指定的线。 -绘制时会忽略画笔的样式(Style)。也就是说,即使样式设为“仅填充(FILL)”也会绘制。 -退化为点的线(长度为0)不会被绘制。 +在可绘制区域绘制由起点坐标(startX, startY)和终点坐标(endX, endY)指定的线. +绘制时会忽略画笔的样式(Style). 也就是说, 即使样式设为“仅填充(FILL)”也会绘制. +退化为点的线(长度为0)不会被绘制. ## canvas.drawRect(r, paint) * `r` {Rect} 矩形边界 * `paint` {Paint} 画笔 -在可绘制区域绘制由矩形边界r指定的矩形。 -绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形。 +在可绘制区域绘制由矩形边界r指定的矩形. +绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形. ## canvas.drawRect(left, top, right, bottom, android.graphics.Paint paint) @@ -131,8 +138,8 @@ canvas.drawRect(0, 0, 100, 100, paint); * `bottom` {number} 矩形下边界y坐标 * `paint` {Paint} 画笔 -在可绘制区域绘制由矩形边界(left, top, right, bottom)指定的矩形。 -绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形。 +在可绘制区域绘制由矩形边界(left, top, right, bottom)指定的矩形. +绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形. ## canvas.drawOval(android.graphics.RectF oval, android.graphics.Paint paint) @@ -158,17 +165,17 @@ canvas.drawRect(0, 0, 100, 100, paint); ## canvas.translate(dx, dy) -* `dx` {number} 向x轴正方向平移的距离,负数表示反方向平移 -* `dy` {number} 向y轴正方向平移的距离,负数表示反方向平移 +* `dx` {number} 向x轴正方向平移的距离, 负数表示反方向平移 +* `dy` {number} 向y轴正方向平移的距离, 负数表示反方向平移 -平移指定距离。 +平移指定距离. ## canvas.scale(sx, sy) -* `sx` {number} 向x轴正方向平移的距离,负数表示反方向平移 -* `sy` {number} 向y轴正方向平移的距离,负数表示反方向平移 +* `sx` {number} 向x轴正方向平移的距离, 负数表示反方向平移 +* `sy` {number} 向y轴正方向平移的距离, 负数表示反方向平移 -以原点为中心,将坐标系平移缩放指定倍数。 +以原点为中心, 将坐标系平移缩放指定倍数. ## canvas.scale(float sx, float sy, float px, float py) diff --git a/api/changelog.md b/api/changelog.md new file mode 100644 index 0000000..562ba7e --- /dev/null +++ b/api/changelog.md @@ -0,0 +1,13 @@ +# 更新日志 (Changelog) + +## 2022/12/01 + +- `新增` 夜间模式主题适配 +- `新增` [E4X](e4x) / [术语](glossaries) / [异常](exceptions) / [数据类型](dataTypes) / [选择器](uiSelectorType) / [控件节点](uiObjectType) / [控件集合](uiObjectCollectionType) 等章节 +- `修复` 章节标题可能显示不全的问题 +- `修复` 代码区域滑动时导致页面滑动的问题 +- `修复` App 文档无法跳转到其他章节的问题 +- `优化` 重新部署文档结构并统一样式 (暂未全部完成) +- `优化` 完善 [脚本化 Java](scriptingJava) 章节 +- `优化` 支持 Java 等语言的语法高亮 (有限支持) +- `优化` 去除章节标题的锚点标记 diff --git a/api/color.md b/api/color.md new file mode 100644 index 0000000..87e73fa --- /dev/null +++ b/api/color.md @@ -0,0 +1,152 @@ +# 颜色 (Colors) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +在Auto.js有两种方式表示一个颜色. + +一种是使用一个字符串"#AARRGGBB"或"#RRGGBB", 其中 AA 是Alpha通道(透明度)的值, RR 是R通道(红色)的值, GG 是G通道(绿色)的值, BB是B通道(蓝色)的值. 例如"#ffffff"表示白色, "#7F000000"表示半透明的黑色. + +另一种是使用一个16进制的"32位整数" 0xAARRGGBB 来表示一个颜色, 例如 `0xFF112233`表示颜色"#112233", `0x11223344`表示颜色"#11223344". + +可以通过`colors.toString()`把颜色整数转换为字符串, 通过`colors.parseColor()`把颜色字符串解析为颜色整数. + +## colors.toString(color) + +* `color` {number} 整数RGB颜色值 +* 返回 {string} + +返回颜色值的字符串, 格式为 "#AARRGGBB". + +## colors.red(color) + +* `color` {number} | {string} 颜色值 +* 返回 {number} + +返回颜色color的R通道的值, 范围0~255. + +## colors.green(color) + +* `color` {number} | {string} 颜色值 +* 返回 {number} + +返回颜色color的G通道的值, 范围0~255. + +## colors.blue(color) + +* `color` {number} | {string} 颜色值 +* 返回 {number} + +返回颜色color的B通道的值, 范围0~255. + +## colors.alpha(color) + +* `color` {number} | {string} 颜色值 +* 返回 {number} + +返回颜色color的Alpha通道的值, 范围0~255. + +## colors.rgb(red, green, blue) + +* red {number} 颜色的R通道的值 +* blue {number} 颜色的G通道的值 +* green {number} 颜色的B通道的值 +* 返回 {number} + +返回这些颜色通道构成的整数颜色值. Alpha通道将是255(不透明). + +## colors.argb(alpha, red, green, blue) + +* `alpha` {number} 颜色的Alpha通道的值 +* `red` {number} 颜色的R通道的值 +* `green` {number} 颜色的G通道的值 +* `blue` {number} 颜色的B通道的值 +* 返回 {number} + +返回这些颜色通道构成的整数颜色值. + +## colors.parseColor(colorStr) + +* `colorStr` {string} 表示颜色的字符串, 例如"#112233" +* 返回 {number} + +返回颜色的整数值. + +## colors.isSimilar(color2, color2[, threshold, algorithm]) + +* `color1` {number} | {string} 颜色值1 +* `color1` {number} | {string} 颜色值2 +* `threshold` {number} 颜色相似度临界值, 默认为4. 取值范围为0~255. 这个值越大表示允许的相似程度越小, 如果这个值为0, 则两个颜色相等时该函数才会返回true. +* `algorithm` {string} 颜色匹配算法, 默认为"diff", 包括: + * "diff": 差值匹配. 与给定颜色的R、G、B差的绝对值之和小于threshold时匹配. + * "rgb": rgb欧拉距离相似度. 与给定颜色color的rgb欧拉距离小于等于threshold时匹配. + * "rgb+": 加权rgb欧拉距离匹配([LAB Delta E](https://en.wikipedia.org/wiki/Color_difference/)). + * "hs": hs欧拉距离匹配. hs为HSV空间的色调值. +* 返回 {Boolean} + +返回两个颜色是否相似. + +## colors.equals(color1, color2) + +* `color1` {number} | {string} 颜色值1 +* `color1` {number} | {string} 颜色值2 +* 返回 {Boolean} + +返回两个颜色是否相等. **注意该函数会忽略Alpha通道的值进行比较*. + +``` +log(colors.equals("#112233", "#112234")); +log(colors.equals(0xFF112233, 0xFF223344)); +``` + +# colors.BLACK + +黑色, 颜色值 #FF000000 + +# colors.DKGRAY + +深灰色, 颜色值 #FF444444 + +# colors.GRAY + +灰色, 颜色值 #FF888888 + +# colors.LTGRAY + +亮灰色, 颜色值 #FFCCCCCC + +# colors.WHITE + +白色, 颜色值 #FFFFFFFF + +# colors.RED + +红色, 颜色值 #FFFF0000 + +# colors.GREEN + +绿色, 颜色值 #FF00FF00 + +# colors.BLUE + +蓝色, 颜色值 #FF0000FF + +# colors.YELLOW + +黄色, 颜色值 #FFFFFF00 + +# colors.CYAN + +青色, 颜色值 #FF00FFFF + +# colors.MAGENTA + +品红色, 颜色值 #FFFF00FF + +# colors.TRANSPARENT + +透明, 颜色值 #00000000 \ No newline at end of file diff --git a/api/console.md b/api/console.md index 7573b03..f88547c 100644 --- a/api/console.md +++ b/api/console.md @@ -1,28 +1,33 @@ -# Console +# 控制台 (Console) -> Stability: 2 - Stable +--- -控制台模块提供了一个和Web浏览器中相似的用于调试的控制台。用于输出一些调试信息、中间结果等。 -console模块中的一些函数也可以直接作为全局函数使用,例如log, print等。 +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +控制台模块提供了一个和Web浏览器中相似的用于调试的控制台. 用于输出一些调试信息、中间结果等. +console模块中的一些函数也可以直接作为全局函数使用, 例如log, print等. ## console.show() -显示控制台。这会显示一个控制台的悬浮窗(需要悬浮窗权限)。 +显示控制台. 这会显示一个控制台的悬浮窗(需要悬浮窗权限). ## console.hide() -隐藏控制台悬浮窗。 +隐藏控制台悬浮窗. ## console.clear() -清空控制台。 +清空控制台. ## console.log([data][, ...args])# * `data` {any} * `...args` {any} -打印到控制台,并带上换行符。 可以传入多个参数,第一个参数作为主要信息,其他参数作为类似于 [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html) 中的代替值(参数都会传给 util.format())。 +打印到控制台, 并带上换行符. 可以传入多个参数, 第一个参数作为主要信息, 其他参数作为类似于 [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html/) 中的代替值(参数都会传给 util.format(/)). ``` const count = 5; @@ -32,44 +37,44 @@ console.log('count:', count); // 打印: count: 5 到 stdout ``` -详见 util.format()。 +详见 util.format(). -该函数也可以作为全局函数使用。 +该函数也可以作为全局函数使用. ## console.verbose([data][, ...args]) * `data` {any} * `...args` {any} -与console.log类似,但输出结果以灰色字体显示。输出优先级低于log,用于输出观察性质的信息。 +与console.log类似, 但输出结果以灰色字体显示. 输出优先级低于log, 用于输出观察性质的信息. ## console.info([data][, ...args]) * `data` {any} * `...args` {any} -与console.log类似,但输出结果以绿色字体显示。输出优先级高于log, 用于输出重要信息。 +与console.log类似, 但输出结果以绿色字体显示. 输出优先级高于log, 用于输出重要信息. ## console.warn([data][, ...args]) * `data` {any} * `...args` {any} -与console.log类似,但输出结果以蓝色字体显示。输出优先级高于info, 用于输出警告信息。 +与console.log类似, 但输出结果以蓝色字体显示. 输出优先级高于info, 用于输出警告信息. ## console.error([data][, ...args]) * `data` {any} * `...args` {any} -与console.log类似,但输出结果以红色字体显示。输出优先级高于warn, 用于输出错误信息。 +与console.log类似, 但输出结果以红色字体显示. 输出优先级高于warn, 用于输出错误信息. ## console.assert(value, message) * value {any} 要断言的布尔值 * message {string} value为false时要输出的信息 -断言。如果value为false则输出错误信息message并停止脚本运行。 +断言. 如果value为false则输出错误信息message并停止脚本运行. ``` var a = 1 + 1; @@ -80,12 +85,12 @@ console.assert(a == 2, "加法出错啦"); **[v4.1.0新增]** -* `label` {String} 计时器标签,可省略 +* `label` {String} 计时器标签, 可省略 -启动一个定时器,用以计算一个操作的持续时间。 -定时器由一个唯一的 `label` 标识。 -当调用 `console.timeEnd()` 时,可以使用相同的 `label` 来停止定时器,并以毫秒为单位将持续时间输出到控制台。 -重复启动同一个标签的定时器会覆盖之前启动同一标签的定时器。 +启动一个定时器, 用以计算一个操作的持续时间. +定时器由一个唯一的 `label` 标识. +当调用 `console.timeEnd()` 时, 可以使用相同的 `label` 来停止定时器, 并以毫秒为单位将持续时间输出到控制台. +重复启动同一个标签的定时器会覆盖之前启动同一标签的定时器. ## console.timeEnd(label) @@ -93,8 +98,8 @@ console.assert(a == 2, "加法出错啦"); * `label` {String} 计时器标签 -停止之前通过调用 `console.time()` 启动的定时器,并打印结果到控制台。 -调用 `console.timeEnd()` 后定时器会被删除。如果不存在标签指定的定时器则会打印 `NaNms`。 +停止之前通过调用 `console.time()` 启动的定时器, 并打印结果到控制台. +调用 `console.timeEnd()` 后定时器会被删除. 如果不存在标签指定的定时器则会打印 `NaNms`. ```js console.time('求和'); @@ -113,7 +118,7 @@ console.timeEnd('求和'); * `data` {any} * `...args` {any} -与console.log类似,同时会打印出调用这个函数所在的调用栈信息(即当前运行的文件、行数等信息)。 +与console.log类似, 同时会打印出调用这个函数所在的调用栈信息(即当前运行的文件、行数等信息). ```js console.trace('Show me'); @@ -127,9 +132,9 @@ console.trace('Show me'); * `data` {any} * `...args` {any} -与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串用eval计算后返回。 +与console.log一样输出信息, 并在控制台显示输入框等待输入. 按控制台的确认按钮后会将输入的字符串用eval计算后返回. -**部分机型可能会有控制台不显示输入框的情况,属于bug。** +**部分机型可能会有控制台不显示输入框的情况, 属于bug. ** 例如: @@ -145,9 +150,9 @@ toast(n + 1); * `data` {any} * `...args` {any} -与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串直接返回。 +与console.log一样输出信息, 并在控制台显示输入框等待输入. 按控制台的确认按钮后会将输入的字符串直接返回. -部分机型可能会有控制台不显示输入框的情况,属于bug。 +部分机型可能会有控制台不显示输入框的情况, 属于bug. 例如: @@ -163,7 +168,7 @@ toast(n + 1); * `w` {number} 宽度 * `h` {number} 高度 -设置控制台的大小,单位像素。 +设置控制台的大小, 单位像素. ``` console.show(); @@ -176,7 +181,7 @@ console.setSize(device.width / 2, device.height / 2); * `x` {number} 横坐标 * `y` {number} 纵坐标 -设置控制台的位置,单位像素。 +设置控制台的位置, 单位像素. ``` console.show(); @@ -187,14 +192,14 @@ console.setPosition(100, 100); **[v4.1.0新增]** -* `config` {Object} 日志配置,可选的项有: - * `file` {string} 日志文件路径,将会把日志写入该文件中 - * `maxFileSize` {number} 最大文件大小,单位字节,默认为512 * 1024 (512KB) - * `rootLevel` {string} 写入的日志级别,默认为"ALL"(所有日志),可以为"OFF"(关闭), "DEBUG", "INFO", "WARN", "ERROR", "FATAL"等。 - * `maxBackupSize` {number} 日志备份文件最大数量,默认为5 - * `filePattern` {string} 日志写入格式,参见[PatternLayout](http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html) +* `config` {Object} 日志配置, 可选的项有: + * `file` {string} 日志文件路径, 将会把日志写入该文件中 + * `maxFileSize` {number} 最大文件大小, 单位字节, 默认为512 * 1024 (512KB) + * `rootLevel` {string} 写入的日志级别, 默认为"ALL"(所有日志), 可以为"OFF"(关闭), "DEBUG", "INFO", "WARN", "ERROR", "FATAL"等. + * `maxBackupSize` {number} 日志备份文件最大数量, 默认为5 + * `filePattern` {string} 日志写入格式, 参见[PatternLayout](http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html/) -设置日志保存的路径和配置。例如把日志保存到"/sdcard/1.txt": +设置日志保存的路径和配置. 例如把日志保存到"/sdcard/1.txt": ``` console.setGlobalLogConfig({ @@ -202,12 +207,12 @@ console.setGlobalLogConfig({ }); ``` -注意该函数会影响所有脚本的日志记录。 +注意该函数会影响所有脚本的日志记录. ## print(text) * text {string} | {Object} 要打印到控制台的信息 -相当于`log(text)`。 +相当于`log(text)`. diff --git a/api/context.md b/api/context.md new file mode 100644 index 0000000..9e115d7 --- /dev/null +++ b/api/context.md @@ -0,0 +1,14 @@ +# 上下文 (Context) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +一个 android.content.Context 子类实例. + +该对象为 ApplicationContext, 因此不能用于创建 [ 界面 / 对话框 ] 等. + +[android.content.Context#getApplicationContext](https://developer.android.com/reference/android/content/Context#getApplicationContext()) \ No newline at end of file diff --git a/api/continuation.md b/api/continuation.md new file mode 100644 index 0000000..0dfec89 --- /dev/null +++ b/api/continuation.md @@ -0,0 +1,10 @@ +# 协程 (Continuation) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + + diff --git a/api/coordinatesBasedAutomation.md b/api/coordinatesBasedAutomation.md deleted file mode 100644 index a09c9ef..0000000 --- a/api/coordinatesBasedAutomation.md +++ /dev/null @@ -1,260 +0,0 @@ -# 基于坐标的触摸模拟 - -> Stability: 2 - Stable - -本章节介绍了一些使用坐标进行点击、滑动的函数。这些函数有的需要安卓7.0以上,有的需要root权限。 - -要获取要点击的位置的坐标,可以在开发者选项中开启"指针位置"。 - -基于坐标的脚本通常会有分辨率的问题,这时可以通过`setScreenMetrics()`函数来进行自动坐标放缩。这个函数会影响本章节的所有点击、长按、滑动等函数。通过设定脚本设计时的分辨率,使得脚本在其他分辨率下自动放缩坐标。 - -控件和坐标也可以相互结合。一些控件是无法点击的(clickable为false), 无法通过`.click()`函数来点击,这时如果安卓版本在7.0以上或者有root权限,就可以通过以下方式来点击: - -``` -//获取这个控件 -var widget = id("xxx").findOne(); -//获取其中心位置并点击 -click(widget.bounds().centerX(), widget.bounds().centerY()); -//如果用root权限则用Tap -``` - -## setScreenMetrics(width, height) - -* width {number} 屏幕宽度,单位像素 -* height {number} 屏幕高度,单位像素 - -设置脚本坐标点击所适合的屏幕宽高。如果脚本运行时,屏幕宽度不一致会自动放缩坐标。 - -例如在1920*1080的设备中,某个操作的代码为 - -``` -setScreenMetrics(1080, 1920); -click(800, 200); -longClick(300, 500); -``` - -那么在其他设备上AutoJs会自动放缩坐标以便脚本仍然有效。例如在540 * 960的屏幕中`click(800, 200)`实际上会点击位置(400, 100)。 - -# 安卓7.0以上的触摸和手势模拟 - -> Stability: 2 - Stable - -**注意以下命令只有Android7.0及以上才有效** - -## click(x, y) - -* `x` {number} 要点击的坐标的x值 -* `y` {number} 要点击的坐标的y值 - -模拟点击坐标(x, y),并返回是否点击成功。只有在点击执行完成后脚本才继续执行。 - -一般而言,只有点击过程(大约150毫秒)中被其他事件中断(例如用户自行点击)才会点击失败。 - -使用该函数模拟连续点击时可能有点击速度过慢的问题,这时可以用`press()`函数代替。 - -## longClick(x, y) - -* `x` {number} 要长按的坐标的x值 -* `y` {number} 要长按的坐标的y值 - -模拟长按坐标(x, y), 并返回是否成功。只有在长按执行完成(大约600毫秒)时脚本才会继续执行。 - -一般而言,只有长按过程中被其他事件中断(例如用户自行点击)才会长按失败。 - -## press(x, y, duration) - -* `x` {number} 要按住的坐标的x值 -* `y` {number} 要按住的坐标的y值 -* `duration` {number} 按住时长,单位毫秒 - -模拟按住坐标(x, y), 并返回是否成功。只有按住操作执行完成时脚本才会继续执行。 - -如果按住时间过短,那么会被系统认为是点击;如果时长超过500毫秒,则认为是长按。 - -一般而言,只有按住过程中被其他事件中断才会操作失败。 - -一个连点器的例子如下: - -``` -//循环100次 -for(var i = 0; i < 100; i++){ - //点击位置(500, 1000), 每次用时1毫秒 - press(500, 1000, 1); -} -``` - -## swipe(x1, y1, x2, y2, duration) - -* `x1` {number} 滑动的起始坐标的x值 -* `y1` {number} 滑动的起始坐标的y值 -* `x2` {number} 滑动的结束坐标的x值 -* `y2` {number} 滑动的结束坐标的y值 -* `duration` {number} 滑动时长,单位毫秒 - -模拟从坐标(x1, y1)滑动到坐标(x2, y2),并返回是否成功。只有滑动操作执行完成时脚本才会继续执行。 - -一般而言,只有滑动过程中被其他事件中断才会滑动失败。 - -## gesture(duration, [x1, y1], [x2, y2], ...) - -* `duration` {number} 手势的时长 -* [x, y] ... 手势滑动路径的一系列坐标 - -模拟手势操作。例如`gesture(1000, [0, 0], [500, 500], [500, 1000])`为模拟一个从(0, 0)到(500, 500)到(500, 100)的手势操作,时长为2秒。 - -## gestures([delay1, duration1, [x1, y1], [x2, y2], ...], [delay2, duration2, [x3, y3], [x4, y4], ...], ...) - -同时模拟多个手势。每个手势的参数为\[delay, duration, 坐标\], delay为延迟多久(毫秒)才执行该手势;duration为手势执行时长;坐标为手势经过的点的坐标。其中delay参数可以省略,默认为0。 - -例如手指捏合: - -``` -gestures([0, 500, [800, 300], [500, 1000]], - [0, 500, [300, 1500], [500, 1000]]); -``` - -# RootAutomator - -> Stability: 2 - Stable - -RootAutomator是一个使用root权限来模拟触摸的对象,用它可以完成触摸与多点触摸,并且这些动作的执行没有延迟。 - -一个脚本中最好只存在一个RootAutomator,并且保证脚本结束退出他。可以在exit事件中退出RootAutomator,例如: - -``` -var ra = new RootAutomator(); -events.on('exit', function(){ - ra.exit(); -}); -//执行一些点击操作 -... - -``` - -**注意以下命令需要root权限** - -## RootAutomator.tap(x, y[, id]) - -* `x` {number} 横坐标 -* `y` {number} 纵坐标 -* `id` {number} 多点触摸id,可选,默认为1,可以通过setDefaultId指定。 - -点击位置(x, y)。其中id是一个整数值,用于区分多点触摸,不同的id表示不同的"手指",例如: - -``` -var ra = new RootAutomator(); -//让"手指1"点击位置(100, 100) -ra.tap(100, 100, 1); -//让"手指2"点击位置(200, 200); -ra.tap(200, 200, 2); -ra.exit(); -``` - -如果不需要多点触摸,则不需要id这个参数。 -多点触摸通常用于手势或游戏操作,例如模拟双指捏合、双指上滑等。 - -某些情况下可能存在tap点击无反应的情况,这时可以用`RootAutomator.press()`函数代替。 - -## RootAutomator.swipe(x1, x2, y1, y2[, duration, id]) - -* `x1` {number} 滑动起点横坐标 -* `y1` {number} 滑动起点纵坐标 -* `x2` {number} 滑动终点横坐标 -* `y2` {number} 滑动终点纵坐标 -* `duration` {number} 滑动时长,单位毫秒,默认值为300 -* `id` {number} 多点触摸id,可选,默认为1 - -模拟一次从(x1, y1)到(x2, y2)的时间为duration毫秒的滑动。 - -## RootAutomator.press(x, y, duration[, id]) - -* `x` {number} 横坐标 -* `y` {number} 纵坐标 -* `duration` {number} 按下时长 -* `id` {number} 多点触摸id,可选,默认为1 - -模拟按下位置(x, y),时长为duration毫秒。 - -## RootAutomator.longPress(x, y[\, id\]) - -* `x` {number} 横坐标 -* `y` {number} 纵坐标 -* `duration` {number} 按下时长 -* `id` {number} 多点触摸id,可选,默认为1 - -模拟长按位置(x, y)。 - -以上为简单模拟触摸操作的函数。如果要模拟一些复杂的手势,需要更底层的函数。 - -## RootAutomator.touchDown(x, y[, id]) - -* `x` {number} 横坐标 -* `y` {number} 纵坐标 -* `id` {number} 多点触摸id,可选,默认为1 - -模拟手指按下位置(x, y)。 - -## RootAutomator.touchMove(x, y[, id]) - -* `x` {number} 横坐标 -* `y` {number} 纵坐标 -* `id` {number} 多点触摸id,可选,默认为1 - -模拟移动手指到位置(x, y)。 - -## RootAutomator.touchUp([id]) - -* `id` {number} 多点触摸id,可选,默认为1 - -模拟手指弹起。 - -# 使用root权限点击和滑动的简单命令 - -> Stability: 1 - Experimental - -注意:本章节的函数在后续版本很可能有改动!请勿过分依赖本章节函数的副作用。推荐使用`RootAutomator`代替本章节的触摸函数。 - -以下函数均需要root权限,可以实现任意位置的点击、滑动等。 - -* 这些函数通常首字母大写以表示其特殊的权限。 -* 这些函数均不返回任何值。 -* 并且,这些函数的执行是异步的、非阻塞的,在不同机型上所用的时间不同。脚本不会等待动作执行完成才继续执行。因此最好在每个函数之后加上适当的sleep来达到期望的效果。 - -例如: - -``` -Tap(100, 100); -sleep(500); -``` - -注意,动作的执行可能无法被停止,例如: - -``` -for(var i = 0; i < 100; i++){ - Tap(100, 100); -} -``` - -这段代码执行后可能会出现在任务管理中停止脚本后点击仍然继续的情况。 -因此,强烈建议在每个动作后加上延时: - -``` -for(var i = 0; i < 100; i++){ - Tap(100, 100); - sleep(500); -} -``` - -## Tap(x, y) - -* x, y {number} 要点击的坐标。 - -点击位置(x, y), 您可以通过"开发者选项"开启指针位置来确定点击坐标。 - -## Swipe(x1, y1, x2, y2, \[duration\]) - -* x1, y1 {number} 滑动起点的坐标 -* x2, y2 {number} 滑动终点的坐标 -* duration {number} 滑动动作所用的时间 - -滑动。从(x1, y1)位置滑动到(x2, y2)位置。 \ No newline at end of file diff --git a/api/_coverpage.md b/api/coverpage.md similarity index 60% rename from api/_coverpage.md rename to api/coverpage.md index 2c77ae4..340c9d1 100644 --- a/api/_coverpage.md +++ b/api/coverpage.md @@ -1,8 +1,8 @@ ![logo](images/logo.png) -# [AutoJs6](https://github.com/SuperMonster003/AutoJs6) +# [AutoJs6](https://github.com/SuperMonster003/AutoJs6/) 安卓平台 JavaScript 自动化工具 -[下载应用](https://github.com/SuperMonster003/AutoJs6/releases) -[阅读文档](#综述) \ No newline at end of file +[下载应用](https://github.com/SuperMonster003/AutoJs6/releases/) +[阅读文档](/overview) \ No newline at end of file diff --git a/api/crypto.md b/api/crypto.md index 66fd6b1..0f04023 100644 --- a/api/crypto.md +++ b/api/crypto.md @@ -1,13 +1,20 @@ -# Crypto +# 密文 (Crypto) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- ** [[Pro 8.0.0新增](https://pro.autojs.org/)] ** -$crypto模块提供了对称加密(例如AES)、非对称加密(例如RSA)、消息摘要(例如MD5, SHA)等支持。 +$crypto模块提供了对称加密(例如AES)、非对称加密(例如RSA)、消息摘要(例如MD5, SHA)等支持. ## $crypto.digest(message, algorithm[, options]) * `message` {any} -* `algorithm` {string} 消息摘要算法,包括: +* `algorithm` {string} 消息摘要算法, 包括: * MD5 * SHA-1 * SHA-256 @@ -15,9 +22,9 @@ $crypto模块提供了对称加密(例如AES)、非对称加密(例如RSA)、消 * SHA-512 * `options` {any} -对信息message使用消息摘要算法`algorithm`进行摘要并返回结果,默认的输出格式为hex。 +对信息message使用消息摘要算法`algorithm`进行摘要并返回结果, 默认的输出格式为hex. -参数message的类型默认为字符串,返回值默认为hex;可以通过options来指定参数message的类型和返回值的类型、格式,比如文件、base64、字节数组、hex等。参见《输入和输出的类型和格式》。 +参数message的类型默认为字符串, 返回值默认为hex;可以通过options来指定参数message的类型和返回值的类型、格式, 比如文件、base64、字节数组、hex等. 参见《输入和输出的类型和格式》. ```javascript // 计算字符串abc的md5 @@ -34,7 +41,7 @@ toastLog($crypto.digest("/sdcard/1.txt", "MD5", { * `data` {any} 明文消息 * `key` {Key} 密钥 -* `algorithm` {string} 加密算法,包括: +* `algorithm` {string} 加密算法, 包括: * AES * AES/ECB/NoPadding * AES/ECB/PKCS5Padding @@ -49,7 +56,7 @@ toastLog($crypto.digest("/sdcard/1.txt", "MD5", { * RSA/ECB/PKCS1Padding * RSA/ECB/NoPadding * ... - 具体可参阅 [javax.crypto.Cipher](https://developer.android.com/reference/javax/crypto/Cipher) + 具体可参阅 [javax.crypto.Cipher](https://developer.android.com/reference/javax/crypto/Cipher/) * `options` {Object} 加密选项 diff --git a/api/dataTypes.md b/api/dataTypes.md new file mode 100644 index 0000000..4068f15 --- /dev/null +++ b/api/dataTypes.md @@ -0,0 +1,1472 @@ +# 数据类型 (Data Types) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +数据类型是用来约束数据的解释. +本章节的数据类型包括 [ number / void / any / object / 泛型 / 交叉类型 ] 等. + +> 注: 此章节的类型概念 与 JavaScript 数据类型 (如 [基本类型](https://developer.mozilla.org/zh-CN/docs/Glossary/Primitive/)) 以及 TypeScript 数据类型 (如 [基础类型](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)) 在概念上可能存在出入, 因此仅适用于对文档内容的辅助理解, 不适用于严格的概念参考. + +## Boolean + +布尔类型. + +**foo(bar)** + +- **bar** { [boolean](#boolean) } + +```js +foo(true); /* 符合预期. */ +foo(false); /* 符合预期. */ +foo(3); /* 不符合预期. */ +``` + +需留意 JavaScript 的短路特性: + +```js +/* 符合预期, 相当于 foo(false). */ +foo(3 > 4); + +/* 不符合预期, 相当于 foo("hello"). */ +foo(3 > 4 || "hello"); + +/* 符合预期, 相当于 foo(false). */ +foo(3 > 4 && "hello"); + +/* 不符合预期, 相当于 foo("hello"). */ +foo(3 > 2 && "hello"); +``` + +## Number + +数字类型. + +常用以下表示方法: + +- `3` - 整数 +- `+3` - 整数 + - 结果与 3 相同, 通常仅用于强调正负性 + - 这里的 "+" 并非符号, 而是一元运算符 +- `-3` - 负数 +- `3.1` - 小数 + - JS 使用 IEEE 754 双精度版本存储数字 + - 参阅: [0.1 + 0.2 !== 0.3](https://github.com/HXWfromDJTU/blog/issues/20) +- `3.0` - 整数 + - 结果与 3 相同, JS 没有 Double 等类型 +- `.1` - 小数, 省略前导 0, 相当于 0.1 +- `2e3` - 科学计数法, 相当于 2 × 10^3, 即 2000 + - 符号 e 表示 10 的幂, e 前后的数字分别称为有效数和幂次 + - 有效数可以为整数或小数字面量: + - `1e2`, `3.1e2`, `-9e2`, `0e2`, `.1e2` 均合法 + - 幂次只能为整数字面量: + - `1e2`, `1e-2` 均合法 + - e 的前后不能有变量或括号等符号: + - `let num = 3;` + - `nume2`, `(num)e2`, `(3)e(2)`, `3e(num)` 均不合法 +- `0x23` - 十六进制 +- `0b101` - 二进制 +- `0o307` - 八进制 +- `NaN` - 特殊数值 + - 参阅: [NaN](glossaries#nan) +- `Infinity` - 无穷大 +- `-Infinity` - 负无穷大 +- `Number.XXX` - Number 对象上的常量 + - 如 [Number\.EPSILON](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON), [Number.MAX_VALUE](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_VALUE) 等 +- `Math.XXX` - Math 对象上的常量 + - 如 [Math.PI](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/PI), [Math.SQRT2](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/SQRT2), [Math.LN2](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/LN2) 等 + +**foo(bar)** + +- **bar** { [number](#number) } + +```js +foo(3); /* 符合预期. */ +foo(3.3); /* 符合预期. */ +foo(3e3); /* 符合预期. */ +foo(NaN); /* 符合预期. */ +``` + +JavaScript 的所有数字都是浮点数, 因此 number 类型对 Double, Float, Long, Integer, Short 等均不作区分. + +```js +3.0 === 3; // true +typeof new java.lang.Double(5.23).doubleValue(); // "number" +``` + +> 注: 如需表示一个很大的数 (超过 `2^53 - 1`), 需要用 [BigInt](glossaries#bigint) 表示. +> 文档中通常不会出现 `bigint` 类型的数据, 包括 `number | bigint` 这样的 [联合类型](#联合类型) 数据. + +## String + +字符串类型. + +常用以下表示方法: + +- `"hello"` - 成对双引号 (`"`) +- `'hello'` - 成对单引号 (`'`) +- ``hello`` - 成对反引号 (```) + - 参阅: [模板字符串](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Template_literals) +- `转义字符` + - 如 `\n`, `\r`, `\uXXXX`, `\xXX` 等 + - 参阅: [转义字符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String#%E8%BD%AC%E4%B9%89%E5%AD%97%E7%AC%A6) + +**foo(bar)** + +- **bar** { [string](#string) } + +```js +foo("3"); /* 符合预期. */ +foo('3.3'); /* 符合预期. */ +foo(`3e3 equals to ${3000}`); /* 符合预期. */ +foo(NaN.toString()); /* 符合预期. */ +``` + +## Array + +数组类型. + +后缀 "[]" 代表数组类型. +如 `number[]` 代表一个数组, 其中的元素全部为 [number](#number) 类型, 且元素数量不限 (包括 0, 即空数组). + +> 注: `number[]` 与 `[number]` 不同, 后者表示 [元组类型](#tuple). + +> 注: 使用 `Array` 这样的 [泛型](#generic) 表示法也可代表数组类型, 但文档通常只采用后缀表示法. + +**foo(bar)** + +- **bar** { [string[]](#array) } + +```js +foo([ "3" ]); /* 符合预期. */ +foo([ 3 ]); /* 不符合预期. */ +foo([ "3", 3 ]); /* 不符合预期. */ +foo([]); /* 符合预期. */ +``` + +## Tuple + +元组类型. + +元组类型严格限制数组的对应类型及元素数量. +如 `[ number, number, string, number ]` 有如下限制: +- - 数组有且必有 4 个元素; +- - 元素类型依次为 number, number, string, number. + +> 注: 需额外注意元组类型与 JSDoc 表示数组方法的异同. +> 另外 JavaScript 中没有元组的概念. + +**foo(bar)** + +- **bar** { [[](#tuple) [string](#string), [number](#number) []](#tuple) } + +```js +foo([ "3" ]); /* 不符合预期. */ +foo([ 3 ]); /* 不符合预期. */ +foo([ "3", 3 ]); /* 符合预期. */ +foo([]); /* 不符合预期. */ +``` + +## Function + +函数类型. + +文档采用 [箭头函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions) 表示一个函数参数. + +**foo(bar)** + +- **bar** { [() =>](#function) [number](#number) } + +上述 [方法签名](documentation#方法签名) 中, bar 为函数参数, 该函数是一个无参函数且返回值为 number 类型. + +```js +foo(Math.random()); /* 不符合预期. */ + +foo(function () { + return Math.random(); +}); /* 符合预期. */ + +foo(function () { + return 'hello'; +}); /* 不符合预期. */ +``` + +**foo(bar)** + +- **bar** { [(a: ](#function)[string](#string)[, b: ](#function)[any](#any)[) => ](#function)[string](#string) } + +上述 [方法签名](documentation#方法签名) 中, bar 为函数参数, 该函数包含两个参函数且返回值为 string 类型. + +```js +/* 参数 a 为 string 类型, b 为 any 类型. */ +foo(function (a, b) { + return a + String(b); /* 字符串拼接. */ +}); /* 符合预期. */ +``` + +## RegExp + +正则表达式类型. + +**foo(bar)** + +- **bar** { [RegExp](#regexp) } + +上述 [方法签名](documentation#方法签名) 中, bar 为正则表达式参数, 是 JavaScript 标准 RegExp 类型: + +1. 字面量 + + `foo(/hello.+world?/)` + +2. RegExp 构造器 + + `new RegExp('hello.+world?')` + +> 参阅: [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp) + +## Any + +任意类型. + +类型 any 能够兼容所有类型. + +**foo(bar)** + +- **bar** { [any](#any) } + +```js +foo(3); /* 符合预期. */ +foo([]); /* 符合预期. */ +foo({}); /* 符合预期. */ +foo(null); /* 符合预期. */ +``` + +尽管 any 可以兼容所有类型, 但仍需提供一个具体的类型, 不能省略: + +```js +foo(); /* 不符合预期. */ +foo(undefined); /* 符合预期. */ +``` + +## Void + +此类型用于表示一个函数没有返回值. + +### 作为函数体返回值 + +**foo(bar)** + +- **bar** { [any](#any) } +- **returns** { [void](#void) } + +Void 作为 foo 函数体的返回值类型, 表示 foo 函数没有返回值: + +```js +function foo() { + console.log("hello"); +} /* 符合预期. */ + +function foo() { + return "hello"; +} /* 不符合预期. */ +``` + +### 作为参数返回值 + +**foo(bar)** + +- **bar** { [() =>](#function) [void](#void) } + +上述 [方法签名](documentation#方法签名) 中, bar 为函数参数, +void 并非表示要求其返回值为 void, +它表示 bar 返回的所有值均被忽略 (即不被关心). + +```js +let arr = []; +foo(() => arr.push(Math.random())); /* 符合预期. */ +console.log(arr); +``` + +### Void 与 Undefined + +**foo(bar)** + +- **bar** { [string](#string) } +- **returns** { [void](#void) } + +在 JavaScript 中, 没有 return 语句的函数将默认返回 [undefined](#undefined). +因此对于函数体, 返回值为 void 相当于 undefined: + +```js +foo(() => { + return; +}) /* 符合预期. */; + +foo(() => { + return undefined; +}) /* 符合预期. */; + +foo(() => { + // Empty body. +}) /* 符合预期. */; + +foo(() => { + return 3; +}) /* 不符合预期. */; +``` + +**foo(bar, baz)** + +- **bar** { [() =>](#function) [void](#void) } +- **baz** { [() =>](#function) [undefined](#undefined) } + +对于函数参数, 返回值 void 与 返回值 undefined 意义不同. +void 表示返回的所有值均被忽略 (参阅 [作为参数返回值](#作为参数返回值)), +而 undefined 表示返回值必须为 undefined 类型. + +```js +foo( + /* bar = */ () => { + return; + }, /* 符合预期. */ + /* baz = */ () => { + return; + }, /* 符合预期. */ +); + +foo( + /* bar = */ () => { + return undefined; + }, /* 符合预期. */ + /* baz = */ () => { + return undefined; + }, /* 符合预期. */ +); + +foo( + /* bar = */ () => { + // Empty body. + }, /* 符合预期. */ + /* baz = */ () => { + // Empty body. + }, /* 符合预期. */ +); + +foo( + /* bar = */ () => { + return 3; + }, /* 符合预期. */ + /* baz = */ () => { + return 3; + }, /* 不符合预期. */ +); +``` + +> 注: 上述方法签名如果将 void 替换为 any, 就 bar 参数是否符合预期方面而言, 效果是相同的. +> 然而两者在语义上有明确不同, void 表示不关心 bar 的返回值, 而 any 表示任意返回值类型均可接受. +> 在设计自定义 API 或设计 TS 声明文件时, 上述区分将显得尤为重要. + +## Never + +## Object + +### 字面量对象类型 + +{{ a: number }} + +## Generic + +## Null + +> 参阅: [MDN #术语](https://developer.mozilla.org/zh-CN/docs/Glossary/Null/) / [MDN #操作符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/null/) / [MDN #Nullish](https://developer.mozilla.org/zh-CN/docs/Glossary/Nullish/) + +## Undefined + +```js +// device.vibrate(text: string, delay?: number): void +typeof device.vibrate("hello") === "undefined"; // true +``` + +> 参阅: [MDN #术语](https://developer.mozilla.org/zh-CN/docs/Glossary/undefined/) / [MDN #全局对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/undefined/) + +## RegExPattern + +正则表达式模式类型. + +通常只在 [操纵泛型](#pattern) 中使用. + +**foo(bar)** + +- **bar** { [Pattern](#pattern)[<](#generic)[/^\d+$/](#regexp)[>](#generic) } + +```js +foo("1"); /* 符合预期. */ +foo("123"); /* 符合预期. */ +foo("hello"); /* 不符合预期. */ +foo("1e3"); /* 不符合预期. */ +foo("1.3"); /* 不符合预期. */ +``` + +## 联合类型 + +# 操作符 + +## in + +## keyof + +## typeof + +## extends + +## index + +## condition + +## readonly + +# 操纵泛型 + +例如 Array + +## Uppercase + +**Uppercase<T>: string** + +通常用于输出转换. +接受 string 类型并生成所有字母大写的同类型数据. + +## Lowercase + +**Lowercase<T>: string** + +通常用于输出转换. +接受 string 类型并生成所有字母小写的同类型数据. + +## Capitalize + +**Capitalize<T>: string** + +通常用于输出转换. +接受 string 类型并生成首字母大写的同类型数据. + +```js + +``` + +## IgnoreCase + +**IgnoreCase<T extends string>: T** + +通常用于输出转换. +接受 string 类型并生成忽略大小写的同类型数据. + +例如, 对于 IgnoreCase<"webUrl">, 以下数据均符合预期: + +```js +[ "webUrl", "WEBURL", "WebUrl", "WEBurl" ]; +``` + +但不能在字符串前后或内部插入其他字符, +如 [ "WEB_URL" / "web-url" / "#WebUrl" ] 等. + +## Pattern + +**Pattern<[T](#generic) [extends](#extends) [RegExPattern](#regexpattern)>: [string](#string)** + +通常用于输入检查. +接受 [正则表达式字面量](glossaries#正则表达式) 并生成通过测试的 [string](#string) 类型数据. + +Pattern 的泛型通配符 T 在文档中也称作 [字符串模式](glossaries#字符串模式). + +**foo(bar)** + +- **bar** { [Pattern](#pattern)[<](#generic)[/^https?:/](#regexpattern)[>](#generic) } + +```js +foo("http is an abbreviation."); /* 不符合预期. */ +foo("https://xxx"); /* 符合预期. */ +foo("ftp://xxx"); /* 不符合预期. */ +``` + +支持 [标记参数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions#%E9%80%9A%E8%BF%87%E6%A0%87%E5%BF%97%E8%BF%9B%E8%A1%8C%E9%AB%98%E7%BA%A7%E6%90%9C%E7%B4%A2): + +**foo(bar)** + +- **bar** { [Pattern](#pattern)[<](#generic)[/^h...[oy]/i](#regexpattern)[>](#generic) } + +```js +foo("Happy"); /* 符合预期. */ +foo("hello"); /* 符合预期. */ +foo("Halloween"); /* 符合预期. */ +foo("history"); /* 符合预期. */ +foo("heroes"); /* 不符合预期. */ +``` + +为便于理解或重复引用, 有些 Pattern 类型会被重新定义为自定义类型, 如 [NumberString](dataTypes#numberstring). + +> 注: 目前 (2022/08) 在 JSDoc 及 TypeScript 中, +> 均不存在使用正则表达式字面量检查字符串的类型检查 (参阅 [StackOverflow](https://stackoverflow.com/questions/51445767/how-to-define-a-regex-matched-string-type-in-typescript)), +> 上述 Pattern 类型仅适用于对文档内容的辅助理解. + +## JavaArray + +Java Array (Java 数组). + +```js +let javaArr = java.lang.reflect.Array + .newInstance(java.lang.Float.TYPE, 3); + +console.log(util.isJavaArray(javaArr)); // true +console.log(Array.isArray(javaArr)); // false +``` + +Java 数组可使用 JavaScript 数组的属性及方法: + +```js +let javaArr = java.lang.reflect.Array + .newInstance(java.lang.Float.TYPE, 3); + +console.log(javaArr.length); // 3 + +console.log(javaArr.slice === Array.prototype.slice); // true +Array.isArray(javaArr.slice(0)); // true +``` + +Java 数组一旦被初始化, 长度将不可改变, [ 改变长度 / 越界赋值 ] 均会失败且抛出异常: + +```js +let javaArr = java.lang.reflect.Array + .newInstance(java.lang.Float.TYPE, 3); + +/* 静默失败. */ +javaArr.length = 20; +console.log(javaArr.length); // 3 + +/* push 或 unshift 导致越界抛出异常. */ +javaArr.push(9); /* Error. */ +javaArr.unshift(9); /* Error. */ + +/* pop 或 shift 不抛出异常但不改变数组长度. */ +javaArr.pop(); +console.log(javaArr.length); // 3 +javaArr.shift(); +console.log(javaArr.length); // 3 + +/* 越界访问不抛出异常, 会返回 undefined. */ +console.log(javaArr[9]); // undefined + +/* 越界赋值将抛出异常. */ +javaArr[9] = 10; /* Error. */ +``` + +Java 数组中的元素将隐式转换为指定的类型, 同时此类型也会被转换为 JavaScript 类型, 如 Java 的 Integer 等均转换为 Number: + +```js +let javaArr = java.lang.reflect.Array + .newInstance(java.lang.Integer.TYPE, 3); + +console.log(javaArr.join()); // '0,0,0' + +/* Number('1a') -> NaN */ +javaArr[0] = '1a'; +console.log(javaArr[0]); // NaN + +/* Number('2.2') -> 2.2 $ JS */ +/* java.lang.Integer(2.2 $ JS) -> 2 $ Java */ +/* Number(2 $ Java) -> 2 $ JS */ +javaArr[2] = '2.2'; +console.log(javaArr[0]); // 2 + +/* 0xFF $ Hexadecimal == 255 $ Decimal / JS */ +/* java.lang.Integer(255 $ JS) -> 255 $ Java */ +/* Number(255 $ Java) -> 255 $ JS */ +javaArr[0] = 0xFF; +console.log(javaArr[0]); // 255 +``` + +> 参阅: [Oracle Docs](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html) + +## JavaArrayList + +Java ArrayList (Java 数组列表). + +与 [Java Array](#javaarray) 不同的是, ArrayList 创建的数组可调整大小: + +```js +let arrList = new java.util.ArrayList(); + +arrList.add(10); +arrList.add('20'); +arrList.add([ '30' ]); +arrList.add(/40/g); + +console.log(arrList.length); // 4 + +arrList.forEach((o) => { + // 10 (Number) + // 20 (String) + // 30 (Array) + // /40/g (RegExp) + console.log(`${o} (${species(o)})`); +}); + +arrList.addAll(arrList); +console.log(arrList.length); // 8 + +arrList.clear(); +console.log(arrList.length); // 0 +``` + +> 参阅: [Oracle Docs](https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html) + +# 自定义类型 + +## NumberString + +数字字符串. + +[字符串模式](glossaries#字符串模式): `/[+-]?(\d+(\.\d+)?(e\d+)?)/`. + +```js +"12"; +"-5"; +"1.5"; +"1.5e3"; +``` + +## ScreenMetricNumberX + +屏幕横向度量值. + +表示方式: + +- 数字 { X >= 1 或 X < -1 } - 横向屏幕宽度值 +- 数字 { X > -1 且 X < 1 } - 横向屏幕宽度值的百分比 +- 数字 { X == -1 } - 横向屏幕宽度值本身 (代指值) + +例如, 对于下面的参数: + +**bottom** { [ScreenMetricNumberX](dataTypes#screenmetricnumberx) } + +bottom 赋值为 50, 表示 X 坐标为 50. +bottom 赋值为 -80, 表示 X 坐标为 -80. +bottom 赋值为 0.5, 表示 X 坐标为 50% 横向屏幕宽度, 即 `0.5 * device.width`. +bottom 赋值为 -0.1, 表示 X 坐标为 -10% 横向屏幕宽度, 即 `-0.1 * device.width`. +bottom 赋值为 -1, 表示 X 坐标为横向屏幕宽度的代指值, 即 `device.width`. + +## ScreenMetricNumberY + +屏幕纵向度量值. + +表示方式: + +- 数字 { Y >= 1 或 Y < -1 } - 纵向屏幕高度值 +- 数字 { Y > -1 且 Y < 1 } - 纵向屏幕高度值的百分比 +- 数字 { Y == -1 } - 纵向屏幕高度值本身 (代指值) + +例如, 对于下面的参数: + +**top** { [ScreenMetricNumberY](dataTypes#screenmetricnumbery) } + +top 赋值为 50, 表示 Y 坐标为 50. +top 赋值为 -80, 表示 Y 坐标为 -80. +top 赋值为 0.5, 表示 Y 坐标为 50% 纵向屏幕高度, 即 `0.5 * device.height`. +top 赋值为 -0.1, 表示 Y 坐标为 -10% 纵向屏幕高度, 即 `-0.1 * device.height`. +top 赋值为 -1, 表示 Y 坐标为纵向屏幕高度的代指值, 即 `device.height`. + +--- + +## OpencvPoint + +org.opencv.core.Point 别名. +表示一个点, 作为控件信息时则表示点在屏幕的相对位置. + +```js +let point = pickup(/.+/, '.'); +console.log(`${point.x}, ${point.y}`); +``` + +部分属性或方法: + +- `[p#]` [x](#p-x) +- `[p#]` [y](#p-y) + +常见可以返回此类型的方法: + +- [UiSelector.pickup](uiSelectorType#m-pickup) + +> 参阅: [OpenCV Docs](https://docs.opencv.org/4.x/javadoc/org/opencv/core/Point.html) + +--- + +

org.opencv.core.Point

+ +--- + +### [C] org.opencv.core.Point + +#### [c] (x, y) + +- **x** { [number](dataTypes#number) } - 点 X 坐标 +- **y** { [number](dataTypes#number) } - 点 Y 坐标 +- **returns** { [org.opencv.core.Point](#c-orgopencvcorepoint) } + +生成一个点. + +```js +console.log(new org.opencv.core.Point(10, 20)); // {10.0, 20.0} +``` + +坐标不会被化为整型: + +```js +console.log(new org.opencv.core.Point(10.8, 20.44)); // {10.8, 20.44} +``` + +#### [c] () + +- **returns** { [org.opencv.core.Point](#c-orgopencvcorepoint) } + +生成一个点, 并初始化为 `{0, 0}` 坐标. + +```js +console.log(new org.opencv.core.Point()); // {0.0, 0.0} +``` + +#### [c] (points) + +- **points** { [number](dataTypes#number)[[]](dataTypes#array) } - 点坐标数组 +- **returns** { [org.opencv.core.Point](#c-orgopencvcorepoint) } + +生成一个点, 并按指定参数初始化坐标. + +两个坐标: + +```js +console.log(new org.opencv.core.Point([ 5, 23 ])); // {5.0, 23.0} +``` + +一个坐标, 此坐标作为 X 坐标, Y 坐标初始化为 0: + +```js +console.log(new org.opencv.core.Point([ 5 ])); // {5.0, 0.0} +``` + +空数组, X 与 Y 坐标均为 0: + +```js +console.log(new org.opencv.core.Point([])); // {0.0, 0.0} +``` + +超过两个坐标, 多余坐标将被忽略: + +```js +console.log(new org.opencv.core.Point([ 5, 23, 7, 8, 9 ])); // {5.0, 23.0} +``` + +### [p#] x + +- { [number](dataTypes#number) } + +点 X 坐标. + +如: Point(**180**, 440) 表示点距屏幕左边缘 180 像素. + +### [p#] y + +- { [number](dataTypes#number) } + +点 Y 坐标. + +如: Point(180, **440**) 表示点距屏幕上边缘 440 像素. + +--- + +## OpencvSize + +org.opencv.core.Size 别名. +表示一个长宽尺寸对象, 作为控件信息时则表示控件矩形在屏幕的控件占用尺寸. + +```js +let size = pickup(/.+/, 'size'); +console.log(`${size.width}x${size.height}`); +``` + +部分属性或方法: + +* `[p#]` [x](#p-width) +* `[p#]` [y](#p-height) + +常见可以返回此类型的方法: + +- [UiObject.size](uiobjectType#m-size) + +> 参阅: [OpenCV Docs](https://docs.opencv.org/4.x/javadoc/org/opencv/core/Size.html) + +--- + +

org.opencv.core.Size

+ +--- + +### [C] org.opencv.core.Size + +#### [c] (width, height) + +- **width** { [number](dataTypes#number) } - 宽度值 +- **height** { [number](dataTypes#number) } - 高度值 +- **returns** { [org.opencv.core.Size](#c-orgopencvcoresize) } + +生成一个尺寸对象. + +```js +console.log(new org.opencv.core.Size(100, 200)); // 100x200 +``` + +坐标不会被化为整型: + +```js +/* 打印时, 数值会转换为整数. */ +console.log(new org.opencv.core.Size(1.8, 3.2)); // 1x3 +/* 但获取宽高值时, 依然保留原始值, 不会被化为整型. */ +console.log(new org.opencv.core.Size(1.8, 3.2).width); // 1.8 +console.log(new org.opencv.core.Size(1.8, 3.2).height); // 3.2 +``` + +#### [c] () + +- **returns** { [org.opencv.core.Size](#c-orgopencvcoresize) } + +生成一个尺寸对象, 并初始化为 `0x0` 宽高尺寸. + +```js +console.log(new org.opencv.core.Size()); // 0x0 +``` + +#### [c] (point) + +- **point** { [OpencvPoint](#opencvpoint) } - 用于表示尺寸的 "点" +- **returns** { [org.opencv.core.Size](#c-orgopencvcoresize) } + +生成一个尺寸对象, 并按参数初始化宽高尺寸. + +```js +const { Size, Point } = org.opencv.core; +console.log(new Size(new Point(5, 23))); // 5x23 +``` + +#### [c] (dimensions) + +- **dimensions** { [number](dataTypes#number)[[]](dataTypes#array) } - 尺寸值数组 +- **returns** { [org.opencv.core.Size](#c-orgopencvcoresize) } + +生成一个尺寸对象, 并按指定参数初始化宽高尺寸. + +两个尺寸值: + +```js +console.log(new org.opencv.core.Size([ 5, 23 ])); // 5x23 +``` + +一个尺寸值, 此尺寸值作为宽度值, 高度值初始化为 0: + +```js +console.log(new org.opencv.core.Size([ 5 ])); // 5x0 +``` + +空数组, 宽度尺寸值均为 0: + +```js +console.log(new org.opencv.core.Size([])); // 0x0 +``` + +超过两个尺寸值, 多余尺寸值将被忽略: + +```js +console.log(new org.opencv.core.Size([ 5, 23, 7, 8, 9 ])); // 5x23 +``` + +### [p#] width + +- { [number](dataTypes#number) } + +尺寸宽度值. + +### [p#] height + +- { [number](dataTypes#number) } + +尺寸高度值. + +--- + +## AndroidRect + +android.graphics.Rect 别名. +表示一个矩形, 作为控件信息时则用于表示控件在屏幕的相对位置及空间范围, 又称 **控件矩形**. + +```js +let bounds = pickup(/.+/, 'bounds'); +console.log(`${bounds.centerX()}, ${bounds.centerY()}`); +``` + +部分属性或方法: + +- `[p#]` [left](#p-left) +- `[p#]` [top](#p-top) +- `[p#]` [right](#p-right) +- `[p#]` [bottom](#p-bottom) +- `[m#]` [width()](#m-width) +- `[m#]` [height()](#m-height) +- `[m#]` [centerX()](#m-centerx) +- `[m#]` [centerY()](#m-centery) +- `[m#]` [exactCenterX()](#m-exactcenterx) +- `[m#]` [exactCenterY()](#m-exactcentery) +- `[m#]` [contains()](#m-contains) +- `[m#]` [intersect()](#m-intersect) +- `[m#]` [intersects()](#m-intersects) + +常见可以返回此类型的方法: + +- [UiObject#bounds](uiObjectType#m-bounds) +- [UiSelector.pickup](uiSelectorType#m-pickup) + +> 参阅: [Android Docs](https://developer.android.com/reference/android/graphics/Rect) + +--- + +

android.graphics.Rect

+ +--- + +### [C] android.graphics.Rect + +#### [c] (left, top, right, bottom) + +- **left** { [number](dataTypes#number) } - 矩形左边界 X 坐标 +- **top** { [number](dataTypes#number) } - 矩形上边界 Y 坐标 +- **right** { [number](dataTypes#number) } - 矩形右边界 X 坐标 +- **bottom** { [number](dataTypes#number) } - 矩形下边界 Y 坐标 +- **returns** { [android.graphics.Rect](#c-androidgraphicsrect) } + +生成一个矩形. + +```js +let rect = new android.graphics.Rect(10, 20, 80, 90); +console.log(rect); // Rect(10, 20 - 80, 90) +``` + +如果坐标值为浮点数, 将做向下取整处理: + +```js +let rect = new android.graphics.Rect(10.2, 20.7, 80.1, 90.92); +console.log(rect); // Rect(10, 20 - 80, 90) +``` + +坐标值可以为 0 或负数: + +```js +let rect = new android.graphics.Rect(0, 0, -80, -90); +console.log(rect); // Rect(0, 0 - -80, -90) +``` + +#### [c] () + +- **returns** { [android.graphics.Rect](#c-androidgraphicsrect) } + +生成一个空矩形. + +```js +let rect = new android.graphics.Rect(); +console.log(rect); // Rect(0, 0 - 0, 0) +``` + +#### [c] (rect) + +- **rect** { [android.graphics.Rect](#c-androidgraphicsrect) } - 参照矩形 +- **returns** { [android.graphics.Rect](#c-androidgraphicsrect) } + +生成一个新矩形, 并按照参照矩形的参数初始化. + +```js +let rectA = new android.graphics.Rect(10, 20, 80, 90); +let rectB = new android.graphics.Rect(rectA); +console.log(rectB); // Rect(10, 20 - 80, 90) +rectB.top = 1; +rectB.bottom = 0; +console.log(rectB); // Rect(10, 1 - 80, 0) +console.log(rectA); // Rect(10, 20 - 80, 90) +``` + +### [p#] left + +- { [number](dataTypes#number) } + +矩形左边界 X 坐标. + +如: Rect(**180**, 440, 750, 1200) 表示矩形左边界距屏幕左边缘 180 像素. + +### [p#] top + +- { [number](dataTypes#number) } + +矩形上边界 Y 坐标. + +如: Rect(180, **440**, 750, 1200) 表示矩形上边界距屏幕上边缘 440 像素. + +### [p#] right + +- { [number](dataTypes#number) } + +矩形右边界 X 坐标. + +如: Rect(180, 440, **750**, 1200) 表示矩形右边界距屏幕左边缘 750 像素. + +### [p#] bottom + +- { [number](dataTypes#number) } + +矩形下边界 Y 坐标. + +如: Rect(180, 440, 750, **1200**) 表示矩形下边界距屏幕上边缘 1200 像素. + +### [m#] width + +#### width() + +- **returns** { [number](dataTypes#number) } + +矩形宽度. + +```js +let rect = new android.graphics.Rect(180, 440, 750, 1200); +console.log(rect.width()); // 570 +``` + +宽度可能为 0 或负数: + +```js +let rectA = new android.graphics.Rect(0, 440, 0, 1200); +console.log(rectA.width()); // 0 +let rectB = new android.graphics.Rect(30, 440, 10, 1200); +console.log(rectB.width()); // -20 +``` + +### [m#] height + +#### height() + +- **returns** { [number](dataTypes#number) } + +矩形高度. + +```js +let rect = new android.graphics.Rect(180, 440, 750, 1200); +console.log(rect.height()); // 760 +``` + +高度可能为 0 或负数: + +```js +let rectA = new android.graphics.Rect(180, 1200, 750, 1200); +console.log(rectA.height()); // 0 +let rectB = new android.graphics.Rect(180, 40, 750, 10); +console.log(rectB.height()); // -30 +``` + +### [m#] centerX + +#### centerX() + +- **returns** { [number](dataTypes#number) } + +矩形中点 X 坐标 (向下取整). + +```js +let rectA = new android.graphics.Rect(180, 440, 750, 1200); +console.log(rectA.centerX()); // 465 + +let rectB = new android.graphics.Rect(100, 200, 101, 201); +console.log(rectB.centerX()); // 100 +``` + +### [m#] centerY + +#### centerY() + +- **returns** { [number](dataTypes#number) } + +矩形中点 Y 坐标 (向下取整). + +```js +let rectA = new android.graphics.Rect(180, 440, 750, 1200); +console.log(rectA.centerY()); // 820 + +let rectB = new android.graphics.Rect(100, 200, 101, 201); +console.log(rectB.centerY()); // 200 +``` + +### [m#] exactCenterX + +#### exactCenterX() + +- **returns** { [number](dataTypes#number) } + +矩形中点 X 坐标 (浮点数). + +```js +let rectA = new android.graphics.Rect(180, 440, 750, 1200); +console.log(rectA.exactCenterX()); // 465 + +let rectB = new android.graphics.Rect(100, 200, 101, 201); +console.log(rectB.exactCenterX()); // 100.5 +``` + +### [m#] exactCenterY + +#### exactCenterY() + +- **returns** { [number](dataTypes#number) } + +矩形中点 Y 坐标 (浮点数). + +```js +let rectA = new android.graphics.Rect(180, 440, 750, 1200); +console.log(rectA.exactCenterY()); // 820 + +let rectB = new android.graphics.Rect(100, 200, 101, 201); +console.log(rectB.exactCenterY()); // 200.5 +``` + +### [m#] contains + +#### contains(rect) + +- **rect** { [android.graphics.Rect](#c-androidgraphicsrect) } - 参照矩形 +- **returns** { [boolean](dataTypes#boolean) } + +返回是否包含另一个矩形. +参照矩形的所有边均在当前矩形内 (包含边重叠情况) 则满足包含条件. +空矩形与任何矩形不存在包含关系. + +```js +let rectThis = new android.graphics.Rect(180, 440, 750, 1200); + +let rectRefA = new android.graphics.Rect(rectThis); +console.log(rectThis.contains(rectRefA)); // true + +let rectRefB = new android.graphics.Rect(200, 440, 750, 1200); +console.log(rectThis.contains(rectRefB)); // true + +let rectRefC = new android.graphics.Rect(); /* 空矩形. */ +console.log(rectThis.contains(rectRefC)); // false +``` + +### [m#] intersect + +#### intersect(rect) + +- **rect** { [android.graphics.Rect](#c-androidgraphicsrect) } - 参照矩形 +- **returns** { [boolean](dataTypes#boolean) } + +返回是否与参展矩形相交 (不包括边界或点重叠的情况). +如果相交, 则返回 true, **且当前矩形被设置为相交部分的矩形**. + +```js +let rectThis = new android.graphics.Rect(0, 0, 600, 600); +let rectRef = new android.graphics.Rect(200, 0, 800, 800); + +console.log(rectThis.intersect(rectRef)); // true + +/* rectThis 被修改. */ +console.log(rectThis); // Rect(200, 0 - 600, 600) +``` + +如果不相交, 则返回 false, 当前矩形不会被修改: + +```js +let rectThis = new android.graphics.Rect(0, 0, 100, 100); +let rectRef = new android.graphics.Rect(100, 0, 800, 800); + +console.log(rectThis.intersect(rectRef)); // false + +/* rectThis 保持原来的值. */ +console.log(rectThis); // Rect(0, 0 - 100, 100) +``` + +空矩形与任意矩形不相交: + +```js +let rectThis = new android.graphics.Rect(0, 0, 100, 100); +let rectRef = new android.graphics.Rect(); +console.log(rectThis.intersect(rectRef)); // false +``` + +### [m] intersects + +#### intersects(rectA, rectB) + +- **rect** { [android.graphics.Rect](#c-androidgraphicsrect) } - 参照矩形 +- **returns** { [boolean](dataTypes#boolean) } + +返回是否和另一个长方形相交. + +此方法近判断是否相交, 不改变任何矩形: + +```js +let rectA = new android.graphics.Rect(0, 0, 600, 600); +let rectB = new android.graphics.Rect(200, 0, 800, 800); + +console.log(android.graphics.Rect.intersects(rectA, rectB)); // true + +/* rectA 和 refB 均保持原来的值. */ +console.log(rectA); // Rect(0, 0 - 600, 600) +console.log(rectB); // Rect(200, 0 - 800, 800) +``` + +需额外留意 [intersects](#m-intersects) 与 [intersect](#m-intersect) 的区别: + +- `[m#] intersect` 为实例方法, `rectA.intersect(rectB)` 需传入一个参数, 当相交时 `rectA` 会被改变, 返回结果为 "是否相交". + +- `[m] intersects` 为静态方法, `Rect.intersects(rectA, rectB)` 需传入两个参数, 且不改变任何矩形, 仅返回 "是否相交" 结果. + +## AndroidBundle + +android.os.Bundle 别名. +表示一个会被打包成捆的容器, 容器内可存储 `键值对 (Key-Value Pair)` 形式的数据. + +```js +let bundleA = new android.os.Bundle(); +bundleA.putInt("num_key", 23); +console.log(bundleA.getInt("num_key") === 23); // true + +let bundleB = new android.os.Bundle(); +let arrList = new java.util.ArrayList(2); +arrList.add("A"); +arrList.add("B"); +bundleB.putStringArrayList("arr_list_key", arrList); +console.log(bundleB.getStringArrayList("arr_list_key").get(0) === "A"); // true +console.log(bundleB.getStringArrayList("arr_list_key").get(1) === "B"); // true +``` + +> 参阅: [Android Docs](https://developer.android.com/reference/android/os/Bundle) + +## DetectCompass + +用于传递给 [控件罗盘](uiObjectType#m-compass) 的参数类型, 又称 `罗盘参数`. + +罗盘参数是 [字符串](dataTypes#string) 类型, 支持单独或组合使用. + +下面列举了部分罗盘参数示例: + +- `p` - 父控件 +- `p2` - 二级父控件 +- `c0` - 索引 0 (首个) 子控件 +- `c2` - 索引 2 子控件 +- `c-1` - 末尾子控件 +- `s5` - 索引 5 兄弟控件 +- `s-2` - 倒数第 2 兄弟控件 +- `s<1` - 相邻左侧兄弟节点 +- `s>1` - 相邻右侧兄弟节点 +- `k2` - 向上寻找可点击控件 (最多 2 级) +- `p4c0>1>1>0s0` - 组合使用 + +[控件罗盘 (UiObject.compass)](uiObjectType#m-compass) 是 [控件探测 (UiObject.detect)](uiObjectType#m-detect) 的衍生方法, 因此类型命名采用了 `DetectCompass`. + +## DetectResult + +[控件探测 (UiObject.detect)](uiObjectType#m-detect) 的结果参数类型, 又称 `探测结果`, 此过程也称为 `结果筛选`. + +- `# / w / widget` - [控件](uiObjectType) +- `$ / txt / content` - [文本内容](uiObjectType#m-content) +- `. / pt / point` - [点](uiObjectType#m-point) +- `UiObjectInvokable` - [控件可调用类型](#uiobjectinvokable) + +```js +/* 控件. */ +detect(w, '#'); +detect(w, 'w'); /* 同上. */ +detect(w, 'widget'); /* 同上. */ + +/* 文本内容. */ +detect(w, '$'); +detect(w, 'txt'); /* 同上. */ +detect(w, 'content'); /* 同上. */ + +/* 点. */ +detect(w, '.'); +detect(w, 'pt'); /* 同上. */ +detect(w, 'point'); /* 同上. */ + +/* UiObjectInvokable (控件可调用类型). */ +detect(w, 'click'); /* i.e. w.click() */ +detect(w, [ 'setText', 'hello' ]); /* i.e. w.setText('hello') */ +``` + +不同于 [PickupResult (拾取结果)](#pickupresult), `探测结果` 的种类相对较少. + +## DetectCallback + +探测回调. + +探测回调用于处理 [控件探测 (UiObject.detect)](uiObjectType#m-detect) 的结果. + +`回调结果` 将影响 `探测结果`, 当 `回调结果` 返回 `undefined` 时, 将直接返回 `探测结果`, 否则返回 `回调结果`: + +```ts +function detect(w: T, callback: (w: T) => R): T | R { + let callbackResult: R = callback(w); + return callbackResult == undefined ? w : callbackResult; +} +``` + +示例: + +```js +let w = pickup(/.+/); + +/* 返回 w.content() 的结果. */ +detect(w, (w) => w.content()); + +/* 返回 w 的结果. */ +detect(w, (w) => { + console.log(w.content()); +}); +``` + +## PickupSelector + +[拾取选择器](uiSelectorType#m-pickup) 的 `选择器参数`. + +`选择器参数` 的类型分为 [单一型选择器](#单一型选择器) 和 [混合型选择器](#混合型选择器). + +### 单一型选择器 + +单一型选择器包含 [ [经典选择器](#经典选择器) / [内容选择器](#内容选择器) / [对象选择器](#对象选择器) ]. + +#### 经典选择器 + +`text('abc')` 或串联形式 `text('abc').clickable().centerX(0.5)`. + +#### 内容选择器 + +字符串 `'abc'` 或正则表达式 `/abc/`. +相当于 `content('abc')` 及 `contentMatch(/abc/)`. + +#### 对象选择器 + +将选择器名称作为 `键 (key)`, 选择器参数作为 `值 (value)`. +若参数多于 1 个, 使用数组包含所有参数; 若无参数, 使用 `[]` (空数组) 或 `null`, 或默认值 (如 `true`). +虽然一个参数也可使用数组, 但通常无必要. + +```js +/* 经典选择器. */ +let selClassic = text('abc').clickable().centerX(0.5).boundsInside(0.2, 0.05, -1, -1).action('CLICK', 'SET_TEXT', 'LONG_CLICK'); + +/* 对象选择器. */ +let selObject = { + text: 'abc', + clickable: [], /* 或 clickable: true . */ + centerX: 0.5, + boundsInside: [ 0.2, 0.05, -1, -1 ], + action: [ 'CLICK', 'SET_TEXT', 'LONG_CLICK' ], +}; +``` + +### 混合型选择器 + +混合型选择器由多个单一型选择器组成. + +用数组表示一个混合型选择器, 其中的元素为单一型选择器: + +```js +pickup([ /he.+/, clickable(true).boundsInside(0.2, 0.05, -1, -1) ]); +``` + +上述示例的选择器参数使用了混合型选择器, 它包含两个单一型选择器, 分别为 [内容选择器](#内容选择器) 和 [经典选择器](#经典选择器). + +上述示例可以转换为单一型选择器: + +```js +/* 对象选择器. */ +pickup({ + contentMatch: /he.+/, + clickable: true, + boundsInside: [ 0.2, 0.05, -1, -1 ], +}); + +/* 经典选择器. */ +pickup(contentMatch(/he.+/).clickable(true).boundsInside(0.2, 0.05, -1, -1)); +``` + +## PickupResult + +[拾取选择器 (UiSelector#pickup)](uiSelectorType#m-pickup) 的结果参数类型, 又称 `拾取结果`, 此过程也称为 `结果筛选`. + +- `# / w / widget` - [控件 (UiObject)](uiObjectType) +- `{} / #{} / {#} / w{} / {w} / wc / collection / list` -> [控件集合 (UiObjectCollection)](uiObjectCollectionType) +- `[] / #[] / [#] / w[] / [w] / ws / widgets` -> [控件 (UiObject)](uiObjectType) 数组 +- `$ / txt / content` - [文本内容 (UiObject#content)](uiObjectType#m-content) +- `$[] / [$] / txt[] / [txt] / content[] / [content] / contents` -> [文本内容 (UiObject#content)](uiObjectType#m-content) 数组 +- `. / pt / point` - [点 (UiObject#point)](uiObjectType#m-point) +- `.[] / [.] / point[] / [point] / pt[] / [pt] / points / pts` -> [点 (UiObject#point)](uiObjectType#m-point) 数组 +- `@ / selector / sel` -> [选择器 (UiSelector)](uiSelectorType) +- `? / exists` -> [存在判断 (UiSelector#exists)](uiSelectorType#m-exists) +- `UiObjectInvokable` - [控件可调用类型](#uiobjectinvokable) + +```js +/* 控件. */ +pickup(sel, '#'); +pickup(sel, 'w'); /* 同上. */ +pickup(sel, 'widget'); /* 同上. */ + +/* 文本内容. */ +pickup(sel, '$'); +pickup(sel, 'txt'); /* 同上. */ +pickup(sel, 'content'); /* 同上. */ + +/* 文本内容数组. */ +pickup(sel, '$[]'); +pickup(sel, 'txt[]'); /* 同上. */ +pickup(sel, '[content]'); /* 同上. */ +pickup(sel, 'contents'); /* 同上. */ + +/* 点. */ +pickup(sel, '.'); +pickup(sel, 'pt'); /* 同上. */ +pickup(sel, 'point'); /* 同上. */ + +/* 点数组. */ +pickup(sel, '.[]'); +pickup(sel, '[.]'); /* 同上. */ +pickup(sel, '[point]'); /* 同上. */ +pickup(sel, 'points'); /* 同上. */ + +/* UiObjectInvokable (控件可调用类型). */ +pickup(sel, 'click'); /* i.e. sel.findOnce().click() */ +pickup(sel, [ 'setText', 'hello' ]); /* i.e. sel.findOnce().setText('hello') */ +``` + +与 [DetectResult (探测结果)](#detectresult) 相比, `拾取结果` 的种类更加丰富. + +## UiObjectInvokable + +控件可调用类型, 用于使用参数形式实现方法调用, 又称 `参化调用`. + +支持所有 [UiObject](uiObjectType) 的实例方法, 如果方法需要传递参数, 需要将参数连同方法名称放入数组后再传递. + +```js +/* 无参方法. */ +detect(w, 'click'); /* i.e. w.click() */ +detect(w, 'imeEnter'); /* i.e. w.imeEnter() */ + +/* 含参方法. */ +detect(w, [ 'child', 0 ]); /* i.e. w.child(0) */ +detect(w, [ 'setText', 'hello' ]); /* i.e. w.setText('hello') */ +detect(w, [ 'setSelection', 2, 3 ]); /* i.e. w.setSelection(2, 3) */ +``` \ No newline at end of file diff --git a/api/device.md b/api/device.md index f445011..893ed76 100644 --- a/api/device.md +++ b/api/device.md @@ -1,22 +1,27 @@ -# Device +# 设备 (Device) -> Stability: 2 - Stable +--- -device模块提供了与设备有关的信息与操作,例如获取设备宽高,内存使用率,IMEI,调整设备亮度、音量等。 +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

-此模块的部分函数,例如调整音量,需要"修改系统设置"的权限。如果没有该权限,会抛出`SecurityException`并跳转到权限设置界面。 +--- + +device模块提供了与设备有关的信息与操作, 例如获取设备宽高, 内存使用率, IMEI, 调整设备亮度、音量等. + +此模块的部分函数, 例如调整音量, 需要"修改系统设置"的权限. 如果没有该权限, 会抛出`SecurityException`并跳转到权限设置界面. ## device.width * {number} -设备屏幕分辨率宽度。例如1080。 +设备屏幕分辨率宽度. 例如1080. ## device.height * {number} -设备屏幕分辨率高度。例如1920。 +设备屏幕分辨率高度. 例如1920. ## device.buildId @@ -24,7 +29,7 @@ device模块提供了与设备有关的信息与操作,例如获取设备宽 Either a changelist number, or a label like "M4-rc20". -修订版本号,或者诸如"M4-rc20"的标识。 +修订版本号, 或者诸如"M4-rc20"的标识. ## device.broad @@ -32,7 +37,7 @@ Either a changelist number, or a label like "M4-rc20". The name of the underlying board, like "goldfish". -设备的主板(?)型号。 +设备的主板(?)型号. ## device.brand @@ -40,7 +45,7 @@ The name of the underlying board, like "goldfish". The consumer-visible brand with which the product/hardware will be associated, if any. -与产品或硬件相关的厂商品牌,如"Xiaomi", "Huawei"等。 +与产品或硬件相关的厂商品牌, 如"Xiaomi", "Huawei"等. ## device.device @@ -48,7 +53,7 @@ The consumer-visible brand with which the product/hardware will be associated, i The name of the industrial design. -设备在工业设计中的名称。 +设备在工业设计中的名称. ## device.model @@ -56,7 +61,7 @@ The name of the industrial design. The end-user-visible name for the end product. -设备型号。 +设备型号. ## device.product @@ -64,7 +69,7 @@ The end-user-visible name for the end product. The name of the overall product. -整个产品的名称。 +整个产品的名称. ## device.bootloader @@ -72,7 +77,7 @@ The name of the overall product. The system bootloader version number. -设备Bootloader的版本。 +设备Bootloader的版本. ## device.hardware @@ -80,7 +85,7 @@ The system bootloader version number. The name of the hardware (from the kernel command line or /proc). -设备的硬件名称(来自内核命令行或者/proc)。 +设备的硬件名称(来自内核命令行或者/proc). ## device.fingerprint @@ -88,7 +93,7 @@ The name of the hardware (from the kernel command line or /proc). A string that uniquely identifies this build. Do not attempt to parse this value. -构建(build)的唯一标识码。 +构建(build)的唯一标识码. ## device.serial @@ -96,7 +101,7 @@ A string that uniquely identifies this build. Do not attempt to parse this value A hardware serial number, if available. Alphanumeric only, case-insensitive. -硬件序列号。 +硬件序列号. ## device.sdkInt @@ -104,7 +109,7 @@ A hardware serial number, if available. Alphanumeric only, case-insensitive. The user-visible SDK version of the framework; its possible values are defined in Build.VERSION_CODES. -安卓系统API版本。例如安卓4.4的sdkInt为19。 +安卓系统API版本. 例如安卓4.4的sdkInt为19. ## device.incremental @@ -118,7 +123,7 @@ The internal value used by the underlying source control to represent this build The user-visible version string. E.g., "1.0" or "3.4b5". -Android系统版本号。例如"5.0", "7.1.1"。 +Android系统版本号. 例如"5.0", "7.1.1". ## device.baseOS @@ -132,7 +137,7 @@ The base OS build the product is based on. The user-visible security patch level. -安全补丁程序级别。 +安全补丁程序级别. ## device.codename @@ -140,7 +145,7 @@ The user-visible security patch level. The current development codename, or the string "REL" if this is a release build. -开发代号,例如发行版是"REL"。 +开发代号, 例如发行版是"REL". ## device.getIMEI() @@ -152,157 +157,157 @@ The current development codename, or the string "REL" if this is a release build * {string} -返回设备的Android ID。 +返回设备的Android ID. -Android ID为一个用16进制字符串表示的64位整数,在设备第一次使用时随机生成,之后不会更改,除非恢复出厂设置。 +Android ID为一个用16进制字符串表示的64位整数, 在设备第一次使用时随机生成, 之后不会更改, 除非恢复出厂设置. ## device.getMacAddress() * {string} -返回设备的Mac地址。该函数需要在有WLAN连接的情况下才能获取,否则会返回null。 +返回设备的Mac地址. 该函数需要在有WLAN连接的情况下才能获取, 否则会返回null. -**可能的后续修改**:未来可能增加有root权限的情况下通过root权限获取,从而在没有WLAN连接的情况下也能返回正确的Mac地址,因此请勿使用此函数判断WLAN连接。 +**可能的后续修改**:未来可能增加有root权限的情况下通过root权限获取, 从而在没有WLAN连接的情况下也能返回正确的Mac地址, 因此请勿使用此函数判断WLAN连接. ## device.getBrightness() * {number} -返回当前的(手动)亮度。范围为0~255。 +返回当前的(手动)亮度. 范围为0~255. ## device.getBrightnessMode() * {number} -返回当前亮度模式,0为手动亮度,1为自动亮度。 +返回当前亮度模式, 0为手动亮度, 1为自动亮度. ## device.setBrightness(b) -* `b` {number} 亮度,范围0~255 +* `b` {number} 亮度, 范围0~255 -设置当前手动亮度。如果当前是自动亮度模式,该函数不会影响屏幕的亮度。 +设置当前手动亮度. 如果当前是自动亮度模式, 该函数不会影响屏幕的亮度. -此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。 +此函数需要"修改系统设置"的权限. 如果没有该权限, 会抛出SecurityException并跳转到权限设置界面. ## device.setBrightnessMode(mode) -* `mode` {number} 亮度模式,0为手动亮度,1为自动亮度 +* `mode` {number} 亮度模式, 0为手动亮度, 1为自动亮度 -设置当前亮度模式。 +设置当前亮度模式. -此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。 +此函数需要"修改系统设置"的权限. 如果没有该权限, 会抛出SecurityException并跳转到权限设置界面. ## device.getMusicVolume() * {number} 整数值 -返回当前媒体音量。 +返回当前媒体音量. ## device.getNotificationVolume() * {number} 整数值 -返回当前通知音量。 +返回当前通知音量. ## device.getAlarmVolume() * {number} 整数值 -返回当前闹钟音量。 +返回当前闹钟音量. ## device.getMusicMaxVolume() * {number} 整数值 -返回媒体音量的最大值。 +返回媒体音量的最大值. ## device.getNotificationMaxVolume() * {number} 整数值 -返回通知音量的最大值。 +返回通知音量的最大值. ## device.getAlarmMaxVolume() * {number} 整数值 -返回闹钟音量的最大值。 +返回闹钟音量的最大值. ## device.setMusicVolume(volume) * `volume` {number} 音量 -设置当前媒体音量。 +设置当前媒体音量. -此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。 +此函数需要"修改系统设置"的权限. 如果没有该权限, 会抛出SecurityException并跳转到权限设置界面. ## device.setNotificationVolume(volume) * `volume` {number} 音量 -设置当前通知音量。 +设置当前通知音量. -此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。 +此函数需要"修改系统设置"的权限. 如果没有该权限, 会抛出SecurityException并跳转到权限设置界面. ## device.setAlarmVolume(volume) * `volume` {number} 音量 -设置当前闹钟音量。 +设置当前闹钟音量. -此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。 +此函数需要"修改系统设置"的权限. 如果没有该权限, 会抛出SecurityException并跳转到权限设置界面. ## device.getBattery() * {number} 0.0~100.0的浮点数 -返回当前电量百分比。 +返回当前电量百分比. ## device.isCharging() * {boolean} -返回设备是否正在充电。 +返回设备是否正在充电. ## device.getTotalMem() * {number} -返回设备内存总量,单位字节(B)。1MB = 1024 * 1024B。 +返回设备内存总量, 单位字节(B). 1MB = 1024 * 1024B. ## device.getAvailMem() * {number} -返回设备当前可用的内存,单位字节(B)。 +返回设备当前可用的内存, 单位字节(B). ## device.isScreenOn() * 返回 {boolean} -返回设备屏幕是否是亮着的。如果屏幕亮着,返回`true`; 否则返回`false`。 +返回设备屏幕是否是亮着的. 如果屏幕亮着, 返回`true`; 否则返回`false`. -需要注意的是,类似于vivo xplay系列的息屏时钟不属于"屏幕亮着"的情况,虽然屏幕确实亮着但只能显示时钟而且不可交互,此时`isScreenOn()`也会返回`false`。 +需要注意的是, 类似于vivo xplay系列的息屏时钟不属于"屏幕亮着"的情况, 虽然屏幕确实亮着但只能显示时钟而且不可交互, 此时`isScreenOn()`也会返回`false`. ## device.wakeUp() -唤醒设备。包括唤醒设备CPU、屏幕等。可以用来点亮屏幕。 +唤醒设备. 包括唤醒设备CPU、屏幕等. 可以用来点亮屏幕. ## device.wakeUpIfNeeded() -如果屏幕没有点亮,则唤醒设备。 +如果屏幕没有点亮, 则唤醒设备. ## device.keepScreenOn([timeout]) -* `timeout` {number} 屏幕保持常亮的时间, 单位毫秒。如果不加此参数,则一直保持屏幕常亮。 +* `timeout` {number} 屏幕保持常亮的时间, 单位毫秒. 如果不加此参数, 则一直保持屏幕常亮. -保持屏幕常亮。 +保持屏幕常亮. -此函数无法阻止用户使用锁屏键等正常关闭屏幕,只能使得设备在无人操作的情况下保持屏幕常亮;同时,如果此函数调用时屏幕没有点亮,则会唤醒屏幕。 +此函数无法阻止用户使用锁屏键等正常关闭屏幕, 只能使得设备在无人操作的情况下保持屏幕常亮;同时, 如果此函数调用时屏幕没有点亮, 则会唤醒屏幕. -在某些设备上,如果不加参数timeout,只能在Auto.js的界面保持屏幕常亮,在其他界面会自动失效,这是因为设备的省电策略造成的。因此,建议使用比较长的时长来代替"一直保持屏幕常亮"的功能,例如`device.keepScreenOn(3600 * 1000)`。 +在某些设备上, 如果不加参数timeout, 只能在Auto.js的界面保持屏幕常亮, 在其他界面会自动失效, 这是因为设备的省电策略造成的. 因此, 建议使用比较长的时长来代替"一直保持屏幕常亮"的功能, 例如`device.keepScreenOn(3600 * 1000)`. -可以使用`device.cancelKeepingAwake()`来取消屏幕常亮。 +可以使用`device.cancelKeepingAwake()`来取消屏幕常亮. ``` //一直保持屏幕常亮 @@ -311,23 +316,23 @@ device.keepScreenOn() ## device.keepScreenDim([timeout]) -* `timeout` {number} 屏幕保持常亮的时间, 单位毫秒。如果不加此参数,则一直保持屏幕常亮。 +* `timeout` {number} 屏幕保持常亮的时间, 单位毫秒. 如果不加此参数, 则一直保持屏幕常亮. -保持屏幕常亮,但允许屏幕变暗来节省电量。此函数可以用于定时脚本唤醒屏幕操作,不需要用户观看屏幕,可以让屏幕变暗来节省电量。 +保持屏幕常亮, 但允许屏幕变暗来节省电量. 此函数可以用于定时脚本唤醒屏幕操作, 不需要用户观看屏幕, 可以让屏幕变暗来节省电量. -此函数无法阻止用户使用锁屏键等正常关闭屏幕,只能使得设备在无人操作的情况下保持屏幕常亮;同时,如果此函数调用时屏幕没有点亮,则会唤醒屏幕。 +此函数无法阻止用户使用锁屏键等正常关闭屏幕, 只能使得设备在无人操作的情况下保持屏幕常亮;同时, 如果此函数调用时屏幕没有点亮, 则会唤醒屏幕. -可以使用`device.cancelKeepingAwake()`来取消屏幕常亮。 +可以使用`device.cancelKeepingAwake()`来取消屏幕常亮. ## device.cancelKeepingAwake() -取消设备保持唤醒状态。用于取消`device.keepScreenOn()`, `device.keepScreenDim()`等函数设置的屏幕常亮。 +取消设备保持唤醒状态. 用于取消`device.keepScreenOn()`, `device.keepScreenDim()`等函数设置的屏幕常亮. ## device.vibrate(millis) -* `millis` {number} 震动时间,单位毫秒 +* `millis` {number} 震动时间, 单位毫秒 -使设备震动一段时间。 +使设备震动一段时间. ``` //震动两秒 @@ -336,4 +341,4 @@ device.vibrate(2000); ## device.cancelVibration() -如果设备处于震动状态,则取消震动。 +如果设备处于震动状态, 则取消震动. diff --git a/api/dialogs.md b/api/dialogs.md index acf3735..ad285ac 100644 --- a/api/dialogs.md +++ b/api/dialogs.md @@ -1,14 +1,19 @@ -# Dialogs +# 对话框 (Dialogs) -> Stability: 2 - Stable +--- -dialogs 模块提供了简单的对话框支持,可以通过对话框和用户进行交互。最简单的例子如下: +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +dialogs 模块提供了简单的对话框支持, 可以通过对话框和用户进行交互. 最简单的例子如下: ``` alert("您好"); ``` -这段代码会弹出一个消息提示框显示"您好",并在用户点击"确定"后继续运行。稍微复杂一点的例子如下: +这段代码会弹出一个消息提示框显示"您好", 并在用户点击"确定"后继续运行. 稍微复杂一点的例子如下: ``` var clear = confirm("要清除所有缓存吗?"); @@ -17,9 +22,9 @@ if(clear){ } ``` -`confirm()`会弹出一个对话框并让用户选择"是"或"否",如果选择"是"则返回true。 +`confirm()`会弹出一个对话框并让用户选择"是"或"否", 如果选择"是"则返回true. -需要特别注意的是,对话框在ui模式下不能像通常那样使用,应该使用回调函数或者[Promise](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise)的形式。理解这一点可能稍有困难。举个例子: +需要特别注意的是, 对话框在ui模式下不能像通常那样使用, 应该使用回调函数或者[Promise](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/)的形式. 理解这一点可能稍有困难. 举个例子: ``` "ui"; @@ -40,19 +45,19 @@ confirm("要清除所有缓存吗?") ## dialogs.alert(title[, content, callback]) -* `title` {string} 对话框的标题。 -* `content` {string} 可选,对话框的内容。默认为空。 -* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 +* `title` {string} 对话框的标题. +* `content` {string} 可选, 对话框的内容. 默认为空. +* `callback` {Function} 回调函数, 可选. 当用户点击确定时被调用,一般用于ui模式. -显示一个只包含“确定”按钮的提示对话框。直至用户点击确定脚本才继续运行。 +显示一个只包含“确定”按钮的提示对话框. 直至用户点击确定脚本才继续运行. -该函数也可以作为全局函数使用。 +该函数也可以作为全局函数使用. ``` -alert("出现错误~", "出现未知错误,请联系脚本作者”); +alert("出现错误~", "出现未知错误, 请联系脚本作者”); ``` -在ui模式下该函数返回一个`Promise`。例如: +在ui模式下该函数返回一个`Promise`. 例如: ``` "ui"; @@ -63,15 +68,15 @@ alert("嘿嘿嘿").then(()=>{ ## dialogs.confirm(title[, content, callback]) -* `title` {string} 对话框的标题。 -* `content` {string} 可选,对话框的内容。默认为空。 -* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 +* `title` {string} 对话框的标题. +* `content` {string} 可选, 对话框的内容. 默认为空. +* `callback` {Function} 回调函数, 可选. 当用户点击确定时被调用,一般用于ui模式. -显示一个包含“确定”和“取消”按钮的提示对话框。如果用户点击“确定”则返回 `true` ,否则返回 `false` 。 +显示一个包含“确定”和“取消”按钮的提示对话框. 如果用户点击“确定”则返回 `true` , 否则返回 `false` . -该函数也可以作为全局函数使用。 +该函数也可以作为全局函数使用. -在ui模式下该函数返回一个`Promise`。例如: +在ui模式下该函数返回一个`Promise`. 例如: ``` "ui"; @@ -82,20 +87,20 @@ confirm("确定吗").then(value=>{ ## dialogs.rawInput(title[, prefill, callback]) -* `title` {string} 对话框的标题。 -* `prefill` {string} 输入框的初始内容,可选,默认为空。 -* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 +* `title` {string} 对话框的标题. +* `prefill` {string} 输入框的初始内容, 可选, 默认为空. +* `callback` {Function} 回调函数, 可选. 当用户点击确定时被调用,一般用于ui模式. -显示一个包含输入框的对话框,等待用户输入内容,并在用户点击确定时将输入的字符串返回。如果用户取消了输入,返回null。 +显示一个包含输入框的对话框, 等待用户输入内容, 并在用户点击确定时将输入的字符串返回. 如果用户取消了输入, 返回null. -该函数也可以作为全局函数使用。 +该函数也可以作为全局函数使用. ``` var name = rawInput("请输入您的名字", "小明"); alert("您的名字是" + name); ``` -在ui模式下该函数返回一个`Promise`。例如: +在ui模式下该函数返回一个`Promise`. 例如: ``` "ui"; @@ -104,7 +109,7 @@ rawInput("请输入您的名字", "小明").then(name => { }); ``` -当然也可以使用回调函数,例如: +当然也可以使用回调函数, 例如: ``` rawInput("请输入您的名字", "小明", name => { @@ -114,9 +119,9 @@ rawInput("请输入您的名字", "小明", name => { ## dialogs.input(title[, prefill, callback]) -等效于 `eval(dialogs.rawInput(title, prefill, callback))`, 该函数和rawInput的区别在于,会把输入的字符串用eval计算一遍再返回,返回的可能不是字符串。 +等效于 `eval(dialogs.rawInput(title, prefill, callback))`, 该函数和rawInput的区别在于, 会把输入的字符串用eval计算一遍再返回, 返回的可能不是字符串. -可以用该函数输入数字、数组等。例如: +可以用该函数输入数字、数组等. 例如: ``` var age = dialogs.input("请输入您的年龄", "18"); @@ -125,7 +130,7 @@ var year = new Date().getYear() + 1900 - age; alert("您的出生年份是" + year); ``` -在ui模式下该函数返回一个`Promise`。例如: +在ui模式下该函数返回一个`Promise`. 例如: ``` "ui"; @@ -141,11 +146,11 @@ dialogs.input("请输入您的年龄", "18").then(age => { ## dialogs.select(title, items, callback) -* `title` {string} 对话框的标题。 -* `items` {Array} 对话框的选项列表,是一个字符串数组。 -* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 +* `title` {string} 对话框的标题. +* `items` {Array} 对话框的选项列表, 是一个字符串数组. +* `callback` {Function} 回调函数, 可选. 当用户点击确定时被调用,一般用于ui模式. -显示一个带有选项列表的对话框,等待用户选择,返回用户选择的选项索引(0 ~ item.length - 1)。如果用户取消了选择,返回-1。 +显示一个带有选项列表的对话框, 等待用户选择, 返回用户选择的选项索引(0 ~ item.length - 1). 如果用户取消了选择, 返回-1. ``` var options = ["选项A", "选项B", "选项C", "选项D"] @@ -157,7 +162,7 @@ if(i >= 0){ } ``` -在ui模式下该函数返回一个`Promise`。例如: +在ui模式下该函数返回一个`Promise`. 例如: ``` "ui"; @@ -169,32 +174,32 @@ dialogs.select("请选择一个选项", ["选项A", "选项B", "选项C", "选 ## dialogs.singleChoice(title, items[, index, callback]) -* `title` {string} 对话框的标题。 -* `items` {Array} 对话框的选项列表,是一个字符串数组。 -* `index` {number} 对话框的初始选项的位置,默认为0。 -* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 +* `title` {string} 对话框的标题. +* `items` {Array} 对话框的选项列表, 是一个字符串数组. +* `index` {number} 对话框的初始选项的位置, 默认为0. +* `callback` {Function} 回调函数, 可选. 当用户点击确定时被调用,一般用于ui模式. -显示一个单选列表对话框,等待用户选择,返回用户选择的选项索引(0 ~ item.length - 1)。如果用户取消了选择,返回-1。 +显示一个单选列表对话框, 等待用户选择, 返回用户选择的选项索引(0 ~ item.length - 1). 如果用户取消了选择, 返回-1. -在ui模式下该函数返回一个`Promise`。 +在ui模式下该函数返回一个`Promise`. ## dialogs.multiChoice(title, items[, indices, callback]) -* `title` {string} 对话框的标题。 -* `items` {Array} 对话框的选项列表,是一个字符串数组。 -* `indices` {Array} 选项列表中初始选中的项目索引的数组,默认为空数组。 -* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 +* `title` {string} 对话框的标题. +* `items` {Array} 对话框的选项列表, 是一个字符串数组. +* `indices` {Array} 选项列表中初始选中的项目索引的数组, 默认为空数组. +* `callback` {Function} 回调函数, 可选. 当用户点击确定时被调用,一般用于ui模式. -显示一个多选列表对话框,等待用户选择,返回用户选择的选项索引的数组。如果用户取消了选择,返回`[]`。 +显示一个多选列表对话框, 等待用户选择, 返回用户选择的选项索引的数组. 如果用户取消了选择, 返回`[]`. -在ui模式下该函数返回一个`Promise`。 +在ui模式下该函数返回一个`Promise`. ## dialogs.build(properties) -* `properties` {Object} 对话框属性,用于配置对话框。 +* `properties` {Object} 对话框属性, 用于配置对话框. * 返回 {Dialog} -创建一个可自定义的对话框,例如: +创建一个可自定义的对话框, 例如: ``` dialogs.build({ @@ -227,17 +232,17 @@ dialogs.build({ * `title` {string} 对话框标题 * `titleColor` {string} | {number} 对话框标题的颜色 * `buttonRippleColor` {string} | {number} 对话框按钮的波纹效果颜色 -* `icon` {string} | {Image} 对话框的图标,是一个URL或者图片对象 +* `icon` {string} | {Image} 对话框的图标, 是一个URL或者图片对象 * `content` {string} 对话框文字内容 * `contentColor`{string} | {number} 对话框文字内容的颜色 -* `contentLineSpacing`{number} 对话框文字内容的行高倍数,1.0为一倍行高 +* `contentLineSpacing`{number} 对话框文字内容的行高倍数, 1.0为一倍行高 * `items` {Array} 对话框列表的选项 * `itemsColor` {string} | {number} 对话框列表的选项的文字颜色 -* `itemsSelectMode` {string} 对话框列表的选项选择模式,可以为: +* `itemsSelectMode` {string} 对话框列表的选项选择模式, 可以为: * `select` 普通选择模式 * `single` 单选模式 * `multi` 多选模式 -* `itemsSelectedIndex` {number} | {Array} 对话框列表中预先选中的项目索引,如果是单选模式为一个索引;多选模式则为数组 +* `itemsSelectedIndex` {number} | {Array} 对话框列表中预先选中的项目索引, 如果是单选模式为一个索引;多选模式则为数组 * `positive` {string} 对话框确定按钮的文字内容(最右边按钮) * `positiveColor` {string} | {number} 对话框确定按钮的文字颜色(最右边按钮) * `neutral` {string} 对话框中立按钮的文字内容(最左边按钮) @@ -247,15 +252,15 @@ dialogs.build({ * `checkBoxPrompt` {string} 勾选框文字内容 * `checkBoxChecked` {boolean} 勾选框是否勾选 * `progress` {Object} 配置对话框进度条的对象: - * `max` {number} 进度条的最大值,如果为-1则为无限循环的进度条 + * `max` {number} 进度条的最大值, 如果为-1则为无限循环的进度条 * `horizontal` {boolean} 如果为true, 则对话框无限循环的进度条为水平进度条 * `showMinMax` {boolean} 是否显示进度条的最大值和最小值 -* `cancelable` {boolean} 对话框是否可取消,如果为false,则对话框只能用代码手动取消 -* `canceledOnTouchOutside` {boolean} 对话框是否在点击对话框以外区域时自动取消,默认为true +* `cancelable` {boolean} 对话框是否可取消, 如果为false, 则对话框只能用代码手动取消 +* `canceledOnTouchOutside` {boolean} 对话框是否在点击对话框以外区域时自动取消, 默认为true * `inputHint` {string} 对话框的输入框的输入提示 * `inputPrefill` {string} 对话框输入框的默认输入内容 -通过这些选项可以自定义一个对话框,并通过监听返回的Dialog对象的按键、输入事件来实现交互。下面是一些例子。 +通过这些选项可以自定义一个对话框, 并通过监听返回的Dialog对象的按键、输入事件来实现交互. 下面是一些例子. 模拟alert对话框: @@ -323,7 +328,7 @@ dialogs.build({ }).show(); ``` -使用这个函数来构造对话框,一个明显的不同是需要使用回调函数而不能像dialogs其他函数一样同步地返回结果;但也可以通过threads模块的方法来实现。例如显示一个输入框并获取输入结果为: +使用这个函数来构造对话框, 一个明显的不同是需要使用回调函数而不能像dialogs其他函数一样同步地返回结果;但也可以通过threads模块的方法来实现. 例如显示一个输入框并获取输入结果为: ``` var input = threads.disposable(); @@ -339,13 +344,13 @@ tosatLog(age); # Dialog -`dialogs.build()`返回的对话框对象,内置一些事件用于响应用户的交互,也可以获取对话框的状态和信息。 +`dialogs.build()`返回的对话框对象, 内置一些事件用于响应用户的交互, 也可以获取对话框的状态和信息. ## 事件: `show` * `dialog` {Dialog} 对话框 -对话框显示时会触发的事件。例如: +对话框显示时会触发的事件. 例如: ``` dialogs.build({ @@ -359,7 +364,7 @@ dialogs.build({ * `dialog` {Dialog} 对话框 -对话框被取消时会触发的事件。一个对话框可能按取消按钮、返回键取消或者点击对话框以外区域取消。例如: +对话框被取消时会触发的事件. 一个对话框可能按取消按钮、返回键取消或者点击对话框以外区域取消. 例如: ``` dialogs.build({ @@ -375,7 +380,7 @@ dialogs.build({ * `dialog` {Dialog} 对话框 -对话框消失时会触发的事件。对话框被取消或者手动调用`dialog.dismiss()`函数都会触发该事件。例如: +对话框消失时会触发的事件. 对话框被取消或者手动调用`dialog.dismiss()`函数都会触发该事件. 例如: ``` var d = dialogs.build({ @@ -395,7 +400,7 @@ setTimeout(()=>{ * `dialog` {Dialog} 对话框 -确定按钮按下时触发的事件。例如: +确定按钮按下时触发的事件. 例如: ``` var d = dialogs.build({ @@ -411,7 +416,7 @@ var d = dialogs.build({ * `dialog` {Dialog} 对话框 -取消按钮按下时触发的事件。例如: +取消按钮按下时触发的事件. 例如: ``` var d = dialogs.build({ @@ -427,7 +432,7 @@ var d = dialogs.build({ * `dialog` {Dialog} 对话框 -中性按钮按下时触发的事件。例如: +中性按钮按下时触发的事件. 例如: ``` var d = dialogs.build({ @@ -443,12 +448,12 @@ var d = dialogs.build({ ## 事件: `any` * `dialog` {Dialog} 对话框 -* `action` {string} 被点击的按钮,可能的值为: +* `action` {string} 被点击的按钮, 可能的值为: * `positive` 确定按钮 * `negative` 取消按钮 * `neutral` 中性按钮 -任意按钮按下时触发的事件。例如: +任意按钮按下时触发的事件. 例如: ``` var d = dialogs.build({ @@ -467,11 +472,11 @@ var d = dialogs.build({ ## 事件: `item_select` -* `index` {number} 被选中的项目索引,从0开始 +* `index` {number} 被选中的项目索引, 从0开始 * `item` {Object} 被选中的项目 * `dialog` {Dialog} 对话框 -对话框列表(itemsSelectMode为"select")的项目被点击选中时触发的事件。例如: +对话框列表(itemsSelectMode为"select")的项目被点击选中时触发的事件. 例如: ``` var d = dialogs.build({ @@ -487,11 +492,11 @@ var d = dialogs.build({ ## 事件: `single_choice` -* `index` {number} 被选中的项目索引,从0开始 +* `index` {number} 被选中的项目索引, 从0开始 * `item` {Object} 被选中的项目 * `dialog` {Dialog} 对话框 -对话框单选列表(itemsSelectMode为"singleChoice")的项目被选中并点击确定时触发的事件。例如: +对话框单选列表(itemsSelectMode为"singleChoice")的项目被选中并点击确定时触发的事件. 例如: ``` var d = dialogs.build({ @@ -511,7 +516,7 @@ var d = dialogs.build({ * `items` {Array} 被选中的项目的数组 * `dialog` {Dialog} 对话框 -对话框多选列表(itemsSelectMode为"multiChoice")的项目被选中并点击确定时触发的事件。例如: +对话框多选列表(itemsSelectMode为"multiChoice")的项目被选中并点击确定时触发的事件. 例如: ``` var d = dialogs.build({ @@ -530,7 +535,7 @@ var d = dialogs.build({ * `text` {string} 输入框的内容 * `dialog` {Dialog} 对话框 -带有输入框的对话框当点击确定时会触发的事件。例如: +带有输入框的对话框当点击确定时会触发的事件. 例如: ``` dialogs.build({ @@ -548,7 +553,7 @@ dialogs.build({ * `text` {string} 输入框的内容 * `dialog` {Dialog} 对话框 -对话框的输入框的文本发生变化时会触发的事件。例如: +对话框的输入框的文本发生变化时会触发的事件. 例如: ``` dialogs.build({ @@ -565,17 +570,17 @@ dialogs.build({ * 返回 {number} -获取当前进度条的进度值,是一个整数 +获取当前进度条的进度值, 是一个整数 ## dialog.getMaxProgress() * 返回 {number} -获取当前进度条的最大进度值,是一个整数 +获取当前进度条的最大进度值, 是一个整数 ## dialog.getActionButton(action) -* `action` {string} 动作,包括: +* `action` {string} 动作, 包括: * `positive` * `negative` * `neutral` diff --git a/api/documentation.md b/api/documentation.md index b0eafcb..75fb926 100644 --- a/api/documentation.md +++ b/api/documentation.md @@ -1,70 +1,433 @@ -# 关于本文档 +# 关于文档 (About) -本文档为Auto.js的文档,解释了Auto.js各个模块的API的使用方法、作用和例子。 +AutoJs6 文档, 包含模块 API 使用方法及用例. +项目复刻 (Fork) 自 [hyb1996/AutoJs-Docs](https://github.com/hyb1996/AutoJs-Docs/) (GitHub). +项目地址: [SuperMonster003/AutoJs6-Documentation](https://github.com/SuperMonster003/AutoJs6-Documentation/) (GitHub). -文档借助Node.js的文档构建工具生成,并在github上开源(https://github.com/hyb1996/AutoJs-Docs ),目前由开发者维护。 +--- -## API稳定性 +## 文档阅读示例 -由于Auto.js处于活跃的更新和开发状态,API可能随时有变动,我们用Stability来标记模块、函数的稳定性。这些标记包括: +### 基础 -```txt -Stability: 0 - Deprecated +#### device.height -弃用的函数、模块或特性,在未来的更新中将很快会被移除或更改。应该在脚本中移除对这些函数的使用,以免后续出现意料之外的问题。 +device 表示全局对象 (这里同时也是一个模块). +".height" 表示访问 device 对象的 height 成员变量. +如 console.log(device.height) 表示在控制台打印当前设备的高度数值. + +#### colors.rgb(red, green, blue) + +colors 与 device 类似, 表示全局对象. +"rgb" 表示方法名称, ".rgb()" 表示调用 colors 的 rgb 方法, 括号内的 red 等表示方法参数. +如 console.log(colors.rgb(255, 128, 64)) 表示在控制台打印一个 RGB 分别为 255, 128 和 64 的颜色数值. + +> 注: 绝大多数情况, 文档不对 "[函数](https://developer.mozilla.org/zh-CN/docs/Glossary/Function/)" 与 "[方法](https://developer.mozilla.org/zh-CN/docs/Glossary/Method/)" 做明确区分. + +### 临时作用域对象 + +通常每个章节都以某个对象作为主题. + +例如上述 `colors.rgb(red, green, blue)` 位于 [Color - 颜色](color) 这个章节. +其中 colors 称为此章节的 "临时作用域对象", +它可能是一个对象, 函数, 甚至 "类", 在文档中使用 __`橙色粗体`__ 表示. + +列举其后续的相关方法及属性时, 将不再重复书写对象本身: + +

colors

+ +[m] rgb + +rgb(red, green, blue) + +... ... + +上述 `rgb` 表示 `colors.rgb`. + +### 参数类型 + +#### colors.rgb(red, green, blue) + +- **red** { [number](dataTypes#number) } +- **green** { [number](dataTypes#number) } +- **blue** { [number](dataTypes#number) } + +参数后的 "{}" 内包含其类型. +上述示例表示需要传入三个 [number](dataTypes#number) 类型的参数. +如 colors.rgb(255, 128, 64) 合法, 而 colors.rgb("abc", 128, 64) 将可能导致非预期结果或出现异常. + +> 注: 点击类型对应的超链接 (如有) 可跳转至类型详情页面. + +### 返回值类型 + +#### colors.rgb(red, green, blue) + +- **red** { [number](dataTypes#number) } +- **green** { [number](dataTypes#number) } +- **blue** { [number](dataTypes#number) } +- **returns** { [number](dataTypes#number) } + +returns 后的 "{}" 内包含返回值类型. +上述示例表示 colors.rgb 方法调用后将返回 [number](dataTypes#number) 类型数据. + +### 属性类型 + +#### colors.RED + +- { [number](dataTypes#number) } + +属性类型包裹在一对花括号中. +上述示例表示 colors 的 RED 属性是 [number](dataTypes#number) 类型数据. + +对象字面量形式的类型则用一对双花括号表示: + +- {{ name: [string](dataTypes#string); age: [number](dataTypes#number) }} + +多行形式: + +- {{ +- -- name: [string](dataTypes#string); +- -- age: [number](dataTypes#number); +- }} + +一个符合上述示例期望的变量: + +```js +let o = { name: "David", age: 13 }; +``` + +可存取的属性在读取时如果有非 undefined 默认值, 则以一对方括号表示: + +- [ 1200 ] { [number](dataTypes#number) } + +上述示例表示一个默认值为 1200 的可存取属性. + +以一对双方括号表示常量: + +- [[ 0.5 ]] { [number](dataTypes#number) } + +上述示例表示一个值为 0.5 的常量属性. + +### 方法签名 + +形如上述 [返回值类型](#返回值类型) 小节的示例, +包含 [ 方法名称 + 参数类型 + 返回值类型 ] 的标志符, 称为 "方法签名". + +> 注: 上述 "方法签名" 定义只用于辅助读者对文档的理解, 并不保证名词解释的合理性. + +### 方法描述 + +#### colors.rgb(red, green, blue) + +- __red__ - R (红色) 通道数值 [ A ] +- __green__ - G (绿色) 通道数值 [ A ] +- __blue__ - B (蓝色) 通道数值 [ A ] +- __@return__ - 颜色数值 [ B ] + +获取 R/G/B 通道组合后的颜色数值. [ C ] + +```js +[ D ] +colors.rgb(255, 128, 64); // -32704 +colors.rgb(255, 128, 64) === 0xFFFF8040 - Math.pow(2, 32); // true +colors.rgb(255, 128, 64) === colors.toInt("#FFFF8040"); // true +colors.rgb(255, 128, 64) === colors.toInt("#FF8040"); // true +``` + +上述示例包含的字母标注含义: + +- [ A ] - 参数描述 +- [ B ] - 方法返回值描述 +- [ C ] - 方法描述 +- [ D ] - 方法使用示例 + +### 可变参数 + +#### files.join(parent, ...child) + +上述示例的 child 参数是 "可变参数", 也称为 "可变长参数" 或 "变长参数". +可变参数可传入任意个 (包括 0 个) 参数: + +```js +let p = files.getSdcardPath(); +files.join(p); /* 0 个可变参数 */ +files.join(p, 'a'); /* 1 个可变参数 */ +files.join(p, 'a', 'b', 'c', 'd'); /* 4 个可变参数 */ ``` -```txt -Stability: 1 - Experimental +文档采用 JSDoc 标准标注可变参数, 需额外注意 JSDoc 的尾数组标识代表容器, 用于容纳展开后的参数: -实验性的函数、模块或特性,在未来的更新中可能会更改或移除。应该谨慎使用这些函数或模块,或者仅用作临时或试验用途。 +```js +/** + * @param {number} x + * @param {number} y + * @param {...number[]} others + */ +function sum(x, y, others) { + /* ... */ +} ``` -```txt -Stability: 2 - Stable +上述示例 others 参数为可变参数, 其中 "...number[]" 代表 others 期望的参数类型为 number, 而非 number[], 最后的 "[]" 代表 "..." 的容器, "..." 与 "[]" 是配对出现的. -稳定的函数、模块或特性,在未来的更新中这些模块已有的函数一般不会被更改,会保证后向兼容性。 +```js +/** + * @param {number} x + * @param {number} y + * @param {...number[][]} others + */ +function sum(x, y, others) { + /* ... */ +} ``` -## 如何阅读本文档 +上述示例 others 期望的参数类型为 number[], 而非 number[][], 同样最后的 "[]" 代表 "..." 的容器. -先看一个例子,下面是[基于控件的操作模拟](coordinates-based-automation.html)的章节中input函数的部分说明。 +```js +/** + * @param {number} x + * @param {number} y + * @param {...number} others + */ +function sum(x, y, others) { + /* ... */ +} +``` + +上述示例 others 的参数类型标识方法 "...number" 也是合法的, 它其实是 "...number[]" 的省略形式. +文档为了避免歧义, 将全部采用完整写法. -## input([i, ]text) +作为强调, "...(SomeType)[]" 这样的可变参数表示方法, 需要把 "..." 和 "[]" 视为一个整体, 中间部分才是期望的参数类型. -* `i` {number} 表示要输入的为第i + 1个输入框 -* `text` {string} 要输入的文本 +### 可选参数 -input表示函数名,括号内的`[i, ]text`为函数的参数。下面是参数列表,"number"表示参数i的类型为数值,"string"表示参数text的类型为字符串。 +#### device.vibrate(text, delay?) -例如input(1, "啦啦啦"),执行这个语句会在屏幕上的第2个输入框处输入"啦啦啦"。 +上述示例的 delay 参数是可选的 (以 "?" 标注). +因此以下调用方式均被支持: -方括号[ ]表示参数为可选参数。也就是说,可以省略i直接调用input。例如input("嘿嘿嘿"),按照文档,这个语句会在屏幕上所有输入框输入"嘿嘿嘿"。 +```js +device.vibrate("hello", 2e3); /* 两秒钟延迟. */ +device.vibrate("hello"); /* 无延迟. */ +``` -调用有可选参数的函数时请不要写上方括号。 +可选参数描述时会以 "[]" 标注: + +- **[ delay ]** { [number](dataTypes#number) } + +如果可选参数包含默认值, 则会以 "=" 标注: + +- **[ delay = 0 ]** { [number](dataTypes#number) } + +详见下述 [参数默认值](#参数默认值) + +### 参数默认值 + +#### device.vibrate(text, delay?) + +- **text** { [string](dataTypes#string) } - 需转换为摩斯码的文本 +- **[ delay = 0 ]** { [number](dataTypes#number) } - 震动延迟 +- **returns** { [void](dataTypes#void) } + +上述示例的 delay 参数是可选的 (以 "?" 标注) 且包含默认值 (以 "=" 标注). +因此以下两种调用方式等效: + +```js +device.vibrate("hello"); +device.vibrate("hello", 0); +``` + +> 注: 上述示例的方法签名 (含默认值标注) 在 TypeScript 中并不合法, 此类签名仅限在文档中使用. +> +> 注: 以 "=" 标注的参数一定是可选的, 此时参数的 "?" 标注可能被省略, 尤其在重载签名拆写的情况下. +> 详情参阅下文的 "方法重载". + +### 方法重载 + +__`Overload 1/17`__ + +#### pickup(selector, compass, resultType) + +__`Overload 2/17`__ + +#### pickup(selector, compass) + +__`Overload 3/17`__ + +#### pickup(selector, resultType) + +... ... + +__`Overload 16/17`__ + +#### pickup(root, selector) + +__`Overload 17/17`__ + +#### pickup() + +包含 "Overload m/n" 标签的方法, 表示重载方法的序数及总量. +如 "Overload 2/3" 表示当前方法签名描述第 2 个重载方法, 总计 3 个, +而 "Overload 5-6/17" 表示当前方法签名涵盖第 5 及 第 6 个重载方法, 总计 17 个. + +重载方法可被简化: + +```text +/* 拆写. */ +device.vibrate(text) +device.vibrate(text, delay) + +/* 合写 (简化). */ +device.vibrate(text, delay?) + +/* 可选参数通常会标注默认值. */ +device.vibrate(text, delay?) +· [ delay = 0 ] { number } + +/* 即使没有 "?" 标注 (针对拆写). */ +device.vibrate(text, delay) +· [ delay = 0 ] { number } +``` + +多数情况下, 文档采用拆写的方式描述重载方法. + +### 方法全局化 + +__`Global`__ + +#### images.requestScreenCapture(landscape) + +包含 "Global" 标签的方法, 表示支持全局化使用, 可省略模块对象调用. +因此以下两种调用方式等效: + +```js +images.requestScreenCapture(false); +requestScreenCapture(false); +``` + +### 方法标签 + +用于简便表示方法的特性: + +- `Global`: [方法全局化](#方法全局化) (可省略模块对象直接调用). +- `Overload 2/3`: [方法重载](#方法重载) [ 第 2 个, 共 3 个 ]. +- `Non-UI`: 方法不能在 UI 模式下调用. +- `6.2.0`: 方法对 AutoJs6 的版本要求 [ 不低于 6.2.0 ]. +- `[6.2.0]`: 与原同名方法相比, 方法的功能, 结果, 签名或使用方式发生变更的起始版本. +- `API>=29`: 方法对 [API 级别](apiLevel) 的要求 [ 不低于 29 ], 当不满足时不抛出异常. +- `API>=29!`: 方法对 [API 级别](apiLevel) 的要求 [ 不低于 29 ], 当不满足时将抛出异常. +- `A11Y`: 方法依赖无障碍服务. +- `A11Y?`: 方法可能会依赖无障碍服务. +- `Async`: 异步执行的方法. +- `Async?`: 可能异步执行的方法 (通过参数控制). +- `Enum`: 枚举类. +- `CONSTANT`: 常量. +- `DEPRECATED`: 已弃用的属性或方法. +- `xProto`: 针对原型的内置对象扩展. +- `xObject`: 针对对象的内置对象扩展. +- `xAlias`: 内置对象扩展时使用不同的方法或属性名称 (别名). + +### 对象标签 + +用于简便表示对象的属性: + +- \[m]: 普通对象方法或类静态成员方法. + - 例如在 `images` 作为 [临时作用域对象](#临时作用域对象) 时: + - `[m] captureScreen` 代表 `images.captureScreen` 方法. +- \[m+]: 具有扩展属性的对象方法. + - 如 auto 本身是一个方法 (或称函数), waitFor 是 auto 的一个扩展方法. + - 以下两种调用方式均可用: `auto()` 及 `auto.waitFor()`. +- \[p]: 属性. + - 例如在 `device` 作为 [临时作用域对象](#临时作用域对象) 时: + - `[p] height` 代表 `device.height` 属性, 而非方法. + - 此标签对 [ Getter / Setter / "类" 属性 / 对象属性 / 方法扩展属性 ] 等不作区分. +- \[I]: Java 接口. +- \[C]: Java 类或 JavaScript 构造函数. +- \[c]: Java 类的构造方法. +- \[m!]: 抽象方法 (针对接口及抽象类). +- \[m=]: 包含默认实现的抽象方法 (针对接口). +- \[m#]: 类的实例成员方法. + - 类的静态成员方法用 [m] 标签标记. + - 例如对于类 `B`, 它有一个实例 `b` (可能通过 `new B()` 等方式获得), + - `[m#] foo` 和 `[m] bar` 的调用方式分别为 + - `b.foo()` 和 `B.bar()`. +- \[p#]: 类的实例成员属性. + - 类的静态成员属性依然用 [p] 标签标记. + - 例如对于类 `F`, 它有一个实例 `f` (可能通过 `new F()` 等方式获得), + - `[p#] foo` 和 `[p] bar` 的调用方式分别为 + - `f.foo` 和 `F.bar`. +- \[@]: 代表 [临时作用域对象](#临时作用域对象) 自身. + - 例如在同一个章节中 + - `[@] apple` [1] + - `apple(c)` [2] + - `[m] getColor` [3] + - `getColor()` [4] + - `[@] banana` [5] + - `[m] banana` [6] + - `banana(x)` [7] + - `banana(x, y)` [8] + - 这个章节有两个 [临时作用域对象](#临时作用域对象), apple 和 banana, 对应 `[1]` 和 `[5]`. + - `[2]` 代表 apple 自身可被调用, 且调用方式为 `apple(c)`, 其中 "c" 为参数. + - `[3]` 代表 apple 的一个方法, 名称为 "getColor", + - 由 `[4]` 得知, 其调用方式为 `apple.getColor()`. + - 注意 `[6]` 与 `[2]` 不同: + - [`6`] 代表 banana 的一个方法, 名称为 "banana", + - 由 `[7]` 和 `[8]` 得知, 其调用方式有两种, + - `banana.banana(x)` 和 `banana.banana(x, y)`. + +### 成员访问 + +成员访问用 "." 表示调用关系, 包括 "类" 静态成员访问, 对象成员访问等. +而实例成员访问则需要 "类" 的实例才能访问, 用 "#" 表示调用关系. +例如 JavaScript 的 Number 本身是一个 "类", 可用的成员访问方式如下: + +```js +Number(2); /* 作为普通函数使用, 无成员访问. */ +Number.EPSILON; /* "类" 静态成员访问, 用 "Number.EPSILON" 标识, 标签为 "[p]". */ +new Number(2); /* 创建 "类" 实例, 无成员访问. */ +new Number(2).toFixed(0); /* 实例成员访问, 用 "Number#toFixed(number)" 标识, 标签为 "[m#]". */ +``` + +实例成员访问示例: + +

UiObject

+ +[m#] bounds() + +```js +/* 正确访问示例 */ + +let w = pickup(/.+/); /* w 是 UiObject 的实例. */ +if (w !== null) { + console.log(w.bounds()); /* 访问 UiObject 实例的 bounds 方法. */ +} + +/* 错误访问示例 */ + +importClass(org.autojs.autojs.core.automator.UiObject); +console.log(UiObject.bounds()); /* 访问的是类 UiObject 的静态方法 bounds. */ +``` -我们再看第二个例子。图片和图色处理中detectsColor函数的部分说明。 +### 模板参数 -## images.detectsColor(image, color, x, y[, threshold = 16, algorithm = "diff"]) +#### foo.bar(a, b) -* `image` {Image} 图片 -* `color` {number} | {string} 要检测的颜色 -* `x` {number} 要检测的位置横坐标 -* `y` {number} 要检测的位置纵坐标 -* `threshold` {number} 颜色相似度临界值,默认为16。取值范围为0~255。 -* `algorithm` {string} 颜色匹配算法,包括: - * "equal": 相等匹配,只有与给定颜色color完全相等时才匹配。 - * "diff": 差值匹配。与给定颜色的R、G、B差的绝对值之和小于threshold时匹配。 - * "rgb": rgb欧拉距离相似度。与给定颜色color的rgb欧拉距离小于等于threshold时匹配。 +- **a** { [T](dataTypes#generic) } +- **b** { [number](dataTypes#number) } +- **returns** { [T](dataTypes#generic) } +- **template** { [T](dataTypes#generic) } - * "rgb+": 加权rgb欧拉距离匹配([LAB Delta E](https://en.wikipedia.org/wiki/Color_difference))。 - * "hs": hs欧拉距离匹配。hs为HSV空间的色调值。 +returns 后的 "{}" 内包含返回值类型. +上述示例表示 colors.rgb 方法调用后将返回 [number](dataTypes#number) 类型数据. -同样地,`[, threshold = 16, algorithm = "rgb"]`为可选参数,并且,等于号=后面的值为参数的默认值。也就是如果不指定该参数,则该参数将会为这个值。 +> 参阅: [泛型](dataTypes#generic) -例如 `images.detectsColor(captureScreen(), "#112233", 100, 200)` 相当于 `images.detectsColor(captureScreen(), "#112233", 100, 200, 16, "rgb")`, -而`images.detectsColor(captureScreen(), "#112233", 100, 200, 64)` 相当于`images.detectsColor(captureScreen(), "#112233", 100, 200, 64, "rgb")`。 +## 声明 -调用有可选参数及默认值的函数时请不要写上方括号和等于号。 \ No newline at end of file +当前项目 (文档) 及 [AutoJs6](https://github.com/SuperMonster003/AutoJs6/) (App) 均为二次开发. +相对于 [原始 App](https://github.com/hyb1996/Auto.js/), 二次开发的 App 中会增加或修改部分模块功能. +相对于 [原始文档](https://github.com/hyb1996/AutoJs-Docs/), 二次开发的文档将进行部分增删或重新编写. +开发者无法保证对 API 的完全理解及文档的无纰漏撰写. +如有任何不当之处, 欢迎提交 [Issue](https://github.com/SuperMonster003/AutoJs6-Documentation/issues/) 或 [PR](https://github.com/SuperMonster003/AutoJs6-Documentation/pulls/). \ No newline at end of file diff --git a/api/e4x.md b/api/e4x.md new file mode 100644 index 0000000..b7e6bc4 --- /dev/null +++ b/api/e4x.md @@ -0,0 +1,31 @@ +# E4X + +> 注: E4X __已弃用__. +> +> 尽管少数浏览器依然支持, 但随着件更新正逐步被废除, 应尽量避免使用. +> AutoJs6 使用 Rhino 引擎, 因此依然保持对 E4X 的支持. +> +> 本章节仅用于技术概念的归档及溯源, 不建议用于脚本编写. + +ECMAScript for XML (E4X) 是对 ECMAScript 的扩展, 增加对 XML 的内在支持. +其目标是在访问 XML 文档时, 提供一种更直观且语法更简洁的的 DOM 接口, 成为处理 XML 文档的新方式. + +```e4x +var sales = + + + + ; + +alert( sales.item.(@type == "carrot").@quantity ); +alert( sales.@vendor ); +for each( var price in sales..@price ) { + alert( price ); +} +delete sales.item[0]; +sales.item += ; +sales.item.(@type == "oranges").@quantity = 4; +``` + +> 参阅: [Wikipedia (英)](https://en.wikipedia.org/wiki/ECMAScript_for_XML) / [Wikipedia (中)](https://zh.wikipedia.org/wiki/E4X) +> 替代: [DOMParser](https://developer.mozilla.org/zh-CN/docs/Web/API/DOMParser) / [DOMSerializer](https://www.npmjs.com/package/dom-serializer) \ No newline at end of file diff --git a/api/engines.md b/api/engines.md index 5a5b515..995e12f 100644 --- a/api/engines.md +++ b/api/engines.md @@ -1,10 +1,15 @@ -# Engines +# 引擎 (Engines) -> Stability: 2 - Stable +--- -engines模块包含了一些与脚本环境、脚本运行、脚本引擎有关的函数,包括运行其他脚本,关闭脚本等。 +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

-例如,获取脚本所在目录: +--- + +engines模块包含了一些与脚本环境、脚本运行、脚本引擎有关的函数, 包括运行其他脚本, 关闭脚本等. + +例如, 获取脚本所在目录: ``` toast(engines.myEngine().cwd()); @@ -12,17 +17,17 @@ toast(engines.myEngine().cwd()); ## engines.execScript(name, script[, config]) -* `name` {string} 要运行的脚本名称。这个名称和文件名称无关,只是在任务管理中显示的名称。 -* `script` {string} 要运行的脚本内容。 +* `name` {string} 要运行的脚本名称. 这个名称和文件名称无关, 只是在任务管理中显示的名称. +* `script` {string} 要运行的脚本内容. * `config` {Object} 运行配置项 - * `delay` {number} 延迟执行的毫秒数,默认为0 - * `loopTimes` {number} 循环运行次数,默认为1。0为无限循环。 - * `interval` {number} 循环运行时两次运行之间的时间间隔,默认为0 - * `path` {Array} | {string} 指定脚本运行的目录。这些路径会用于require时寻找模块文件。 + * `delay` {number} 延迟执行的毫秒数, 默认为0 + * `loopTimes` {number} 循环运行次数, 默认为1. 0为无限循环. + * `interval` {number} 循环运行时两次运行之间的时间间隔, 默认为0 + * `path` {Array} | {string} 指定脚本运行的目录. 这些路径会用于require时寻找模块文件. -在新的脚本环境中运行脚本script。返回一个[ScriptExectuion](#engines_scriptexecution)对象。 +在新的脚本环境中运行脚本script. 返回一个[ScriptExectuion](#engines_scriptexecution)对象. -所谓新的脚本环境,指定是,脚本中的变量和原脚本的变量是不共享的,并且,脚本会在新的线程中运行。 +所谓新的脚本环境, 指定是, 脚本中的变量和原脚本的变量是不共享的, 并且, 脚本会在新的线程中运行. 最简单的例子如下: @@ -30,27 +35,27 @@ toast(engines.myEngine().cwd()); engines.execScript("hello world", "toast('hello world')"); ``` -如果要循环运行,则: +如果要循环运行, 则: ``` -//每隔3秒运行一次脚本,循环10次 +//每隔3秒运行一次脚本, 循环10次 engines.execScript("hello world", "toast('hello world')", { loopTimes: 10, interval: 3000 }); ``` -用字符串来编写脚本非常不方便,可以结合 `Function.toString()`的方法来执行特定函数: +用字符串来编写脚本非常不方便, 可以结合 `Function.toString()`的方法来执行特定函数: ``` function helloWorld(){ - //注意,这里的变量和脚本主体的变量并不共享 + //注意, 这里的变量和脚本主体的变量并不共享 toast("hello world"); } engines.execScript("hello world", "helloWorld();\n" + helloWorld.toString()); ``` -如果要传递变量,则可以把这些封装成一个函数: +如果要传递变量, 则可以把这些封装成一个函数: ``` function exec(action, args){ @@ -58,7 +63,7 @@ function exec(action, args){ engines.execScript(action.name, action.name + "(" + JSON.stringify(args) + ");\n" + action.toString()); } -//要执行的函数,是一个简单的加法 +//要执行的函数, 是一个简单的加法 function add(args){ toast(args.a + args.b); } @@ -69,14 +74,14 @@ exec(add, {a: 1, b:2}); ## engines.execScriptFile(path[, config]) -* `path` {string} 要运行的脚本路径。 +* `path` {string} 要运行的脚本路径. * `config` {Object} 运行配置项 - * `delay` {number} 延迟执行的毫秒数,默认为0 - * `loopTimes` {number} 循环运行次数,默认为1。0为无限循环。 - * `interval` {number} 循环运行时两次运行之间的时间间隔,默认为0 - * `path` {Array} | {string} 指定脚本运行的目录。这些路径会用于require时寻找模块文件。 + * `delay` {number} 延迟执行的毫秒数, 默认为0 + * `loopTimes` {number} 循环运行次数, 默认为1. 0为无限循环. + * `interval` {number} 循环运行时两次运行之间的时间间隔, 默认为0 + * `path` {Array} | {string} 指定脚本运行的目录. 这些路径会用于require时寻找模块文件. -在新的脚本环境中运行脚本文件path。返回一个[ScriptExecution](#ScriptExecution)对象。 +在新的脚本环境中运行脚本文件path. 返回一个[ScriptExecution](#ScriptExecution)对象. ``` engines.execScriptFile("/sdcard/脚本/1.js"); @@ -84,14 +89,14 @@ engines.execScriptFile("/sdcard/脚本/1.js"); ## engines.execAutoFile(path[, config]) -* `path` {string} 要运行的录制文件路径。 +* `path` {string} 要运行的录制文件路径. * `config` {Object} 运行配置项 - * `delay` {number} 延迟执行的毫秒数,默认为0 - * `loopTimes` {number} 循环运行次数,默认为1。0为无限循环。 - * `interval` {number} 循环运行时两次运行之间的时间间隔,默认为0 - * `path` {Array} | {string} 指定脚本运行的目录。这些路径会用于require时寻找模块文件。 + * `delay` {number} 延迟执行的毫秒数, 默认为0 + * `loopTimes` {number} 循环运行次数, 默认为1. 0为无限循环. + * `interval` {number} 循环运行时两次运行之间的时间间隔, 默认为0 + * `path` {Array} | {string} 指定脚本运行的目录. 这些路径会用于require时寻找模块文件. -在新的脚本环境中运行录制文件path。返回一个[ScriptExecution](#ScriptExecution)对象。 +在新的脚本环境中运行录制文件path. 返回一个[ScriptExecution](#ScriptExecution)对象. ``` engines.execAutoFile("/sdcard/脚本/1.auto"); @@ -99,30 +104,30 @@ engines.execAutoFile("/sdcard/脚本/1.auto"); ## engines.stopAll() -停止所有正在运行的脚本。包括当前脚本自身。 +停止所有正在运行的脚本. 包括当前脚本自身. ## engines.stopAllAndToast() -停止所有正在运行的脚本并显示停止的脚本数量。包括当前脚本自身。 +停止所有正在运行的脚本并显示停止的脚本数量. 包括当前脚本自身. ## engines.myEngine() 返回当前脚本的脚本引擎对象([ScriptEngine](#engines_scriptengine)) **[v4.1.0新增]** -特别的,该对象可以通过`execArgv`来获取他的运行参数,包括外部参数、intent等。例如: +特别的, 该对象可以通过`execArgv`来获取他的运行参数, 包括外部参数、intent等. 例如: ``` log(engines.myEngine().execArgv); ``` -普通脚本的运行参数通常为空,通过定时任务的广播启动的则可以获取到启动的intent。 +普通脚本的运行参数通常为空, 通过定时任务的广播启动的则可以获取到启动的intent. ## engines.all() * 返回 {Array} -返回当前所有正在运行的脚本的脚本引擎[ScriptEngine](#engines_scriptengine)的数组。 +返回当前所有正在运行的脚本的脚本引擎[ScriptEngine](#engines_scriptengine)的数组. ``` log(engines.all()); @@ -130,9 +135,9 @@ log(engines.all()); # ScriptExecution -执行脚本时返回的对象,可以通过他获取执行的引擎、配置等,也可以停止这个执行。 +执行脚本时返回的对象, 可以通过他获取执行的引擎、配置等, 也可以停止这个执行. -要停止这个脚本的执行,使用`exectuion.getEngine().forceStop()`. +要停止这个脚本的执行, 使用`exectuion.getEngine().forceStop()`. ## ScriptExecution.getEngine() @@ -144,23 +149,23 @@ log(engines.all()); # ScriptEngine -脚本引擎对象。 +脚本引擎对象. ## ScriptEngine.forceStop() -停止脚本引擎的执行。 +停止脚本引擎的执行. ## ScriptEngine.cwd() * 返回 {string} -返回脚本执行的路径。对于一个脚本文件而言为这个脚本所在的文件夹;对于其他脚本,例如字符串脚本,则为`null`或者执行时的设置值。 +返回脚本执行的路径. 对于一个脚本文件而言为这个脚本所在的文件夹;对于其他脚本, 例如字符串脚本, 则为`null`或者执行时的设置值. ## ScriptEngine.getSource() * 返回 [ScriptSource](#engines_scriptsource) -返回当前脚本引擎正在执行的脚本对象。 +返回当前脚本引擎正在执行的脚本对象. ``` log(engines.myEngine().getSource()); @@ -171,7 +176,7 @@ log(engines.myEngine().getSource()); * `eventName` {string} 事件名称 * `...args` {any} 事件参数 -向该脚本引擎发送一个事件,该事件可以在该脚本引擎对应的脚本的events模块监听到并在脚本主线程执行事件处理。 +向该脚本引擎发送一个事件, 该事件可以在该脚本引擎对应的脚本的events模块监听到并在脚本主线程执行事件处理. 例如脚本receiver.js的内容如下: @@ -197,7 +202,7 @@ e.getEngine().emit("say", "你好"); # ScriptConfig -脚本执行时的配置。 +脚本执行时的配置. ## delay @@ -221,7 +226,7 @@ e.getEngine().emit("say", "你好"); * 返回 {Array} -返回一个字符串数组表示脚本运行时模块寻找的路径。 +返回一个字符串数组表示脚本运行时模块寻找的路径. diff --git a/api/events.md b/api/events.md index 1fef8f8..a35aceb 100644 --- a/api/events.md +++ b/api/events.md @@ -1,12 +1,17 @@ -# Events +# 事件监听 (Events) -> Stability: 2 - Stable +--- -events模块提供了监听手机通知、按键、触摸的接口。您可以用他配合自动操作函数完成自动化工作。 +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

-events本身是一个[EventEmiiter](#events_eventemitter), 但内置了一些事件、包括按键事件、通知事件、Toast事件等。 +--- -需要注意的是,事件的处理是单线程的,并且仍然在原线程执行,如果脚本主体或者其他事件处理中有耗时操作、轮询等,则事件将无法得到及时处理(会进入事件队列等待脚本主体或其他事件处理完成才执行)。例如: +events模块提供了监听手机通知、按键、触摸的接口. 您可以用他配合自动操作函数完成自动化工作. + +events本身是一个[EventEmiiter](#events_eventemitter), 但内置了一些事件、包括按键事件、通知事件、Toast事件等. + +需要注意的是, 事件的处理是单线程的, 并且仍然在原线程执行, 如果脚本主体或者其他事件处理中有耗时操作、轮询等, 则事件将无法得到及时处理(会进入事件队列等待脚本主体或其他事件处理完成才执行). 例如: ``` auto(); @@ -22,22 +27,22 @@ while(true){ ## events.emitter() -返回一个新的[EventEmitter](#events_eventemitter)。这个EventEmitter没有内置任何事件。 +返回一个新的[EventEmitter](#events_eventemitter). 这个EventEmitter没有内置任何事件. ## events.observeKey() -启用按键监听,例如音量键、Home键。按键监听使用无障碍服务实现,如果无障碍服务未启用会抛出异常并提示开启。 +启用按键监听, 例如音量键、Home键. 按键监听使用无障碍服务实现, 如果无障碍服务未启用会抛出异常并提示开启. -只有这个函数成功执行后, `onKeyDown`, `onKeyUp`等按键事件的监听才有效。 +只有这个函数成功执行后, `onKeyDown`, `onKeyUp`等按键事件的监听才有效. -该函数在安卓4.3以上才能使用。 +该函数在安卓4.3以上才能使用. ## events.onKeyDown(keyName, listener) * `keyName` {string} 要监听的按键名称 -* `listener` {Function} 按键监听器。参数为一个[KeyEvent](#events_keyevent)。 +* `listener` {Function} 按键监听器. 参数为一个[KeyEvent](#events_keyevent). -注册一个按键监听函数,当有keyName对应的按键被按下会调用该函数。可用的按键名称参见[Keys](#events_keys)。 +注册一个按键监听函数, 当有keyName对应的按键被按下会调用该函数. 可用的按键名称参见[Keys](#events_keys). 例如: @@ -58,11 +63,11 @@ events.onKeyDown("menu", function(event){ ## events.onKeyUp(keyName, listener) * `keyName` {string} 要监听的按键名称 -* `listener` {Function} 按键监听器。参数为一个[KeyEvent](#events_keyevent)。 +* `listener` {Function} 按键监听器. 参数为一个[KeyEvent](#events_keyevent). -注册一个按键监听函数,当有keyName对应的按键弹起会调用该函数。可用的按键名称参见[Keys](#events_keys)。 +注册一个按键监听函数, 当有keyName对应的按键弹起会调用该函数. 可用的按键名称参见[Keys](#events_keys). -一次完整的按键动作包括了按键按下和弹起。按下事件会在手指按下一个按键的"瞬间"触发, 弹起事件则在手指放开这个按键时触发。 +一次完整的按键动作包括了按键按下和弹起. 按下事件会在手指按下一个按键的"瞬间"触发, 弹起事件则在手指放开这个按键时触发. 例如: @@ -83,45 +88,45 @@ events.onKeyDown("home", function(event){ ## events.onceKeyDown(keyName, listener) * `keyName` {string} 要监听的按键名称 -* `listener` {Function} 按键监听器。参数为一个[KeyEvent](#events_keyevent) +* `listener` {Function} 按键监听器. 参数为一个[KeyEvent](#events_keyevent) -注册一个按键监听函数,当有keyName对应的按键被按下时会调用该函数,之后会注销该按键监听器。 +注册一个按键监听函数, 当有keyName对应的按键被按下时会调用该函数, 之后会注销该按键监听器. -也就是listener只有在onceKeyDown调用后的第一次按键事件被调用一次。 +也就是listener只有在onceKeyDown调用后的第一次按键事件被调用一次. ## events.onceKeyUp(keyName, listener) * `keyName` {string} 要监听的按键名称 -* `listener` {Function} 按键监听器。参数为一个[KeyEvent](#events_keyevent) +* `listener` {Function} 按键监听器. 参数为一个[KeyEvent](#events_keyevent) -注册一个按键监听函数,当有keyName对应的按键弹起时会调用该函数,之后会注销该按键监听器。 +注册一个按键监听函数, 当有keyName对应的按键弹起时会调用该函数, 之后会注销该按键监听器. -也就是listener只有在onceKeyUp调用后的第一次按键事件被调用一次。 +也就是listener只有在onceKeyUp调用后的第一次按键事件被调用一次. ## events.removeAllKeyDownListeners(keyName) * `keyName` {string} 按键名称 -删除该按键的KeyDown(按下)事件的所有监听。 +删除该按键的KeyDown(按下)事件的所有监听. ## events.removeAllKeyUpListeners(keyName) * `keyName` {string} 按键名称 -删除该按键的KeyUp(弹起)事件的所有监听。 +删除该按键的KeyUp(弹起)事件的所有监听. ## events.setKeyInterceptionEnabled([key, ]enabled) * `enabled` {boolean} * `key` {string} 要屏蔽的按键 -设置按键屏蔽是否启用。所谓按键屏蔽指的是,屏蔽原有按键的功能,例如使得音量键不再能调节音量,但此时仍然能通过按键事件监听按键。 +设置按键屏蔽是否启用. 所谓按键屏蔽指的是, 屏蔽原有按键的功能, 例如使得音量键不再能调节音量, 但此时仍然能通过按键事件监听按键. -如果不加参数key则会屏蔽所有按键。 +如果不加参数key则会屏蔽所有按键. -例如,调用`events.setKeyInterceptionEnabled(true)`会使系统的音量、Home、返回等键不再具有调节音量、回到主页、返回的作用,但此时仍然能通过按键事件监听按键。 +例如, 调用`events.setKeyInterceptionEnabled(true)`会使系统的音量、Home、返回等键不再具有调节音量、回到主页、返回的作用, 但此时仍然能通过按键事件监听按键. -该函数通常于按键监听结合,例如想监听音量键并使音量键按下时不弹出音量调节框则为: +该函数通常于按键监听结合, 例如想监听音量键并使音量键按下时不弹出音量调节框则为: ``` events.setKeyInterceptionEnabled("volume_up", true); @@ -131,35 +136,35 @@ events.onKeyDown("volume_up", ()=>{ }); ``` -只要有一个脚本屏蔽了某个按键,该按键便会被屏蔽;当脚本退出时,会自动解除所有按键屏蔽。 +只要有一个脚本屏蔽了某个按键, 该按键便会被屏蔽;当脚本退出时, 会自动解除所有按键屏蔽. ## events.observeTouch() -启用屏幕触摸监听。(需要root权限) +启用屏幕触摸监听. (需要root权限) -只有这个函数被成功执行后, 触摸事件的监听才有效。 +只有这个函数被成功执行后, 触摸事件的监听才有效. -没有root权限调用该函数则什么也不会发生。 +没有root权限调用该函数则什么也不会发生. ## events.setTouchEventTimeout(timeout) -* `timeout` {number} 两个触摸事件的最小间隔。单位毫秒。默认为10毫秒。如果number小于0,视为0处理。 +* `timeout` {number} 两个触摸事件的最小间隔. 单位毫秒. 默认为10毫秒. 如果number小于0, 视为0处理. -设置两个触摸事件分发的最小时间间隔。 +设置两个触摸事件分发的最小时间间隔. -例如间隔为10毫秒的话,前一个触摸事件发生并被注册的监听器处理后,至少要过10毫秒才能分发和处理下一个触摸事件,这10毫秒之间的触摸将会被忽略。 +例如间隔为10毫秒的话, 前一个触摸事件发生并被注册的监听器处理后, 至少要过10毫秒才能分发和处理下一个触摸事件, 这10毫秒之间的触摸将会被忽略. -建议在满足需要的情况下尽量提高这个间隔。一个简单滑动动作可能会连续触发上百个触摸事件,如果timeout设置过低可能造成事件拥堵。强烈建议不要设置timeout为0。 +建议在满足需要的情况下尽量提高这个间隔. 一个简单滑动动作可能会连续触发上百个触摸事件, 如果timeout设置过低可能造成事件拥堵. 强烈建议不要设置timeout为0. ## events.getTouchEventTimeout() -返回触摸事件的最小时间间隔。 +返回触摸事件的最小时间间隔. ## events.onTouch(listener) -* `listener` {Function} 参数为[Point](images.html#images_point)的函数 +* `listener` {Function} 参数为[Point](images#images_point)的函数 -注册一个触摸监听函数。相当于`on("touch", listener)`。 +注册一个触摸监听函数. 相当于`on("touch", listener)`. 例如: @@ -175,14 +180,14 @@ events.onTouch(function(p){ ## events.removeAllTouchListeners() -删除所有事件监听函数。 +删除所有事件监听函数. ## 事件: 'key' * `keyCode` {number} 键值 * `event` {KeyEvent} 事件 -当有按键被按下或弹起时会触发该事件。 +当有按键被按下或弹起时会触发该事件. 例如: ``` @@ -218,7 +223,7 @@ events.on("key", function(keyCode, event){ * `keyCode` {number} 键值 * `event` {KeyEvent} 事件 -当有按键被按下时会触发该事件。 +当有按键被按下时会触发该事件. ``` auto(); @@ -233,7 +238,7 @@ events.on("key_down", function(keyCode, event){ * `keyCode` {number} 键值 * `event` {KeyEvent} 事件 -当有按键弹起时会触发该事件。 +当有按键弹起时会触发该事件. ``` auto(); @@ -245,10 +250,10 @@ events.on("key_up", function(keyCode, event){ ## 事件: 'exit` -当脚本正常或者异常退出时会触发该事件。事件处理中如果有异常抛出,则立即中止exit事件的处理(即使exit事件有多个处理函数)并在控制台和日志中打印该异常。 +当脚本正常或者异常退出时会触发该事件. 事件处理中如果有异常抛出, 则立即中止exit事件的处理(即使exit事件有多个处理函数)并在控制台和日志中打印该异常. -一个脚本停止运行时,会关闭该脚本的所有悬浮窗,触发exit事件,之后再回收资源。如果exit事件的处理中有死循环,则后续资源无法得到及时回收。 -此时脚本会停留在任务列表,如果在任务列表中关闭,则会强制结束exit事件的处理并回收后续资源。 +一个脚本停止运行时, 会关闭该脚本的所有悬浮窗, 触发exit事件, 之后再回收资源. 如果exit事件的处理中有死循环, 则后续资源无法得到及时回收. +此时脚本会停留在任务列表, 如果在任务列表中关闭, 则会强制结束exit事件的处理并回收后续资源. ``` log("开始运行") @@ -260,14 +265,14 @@ log("即将结束运行"); ## events.observeNotification() -开启通知监听。例如QQ消息、微信消息、推送等通知。 +开启通知监听. 例如QQ消息、微信消息、推送等通知. -通知监听依赖于通知服务,如果通知服务没有运行,会抛出异常并跳转到通知权限开启界面。(有时即使通知权限已经开启通知服务也没有运行,这时需要关闭权限再重新开启一次) +通知监听依赖于通知服务, 如果通知服务没有运行, 会抛出异常并跳转到通知权限开启界面. (有时即使通知权限已经开启通知服务也没有运行, 这时需要关闭权限再重新开启一次) 例如: ``` -events.obverseNotification(); +events.observeNotification(); events.onNotification(function(notification){ log(notification.getText()); }); @@ -275,9 +280,9 @@ events.onNotification(function(notification){ ## events.observeToast() -开启Toast监听。 +开启Toast监听. -Toast监听依赖于无障碍服务,因此此函数会确保无障碍服务运行。 +Toast监听依赖于无障碍服务, 因此此函数会确保无障碍服务运行. ## 事件: 'toast' @@ -285,9 +290,9 @@ Toast监听依赖于无障碍服务,因此此函数会确保无障碍服务运 * `getText()` 获取Toast的文本内容 * `getPackageName()` 获取发出Toast的应用包名 -当有应用发出toast(气泡消息)时会触发该事件。但Auto.js软件本身的toast除外。 +当有应用发出toast(气泡消息)时会触发该事件. 但Auto.js软件本身的toast除外. -例如,要记录发出所有toast的应用: +例如, 要记录发出所有toast的应用: ``` events.observeToast(); @@ -300,7 +305,7 @@ events.onToast(function(toast){ * `notification` [Notification](#events_notification) 通知对象 -当有应用发出通知时会触发该事件,参数为[Notification](#events_notification)。 +当有应用发出通知时会触发该事件, 参数为[Notification](#events_notification). 例如: @@ -313,19 +318,19 @@ events.on("notification", function(n){ # Notification -通知对象,可以获取通知详情,包括通知标题、内容、发出通知的包名、时间等,也可以对通知进行操作,比如点击、删除。 +通知对象, 可以获取通知详情, 包括通知标题、内容、发出通知的包名、时间等, 也可以对通知进行操作, 比如点击、删除. ## Notification.number * {number} -通知数量。例如QQ连续收到两条消息时number为2。 +通知数量. 例如QQ连续收到两条消息时number为2. ## Notification.when * {number} -通知发出时间的时间戳,可以用于构造`Date`对象。例如: +通知发出时间的时间戳, 可以用于构造`Date`对象. 例如: ``` events.observeNotification(); @@ -338,42 +343,40 @@ events.on("notification", function(n){ * 返回 {string} -获取发出通知的应用包名。 +获取发出通知的应用包名. ## Notification.getTitle() * 返回 {string} -获取通知的标题。 +获取通知的标题. ## Notification.getText() * 返回 {string} -获取通知的内容。 +获取通知的内容. ## Notification.click() -点击该通知。例如对于一条QQ消息,点击会进入具体的聊天界面。 +点击该通知. 例如对于一条QQ消息, 点击会进入具体的聊天界面. ## Notification.delete() -删除该通知。该通知将从通知栏中消失。 +删除该通知. 该通知将从通知栏中消失. # KeyEvent -> Stability: 2 - Stable - ## KeyEvent.getAction() -返回事件的动作。包括: +返回事件的动作. 包括: * `KeyEvent.ACTION_DOWN` 按下事件 * `KeyEvent.ACTION_UP` 弹起事件 ## KeyEvent.getKeyCode() -返回按键的键值。包括: +返回按键的键值. 包括: * `KeyEvent.KEYCODE_HOME` 主页键 * `KeyEvent.KEYCODE_BACK` 返回键 @@ -385,20 +388,18 @@ events.on("notification", function(n){ * 返回 {number} -返回事件发生的时间戳。 +返回事件发生的时间戳. ## KeyEvent.getDownTime() -返回最近一次按下事件的时间戳。如果本身是按下事件,则与`getEventTime()`相同。 +返回最近一次按下事件的时间戳. 如果本身是按下事件, 则与`getEventTime()`相同. ## KeyEvent.keyCodeToString(keyCode) -把键值转换为字符串。例如KEYCODE_HOME转换为"KEYCODE_HOME"。 +把键值转换为字符串. 例如KEYCODE_HOME转换为"KEYCODE_HOME". # keys -> Stability: 2 - Stable - 按键事件中所有可用的按键名称为: * `volume_up` 音量上键 @@ -409,15 +410,13 @@ events.on("notification", function(n){ # EventEmitter -> Stability: 2 - Stable - ## EventEmitter.defaultMaxListeners -每个事件默认可以注册最多 10 个监听器。 单个 EventEmitter 实例的限制可以使用 emitter.setMaxListeners(n) 方法改变。 所有 EventEmitter 实例的默认值可以使用 EventEmitter.defaultMaxListeners 属性改变。 +每个事件默认可以注册最多 10 个监听器. 单个 EventEmitter 实例的限制可以使用 emitter.setMaxListeners(n) 方法改变. 所有 EventEmitter 实例的默认值可以使用 EventEmitter.defaultMaxListeners 属性改变. -设置 EventEmitter.defaultMaxListeners 要谨慎,因为会影响所有 EventEmitter 实例,包括之前创建的。 因而,调用 emitter.setMaxListeners(n) 优先于 EventEmitter.defaultMaxListeners。 +设置 EventEmitter.defaultMaxListeners 要谨慎, 因为会影响所有 EventEmitter 实例, 包括之前创建的. 因而, 调用 emitter.setMaxListeners(n) 优先于 EventEmitter.defaultMaxListeners. -注意,与Node.js不同,**这是一个硬性限制**。 EventEmitter 实例不允许添加更多的监听器,监听器超过最大数量时会抛出TooManyListenersException。 +注意, 与Node.js不同, **这是一个硬性限制**. EventEmitter 实例不允许添加更多的监听器, 监听器超过最大数量时会抛出TooManyListenersException. ``` emitter.setMaxListeners(emitter.getMaxListeners() + 1); @@ -432,20 +431,20 @@ emitter.once('event', () => { * `eventName` {any} * `listener` {Function} -emitter.on(eventName, listener) 的别名。 +emitter.on(eventName, listener) 的别名. ## EventEmitter.emit(eventName[, ...args]) * `eventName` {any} * `args` {any} -按监听器的注册顺序,同步地调用每个注册到名为 eventName 事件的监听器,并传入提供的参数。 +按监听器的注册顺序, 同步地调用每个注册到名为 eventName 事件的监听器, 并传入提供的参数. -如果事件有监听器,则返回 true ,否则返回 false。 +如果事件有监听器, 则返回 true , 否则返回 false. ## EventEmitter.eventNames() -返回一个列出触发器已注册监听器的事件的数组。 数组中的值为字符串或符号。 +返回一个列出触发器已注册监听器的事件的数组. 数组中的值为字符串或符号. ``` const myEE = events.emitter(); @@ -461,19 +460,19 @@ console.log(myEE.eventNames()); ## EventEmitter.getMaxListeners() -返回 EventEmitter 当前的最大监听器限制值,该值可以通过 emitter.setMaxListeners(n) 设置或默认为 EventEmitter.defaultMaxListeners。 +返回 EventEmitter 当前的最大监听器限制值, 该值可以通过 emitter.setMaxListeners(n) 设置或默认为 EventEmitter.defaultMaxListeners. ## EventEmitter.listenerCount(eventName) * `eventName` {string} 正在被监听的事件名 -返回正在监听名为 eventName 的事件的监听器的数量。 +返回正在监听名为 eventName 的事件的监听器的数量. ## EventEmitter.listeners(eventName) * `eventName` {string} -返回名为 eventName 的事件的监听器数组的副本。 +返回名为 eventName 的事件的监听器数组的副本. ``` server.on('connection', (stream) => { @@ -488,7 +487,7 @@ console.log(util.inspect(server.listeners('connection'))); * `eventName` {any} 事件名 * `listener` {Function} 回调函数 -添加 listener 函数到名为 eventName 的事件的监听器数组的末尾。 不会检查 listener 是否已被添加。 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。 +添加 listener 函数到名为 eventName 的事件的监听器数组的末尾. 不会检查 listener 是否已被添加. 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次. ``` server.on('connection', (stream) => { @@ -496,9 +495,9 @@ server.on('connection', (stream) => { }); ``` -返回一个 EventEmitter 引用,可以链式调用。 +返回一个 EventEmitter 引用, 可以链式调用. -默认情况下,事件监听器会按照添加的顺序依次调用。 emitter.prependListener() 方法可用于将事件监听器添加到监听器数组的开头。 +默认情况下, 事件监听器会按照添加的顺序依次调用. emitter.prependListener() 方法可用于将事件监听器添加到监听器数组的开头. ``` const myEE = events.emitter(); @@ -515,7 +514,7 @@ myEE.emit('foo'); * `eventName` {any} 事件名 * `listener` {Function} 回调函数 -添加一个单次 listener 函数到名为 eventName 的事件。 下次触发 eventName 事件时,监听器会被移除,然后调用。 +添加一个单次 listener 函数到名为 eventName 的事件. 下次触发 eventName 事件时, 监听器会被移除, 然后调用. ``` server.once('connection', (stream) => { @@ -523,9 +522,9 @@ server.once('connection', (stream) => { }); ``` -返回一个 EventEmitter 引用,可以链式调用。 +返回一个 EventEmitter 引用, 可以链式调用. -默认情况下,事件监听器会按照添加的顺序依次调用。 emitter.prependOnceListener() 方法可用于将事件监听器添加到监听器数组的开头。 +默认情况下, 事件监听器会按照添加的顺序依次调用. emitter.prependOnceListener() 方法可用于将事件监听器添加到监听器数组的开头. ``` const myEE = events.emitter(); @@ -542,7 +541,7 @@ myEE.emit('foo'); * `eventName` {any} 事件名 * `listener` {Function} 回调函数 -添加 listener 函数到名为 eventName 的事件的监听器数组的开头。 不会检查 listener 是否已被添加。 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。 +添加 listener 函数到名为 eventName 的事件的监听器数组的开头. 不会检查 listener 是否已被添加. 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次. ``` server.prependListener('connection', (stream) => { @@ -550,14 +549,14 @@ server.prependListener('connection', (stream) => { }); ``` -返回一个 EventEmitter 引用,可以链式调用。 +返回一个 EventEmitter 引用, 可以链式调用. ## EventEmitter.prependOnceListener(eventName, listener) * `eventName` {any} 事件名 * `listener` {Function} 回调函数 -添加一个单次 listener 函数到名为 eventName 的事件的监听器数组的开头。 下次触发 eventName 事件时,监听器会被移除,然后调用。 +添加一个单次 listener 函数到名为 eventName 的事件的监听器数组的开头. 下次触发 eventName 事件时, 监听器会被移除, 然后调用. ``` server.prependOnceListener('connection', (stream) => { @@ -565,24 +564,24 @@ server.prependOnceListener('connection', (stream) => { }); ``` -返回一个 EventEmitter 引用,可以链式调用。 +返回一个 EventEmitter 引用, 可以链式调用. ## EventEmitter.removeAllListeners(\[eventName\]) * `eventName` {any} -移除全部或指定 eventName 的监听器。 +移除全部或指定 eventName 的监听器. -注意,在代码中移除其他地方添加的监听器是一个不好的做法,尤其是当 EventEmitter 实例是其他组件或模块创建的。 +注意, 在代码中移除其他地方添加的监听器是一个不好的做法, 尤其是当 EventEmitter 实例是其他组件或模块创建的. -返回一个 EventEmitter 引用,可以链式调用。 +返回一个 EventEmitter 引用, 可以链式调用. ## EventEmitter.removeListener(eventName, listener) * `eventName` {any} * `listener` {Function} -从名为 eventName 的事件的监听器数组中移除指定的 listener。 +从名为 eventName 的事件的监听器数组中移除指定的 listener. ``` const callback = (stream) => { @@ -593,9 +592,9 @@ server.on('connection', callback); server.removeListener('connection', callback); ``` -removeListener 最多只会从监听器数组里移除一个监听器实例。 如果任何单一的监听器被多次添加到指定 eventName 的监听器数组中,则必须多次调用 removeListener 才能移除每个实例。 +removeListener 最多只会从监听器数组里移除一个监听器实例. 如果任何单一的监听器被多次添加到指定 eventName 的监听器数组中, 则必须多次调用 removeListener 才能移除每个实例. -注意,一旦一个事件被触发,所有绑定到它的监听器都会按顺序依次触发。 这意味着,在事件触发后、最后一个监听器完成执行前,任何 removeListener() 或 removeAllListeners() 调用都不会从 emit() 中移除它们。 随后的事件会像预期的那样发生。 +注意, 一旦一个事件被触发, 所有绑定到它的监听器都会按顺序依次触发. 这意味着, 在事件触发后、最后一个监听器完成执行前, 任何 removeListener() 或 removeAllListeners() 调用都不会从 emit() 中移除它们. 随后的事件会像预期的那样发生. ``` const myEmitter = events.emitter(); @@ -613,37 +612,37 @@ myEmitter.on('event', callbackA); myEmitter.on('event', callbackB); -// callbackA 移除了监听器 callbackB,但它依然会被调用。 +// callbackA 移除了监听器 callbackB, 但它依然会被调用. // 触发是内部的监听器数组为 [callbackA, callbackB] myEmitter.emit('event'); // 打印: // A // B -// callbackB 被移除了。 +// callbackB 被移除了. // 内部监听器数组为 [callbackA] myEmitter.emit('event'); // 打印: // A ``` -因为监听器是使用内部数组进行管理的,所以调用它会改变在监听器被移除后注册的任何监听器的位置索引。 虽然这不会影响监听器的调用顺序,但意味着由 emitter.listeners() 方法返回的监听器数组副本需要被重新创建。 +因为监听器是使用内部数组进行管理的, 所以调用它会改变在监听器被移除后注册的任何监听器的位置索引. 虽然这不会影响监听器的调用顺序, 但意味着由 emitter.listeners() 方法返回的监听器数组副本需要被重新创建. -返回一个 EventEmitter 引用,可以链式调用。 +返回一个 EventEmitter 引用, 可以链式调用. ## EventEmitter.setMaxListeners(n) * `n` {number} -默认情况下,如果为特定事件添加了超过 10 个监听器,则 EventEmitter 会打印一个警告。 此限制有助于寻找内存泄露。 但是,并不是所有的事件都要被限为 10 个。 emitter.setMaxListeners() 方法允许修改指定的 EventEmitter 实例的限制。 值设为 Infinity(或 0)表明不限制监听器的数量。 +默认情况下, 如果为特定事件添加了超过 10 个监听器, 则 EventEmitter 会打印一个警告. 此限制有助于寻找内存泄露. 但是, 并不是所有的事件都要被限为 10 个. emitter.setMaxListeners() 方法允许修改指定的 EventEmitter 实例的限制. 值设为 Infinity(或 0)表明不限制监听器的数量. -返回一个 EventEmitter 引用,可以链式调用。 +返回一个 EventEmitter 引用, 可以链式调用. # events.broadcast: 脚本间广播 -脚本间通信除了使用engines模块提供的`ScriptEngine.emit()`方法以外,也可以使用events模块提供的broadcast广播。 +脚本间通信除了使用engines模块提供的`ScriptEngine.emit()`方法以外, 也可以使用events模块提供的broadcast广播. -events.broadcast本身是一个EventEmitter,但它的事件是在脚本间共享的,所有脚本都能发送和监听这些事件;事件处理会在脚本主线程执行(后续可能加入函数`onThisThread(eventName, ...args)`来提供在其他线程执行的能力)。 +events.broadcast本身是一个EventEmitter, 但它的事件是在脚本间共享的, 所有脚本都能发送和监听这些事件;事件处理会在脚本主线程执行(后续可能加入函数`onThisThread(eventName, ...args)`来提供在其他线程执行的能力). 例如在一个脚本发送一个广播hello: diff --git a/api/exceptions.md b/api/exceptions.md new file mode 100644 index 0000000..79efe4b --- /dev/null +++ b/api/exceptions.md @@ -0,0 +1,1071 @@ +# 异常 (Exceptions) + +当运行时发生错误, 新创建的 Error 对象会被抛出. +除通用的 Error 构造器外, JavaScript 还有其它类型的错误构造器, 详见下述 [JavaScript 错误类型](#javascript) 小节. + +> 注: Java 有 Error (错误) 和 Exception (异常) 之分, 它们都扩展自 Throwable 类. +> JavaScript 有 Error (错误) 全局对象, 以及一系列细分 Error 对象 (如 TypeError 等). +> 虽然 JavaScript 没有 Exception (异常) 对象, 但章节标题依然采用 "异常" (而非 "错误"). + +用 `try...catch` 语句可以捕获并处理异常, 详见下述 [异常处理](#trycatch-语句) 小节. + +在 `catch {}` (即 `catch 块`) 中, `e` 对象可用于获取异常相关信息, +而 Rhino 引擎重新包装了 `e` 对象, 通过 [`e.javaException`](#p-javaexception) 和 [`e.rhinoException`](#p-rhinoexception) 可获取更多异常信息, +详见下述 [catch 块](#catch-块) 小节. + +# 错误类型 + +## JavaScript + +以下内置错误类型在 Rhino 引擎中均得以实现, 在 AutoJs6 支持全局调用. +如需创建自定义错误类型, 参阅下述 [自定义错误类型](#自定义) 小节. + +### Error + +通用 Error 构造器. + +内置错误类型 TypeError, RangeError 等均扩展自 Error 构造器. +例如, 如果一个对象是 TypeError 的实例, 则也一定是 Error 的实例. + +Error 实例的属性及方法可参阅 [Error 对象](#Error-对象) 章节. + +```js +try { + android(); +} catch (e) { + console.log(e instanceof TypeError); // true + console.log(e instanceof Error); // true +} +``` + +### RangeError + +越界错误. + +RangeError 实例代表了当一个值不在其所允许的范围或者集合中的错误. + +```js +/* 创建或应用. */ + +const check = function (num) { + const MIN = 1; + const MAX = 500; + if (num < MIN || num > MAX) { + throw new RangeError(`Number ${num} must be between ${MIN} and ${MAX}.`); + } +}; + +try { + check(523); +} catch (e) { + if (e instanceof RangeError) { + console.error("发生越界错误."); + throw e; + } +} + +/* 复现 (Array 构造器参数不合法). */ + +// RangeError: Inappropriate array length. +let a = Array(-1); + +/* 复现 (Number 部分实例方法参数不合法). */ + +// RangeError: Precision -1 out of range. +let n = (23).toExponential(-1); +// RangeError: Precision -2 out of range. +let n = (23).toFixed(-2); +// RangeError: Precision -3 out of range. +let n = (23).toPrecision(-3); + +``` + +### ReferenceError + +引用错误. + +ReferenceError 实例代表了当一个不存在或尚未初始化的变量被引用时发生的错误. + +```js +/* 创建或应用. */ + +const f = function (num, options) { + if (typeof options === 'undefined') { + throw new ReferenceError("Invalid options."); + } + /* Other code... */ +}; +f(23); + +/* 复现. */ + +// ReferenceError: a is not defined. +a; + +``` + +### SyntaxError + +语法错误. + +SyntaxError 实例代表了当 Javascript 引擎发现 tokens 不合法或 token 顺序不合法时抛出的错误. + +```badjs +/* 创建或应用. */ + +try { + eval("..."); +} catch (e) { + if (e instanceof SyntaxError) { + console.error("发生语法错误, 位于 eval."); + throw e; + } +} + +/* 复现. */ + +// Firefox 103.0 - SyntaxError: expected expression, got '??'. +// Node.js 17.3.0 - SyntaxError: Unexpected token '??'. +// AutoJs6 - syntax error. +?? +``` + +> 注: Rhino 内置了 token 解析器, +> 因此多数语法错误会被重新解析后再抛出, +> 而其他多数 JavaScript 引擎则直接抛出 SyntaxError. + +> Rhino 相关类或文件: +> [org.mozilla.javascript.Parser](https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/Parser.java) +> [org.mozilla.javascript.TokenStream](https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/TokenStream.java) +> [Messages.properties](https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/resources/Messages.properties) + +### TypeError + +类型错误. + +TypeError 实例代表了当一个值的类型为非预期类型时发生的错误. + +```js +/* 创建或应用. */ + +try { + throw new TypeError('Hello', "someFile.js", 10); +} catch (e) { + console.log(e instanceof TypeError); // true + console.log(e.message); // "Hello" + console.log(e.name); // "TypeError" + console.log(e.fileName); // "someFile.js" + console.log(e.lineNumber); // 10 +} + +/* 复现. */ + +// TypeError: Cannot call method "f" of null. +null.f(); + +// TypeError: java is not a function, it is object. +java(); + +// TypeError: day is not a function, it is string. +[].every("day"); +``` + +### URIError + +URI 错误. + +URIError 实例代表了当以一种错误方式使用全局 URI 处理方法时产生的错误. + +```js +/* 创建或应用. */ + +try { + throw new URIError('Hello', 'someFile.js', 10); +} catch (e) { + console.log(e instanceof URIError); // true + console.log(e.message); // "Hello" + console.log(e.name); // "URIError" + console.log(e.fileName); // "someFile.js" + console.log(e.lineNumber); // 10 +} + +/* 复现. */ + +// URIError: Malformed URI sequence. +decodeURIComponent('%'); +``` + +### InternalError + +内部错误. + +InternalError 实例代表了出现在 JavaScript 引擎内部的错误. + +```js +/* 创建或应用. */ + +try { + console.log(App.HELLO); +} catch (e) { + if (e instanceof InternalError) { + console.error("发生内部错误: 未知的预置 App."); + throw e; + } +} + +/* 复现. */ + +// InternalError: Java method "vibrate" cannot be assigned to. +runtime.device.vibrate = 1; +``` + +### EvalError + +Eval 错误. + +EvalError 实例代表了一个关于 [eval](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/eval) 方法的错误. + +```js +/* 创建或应用. */ + +try { + throw new EvalError('Hello', 'someFile.js', 10); +} catch (e) { + console.log(e instanceof EvalError); // true + console.log(e.message); // "Hello" + console.log(e.name); // "EvalError" + console.log(e.fileName); // "someFile.js" + console.log(e.lineNumber); // 10 +} + +/* 复现. */ + +/* eval 抛出具体类型的错误, 而非 EvalError, 因此基本无法复现. */ + +// ReferenceError: "hello" is not defined. +eval("hello()"); + +/* 除非像上述 "创建 EvalError" 示例一样抛出错误, */ +/* 或在 eval 中显式抛出 EvalError, */ +/* 但这样做通常没有意义. */ + +// EvalError: test +eval("throw EvalError('test')"); +``` + +> 注: EvalError 不在当前 (2022/07) ECMAScript 规范中使用, 因此不会被运行时抛出. +> 但是对象本身仍然与规范的早期版本向后兼容. + +## DOM + +DOMException 接口代表访问 Web API 属性或执行 [DOM (文档对象模型)](https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model/Introduction) 相关操作时发生的异常, 此异常通常是面对浏览器环境的. + +当一个操作不可执行时, 如试图创建一个无效的 DOM 或通过一个不存在的节点作为参数节点操作方法, 会抛出 DOMException 异常: + +```js +let node = document.getElementsByTagName('h1').item(0); +let refnode = node.nextSibling; +let newnode = document.createTextNode('test'); +node.insertBefore(newnode, refnode); /* 抛出 DOMException 异常. */ +``` + +DOMException 包含详细的名称分类, 如 [ "NotFoundError" / "InvalidStateError" / "NoModificationAllowedError" ] 等, 详情参阅 [Web IDL](https://webidl.spec.whatwg.org/#idl-DOMException-error-names). + +## Java + +Java 异常 (Exception) 类扩展自 Throwable 类, Exception 实例可使用 [throw 关键字](#throw-语句) 抛出. + +Exception 可以被 [try...catch 语句](#trycatch-语句) 捕获并处理. + +Error 类也扩展自 Throwable 类, 与 Exception 稍有不同, Error 往往是 Java 程序运行中不可预料且无法恢复的异常情况, 如 [ OutOfMemoryError / NoClassDefFoundError / StackOverflowError ] 等. + +由 Java 方法抛出的原始异常, 指向 catch 块中异常对象的 [javaException](#p-javaexception) 属性. + +常见的 Java 异常: + +```text +Exception +│ +├─ RuntimeException +│ │ +│ ├─ NullPointerException +│ │ +│ ├─ IndexOutOfBoundsException +│ │ +│ ├─ SecurityException +│ │ +│ └─ IllegalArgumentException +│ │ +│ └─ NumberFormatException +│ +├─ IOException +│ │ +│ ├─ UnsupportedCharsetException +│ │ +│ ├─ FileNotFoundException +│ │ +│ └─ SocketException +│ +├─ ParseException +│ +├─ GeneralSecurityException +│ +├─ SQLException +│ +└─ TimeoutException +``` + +> 参阅: [廖雪峰](https://www.liaoxuefeng.com/wiki/1252599548343744/1264737765214592) / [GeeksForGeeks ](https://www.geeksforgeeks.org/errors-v-s-exceptions-in-java/) + +## Rhino + +Rhino 异常可以视为特殊的 [Java 异常](#java), 由 Rhino [运行时 (Runtime)](runtime) 包装为对象, 指向 catch 块中异常对象的 [rhinoException](#p-rhinoexception) 属性. + +## 自定义 + +本小节列举了几种自定义错误类型的方法. + +- 借助标准内置对象 Error: + +```js +function InvalidFruitError(msg) { + Error.call(this); + this.message = `Name of "${msg}" must end with "Fruit"`; + this.name = this.constructor.name; +} + +InvalidFruitError.prototype = Object.create(Error.prototype, { + constructor: { value: InvalidFruitError }, +}); + +function ensureFruitName(name) { + if (!name.endsWith("Fruit")) { + throw new InvalidFruitError(name); + } +} + +// InvalidFruitError: Name of "coconut" must end with "Fruit"... +[ "appleFruit", "bananaFruit", "coconut" ].forEach(ensureFruitName); +``` + +- 模拟标准内置对象 Error 的行为: + +```js +function InvalidFruitError(msg) { + this.message = `Name of "${msg}" must end with "Fruit"`; + this.name = this.constructor.name; + this.toString = () => `${this.name}: ${this.message}`; +} + +function ensureFruitName(name) { + if (!name.endsWith("Fruit")) { + throw new InvalidFruitError(name); + } +} + +// InvalidFruitError: Name of "coconut" must end with "Fruit"... +[ "appleFruit", "bananaFruit", "coconut" ].forEach(ensureFruitName); +``` + +- 摘录自 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Error) 的示例: + +```js +function CustomError(foo, message, fileName, lineNumber) { + var instance = new Error(message, fileName, lineNumber); + instance.foo = foo; + Object.setPrototypeOf(instance, CustomError.prototype); + if (Error.captureStackTrace) { + Error.captureStackTrace(instance, CustomError); + } + return instance; +} + +Object.setPrototypeOf(CustomError.prototype, Error.prototype); + +Object.setPrototypeOf(CustomError, Error); + +CustomError.prototype.name = 'CustomError'; + +try { + throw new CustomError('baz', 'bazMessage'); +} catch (e) { + console.error(e.name); // CustomError + console.error(e.foo); // baz + console.error(e.message); // bazMessage +} + +``` + +# 异常处理 + +## throw 语句 + +使用 throw 语句抛出一个异常. +此异常可以是一个含有值的表达式, 包括 [ 数字 / 字符串 / 布尔 / 对象 ] 等多种类型. + +不同语言的语法存在差异: + +```java +/* Java. */ + +/* new 不可省略. */ +throw new Exception("foo"); +``` + +```kotlin +/* Kotlin. */ + +/* Kotlin 语言无 new 关键字. */ +throw Exception("foo"); +``` + +```js +/* JavaScript. */ + +/* Error 实例. */ +throw new Error("foo"); + +/* Error 实例 (new 关键字省略). */ +throw Error("foo"); + +/* 数字或字符串等表达式. */ +throw "foo"; /* 合法. */ +throw 23; /* 合法. */ +throw true; /* 合法. */ + +/* 对象. */ +throw { + name: "CustomError", + message: "test", + __proto__: Error.prototype, +}; + +/* 自定义 "类" 的实例. */ +function TestError(msg) { + this.message = msg; + this.name = this.constructor.name; + this.toString = () => `${this.name}: ${this.message}`; +} + +throw new TestError("foo"); /* new 关键字不可省略. */ + +``` + +## try...catch 语句 + +try...catch 语句标记一块待尝试的语句, 若语句出现异常 (包括主动使用 throw 语句抛出的异常), 该异常会被 try...catch 语句捕获: + +```js +try { + throw new java.lang.RuntimeException("hello"); +} catch (e) { + console.log(e.message); // "hello" +} +``` + +try...catch 语句必须有 try 代码块, 最多 1 个 catch 代码块 (catch 代码块省略时, 则必须存在 finally 代码块). + +## catch 块 + +又名 "捕捉块". + +使用 catch 块处理在 try 代码块中产生的异常. +catch 块中的语句只有 try 代码块中抛出异常时才会执行. + +```js +try { + 0 === 1; +} catch (e) { + /* 不会执行, 因为 try 块中无异常. */ + console.log(e.message); +} +``` + +catch 块中的异常信息对象 (通常用 e 或 ex 表示) 包含了异常的基本信息, 如 [ message / name / javaException / stack ] 等, 详见 [Error 对象](#error 对象) 章节. + +Rhino 内置的 token 解析器可以解析像 Java 语言一样的多 catch 语句: + +```badjs +function classForName(name) { + try { + return java.lang.Class.forName(name); + } catch (e if e.javaException instanceof java.lang.ClassNotFoundException) { + print(`Class ${name} not found`); + } catch (e if e.javaException instanceof java.lang.NullPointerException) { + print("Class name is null"); + } +} + +classForName("NonExistingClass"); // Class NonExistingClass not found +classForName(null); // Class name is null +``` + +上述示例代码在 AutoJs6 可正常运行并返回预期结果, 但不符合 ECMAScript 语法, 且暂未发现任何可以解析此语法的 IDE, 使用后会造成 IDE 报错并在格式化代码时出现排版异常. + +使用传统的单 catch 块语句可能会是更好的选择: + +```js +try { + return java.lang.Class.forName(name); +} catch (e) { + if (e.javaException instanceof java.lang.ClassNotFoundException) { + print(`Class ${name} not found`); + } else if (e.javaException instanceof java.lang.NullPointerException) { + print("Class name is null"); + } +} +``` + +## finally 块 + +finally 块中的语句无论 try 代码块是否抛出异常都会执行. + +```js +try { + /* 打开并写入文件, 此处可能出现异常. */ +} catch (e) { + /* 处理异常. */ +} finally { + /* 关闭文件, 释放资源. */ +} +``` + +finally 块存在时, catch 块可省略: + +```js +try { + /* 打开并写入文件, 此处可能出现异常. */ +} finally { + /* 关闭文件, 释放资源, 然后将异常重新抛出 (因为没有 catch 块捕获异常). */ +} +``` + +如果 finally 块有返回值, 该值会是整个 try...catch...finally 流程的返回值, 而不论在 try 和 catch 块中语句的返回值: + +```js +function f() { + try { + console.log(0); + throw "error"; + } catch (e) { + console.log(1); + return true; /* return 语句被暂时搁置, 直至 finally 块语句全部执行完成. */ + console.log(2); /* 不可达. */ + } finally { + console.log(3); + return false; /* 覆盖上一个 return 语句. */ + console.log(4); /* 不可达. */ + } + /* `return false` 已执行. */ + console.log(5); /* 不可达. */ +} + +let res = f(); // 控制台打印 0, 1, 3 +console.log(res); // false +``` + +# Error 对象 + +Error 对象是一个 JavaScript 构造函数, 它的实例通常由 [catch 块](#catch-块) 中的参数引用, 或通过 new 关键字产生相应的 Error 实例对象: + +```js +try { + let err = new Error("test"); + console.log(err instanceof Error); // true + throw err; +} catch (e) { + console.log(e instanceof Error); // true +} +``` + +对于 AutoJs6, catch 块中的引用对象 (通常用 e 或 ex 表示) 还由 Rhino 引擎额外封装了 [javaException](#p-javaexception) 和 [rhinoException](#p-rhinoexception) 两个对象. + +--- + +

Error

+ +--- + +## [@] Error + +## [p#] name + +- { [string](dataTypes#string) } + +表示 Error 类型的名称, 初始值为 "Error". + +```js +console.log(new SyntaxError('hello').name); // "SyntaxError" + +try { + throw TypeError('world'); +} catch (e) { + console.log(e.name); // "TypeError" +} +``` + +## [p#] message + +- { [string](dataTypes#string) } + +错误相关信息的描述. + +```js +console.log(new SyntaxError('hello').name); // "hello" + +try { + Math.random().toFixed(-1); +} catch (e) { + console.log(e.message); // "精度 -1 超出范围." +} +``` + +## [p#] stack + +- { [string](dataTypes#string) | [any](dataTypes#any) } + +stack 属性描述了 Error 对象的 [栈追踪 (Stack Trace)](https://zh.wikipedia.org/wiki/%E6%A0%88%E8%BF%BD%E8%B8%AA) 信息. + +栈追踪描述了程序运行过程中某个时间点上的活跃栈帧信息. +用户在日志中可以查看程序出错时的栈追踪信息, 用以自行排查代码异常或将栈追踪信息反馈给开发者. + +```js +try { + !function test() { + throw Error('hello'); + }(); +} catch (e) { + /* 打印栈追踪信息. */ + console.log(e.stack); +} +``` + +一个已格式化的栈追踪信息示例: + +```text +ReferenceError: FAIL is not defined + at Constraint.execute (deltablue.js:525:2) + at Constraint.recalculate (deltablue.js:424:21) + at Planner.addPropagate (deltablue.js:701:6) + at Constraint.satisfy (deltablue.js:184:15) + at Planner.incrementalAdd (deltablue.js:591:21) + at Constraint.addConstraint (deltablue.js:162:10) + at Constraint.BinaryConstraint (deltablue.js:346:7) + at Constraint.EqualityConstraint (deltablue.js:515:38) + at chainTest (deltablue.js:807:6) + at deltaBlue (deltablue.js:879:2) +``` + +栈追踪的信息量由栈帧数量体现, 通过 [Error.stackTraceLimit](#p-stacktracelimit) 可设置栈帧数量. +栈追踪信息可通过 [Error.captureStackTrace](#m-capturestacktrace) 附加到一个 JavaScript 对象 (如 obj) 上, 通过访问 obj.stack 可随时查看栈追踪信息. +[Error.prepareStackTrace](#m-preparestacktrace) 还可以自定义栈追踪信息的输出格式. + +stack 属性默认返回 string 类型, 如果设置了 prepareStackTrace 格式函数, 则 stack 类型将与格式函数的返回值类型一致: + +```js +Error.prepareStackTrace = function (e, frames) { + return frames.reduce((a, b) => a.concat([ b.getFunctionName() ]), []); +}; +try { + !function test() { + throw Error('hello'); + }(); +} catch (e) { + /* 此时 stack 是一个数组. */ + console.log(Array.isArray(e.stack)); // true +} +``` + +## [p#] lineNumber + +- { [number](dataTypes#number) } + +表示抛出异常的代码在源文件中的行号. + +```js +try { + throw Error("hello"); +} catch (e) { + console.log(e.lineNumber); /* e.g. 5 */ +} +``` + +## [p#] fileName + +- { [string](dataTypes#string) } + +表示抛出异常的代码所在源文件的文件路径. + +```js +try { + throw Error("hello"); +} catch (e) { + console.log(e.fileName); /* e.g. "/storage/emulated/0/Scripts/test.js" */ +} +``` + +## [p#] javaException + +- { [java.lang.Exception](#java) } + +由 Java 方法抛出的原始异常. + +```js +try { + /* 一些其他代码, 可能会抛出异常. */ + /* ... */ + + /* 启动一个不存在的 Activity. */ + app.startActivity({}); +} catch (e) { + // '[JavaException: android.content.ActivityNotFoundException: No Activity found to handle Intent { flg=0x10000000 }]' + console.log(e); + + console.log(e.javaException !== undefined); // true + + /* 借助 e.javaException 类型处理不同的异常. */ + if (e.javaException instanceof org.json.JSONException) { + console.error('JSON 解析错误'); + throw (e); + } + if (e.javaException instanceof android.content.ActivityNotFoundException) { + console.error('指定的 Activity 不存在'); + throw (e); + } + if (e.javaException instanceof ScriptInterruptedException) { + /* 忽略错误, 直接退出. */ + exit(); + } + if (e.javaException instanceof java.lang.RuntimeException) { + /* ... */ + throw (e); + } + /* ... */ +} +``` + +## [p#] rhinoException + +- { [java.lang.Exception](#java) } + +由 Rhino 运行时 (Runtime) 包装的异常对象. + +```js +try { + f(); +} catch (e) { + // '[ReferenceError: "f" is not defined.]' + console.log(e); + + /* e 是 ReferenceError 类型. */ + console.log(e instanceof ReferenceError); // true + /* 同时也是 Error 类型. */ + console.log(e instanceof Error); // true + + /* 对象 e 是 Error 类型, 为 JavaScript 异常. */ + /* 但 e.rhinoException 是 java.lang.Exception 类型, 为 Java 异常. */ + console.log(e.rhinoException instanceof java.lang.Exception); // true + console.log(e.rhinoException instanceof org.mozilla.javascript.EcmaError); // true + console.log(e.rhinoException instanceof Error); // false + + /* 因为 try 代码块没有触发 Java 异常, 因此 e.javaException 是 undefined. */ + console.log(e.javaException); // undefined +} +``` + +## [m#] toString + +### toString() + +- **returns** { [string](dataTypes#string) } + +返回一个表示指定 Error 对象的字符串. + +```js +let errA = new Error('hello'); +console.log(errA.toString()); // "Error: hello" + +let errB = new TypeError('world'); +console.log(errB.toString()); // "TypeError: world" + +errB.name = 'Banana'; +console.log(errB.toString()); // "Banana: world" + +errB.message = "yellow"; +console.log(errB.toString()); // "Banana: yellow" +``` + +## [m#] toSource + +### toSource() + +- **returns** { [string](dataTypes#string) } + +返回表示 Error 对象源代码的字符串. + +```js +let err = new Error('hello'); +console.log(err.toSource()); // (new Error("hello", "")) + +try { + Math.random().toFixed(-1); +} catch (e) { + console.log(e.toSource()); // (new RangeError("\u7cbe\u5ea6 -1 \u8d85\u51fa\u8303\u56f4.", "\u8fdc\u7a0b$Untitled-52js", 3)) + throw eval(e.toSource()); /* 可供 eval 作为参数接收. */ +} +``` + +## [p] stackTraceLimit + +- [ Infinity ] { [number](dataTypes#number) } + +设置或读取收集的栈追踪信息的栈帧数量. + +> 注: 该属性虽以 [数据描述符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#%E6%8F%8F%E8%BF%B0) 描述, 但通常以 [存取描述符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#%E6%8F%8F%E8%BF%B0) 方式使用, 且 setter 更具有实际意义. + +栈帧默认值为 Infinity, 即无数量限制: + +```js +console.log(Error.stackTraceLimit); // Infinity +``` + +设置一个数量限制: + +```js +Error.stackTraceLimit = 23; +console.log(Error.stackTraceLimit); // 23 +``` + +恢复默认的数量限制值 (Infinity) 有多种方式: + +```js +/* 直接设置 Infinity. */ +Error.stackTraceLimit = Infinity; + +/* 设置 NaN. */ +Error.stackTraceLimit = NaN; + +/* 设置负数. */ +Error.stackTraceLimit = -1; + +/* 设置无法转换为非 NaN 的 number 类型. */ +Error.stackTraceLimit = "hello"; +``` + +注意与 0 的区分, 将数量限制设置为 0 表示不显示栈追踪信息, 即禁用栈追踪收集. + +```js +let infoA = {}; +let infoB = {}; + +try { + throw Error("hello"); +} catch (e) { + Error.captureStackTrace(infoA); + + Error.stackTraceLimit = 0; + Error.captureStackTrace(infoB); + + console.log(infoA.stack); /* 打印栈追踪信息. */ + console.log(infoB.stack); /* 打印空字符串. */ + + Error.stackTraceLimit = Infinity; + Error.captureStackTrace(infoB); + console.log(infoB.stack); /* 打印栈追踪信息. */ +} +``` + +> 参阅: [V8 Dev](https://v8.dev/docs/stack-trace-api) + +## [m] prepareStackTrace + +用于自定义栈追踪信息格式, 该属性以函数形式发挥作用, 因此文档称之为 "格式函数". + +> 注: 该属性虽以 [数据描述符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#%E6%8F%8F%E8%BF%B0) 描述, 但通常以 [存取描述符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#%E6%8F%8F%E8%BF%B0) 方式使用, 且 setter 更具有实际意义. + +格式函数默认值为 undefined, 即使用默认格式输出栈追踪信息: + +```js +console.log(Error.prepareStackTrace); // undefined +``` + +设置自定义格式函数: + +```js +Error.prepareStackTrace = function (e, frames) { + /* ... */ +}; +``` + +恢复默认的格式函数: + +```js +/* 直接设置 null 或 undefined. */ +Error.prepareStackTrace = undefined; + +/* 除函数, null 和 undefined 外, 赋值其他类型的操作将被忽略, 不会有任何效果. */ +Error.prepareStackTrace = "hello"; /* 无任何效果. */ +``` + +> 参阅: [V8 Dev](https://v8.dev/docs/stack-trace-api) + +### prepareStackTrace(errorObj, stackTraces) + +- **errorObj** { [Error](#error 对象) } - 异常对象的引用 +- **stackTraces** { [NodeJS.CallSite](https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules__types_node_globals_d_.nodejs.callsite.html)[[]](dataTypes#array) } - 栈追踪数组 (栈帧组) +- **returns** { [any](dataTypes#any) } + +设置或查看用于自定义栈追踪信息的格式函数. + +```js +Error.prepareStackTrace = function (e, frames) { + /* ... */ +}; +``` + +上述示例中的 frames (以及方法签名中的 stackTraces) 表示栈帧组, 每一个栈帧都是 [NodeJS.CallSite](https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules__types_node_globals_d_.nodejs.callsite.html) 类型. + +下面列出了部分栈帧可用的属性或方法: + +- `[m#]` getColumnNumber +- `[m#]` getEvalOrigin +- `[m#]` getFileName +- `[m#]` getFunction +- `[m#]` getFunctionName +- `[m#]` getLineNumber +- `[m#]` getMethodName +- `[m#]` getThis +- `[m#]` getTypeName +- `[m#]` isConstructor +- `[m#]` isEval +- `[m#]` isNative +- `[m#]` isToplevel + +格式函数的返回值决定了 stack 属性的类型值. + +```js +/* 格式函数返回值为 string 类型. */ +Error.prepareStackTrace = function (e, frames) { + return frames.map(f => `${f.getFunctionName()}: ${f.getLineNumber()}`).join('\n'); +}; +try { + !function test() { + throw Error('hello'); + }(); +} catch (e) { + /* 与格式函数返回值同为 string 类型. */ + console.log(e.stack); +} +``` + +## [m] captureStackTrace + +附加 stack 属性 (栈追踪信息) 到任意对象上. + +> 参阅: [V8 Dev](https://v8.dev/docs/stack-trace-api) + +### captureStackTrace(errorObj, constructorOpt) + +- **errorObj** { [object](dataTypes#object) } - 用于附加 stack 属性的任意对象 +- **constructorOpt** { [Function](dataTypes#function) } - 用于在栈追踪信息中触发隐藏的函数 +- **returns** { [void](dataTypes#void) } + +附加 stack 属性 (栈追踪信息) 到任意对象上, 并支持在栈追踪信息中隐藏以指定的调用函数为起点的栈帧信息. + +```js +let info = {}; +try { + function a() { + Error.captureStackTrace(info); + return (0.5).toFixed(-1); + } + + function b() { + a(); + } + + !function c() { + b(); + }(); +} catch (e) { + /* info 对象被附加了 stack 属性. */ + /* stack 信息中包含函数 a, b, c 的栈帧信息, 顺序为 a (栈顶) -> b -> c (栈底). */ + console.log(info.stack); +} +``` + +部分信息对于用户或开发者可能没有参考意义, 此时可使用 constructorOpt 参数隐藏部分栈帧信息: + +```js +let info = {}; +try { + function a() { + /* b (含) 及其栈顶方向的所有函数调用均将隐藏. */ + Error.captureStackTrace(info, b); + return (0.5).toFixed(-1); + } + + function b() { + a(); + } + + !function c() { + b(); + }(); +} catch (e) { + /* stack 信息中 b 和 a 被隐藏, 仅 c 被保留. */ + console.log(info.stack); +} +``` + +上述示例中的 `b` 如果替换为 `c`, 则所有栈帧全部被隐藏, `info.stack` 将返回 `undefined` (即 stack 没有被附加到 info 对象上): + +```js +let info = {}; +try { + function a() { + /* 传入栈底方法 (c) 将不会触发 stack 属性的附加操作. */ + Error.captureStackTrace(info, c); + return (0.5).toFixed(-1); + } + + function b() { + a(); + } + + !function c() { + b(); + }(); +} catch (e) { + /* stack 信息未被附加. */ + console.log(info.stack); // undefined + console.log("stack" in info); // false +} +``` + +如果传入一个栈帧中不存在的函数, 则 `info.stack` 将返回 `""` (空字符串): + +```js +let info = {}; +try { + function irrelevant() { + // Empty body. + } + + function a() { + /* 传入一个与所有栈帧无关的函数, 将导致 stack 属性值变为空字符串. */ + Error.captureStackTrace(info, irrelevant); + return (0.5).toFixed(-1); + } + + function b() { + a(); + } + + !function c() { + b(); + }(); +} catch (e) { + console.log(info.stack); // "" +} +``` \ No newline at end of file diff --git a/api/files.md b/api/files.md index 754f3c8..a70daba 100644 --- a/api/files.md +++ b/api/files.md @@ -1,17 +1,22 @@ -# Files +# 文件 (Files) -> Stability: 2 - Stable +--- -files模块提供了一些常见的文件处理,包括文件读写、移动、复制、删掉等。 +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

-一次性的文件读写可以直接使用`files.read()`, `files.write()`, `files.append()`等方便的函数,但如果需要频繁读写或随机读写,则使用`open()`函数打开一个文件对象来操作文件,并在操作完毕后调用`close()`函数关闭文件。 +--- + +files模块提供了一些常见的文件处理, 包括文件读写、移动、复制、删掉等. + +一次性的文件读写可以直接使用`files.read()`, `files.write()`, `files.append()`等方便的函数, 但如果需要频繁读写或随机读写, 则使用`open()`函数打开一个文件对象来操作文件, 并在操作完毕后调用`close()`函数关闭文件. ## files.isFile(path) * `path` {string} 路径 * 返回 {boolean} -返回路径path是否是文件。 +返回路径path是否是文件. ``` log(files.isDir("/sdcard/文件夹/")); //返回false @@ -23,7 +28,7 @@ log(files.isDir("/sdcard/文件.txt")); //返回true * `path` {string} 路径 * 返回 {boolean} -返回路径path是否是文件夹。 +返回路径path是否是文件夹. ``` log(files.isDir("/sdcard/文件夹/")); //返回true @@ -35,7 +40,7 @@ log(files.isDir("/sdcard/文件.txt")); //返回false * `path` {string} 路径 * 返回 {boolean} -返回文件夹path是否为空文件夹。如果该路径并非文件夹,则直接返回`false`。 +返回文件夹path是否为空文件夹. 如果该路径并非文件夹, 则直接返回`false`. ## files.join(parent, child) @@ -43,14 +48,14 @@ log(files.isDir("/sdcard/文件.txt")); //返回false * `child` {string} 子路径 * 返回 {string} -连接两个路径并返回,例如`files.join("/sdcard/", "1.txt")`返回"/sdcard/1.txt"。 +连接两个路径并返回, 例如`files.join("/sdcard/", "1.txt")`返回"/sdcard/1.txt". ## files.create(path) * `path` {string} 路径 * 返回 {boolean} -创建一个文件或文件夹并返回是否创建成功。如果文件已经存在,则直接返回`false`。 +创建一个文件或文件夹并返回是否创建成功. 如果文件已经存在, 则直接返回`false`. ``` files.create("/sdcard/新文件夹/"); @@ -61,7 +66,7 @@ files.create("/sdcard/新文件夹/"); * `path` {string} 路径 * 返回 {boolean} -创建一个文件或文件夹并返回是否创建成功。如果文件所在文件夹不存在,则先创建他所在的一系列文件夹。如果文件已经存在,则直接返回`false`。 +创建一个文件或文件夹并返回是否创建成功. 如果文件所在文件夹不存在, 则先创建他所在的一系列文件夹. 如果文件已经存在, 则直接返回`false`. ``` files.createWithDirs("/sdcard/新文件夹/新文件夹/新文件夹/1.txt"); @@ -72,23 +77,23 @@ files.createWithDirs("/sdcard/新文件夹/新文件夹/新文件夹/1.txt"); * `path` {string} 路径 * 返回 {boolean} -返回在路径path处的文件是否存在。 +返回在路径path处的文件是否存在. ## files.ensureDir(path) * `path` {string} 路径 -确保路径path所在的文件夹存在。如果该路径所在文件夹不存在,则创建该文件夹。 +确保路径path所在的文件夹存在. 如果该路径所在文件夹不存在, 则创建该文件夹. -例如对于路径"/sdcard/Download/ABC/1.txt",如果/Download/文件夹不存在,则会先创建Download,再创建ABC文件夹。 +例如对于路径"/sdcard/Download/ABC/1.txt", 如果/Download/文件夹不存在, 则会先创建Download, 再创建ABC文件夹. ## files.read(path[, encoding = "utf-8"]) * `path` {string} 路径 -* `encoding` {string} 字符编码,可选,默认为utf-8 +* `encoding` {string} 字符编码, 可选, 默认为utf-8 * 返回 {string} -读取文本文件path的所有内容并返回。如果文件不存在,则抛出`FileNotFoundException`。 +读取文本文件path的所有内容并返回. 如果文件不存在, 则抛出`FileNotFoundException`. ``` log(files.read("/sdcard/1.txt")); @@ -99,9 +104,9 @@ log(files.read("/sdcard/1.txt")); * `path` {string} 路径 * 返回 {byte[]} -读取文件path的所有内容并返回一个字节数组。如果文件不存在,则抛出`FileNotFoundException`。 +读取文件path的所有内容并返回一个字节数组. 如果文件不存在, 则抛出`FileNotFoundException`. -注意,该数组是Java的数组,不具有JavaScript数组的forEach, slice等函数。 +注意, 该数组是Java的数组, 不具有JavaScript数组的forEach, slice等函数. 一个以16进制形式打印文件的例子如下: @@ -120,7 +125,7 @@ log(sb.toString()); * `text` {string} 要写入的文本内容 * `encoding` {string} 字符编码 -把text写入到文件path中。如果文件存在则覆盖,不存在则创建。 +把text写入到文件path中. 如果文件存在则覆盖, 不存在则创建. ``` var text = "文件内容"; @@ -133,9 +138,9 @@ app.viewFile("/sdcard/1.txt"); ## files.writeBytes(path, bytes) * `path` {string} 路径 -* `bytes` {byte[]} 字节数组,要写入的二进制数据 +* `bytes` {byte[]} 字节数组, 要写入的二进制数据 -把bytes写入到文件path中。如果文件存在则覆盖,不存在则创建。 +把bytes写入到文件path中. 如果文件存在则覆盖, 不存在则创建. ## files.append(path, text[, encoding = 'utf-8']) @@ -143,7 +148,7 @@ app.viewFile("/sdcard/1.txt"); * `text` {string} 要写入的文本内容 * `encoding` {string} 字符编码 -把text追加到文件path的末尾。如果文件不存在则创建。 +把text追加到文件path的末尾. 如果文件不存在则创建. ``` var text = "追加的文件内容"; @@ -156,9 +161,9 @@ app.viewFile("/sdcard/1.txt"); ## files.appendBytes(path, text[, encoding = 'utf-8']) * `path` {string} 路径 -* `bytes` {byte[]} 字节数组,要写入的二进制数据 +* `bytes` {byte[]} 字节数组, 要写入的二进制数据 -把bytes追加到文件path的末尾。如果文件不存在则创建。 +把bytes追加到文件path的末尾. 如果文件不存在则创建. ## files.copy(fromPath, toPath) @@ -166,7 +171,7 @@ app.viewFile("/sdcard/1.txt"); * `toPath` {string} 复制到的文件路径 * 返回 {boolean} -复制文件,返回是否复制成功。例如`files.copy("/sdcard/1.txt", "/sdcard/Download/1.txt")`。 +复制文件, 返回是否复制成功. 例如`files.copy("/sdcard/1.txt", "/sdcard/Download/1.txt")`. ## files.move(fromPath, toPath) @@ -174,7 +179,7 @@ app.viewFile("/sdcard/1.txt"); * `toPath` {string} 移动到的文件路径 * 返回 {boolean} -移动文件,返回是否移动成功。例如`files.move("/sdcard/1.txt", "/sdcard/Download/1.txt")`会把1.txt文件从sd卡根目录移动到Download文件夹。 +移动文件, 返回是否移动成功. 例如`files.move("/sdcard/1.txt", "/sdcard/Download/1.txt")`会把1.txt文件从sd卡根目录移动到Download文件夹. ## files.rename(path, newName) @@ -182,7 +187,7 @@ app.viewFile("/sdcard/1.txt"); * `newName` {string} 要重命名的新文件名 * 返回 {boolean} -重命名文件,并返回是否重命名成功。例如`files.rename("/sdcard/1.txt", "2.txt")`。 +重命名文件, 并返回是否重命名成功. 例如`files.rename("/sdcard/1.txt", "2.txt")`. ## files.renameWithoutExtension(path, newName) @@ -190,35 +195,35 @@ app.viewFile("/sdcard/1.txt"); * `newName` {string} 要重命名的新文件名 * 返回 {boolean} -重命名文件,不包含拓展名,并返回是否重命名成功。例如`files.rename("/sdcard/1.txt", "2")`会把"1.txt"重命名为"2.txt"。 +重命名文件, 不包含拓展名, 并返回是否重命名成功. 例如`files.rename("/sdcard/1.txt", "2")`会把"1.txt"重命名为"2.txt". ## files.getName(path) * `path` {string} 路径 * 返回 {string} -返回文件的文件名。例如`files.getName("/sdcard/1.txt")`返回"1.txt"。 +返回文件的文件名. 例如`files.getName("/sdcard/1.txt")`返回"1.txt". ## files.getNameWithoutExtension(path) * `path` {string} 路径 * 返回 {string} -返回不含拓展名的文件的文件名。例如`files.getName("/sdcard/1.txt")`返回"1"。 +返回不含拓展名的文件的文件名. 例如`files.getName("/sdcard/1.txt")`返回"1". ## files.getExtension(path) * `path` {string} 路径 * 返回 {string} -返回文件的拓展名。例如`files.getExtension("/sdcard/1.txt")`返回"txt"。 +返回文件的拓展名. 例如`files.getExtension("/sdcard/1.txt")`返回"txt". ## files.remove(path) * `path` {string} 路径 * 返回 {boolean} -删除文件或**空文件夹**,返回是否删除成功。 +删除文件或**空文件夹**, 返回是否删除成功. ## files.removeDir(path) @@ -226,35 +231,35 @@ app.viewFile("/sdcard/1.txt"); * `path` {string} 路径 * 返回 {boolean} -删除文件夹,如果文件夹不为空,则删除该文件夹的所有内容再删除该文件夹,返回是否全部删除成功。 +删除文件夹, 如果文件夹不为空, 则删除该文件夹的所有内容再删除该文件夹, 返回是否全部删除成功. ## files.getSdcardPath() * 返回 {string} -返回SD卡路径。所谓SD卡,即外部存储器。 +返回SD卡路径. 所谓SD卡, 即外部存储器. ## files.cwd() * 返回 {string} -返回脚本的"当前工作文件夹路径"。该路径指的是,如果脚本本身为脚本文件,则返回这个脚本文件所在目录;否则返回`null`获取其他设定路径。 +返回脚本的"当前工作文件夹路径". 该路径指的是, 如果脚本本身为脚本文件, 则返回这个脚本文件所在目录;否则返回`null`获取其他设定路径. -例如,对于脚本文件"/sdcard/脚本/1.js"运行`files.cwd()`返回"/sdcard/脚本/"。 +例如, 对于脚本文件"/sdcard/脚本/1.js"运行`files.cwd()`返回"/sdcard/脚本/". ## files.path(relativePath) * `relativePath` {string} 相对路径 * 返回 {string} -返回相对路径对应的绝对路径。例如`files.path("./1.png")`,如果运行这个语句的脚本位于文件夹"/sdcard/脚本/"中,则返回`"/sdcard/脚本/1.png"`。 +返回相对路径对应的绝对路径. 例如`files.path("./1.png")`, 如果运行这个语句的脚本位于文件夹"/sdcard/脚本/"中, 则返回`"/sdcard/脚本/1.png"`. ## files.listDir(path[, filter]) * `path` {string} 路径 -* `filter` {Function} 过滤函数,可选。接收一个`string`参数(文件名),返回一个`boolean`值。 +* `filter` {Function} 过滤函数, 可选. 接收一个`string`参数(文件名), 返回一个`boolean`值. -列出文件夹path下的满足条件的文件和文件夹的名称的数组。如果不加filter参数,则返回所有文件和文件夹。 +列出文件夹path下的满足条件的文件和文件夹的名称的数组. 如果不加filter参数, 则返回所有文件和文件夹. 列出sdcard目录下所有文件和文件夹为: @@ -275,66 +280,66 @@ log(jsFiles); ## open(path[, mode = "r", encoding = "utf-8", bufferSize = 8192]) -* `path` {string} 文件路径,例如"/sdcard/1.txt"。 -* `mode` {string} 文件打开模式,包括: - * "r": 只读文本模式。该模式下只能对文件执行**文本**读取操作。 - * "w": 只写文本模式。该模式下只能对文件执行**文本**覆盖写入操作。 - * "a": 附加文本模式。该模式下将会把写入的文本附加到文件末尾。 - * "rw": 随机读写文本模式。该模式下将会把写入的文本附加到文件末尾。 - 目前暂不支持二进制模式,随机读写模式。 -* `encoding` {string} 字符编码。 -* `bufferSize` {number} 文件读写的缓冲区大小。 +* `path` {string} 文件路径, 例如"/sdcard/1.txt". +* `mode` {string} 文件打开模式, 包括: + * "r": 只读文本模式. 该模式下只能对文件执行**文本**读取操作. + * "w": 只写文本模式. 该模式下只能对文件执行**文本**覆盖写入操作. + * "a": 附加文本模式. 该模式下将会把写入的文本附加到文件末尾. + * "rw": 随机读写文本模式. 该模式下将会把写入的文本附加到文件末尾. + 目前暂不支持二进制模式, 随机读写模式. +* `encoding` {string} 字符编码. +* `bufferSize` {number} 文件读写的缓冲区大小. -打开一个文件。根据打开模式返回不同的文件对象。包括: +打开一个文件. 根据打开模式返回不同的文件对象. 包括: -* "r": 返回一个ReadableTextFile对象。 -* "w", "a": 返回一个WritableTextFile对象。 +* "r": 返回一个ReadableTextFile对象. +* "w", "a": 返回一个WritableTextFile对象. -对于"w"模式,如果文件并不存在,则会创建一个,已存在则会清空该文件内容;其他模式文件不存在会抛出FileNotFoundException。 +对于"w"模式, 如果文件并不存在, 则会创建一个, 已存在则会清空该文件内容;其他模式文件不存在会抛出FileNotFoundException. # ReadableTextFile -可读文件对象。 +可读文件对象. ## ReadableTextFile.read() -返回该文件剩余的所有内容的字符串。 +返回该文件剩余的所有内容的字符串. ## ReadableTextFile.read(maxCount) * `maxCount` {Number} 最大读取的字符数量 -读取该文件接下来最长为maxCount的字符串并返回。即使文件剩余内容不足maxCount也不会出错。 +读取该文件接下来最长为maxCount的字符串并返回. 即使文件剩余内容不足maxCount也不会出错. ## ReadableTextFile.readline() -读取一行并返回(不包含换行符)。 +读取一行并返回(不包含换行符). ## ReadableTextFile.readlines() -读取剩余的所有行,并返回它们按顺序组成的字符串数组。 +读取剩余的所有行, 并返回它们按顺序组成的字符串数组. ## close() -关闭该文件。 +关闭该文件. **打开一个文件不再使用时务必关闭** # PWritableTextFile -可写文件对象。 +可写文件对象. ## PWritableTextFile.write(text) * `text` {string} 文本 -把文本内容text写入到文件中。 +把文本内容text写入到文件中. ## PWritableTextFile.writeline(line) * `text` {string} 文本 -把文本line写入到文件中并写入一个换行符。 +把文本line写入到文件中并写入一个换行符. ## PWritableTextFile.writelines(lines) @@ -344,10 +349,10 @@ log(jsFiles); ## PWritableTextFile.flush() -把缓冲区内容输出到文件中。 +把缓冲区内容输出到文件中. ## PWritableTextFile.close() -关闭文件。同时会被缓冲区内容输出到文件。 +关闭文件. 同时会被缓冲区内容输出到文件. -**打开一个文件写入后,不再使用时务必关闭,否则文件可能会丢失** +**打开一个文件写入后, 不再使用时务必关闭, 否则文件可能会丢失** diff --git a/api/floaty.md b/api/floaty.md index 4e3bb8c..f4c67a7 100644 --- a/api/floaty.md +++ b/api/floaty.md @@ -1,8 +1,15 @@ -# Floaty +# 悬浮窗 (Floaty) -floaty模块提供了悬浮窗的相关函数,可以在屏幕上显示自定义悬浮窗,控制悬浮窗大小、位置等。 +--- -悬浮窗在脚本停止运行时会自动关闭,因此,要保持悬浮窗不被关闭,可以用一个空的setInterval来实现,例如: +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +floaty模块提供了悬浮窗的相关函数, 可以在屏幕上显示自定义悬浮窗, 控制悬浮窗大小、位置等. + +悬浮窗在脚本停止运行时会自动关闭, 因此, 要保持悬浮窗不被关闭, 可以用一个空的setInterval来实现, 例如: ``` setInterval(()=>{}, 1000); @@ -12,11 +19,11 @@ setInterval(()=>{}, 1000); * `layout` {xml} | {View} 悬浮窗界面的XML或者View -指定悬浮窗的布局,创建并**显示**一个悬浮窗,返回一个`FloatyWindow`对象。 +指定悬浮窗的布局, 创建并**显示**一个悬浮窗, 返回一个`FloatyWindow`对象. -该悬浮窗自带关闭、调整大小、调整位置按键,可根据需要调用`setAdjustEnabled()`函数来显示或隐藏。 +该悬浮窗自带关闭、调整大小、调整位置按键, 可根据需要调用`setAdjustEnabled()`函数来显示或隐藏. -其中layout参数可以是xml布局或者一个View,更多信息参见ui模块的说明。 +其中layout参数可以是xml布局或者一个View, 更多信息参见ui模块的说明. 例子: @@ -31,9 +38,9 @@ setTimeout(()=>{ }, 2000); ``` -这段代码运行后将会在屏幕上显示悬浮文字,并在两秒后消失。 +这段代码运行后将会在屏幕上显示悬浮文字, 并在两秒后消失. -另外,因为脚本运行的线程不是UI线程,而所有对控件的修改操作需要在UI线程执行,此时需要用`ui.run`,例如: +另外, 因为脚本运行的线程不是UI线程, 而所有对控件的修改操作需要在UI线程执行, 此时需要用`ui.run`, 例如: ``` ui.run(function(){ @@ -41,17 +48,17 @@ ui.run(function(){ }); ``` -有关返回的`FloatyWindow`对象的说明,参见下面的`FloatyWindow`章节。 +有关返回的`FloatyWindow`对象的说明, 参见下面的`FloatyWindow`章节. ## floaty.rawWindow(layout) * `layout` {xml} | {View} 悬浮窗界面的XML或者View -指定悬浮窗的布局,创建并**显示**一个原始悬浮窗,返回一个`FloatyRawWindow`对象。 +指定悬浮窗的布局, 创建并**显示**一个原始悬浮窗, 返回一个`FloatyRawWindow`对象. -与`floaty.window()`函数不同的是,该悬浮窗不会增加任何额外设施(例如调整大小、位置按钮),您可以根据自己需要编写任何布局。 +与`floaty.window()`函数不同的是, 该悬浮窗不会增加任何额外设施(例如调整大小、位置按钮), 您可以根据自己需要编写任何布局. -而且,该悬浮窗支持完全全屏,可以覆盖状态栏,因此可以做护眼模式之类的应用。 +而且, 该悬浮窗支持完全全屏, 可以覆盖状态栏, 因此可以做护眼模式之类的应用. ``` var w = floaty.rawWindow( @@ -67,76 +74,76 @@ setTimeout(()=>{ }, 2000); ``` -这段代码运行后将会在屏幕上显示悬浮文字,并在两秒后消失。 +这段代码运行后将会在屏幕上显示悬浮文字, 并在两秒后消失. -有关返回的`FloatyRawWindow`对象的说明,参见下面的`FloatyRawWindow`章节。 +有关返回的`FloatyRawWindow`对象的说明, 参见下面的`FloatyRawWindow`章节. ## floaty.closeAll() -关闭所有本脚本的悬浮窗。 +关闭所有本脚本的悬浮窗. # FloatyWindow -悬浮窗对象,可通过`FloatyWindow.{id}`获取悬浮窗界面上的元素。例如, 悬浮窗window上一个控件的id为aaa, 那么`window.aaa`即可获取到该控件,类似于ui。 +悬浮窗对象, 可通过`FloatyWindow.{id}`获取悬浮窗界面上的元素. 例如, 悬浮窗window上一个控件的id为aaa, 那么`window.aaa`即可获取到该控件, 类似于ui. ## window.setAdjustEnabled(enabled) * `enabled` {boolean} 是否启用悬浮窗调整(大小、位置) -如果enabled为true,则在悬浮窗左上角、右上角显示可供位置、大小调整的标示,就像控制台一样; -如果enabled为false,则隐藏上述标示。 +如果enabled为true, 则在悬浮窗左上角、右上角显示可供位置、大小调整的标示, 就像控制台一样; +如果enabled为false, 则隐藏上述标示. ## window.setPosition(x, y) * `x` {number} x * `x` {number} y -设置悬浮窗位置。 +设置悬浮窗位置. ## window.getX() -返回悬浮窗位置的X坐标。 +返回悬浮窗位置的X坐标. ## window.getY() -返回悬浮窗位置的Y坐标。 +返回悬浮窗位置的Y坐标. ## window.setSize(width, height) * `width` {number} 宽度 * `height` {number} 高度 -设置悬浮窗宽高。 +设置悬浮窗宽高. ## window.getWidht() -返回悬浮窗宽度。 +返回悬浮窗宽度. ## window.getHeight() -返回悬浮窗高度。 +返回悬浮窗高度. ## window.close() -关闭悬浮窗。如果悬浮窗已经是关闭状态,则此函数将不执行任何操作。 +关闭悬浮窗. 如果悬浮窗已经是关闭状态, 则此函数将不执行任何操作. -被关闭后的悬浮窗不能再显示。 +被关闭后的悬浮窗不能再显示. ## window.exitOnClose() -使悬浮窗被关闭时自动结束脚本运行。 +使悬浮窗被关闭时自动结束脚本运行. # FloatyRawWindow -原始悬浮窗对象,可通过`window.{id}`获取悬浮窗界面上的元素。例如, 悬浮窗window上一个控件的id为aaa, 那么`window.aaa`即可获取到该控件,类似于ui。 +原始悬浮窗对象, 可通过`window.{id}`获取悬浮窗界面上的元素. 例如, 悬浮窗window上一个控件的id为aaa, 那么`window.aaa`即可获取到该控件, 类似于ui. ## window.setTouchable(touchable) * `touchable` {Boolean} 是否可触摸 -设置悬浮窗是否可触摸,如果为true, 则悬浮窗将接收到触摸、点击等事件并且无法继续传递到悬浮窗下面;如果为false, 悬浮窗上的触摸、点击等事件将被直接传递到悬浮窗下面。处于安全考虑,被悬浮窗接收的触摸事情无法再继续传递到下层。 +设置悬浮窗是否可触摸, 如果为true, 则悬浮窗将接收到触摸、点击等事件并且无法继续传递到悬浮窗下面;如果为false, 悬浮窗上的触摸、点击等事件将被直接传递到悬浮窗下面. 处于安全考虑, 被悬浮窗接收的触摸事情无法再继续传递到下层. -可以用此特性来制作护眼模式脚本。 +可以用此特性来制作护眼模式脚本. ``` var w = floaty.rawWindow( @@ -157,24 +164,24 @@ setTimeout(()=>{ * `x` {number} x * `x` {number} y -设置悬浮窗位置。 +设置悬浮窗位置. ## window.getX() -返回悬浮窗位置的X坐标。 +返回悬浮窗位置的X坐标. ## window.getY() -返回悬浮窗位置的Y坐标。 +返回悬浮窗位置的Y坐标. ## window.setSize(width, height) * `width` {number} 宽度 * `height` {number} 高度 -设置悬浮窗宽高。 +设置悬浮窗宽高. -特别地,如果设置为-1,则为占满全屏;设置为-2则为根据悬浮窗内容大小而定。例如: +特别地, 如果设置为-1, 则为占满全屏;设置为-2则为根据悬浮窗内容大小而定. 例如: ``` var w = floaty.rawWindow( @@ -193,18 +200,18 @@ setTimeout(()=>{ ## window.getWidht() -返回悬浮窗宽度。 +返回悬浮窗宽度. ## window.getHeight() -返回悬浮窗高度。 +返回悬浮窗高度. ## window.close() -关闭悬浮窗。如果悬浮窗已经是关闭状态,则此函数将不执行任何操作。 +关闭悬浮窗. 如果悬浮窗已经是关闭状态, 则此函数将不执行任何操作. -被关闭后的悬浮窗不能再显示。 +被关闭后的悬浮窗不能再显示. ## window.exitOnClose() -使悬浮窗被关闭时自动结束脚本运行。 +使悬浮窗被关闭时自动结束脚本运行. diff --git a/api/global.md b/api/global.md new file mode 100644 index 0000000..ee89d4b --- /dev/null +++ b/api/global.md @@ -0,0 +1,1023 @@ +# 全局对象 (Global) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Nov 20, 2022.

+ +--- + +在 JavaScript 中, [几乎一切都是对象](https://stackoverflow.com/questions/9108925/how-is-almost-everything-in-javascript-an-object/). +此处的全局 "对象" 包括 [ 变量 / 方法 / 构造器 ] 等. +全局对象随处可用, 包括 ECMA 标准内置对象 (如 [ Number / RegExp / String ] 等). + +AutoJs6 的内置模块均支持全局使用, 如 `app`, `images`, `device` 等. + +为便于使用, 一些 AutoJs6 模块中的方法也被全局化, +如 `images.captureScreen()`, `dialogs.alert()`, `app.launch()` 等. +全局化方法均以 `Global` 标签标注. + +脚本文件可直接运行使用, 也可作为模块被导入使用 (`require` 方法). +当作为模块使用时, `exports` 和 `module` 可作为全局对象使用. +另在 UI 模式下也有一些专属全局对象, 如 `activity`. + +## 覆写保护 + +AutoJs6 对部分全局对象及内置模块增加了覆写保护. +以下全局声明或赋值将导致异常或非预期结果: + +```js +/* 以全局对象 selector 为例. */ + +/* 声明无效. */ +let selector = 1; /* 异常: 变量 selector 重复声明. */ +const selector = 1; /* 同上. */ +var selector = 1; /* 同上. */ + +/* 覆写无效 (非严格模式). */ +selector = 1; +typeof selector; // "function" - 静默失败, 覆写未生效. + +/* 覆写无效 (严格模式). */ +"use strict"; +selector = 1; /* 异常: 无法修改只读属性: selector. */ +``` + +局部作用域不受上述情况影响: + +```js +(function () { + let selector = 1; + return typeof selector; +})(); // "number" +``` + +截至目前 (2022/10) 受覆写保护的对象有: + +```text +selector +continuation +``` + +--- + +

global

+ +--- + +## [@] global + +global 为 AutoJs6 的默认顶级作用域对象, 可作为全局对象使用: + +```js +typeof global; // "object" +typeof global.sleep; // "function" +``` + +另, 访问顶级作用域对象也可通过以下代码: + +```js +runtime.topLevelScope; +``` + +`runtime.topLevelScope` 本身有 `"global"` 属性, 因此全局对象 `global` 也一样拥有: + +```js +typeof runtime.topLevelScope.global; // "object" + +global.global === global; // true +global.global.global.global === global; // true +``` + +global 对象可以增加属性, 也可以覆写甚至删除属性 (部分被保护): + +```js +global.hello = "hello"; +delete global.hello; +``` + +global 对象本身是可被覆写的: + +```js +typeof global; // "object" +global = 3; +typeof global; // "number" +``` + +如果 global 对象被意外重写 (虽然概率很低), +可通过 `runtime.topLevelScope` 访问或还原: + +```js +global = 3; /* 覆写 global 对象. */ +typeof global; // "number" +typeof global.sleep; // "undefined" +typeof runtime.topLevelScope.sleep; // "function" + +global = runtime.topLevelScope; /* 还原 global 对象. */ +typeof global; // "object" +typeof global.sleep; // "function" +``` + +## [m] sleep + +### sleep(millis) + +**`Overload 1/3`** **`Non-UI`** + +- **millis** { [number](dataTypes#number) } - 休眠时间 (毫秒) +- **returns** { [void](dataTypes#void) } + +使当前线程休眠一段时间. + +```js +/* 休眠 9 秒钟. */ +sleep(9000); +/* 休眠 9 秒钟 (使用科学计数法). */ +sleep(9e3); +``` + +### sleep(millisMin, millisMax) + +**`6.2.0`** **`Overload 2/3`** **`Non-UI`** + +- **millisMin** { [number](dataTypes#number) } - 休眠时间下限 (毫秒) +- **millisMax** { [number](dataTypes#number) } - 休眠时间上限 (毫秒) +- **returns** { [void](dataTypes#void) } + +使当前线程休眠一段时间, 该时间随机落在 millisMin 和 millisMax 之间. + +```js +/* 随机休眠 3 - 5 秒钟. */ +sleep(3e3, 5e3); +``` + +### sleep(millis, bounds) + +**`6.2.0`** **`Overload 3/3`** **`Non-UI`** + +- **millis** { [number](dataTypes#number) } - 休眠时间 (毫秒) +- **bounds** { [NumberString](dataTypes#NumberString) | [string](dataTypes#string) } - 浮动值 +- **returns** { [void](dataTypes#void) } + +使当前线程休眠一段时间, 该时间随机落在 millis ± bounds 之间. +bounds 参数为 [数字字符串](dataTypes#NumberString) 类型 (如 "12"), 或在字符串开头附加 "±" 明确参数含义 (如 "±12"). + +```js +/* 随机休眠 3 - 5 秒钟 (即 4 ± 1 秒钟). */ +sleep(4e3, "1e3"); +sleep(4e3, "±1e3"); /* 同上. */ +``` + +--- + +## [m] currentPackage + +### currentPackage() + +**`A11Y`** + +- **returns** { [string](dataTypes#string) } + +获取最近一次监测到的应用包名, 并视为当前正在运行的应用包名. + +--- + +## [m] currentActivity + +### currentActivity() + +**`A11Y`** + +- **returns** { [string](dataTypes#string) } + +获取最近一次监测到的活动名称, 并视为当前正在运行的活动名称. + +--- + +## [m] setClip + +### setClip(text) + +- **text** { [string](dataTypes#string) } - 剪贴板内容 +- **returns** { [void](dataTypes#void) } + +设置系统剪贴板内容. + +> 参阅: [getClip](#m-getclip) / [UiObject#paste](uiObjectType#m-paste) + +--- + +## [m] getClip + +### getClip() + +- **returns** { [string](dataTypes#string) } - 系统剪贴板内容 + +需额外留意, 自 [Android API 29 (10) [Q]](apiLevel) 起, 剪贴板数据的访问将受到限制: + +为更好地保护用户隐私权, 除默认输入法及当前获取焦点的前置应用外, 均无法访问剪贴板数据. + +```js +setClip("test"); + +/* 安卓 10 以下: 打印 "test". */ +/* 安卓 10 及以上: 若 AutoJs6 前置, 打印 "test", 否则打印空字符串. */ +console.log(getClip()); +``` + +> 参阅: [setClip](#m-setclip) / [UiObject#paste](uiObjectType#m-paste) + +> 参阅: [Android Docs](https://developer.android.com/about/versions/10/privacy/changes#clipboard-data) + +--- + +## [m+] toast + +### toast(text) + +**`Overload 1/4`** **`Async`** + +- **text** { [string](dataTypes#string) } - 消息内容 +- **returns** { [void](dataTypes#void) } + +显示一个 [消息浮动框](https://developer.android.com/guide/topics/ui/notifiers/toasts?hl=zh-cn). + +消息框的显示默认是依次进行的: + +```js +/* 显示消息框 2 秒钟. */ +toast("hello"); +/* 显示消息框 2 秒钟, 且在前一个消息框消失后才显示. */ +toast("world"); +/* 显示消息框 2 秒钟, 且在前一个消息框消失后才显示. */ +toast("hello world"); +``` + +### toast(text, isLong) + +**`Overload 2/4`** **`Async`** + +- **text** { [string](dataTypes#string) } - 消息内容 +- **isLong = false** { "long" | "l" | "short" | "s" | [boolean](dataTypes#boolean) } - 是否以较长时间显示 +- **returns** { [void](dataTypes#void) } + +控制单个消息框显示时长: + +```js +toast("hello", 'long'); /* 显示消息框 3.5 秒钟. */ +toast("hello", true); /* 同上. */ +``` + +> 注: 仅有 [ 长 / 短 ] 两种时长, 此时长由安卓系统决定. +> 通常, 短时为 2 秒, 长时为 3.5 秒. + +### toast(text, isLong, isForcible) + +**`Overload 3/4`** **`Async`** + +- **text** { [string](dataTypes#string) } - 消息内容 +- **isLong = false** { "long" | "l" | "short" | "s" | [boolean](dataTypes#boolean) } - 是否以较长时间显示 +- **isForcible = false** { "forcible" | "f" | [boolean](dataTypes#boolean) } - 是否强制覆盖显示 +- **returns** { [void](dataTypes#void) } + +使用 "强制覆盖显示" 参数可立即显示消息框: + +```js +toast("hello"); +/* 显示消息框 2 秒钟, 且立即显示, 前一个消息框 "hello" 被 "覆盖". */ +toast("world", "short", "forcible"); +``` + +> 注: 强制覆盖仅对当前脚本有效, 对其他脚本及应用程序无效. + +### toast(text, isForcible) + +**`Overload 4/4`** **`Async`** + +- **text** { [string](dataTypes#string) } - 消息内容 +- **isForcible** { "forcible" | "f" } - 强制覆盖显示 (字符标识) +- **returns** { [void](dataTypes#void) } + +此方法相当于忽略 isLong 参数: + +```js +toast("hello"); +/* 显示消息框 2 秒钟, 且立即显示, 前一个消息框 "hello" 被 "覆盖". */ +toast("world", "forcible"); +``` + +> 注: 此方法的 isForcible 参数只能为具有明确意义的字符标识, 不能为 boolean 类型或其他类型, 否则 isForcible 将被视为 isLong. + +### toast.dismissAll() + +- **returns** { [void](dataTypes#void) } + +强制取消显示所有消息框. + +```js +toast("hello"); +toast("world"); +toast("of"); +toast("JavaScript"); + +sleep(1e3); + +/* "hello" 显示 1 秒后消失, "world" 及其他消息框均不再显示. */ +/* 若无 sleep 语句, 由于 toast 是异步的, 上述消息框均不会显示. */ +toast.dismissAll(); + +/* dismissAll 仅对已在队列中的消息框有效, 因此下述消息框正常显示. */ +toast("forcibly dismissed"); +``` + +> 注: 强制取消显示仅对当前脚本有效, 对其他脚本及应用程序无效. + +--- + +## [m] toastLog + +显示消息浮动框并在控制台打印消息. +相当于以下代码组合: + +```js +toast(text, ...args); +console.log(text); +``` + +因此, 方法重载与 [toast](#m-toast) 完全一致. + +> 注: 虽然 toast 方法异步, 但 console.log 方法同步, 因此 toastLog 方法也为同步. + +### toastLog(text) + +**`Overload 1/4`** + +- **text** { [string](dataTypes#string) } - 消息内容 +- **returns** { [void](dataTypes#void) } + +> 参阅: [toast(text)](#toasttext) + +### toastLog(text, isLong) + +**`Overload 2/4`** + +- **text** { [string](dataTypes#string) } - 消息内容 +- **isLong = false** { "long" | "l" | "short" | "s" | [boolean](dataTypes#boolean) } - 是否以较长时间显示 +- **returns** { [void](dataTypes#void) } + +> 参阅: [toast(text, isLong)](#toasttext-islong) + +### toastLog(text, isLong, isForcible) + +**`Overload 3/4`** + +- **text** { [string](dataTypes#string) } - 消息内容 +- **isLong = false** { "long" | "l" | "short" | "s" | [boolean](dataTypes#boolean) } - 是否以较长时间显示 +- **isForcible = false** { "forcible" | "f" | [boolean](dataTypes#boolean) } - 是否强制覆盖显示 +- **returns** { [void](dataTypes#void) } + +> 参阅: [toast(text, isLong, isForcible)](#toasttext-islong-isforcible) + +### toastLog(text, isForcible) + +**`Overload 4/4`** + +- **text** { [string](dataTypes#string) } - 消息内容 +- **isForcible** { "forcible" | "f" } - 强制覆盖显示 (字符标识) +- **returns** { [void](dataTypes#void) } + +> 参阅: [toast(text, isForcible)](#toasttext-isforcible) + +--- + +## [m] wait + +### wait(condition) + +**`6.2.0`** **`Overload 1/6`** **`A11Y?`** **`Non-UI`** + +- **condition** { [(() => any)](dataTypes#function) | [PickupSelector](dataTypes#pickupselector) } - 结束等待条件 +- **returns** { [boolean](dataTypes#boolean) } + +阻塞等待, 直到条件满足. +默认等待时间为 10 秒, 条件检查间隔为 200 毫秒. +若超时, 放弃等待, 并返回特定的条件超时结果 (如 false). +若超时之前条件得以满足, 结束等待, 并返回特定的条件满足结果 (如 true). + +> 注: 不同于 while 和 for 等循环语句的 "条件", +> 该方法的条件是结束等待条件, 只要不满足条件, 就一直等待. +> 而循环语句的条件, 是只要满足条件, 就一直循环. + +等待条件支持函数及选择器. + +函数示例, 等待设备屏幕关闭: + +```js +wait(function () { + return device.isScreenOff(); +}); + +/* 使用箭头函数. */ +wait(() => device.isScreenOff()); + +/* 使用 bind. */ +wait(device.isScreenOff.bind(device)); + +/* 对结果分支处理. */ +if (wait(() => device.isScreenOff())) { + console.log("等待屏幕关闭成功"); +} else { + console.log("等待屏幕关闭超时"); +} +``` + +选择器示例, 等待文本为 "立即开始" 的控件出现: + +```js +/* 以下三种方式为 Pickup 选择器的不同格式, 效果相同. */ +wait("立即开始"); +wait(content("立即开始")); /* 同上. */ +wait({ content: "立即开始" }); /* 同上. */ + +/* 对比上述函数方式. */ +wait(() => content("立即开始").exists()); +wait(() => pickup("立即开始", "?")); /* 同上. */ +``` + +等待条件的满足与否, 与函数返回值有关. +例如当函数返回 true 时, 等待条件即满足. + +下面列出不满足条件的几种返回值: +[ [false](dataTypes#boolean) / [null](dataTypes#null) / [undefined](dataTypes#undefined) / [NaN](https://developer.mozilla.org/zh-CN/docs/Glossary/NaN/) ] +除此之外的返回值均视为满足条件 (包括空字符串和数字 0 等). + +一种常见的错误用例, 即函数条件缺少返回值: + +```js +wait(() => { + if (device.isScreenOff()) { + console.log("屏幕已成功关闭"); + } +}); +``` + +上述示例中, 等待条件永远无法满足, 因函数一直返回 undefined. + +添加合适的返回值即可修正: + +```js +wait(() => { + if (device.isScreenOff()) { + console.log("屏幕已成功关闭"); + return true; + } +}); +``` + +> 参阅: [pickup](uiSelectorType#m-pickup) + +### wait(condition, limit) + +**`6.2.0`** **`Overload 2/6`** **`A11Y?`** **`Non-UI`** + +- **condition** { [(() => any)](dataTypes#function) | [PickupSelector](uiSelectorType#m-pickup) } - 结束等待条件 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **returns** { [boolean](dataTypes#boolean) } + +[wait(condition)](#waitcondition) 增加条件检测限制. +达到限制后, 表示等待超时, 并放弃等待. +限制分为 "次数限制" (limit < 100) 和 "时间限制" (limit >= 100). + +```js +/* 等待屏幕关闭, 最多检测屏幕状态 20 次. */ +wait(() => device.isScreenOff(), 20); /* limit < 100, 视为次数限制. */ +/* 等待屏幕关闭, 最多检测屏幕状态 5 秒钟. */ +wait(() => device.isScreenOff(), 5e3); /* limit >= 100, 视为时间限制. */ +``` + +### wait(condition, limit, interval) + +**`6.2.0`** **`Overload 3/6`** **`A11Y?`** **`Non-UI`** + +- **condition** { [(() => any)](dataTypes#function) | [PickupSelector](uiSelectorType#m-pickup) } - 结束等待条件 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **interval** { [number](dataTypes#number) } - 等待条件检测间隔 +- **returns** { [boolean](dataTypes#boolean) } + +[wait(condition, limit)](#waitcondition-limit) 增加条件检测间隔. +只要条件不满足, wait() 方法会持续检测, 直到条件满足或达到检测限制. +interval 参数用于设置条件检测之间的间歇时长, 默认为 200 毫秒. + +```text +检查条件 (不满足) - 间歇 - 检查条件 (不满足) - 间歇 - 检查条件... +``` + +```js +/* 等待屏幕关闭, 最多检测屏幕状态 20 次, 每次检查间歇 3 秒钟. */ +wait(() => device.isScreenOff(), 20, 3e3); +/* 等待屏幕关闭, 最多检测屏幕状态 20 次, 并采用不间断检测 (无间歇). */ +wait(() => device.isScreenOff(), 20, 0); +``` + +> 注: 在最后一次条件检查之后, 将不再发生间歇. +> 包括条件满足或达到检测限制. +> +> 例如在第三次检查时, 条件满足: +> 检查 (×) - 间歇 - 检查 (×) - 间歇 - 检查 (√) - 立即结束 wait() + +### wait(condition, callback) + +**`6.2.0`** **`Overload 4/6`** **`A11Y?`** **`Non-UI`** + +- **condition** { [(() => T)](dataTypes#function) | [PickupSelector](uiSelectorType#m-pickup) } - 结束等待条件 +- **callback** {{ + - then?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) + - else?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) +- }} - 等待结束回调对象 +- **returns** { [R](dataTypes#generic) extends [void](dataTypes#void) ? [boolean](dataTypes#boolean) : [R](dataTypes#generic) } +- **template** [T](dataTypes#generic), [R](dataTypes#generic) + +[wait(condition)](#waitcondition) 增加回调对象. + +回调对象集合了两个方法, then 与 else 分别对应等待成功与等待失败的情况: + +```js +wait(() => device.isScreenOff(), { + then: () => console.log("等待屏幕关闭成功"), + else: () => console.log("等待屏幕关闭超时"), +}); +``` + +两种方法都将最后一次检查结果作为实参, 可在方法体内直接使用: + +```js +/* 等待一个落在 99.99 到 100 区间的随机数. */ +wait(() => { + let num = Math.random() * 100; + return num > 99.99 && num; +}, { + then(o) { + console.log(`获取随机数成功, 数字是: ${o}`); + }, + else() { + console.log("获取 99.99 到 100 的随机数超时"); + }, +}); +``` + +> 注: else 回调方法的参数只能是 [ [false](dataTypes#boolean) / [null](dataTypes#null) / [undefined](dataTypes#undefined) / [NaN](https://developer.mozilla.org/zh-CN/docs/Glossary/NaN/) ], +> 因此 else 的参数几乎不会用到. + +需特别注意, 回调方法的返回值具有穿透性. +在回调方法内使用 return 语句, 将直接影响 wait() 的返回值 (undefined 除外). + +上述示例中, then 和 else 回调都没有返回值, 因此 wait() 返回值是 boolean 类型, 表示等待条件是否满足. +下述示例在回调函数中增加了返回值 (非 undefined), 则 wait() 也将返回这个值. + +```js +let result = wait(() => { + let num = Math.random() * 100; + return num > 99.99 && num; +}, { + then(o) { + console.log(`获取随机数成功`); + return o; + }, + else() { + console.log("获取 99.99 到 100 的随机数超时"); + return NaN; + }, +}); +result; /* 一个数字 (如 99.99732126036437) 或 NaN. */ +``` + +上述示例如果等待条件满足, 则返回 then 的返回值 (number 类型), +等待条件超时, 则返回 else 的返回值 (NaN, 也为 number 类型). + +如果去掉 else 的返回语句, 则等待条件超时后, wait() 将返回 false (boolean 类型). + +如需对 wait() 的返回值做进一步处理, 则建议两个回调方法的返回值类型一致: + +```js +wait(() => { + let num = Math.random() * 100; + return num > 99.99 && num; +}, { + then(o) { + return [ o - 1, o, o + 1 ]; + }, + else() { + /* 即使等待条件超时, 也可调用 forEach 方法. */ + return []; + }, +}).forEach(x => console.log(x)); +``` + +### wait(condition, limit, callback) + +**`6.2.0`** **`Overload 5/6`** **`A11Y?`** **`Non-UI`** + +- **condition** { [(() => T)](dataTypes#function) | [PickupSelector](uiSelectorType#m-pickup) } - 结束等待条件 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **callback** {{ + - then?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) + - else?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) +- }} - 等待结束回调对象 +- **returns** { [R](dataTypes#generic) extends [void](dataTypes#void) ? [boolean](dataTypes#boolean) : [R](dataTypes#generic) } +- **template** [T](dataTypes#generic), [R](dataTypes#generic) + +[wait(condition, callback)](#waitcondition-callback) 增加条件检测限制. + +> 参阅: [wait(condition, limit)](#waitcondition-limit) + +### wait(condition, limit, interval, callback) + +**`6.2.0`** **`Overload 6/6`** **`A11Y?`** **`Non-UI`** + +- **condition** { [(() => T)](dataTypes#function) | [PickupSelector](uiSelectorType#m-pickup) } - 结束等待条件 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **interval** { [number](dataTypes#number) } - 等待条件检测间隔 +- **callback** {{ + - then?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) + - else?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) +- }} - 等待结束回调对象 +- **returns** { [R](dataTypes#generic) extends [void](dataTypes#void) ? [boolean](dataTypes#boolean) : [R](dataTypes#generic) } +- **template** [T](dataTypes#generic), [R](dataTypes#generic) + +[wait(condition, limit, callback)](#waitcondition-callback) 增加条件检测间隔. + +> 参阅: [wait(condition, limit, interval)](#waitcondition-limit-interval) + +--- + +## [m] waitForActivity + +等待指定名称的 Activity 出现 (前置). +此方法相当于 `wait(() => currentActivity() === activityName, ...args)`, +因此其所有重载方法的结构与 wait 一致. +为节约篇幅, 将仅列出方法签名等重要信息. + +### waitForActivity(activityName) + +**`6.2.0`** **`Overload 1/6`** **`A11Y?`** **`Non-UI`** + +- **activityName** { [string](dataTypes#string) } - 目标活动名称 +- **returns** { [boolean](dataTypes#boolean) } + +> 参阅:[wait(condition)](#waitcondition) + +### waitForActivity(activityName, limit) + +**`6.2.0`** **`Overload 2/6`** **`A11Y?`** **`Non-UI`** + +- **activityName** { [string](dataTypes#string) } - 目标活动名称 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **returns** { [boolean](dataTypes#boolean) } + +> 参阅:[wait(condition, limit)](#waitcondition-limit) + +### waitForActivity(activityName, limit, interval) + +**`6.2.0`** **`Overload 3/6`** **`A11Y?`** **`Non-UI`** + +- **activityName** { [string](dataTypes#string) } - 目标活动名称 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **interval** { [number](dataTypes#number) } - 等待条件检测间隔 +- **returns** { [boolean](dataTypes#boolean) } + +> 参阅:[wait(condition, limit, interval)](#waitcondition-limit-interval) + +### waitForActivity(activityName, callback) + +**`6.2.0`** **`Overload 4/6`** **`A11Y?`** **`Non-UI`** + +- **activityName** { [string](dataTypes#string) } - 目标活动名称 +- **callback** {{ + - then?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) + - else?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) +- }} - 等待结束回调对象 +- **returns** { [R](dataTypes#generic) extends [void](dataTypes#void) ? [boolean](dataTypes#boolean) : [R](dataTypes#generic) } +- **template** [T](dataTypes#generic), [R](dataTypes#generic) + +> 参阅: [wait(condition, callback)](#waitcondition-callback) + +### waitForActivity(activityName, limit, callback) + +**`6.2.0`** **`Overload 5/6`** **`A11Y?`** **`Non-UI`** + +- **activityName** { [string](dataTypes#string) } - 目标活动名称 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **callback** {{ + - then?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) + - else?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) +- }} - 等待结束回调对象 +- **returns** { [R](dataTypes#generic) extends [void](dataTypes#void) ? [boolean](dataTypes#boolean) : [R](dataTypes#generic) } +- **template** [T](dataTypes#generic), [R](dataTypes#generic) + +> 参阅: [wait(condition, limit, callback)](#waitcondition-limit-callback) + +### waitForActivity(activityName, limit, interval, callback) + +**`6.2.0`** **`Overload 6/6`** **`A11Y?`** **`Non-UI`** + +- **activityName** { [string](dataTypes#string) } - 目标活动名称 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **interval** { [number](dataTypes#number) } - 等待条件检测间隔 +- **callback** {{ + - then?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) + - else?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) +- }} - 等待结束回调对象 +- **returns** { [R](dataTypes#generic) extends [void](dataTypes#void) ? [boolean](dataTypes#boolean) : [R](dataTypes#generic) } +- **template** [T](dataTypes#generic), [R](dataTypes#generic) + +> 参阅: [wait(condition, limit, interval, callback)](#waitcondition-limit-interval-callback) + +--- + +## [m] waitForPackage + +等待指定包名的应用出现 (前置). +此方法相当于 `wait(() => currentPackage() === packageName, ...args)`, +因此其所有重载方法的结构与 wait 一致. +为节约篇幅, 将仅列出方法签名等重要信息. + +### waitForPackage(packageName) + +**`6.2.0`** **`Overload 1/6`** **`A11Y?`** **`Non-UI`** + +- **packageName** { [string](dataTypes#string) } - 目标应用包名 +- **returns** { [boolean](dataTypes#boolean) } + +> 参阅:[wait(condition)](#waitcondition) + +### waitForPackage(packageName, limit) + +**`6.2.0`** **`Overload 2/6`** **`A11Y?`** **`Non-UI`** + +- **packageName** { [string](dataTypes#string) } - 目标应用包名 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **returns** { [boolean](dataTypes#boolean) } + +> 参阅:[wait(condition, limit)](#waitcondition-limit) + +### waitForPackage(packageName, limit, interval) + +**`6.2.0`** **`Overload 3/6`** **`A11Y?`** **`Non-UI`** + +- **packageName** { [string](dataTypes#string) } - 目标应用包名 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **interval** { [number](dataTypes#number) } - 等待条件检测间隔 +- **returns** { [boolean](dataTypes#boolean) } + +> 参阅:[wait(condition, limit, interval)](#waitcondition-limit-interval) + +### waitForPackage(packageName, callback) + +**`6.2.0`** **`Overload 4/6`** **`A11Y?`** **`Non-UI`** + +- **packageName** { [string](dataTypes#string) } - 目标应用包名 +- **callback** {{ + - then?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) + - else?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) +- }} - 等待结束回调对象 +- **returns** { [R](dataTypes#generic) extends [void](dataTypes#void) ? [boolean](dataTypes#boolean) : [R](dataTypes#generic) } +- **template** [T](dataTypes#generic), [R](dataTypes#generic) + +> 参阅: [wait(condition, callback)](#waitcondition-callback) + +### waitForPackage(packageName, limit, callback) + +**`6.2.0`** **`Overload 5/6`** **`A11Y?`** **`Non-UI`** + +- **packageName** { [string](dataTypes#string) } - 目标应用包名 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **callback** {{ + - then?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) + - else?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) +- }} - 等待结束回调对象 +- **returns** { [R](dataTypes#generic) extends [void](dataTypes#void) ? [boolean](dataTypes#boolean) : [R](dataTypes#generic) } +- **template** [T](dataTypes#generic), [R](dataTypes#generic) + +> 参阅: [wait(condition, limit, callback)](#waitcondition-limit-callback) + +### waitForPackage(packageName, limit, interval, callback) + +**`6.2.0`** **`Overload 6/6`** **`A11Y?`** **`Non-UI`** + +- **packageName** { [string](dataTypes#string) } - 目标应用包名 +- **limit** { [number](dataTypes#number) } - 等待条件检测限制 +- **interval** { [number](dataTypes#number) } - 等待条件检测间隔 +- **callback** {{ + - then?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) + - else?(result?: [T](dataTypes#generic)): [R](dataTypes#generic) +- }} - 等待结束回调对象 +- **returns** { [R](dataTypes#generic) extends [void](dataTypes#void) ? [boolean](dataTypes#boolean) : [R](dataTypes#generic) } +- **template** [T](dataTypes#generic), [R](dataTypes#generic) + +> 参阅: [wait(condition, limit, interval, callback)](#waitcondition-limit-interval-callback) + +--- + +## [m] exit + +### exit() + +停止脚本运行. + +通过抛出 `ScriptInterruptedException` 异常实现脚本停止. +因此用 `try` 包裹 `exit()` 语句将会使脚本继续运行片刻: + +```js +try { + log('exit now'); + exit(); + log("after"); /* 控制台不会打印 "after". */ +} catch (e) { + e.javaException instanceof ScriptInterruptedException; // true +} +while (true) log("hello"); /* 控制台将打印一定数量的 "hello". */ +``` + +如果编写的脚本对 "是否停止" 的状态十分敏感, +即要求 exit() 之后的代码一定不被执行, +则可通过附加状态判断实现上述需求: + +```js +if (!isStopped()) { + // 其他代码... +} +``` + +因此上述示例如果加上状态判断, "hello" 将不会被打印: + +```js +try { + log('exit now'); + exit(); +} catch (_) { + // Ignored. +} +if (!isStopped()) { + while (true) log("hello"); /* 控制台不会打印 "hello". */ +} +``` + +除了 [isStopped](#isstopped), 还可通过 threads 或 engines 获取停止状态: + +```js +/* threads. */ +if (!threads.currentThread().isInterrupted()) { + // 其他代码... +} + +/* engines. */ +if (!engines.myEngine().isStopped()) { + // 其他代码... +} +``` + +--- + +## [m] random + +### random() + +- **returns** { [number](dataTypes#number) } + +与 Math.random() 相同, 返回落在 [0, 1) 区间的随机数字. + +### random(min, max) + +- **min** { [number](dataTypes#number) } - 随机数下限 +- **max** { [number](dataTypes#number) } - 随机数上限 +- **returns** { [number](dataTypes#number) } + +返回落在 [min, max] 区间的随机数字. + +> 注: random(min, max) 右边界闭合, 而 random() 右边界开放. + +--- + +## [m] requiresApi + +### requiresApi(api) + +- **api** { [number](dataTypes#number) } - 安卓 API 级别 + +脚本运行的最低 API 级别要求. + +例如要求脚本运行不低于 [Android API 30 (11) [R]](apiLevel): + +```js +requiresApi(30); +requiresApi(util.versionCodes.R.apiLevel); /* 同上. */ +requiresApi(android.os.Build.VERSION_CODES.R); /* 同上. */ +``` + +若 API 级别不符合要求, 脚本抛出异常并停止继续执行. + +> 参阅: +> - [Android API Level - 安卓 API 级别](apiLevel) +> - [util.versionCodes](util#versioncodes) + +--- + +## [m] requiresAutojsVersion + +### requiresAutojsVersion(versionName) + +- **versionName** { [string](dataTypes#string) } - AutoJs6 版本名称 +- **returns** { [void](dataTypes#void) } + +脚本运行的最低 AutoJs6 版本要求 (版本名称). + +```js +requiresAutojsVersion("6.2.0"); +``` + +可通过 `autojs.versionName` 查看 AutoJs6 版本名称. + +> 参阅: [autojs.versionName](autojs#versionname) + +### requiresAutojsVersion(versionCode) + +- **versionCode** { [number](dataTypes#number) } - AutoJs6 版本号 +- **returns** { [void](dataTypes#void) } + +脚本运行的最低 AutoJs6 版本要求 (版本号). + +```js +requiresAutojsVersion(1024); +``` + +可通过 `autojs.versionCode` 查看 AutoJs6 版本号. + +> 参阅: [autojs.versionCode](autojs#versioncode) + +## [m] importPackage + +### importPackage(...pkg) + +- **pkg** { ...( [string](dataTypes#string) | [object](dataTypes#object) ) } - 需导入的 Java 包 +- **returns** { [void](dataTypes#void) } + +```js +/* 导入一个 Java 包. */ + +importPackage(java.lang); +importPackage('java.lang'); /* 同上. */ + +/* 导入多个 Java 包. */ + +importPackage(java.io); +importPackage(java.lang); +importPackage(java.util); + +importPackage(java.io, java.lang, java.util); /* 同上. */ +``` + +> 参阅: [访问 Java 包和类](scriptingJava#访问-Java-包和类) + +## [m] importClass + +### importClass(...cls) + +- **cls** { ...( [string](dataTypes#string) | [object](dataTypes#object) ) } - 需导入的 Java 类 +- **returns** { [void](dataTypes#void) } + +```js +/* 导入一个 Java 类. */ + +importClass(java.lang.Integer); +importClass('java.lang.Integer'); /* 同上. */ + +/* 导入多个 Java 类. */ + +importClass(java.io.File); +importClass(java.lang.Integer); +importClass(java.util.HashMap); + +importClass( + java.io.File, + java.lang.Integer, + java.util.HashMap, +); /* 同上. */ +``` + +> 参阅: [访问 Java 包和类](scriptingJava#访问-Java-包和类) + +## [m] isStopped + +### isStopped() + +- **returns** { [boolean](dataTypes#boolean) } + +检测脚本当前线程是否已停止. + +## [m] selector + +### selector() + +- **returns** { [UiSelector](uiSelectorType) } + +构建一个 "空" [选择器](uiSelectorType). \ No newline at end of file diff --git a/api/globals.md b/api/globals.md deleted file mode 100644 index 1ff0f40..0000000 --- a/api/globals.md +++ /dev/null @@ -1,223 +0,0 @@ -# 全局变量与函数 - -全局变量和函数在所有模块中均可使用。 但以下变量的作用域只在模块内,详见 [module](modules): - -* exports -* module -* require() - 以下的对象是特定于 Auto.js 的。 有些内置对象是 JavaScript 语言本身的一部分,它们也是全局的。 - -一些模块中的函数为了使用方便也可以直接全局使用,这些函数在此不再赘述。例如timers模块的setInterval, setTimeout等函数。 - -## sleep(n) - -* `n` {number} 毫秒数 - -暂停运行n**毫秒**的时间。1秒等于1000毫秒。 - -``` -//暂停5秒 -sleep(5000); -``` - -## currentPackage() - -* 返回 {string} - -返回最近一次监测到的正在运行的应用的包名,一般可以认为就是当前正在运行的应用的包名。 - -此函数依赖于无障碍服务,如果服务未启动,则抛出异常并提示用户启动。 - -## currentActivity() - -* 返回 {string} - -返回最近一次监测到的正在运行的Activity的名称,一般可以认为就是当前正在运行的Activity的名称。 - -此函数依赖于无障碍服务,如果服务未启动,则抛出异常并提示用户启动。 - -## setClip(text) - -* `text` {string} 文本 - -设置剪贴板内容。此剪贴板即系统剪贴板,在一般应用的输入框中"粘贴"既可使用。 - -``` -setClip("剪贴板文本"); -``` - -## getClip() - -* 返回 {string} - -返回系统剪贴板的内容。 - -``` -toast("剪贴板内容为:" + getClip()); -``` - -## toast(message) - -* message {string} 要显示的信息 - -以气泡显示信息message几秒。(具体时间取决于安卓系统,一般都是2秒) - -注意,信息的显示是"异步"执行的,并且,不会等待信息消失程序才继续执行。如果在循环中执行该命令,可能出现脚本停止运行后仍然有不断的气泡信息出现的情况。 -例如: - -``` -for(var i = 0; i < 100; i++){ - toast(i); -} -``` - -运行这段程序以后,会很快执行完成,且不断弹出消息,在任务管理中关闭所有脚本也无法停止。 -要保证气泡消息才继续执行可以用: - -``` -for(var i = 0; i < 100; i++){ - toast(i); - sleep(2000); -} -``` - -或者修改toast函数: - -``` -var _toast_ = toast; -toast = function(message){ - _toast_(message); - sleep(2000); -} -for(var i = 0; i < 100; i++){ - toast(i); -} -``` - -## toastLog(message) - -* message {string} 要显示的信息 - -相当于`toast(message);log(message)`。显示信息message并在控制台中输出。参见console.log。 - -## waitForActivity(activity[, period = 200]) - -* `activity` Activity名称 -* `period` 轮询等待间隔(毫秒) - -等待指定的Activity出现,period为检查Activity的间隔。 - -## waitForPackage(package[, period = 200]) - -* `package` 包名 -* `period` 轮询等待间隔(毫秒) - -等待指定的应用出现。例如`waitForPackage("com.tencent.mm")`为等待当前界面为微信。 - -## exit() - -立即停止脚本运行。 - -立即停止是通过抛出`ScriptInterrupttedException`来实现的,因此如果用`try...catch`把exit()函数的异常捕捉,则脚本不会立即停止,仍会运行几行后再停止。 - -## random(min, max) - -* `min` {number} 随机数产生的区间下界 -* `max` {number} 随机数产生的区间上界 -* 返回 {number} - -返回一个在[min...max]之间的随机数。例如random(0, 2)可能产生0, 1, 2。 - -## random() - -* 返回 {number} - -返回在[0, 1)的随机浮点数。 - -## requiresApi(api) - -* `api` Android版本号 - -表示此脚本需要Android API版本达到指定版本才能运行。例如`requiresApi(19)`表示脚本需要在Android 4.4以及以上运行。 - -调用该函数时会判断运行脚本的设备系统的版本号,如果没有达到要求则抛出异常。 - -可以参考以下Android API和版本的对照表: - -平台版本: API级别 - -Android 7.0: 24 - -Android 6.0: 23 - -Android 5.1: 22 - -Android 5.0: 21 - -Android 4.4W: 20 - -Android 4.4: 19 - -Android 4.3: 18 - -## requiresAutojsVersion(version) - -* `version` {string} | {number} Auto.js的版本或版本号 - -表示此脚本需要Auto.js版本达到指定版本才能运行。例如`requiresAutojsVersion("3.0.0 Beta")`表示脚本需要在Auto.js 3.0.0 Beta以及以上运行。 - -调用该函数时会判断运行脚本的Auto.js的版本号,如果没有达到要求则抛出异常。 - -version参数可以是整数表示版本号,例如`requiresAutojsVersion(250)`;也可以是字符串格式表示的版本,例如"3.0.0 Beta", "3.1.0 Alpha4", "3.2.0"等。 - -可以通过`app.autojs.versionCode`和`app.autojs.versionName`获取当前的Auto.js版本号和版本。 - -## runtime.requestPermissions(permissions) - -* `permissions` {Array} 权限的字符串数组 - -动态申请安卓的权限。例如: - -``` -//请求GPS权限 -runtime.requestPermissions(["access_fine_location"]); -``` - -尽管安卓有很多权限,但必须写入Manifest才能动态申请,为了防止权限的滥用,目前Auto.js只能额外申请两个权限: - -* `access_fine_location` GPS权限 -* `record_audio` 录音权限 - -您可以通过APK编辑器来增加Auto.js以及Auto.js打包的应用的权限。 - -安卓所有的权限列表参见[Permissions Overview](https://developer.android.com/guide/topics/permissions/overview)。(并没有用) - -## runtime.loadJar(path) - -* `path` {string} jar文件路径 - -加载目标jar文件,加载成功后将可以使用该Jar文件的类。 - -``` -// 加载jsoup.jar -runtime.loadJar("./jsoup.jar"); -// 使用jsoup解析html -importClass(org.jsoup.Jsoup); -log(Jsoup.parse(files.read("./test.html"))); -``` - -(jsoup是一个Java实现的解析Html DOM的库,可以在[Jsoup](https://jsoup.org/download)下载) - -## runtime.loadDex(path) - -* `path` {string} dex文件路径 - -加载目标dex文件,加载成功后将可以使用该dex文件的类。 - -因为加载jar实际上是把jar转换为dex再加载的,因此加载dex文件会比jar文件快得多。可以使用Android SDK的build tools的dx工具把jar转换为dex。 - -## context - -全局变量。一个android.content.Context对象。 - -注意该对象为ApplicationContext,因此不能用于界面、对话框等的创建。 \ No newline at end of file diff --git a/api/glossaries.md b/api/glossaries.md new file mode 100644 index 0000000..c560b45 --- /dev/null +++ b/api/glossaries.md @@ -0,0 +1,566 @@ +# 术语 (Glossaries) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +## 内置模块 + +AutoJs6 内置模块指脚本可全局使用的 JavaScript 模块. +这些模块多数已在文档中列出, 如 `app`, `images`, `device` 等. + +### 查看内置模块源代码 + +除 [直接查看开源代码](https://github.com/SuperMonster003/AutoJs6/tree/master/app/src/main/assets/modules) 外, 还可以将内置模块解压到本地存储后查看: +下载 [AutoJs6 APK](https://github.com/SuperMonster003/AutoJs6/releases/latest) 并使用压缩软件将 APK 内的 `\assets\modules` 文件夹解压到本地. +模块通常以 `__%name%__.js` 格式命名, 其中 `%name%` 对应模块名. +可使用文本编辑器等软件查看模块源代码. + +### 修改或增加内置模块 + +> 注: 此小节内容可能需要用户具备一定的编程基础及开发经验. + +> 注: 此操作需要重新打包生成 `新的 AutoJs6 APK` (下文作 `新生 APK`). +> 因 `新生 APK` 包名发生变化, 需卸载已安装的 `开源 AutoJs6 APK` (下文作 `开源 APK`) 后再安装 `新生 APK`. +> 当 `开源 APK` 出现新版本时, 同样需卸载 `新生 APK` 才能安装新版本的 `开源 APK`. +> 此时, 修改或增加的内置模块将失效. +> 如欲将自己的代码整合到 `开源 APK` 中, 可向开源项目提交 [Pull Request (PR)](https://github.com/SuperMonster003/AutoJs6/pulls). + +克隆 (Clone) [AutoJs6 源码](https://github.com/SuperMonster003/AutoJs6). +使用 [Android Studio](https://developer.android.com/studio/archive) 打开并完成项目构建 (Build). +定位 `\app\src\main\assets\modules` 目录. + +#### 修改模块 + +修改目录中的模块代码后直接打包生成新的 APK. + +#### 增加模块 + +以增加一个 date 模块为例, 该模块有一个 `date.toFullTimeString()` 方法. + +在 `\app\src\main\assets\modules` 目录新建 `__date__.js` 文件, 此文件将作为增加的内置模块. + +供参考的文件内容: + +```js +module.exports = function () { + return { + toFullTimeString() { + let now = new Date(); + let pad = x => x.toString().padStart(2, '0'); + return [ now.getHours(), now.getMinutes(), now.getSeconds() ].map(pad).join(':'); + }, + }; +}; +``` + +打开 "初始化脚本", 即 `\app\src\main\assets\init.js`. +将 date 模块添加到 "初始化脚本" 中: + +```js +/* ... */ + +let $ = { + /* ... */ + bindModules() { + _.bind([ + /* ... */ + + [ 'date', 'RootAutomator', 'floaty', /* 其他模块... */ ], + + /* ... */ + ]); + }, + /* ... */ +}; + +/* ... */ +``` + +添加完成后即可打包生成新的 APK. + +## 编译器 + +语法编译器是一个能够逐行读取代码的程序. +它了解代码如何匹配编程语言所定义的语法, 以及代码应该做什么. + +## JavaScript 引擎 + +JavaScript 引擎是一个计算机程序. +它接收 JavaScript 源代码并将其编译成 CPU 可以理解的二进制指令 (机器码). + +### 引擎与运行环境 + +运行环境也称为运行时环境, 引擎需要在运行环境中 + +JavaScript 引擎通常由浏览器供应商开发, 主流浏览器通常有自己开发的引擎: + +- Chrome - V8 +- Firefox - SpiderMonkey +- IE - Chakra + +## 运行时 + +即 `Runtime`. + +Runtime 是一个通用术语, 指代码运行所需的 [ 库 / 框架 / 平台 ]. + +> 参阅: [runtime](runtime) (全局对象) + +## 上下文 + +Context. + +> 参阅: [context](context) (全局对象) + +## 字符串模式 + +表示需要匹配指定正则表达式的字符串. + +如字符串模式为 `/\d/`, 则要求给定的字符串 `str` 满足以下语句: + +```js +/\d/.test(str) === true; +``` + +因此以下示例均满足要求: +`"1"`, `"1a"`, `"a1"`, `"hello 2011"`. + +字符串模式支持正则表达式的 [标记参数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions#%E9%80%9A%E8%BF%87%E6%A0%87%E5%BF%97%E8%BF%9B%E8%A1%8C%E9%AB%98%E7%BA%A7%E6%90%9C%E7%B4%A2), 如 `/hello/i`. + +## NaN + +NaN 表示 "不是一个数字", 即 **N**ot **A** **N**umber. + +NaN 是一个数值, 在 JavaScript 中可以使用 `isNaN` 或 `Number.isNaN` 检测: + +```js +let n = 0 / 0; +isNaN(n); // true +Number.isNaN(n); // true + +let m = null; +isNaN(m); // false +Number.isNaN(m); // false + +let l = undefined; +isNaN(l); // true +Number.isNaN(l); // false +``` + +> 参阅: [MDN #术语](https://developer.mozilla.org/zh-CN/docs/Glossary/NaN/) / [MDN #全局对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/NaN/) / [isNaN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/isNaN) / [Number.isNaN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) + +## 正则表达式 + +也作 [ 正则 / Regular Expression / RegEx / REGEX / RegExp / REX (非正式) ] 等. + +正则表达式字面量在 JavaScript 中用一对 `/` 符号表示, +如 `/\d/`, `/^[0-9a-f]{6}$/`, `/[bc]+?(?=y{2,})/i` 等. + +> 参阅: [MDN #指南](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions) + +## Truthy + +Truthy (真值) 指 `Boolean(truthy)` 返回 `true` 的值. +从另一个角度看, [Falsy (假值)](#falsy) 以外的任何值都为真值. + +> 参阅: [MDN #术语](https://developer.mozilla.org/zh-CN/docs/Glossary/Truthy/) + +## Falsy + +目前 (2022/08), JavaScript 共有 8 个 Falsy (假值): + +1. false ([boolean](dataTypes#boolean)) +2. 0 ([number](dataTypes#number)) +3. -0 ([number](dataTypes#number)) +4. 0n ([bigint](#bigint)) +5. [空字符串](#空字符串) +6. [null](dataTypes#null) +7. [undefined](dataTypes#undefined) +8. [NaN](#nan) + +需留意 `0` 与 `-0` 是不同的值: + +```js +0 === -0; // true +Object.is(0, -0); // false +Object.is(0n, -0n); // true + +let n = -0; +n.toString(); // "0" +Object.is(n, -0) ? `-${n}` : `${n}`; // "-0" +``` + +> 参阅: [MDN #术语](https://developer.mozilla.org/zh-CN/docs/Glossary/Falsy/) / [Object.is](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is) + +## 空字符串 + +通常用 `""` 表示空字符串, 它是一个长度为 0 的 string 类型数据. + +以下几种表示方式均可代表空字符串: + +```js +[ "", '', ``, String() ]; +``` + +## BigInt + +一种可以表示任意精度格式整数的数字类型. + +如 `3n`, `16777216n`, `-1n` 均合法. +如 `3.1n`, `2ne5`, `2e5n` 均不合法. + +> 参阅: [MDN #术语](https://developer.mozilla.org/zh-CN/docs/Glossary/BigInt) / [MDN #全局对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt) + +## 枚举 + +枚举是组织收集有关联变量的一种方式, 许多程序语言如 [ C / C# / Java ] 等都有枚举数据类型. + +## 内置对象 + +又称 [ 原生对象 / 标准内置对象 ], 内置对象与宿主无关, 是独立于宿主环境的 ECMAScript 实现提供的对象. +它们在 ECMAScript 程序开始执行前就存在, 本身就是实例化内置对象, 开发者无需再去实例化. +内置对象是原生对象的子集, 如常用的 [ Object / Function / Array / String / Boolean / Number / Date / RegExp / Error / Math / JSON ] 等都是内置对象. + +> 参阅: [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects) + +## 内置对象扩展 + +对 [内置对象](#内置对象) 的扩展主要有两种类型, 属性扩展及原型扩展. + +属性扩展: + +```js +Object.saySomething = function (content) { + console.log(content); +}; + +Math.sum = function (x, y) { + return +x + +y; +}; +``` + +调用时直接使用 `A.b` 形式: + +```js +Object.saySomething("hello"); /* print "hello". */ +console.log(Math.sum(2, 3)); // 5 +``` + +原型扩展: + +```js +Array.prototype.sorted = function () { + return this.slice().sort(); +}; +Number.prototype.toFixedNum = function (fraction) { + return +this.toFixed(fraction); +}; +``` + +调用时可在相应对象实例上使用: + +```js +let arr = [ 1, 2, 9, 3 ]; +console.log(arr.sorted()); // [ 1, 2, 3, 9 ] +console.log(arr); // [ 1, 2, 9, 3 ] + +let num = 375.201; +console.log(num.toFixed(2)); // "375.20" +console.log(num.toFixedNum(2)); // 375.2 +``` + +上述扩展均为自定义扩展, 它们实现了自定义的属性或方法, 往往是针对个人项目使用的. +而针对 ECMAScript 规范中的新功能的扩展, 则被称为 [填泥 (Polyfill)](#polyfill). + +> 注: 扩展内置对象往往是 **危险** 的. +> +> 扩展 JavaScript 原生对象意味着将属性或方法添加到其原型或内置对象上, +> 其潜在的危险包括但不限于以下几种情况: +> +> 1. 无意中修改或覆盖了 JavaScript 标准的内置方法 +> 2. 自定义扩展方法被定义者或合作开发者修改后需修改大量依赖代码甚至出错 +> 3. 导入库时可能存在与本地同名扩展方法冲突 +> 4. 导入多个库时可能存在库之间的同名扩展方法冲突 +> 5. 未来标准更新后可能与已有扩展方法冲突 +> +> 因此建议使用模块化编程替代对象扩展. +> 如确实有对象扩展需求, 建议新建一个与原生对象名称相似但不同的对象进行扩展, 如下文示例. + +上述扩展方式均可采用更安全 (但使用起来可能相对复杂) 的方式实现: + +```js +let Objectx = {}; + +Objectx.saySomething = function (content) { + console.log(content); +}; + +Objectx.saySomething("hello"); /* print "hello". */ + +let Mathx = {}; + +Mathx.sum = function (x, y) { + return +x + +y; +}; + +console.log(Mathx.sum(2, 3)); // 5 + +let Arrayx = {}; + +Arrayx.sorted = function (arr) { + return arr.slice().sort(); +}; + +let arr = [ 1, 2, 9, 3 ]; +console.log(Arrayx.sorted(arr)); // [ 1, 2, 3, 9 ] + +let Numberx = {}; + +Numberx.toFixedNum = function (num, fraction) { + return +num.toFixed(fraction); +}; + +let num = 375.201; +console.log(Numberx.toFixedNum(num, 2)); // 375.2 +``` + +AutoJs6 默认提供了一些内置对象扩展, 如 [ [Arrayx](arrayx), [Numberx](numberx), [Mathx](mathx) ] 等. + +这些扩展默认是安全的, 统一为 `Ax.b` 的调用方式, +如 `Arrayx.intersect`, `Mathx.sum`, `Numberx.clamp`. + +```js +console.log(Arrayx.intersect([ 1, 2, 3, 4 ], [ 1, 3, 5 ])); // [ 1, 3 ] +console.log(Numberx.clamp(Math.random(), [ 0.3, 0.5 ])); // e.g. 0.4251169347959409 +``` + +而另一种不安全的扩展, 即直接扩展内置对象, 可实现更为便捷的调用: + +```js +console.log([ 1, 2, 3, 4 ].intersect([ 1, 3, 5 ])); // [ 1, 3 ] +console.log(Math.random().clamp([ 0.3, 0.5 ])); // e.g. 0.4251169347959409 +``` + +如需默认进行直接扩展, 可选择以下任一方式: + +- 在脚本中加入代码片段: `plugins.extendAll();` +- AutoJs6 应用设置 - 扩展性 - JavaScript 内置对象扩展 - [ 启用 ] + +进行直接扩展后, 所有扩展属性和方法会按需附加到内置对象或其原型上, 详情参考每个扩展对象的小节内容. +直接扩展是不安全的, 需谨慎使用. + +> 参阅: [StackOverflow](https://stackoverflow.com/questions/14034180/why-is-extending-native-objects-a-bad-practice) / [lucybain.com](https://lucybain.com/blog/2014/js-extending-built-in-objects/) + +## Polyfill + +又称 [ 代码填泥 / 填泥 / 腻子 / 泥子 ], 是一个完整的代码块, 用于为不支持原生 ECMAScript 新功能的环境提供功能支持. +详见 [代码填泥](polyfill) 章节. + +## Shim + +又称 [ 代码垫片 / 垫片 / 填隙片 ], 是一种小型函数库, 可以用来截取 API 调用或修改传入参数, 最后自行处理对应操作或者将操作交由其它地方执行. +垫片可以在新环境中支持老 API, 也可以在老环境里支持新 API. +一些程序并没有针对某些平台开发, 也可以通过使用垫片来辅助运行. + +Shim 与 Polyfill 的不同, 可参阅 [代码填泥](polyfill) 章节. + +## 应用资源 + +应用资源指代码使用的附加文件和静态内容, 如 [ 位图 / 布局定义 / 界面字符串 / 动画说明 ] 等. + +通常, 应用资源与代码是分离的, 以便于独立维护. +资源可进行分组并放入专门命名的资源目录中, 如 [ animator / color / drawable / layout / values / menu ] 等. +在运行时, Android 会根据当前配置使用合适的资源, 如根据屏幕尺寸提供不同的界面布局或根据语言设置提供不同的字符串. + +将应用资源分离之后, 可使用项目的 R 类中生成的 [资源 ID](#资源-ID) 对其进行访问. + +```js +console.log(context.getString(R.string.text_app_name_powerpoint)); // PowerPoint +console.log(R.id.explorer_item_list); /* e.g. 2131296535 */ +console.log(`0x${java.lang.Integer.toHexString(R.id.explorer_item_list)}`); /* e.g. 0x7f090117 */ +``` + +> 参阅: [Android Docs](https://developer.android.com/guide/topics/resources/providing-resources?hl=zh-cn) + +## 资源 ID + +在代码中使用 R 类的子类中的静态整数可访问 [应用资源](#应用资源): + +```js +/* 资源 ID 是一个整数. */ +console.log(R.string.text_app_name_autojspro); /* e.g. 2131887020 */ +``` + +根据资源类型获取类型值 (string): + +```js +console.log(context.getString(R.string.text_app_name_autojspro)); /* e.g. AutoJsPro */ +``` + +根据资源类型获取类型值 (drawable): + +```js +/* 绘制一个淡绿色的铃铛图标. */ + +'ui'; + +ui.layout( + +); + +ui.img.setImageResource(R.drawable.ic_ali_notification); +``` + +在 XML 中也可访问 `资源 ID`: + +```js +/* 绘制一个淡绿色的铃铛图标. */ + +'ui'; + +ui.layout( + +); +``` + +将 `资源 ID` 的十六进制值与 `0x` 前缀组合, 可作为控件的 [idHex](uiObjectType#m-idhex) 信息: + +```js +/* 直接对资源 ID 值组合. */ +console.log(`0x${java.lang.Integer.toHexString(R.id.explorer_item_list)}`); /* e.g. 0x7f090117 */ + +/* 在 AutoJs6 主页找到对应控件并获取其 idHex 值. */ +console.log(idMatch(/explorer_item_list/).findOnce().idHex()); /* e.g. 0x7f090117 */ +``` + +## 控件层级 + +类似 HTML 的层级绘制, 安卓的视图 (View) 嵌套也会形成层级, 外部视图成为其内部视图的父控件 (Parent Node), 内部视图成为外部视图的子控件 (Child Node). + +在 AutoJs6 中, 控件由 [UiObject](uiObjectType) 表示. + +以下方式可获取当前窗口的控件层级: + +- 使用 AutoJs6 获取 + - AutoJs6 主页侧拉抽屉 -> 悬浮窗 [开启] -> 悬浮图标 [点击] + - 方案 A: 蓝色按钮 [点击] -> (布局范围分析) -> 控件图示 [点击] -> 在布局层次中查看 + - 方案 B: 蓝色按钮 [长按] -> 布局层次分析 [点击] + +- 使用 uiautomatorviewer 工具获取 + - 位置 (以 Windows 为例): `"%ANDROID_HOME%\tools\bin\uiautomatorviewer.bat"` + +- 使用 Android Studio 的 Layout Inspector 工具获取 + - Android Studio -> Tools -> Layout Inspector + +- 使用 ADB Shell 的 dumpsys 指令获取 + - 使用 AutoJs6 执行代码 `console.log(shell('dumpsys activity top').result);` + +## 信息集控件 + +信息集控件指拥有一个非 null 的 [无障碍节点信息集 (AccessibilityNodeInfo.CollectionInfo)](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.CollectionInfo) 实例的控件. + +```js +let info = w.getCollectionInfo(); +console.log(info); +``` + +信息集控件包含一系列子控件, 它们类似 HTML 表格那样按行列方式分布. +例如垂直列表是一个信息集控件, 它有一列多行作为子控件; 表格也是一个信息集控件, 它有多列多行作为子控件. +这些子控件均拥有非 null 的 [无障碍节点子项信息集 (AccessibilityNodeInfo.CollectionItemInfo)](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.CollectionItemInfo): + +```js +/* 通常, 在 AutoJs6 主页即可获取到至少一个信息集控件. */ +scrollable().find().some((w) => { + let info = w.getCollectionInfo(); + if (info !== null) { + + /* 展示常用的信息集实例属性或方法. */ + + console.log(`rowCount: ${info.getRowCount()}`); /* 对应 w.rowCount() 封装方法. */ + console.log(`columnCount: ${info.getColumnCount()}`); /* 对应 w.columnCount() 封装方法. */ + + w.children().forEach((c) => { + let itemInfo = c.getCollectionItemInfo(); + if (itemInfo !== null) { + + /* 展示常用的子项信息集实例属性或方法. */ + + console.log(c.bounds()); + console.log(`rowIndex: ${itemInfo.getRowIndex()}`); /* 对应 w.row() 封装方法. */ + console.log(`columnIndex: ${itemInfo.getColumnIndex()}`); /* 对应 w.column() 封装方法. */ + console.log(`rowSpan: ${itemInfo.getRowSpan()}`); /* 对应 w.rowSpan() 封装方法. */ + console.log(`columnSpan: ${itemInfo.getColumnSpan()}`); /* 对应 w.columnSpan() 封装方法. */ + console.log(`selected: ${itemInfo.isSelected()}`); + console.log(`rowTitle: ${itemInfo.getRowTitle()}`); + console.log(`columnTitle: ${itemInfo.getColumnTitle()}`); + console.log('-'.repeat(33)); + } + }); + + return /* @some */ true; + } +}); +``` + +部分属性或方法: + +- `[m#]` getRowCount / `[p#]` rowCount +- `[m#]` getColumnCount / `[p#]` columnCount + +常见与信息集存在关联的类: + +- android.widget.GridView +- android.widget.ListView +- android.widget.RadioGroup +- com.android.systemui.qs.TileLayout +- androidx.recyclerview.widget.RecyclerView + +## 子项信息集控件 + +子项信息集控件指拥有 [无障碍节点子项信息集 (AccessibilityNodeInfo.CollectionItemInfo)](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.CollectionItemInfo) 实例的一组控件, 它们共同属于同一个 [信息集控件](#信息集控件). + +部分属性或方法: + +- `[m#]` getRowIndex / `[p#]` rowIndex +- `[m#]` getColumnIndex / `[p#]` columnIndex +- `[m#]` getRowSpan / `[p#]` rowSpan +- `[m#]` getColumnSpan / `[p#]` columnSpan +- `[m#]` isSelected / `[p#]` selected +- `[m#]` getRowTitle / `[p#]` rowTitle +- `[m#]` getColumnTitle / `[p#]` columnTitle + +## 控件矩形外展 + +[控件矩形](dataTypes.md#androidrect) 边界为非整数时, 外展将对各个边界做如下取整操作: + +- left - 左边界左移, 即向下取整, 类似 JavaScript 语言的 Math.floor(left) +- top - 上边界上移, 即向下取整, 类似 JavaScript 语言的 Math.floor(top) +- right - 右边界右移, 即向上取整, 类似 JavaScript 语言的 Math.ceil(right) +- bottom - 下边界下移, 即向上取整, 类似 JavaScript 语言的 Math.ceil(bottom) + +例如: + +`Rect(10.9, 12.6 - 120.37, 1882.02)` 外展后得到 +`Rect(10, 12, 121, 1883)` + +> 注: "外展" 源自健身术语 + +## 控件矩形内收 + +[控件矩形](dataTypes.md#androidrect) 边界为非整数时, 外展将对各个边界做如下取整操作: + +- left - 左边界右移, 即向上取整, 类似 JavaScript 语言的 Math.ceil(left) +- top - 上边界下移, 即向上取整, 类似 JavaScript 语言的 Math.ceil(top) +- right - 右边界左移, 即向下取整, 类似 JavaScript 语言的 Math.floor(right) +- bottom - 下边界上移, 即向下取整, 类似 JavaScript 语言的 Math.floor(bottom) + +例如: + +`Rect(10.9, 12.6 - 120.37, 1882.02)` 内收后得到 +`Rect(11, 13, 120, 1882)` + +> 注: "内收" 源自健身术语 \ No newline at end of file diff --git a/api/http.md b/api/http.md index d30ace6..c981dba 100644 --- a/api/http.md +++ b/api/http.md @@ -1,16 +1,21 @@ # HTTP -> Stability: 2 - Stable +--- -http模块提供一些进行http请求的函数。 +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +http模块提供一些进行http请求的函数. ## http.get(url[, options, callback]) -* `url` {string} 请求的URL地址,需要以"http://"或"https://"开头。如果url没有以"http://"开头,则默认为"http://"。 -* `options` {Object} 请求选项。参见[http.request()][]。 -* `callback` {Function} 回调函数,可选,其参数是一个[Response][]对象。如果不加回调函数,则该请求将阻塞、同步地执行。 +* `url` {string} 请求的URL地址, 需要以"http://"或"https://"开头. 如果url没有以"http://"开头, 则默认为"http://". +* `options` {Object} 请求选项. 参见[http.request()][]. +* `callback` {Function} 回调函数, 可选, 其参数是一个[Response][]对象. 如果不加回调函数, 则该请求将阻塞、同步地执行. -对地址url进行一次HTTP GET 请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。 +对地址url进行一次HTTP GET 请求. 如果没有回调函数, 则在请求完成或失败时返回此次请求的响应(参见[Response][]). 最简单GET请求如下: @@ -35,7 +40,7 @@ http.get("www.baidu.com", {}, function(res, err){ }); ``` -如果要增加HTTP头部信息,则在options参数中添加,例如: +如果要增加HTTP头部信息, 则在options参数中添加, 例如: ``` console.show(); @@ -66,14 +71,14 @@ if(res.statusCode != 200){ ## http.post(url, data[, options, callback]) -* `url` {string} 请求的URL地址,需要以"http://"或"https://"开头。如果url没有以"http://"开头,则默认为"http://"。 -* `data` {string} | {Object} POST数据。 -* `options` {Object} 请求选项。 -* `callback` {Function} 回调,其参数是一个[Response][]对象。如果不加回调参数,则该请求将阻塞、同步地执行。 +* `url` {string} 请求的URL地址, 需要以"http://"或"https://"开头. 如果url没有以"http://"开头, 则默认为"http://". +* `data` {string} | {Object} POST数据. +* `options` {Object} 请求选项. +* `callback` {Function} 回调, 其参数是一个[Response][]对象. 如果不加回调参数, 则该请求将阻塞、同步地执行. -对地址url进行一次HTTP POST 请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。 +对地址url进行一次HTTP POST 请求. 如果没有回调函数, 则在请求完成或失败时返回此次请求的响应(参见[Response][]). -其中POST数据可以是字符串或键值对。具体含义取决于options.contentType的值。默认为"application/x-www-form-urlencoded"(表单提交), 这种方式是JQuery的ajax函数的默认方式。 +其中POST数据可以是字符串或键值对. 具体含义取决于options.contentType的值. 默认为"application/x-www-form-urlencoded"(表单提交), 这种方式是JQuery的ajax函数的默认方式. 一个模拟表单提交登录淘宝的例子如下: @@ -95,14 +100,14 @@ if(html.contains("页面跳转中")){ ## http.postJson(url[, data, options, callback]) -* `url` {string} 请求的URL地址,需要以"http://"或"https://"开头。如果url没有以"http://"开头,则默认为"http://"。 -* `data` {Object} POST数据。 -* `options` {Object} 请求选项。 -* `callback` {Function} 回调,其参数是一个[Response][]对象。如果不加回调参数,则该请求将阻塞、同步地执行。 +* `url` {string} 请求的URL地址, 需要以"http://"或"https://"开头. 如果url没有以"http://"开头, 则默认为"http://". +* `data` {Object} POST数据. +* `options` {Object} 请求选项. +* `callback` {Function} 回调, 其参数是一个[Response][]对象. 如果不加回调参数, 则该请求将阻塞、同步地执行. -以JSON格式向目标Url发起POST请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。 +以JSON格式向目标Url发起POST请求. 如果没有回调函数, 则在请求完成或失败时返回此次请求的响应(参见[Response][]). -JSON格式指的是,将会调用`JSON.stringify()`把data对象转换为JSON字符串,并在HTTP头部信息中把"Content-Type"属性置为"application/json"。这种方式是AngularJS的ajax函数的默认方式。 +JSON格式指的是, 将会调用`JSON.stringify()`把data对象转换为JSON字符串, 并在HTTP头部信息中把"Content-Type"属性置为"application/json". 这种方式是AngularJS的ajax函数的默认方式. 一个调用图灵机器人接口的例子如下: @@ -118,19 +123,19 @@ toastLog(r.body.string()); ## http.postMultipart(url, files[, options, callback]) -* `url` {string} 请求的URL地址,需要以"http://"或"https://"开头。如果url没有以"http://"开头,则默认为"http://"。 -* `files` {Object} POST数据。 -* `options` {Object} 请求选项。 -* `callback` {Function} 回调,其参数是一个`Response`对象。如果不加回调参数,则该请求将阻塞、同步地执行。 +* `url` {string} 请求的URL地址, 需要以"http://"或"https://"开头. 如果url没有以"http://"开头, 则默认为"http://". +* `files` {Object} POST数据. +* `options` {Object} 请求选项. +* `callback` {Function} 回调, 其参数是一个`Response`对象. 如果不加回调参数, 则该请求将阻塞、同步地执行. -向目标地址发起类型为multipart/form-data的请求(通常用于文件上传等), 其中files参数是{name1: value1, name2: value2, ...}的键值对,value的格式可以是以下几种情况: +向目标地址发起类型为multipart/form-data的请求(通常用于文件上传等), 其中files参数是{name1: value1, name2: value2, ...}的键值对, value的格式可以是以下几种情况: 1. `string` -2. 文件类型,即open()返回的类型 +2. 文件类型, 即open()返回的类型 3. [fileName, filePath] 4. [fileName, mimeType, filePath] -其中1属于非文件参数,2、3、4为文件参数。举个例子,最简单的文件上传的请求为: +其中1属于非文件参数, 2、3、4为文件参数. 举个例子, 最简单的文件上传的请求为: ``` var res = http.postMultipart(url, { @@ -139,7 +144,7 @@ var res = http.postMultipart(url, { log(res.body.string()); ``` -如果使用格式2,则代码为 +如果使用格式2, 则代码为 ``` var res = http.postMultipart(url, { @@ -148,7 +153,7 @@ var res = http.postMultipart(url, { log(res.body.string()); ``` -如果使用格式3,则代码为 +如果使用格式3, 则代码为 ``` var res = http.postMultipart(url, { @@ -157,7 +162,7 @@ var res = http.postMultipart(url, { log(res.body.string()); ``` -如果使用格式2的同时要附带非文件参数"appId=abcdefghijk",则为: +如果使用格式2的同时要附带非文件参数"appId=abcdefghijk", 则为: ``` var res = http.postMultipart(url, { @@ -169,40 +174,40 @@ log(res.body.string()); ## http.request(url[, options, callback]) -* `url` {string} 请求的URL地址,需要以"http://"或"https://"开头。如果url没有以"http://"开头,则默认为"http://"。 -* `options` {Object} 请求选项。参见[http.buildRequest()][]。 -* `callback` {Function} 回调,其参数是一个[Response][]对象。如果不加回调参数,则该请求将阻塞、同步地执行。 +* `url` {string} 请求的URL地址, 需要以"http://"或"https://"开头. 如果url没有以"http://"开头, 则默认为"http://". +* `options` {Object} 请求选项. 参见[http.buildRequest()][]. +* `callback` {Function} 回调, 其参数是一个[Response][]对象. 如果不加回调参数, 则该请求将阻塞、同步地执行. -对目标地址url发起一次HTTP请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。 +对目标地址url发起一次HTTP请求. 如果没有回调函数, 则在请求完成或失败时返回此次请求的响应(参见[Response][]). 选项options可以包含以下属性: -* `headers` {Object} 键值对形式的HTTP头部信息。有关HTTP头部信息,参见[菜鸟教程:HTTP响应头信息](http://www.runoob.com/http/http-header-fields.html)。 -* `method` {string} HTTP请求方法。包括"GET", "POST", "PUT", "DELET", "PATCH"。 -* `contentType` {string} HTTP头部信息中的"Content-Type", 表示HTTP请求的内容类型。例如"text/plain", "application/json"。更多信息参见[菜鸟教程:HTTP contentType](http://www.runoob.com/http/http-content-type.html)。 -* `body` {string} | {Array} | {Function} HTTP请求的内容。可以是一个字符串,也可以是一个字节数组;或者是一个以[BufferedSink](https://github.com/square/okio/blob/master/okio/src/main/java/okio/BufferedSink.java)为参数的函数。 +* `headers` {Object} 键值对形式的HTTP头部信息. 有关HTTP头部信息, 参见[菜鸟教程:HTTP响应头信息](http://www.runoob.com/http/http-header-fields.html). +* `method` {string} HTTP请求方法. 包括"GET", "POST", "PUT", "DELET", "PATCH". +* `contentType` {string} HTTP头部信息中的"Content-Type", 表示HTTP请求的内容类型. 例如"text/plain", "application/json". 更多信息参见[菜鸟教程:HTTP contentType](http://www.runoob.com/http/http-content-type.html). +* `body` {string} | {Array} | {Function} HTTP请求的内容. 可以是一个字符串, 也可以是一个字节数组;或者是一个以[BufferedSink](https://github.com/square/okio/blob/master/okio/src/main/java/okio/BufferedSink.java/)为参数的函数. -该函数是get, post, postJson等函数的基础函数。因此除非是PUT, DELET等请求,或者需要更高定制的HTTP请求,否则直接使用get, post, postJson等函数会更加方便。 +该函数是get, post, postJson等函数的基础函数. 因此除非是PUT, DELET等请求, 或者需要更高定制的HTTP请求, 否则直接使用get, post, postJson等函数会更加方便. # Response -HTTP请求的响应。 +HTTP请求的响应. ## Response.statusCode * {number} -当前响应的HTTP状态码。例如200(OK), 404(Not Found)等。 +当前响应的HTTP状态码. 例如200(OK), 404(Not Found)等. -有关HTTP状态码的信息,参见[菜鸟教程:HTTP状态码](http://www.runoob.com/http/http-status-codes.html)。 +有关HTTP状态码的信息, 参见[菜鸟教程:HTTP状态码](http://www.runoob.com/http/http-status-codes.html). ## Response.statusMessage * {string} -当前响应的HTTP状态信息。例如"OK", "Bad Request", "Forbidden"。 +当前响应的HTTP状态信息. 例如"OK", "Bad Request", "Forbidden". -有关HTTP状态码的信息,参见[菜鸟教程:HTTP状态码](http://www.runoob.com/http/http-status-codes.html)。 +有关HTTP状态码的信息, 参见[菜鸟教程:HTTP状态码](http://www.runoob.com/http/http-status-codes.html). 例子: @@ -221,9 +226,9 @@ if(res.statusCode >= 200 && res.statusCode < 300){ * {Object} -当前响应的HTTP头部信息。该对象的键是响应头名称,值是各自的响应头值。 所有响应头名称都是小写的(吗)。 +当前响应的HTTP头部信息. 该对象的键是响应头名称, 值是各自的响应头值. 所有响应头名称都是小写的(吗). -有关HTTP头部信息,参见[菜鸟教程:HTTP响应头信息](http://www.runoob.com/http/http-header-fields.html)。 +有关HTTP头部信息, 参见[菜鸟教程:HTTP响应头信息](http://www.runoob.com/http/http-header-fields.html). 例子: @@ -240,24 +245,24 @@ for(var headerName in res.headers){ * {Object} -当前响应的内容。他有以下属性和函数: +当前响应的内容. 他有以下属性和函数: * bytes() {Array} 以字节数组形式返回响应内容 * string() {string} 以字符串形式返回响应内容 -* json() {Object} 把响应内容作为JSON格式的数据并调用JSON.parse,返回解析后的对象 +* json() {Object} 把响应内容作为JSON格式的数据并调用JSON.parse, 返回解析后的对象 * contentType {string} 当前响应的内容类型 ## Response.request * {Request} - 当前响应所对应的请求。参见[Request][]。 + 当前响应所对应的请求. 参见[Request][]. ## Response.url * {number} - 当前响应所对应的请求URL。 + 当前响应所对应的请求URL. ## Response.method * {string} - 当前响应所对应的HTTP请求的方法。例如"GET", "POST", "PUT"等。 + 当前响应所对应的HTTP请求的方法. 例如"GET", "POST", "PUT"等. diff --git a/api/i18n.md b/api/i18n.md new file mode 100644 index 0000000..47d679a --- /dev/null +++ b/api/i18n.md @@ -0,0 +1,10 @@ +# 国际化 (Internationalization) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + + diff --git a/api/image.md b/api/image.md new file mode 100644 index 0000000..18b74e5 --- /dev/null +++ b/api/image.md @@ -0,0 +1,798 @@ +# 图像 (Images) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +images模块提供了一些手机设备中常见的图片处理函数, 包括截图、读写图片、图片剪裁、旋转、二值化、找色找图等. + +该模块分为两个部分, 找图找色部分和图片处理部分. + +需要注意的是, image对象创建后尽量在不使用时进行回收, 同时避免循环创建大量图片. 因为图片是一种占用内存比较大的资源, 尽管Auto.js通过各种方式(比如图片缓存机制、垃圾回收时回收图片、脚本结束时回收所有图片)尽量降低图片资源的泄漏和内存占用, 但是糟糕的代码仍然可以占用大量内存. + +Image对象通过调用`recycle()`函数来回收. 例如: + +``` +// 读取图片 +var img = images.read("./1.png"); +//对图片进行操作 +... +// 回收图片 +img.recycle(); +``` + +例外的是, `caputerScreen()`返回的图片不需要回收. + +## 图片处理 + +## images.read(path) + +* `path` {string} 图片路径 + +读取在路径path的图片文件并返回一个Image对象. 如果文件不存在或者文件无法解码则返回null. + +## images.load(url) + +* `url` {string} 图片URL地址 + +加载在地址URL的网络图片并返回一个Image对象. 如果地址不存在或者图片无法解码则返回null. + +## images.copy(img) + +* `img` {Image} 图片 +* 返回 {Image} + +复制一张图片并返回新的副本. 该函数会完全复制img对象的数据. + +## images.save(image, path[, format = "png", quality = 100]) + +* `image` {Image} 图片 +* `path` {string} 路径 +* `format` {string} 图片格式, 可选的值为: + * `png` + * `jpeg`/`jpg` + * `webp` +* `quality` {number} 图片质量, 为0~100的整数值 + +把图片image以PNG格式保存到path中. 如果文件不存在会被创建;文件存在会被覆盖. + +``` +//把图片压缩为原来的一半质量并保存 +var img = images.read("/sdcard/1.png"); +images.save(img, "/sdcard/1.jpg", "jpg", 50); +app.viewFile("/sdcard/1.jpg"); +``` + +## images.fromBase64(base64) + +* `base64` {string} 图片的Base64数据 +* 返回 {Image} + +解码Base64数据并返回解码后的图片Image对象. 如果base64无法解码则返回`null`. + +## images.toBase64(img[, format = "png", quality = 100]) + +* `image` {image} 图片 +* `format` {string} 图片格式, 可选的值为: + * `png` + * `jpeg`/`jpg` + * `webp` +* `quality` {number} 图片质量, 为0~100的整数值 +* 返回 {string} + +把图片编码为base64数据并返回. + +## images.fromBytes(bytes) + +* `bytes` {byte[]} 字节数组 + +解码字节数组bytes并返回解码后的图片Image对象. 如果bytes无法解码则返回`null`. + +## images.toBytes(img[, format = "png", quality = 100]) + +* `image` {image} 图片 +* `format` {string} 图片格式, 可选的值为: + * `png` + * `jpeg`/`jpg` + * `webp` +* `quality` {number} 图片质量, 为0~100的整数值 +* 返回 {byte[]} + +把图片编码为字节数组并返回. + +## images.clip(img, x, y, w, h) + +* `img` {Image} 图片 +* `x` {number} 剪切区域的左上角横坐标 +* `y` {number} 剪切区域的左上角纵坐标 +* `w` {number} 剪切区域的宽度 +* `h` {number} 剪切区域的高度 +* 返回 {Image} + +从图片img的位置(x, y)处剪切大小为w * h的区域, 并返回该剪切区域的新图片. + +``` +var src = images.read("/sdcard/1.png"); +var clip = images.clip(src, 100, 100, 400, 400); +images.save(clip, "/sdcard/clip.png"); +``` + +## images.resize(img, size[, interpolation]) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `size` {Array} 两个元素的数组[w, h], 分别表示宽度和高度;如果只有一个元素, 则宽度和高度相等 +* `interpolation` {string} 插值方法, 可选, 默认为"LINEAR"(线性插值), 可选的值有: + * `NEAREST` 最近邻插值 + * `LINEAR` 线性插值(默认) + * `AREA` 区域插值 + * `CUBIC` 三次样条插值 + * `LANCZOS4` Lanczos插值 + 参见[InterpolationFlags](https://docs.opencv.org/3.4.4/da/d54/group__imgproc__transform.html#ga5bb5a1fea74ea38e1a5445ca803ff121/) + +* 返回 {Image} + +调整图片大小, 并返回调整后的图片. 例如把图片放缩为200*300:`images.resize(img, [200, 300])`. + +参见[Imgproc.resize](https://docs.opencv.org/3.4.4/da/d54/group__imgproc__transform.html#ga47a974309e9102f5f08231edc7e7529d/). + +## images.scale(img, fx, fy[, interpolation]) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `fx` {number} 宽度放缩倍数 +* `fy` {number} 高度放缩倍数 +* `interpolation` {string} 插值方法, 可选, 默认为"LINEAR"(线性插值), 可选的值有: + * `NEAREST` 最近邻插值 + * `LINEAR` 线性插值(默认) + * `AREA` 区域插值 + * `CUBIC` 三次样条插值 + * `LANCZOS4` Lanczos插值 + 参见[InterpolationFlags](https://docs.opencv.org/3.4.4/da/d54/group__imgproc__transform.html#ga5bb5a1fea74ea38e1a5445ca803ff121/) + +* 返回 {Image} + +放缩图片, 并返回放缩后的图片. 例如把图片变成原来的一半:`images.scale(img, 0.5, 0.5)`. + +参见[Imgproc.resize](https://docs.opencv.org/3.4.4/da/d54/group__imgproc__transform.html#ga47a974309e9102f5f08231edc7e7529d/). + +## images.rotate(img, degress[, x, y]) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `degress` {number} 旋转角度. +* `x` {number} 旋转中心x坐标, 默认为图片中点 +* `y` {number} 旋转中心y坐标, 默认为图片中点 +* 返回 {Image} + +将图片逆时针旋转degress度, 返回旋转后的图片对象. + +例如逆时针旋转90度为`images.rotate(img, 90)`. + +## images.concat(img1, image2[, direction]) + +**[v4.1.0新增]** + +* `img1` {Image} 图片1 +* `img2` {Image} 图片2 +* direction {string} 连接方向, 默认为"RIGHT", 可选的值有: + * `LEFT` 将图片2接到图片1左边 + * `RIGHT` 将图片2接到图片1右边 + * `TOP` 将图片2接到图片1上边 + * `BOTTOM` 将图片2接到图片1下边 +* 返回 {Image} + +连接两张图片, 并返回连接后的图像. 如果两张图片大小不一致, 小的那张将适当居中. + +## images.grayscale(img) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* 返回 {Image} + +灰度化图片, 并返回灰度化后的图片. + +## image.threshold(img, threshold, maxVal[, type]) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `threshold` {number} 阈值 +* `maxVal` {number} 最大值 +* `type` {string} 阈值化类型, 默认为"BINARY", 参见[ThresholdTypes](https://docs.opencv.org/3.4.4/d7/d1b/group__imgproc__misc.html#gaa9e58d2860d4afa658ef70a9b1115576/), 可选的值: + * `BINARY` + * `BINARY_INV` + * `TRUNC` + * `TOZERO` + * `TOZERO_INV` + * `OTSU` + * `TRIANGLE` + +* 返回 {Image} + +将图片阈值化, 并返回处理后的图像. 可以用这个函数进行图片二值化. 例如:`images.threshold(img, 100, 255, "BINARY")`, 这个代码将图片中大于100的值全部变成255, 其余变成0, 从而达到二值化的效果. 如果img是一张灰度化图片, 这个代码将会得到一张黑白图片. + +可以参考有关博客(比如[threshold函数的使用](https://blog.csdn.net/u012566751/article/details/77046445/))或者OpenCV文档[threshold](https://docs.opencv.org/3.4.4/d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57/). + +## images.adaptiveThreshold(img, maxValue, adaptiveMethod, thresholdType, blockSize, C) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `maxValue` {number} 最大值 +* `adaptiveMethod` {string} 在一个邻域内计算阈值所采用的算法, 可选的值有: + * `MEAN_C` 计算出领域的平均值再减去参数C的值 + * `GAUSSIAN_C` 计算出领域的高斯均值再减去参数C的值 +* `thresholdType` {string} 阈值化类型, 可选的值有: + * `BINARY` + * `BINARY_INV` +* `blockSize` {number} 邻域块大小 +* `C` {number} 偏移值调整量 +* 返回 {Image} + +对图片进行自适应阈值化处理, 并返回处理后的图像. + +可以参考有关博客(比如[threshold与adaptiveThreshold](https://blog.csdn.net/guduruyu/article/details/68059450/))或者OpenCV文档[adaptiveThreshold](https://docs.opencv.org/3.4.4/d7/d1b/group__imgproc__misc.html#ga72b913f352e4a1b1b397736707afcde3 +/). + +## images.cvtColor(img, code[, dstCn]) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `code` {string} 颜色空间转换的类型, 可选的值有一共有205个(参见[ColorConversionCodes](https://docs.opencv.org/3.4.4/d8/d01/group__imgproc__color__conversions.html#ga4e0972be5de079fed4e3a10e24ef5ef0/)), 这里只列出几个: + * `BGR2GRAY` BGR转换为灰度 + * `BGR2HSV ` BGR转换为HSV + * `` +* `dstCn` {number} 目标图像的颜色通道数量, 如果不填写则根据其他参数自动决定. +* 返回 {Image} + +对图像进行颜色空间转换, 并返回转换后的图像. + +可以参考有关博客(比如[颜色空间转换](https://blog.csdn.net/u011574296/article/details/70896811?locationNum=14&fps=1))或者OpenCV文档[cvtColor](https://docs.opencv.org/3.4.4/d8/d01/group__imgproc__color__conversions.html#ga397ae87e1288a81d2363b61574eb8cab/). + +## images.inRange(img, lowerBound, upperBound) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `lowerBound` {string} | {number} 颜色下界 +* `upperBound` {string} | {number} 颜色下界 +* 返回 {Image} + +将图片二值化, 在lowerBound~upperBound范围以外的颜色都变成0, 在范围以内的颜色都变成255. + +例如`images.inRange(img, "#000000", "#222222")`. + +## images.interval(img, color, interval) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `color` {string} | {number} 颜色值 +* `interval` {number} 每个通道的范围间隔 +* 返回 {Image} + +将图片二值化, 在color-interval ~ color+interval范围以外的颜色都变成0, 在范围以内的颜色都变成255. 这里对color的加减是对每个通道而言的. + +例如`images.interval(img, "#888888", 16)`, 每个通道的颜色值均为0x88, 加减16后的范围是[0x78, 0x98], 因此这个代码将把#787878~#989898的颜色变成#FFFFFF, 而把这个范围以外的变成#000000. + +## images.blur(img, size[, anchor, type]) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `size` {Array} 定义滤波器的大小, 如[3, 3] +* `anchor` {Array} 指定锚点位置(被平滑点), 默认为图像中心 +* `type` {string} 推断边缘像素类型, 默认为"DEFAULT", 可选的值有: + * `CONSTANT` iiiiii|abcdefgh|iiiiiii with some specified i + * `REPLICATE` aaaaaa|abcdefgh|hhhhhhh + * `REFLECT` fedcba|abcdefgh|hgfedcb + * `WRAP` cdefgh|abcdefgh|abcdefg + * `REFLECT_101` gfedcb|abcdefgh|gfedcba + * `TRANSPARENT` uvwxyz|abcdefgh|ijklmno + * `REFLECT101` same as BORDER_REFLECT_101 + * `DEFAULT` same as BORDER_REFLECT_101 + * `ISOLATED` do not look outside of ROI +* 返回 {Image} + +对图像进行模糊(平滑处理), 返回处理后的图像. + +可以参考有关博客(比如[实现图像平滑处理](https://www.cnblogs.com/denny402/p/3848316.html))或者OpenCV文档[blur](https://docs.opencv.org/3.4.4/d4/d86/group__imgproc__filter.html#ga8c45db9afe636703801b0b2e440fce37/). + +## images.medianBlur(img, size) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `size` {Array} 定义滤波器的大小, 如[3, 3] +* 返回 {Image} + +对图像进行中值滤波, 返回处理后的图像. + +可以参考有关博客(比如[实现图像平滑处理](https://www.cnblogs.com/denny402/p/3848316.html))或者OpenCV文档[blur](https://docs.opencv.org/3.4.4/d4/d86/group__imgproc__filter.html#ga564869aa33e58769b4469101aac458f9/). + +## images.gaussianBlur(img, size[, sigmaX, sigmaY, type]) + +**[v4.1.0新增]** + +* `img` {Image} 图片 +* `size` {Array} 定义滤波器的大小, 如[3, 3] +* `sigmaX` {number} x方向的标准方差, 不填写则自动计算 +* `sigmaY` {number} y方向的标准方差, 不填写则自动计算 +* `type` {string} 推断边缘像素类型, 默认为"DEFAULT", 参见`images.blur` +* 返回 {Image} + +对图像进行高斯模糊, 返回处理后的图像. + +可以参考有关博客(比如[实现图像平滑处理](https://www.cnblogs.com/denny402/p/3848316.html))或者OpenCV文档[GaussianBlur](https://docs.opencv.org/3.4.4/d4/d86/group__imgproc__filter.html#gaabe8c836e97159a9193fb0b11ac52cf1/). + +## images.matToImage(mat) + +**[v4.1.0新增]** + +* `mat` {Mat} OpenCV的Mat对象 +* 返回 {Image} + +把Mat对象转换为Image对象. + +## 找图找色 + +## images.requestScreenCapture([landscape]) + +* `landscape` {boolean} 布尔值, 表示将要执行的截屏是否为横屏. 如果landscape为false, 则表示竖屏截图; true为横屏截图. + +向系统申请屏幕截图权限, 返回是否请求成功. + +第一次使用该函数会弹出截图权限请求, 建议选择“总是允许”. + +这个函数只是申请截图权限, 并不会真正执行截图, 真正的截图函数是`captureScreen()`. + +该函数在截图脚本中只需执行一次, 而无需每次调用`captureScreen()`都调用一次. + +**如果不指定landscape值, 则截图方向由当前设备屏幕方向决定**, 因此务必注意执行该函数时的屏幕方向. + +建议在本软件界面运行该函数, 在其他软件界面运行时容易出现一闪而过的黑屏现象. + +示例: + +``` +//请求截图 +if(!requestScreenCapture()){ + toast("请求截图失败"); + exit(); +} +//连续截图10张图片(间隔1秒)并保存到存储卡目录 +for(var i = 0; i < 10; i++){ + captureScreen("/sdcard/screencapture" + i + ".png"); + sleep(1000); +} + +``` + +该函数也可以作为全局函数使用. + +## images.captureScreen() + +截取当前屏幕并返回一个Image对象. + +没有截图权限时执行该函数会抛出SecurityException. + +该函数不会返回null, 两次调用可能返回相同的Image对象. 这是因为设备截图的更新需要一定的时间, 短时间内(一般来说是16ms)连续调用则会返回同一张截图. + +截图需要转换为Bitmap格式, 从而该函数执行需要一定的时间(0~20ms). + +另外在requestScreenCapture()执行成功后需要一定时间后才有截图可用, 因此如果立即调用captureScreen(), 会等待一定时间后(一般为几百ms)才返回截图. + +例子: + +``` +//请求横屏截图 +requestScreenCapture(true); +//截图 +var img = captureScreen(); +//获取在点(100, 100)的颜色值 +var color = images.pixel(img, 100, 100); +//显示该颜色值 +toast(colors.toString(color)); +``` + +该函数也可以作为全局函数使用. + +## images.captureScreen(path) + +* `path` {string} 截图保存路径 + +截取当前屏幕并以PNG格式保存到path中. 如果文件不存在会被创建;文件存在会被覆盖. + +该函数不会返回任何值. 该函数也可以作为全局函数使用. + +## images.pixel(image, x, y) + +* `image` {Image} 图片 +* `x` {number} 要获取的像素的横坐标. +* `y` {number} 要获取的像素的纵坐标. + +返回图片image在点(x, y)处的像素的ARGB值. + +该值的格式为0xAARRGGBB, 是一个"32位整数"(虽然JavaScript中并不区分整数类型和其他数值类型). + +坐标系以图片左上角为原点. 以图片左侧边为y轴, 上侧边为x轴. + +## images.findColor(image, color, options) + +* `image` {Image} 图片 +* `color` {number} | {string} 要寻找的颜色的RGB值. 如果是一个整数, 则以0xRRGGBB的形式代表RGB值(A通道会被忽略);如果是字符串, 则以"#RRGGBB"代表其RGB值. +* `options` {Object} 选项 + +在图片中寻找颜色color. 找到时返回找到的点Point, 找不到时返回null. + +选项包括: + +* `region` {Array} 找色区域. 是一个两个或四个元素的数组. (region[0], region[1])表示找色区域的左上角;region[2]*region[3]表示找色区域的宽高. 如果只有region只有两个元素, 则找色区域为(region[0], region[1])到屏幕右下角. 如果不指定region选项, 则找色区域为整张图片. +* `threshold` {number} 找色时颜色相似度的临界值, 范围为0~255(越小越相似, 0为颜色相等, 255为任何颜色都能匹配). 默认为4. threshold和浮点数相似度(0.0~1.0)的换算为 similarity = (255 - threshold) / 255. + +该函数也可以作为全局函数使用. + +一个循环找色的例子如下: + +``` +requestScreenCapture(); + +//循环找色, 找到红色(#ff0000)时停止并报告坐标 +while(true){ + var img = captureScreen(); + var point = findColor(img, "#ff0000"); + if(point){ + toast("找到红色, 坐标为(" + point.x + ", " + point.y + ")"); + } +} + +``` + +一个区域找色的例子如下: + +``` +//读取本地图片/sdcard/1.png +var img = images.read("/sdcard/1.png"); +//判断图片是否加载成功 +if(!img){ + toast("没有该图片"); + exit(); +} +//在该图片中找色, 指定找色区域为在位置(400, 500)的宽为300长为200的区域, 指定找色临界值为4 +var point = findColor(img, "#00ff00", { + region: [400, 500, 300, 200], + threshold: 4 + }); +if(point){ + toast("找到啦:" + point); +}else{ + toast("没找到"); +} +``` + +## images.findColorInRegion(img, color, x, y[, width, height, threshold]) + +区域找色的简便方法. + +相当于 + +``` +images.findColor(img, color, { + region: [x, y, width, height], + threshold: threshold +}); +``` + +该函数也可以作为全局函数使用. + +## images.findColorEquals(img, color[, x, y, width, height]) + +* `img` {Image} 图片 +* `color` {number} | {string} 要寻找的颜色 +* `x` {number} 找色区域的左上角横坐标 +* `y` {number} 找色区域的左上角纵坐标 +* `width` {number} 找色区域的宽度 +* `height` {number} 找色区域的高度 +* 返回 {Point} + +在图片img指定区域中找到颜色和color完全相等的某个点, 并返回该点的左边;如果没有找到, 则返回`null`. + +找色区域通过`x`, `y`, `width`, `height`指定, 如果不指定找色区域, 则在整张图片中寻找. + +该函数也可以作为全局函数使用. + +示例: +(通过找QQ红点的颜色来判断是否有未读消息) + +``` +requestScreenCapture(); +launchApp("QQ"); +sleep(1200); +var p = findColorEquals(captureScreen(), "#f64d30"); +if(p){ + toast("有未读消息"); +}else{ + toast("没有未读消息"); +} +``` + +## images.findMultiColors(img, firstColor, colors[, options]) + +* `img` {Image} 要找色的图片 +* `firstColor` {number} | {string} 第一个点的颜色 +* `colors` {Array} 表示剩下的点相对于第一个点的位置和颜色的数组, 数组的每个元素为[x, y, color] +* `options` {Object} 选项, 包括: + * `region` {Array} 找色区域. 是一个两个或四个元素的数组. (region[0], region[1])表示找色区域的左上角;region[2]*region[3]表示找色区域的宽高. 如果只有region只有两个元素, 则找色区域为(region[0], region[1])到屏幕右下角. 如果不指定region选项, 则找色区域为整张图片. + * `threshold` {number} 找色时颜色相似度的临界值, 范围为0~255(越小越相似, 0为颜色相等, 255为任何颜色都能匹配). 默认为4. threshold和浮点数相似度(0.0~1.0)的换算为 similarity = (255 - threshold) / 255. + +多点找色, 类似于按键精灵的多点找色, 其过程如下: + +1. 在图片img中找到颜色firstColor的位置(x0, y0) +2. 对于数组colors的每个元素[x, y, color], 检查图片img在位置(x + x0, y + y0)上的像素是否是颜色color, 是的话返回(x0, y0), 否则继续寻找firstColor的位置, 重新执行第1步 +3. 整张图片都找不到时返回`null` + +例如, 对于代码`images.findMultiColors(img, "#123456", [[10, 20, "#ffffff"], [30, 40, "#000000"]])`, 假设图片在(100, 200)的位置的颜色为#123456, 这时如果(110, 220)的位置的颜色为#fffff且(130, 240)的位置的颜色为#000000, 则函数返回点(100, 200). + +如果要指定找色区域, 则在options中指定, 例如: + +``` +var p = images.findMultiColors(img, "#123456", [[10, 20, "#ffffff"], [30, 40, "#000000"]], { + region: [0, 960, 1080, 960] +}); +``` + +## images.detectsColor(image, color, x, y[, threshold = 16, algorithm = "diff"]) + +* `image` {Image} 图片 +* `color` {number} | {string} 要检测的颜色 +* `x` {number} 要检测的位置横坐标 +* `y` {number} 要检测的位置纵坐标 +* `threshold` {number} 颜色相似度临界值, 默认为16. 取值范围为0~255. +* `algorithm` {string} 颜色匹配算法, 包括: + * "equal": 相等匹配, 只有与给定颜色color完全相等时才匹配. + * "diff": 差值匹配. 与给定颜色的R、G、B差的绝对值之和小于threshold时匹配. + * "rgb": rgb欧拉距离相似度. 与给定颜色color的rgb欧拉距离小于等于threshold时匹配. + + * "rgb+": 加权rgb欧拉距离匹配([LAB Delta E](https://en.wikipedia.org/wiki/Color_difference/)). + * "hs": hs欧拉距离匹配. hs为HSV空间的色调值. + +返回图片image在位置(x, y)处是否匹配到颜色color. 用于检测图片中某个位置是否是特定颜色. + +一个判断微博客户端的某个微博是否被点赞过的例子: + +``` +requestScreenCapture(); +//找到点赞控件 +var like = id("ly_feed_like_icon").findOne(); +//获取该控件中点坐标 +var x = like.bounds().centerX(); +var y = like.bounds().centerY(); +//截图 +var img = captureScreen(); +//判断在该坐标的颜色是否为橙红色 +if(images.detectsColor(img, "#fed9a8", x, y)){ + //是的话则已经是点赞过的了, 不做任何动作 +}else{ + //否则点击点赞按钮 + like.click(); +} +``` + +## images.findImage(img, template[, options]) + +* `img` {Image} 大图片 +* `template` {Image} 小图片(模板) +* `options` {Object} 找图选项 + +找图. 在大图片img中查找小图片template的位置(模块匹配), 找到时返回位置坐标(Point), 找不到时返回null. + +选项包括: + +* `threshold` {number} 图片相似度. 取值范围为0~1的浮点数. 默认值为0.9. +* `region` {Array} 找图区域. 参见findColor函数关于region的说明. +* `level` {number} **一般而言不必修改此参数**. 不加此参数时该参数会根据图片大小自动调整. 找图算法是采用图像金字塔进行的, level参数表示金字塔的层次, level越大可能带来越高的找图效率, 但也可能造成找图失败(图片因过度缩小而无法分辨)或返回错误位置. 因此, 除非您清楚该参数的意义并需要进行性能调优, 否则不需要用到该参数. + +该函数也可以作为全局函数使用. + +一个最简单的找图例子如下: + +``` +var img = images.read("/sdcard/大图.png"); +var templ = images.read("/sdcard/小图.png"); +var p = findImage(img, templ); +if(p){ + toast("找到啦:" + p); +}else{ + toast("没找到"); +} +``` + +稍微复杂点的区域找图例子如下: + +``` +auto(); +requestScreenCapture(); +var wx = images.read("/sdcard/微信图标.png"); +//返回桌面 +home(); +//截图并找图 +var p = findImage(captureScreen(), wx, { + region: [0, 50], + threshold: 0.8 +}); +if(p){ + toast("在桌面找到了微信图标啦: " + p); +}else{ + toast("在桌面没有找到微信图标"); +} +``` + +## images.findImageInRegion(img, template, x, y[, width, height, threshold]) + +区域找图的简便方法. 相当于: + +``` +images.findImage(img, template, { + region: [x, y, width, height], + threshold: threshold +}) +``` + +该函数也可以作为全局函数使用. + +## images.matchTemplate(img, template, options) + +**[v4.1.0新增]** + +* `img` {Image} 大图片 +* `template` {Image} 小图片(模板) +* `options` {Object} 找图选项: + * `threshold` {number} 图片相似度. 取值范围为0~1的浮点数. 默认值为0.9. + * `region` {Array} 找图区域. 参见findColor函数关于region的说明. + * `max` {number} 找图结果最大数量, 默认为5 + * `level` {number} **一般而言不必修改此参数**. 不加此参数时该参数会根据图片大小自动调整. 找图算法是采用图像金字塔进行的, level参数表示金字塔的层次, level越大可能带来越高的找图效率, 但也可能造成找图失败(图片因过度缩小而无法分辨)或返回错误位置. 因此, 除非您清楚该参数的意义并需要进行性能调优, 否则不需要用到该参数. +* 返回 {MatchingResult} + +在大图片中搜索小图片, 并返回搜索结果MatchingResult. 该函数可以用于找图时找出多个位置, 可以通过max参数控制最大的结果数量. 也可以对匹配结果进行排序、求最值等操作. + +# MatchingResult + +**[v4.1.0新增]** + +## matches + +* {Array} 匹配结果的数组. + +数组的元素是一个Match对象: + +* `point` {Point} 匹配位置 +* `similarity` {number} 相似度 + +例如: + +``` +var result = images.matchTemplate(img, template, { + max: 100 +}); +result.matches.forEach(match => { + log("point = " + match.point + ", similarity = " + match.similarity); +}); +``` + +## points + +* {Array} 匹配位置的数组. + +## first() + +* 返回 {Match} + +第一个匹配结果. 如果没有任何匹配, 则返回`null`. + +## last() + +* 返回 {Match} + +最后一个匹配结果. 如果没有任何匹配, 则返回`null`. + +## leftmost() + +* 返回 {Match} + +位于大图片最左边的匹配结果. 如果没有任何匹配, 则返回`null`. + +## topmost() + +* 返回 {Match} + +位于大图片最上边的匹配结果. 如果没有任何匹配, 则返回`null`. + +## rightmost() + +* 返回 {Match} + +位于大图片最右边的匹配结果. 如果没有任何匹配, 则返回`null`. + +## bottommost() + +* 返回 {Match} + +位于大图片最下边的匹配结果. 如果没有任何匹配, 则返回`null`. + +## best() + +* 返回 {Match} + +相似度最高的匹配结果. 如果没有任何匹配, 则返回`null`. + +## worst() + +* 返回 {Match} + +相似度最低的匹配结果. 如果没有任何匹配, 则返回`null`. + +## sortBy(cmp) + +* cmp {Function}|{string} 比较函数, 或者是一个字符串表示排序方向. 例如"left"表示将匹配结果按匹配位置从左往右排序、"top"表示将匹配结果按匹配位置从上往下排序, "left-top"表示将匹配结果按匹配位置从左往右、从上往下排序. 方向包括`left`(左), `top` (上), `right` (右), `bottom`(下). +* {MatchingResult} + +对匹配结果进行排序, 并返回排序后的结果. + +``` +var result = images.matchTemplate(img, template, { + max: 100 +}); +log(result.sortBy("top-right")); +``` + +# Image + +表示一张图片, 可以是截图的图片, 或者本地读取的图片, 或者从网络获取的图片. + +## Image.getWidth() + +返回以像素为单位图片宽度. + +## Image.getHeight() + +返回以像素为单位的图片高度. + +## Image.saveTo(path) + +* `path` {string} 路径 + +把图片保存到路径path. (如果文件存在则覆盖) + +## Image.pixel(x, y) + +* `x` {number} 横坐标 +* `y` {number} 纵坐标 + +返回图片image在点(x, y)处的像素的ARGB值. + +该值的格式为0xAARRGGBB, 是一个"32位整数"(虽然JavaScript中并不区分整数类型和其他数值类型). + +坐标系以图片左上角为原点. 以图片左侧边为y轴, 上侧边为x轴. + +## + +# Point + +findColor, findImage返回的对象. 表示一个点(坐标). + +## Point.x + +横坐标. + +## Point.y + +纵坐标. diff --git a/api/images.md b/api/images.md deleted file mode 100644 index fa5b7ee..0000000 --- a/api/images.md +++ /dev/null @@ -1,941 +0,0 @@ -# colors - -> Stability: 2 - Stable - -在Auto.js有两种方式表示一个颜色。 - -一种是使用一个字符串"#AARRGGBB"或"#RRGGBB",其中 AA 是Alpha通道(透明度)的值,RR 是R通道(红色)的值,GG 是G通道(绿色)的值,BB是B通道(蓝色)的值。例如"#ffffff"表示白色, "#7F000000"表示半透明的黑色。 - -另一种是使用一个16进制的"32位整数" 0xAARRGGBB 来表示一个颜色,例如 `0xFF112233`表示颜色"#112233", `0x11223344`表示颜色"#11223344"。 - -可以通过`colors.toString()`把颜色整数转换为字符串,通过`colors.parseColor()`把颜色字符串解析为颜色整数。 - -## colors.toString(color) - -* `color` {number} 整数RGB颜色值 -* 返回 {string} - -返回颜色值的字符串,格式为 "#AARRGGBB"。 - -## colors.red(color) - -* `color` {number} | {string} 颜色值 -* 返回 {number} - -返回颜色color的R通道的值,范围0~255. - -## colors.green(color) - -* `color` {number} | {string} 颜色值 -* 返回 {number} - -返回颜色color的G通道的值,范围0~255. - -## colors.blue(color) - -* `color` {number} | {string} 颜色值 -* 返回 {number} - -返回颜色color的B通道的值,范围0~255. - -## colors.alpha(color) - -* `color` {number} | {string} 颜色值 -* 返回 {number} - -返回颜色color的Alpha通道的值,范围0~255. - -## colors.rgb(red, green, blue) - -* red {number} 颜色的R通道的值 -* blue {number} 颜色的G通道的值 -* green {number} 颜色的B通道的值 -* 返回 {number} - -返回这些颜色通道构成的整数颜色值。Alpha通道将是255(不透明)。 - -## colors.argb(alpha, red, green, blue) - -* `alpha` {number} 颜色的Alpha通道的值 -* `red` {number} 颜色的R通道的值 -* `green` {number} 颜色的G通道的值 -* `blue` {number} 颜色的B通道的值 -* 返回 {number} - -返回这些颜色通道构成的整数颜色值。 - -## colors.parseColor(colorStr) - -* `colorStr` {string} 表示颜色的字符串,例如"#112233" -* 返回 {number} - -返回颜色的整数值。 - -## colors.isSimilar(color2, color2[, threshold, algorithm]) - -* `color1` {number} | {string} 颜色值1 -* `color1` {number} | {string} 颜色值2 -* `threshold` {number} 颜色相似度临界值,默认为4。取值范围为0~255。这个值越大表示允许的相似程度越小,如果这个值为0,则两个颜色相等时该函数才会返回true。 -* `algorithm` {string} 颜色匹配算法,默认为"diff", 包括: - * "diff": 差值匹配。与给定颜色的R、G、B差的绝对值之和小于threshold时匹配。 - * "rgb": rgb欧拉距离相似度。与给定颜色color的rgb欧拉距离小于等于threshold时匹配。 - * "rgb+": 加权rgb欧拉距离匹配([LAB Delta E](https://en.wikipedia.org/wiki/Color_difference))。 - * "hs": hs欧拉距离匹配。hs为HSV空间的色调值。 -* 返回 {Boolean} - -返回两个颜色是否相似。 - -## colors.equals(color1, color2) - -* `color1` {number} | {string} 颜色值1 -* `color1` {number} | {string} 颜色值2 -* 返回 {Boolean} - -返回两个颜色是否相等。**注意该函数会忽略Alpha通道的值进行比较*。 - -``` -log(colors.equals("#112233", "#112234")); -log(colors.equals(0xFF112233, 0xFF223344)); -``` - -# colors.BLACK - -黑色,颜色值 #FF000000 - -# colors.DKGRAY - -深灰色,颜色值 #FF444444 - -# colors.GRAY - -灰色,颜色值 #FF888888 - -# colors.LTGRAY - -亮灰色,颜色值 #FFCCCCCC - -# colors.WHITE - -白色,颜色值 #FFFFFFFF - -# colors.RED - -红色,颜色值 #FFFF0000 - -# colors.GREEN - -绿色,颜色值 #FF00FF00 - -# colors.BLUE - -蓝色,颜色值 #FF0000FF - -# colors.YELLOW - -黄色,颜色值 #FFFFFF00 - -# colors.CYAN - -青色,颜色值 #FF00FFFF - -# colors.MAGENTA - -品红色,颜色值 #FFFF00FF - -# colors.TRANSPARENT - -透明,颜色值 #00000000 - -# Images - -> Stability: 2 - Stable - -images模块提供了一些手机设备中常见的图片处理函数,包括截图、读写图片、图片剪裁、旋转、二值化、找色找图等。 - -该模块分为两个部分,找图找色部分和图片处理部分。 - -需要注意的是,image对象创建后尽量在不使用时进行回收,同时避免循环创建大量图片。因为图片是一种占用内存比较大的资源,尽管Auto.js通过各种方式(比如图片缓存机制、垃圾回收时回收图片、脚本结束时回收所有图片)尽量降低图片资源的泄漏和内存占用,但是糟糕的代码仍然可以占用大量内存。 - -Image对象通过调用`recycle()`函数来回收。例如: - -``` -// 读取图片 -var img = images.read("./1.png"); -//对图片进行操作 -... -// 回收图片 -img.recycle(); -``` - -例外的是,`caputerScreen()`返回的图片不需要回收。 - -## 图片处理 - -## images.read(path) - -* `path` {string} 图片路径 - -读取在路径path的图片文件并返回一个Image对象。如果文件不存在或者文件无法解码则返回null。 - -## images.load(url) - -* `url` {string} 图片URL地址 - -加载在地址URL的网络图片并返回一个Image对象。如果地址不存在或者图片无法解码则返回null。 - -## images.copy(img) - -* `img` {Image} 图片 -* 返回 {Image} - -复制一张图片并返回新的副本。该函数会完全复制img对象的数据。 - -## images.save(image, path[, format = "png", quality = 100]) - -* `image` {Image} 图片 -* `path` {string} 路径 -* `format` {string} 图片格式,可选的值为: - * `png` - * `jpeg`/`jpg` - * `webp` -* `quality` {number} 图片质量,为0~100的整数值 - -把图片image以PNG格式保存到path中。如果文件不存在会被创建;文件存在会被覆盖。 - -``` -//把图片压缩为原来的一半质量并保存 -var img = images.read("/sdcard/1.png"); -images.save(img, "/sdcard/1.jpg", "jpg", 50); -app.viewFile("/sdcard/1.jpg"); -``` - -## images.fromBase64(base64) - -* `base64` {string} 图片的Base64数据 -* 返回 {Image} - -解码Base64数据并返回解码后的图片Image对象。如果base64无法解码则返回`null`。 - -## images.toBase64(img[, format = "png", quality = 100]) - -* `image` {image} 图片 -* `format` {string} 图片格式,可选的值为: - * `png` - * `jpeg`/`jpg` - * `webp` -* `quality` {number} 图片质量,为0~100的整数值 -* 返回 {string} - -把图片编码为base64数据并返回。 - -## images.fromBytes(bytes) - -* `bytes` {byte[]} 字节数组 - -解码字节数组bytes并返回解码后的图片Image对象。如果bytes无法解码则返回`null`。 - -## images.toBytes(img[, format = "png", quality = 100]) - -* `image` {image} 图片 -* `format` {string} 图片格式,可选的值为: - * `png` - * `jpeg`/`jpg` - * `webp` -* `quality` {number} 图片质量,为0~100的整数值 -* 返回 {byte[]} - -把图片编码为字节数组并返回。 - -## images.clip(img, x, y, w, h) - -* `img` {Image} 图片 -* `x` {number} 剪切区域的左上角横坐标 -* `y` {number} 剪切区域的左上角纵坐标 -* `w` {number} 剪切区域的宽度 -* `h` {number} 剪切区域的高度 -* 返回 {Image} - -从图片img的位置(x, y)处剪切大小为w * h的区域,并返回该剪切区域的新图片。 - -``` -var src = images.read("/sdcard/1.png"); -var clip = images.clip(src, 100, 100, 400, 400); -images.save(clip, "/sdcard/clip.png"); -``` - -## images.resize(img, size[, interpolation]) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `size` {Array} 两个元素的数组[w, h],分别表示宽度和高度;如果只有一个元素,则宽度和高度相等 -* `interpolation` {string} 插值方法,可选,默认为"LINEAR"(线性插值),可选的值有: - * `NEAREST` 最近邻插值 - * `LINEAR` 线性插值(默认) - * `AREA` 区域插值 - * `CUBIC` 三次样条插值 - * `LANCZOS4` Lanczos插值 - 参见[InterpolationFlags](https://docs.opencv.org/3.4.4/da/d54/group__imgproc__transform.html#ga5bb5a1fea74ea38e1a5445ca803ff121) - -* 返回 {Image} - -调整图片大小,并返回调整后的图片。例如把图片放缩为200*300:`images.resize(img, [200, 300])`。 - -参见[Imgproc.resize](https://docs.opencv.org/3.4.4/da/d54/group__imgproc__transform.html#ga47a974309e9102f5f08231edc7e7529d)。 - -## images.scale(img, fx, fy[, interpolation]) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `fx` {number} 宽度放缩倍数 -* `fy` {number} 高度放缩倍数 -* `interpolation` {string} 插值方法,可选,默认为"LINEAR"(线性插值),可选的值有: - * `NEAREST` 最近邻插值 - * `LINEAR` 线性插值(默认) - * `AREA` 区域插值 - * `CUBIC` 三次样条插值 - * `LANCZOS4` Lanczos插值 - 参见[InterpolationFlags](https://docs.opencv.org/3.4.4/da/d54/group__imgproc__transform.html#ga5bb5a1fea74ea38e1a5445ca803ff121) - -* 返回 {Image} - -放缩图片,并返回放缩后的图片。例如把图片变成原来的一半:`images.scale(img, 0.5, 0.5)`。 - -参见[Imgproc.resize](https://docs.opencv.org/3.4.4/da/d54/group__imgproc__transform.html#ga47a974309e9102f5f08231edc7e7529d)。 - -## images.rotate(img, degress[, x, y]) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `degress` {number} 旋转角度。 -* `x` {number} 旋转中心x坐标,默认为图片中点 -* `y` {number} 旋转中心y坐标,默认为图片中点 -* 返回 {Image} - -将图片逆时针旋转degress度,返回旋转后的图片对象。 - -例如逆时针旋转90度为`images.rotate(img, 90)`。 - -## images.concat(img1, image2[, direction]) - -**[v4.1.0新增]** - -* `img1` {Image} 图片1 -* `img2` {Image} 图片2 -* direction {string} 连接方向,默认为"RIGHT",可选的值有: - * `LEFT` 将图片2接到图片1左边 - * `RIGHT` 将图片2接到图片1右边 - * `TOP` 将图片2接到图片1上边 - * `BOTTOM` 将图片2接到图片1下边 -* 返回 {Image} - -连接两张图片,并返回连接后的图像。如果两张图片大小不一致,小的那张将适当居中。 - -## images.grayscale(img) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* 返回 {Image} - -灰度化图片,并返回灰度化后的图片。 - -## image.threshold(img, threshold, maxVal[, type]) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `threshold` {number} 阈值 -* `maxVal` {number} 最大值 -* `type` {string} 阈值化类型,默认为"BINARY",参见[ThresholdTypes](https://docs.opencv.org/3.4.4/d7/d1b/group__imgproc__misc.html#gaa9e58d2860d4afa658ef70a9b1115576), 可选的值: - * `BINARY` - * `BINARY_INV` - * `TRUNC` - * `TOZERO` - * `TOZERO_INV` - * `OTSU` - * `TRIANGLE` - -* 返回 {Image} - -将图片阈值化,并返回处理后的图像。可以用这个函数进行图片二值化。例如:`images.threshold(img, 100, 255, "BINARY")`,这个代码将图片中大于100的值全部变成255,其余变成0,从而达到二值化的效果。如果img是一张灰度化图片,这个代码将会得到一张黑白图片。 - -可以参考有关博客(比如[threshold函数的使用](https://blog.csdn.net/u012566751/article/details/77046445))或者OpenCV文档[threshold](https://docs.opencv.org/3.4.4/d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57)。 - -## images.adaptiveThreshold(img, maxValue, adaptiveMethod, thresholdType, blockSize, C) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `maxValue` {number} 最大值 -* `adaptiveMethod` {string} 在一个邻域内计算阈值所采用的算法,可选的值有: - * `MEAN_C` 计算出领域的平均值再减去参数C的值 - * `GAUSSIAN_C` 计算出领域的高斯均值再减去参数C的值 -* `thresholdType` {string} 阈值化类型,可选的值有: - * `BINARY` - * `BINARY_INV` -* `blockSize` {number} 邻域块大小 -* `C` {number} 偏移值调整量 -* 返回 {Image} - -对图片进行自适应阈值化处理,并返回处理后的图像。 - -可以参考有关博客(比如[threshold与adaptiveThreshold](https://blog.csdn.net/guduruyu/article/details/68059450))或者OpenCV文档[adaptiveThreshold](https://docs.opencv.org/3.4.4/d7/d1b/group__imgproc__misc.html#ga72b913f352e4a1b1b397736707afcde3 -)。 - -## images.cvtColor(img, code[, dstCn]) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `code` {string} 颜色空间转换的类型,可选的值有一共有205个(参见[ColorConversionCodes](https://docs.opencv.org/3.4.4/d8/d01/group__imgproc__color__conversions.html#ga4e0972be5de079fed4e3a10e24ef5ef0)),这里只列出几个: - * `BGR2GRAY` BGR转换为灰度 - * `BGR2HSV ` BGR转换为HSV - * `` -* `dstCn` {number} 目标图像的颜色通道数量,如果不填写则根据其他参数自动决定。 -* 返回 {Image} - -对图像进行颜色空间转换,并返回转换后的图像。 - -可以参考有关博客(比如[颜色空间转换](https://blog.csdn.net/u011574296/article/details/70896811?locationNum=14&fps=1))或者OpenCV文档[cvtColor](https://docs.opencv.org/3.4.4/d8/d01/group__imgproc__color__conversions.html#ga397ae87e1288a81d2363b61574eb8cab)。 - -## images.inRange(img, lowerBound, upperBound) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `lowerBound` {string} | {number} 颜色下界 -* `upperBound` {string} | {number} 颜色下界 -* 返回 {Image} - -将图片二值化,在lowerBound~upperBound范围以外的颜色都变成0,在范围以内的颜色都变成255。 - -例如`images.inRange(img, "#000000", "#222222")`。 - -## images.interval(img, color, interval) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `color` {string} | {number} 颜色值 -* `interval` {number} 每个通道的范围间隔 -* 返回 {Image} - -将图片二值化,在color-interval ~ color+interval范围以外的颜色都变成0,在范围以内的颜色都变成255。这里对color的加减是对每个通道而言的。 - -例如`images.interval(img, "#888888", 16)`,每个通道的颜色值均为0x88,加减16后的范围是[0x78, 0x98],因此这个代码将把#787878~#989898的颜色变成#FFFFFF,而把这个范围以外的变成#000000。 - -## images.blur(img, size[, anchor, type]) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `size` {Array} 定义滤波器的大小,如[3, 3] -* `anchor` {Array} 指定锚点位置(被平滑点),默认为图像中心 -* `type` {string} 推断边缘像素类型,默认为"DEFAULT",可选的值有: - * `CONSTANT` iiiiii|abcdefgh|iiiiiii with some specified i - * `REPLICATE` aaaaaa|abcdefgh|hhhhhhh - * `REFLECT` fedcba|abcdefgh|hgfedcb - * `WRAP` cdefgh|abcdefgh|abcdefg - * `REFLECT_101` gfedcb|abcdefgh|gfedcba - * `TRANSPARENT` uvwxyz|abcdefgh|ijklmno - * `REFLECT101` same as BORDER_REFLECT_101 - * `DEFAULT` same as BORDER_REFLECT_101 - * `ISOLATED` do not look outside of ROI -* 返回 {Image} - -对图像进行模糊(平滑处理),返回处理后的图像。 - -可以参考有关博客(比如[实现图像平滑处理](https://www.cnblogs.com/denny402/p/3848316.html))或者OpenCV文档[blur](https://docs.opencv.org/3.4.4/d4/d86/group__imgproc__filter.html#ga8c45db9afe636703801b0b2e440fce37)。 - -## images.medianBlur(img, size) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `size` {Array} 定义滤波器的大小,如[3, 3] -* 返回 {Image} - -对图像进行中值滤波,返回处理后的图像。 - -可以参考有关博客(比如[实现图像平滑处理](https://www.cnblogs.com/denny402/p/3848316.html))或者OpenCV文档[blur](https://docs.opencv.org/3.4.4/d4/d86/group__imgproc__filter.html#ga564869aa33e58769b4469101aac458f9)。 - -## images.gaussianBlur(img, size[, sigmaX, sigmaY, type]) - -**[v4.1.0新增]** - -* `img` {Image} 图片 -* `size` {Array} 定义滤波器的大小,如[3, 3] -* `sigmaX` {number} x方向的标准方差,不填写则自动计算 -* `sigmaY` {number} y方向的标准方差,不填写则自动计算 -* `type` {string} 推断边缘像素类型,默认为"DEFAULT",参见`images.blur` -* 返回 {Image} - -对图像进行高斯模糊,返回处理后的图像。 - -可以参考有关博客(比如[实现图像平滑处理](https://www.cnblogs.com/denny402/p/3848316.html))或者OpenCV文档[GaussianBlur](https://docs.opencv.org/3.4.4/d4/d86/group__imgproc__filter.html#gaabe8c836e97159a9193fb0b11ac52cf1)。 - -## images.matToImage(mat) - -**[v4.1.0新增]** - -* `mat` {Mat} OpenCV的Mat对象 -* 返回 {Image} - -把Mat对象转换为Image对象。 - -## 找图找色 - -## images.requestScreenCapture([landscape]) - -* `landscape` {boolean} 布尔值, 表示将要执行的截屏是否为横屏。如果landscape为false, 则表示竖屏截图; true为横屏截图。 - -向系统申请屏幕截图权限,返回是否请求成功。 - -第一次使用该函数会弹出截图权限请求,建议选择“总是允许”。 - -这个函数只是申请截图权限,并不会真正执行截图,真正的截图函数是`captureScreen()`。 - -该函数在截图脚本中只需执行一次,而无需每次调用`captureScreen()`都调用一次。 - -**如果不指定landscape值,则截图方向由当前设备屏幕方向决定**,因此务必注意执行该函数时的屏幕方向。 - -建议在本软件界面运行该函数,在其他软件界面运行时容易出现一闪而过的黑屏现象。 - -示例: - -``` -//请求截图 -if(!requestScreenCapture()){ - toast("请求截图失败"); - exit(); -} -//连续截图10张图片(间隔1秒)并保存到存储卡目录 -for(var i = 0; i < 10; i++){ - captureScreen("/sdcard/screencapture" + i + ".png"); - sleep(1000); -} - -``` - -该函数也可以作为全局函数使用。 - -## images.captureScreen() - -截取当前屏幕并返回一个Image对象。 - -没有截图权限时执行该函数会抛出SecurityException。 - -该函数不会返回null,两次调用可能返回相同的Image对象。这是因为设备截图的更新需要一定的时间,短时间内(一般来说是16ms)连续调用则会返回同一张截图。 - -截图需要转换为Bitmap格式,从而该函数执行需要一定的时间(0~20ms)。 - -另外在requestScreenCapture()执行成功后需要一定时间后才有截图可用,因此如果立即调用captureScreen(),会等待一定时间后(一般为几百ms)才返回截图。 - -例子: - -``` -//请求横屏截图 -requestScreenCapture(true); -//截图 -var img = captureScreen(); -//获取在点(100, 100)的颜色值 -var color = images.pixel(img, 100, 100); -//显示该颜色值 -toast(colors.toString(color)); -``` - -该函数也可以作为全局函数使用。 - -## images.captureScreen(path) - -* `path` {string} 截图保存路径 - -截取当前屏幕并以PNG格式保存到path中。如果文件不存在会被创建;文件存在会被覆盖。 - -该函数不会返回任何值。该函数也可以作为全局函数使用。 - -## images.pixel(image, x, y) - -* `image` {Image} 图片 -* `x` {number} 要获取的像素的横坐标。 -* `y` {number} 要获取的像素的纵坐标。 - -返回图片image在点(x, y)处的像素的ARGB值。 - -该值的格式为0xAARRGGBB,是一个"32位整数"(虽然JavaScript中并不区分整数类型和其他数值类型)。 - -坐标系以图片左上角为原点。以图片左侧边为y轴,上侧边为x轴。 - -## images.findColor(image, color, options) - -* `image` {Image} 图片 -* `color` {number} | {string} 要寻找的颜色的RGB值。如果是一个整数,则以0xRRGGBB的形式代表RGB值(A通道会被忽略);如果是字符串,则以"#RRGGBB"代表其RGB值。 -* `options` {Object} 选项 - -在图片中寻找颜色color。找到时返回找到的点Point,找不到时返回null。 - -选项包括: - -* `region` {Array} 找色区域。是一个两个或四个元素的数组。(region[0], region[1])表示找色区域的左上角;region[2]*region[3]表示找色区域的宽高。如果只有region只有两个元素,则找色区域为(region[0], region[1])到屏幕右下角。如果不指定region选项,则找色区域为整张图片。 -* `threshold` {number} 找色时颜色相似度的临界值,范围为0~255(越小越相似,0为颜色相等,255为任何颜色都能匹配)。默认为4。threshold和浮点数相似度(0.0~1.0)的换算为 similarity = (255 - threshold) / 255. - -该函数也可以作为全局函数使用。 - -一个循环找色的例子如下: - -``` -requestScreenCapture(); - -//循环找色,找到红色(#ff0000)时停止并报告坐标 -while(true){ - var img = captureScreen(); - var point = findColor(img, "#ff0000"); - if(point){ - toast("找到红色,坐标为(" + point.x + ", " + point.y + ")"); - } -} - -``` - -一个区域找色的例子如下: - -``` -//读取本地图片/sdcard/1.png -var img = images.read("/sdcard/1.png"); -//判断图片是否加载成功 -if(!img){ - toast("没有该图片"); - exit(); -} -//在该图片中找色,指定找色区域为在位置(400, 500)的宽为300长为200的区域,指定找色临界值为4 -var point = findColor(img, "#00ff00", { - region: [400, 500, 300, 200], - threshold: 4 - }); -if(point){ - toast("找到啦:" + point); -}else{ - toast("没找到"); -} -``` - -## images.findColorInRegion(img, color, x, y[, width, height, threshold]) - -区域找色的简便方法。 - -相当于 - -``` -images.findColor(img, color, { - region: [x, y, width, height], - threshold: threshold -}); -``` - -该函数也可以作为全局函数使用。 - -## images.findColorEquals(img, color[, x, y, width, height]) - -* `img` {Image} 图片 -* `color` {number} | {string} 要寻找的颜色 -* `x` {number} 找色区域的左上角横坐标 -* `y` {number} 找色区域的左上角纵坐标 -* `width` {number} 找色区域的宽度 -* `height` {number} 找色区域的高度 -* 返回 {Point} - -在图片img指定区域中找到颜色和color完全相等的某个点,并返回该点的左边;如果没有找到,则返回`null`。 - -找色区域通过`x`, `y`, `width`, `height`指定,如果不指定找色区域,则在整张图片中寻找。 - -该函数也可以作为全局函数使用。 - -示例: -(通过找QQ红点的颜色来判断是否有未读消息) - -``` -requestScreenCapture(); -launchApp("QQ"); -sleep(1200); -var p = findColorEquals(captureScreen(), "#f64d30"); -if(p){ - toast("有未读消息"); -}else{ - toast("没有未读消息"); -} -``` - -## images.findMultiColors(img, firstColor, colors[, options]) - -* `img` {Image} 要找色的图片 -* `firstColor` {number} | {string} 第一个点的颜色 -* `colors` {Array} 表示剩下的点相对于第一个点的位置和颜色的数组,数组的每个元素为[x, y, color] -* `options` {Object} 选项,包括: - * `region` {Array} 找色区域。是一个两个或四个元素的数组。(region[0], region[1])表示找色区域的左上角;region[2]*region[3]表示找色区域的宽高。如果只有region只有两个元素,则找色区域为(region[0], region[1])到屏幕右下角。如果不指定region选项,则找色区域为整张图片。 - * `threshold` {number} 找色时颜色相似度的临界值,范围为0~255(越小越相似,0为颜色相等,255为任何颜色都能匹配)。默认为4。threshold和浮点数相似度(0.0~1.0)的换算为 similarity = (255 - threshold) / 255. - -多点找色,类似于按键精灵的多点找色,其过程如下: - -1. 在图片img中找到颜色firstColor的位置(x0, y0) -2. 对于数组colors的每个元素[x, y, color],检查图片img在位置(x + x0, y + y0)上的像素是否是颜色color,是的话返回(x0, y0),否则继续寻找firstColor的位置,重新执行第1步 -3. 整张图片都找不到时返回`null` - -例如,对于代码`images.findMultiColors(img, "#123456", [[10, 20, "#ffffff"], [30, 40, "#000000"]])`,假设图片在(100, 200)的位置的颜色为#123456, 这时如果(110, 220)的位置的颜色为#fffff且(130, 240)的位置的颜色为#000000,则函数返回点(100, 200)。 - -如果要指定找色区域,则在options中指定,例如: - -``` -var p = images.findMultiColors(img, "#123456", [[10, 20, "#ffffff"], [30, 40, "#000000"]], { - region: [0, 960, 1080, 960] -}); -``` - -## images.detectsColor(image, color, x, y[, threshold = 16, algorithm = "diff"]) - -* `image` {Image} 图片 -* `color` {number} | {string} 要检测的颜色 -* `x` {number} 要检测的位置横坐标 -* `y` {number} 要检测的位置纵坐标 -* `threshold` {number} 颜色相似度临界值,默认为16。取值范围为0~255。 -* `algorithm` {string} 颜色匹配算法,包括: - * "equal": 相等匹配,只有与给定颜色color完全相等时才匹配。 - * "diff": 差值匹配。与给定颜色的R、G、B差的绝对值之和小于threshold时匹配。 - * "rgb": rgb欧拉距离相似度。与给定颜色color的rgb欧拉距离小于等于threshold时匹配。 - - * "rgb+": 加权rgb欧拉距离匹配([LAB Delta E](https://en.wikipedia.org/wiki/Color_difference))。 - * "hs": hs欧拉距离匹配。hs为HSV空间的色调值。 - -返回图片image在位置(x, y)处是否匹配到颜色color。用于检测图片中某个位置是否是特定颜色。 - -一个判断微博客户端的某个微博是否被点赞过的例子: - -``` -requestScreenCapture(); -//找到点赞控件 -var like = id("ly_feed_like_icon").findOne(); -//获取该控件中点坐标 -var x = like.bounds().centerX(); -var y = like.bounds().centerY(); -//截图 -var img = captureScreen(); -//判断在该坐标的颜色是否为橙红色 -if(images.detectsColor(img, "#fed9a8", x, y)){ - //是的话则已经是点赞过的了,不做任何动作 -}else{ - //否则点击点赞按钮 - like.click(); -} -``` - -## images.findImage(img, template[, options]) - -* `img` {Image} 大图片 -* `template` {Image} 小图片(模板) -* `options` {Object} 找图选项 - -找图。在大图片img中查找小图片template的位置(模块匹配),找到时返回位置坐标(Point),找不到时返回null。 - -选项包括: - -* `threshold` {number} 图片相似度。取值范围为0~1的浮点数。默认值为0.9。 -* `region` {Array} 找图区域。参见findColor函数关于region的说明。 -* `level` {number} **一般而言不必修改此参数**。不加此参数时该参数会根据图片大小自动调整。找图算法是采用图像金字塔进行的, level参数表示金字塔的层次, level越大可能带来越高的找图效率,但也可能造成找图失败(图片因过度缩小而无法分辨)或返回错误位置。因此,除非您清楚该参数的意义并需要进行性能调优,否则不需要用到该参数。 - -该函数也可以作为全局函数使用。 - -一个最简单的找图例子如下: - -``` -var img = images.read("/sdcard/大图.png"); -var templ = images.read("/sdcard/小图.png"); -var p = findImage(img, templ); -if(p){ - toast("找到啦:" + p); -}else{ - toast("没找到"); -} -``` - -稍微复杂点的区域找图例子如下: - -``` -auto(); -requestScreenCapture(); -var wx = images.read("/sdcard/微信图标.png"); -//返回桌面 -home(); -//截图并找图 -var p = findImage(captureScreen(), wx, { - region: [0, 50], - threshold: 0.8 -}); -if(p){ - toast("在桌面找到了微信图标啦: " + p); -}else{ - toast("在桌面没有找到微信图标"); -} -``` - -## images.findImageInRegion(img, template, x, y[, width, height, threshold]) - -区域找图的简便方法。相当于: - -``` -images.findImage(img, template, { - region: [x, y, width, height], - threshold: threshold -}) -``` - -该函数也可以作为全局函数使用。 - -## images.matchTemplate(img, template, options) - -**[v4.1.0新增]** - -* `img` {Image} 大图片 -* `template` {Image} 小图片(模板) -* `options` {Object} 找图选项: - * `threshold` {number} 图片相似度。取值范围为0~1的浮点数。默认值为0.9。 - * `region` {Array} 找图区域。参见findColor函数关于region的说明。 - * `max` {number} 找图结果最大数量,默认为5 - * `level` {number} **一般而言不必修改此参数**。不加此参数时该参数会根据图片大小自动调整。找图算法是采用图像金字塔进行的, level参数表示金字塔的层次, level越大可能带来越高的找图效率,但也可能造成找图失败(图片因过度缩小而无法分辨)或返回错误位置。因此,除非您清楚该参数的意义并需要进行性能调优,否则不需要用到该参数。 -* 返回 {MatchingResult} - -在大图片中搜索小图片,并返回搜索结果MatchingResult。该函数可以用于找图时找出多个位置,可以通过max参数控制最大的结果数量。也可以对匹配结果进行排序、求最值等操作。 - -# MatchingResult - -**[v4.1.0新增]** - -## matches - -* {Array} 匹配结果的数组。 - -数组的元素是一个Match对象: - -* `point` {Point} 匹配位置 -* `similarity` {number} 相似度 - -例如: - -``` -var result = images.matchTemplate(img, template, { - max: 100 -}); -result.matches.forEach(match => { - log("point = " + match.point + ", similarity = " + match.similarity); -}); -``` - -## points - -* {Array} 匹配位置的数组。 - -## first() - -* 返回 {Match} - -第一个匹配结果。如果没有任何匹配,则返回`null`。 - -## last() - -* 返回 {Match} - -最后一个匹配结果。如果没有任何匹配,则返回`null`。 - -## leftmost() - -* 返回 {Match} - -位于大图片最左边的匹配结果。如果没有任何匹配,则返回`null`。 - -## topmost() - -* 返回 {Match} - -位于大图片最上边的匹配结果。如果没有任何匹配,则返回`null`。 - -## rightmost() - -* 返回 {Match} - -位于大图片最右边的匹配结果。如果没有任何匹配,则返回`null`。 - -## bottommost() - -* 返回 {Match} - -位于大图片最下边的匹配结果。如果没有任何匹配,则返回`null`。 - -## best() - -* 返回 {Match} - -相似度最高的匹配结果。如果没有任何匹配,则返回`null`。 - -## worst() - -* 返回 {Match} - -相似度最低的匹配结果。如果没有任何匹配,则返回`null`。 - -## sortBy(cmp) - -* cmp {Function}|{string} 比较函数,或者是一个字符串表示排序方向。例如"left"表示将匹配结果按匹配位置从左往右排序、"top"表示将匹配结果按匹配位置从上往下排序,"left-top"表示将匹配结果按匹配位置从左往右、从上往下排序。方向包括`left`(左), `top` (上), `right` (右), `bottom`(下)。 -* {MatchingResult} - -对匹配结果进行排序,并返回排序后的结果。 - -``` -var result = images.matchTemplate(img, template, { - max: 100 -}); -log(result.sortBy("top-right")); -``` - -# Image - -表示一张图片,可以是截图的图片,或者本地读取的图片,或者从网络获取的图片。 - -## Image.getWidth() - -返回以像素为单位图片宽度。 - -## Image.getHeight() - -返回以像素为单位的图片高度。 - -## Image.saveTo(path) - -* `path` {string} 路径 - -把图片保存到路径path。(如果文件存在则覆盖) - -## Image.pixel(x, y) - -* `x` {number} 横坐标 -* `y` {number} 纵坐标 - -返回图片image在点(x, y)处的像素的ARGB值。 - -该值的格式为0xAARRGGBB,是一个"32位整数"(虽然JavaScript中并不区分整数类型和其他数值类型)。 - -坐标系以图片左上角为原点。以图片左侧边为y轴,上侧边为x轴。 - -## - -# Point - -findColor, findImage返回的对象。表示一个点(坐标)。 - -## Point.x - -横坐标。 - -## Point.y - -纵坐标。 diff --git a/api/index.html b/api/index.html index 9c98749..ddb13b9 100644 --- a/api/index.html +++ b/api/index.html @@ -8,28 +8,15 @@ - +
- - - + + + diff --git a/api/intentType.md b/api/intentType.md new file mode 100644 index 0000000..39d2050 --- /dev/null +++ b/api/intentType.md @@ -0,0 +1,52 @@ +# 意图 (Intent) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Nov 18, 2022.

+ +--- + +作为概念, Intent (意图) 是一个消息传递对象. + +在 AutoJs6 中, Intent 作为全局对象, 支持全局调用: + +```js +/* 仅用作示例展示. */ +app.startActivity(new Intent() + .setAction("x") + .setData('x') + .setClass('x') + .setFlags(0)); + +/* app.intent 方法返回 Intent 类型数据. */ +console.log(app.intent({ data: 'protocol://data' }) instanceof Intent); // true + +/* Intent 是 android.conent.Intent 的别名. */ +console.log(Intent === android.content.Intent); // true +``` + +您可以使用它从其他应用组件请求操作. 尽管 Intent 可以通过多种方式促进组件之间的通信, 但其基本用例主要包括以下三个: + +* 启动活动(Activity): + Activity 表示应用中的一个"屏幕". 例如应用主入口都是一个Activity, 应用的功能通常也以Activity的形式独立, 例如微信的主界面、朋友圈、聊天窗口都是不同的Activity. 通过将 Intent 传递给 startActivity(), 您可以启动新的 Activity 实例. Intent 描述了要启动的 Activity, 并携带了任何必要的数据. + +* 启动服务(Service): + Service 是一个不使用用户界面而在后台执行操作的组件. 通过将 Intent 传递给 startService(), 您可以启动服务执行一次性操作(例如, 下载文件). Intent 描述了要启动的服务, 并携带了任何必要的数据. + +* 传递广播: + 广播是任何应用均可接收的消息. 系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播. 通过将 Intent 传递给 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast(), 您可以将广播传递给其他应用. + +本模块提供了构建Intent的函数(`app.intent()`), 启动Activity的函数`app.startActivity()`, 发送广播的函数`app.sendBroadcast()`. + +使用这些方法可以用来方便的调用其他应用. 例如直接打开某个QQ号的个人卡片页, 打开某个QQ号的聊天窗口等. + +```js +let qq = "2732014414"; +app.startActivity({ + action: "android.intent.action.VIEW", + data: "mqq://im/chat?chat_type=wpa&version=1&src_type=web&uin=" + qq, + packageName: "com.tencent.mobileqq", +}); + +``` \ No newline at end of file diff --git a/api/keys.md b/api/keys.md index 7bc8d93..1300b34 100644 --- a/api/keys.md +++ b/api/keys.md @@ -1,126 +1,133 @@ -# Keys +# 按键 (Keys) -按键模拟部分提供了一些模拟物理按键的全局函数,包括Home、音量键、照相键等,有的函数依赖于无障碍服务,有的函数依赖于root权限。 +--- -一般来说,以大写字母开头的函数都依赖于root权限。执行此类函数时,如果没有root权限,则函数执行后没有效果,并会在控制台输出一个警告。 +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +按键模拟部分提供了一些模拟物理按键的全局函数, 包括Home、音量键、照相键等, 有的函数依赖于无障碍服务, 有的函数依赖于root权限. + +一般来说, 以大写字母开头的函数都依赖于root权限. 执行此类函数时, 如果没有root权限, 则函数执行后没有效果, 并会在控制台输出一个警告. ## back() * 返回 {boolean} -模拟按下返回键。返回是否执行成功。 -此函数依赖于无障碍服务。 +模拟按下返回键. 返回是否执行成功. +此函数依赖于无障碍服务. ## home() * 返回 {boolean} -模拟按下Home键。返回是否执行成功。 -此函数依赖于无障碍服务。 +模拟按下Home键. 返回是否执行成功. +此函数依赖于无障碍服务. ## powerDialog() * 返回 {boolean} -弹出电源键菜单。返回是否执行成功。 -此函数依赖于无障碍服务。 +弹出电源键菜单. 返回是否执行成功. +此函数依赖于无障碍服务. ## notifications() * 返回 {boolean} -拉出通知栏。返回是否执行成功。 -此函数依赖于无障碍服务。 +拉出通知栏. 返回是否执行成功. +此函数依赖于无障碍服务. ## quickSettings() * 返回 {boolean} -显示快速设置(下拉通知栏到底)。返回是否执行成功。 -此函数依赖于无障碍服务。 +显示快速设置(下拉通知栏到底). 返回是否执行成功. +此函数依赖于无障碍服务. ## recents() * 返回 {boolean} -显示最近任务。返回是否执行成功。 -此函数依赖于无障碍服务。 +显示最近任务. 返回是否执行成功. +此函数依赖于无障碍服务. ## splitScreen() * 返回 {boolean} -分屏。返回是否执行成功。 -此函数依赖于无障碍服务, 并且需要系统自身功能的支持。 +分屏. 返回是否执行成功. +此函数依赖于无障碍服务, 并且需要系统自身功能的支持. ## Home() -模拟按下Home键。 -此函数依赖于root权限。 +模拟按下Home键. +此函数依赖于root权限. ## Back() -模拟按下返回键。 -此函数依赖于root权限。 +模拟按下返回键. +此函数依赖于root权限. ## Power() -模拟按下电源键。 -此函数依赖于root权限。 +模拟按下电源键. +此函数依赖于root权限. ## Menu() -模拟按下菜单键。 -此函数依赖于root权限。 +模拟按下菜单键. +此函数依赖于root权限. ## VolumeUp() -按下音量上键。 -此函数依赖于root权限。 +按下音量上键. +此函数依赖于root权限. ## VolumeDown() -按键音量上键。 -此函数依赖于root权限。 +按键音量上键. +此函数依赖于root权限. ## Camera() -模拟按下照相键。 +模拟按下照相键. ## Up() -模拟按下物理按键上。 -此函数依赖于root权限。 +模拟按下物理按键上. +此函数依赖于root权限. ## Down() -模拟按下物理按键下。 -此函数依赖于root权限。 +模拟按下物理按键下. +此函数依赖于root权限. ## Left() -模拟按下物理按键左。 -此函数依赖于root权限。 +模拟按下物理按键左. +此函数依赖于root权限. ## Right() -模拟按下物理按键右。 -此函数依赖于root权限。 +模拟按下物理按键右. +此函数依赖于root权限. ## OK() -模拟按下物理按键确定。 -此函数依赖于root权限。 +模拟按下物理按键确定. +此函数依赖于root权限. ## Text(text) -* text {string} 要输入的文字,只能为英文或英文符号 - 输入文字text。例如`Text("aaa");` +* text {string} 要输入的文字, 只能为英文或英文符号 + 输入文字text. 例如`Text("aaa");` ## KeyCode(code) -* code {number} | 要按下的按键的数字代码或名称。参见下表。 - 模拟物理按键。例如`KeyCode(29)`和`KeyCode("KEYCODE_A")`是按下A键。 +* code {number} | 要按下的按键的数字代码或名称. 参见下表. + 模拟物理按键. 例如`KeyCode(29)`和`KeyCode("KEYCODE_A")`是按下A键. # 附录: KeyCode对照表 diff --git a/api/mathx.md b/api/mathx.md new file mode 100644 index 0000000..b4e82a3 --- /dev/null +++ b/api/mathx.md @@ -0,0 +1,673 @@ +# Mathx (Math 扩展) + +Mathx 用于扩展 JavaScript 标准内置对象 Math 的功能 (参阅 [内置对象扩展](glossaries#内置对象扩展)). + +Mathx 全局可用: + +```js +console.log(typeof Mathx); // "object" +console.log(typeof Mathx.sum); // "function" +``` + +当启用内置扩展后, Mathx 将被应用在 Math 上: + +```js +console.log(typeof Math.sum); // "function" +``` + +## 启用内置扩展 + +内置扩展默认被禁用, 以下任一方式可启用内置扩展: + +- 在脚本中加入代码片段: `plugins.extendAll();` 或 `plugins.extend('Math');` +- AutoJs6 应用设置 - 扩展性 - JavaScript 内置对象扩展 - [ 启用 ] + +当上述应用设置启用时, 所有脚本均默认启用内置扩展. +当上述应用设置禁用时, 只有加入上述代码片段的脚本才会启用内置扩展. +内置扩展往往是不安全的, 除非明确了解内置扩展的原理及风险, 否则不建议启用. + +> 注: 因 Mathx 所有属性及方法均为 "针对对象的内置对象扩展", 在启用内置对象扩展时, 使用方法仅仅是 Mathx 与 Math 的差别, 故示例代码中将不再赘述相关示例, 但别名方法例外 (如 min 的别名 mini, max 的别名 maxi 等). + +--- + +

Mathx

+ +--- + +## [m] randInt + +randInt 用于返回一个指定范围内的随机整数. + +因 NaN 不属于整数范畴, 故 randInt 内部实现对范围数值中出现的 NaN 做了过滤处理, 进而使 NaN 失去了常见的传染性. + +```js +/* NaN 不属于整数范畴. */ +console.log(Number.isInteger(NaN)); // false + +/* NaN 出现在范围数值中时将失去其传染性. */ +console.log(Number.isNaN(Mathx.randInt(1, NaN))); // false +``` + +若范围数值中出现非整数数值, 将范围中极小值作 `Math.ceil()` 处理, 极大值作 `Math.floor()` 处理. + +```js +console.log(Mathx.randInt(1.8, 3.3)); /* 视为 randInt(2, 3). */ +console.log(Mathx.randInt(-9.7, -2.4)); /* 视为 randInt(-9, -3). */ +``` + +### randInt(start, stop) + +**`6.2.0`** **`xObject`** **`Overload 1/5`** + +- **start** { [number](dataTypes#number) } - 范围起点 (含) +- **stop** { [number](dataTypes#number) } - 范围止点 (含) +- **returns** { [number](dataTypes#number) } - 范围内随机整数 + +返回一个指定范围内的随机整数. + +```js +/* 10 - 20 的随机整数. */ +console.log(Mathx.randInt(10, 20)); // e.g. 13 + +/* start 和 stop 可自动交换. */ +console.log(Mathx.randInt(20, 10)); /* 与 randInt(10, 20) 效果相同. */ + +/* start 与 stop 相等时返回唯一值. */ +console.log(Mathx.randInt(15, 15)); // 15 +``` + +### randInt(stop) + +**`6.2.0`** **`xObject`** **`Overload 2/5`** + +- **stop** { [number](dataTypes#number) } - 范围止点 (含) +- **returns** { [number](dataTypes#number) } - 范围内随机整数 + +返回一个指定范围内的随机整数, stop 参数作为止点, 0 作为起点. +相当于 `randInt(0, stop)`. +当 stop 为负数时, 起止点将自动交换. + +```js +/* 10 - 20 的随机整数. */ +console.log(Mathx.randInt(10, 20)); // e.g. 13 + +/* start 和 stop 可自动交换. */ +console.log(Mathx.randInt(20, 10)); /* 与 randInt(10, 20) 效果相同. */ + +/* start 与 stop 相等时返回唯一值. */ +console.log(Mathx.randInt(15, 15)); // 15 +``` + +### randInt(range?) + +**`6.2.0`** **`xObject`** **`Overload [3-4]/5`** + +- **[ range = [ [MIN_SAFE_INTEGER](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER), [MAX_SAFE_INTEGER](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER) ] ]** { [number](dataTypes#number)[[]](dataTypes#array) } - 范围数组 (含两边界) +- **returns** { [number](dataTypes#number) } - 范围内随机整数 + +返回一个指定范围内的随机整数. + +```js +/* 10 - 20 的随机整数. */ +console.log(Mathx.randInt([ 10, 20 ])); // e.g. 13 + +/* 范围数组中的数字没有数量限制, 排序后将两个极值作为边界. */ +console.log(Mathx.randInt([ 20, 10, 13, 11, 15 ])); /* 与 randInt([ 10, 20 ]) 效果相同. */ + +/* 范围数组中排序后极值相同时将直接返回此极值. */ +console.log(Mathx.randInt([ 15, 15, 15 ])); // 15 + +/* 范围数组为空数组或省略参数时将返回任意 "安全" 整数. */ +console.log(Mathx.randInt([])); // e.g. -7217922776918875 +console.log(Mathx.randInt([ Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER ])); /* 效果同上. */ +console.log(Mathx.randInt()); /* 效果同上. */ +``` + +### randInt(...numbers) + +**`6.2.0`** **`xObject`** **`Overload 5/5`** + +- **numbers** { [...](documentation#可变参数)[number](dataTypes#number)[[]](documentation#可变参数) } - 范围数组 (含两边界) +- **returns** { [number](dataTypes#number) } - 范围内随机整数 + +返回一个指定范围内的随机整数. + +```js +/* 10 - 20 的随机整数. */ +console.log(Mathx.randInt(10, 20)); // e.g. 13 + +/* 可变参数没有数量限制, 排序后将两个极值作为边界. */ +console.log(Mathx.randInt(20, 10, 13, 11, 15)); /* 与 randInt(10, 20) 效果相同. */ + +/* 可变参数排序后极值相同时将直接返回此极值. */ +console.log(Mathx.randInt(15, 15, 15)); // 15 +``` + +## [m] sum + +求和. + +### sum(...numbers) + +**`6.2.0`** **`xObject`** **`Overload 1/3`** + +- **numbers** { [...](documentation#可变参数)[number](dataTypes#number)[[]](documentation#可变参数) } - 待求和数字 +- **returns** { [number](dataTypes#number) } + +返回所有数字的加和结果. + +```js +/* 整数求和. */ +console.log(Mathx.sum(1, 2, 3)); // 6 +console.log(Mathx.sum(9)); // 9 + +/* 浮点数求和. */ +console.log(Mathx.sum(0.1, 0.1)); // 0.2 +console.log(Mathx.sum(0.1, 0.2, 0.3)); // 0.6000000000000001 + +/* NaN 保持其传染性. */ +console.log(Mathx.sum(7, 8, 9, NaN)); // NaN + +/* 无参将返回 NaN. */ +console.log(Mathx.sum()); // NaN + +/* 元素将被 Number 化. */ +console.log(Mathx.sum('1', '2', '3')); // 6 +console.log(Mathx.sum({ valueOf: () => 11 }, { valueOf: () => 12 })); // 23 + +/* 嵌套将被展平. */ +console.log(Mathx.sum(1, [ 2, [ 3, [ 4 ] ], 5 ])); // 15 +``` + +### sum(numbers, fraction?) + +**`6.2.0`** **`xObject`** **`Overload [2-3]/3`** + +- **numbers** { [number](dataTypes#number)[[]](dataTypes#array) } - 待求和数字数组 +- **[ fraction = undefined ]** { [number](dataTypes#number) } - 小数点后的数字个数 +- **returns** { [number](dataTypes#number) } + +返回数组内数字的加和结果, 并根据 fraction 参数进行可能的四舍五入. + +```js +console.log(Mathx.sum([ 1, 2, 3 ])); // 6 +console.log(Mathx.sum([ 1, 2, [ 3 ] ])); // 6 +console.log(Mathx.sum([ 0.1, 0.2, 0.309 ], 2)); // 0.61 +console.log(Mathx.sum([ 0.1, 0.2, 0.309 ], 10)); // 0.609 +console.log(Mathx.sum([ 0.1, 0.2, 0.309 ], 0)); // 1 +``` + +## [m] avg + +算术平均数. + +### avg(...numbers) + +**`6.2.0`** **`xObject`** **`Overload 1/3`** + +- **numbers** { [...](documentation#可变参数)[number](dataTypes#number)[[]](documentation#可变参数) } - 待求算术平均值的数字 +- **returns** { [number](dataTypes#number) } + +返回所有数字的算术平均值. + +```js +/* 常规运算. */ +console.log(Mathx.avg(1, 2, 3)); // 2 +console.log(Mathx.avg(9)); // 9 +console.log(Mathx.avg(0.1, 0.1)); // 0.1 +console.log(Mathx.avg(0.1, 0.2, 0.3)); // 0.20000000000000004 + +/* NaN 保持其传染性. */ +console.log(Mathx.avg(7, 8, 9, NaN)); // NaN + +/* 无参将返回 NaN. */ +console.log(Mathx.avg()); // NaN + +/* 元素将被 Number 化. */ +console.log(Mathx.avg('1', '2', '3')); // 2 +console.log(Mathx.avg({ valueOf: () => 11 }, { valueOf: () => 12 })); // 11.5 + +/* 嵌套将被展平. */ +console.log(Mathx.avg(1, [ 2, [ 3, [ 4 ] ], 5 ])); // 3 +``` + +### avg(numbers, fraction?) + +**`6.2.0`** **`xObject`** **`Overload [2-3]/3`** + +- **numbers** { [number](dataTypes#number)[[]](dataTypes#array) } - 待求算术平均值数字数组 +- **[ fraction = undefined ]** { [number](dataTypes#number) } - 小数点后的数字个数 +- **returns** { [number](dataTypes#number) } + +返回数组内数字的算术平均值, 并根据 fraction 参数进行可能的四舍五入. + +```js +console.log(Mathx.avg([ 1, 2, 3 ])); // 2 +console.log(Mathx.avg([ 1, 2, [ 3 ] ])); // 2 +console.log(Mathx.avg([ 0.1, 0.2, 0.309 ], 2)); // 0.2 +console.log(Mathx.avg([ 0.1, 0.2, 0.309 ], 10)); // 0.203 +console.log(Mathx.avg([ 0.1, 0.2, 0.309 ], 0)); // 0 +``` + +## [m] median + +中位数. + +### median(...numbers) + +**`6.2.0`** **`xObject`** **`Overload 1/3`** + +- **numbers** { [...](documentation#可变参数)[number](dataTypes#number)[[]](documentation#可变参数) } - 待求中位数的数字 +- **returns** { [number](dataTypes#number) } + +返回所有数字的中位数. + +```js +/* 常规运算. */ +console.log(Mathx.median(1, 2, 3)); // 2 +console.log(Mathx.median(9)); // 9 +console.log(Mathx.median(0.1, 0.1)); // 0.1 +console.log(Mathx.median(0.1, 0.2, 0.3)); // 0.2 + +/* NaN 保持其传染性. */ +console.log(Mathx.median(7, 8, 9, NaN)); // NaN + +/* 无参将返回 NaN. */ +console.log(Mathx.median()); // NaN + +/* 元素将被 Number 化. */ +console.log(Mathx.median('1', '2', '3')); // 2 +console.log(Mathx.median({ valueOf: () => 11 }, { valueOf: () => 12 })); // 11.5 + +/* 嵌套将被展平. */ +console.log(Mathx.median(1, [ 2, [ 3, [ 4 ] ], 5 ])); // 3 +``` + +### median(numbers, fraction?) + +**`6.2.0`** **`xObject`** **`Overload [2-3]/3`** + +- **numbers** { [number](dataTypes#number)[[]](dataTypes#array) } - 待求中位数数字数组 +- **[ fraction = undefined ]** { [number](dataTypes#number) } - 小数点后的数字个数 +- **returns** { [number](dataTypes#number) } + +返回数组内数字的中位数, 并根据 fraction 参数进行可能的四舍五入. + +```js +console.log(Mathx.median([ 1, 2, 3 ])); // 2 +console.log(Mathx.median([ 1, 2, [ 3 ] ])); // 2 +console.log(Mathx.median([ 0.1, 0.2, 0.309 ], 2)); // 0.2 +console.log(Mathx.median([ 0.1, 0.2, 0.309 ], 10)); // 0.203 +console.log(Mathx.median([ 0.1, 0.2, 0.309 ], 0)); // 0 +``` + +## [m] var + +方差. + +### var(...numbers) + +**`6.2.0`** **`xObject`** **`Overload 1/3`** + +- **numbers** { [...](documentation#可变参数)[number](dataTypes#number)[[]](documentation#可变参数) } - 待求方差的数字 +- **returns** { [number](dataTypes#number) } + +返回所有数字的方差. + +```js +/* 常规运算. */ +console.log(Mathx.var(1, 2, 3)); // 0.6666666666666666 +console.log(Mathx.var(1, 2, 3, 4, 5)); // 2 +console.log(Mathx.var(9)); // 0 +console.log(Mathx.var(0.1, 0.1)); // 0 +console.log(Mathx.var(0.1, 0.2, 0.3)); // 0.006666666666666665 + +/* NaN 保持其传染性. */ +console.log(Mathx.var(7, 8, 9, NaN)); // NaN + +/* 无参将返回 NaN. */ +console.log(Mathx.var()); // NaN + +/* 元素将被 Number 化. */ +console.log(Mathx.var('1', '2', '3')); // 0.6666666666666666 +console.log(Mathx.var({ valueOf: () => 11 }, { valueOf: () => 12 })); // 0.25 + +/* 嵌套将被展平. */ +console.log(Mathx.var(1, [ 2, [ 3, [ 4 ] ], 5 ])); // 2 +``` + +### var(numbers, fraction?) + +**`6.2.0`** **`xObject`** **`Overload [2-3]/3`** + +- **numbers** { [number](dataTypes#number)[[]](dataTypes#array) } - 待求方差数字数组 +- **[ fraction = undefined ]** { [number](dataTypes#number) } - 小数点后的数字个数 +- **returns** { [number](dataTypes#number) } + +返回数组内数字的方差, 并根据 fraction 参数进行可能的四舍五入. + +```js +console.log(Mathx.var([ 1, 2, 3 ])); // 0.6666666666666666 +console.log(Mathx.var([ 1, 2, [ 3 ] ])); // 0.6666666666666666 +console.log(Mathx.var([ 0.1, 0.2, 0.309 ], 2)); // 0.01 +console.log(Mathx.var([ 0.1, 0.2, 0.309 ], 10)); // 0.0072846667 +console.log(Mathx.var([ 0.1, 0.2, 0.309 ], 0)); // 0 +``` + +## [m] std + +标准差 (均方差). + +### std(...numbers) + +**`6.2.0`** **`xObject`** **`Overload 1/3`** + +- **numbers** { [...](documentation#可变参数)[number](dataTypes#number)[[]](documentation#可变参数) } - 待求标准差的数字 +- **returns** { [number](dataTypes#number) } + +返回所有数字的标准差. + +```js +/* 常规运算. */ +console.log(Mathx.std(1, 2, 3)); // 0.816496580927726 +console.log(Mathx.std(1, 2, 3, 4, 5)); // 1.4142135623730951 +console.log(Mathx.std(9)); // 0 +console.log(Mathx.std(0.1, 0.1)); // 0 +console.log(Mathx.std(0.1, 0.2, 0.3)); // 0.0816496580927726 + +/* NaN 保持其传染性. */ +console.log(Mathx.std(7, 8, 9, NaN)); // NaN + +/* 无参将返回 NaN. */ +console.log(Mathx.std()); // NaN + +/* 元素将被 Number 化. */ +console.log(Mathx.std('1', '2', '3')); // 0.0816496580927726 +console.log(Mathx.std({ valueOf: () => 11 }, { valueOf: () => 12 })); // 0.5 + +/* 嵌套将被展平. */ +console.log(Mathx.std(1, [ 2, [ 3, [ 4 ] ], 5 ])); // 1.4142135623730951 +``` + +### std(numbers, fraction?) + +**`6.2.0`** **`xObject`** **`Overload [2-3]/3`** + +- **numbers** { [number](dataTypes#number)[[]](dataTypes#array) } - 待求标准差数字数组 +- **[ fraction = undefined ]** { [number](dataTypes#number) } - 小数点后的数字个数 +- **returns** { [number](dataTypes#number) } + +返回数组内数字的标准差, 并根据 fraction 参数进行可能的四舍五入. + +```js +console.log(Mathx.std([ 1, 2, 3 ])); // 0.816496580927726 +console.log(Mathx.std([ 1, 2, [ 3 ] ])); // 0.816496580927726 +console.log(Mathx.std([ 0.1, 0.2, 0.309 ], 2)); // 0.09 +console.log(Mathx.std([ 0.1, 0.2, 0.309 ], 10)); // 0.0853502587 +console.log(Mathx.std([ 0.1, 0.2, 0.309 ], 0)); // 0 +``` + +## [m] cv + +变异系数 (离散系数). + +### cv(...numbers) + +**`6.2.0`** **`xObject`** **`Overload 1/3`** + +- **numbers** { [...](documentation#可变参数)[number](dataTypes#number)[[]](documentation#可变参数) } - 待求变异系数的数字 +- **returns** { [number](dataTypes#number) } + +返回所有数字的变异系数. + +```js +/* 常规运算. */ +console.log(Mathx.cv(1, 2, 3)); // 0.5 +console.log(Mathx.cv(1, 2, 3, 4, 5)); // 0.5270462766947299 +console.log(Mathx.cv(9)); // NaN +console.log(Mathx.cv(0.1, 0.1)); // 0 +console.log(Mathx.cv([ 0.1, 0.2, 0.3 ], 2)); // 0.5 + +/* NaN 保持其传染性. */ +console.log(Mathx.cv(7, 8, 9, NaN)); // NaN + +/* 无参将返回 NaN. */ +console.log(Mathx.cv()); // NaN + +/* 元素将被 Number 化. */ +console.log(Mathx.cv('1', '2', '3')); // 0.5 +console.log(Mathx.cv({ valueOf: () => 11 }, { valueOf: () => 12 })); // 0.06148754619013457 + +/* 嵌套将被展平. */ +console.log(Mathx.cv(1, [ 2, [ 3, [ 4 ] ], 5 ])); // 0.5270462766947299 +``` + +### cv(numbers, fraction?) + +**`6.2.0`** **`xObject`** **`Overload [2-3]/3`** + +- **numbers** { [number](dataTypes#number)[[]](dataTypes#array) } - 待求变异系数数字数组 +- **[ fraction = undefined ]** { [number](dataTypes#number) } - 小数点后的数字个数 +- **returns** { [number](dataTypes#number) } + +返回数组内数字的变异系数, 并根据 fraction 参数进行可能的四舍五入. + +```js +console.log(Mathx.cv([ 1, 2, 3 ])); // 0.5 +console.log(Mathx.cv([ 1, 2, [ 3 ] ])); // 0.5 +console.log(Mathx.cv([ 0.1, 0.2, 0.309 ], 2)); // 0.51 +console.log(Mathx.cv([ 0.1, 0.2, 0.309 ], 10)); // 0.5149373973 +console.log(Mathx.cv([ 0.1, 0.2, 0.309 ], 0)); // 1 +``` + +## [m] max + +最大值. + +内置扩展别名: maxi. + +```js +console.log(Mathx.max([ 5, 23 ])); // 23 + +/* 启用内置对象扩展后. */ +console.log(Math.maxi([ 5, 23 ])); // 23 +``` + +需额外留意空数组作为参数及无参时的一些情况: + +```js +console.log(Math.max()); // -Infinity +console.log(Mathx.max()); // -Infinity + +console.log(Math.max([])); // 0 +console.log(Mathx.max([])); // -Infinity +``` + +### max(...numbers) + +**`6.2.0`** **`xObject`** **`xAlias`** **`Overload 1/3`** + +- **numbers** { [...](documentation#可变参数)[number](dataTypes#number)[[]](documentation#可变参数) } - 待求最大值的数字 +- **returns** { [number](dataTypes#number) } + +返回所有参数中的最大数字. + +```js +/* 常规运算. */ +console.log(Mathx.max(1, 2, 3)); // 3 +console.log(Mathx.max(9)); // 9 +console.log(Mathx.max(0.1, 0.1)); // 0.1 + +/* NaN 保持其传染性. */ +console.log(Mathx.max(7, 8, 9, NaN)); // NaN + +/* 无参将返回与 Math.max() 相同的值. */ +console.log(Mathx.max()); // -Infinity + +/* 元素将被 Number 化. */ +console.log(Mathx.max('1', '2', '3')); // 3 +console.log(Mathx.max({ valueOf: () => 11 }, { valueOf: () => 12 })); // 12 + +/* 嵌套将被展平. */ +console.log(Mathx.max(1, [ 2, [ 3, [ 4 ] ], 5 ])); // 5 +``` + +### max(numbers, fraction?) + +**`6.2.0`** **`xObject`** **`xAlias`** **`Overload [2-3]/3`** + +- **numbers** { [number](dataTypes#number)[[]](dataTypes#array) } - 待求最大值的数字数组 +- **[ fraction = undefined ]** { [number](dataTypes#number) } - 小数点后的数字个数 +- **returns** { [number](dataTypes#number) } + +返回数组内参数的最大数字, 并根据 fraction 参数进行可能的四舍五入. + +```js +console.log(Mathx.max([ 1, 2, 3 ])); // 3 +console.log(Mathx.max([ 1, 2, [ 3 ] ])); // 3 +console.log(Mathx.max([ 0.1, 0.2, 0.309 ], 2)); // 0.31 +console.log(Mathx.max([ 0.1, 0.2, 0.309 ], 10)); // 0.309 +console.log(Mathx.max([ 0.1, 0.2, 0.309 ], 0)); // 0 + +/* 相当于无参调用 Math.max(). */ +console.log(Mathx.max([])); // -Infinity +``` + +## [m] min + +最小值. + +内置扩展别名: mini. + +```js +console.log(Mathx.min([ 5, 23 ])); // 5 + +/* 启用内置对象扩展后. */ +console.log(Math.mini([ 5, 23 ])); // 5 +``` + +需额外留意空数组作为参数及无参时的一些情况: + +```js +console.log(Math.min()); // Infinity +console.log(Mathx.min()); // Infinity + +console.log(Math.min([])); // 0 +console.log(Mathx.min([])); // Infinity +``` + +### min(...numbers) + +**`6.2.0`** **`xObject`** **`xAlias`** **`Overload 1/3`** + +- **numbers** { [...](documentation#可变参数)[number](dataTypes#number)[[]](documentation#可变参数) } - 待求最小值的数字 +- **returns** { [number](dataTypes#number) } + +返回所有参数中的最小数字. + +```js +/* 常规运算. */ +console.log(Mathx.min(1, 2, 3)); // 1 +console.log(Mathx.min(9)); // 9 +console.log(Mathx.min(0.1, 0.1)); // 0.1 + +/* NaN 保持其传染性. */ +console.log(Mathx.min(7, 8, 9, NaN)); // NaN + +/* 无参将返回与 Math.min() 相同的值. */ +console.log(Mathx.min()); // Infinity + +/* 元素将被 Number 化. */ +console.log(Mathx.min('1', '2', '3')); // 1 +console.log(Mathx.min({ valueOf: () => 11 }, { valueOf: () => 12 })); // 11 + +/* 嵌套将被展平. */ +console.log(Mathx.min(1, [ 2, [ 3, [ 4 ] ], 5 ])); // 1 +``` + +### min(numbers, fraction?) + +**`6.2.0`** **`xObject`** **`xAlias`** **`Overload [2-3]/3`** + +- **numbers** { [number](dataTypes#number)[[]](dataTypes#array) } - 待求最小值的数字数组 +- **[ fraction = undefined ]** { [number](dataTypes#number) } - 小数点后的数字个数 +- **returns** { [number](dataTypes#number) } + +返回数组内参数的最小数字, 并根据 fraction 参数进行可能的四舍五入. + +```js +console.log(Mathx.min([ 1, 2, 3 ])); // 1 +console.log(Mathx.min([ 1, 2, [ 3 ] ])); // 1 +console.log(Mathx.min([ 0.1, 0.2, 0.309 ], 2)); // 0.1 +console.log(Mathx.min([ 0.1, 0.2, 0.309 ], 10)); // 0.1 +console.log(Mathx.min([ 0.1, 0.2, 0.309 ], 0)); // 0 + +/* 相当于无参调用 Math.min(). */ +console.log(Mathx.min([])); // Infinity +``` + +## [m] dist + +两点间距离. + +上述 "距离" 指的是 [欧几里得距离 (Euclidean distance)](https://zh.wikipedia.org/wiki/%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97%E8%B7%9D%E7%A6%BB), 亦称作欧氏距离. + +### dist(pointA, pointB, fraction?) + +**`6.2.0`** **`xObject`** **`Overload [1-2]/4`** + +- **pointA** { [\[](dataTypes#tuple) [number](dataTypes#number), [number](dataTypes#number) [\]](dataTypes#tuple) | [{](dataTypes#object) x: [number](dataTypes#number), y: [number](dataTypes#number) [}](dataTypes#object) | [OpencvPoint](dataTypes#opencvpoint) | [AndroidRect](dataTypes#androidrect) } +- **pointB** { [\[](dataTypes#tuple) [number](dataTypes#number), [number](dataTypes#number) [\]](dataTypes#tuple) | [{](dataTypes#object) x: [number](dataTypes#number), y: [number](dataTypes#number) [}](dataTypes#object) | [OpencvPoint](dataTypes#opencvpoint) | [AndroidRect](dataTypes#androidrect) } +- **[ fraction = undefined ]** { [number](dataTypes#number) } - 小数点后的数字个数 +- **returns** { [number](dataTypes#number) } + +返回两点间距离, 并根据 fraction 参数进行可能的四舍五入. + +```js +const Point = org.opencv.core.Point; +const Rect = android.graphics.Rect; + +/* 无参返回 NaN. */ +console.log(Mathx.dist()); // NaN + +/* 以数组作点. */ +console.log(Mathx.dist([ 0, 0 ], [ 3, 4 ])); // 5 + +/* 以包含 x 及 y 属性的对象作点. */ +console.log(Mathx.dist({ x: 0, y: 0 }, { x: 3, y: 4 })); // 5 + +/* 以 Point 作点. */ +console.log(Mathx.dist(new Point(6, 7), new Point(12, 15))); // 10 + +/* 以 Rect 作点, 此时其中心点作为参照点. */ +console.log(Mathx.dist(new Rect(0, 0, 10, 10), new Rect(10, 10, 20, 20))); // 14.142135623730951 +console.log(Mathx.dist(new Rect(0, 0, 10, 10), new Rect(10, 10, 20, 20), 3)); // 14.142 + +/* 两点类型可以不一致. */ +console.log(Mathx.dist([ 0, 0 ], new Point(3, 4))); // 5 +console.log(Mathx.dist([ 0, 0 ], new Rect(0, 0, 6, 8))); // 5 + +/* 点可以用浮点数表示. */ +console.log(Mathx.dist(new Point(0, 0), new Point(0.5, 0.5))); // 0.7071067811865476 + +/* Rect 的中心点也可以为浮点数. */ +console.log(Mathx.dist(new Point(0, 0), new Rect(0, 0, 1, 1))); // 0.7071067811865476 +``` + +### dist(rect, fraction?) + +**`6.2.0`** **`xObject`** **`Overload [3-4]/4`** + +- **rect** { [AndroidRect](dataTypes#androidrect) } - 矩形 +- **[ fraction = undefined ]** { [number](dataTypes#number) } - 小数点后的数字个数 +- **returns** { [number](dataTypes#number) } + +返回矩形的对角点间距, 并根据 fraction 参数进行可能的四舍五入. + +```js +/* 长 6 (12 - 6) 宽 8 (15 - 7) 的矩形, 对角线长度为 10. */ +console.log(Mathx.dist(new Rect(6, 7, 12, 15))); // 10 + +/* 边长为 1 的正方形, 对角线长度为 √2 (根号 2). 结果保留 3 位小数. */ +console.log(Mathx.dist(new Rect(0, 0, 1, 1), 3)); // 1.414 +``` diff --git a/api/media.md b/api/media.md index cbc33c4..ef71eb8 100644 --- a/api/media.md +++ b/api/media.md @@ -1,10 +1,15 @@ -# Media +# 多媒体 (Media) -> Stability: 2 - Stable +--- -media模块提供多媒体编程的支持。目前仅支持音乐播放和媒体文件扫描。后续会结合UI加入视频播放等功能。 +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

-需要注意是,使用该模块播放音乐时是在后台异步播放的,在脚本结束后会自动结束播放,因此可能需要插入诸如`sleep()`的语句来使脚本保持运行。例如: +--- + +media模块提供多媒体编程的支持. 目前仅支持音乐播放和媒体文件扫描. 后续会结合UI加入视频播放等功能. + +需要注意是, 使用该模块播放音乐时是在后台异步播放的, 在脚本结束后会自动结束播放, 因此可能需要插入诸如`sleep()`的语句来使脚本保持运行. 例如: ``` //播放音乐 @@ -17,9 +22,9 @@ sleep(media.getMusicDuration()); * `path` {string} 媒体文件路径 -扫描路径path的媒体文件,将它加入媒体库中;或者如果该文件以及被删除,则通知媒体库移除该文件。 +扫描路径path的媒体文件, 将它加入媒体库中;或者如果该文件以及被删除, 则通知媒体库移除该文件. -媒体库包括相册、音乐库等,因此该函数可以用于把某个图片文件加入相册。 +媒体库包括相册、音乐库等, 因此该函数可以用于把某个图片文件加入相册. ``` //请求截图 @@ -36,10 +41,10 @@ media.scanFile(path); ## media.playMusic(path[, volume, looping]) * `path` {string} 音乐文件路径 -* `volume` {number} 播放音量,为0~1的浮点数,默认为1 -* `looping` {boolean} 是否循环播放,如果looping为`true`则循环播放,默认为`false` +* `volume` {number} 播放音量, 为0~1的浮点数, 默认为1 +* `looping` {boolean} 是否循环播放, 如果looping为`true`则循环播放, 默认为`false` -播放音乐文件path。该函数不会显示任何音乐播放界面。如果文件不存在或者文件不是受支持的音乐格式,则抛出`UncheckedIOException`异常。 +播放音乐文件path. 该函数不会显示任何音乐播放界面. 如果文件不存在或者文件不是受支持的音乐格式, 则抛出`UncheckedIOException`异常. ``` //播放音乐 @@ -48,7 +53,7 @@ media.playMusic("/sdcard/1.mp3"); sleep(media.getMusicDuration()); ``` -如果要循环播放音乐,则使用looping参数: +如果要循环播放音乐, 则使用looping参数: ``` ``` @@ -61,15 +66,15 @@ sleep(media.getMusicDuration() * 3); ``` ``` -如果要使用音乐播放器播放音乐,调用`app.viewFile(path)`函数。 +如果要使用音乐播放器播放音乐, 调用`app.viewFile(path)`函数. ## media.musicSeekTo(msec) -* `msec` {number} 毫秒数,表示音乐进度 +* `msec` {number} 毫秒数, 表示音乐进度 -把当前播放进度调整到时间msec的位置。如果当前没有在播放音乐,则调用函数没有任何效果。 +把当前播放进度调整到时间msec的位置. 如果当前没有在播放音乐, 则调用函数没有任何效果. -例如,要把音乐调到1分钟的位置,为`media.musicSeekTo(60 * 1000)`。 +例如, 要把音乐调到1分钟的位置, 为`media.musicSeekTo(60 * 1000)`. ``` //播放音乐 @@ -82,30 +87,30 @@ sleep(media.getMusicDuration() - 30 * 1000); ## media.pauseMusic() -暂停音乐播放。如果当前没有在播放音乐,则调用函数没有任何效果。 +暂停音乐播放. 如果当前没有在播放音乐, 则调用函数没有任何效果. ## media.resumeMusic() -继续音乐播放。如果当前没有播放过音乐,则调用该函数没有任何效果。 +继续音乐播放. 如果当前没有播放过音乐, 则调用该函数没有任何效果. ## media.stopMusic() -停止音乐播放。如果当前没有在播放音乐,则调用函数没有任何效果。 +停止音乐播放. 如果当前没有在播放音乐, 则调用函数没有任何效果. ## media.isMusicPlaying() * 返回 {boolean} -返回当前是否正在播放音乐。 +返回当前是否正在播放音乐. ## media.getMusicDuration() * 返回 {number} -返回当前音乐的时长。单位毫秒。 +返回当前音乐的时长. 单位毫秒. ## media.getMusicCurrentPosition() * 返回 {number} -返回当前音乐的播放进度(已经播放的时间),单位毫秒。 \ No newline at end of file +返回当前音乐的播放进度(已经播放的时间), 单位毫秒. \ No newline at end of file diff --git a/api/modules.md b/api/modules.md index 164293c..e01c123 100644 --- a/api/modules.md +++ b/api/modules.md @@ -1,17 +1,22 @@ -# module (模块) +# 模块 (Module) -> Stability: 2 - Stable +--- -Auto.js 有一个简单的模块加载系统。 在 Auto.js 中,文件和模块是一一对应的(每个文件被视为一个独立的模块)。 +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

-例子,假设有一个名为 foo.js 的文件: +--- + +Auto.js 有一个简单的模块加载系统. 在 Auto.js 中, 文件和模块是一一对应的(每个文件被视为一个独立的模块). + +例子, 假设有一个名为 foo.js 的文件: ``` var circle = require('circle.js'); console.log("半径为 4 的圆的面积是 %d", circle.area(4)); ``` -在第一行中,foo.js 加载了同一目录下的 circle.js 模块。 +在第一行中, foo.js 加载了同一目录下的 circle.js 模块. circle.js 文件的内容为: @@ -29,13 +34,13 @@ circle.circumference = (r) => 2 * PI * r; module.exports = circle; ``` -circle.js 模块导出了 area() 和 circumference() 两个函数。 通过在特殊的 exports 对象上指定额外的属性,函数和对象可以被添加到模块的根部。 +circle.js 模块导出了 area() 和 circumference() 两个函数. 通过在特殊的 exports 对象上指定额外的属性, 函数和对象可以被添加到模块的根部. -模块内的本地变量是私有的。 在这个例子中,变量 PI 是 circle.js 私有的,不会影响到加载他的脚本的变量环境。 +模块内的本地变量是私有的. 在这个例子中, 变量 PI 是 circle.js 私有的, 不会影响到加载他的脚本的变量环境. -module.exports属性可以被赋予一个新的值(例如函数或对象)。 +module.exports属性可以被赋予一个新的值(例如函数或对象). -如下,bar.js 会用到 square 模块,square 导出一个构造函数: +如下, bar.js 会用到 square 模块, square 导出一个构造函数: ``` const square = require('square.js'); @@ -43,7 +48,7 @@ const mySquare = square(2); console.log("正方形的面积是 %d", mySquare.area()); square 模块定义在 square.js 中: -// 赋值给 `exports` 不会修改模块,必须使用 `module.exports` +// 赋值给 `exports` 不会修改模块, 必须使用 `module.exports` module.exports = function(width) { return { area: () => width ** 2 diff --git a/api/numberx.md b/api/numberx.md new file mode 100644 index 0000000..35d4065 --- /dev/null +++ b/api/numberx.md @@ -0,0 +1,301 @@ +# Numberx (Number 扩展) + +Numberx 用于扩展 JavaScript 标准内置对象 Number 的功能 (参阅 [内置对象扩展](glossaries#内置对象扩展)). + +Numberx 全局可用: + +```js +console.log(typeof Numberx); // "object" +console.log(typeof Numberx.clamp); // "function" +``` + +当启用内置扩展后, Numberx 将被应用在 Number 及其原型上: + +```js +console.log(typeof Number.prototype.clamp); // "function" +console.log(typeof (123).clamp); // "function" +``` + +## 启用内置扩展 + +内置扩展默认被禁用, 以下任一方式可启用内置扩展: + +- 在脚本中加入代码片段: `plugins.extendAll();` 或 `plugins.extend('Number');` +- AutoJs6 应用设置 - 扩展性 - JavaScript 内置对象扩展 - [ 启用 ] + +当上述应用设置启用时, 所有脚本均默认启用内置扩展. +当上述应用设置禁用时, 只有加入上述代码片段的脚本才会启用内置扩展. +内置扩展往往是不安全的, 除非明确了解内置扩展的原理及风险, 否则不建议启用. + +--- + +

Numberx

+ +--- + +## [p] ICU + +**`6.2.0`** **`xObject`** **`CONSTANT`** + +- [[ 996 ]] { [number](dataTypes#number) } + +996.ICU - Developers' lives matter (程序员生命健康值得呵护). + +常量值: 996 + +```js +/* 静态常量. */ +console.log(`${Numberx.ICU} is not only a number`); + +/* 启用内置对象扩展后. */ +console.log(`${Number.ICU} is not only a number`); +``` + +> 参阅: [Wikipedia](https://zh.wikipedia.org/wiki/996%E5%B7%A5%E4%BD%9C%E5%88%B6) / [GitHub](https://github.com/996icu/996.ICU) + +## [m] ensureNumber + +### ensureNumber(...o) + +**`6.2.0`** **`xObject`** + +- **o** { [...](documentation#可变参数)[any](dataTypes#any)[[]](documentation#可变参数) } - 任意对象 +- **returns** { [void](dataTypes#void) } + +相当于严格类型检查, 当任意一个 o 不满足 `typeof o === 'number'` 时抛出异常. + +```js +/* 确保每一个对象都是 number 基本类型. */ + +console.log(Numberx.ensureNumber(9)); /* 无异常. */ +console.log(Numberx.ensureNumber(null)); /* 抛出异常. */ +console.log(Numberx.ensureNumber(NaN, 0, Infinity)); /* 无异常. */ + +/* 启用内置对象扩展后. */ + +console.log(Number.ensureNumber(9)); /* 无异常. */ +console.log(Number.ensureNumber(null)); /* 抛出异常. */ +console.log(Number.ensureNumber(NaN, 0, Infinity)); /* 无异常. */ +``` + +## [m] clamp + +### clamp(num, ...clamps) + +**`6.2.0`** **`xProto`** + +- **num** { [number](dataTypes#number) } - 待处理数字 +- **clamps** { [...](documentation#可变参数)([number](dataTypes#number) | [number](dataTypes#number)[[]](dataTypes#array))[[]](documentation#可变参数) } - 限制范围 +- **returns** { [number](dataTypes#number) } + +返回限制在指定范围内的数字. +当给定数字不在限制范围内时, 则就近返回一个范围边界值. + +通常限制范围用两个大小不同的数字数组表示, +如范围 10 - 30 可用 `[ 10, 30 ]` 表示. +范围参数会根据给定参数排序后挑选出最大值与最小值作为限制上限及下限, +因此 `[ 30, 10 ]` 与 `[ 10, 30 ]` 效果相同, +而与 `[ 10, 11, 15, 22, 30 ]` 或 `[ 20, 30, 25, 10 ]` 等效果也相同: + +```js +let num = Math.random() * 50; +console.log(Numberx.clamp(num, [ 10, 30 ])); +console.log(Numberx.clamp(num, [ 10, 12, 30, 22, 20 ])); /* 同上. */ + +/* 启用内置对象扩展后. */ + +console.log(num.clamp([ 10, 30 ])); /* 同上. */ +console.log(num.clamp([ 10, 12, 30, 22, 20 ])); /* 同上. */ +``` + +因范围参数 clamps 是 [可变参数](documentation#可变参数), 因此以下两种调用方式效果相同: + +```js +let num = Math.random() * 50; +console.log(Numberx.clamp(num, [ 10, 30 ])); +console.log(Numberx.clamp(num, 10, 30)); /* 同上. */ + +/* 启用内置对象扩展后. */ + +console.log(num.clamp([ 10, 30 ])); /* 同上. */ +console.log(num.clamp(10, 30)); /* 同上. */ +``` + +当限制范围参数是 1 个数字时, 相当于 `[ x, x ]`, 则一定返回 x 本身; +当限制范围参数是 0 个数字时 (省略或空数组), 则返回 num 本身: + +```js +let num = 307; +console.log(Numberx.clamp(num, 523)); // 523 +console.log(Numberx.clamp(num)); // 307 + +/* 启用内置对象扩展后. */ + +console.log(num.clamp(523)); // 523 +console.log(num.clamp()); // 307 +``` + +## [m] toFixedNum + +### toFixedNum(num, fraction?) + +**`6.2.0`** **`xProto`** + +- **num** { [number](dataTypes#number) } - 待处理数字 +- **[ fraction = 0 ]** { [number](dataTypes#number) } - 小数点后数字的个数 +- **returns** { [number](dataTypes#number) } + +使用定点表示法来格式化一个数值, 然后返回其对应的 number 值. + +```js +console.log(Numberx.toFixedNum(123.456, 2)); // 123.46 +console.log(Numberx.toFixedNum(3.004, 2)); // 3 +console.log(Numberx.toFixedNum(1.23456e3)); // 1235 + +/* 启用内置对象扩展后. */ + +console.log((123.456).toFixedNum(2)); // 123.46 +console.log((3.004).toFixedNum(2)); // 3 +console.log((1.23456e3).toFixedNum()); // 1235 +``` + +## [m] padStart + +### padStart(num, targetLength, pad?) + +**`6.2.0`** **`xProto`** + +- **num** { [number](dataTypes#number) } - 待处理数字 +- **targetLength** { [number](dataTypes#number) } - 当前数字需要填充到的目标字符串长度. 如果此长度小于 num 参数的字符串长度, 则返回 num 参数的字符串本身. +- **[ pad = '0' ]** { [string](dataTypes#string) | [number](dataTypes#number) } - 填充字符串. 如果字符串太长, 使填充后的字符串长度超过了目标长度, 则只保留最左侧部分, 其他部分会被截断. 此参数的默认值为 "0"(U+0030)。 +- **returns** { [string](dataTypes#string) } + +此方法用一个字符串填充当前数字 (如果需要的话则重复填充), 返回填充后达到指定长度的字符串. +填充从当前数字对应字符串的开头开始. + +```js +console.log(Numberx.padStart(5, 2, 0)); // "05" +console.log(Numberx.padStart(5, 2)); // "05" +console.log(Numberx.padStart(3, 3)); // "003" +console.log(Numberx.padStart(99, 5, "AB")); // "ABA99" + +/* 启用内置对象扩展后. */ + +console.log((5).padStart(2, 0)); // "05" +console.log((5).padStart(2)); // "05" +console.log((3).padStart(3)); // "003" +console.log((99).padStart(5, "AB")); // "ABA99" +``` + +格式化日期与时间: + +```js +let pad = x => Numberx.padStart(x, 2, 0); /* 启用内置对象扩展后: `x.padStart(2, 0)`. */ +let now = new Date(); +let date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`; +let time = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`; +console.log(`${date} ${time}`); /* e.g. "2022-11-01 08:47:15" */ +``` + +## [m] padEnd + +### padEnd(num, targetLength, pad?) + +**`6.2.0`** **`xProto`** + +- **num** { [number](dataTypes#number) } - 待处理数字 +- **targetLength** { [number](dataTypes#number) } - 当前数字需要填充到的目标字符串长度. 如果此长度小于 num 参数的字符串长度, 则返回 num 参数的字符串本身. +- **[ pad = '0' ]** { [string](dataTypes#string) | [number](dataTypes#number) } - 填充字符串. 如果字符串太长, 使填充后的字符串长度超过了目标长度, 则只保留最左侧部分, 其他部分会被截断. 此参数的默认值为 "0"(U+0030)。 +- **returns** { [string](dataTypes#string) } + +此方法用一个字符串填充当前数字 (如果需要的话则重复填充), 返回填充后达到指定长度的字符串. +填充从当前数字对应字符串的末尾开始. + +```js +console.log(Numberx.padEnd(5, 2, 0)); // "50" +console.log(Numberx.padEnd(5, 2)); // "50" +console.log(Numberx.padEnd(3, 3)); // "300" +console.log(Numberx.padEnd(99.1, 5)); // "99.10" + +/* 启用内置对象扩展后. */ + +console.log((5).padEnd(2, 0)); // "50" +console.log((5).padEnd(2)); // "50" +console.log((3).padEnd(3)); // "300" +console.log((99.1).padEnd(5)); // "99.10" +``` + +## [m] parseFloat + +### parseFloat(string, radix) + +**`6.2.0`** **`xObject`** + +- **string** { [string](dataTypes#string) } - 待解析的字符串 +- **radix** { [number](dataTypes#number) } - 进制的基数 +- **returns** { [number](dataTypes#number) } + +根据指定的进制基数, 解析字符串并返回一个数字. + +标准内置对象 Number 的 parseInt 支持进制基数 radix 参数, 即 parseInt(string, radix), 但 parseFloat 不支持 radix 参数. + +此扩展方法对上述 radix 参数提供了支持: + +```js +console.log(Numberx.parseFloat("0.8", 16)); // 0.5 +console.log(Numberx.parseFloat("0.101", 2)); // 0.625 + +/* 启用内置对象扩展后. */ + +console.log(Number.parseFloat("0.8", 16)); // 0.5 +console.log(Number.parseFloat("0.101", 2)); // 0.625 +console.log(parseFloat("0.8", 16)); // 0.5 +console.log(parseFloat("0.101", 2)); // 0.625 +``` + +## [m] parsePercent + +### parsePercent(percentage) + +**`6.2.0`** **`xObject`** + +- **percentage** { [string](dataTypes#string) | [number](dataTypes#number) } - 百分数字符串或任意数字 +- **returns** { [number](dataTypes#number) } + +此方法用于解析一个百分数字符串 (如 `"5%"`) 并返回其代表的数值. +如果 percentage 是一个 number 基本类型值, 则直接返回. + +```js +console.log(Numberx.parsePercent('1%')); // 0.01 +console.log(Numberx.parsePercent('50.00%')); // 0.5 +console.log(Numberx.parsePercent('2%%')); // 0.0002 + +/* 启用内置对象扩展后. */ + +console.log(Number.parsePercent('1%')); // 0.01 +console.log(Number.parsePercent('50.00%')); // 0.5 +console.log(Number.parsePercent('2%%')); // 0.0002 +``` + +## [m] parseRatio + +### parseRatio(ratio) + +**`6.2.0`** **`xObject`** + +- **ratio** { [string](dataTypes#string) } - 比率字符串 +- **returns** { [number](dataTypes#number) } + +此方法用于解析一个比率字符串 (如 `"5:23"`) 并返回其代表的数值. + +```js +console.log(Numberx.parseRatio('3:2')); // 1.5 +console.log(Numberx.parseRatio('3:0.1')); // 30 +console.log(Numberx.parseRatio('0.1:0.01')); // 10 + +/* 启用内置对象扩展后. */ + +console.log(Number.parseRatio('3:2')); // 1.5 +console.log(Number.parseRatio('3:0.1')); // 30 +console.log(Number.parseRatio('0.1:0.01')); // 10 +``` diff --git a/api/overview.md b/api/overview.md index cd91989..89a06b9 100644 --- a/api/overview.md +++ b/api/overview.md @@ -1,28 +1,45 @@ -# 综述 - -Auto.js使用[JavaScript](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)作为脚本语言,目前使用[Rhino 1.7.15-snapshot](https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/Rhino)作为脚本引擎,支持ES5与部分ES6特性。 - -* 因为Auto.js是基于JavaScript的,学习Auto.js的API之前建议先学习JavaScript的基本语法和内置对象,可以使用教程前面的两个JavaScript教程链接来学习。 -* 如果您想要使用TypeScript来开发,目前已经有开发者公布了一个可以把使用TypeScript进行Auto.js开发的工具,参见[Auto.js DevTools](https://github.com/pboymt/autojs-dev)。 -* 如果想要在电脑而不是手机上开发Auto.js,可以使用VS Code以及相应的Auto.js插件使得在电脑上编辑的脚本能推送到手机运行,参见[Auto.js-VSCode-Extension](https://github.com/hyb1996/Auto.js-VSCode-Extension),Auto.js Pro用户则需要使用[Auto.js-Pro-Ext](https://marketplace.visualstudio.com/items?itemName=hyb1996.auto-js-pro-ext)。 -* 支持Node.js已在计划中,预计2020年底在Auto.js Pro中支持 - -本文档的章节大致上是以模块来分的,总体上可以分成"自动操作"类模块(控件操作、触摸模拟、按键模拟等)和其他类模块(设备、应用、界面等)。 - -"自动操作"的部分又可以大致分为基于控件和基于坐标的操作。基于坐标的操作是传统按键精灵、触摸精灵等脚本软件采用的方式,通过屏幕坐标来点击、长按指定位置模拟操作,从而到达目的。例如`click(100, 200)`, `press(100, 200, 500)`等。这种方式在游戏类脚本中比较有可行性,结合找图找色、坐标放缩功能也能达到较好的兼容性。但是,这种方式对一般软件脚本却难以达到想要的效果,而且这种方式需要安卓7.0版本以上或者root权限才能执行。所以对于一般软件脚本(例如批量添加联系人、自动提取短信验证码等等),我们采用基于控件的模拟操作方式,结合通知事情、按键事情等达成更好的工作流。这些部分的文档参见[基于控件的操作](widgets-based-automation.html)和[基于坐标的操作](coordinates-based-automation.html)。 - -其他部分主要包括: - -* app: 应用。启动应用,卸载应用,使用应用查看、编辑文件、访问网页,发送应用间广播等。 -* console: 控制台。记录运行的日志、错误、信息等。 -* device: 设备。获取设备屏幕宽高、系统版本等信息,控制设备音量、亮度等。 -* engines: 脚本引擎。用于启动其他脚本。 -* events: 事件与监听。按键监听,通知监听,触摸监听等。 -* floaty: 悬浮窗。用于显示自定义的悬浮窗。 -* files: 文件系统。文件创建、获取信息、读写。 -* http: HTTP。发送HTTP请求,例如GET, POST等。 -* images, colors: 图片和图色处理。截图,剪切图片,找图找色,读取保存图片等。 -* keys: 按键模拟。比如音量键、Home键模拟等。 -* shell: Shell命令。 -* threads: 多线程支持。 -* ui: UI界面。用于显示自定义的UI界面,和用户交互。 \ No newline at end of file +# 综述 (Overview) + +--- + +AutoJs6: 安卓平台 JavaScript 自动化工具. + +- 脚本语言: [JavaScript](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/) +- 脚本引擎: [Rhino](https://github.com/mozilla/rhino/) +- 支持特性: [ES5](https://262.ecma-international.org/5.1/) (全部), [ES6](https://262.ecma-international.org/6.0/) (部分) + +--- + +扩展阅读: + +- 了解 JavaScript + - [MDN - JavaScript 基础](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/) + - [JavaScript.info - JavaScript 教程](https://zh.javascript.info/) +- 查看 Rhino 引擎兼容性列表 + - [Rhino ES2015 Support](https://mozilla.github.io/rhino/compat/engines.html) +- 使用 PC (个人计算机) 开发 + - [AutoJs6 VSCode Extension](https://github.com/SuperMonster003/AutoJs6-VSCode-Extension/) +- 使用 Node.js 开发 + - [Auto.js Pro](https://pro.autojs.org/) +- 使用 TypeScript 开发 + - [Auto.js DevTools](https://github.com/pboymt/autojs-dev/) + +--- + +阅读文档: + +- 宽屏设备网页阅读 + - 点击左侧边栏条目 - 阅读相关章节 +- 移动设备网页阅读 + - 点击左下方抽屉按钮 - 展开侧边栏并点击条目 - 阅读相关章节 +- AutoJs6 阅读 + - 点击首页 "文档" 标签 - 点击条目 - 阅读相关章节 + - 点击首页右上方 "搜索" 图标 - 在当前页面检索内容 + - 文档页面左上方的导航链接可实现页面跳转: + - 点击 "索引" - 跳转至章节索引页面 + - 点击 "查看全部" - 所有章节内容在同一页面列出 + - 阅读文档时, "文档" 标签可作为快捷按钮使用: + - 点击 "文档" 标签 - 返回至当前页面顶部 + - 长按 "文档" 标签 - 跳转至章节索引页面 + +> 注: 初次阅读文档或首次使用 AutoJs6 建议从 [关于文档](documentation) 开始. \ No newline at end of file diff --git a/api/plugins.md b/api/plugins.md new file mode 100644 index 0000000..3d52bcb --- /dev/null +++ b/api/plugins.md @@ -0,0 +1,10 @@ +# 插件 (Plugins) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + + diff --git a/api/polyfill.md b/api/polyfill.md new file mode 100644 index 0000000..9cf0c8b --- /dev/null +++ b/api/polyfill.md @@ -0,0 +1,30 @@ +# 代码填泥 (Polyfill) + +## 定义 + +代码填泥, 又称 [ 填泥 / 腻子 / 泥子 ], 是一个完整的代码块, 用于为不支持原生 ECMAScript 新功能的环境提供功能支持. +例如 Polyfill 可以让 AutoJs6 模拟 Array 原型上的 [at](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/at) 等新增方法. + +之所以被称为填泥, 是因为它主要用于 "抹平" 不同环境的功能支持差异. +换句话说, 它是当前环境中某个标准 API 缺失后的手动实现. + +> 参阅: [MDN](https://developer.mozilla.org/zh-CN/docs/Glossary/Polyfill) + +## AutoJs6 实现 + +截至 2022 年 10 月, AutoJs6 实现了以下代码填泥: + +- [Array.prototype.at](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/at) +- [Array.prototype.flat](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) +- ~~[Object.getOwnPropertyDescriptors](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptors)~~ (Rhino 1.7.15-SNAPSHOT 已实现) + +## 区别于 Shim (代码垫片) + +Polyfill 与 Shim (代码垫片) 不同, Shim 针对的是环境, Polyfill 针对的是API. + +Shim 作为 "垫片", 主要用于为旧环境提供 API 或提供新功能. +在使用 Shim 时, 通常不会在意旧环境是否已存在某 API, 它会直接改变或重新定义某些全局对象, 以实现在不同环境下功能的完整性和统一性. + +Polyfill 是腻子, 用于抹平不同环境下的 API 差异, 它会判断旧环境是否已经存在 API, 不存在时才会添加新API. + +> 参阅: [掘金](https://juejin.cn/post/6844904050882772999) / [StackOverflow](https://stackoverflow.com/questions/6599815/what-is-the-difference-between-a-shim-and-a-polyfill) \ No newline at end of file diff --git a/api/progress.md b/api/progress.md new file mode 100644 index 0000000..a57b318 --- /dev/null +++ b/api/progress.md @@ -0,0 +1,69 @@ +# 部署进度 (Progress) + +本章节展示了 AutoJs6 文档各个章节的部署进度. +文档以 Auto.js 4.1.1 Alpha2 的原始文档为基础, 逐步完成部署及更新. + +--- + +标记含义: + +- √ - 全部完成 (或具备基础完整度) +- × - 暂未开始部署 (或暂时保留原始文档内容) +- < 10% - 预估部署进度, 小于指定百分比 +- > 20% - 预估部署进度, 大于指定百分比 + +--- + +| 章节 | 部署进度 | +|:---------------------------------------------------:|:--------:| +| [Global - 全局对象](global) | > 70% | +| [Automator - 自动化](automator) | > 60% | +| [AutoJs6 - 本体应用](autojs) | × | +| [App - 通用应用](app) | × | +| [Color - 颜色](color) | × | +| [Image - 图像](image) | × | +| [Keys - 按键](keys) | × | +| [Device - 设备](device) | × | +| [Storage - 储存](storages) | × | +| [File - 文件](files) | × | +| [Engine - 引擎](engines) | × | +| [Task - 任务](tasks) | × | +| [Module - 模块](modules) | × | +| [Plugins - 插件](plugins) | × | +| [Console - 控制台](console) | × | +| [Shell](shell) | × | +| [Media - 多媒体](media) | × | +| [Sensor - 传感器](sensors) | × | +| [Recorder - 记录器](recorder) | × | +| [Timer - 定时器](timers) | × | +| [Thread - 线程](threads) | × | +| [Continuation - 协程](continuation) | × | +| [Event - 事件监听](events) | × | +| [Dialog - 对话框](dialogs) | × | +| [Floaty - 悬浮窗](floaty) | × | +| [Canvas - 画布](canvas) | × | +| [UI - 用户界面](ui) | × | +| [Web - 万维网](web) | × | +| [HTTP](http) | × | +| [Base64](base64) | × | +| [Crypto - 密文](crypto) | × | +| [Internationalization - 国际化](i18n) | × | +| [E4X](e4x) | √ | +| [Glossaries - 术语](glossaries) | √ | +| [Exceptions - 异常](exceptions) | √ | +| [Intent - 意图](intentType) | < 10% | +| [Runtime - 运行时](runtime) | × | +| [Context - 上下文](context) | × | +| [Activity - 活动](activity) | × | +| [Data Types - 数据类型](dataTypes) | > 70% | +| [UiSelector - 选择器](uiSelectorType) | √ | +| [UiObject - 控件节点](uiObjectType) | √ | +| [UiObjectCollection - 控件集合](uiObjectCollectionType) | √ | +| [UiObjectActions - 控件节点行为](uiObjectActionsType) | √ | +| [App - 应用枚举类](appType) | √ | +| [Polyfill - 代码填泥](polyfill) | √ | +| [Arrayx - Array 扩展](arrayx) | √ | +| [Numberx - Number 扩展](numberx) | √ | +| [Mathx - Math 扩展](mathx) | √ | +| [Scripting Java - 脚本化 Java](scriptingJava) | √ | +| [Android API Level - 安卓 API 级别](apiLevel) | √ | \ No newline at end of file diff --git a/api/qa.md b/api/qa.md index 012894f..96cb64c 100644 --- a/api/qa.md +++ b/api/qa.md @@ -1,62 +1,81 @@ -# Q & A +# 疑难解答 (Q & A) -## 如何定时运行脚本nenenene +--- -点击脚本右边的菜单按钮->更多->定时任务即可定时运行脚本,但是必须保持Auto.js后台运行(自启动白名单、电源管理白名单等)。同时,可以在脚本的开头使用`device.wakeUp()`来唤醒屏幕;但是,Auto.js没有解锁屏幕的功能,因此难以在有锁屏密码的设备上达到效果。 +## 图像 -## 定时任何如何获取外部参数 +### 区域截图 -如果一个脚本是用intent"启动"的,比如定时任务中的特定事件(网络状态变化等)触发而启动的,则可以通过`engines.myEngine().execArgv.intent`获取启动的intent,从而获取外部参数。 +AutoJs6 不支持区域截图. +只能通过 `images.captureScreen` 截取屏幕后使用 `images.clip` 等方式做进一步处理. -## 如何把图片和脚本一起打包,或者打包多个脚本 +## 定时任务 -如果除了单脚本以外还有其他脚本、图片、音乐等资源一起打包,则需要使用项目功能。 +### 定时运行脚本 -点击Auto.js的"+"号,选择项目,填写项目名称、包名等信息以后,点击"√"即可新建一个项目。可以在项目中放多个脚本、模块、资源文件,点击项目工具栏的apk打包图标即可打包一个项目,点击工具栏可以重新配置项目。 +脚本右侧菜单 -> 定时任务, 即可定时运行脚本. +需保持 AutoJs6 后台运行, 包括 [ 自启动白名单 / 忽略电池优化 / 忽略后台活动限制 / 系统多任务保留 ] 等. +在设备关屏情况下, 可使用 `device.wakeUp()` 唤醒屏幕. +但 AutoJs6 暂未提供解锁功能, 因此可能需要根据设备自行设计解锁代码. -例如,主脚本要读取同一文件夹下的图片1.png,再执行找图,则可以通过`images.read("./1.png")`来读取,其中"./1.png"表示同一目录1.png图片;ui中的图片控件要引用同一文件夹的2.png图片则为``。Auto.js内置的函数和模块都支持相对路径,但是,其他情况则需要使用`files.path()`函数来把相对路径转换为绝对路径。 +### 定时任务获取外部参数 -## 如何使打包的应用不显示主界面 +若脚本由 intent (如网络状态变化等特定事件) 触发启动, 可通过 `engines.myEngine().execArgv.intent` 获取 intent, 进而获取外部参数. -需要使用项目功能。新建项目后,修改项目下的`project.json`文件,增加以下条目: +## 打包应用 -``` -"launchConfig": { +### 图片等资源共同打包及多脚本打包 + +上述需求需使用 "项目" 功能. + +点击 AutoJs6 主页面 "+" 图标, 选择项目, 填写信息后可新建一个项目. +项目支持存放多个 [ 脚本 / 模块 / 资源文件 ]. +项目工具栏的 APK 打包图标, 点击可打包一个项目. + +例如: +脚本读取同目录 `1.png`: `images.read("./1.png")`. +UI 脚本图片控件引用同目录 `2.png`: ``. +AutoJs6 内置模块支持相对路径引用, 其他情况可能需借助 `files.path()` 转换为绝对路径. + +### 打包应用不显示主界面 + +需使用 "项目" 功能. +新建项目后, 在项目目录 `project.json` 文件中增加以下条目: + +```json +{ + "launchConfig": { "hideLogs": true + } } ``` -例如: +例如: -``` +```json { - "name": "项目名称", + "name": "First-Project", "versionName": "1.0.0", "versionCode": 1, - "packageName": "org.autojs.example", + "packageName": "org.autojs.example.first", "main": "main.js", "launchConfig": { - "hideLogs": true + "hideLogs": true } } ``` -"launchConfig"表示启动配置,"hideLogs"表示隐藏日志。 +## 功能扩展 -参见项目与项目配置。 +AutoJs6 支持直接调用 [ Java / Android / 扩展库 ] 等 API. +对于 AutoJs6 没有内置的功能, 可进行 Java 脚本化, 即直接参照 Java (或 Kotlin 等) 源码, 转换为 JavaScript 代码. +例如: -## Auto.js自带的模块和函数中没有的功能如何实现 - -由于Auto.js支持直接调用Android的API,对于Auto.js没有内置的函数,可以直接通过修改Android代码为JavaScript代码实现。例如旋转图片的Android代码为: - -``` +```java import android.graphics.Bitmap; import android.graphics.Matrix; -public static Bitmap rotate(final Bitmap src, - final int degrees, - final float px, - final float py) { +public static Bitmap rotate(Bitmap src, int degrees, float px, float py) { if (degrees == 0) return src; Matrix matrix = new Matrix(); matrix.setRotate(degrees, px, py); @@ -65,19 +84,19 @@ public static Bitmap rotate(final Bitmap src, } ``` -转换为JavaScript的代码后为: +转换为 JavaScript 代码: -``` +```js importClass(android.graphics.Bitmap); importClass(android.graphics.Matrix); -function rotate(src, degrees, px, py){ +function rotate(src, degrees, px, py) { if (degrees == 0) return src; - var matrix = new Matrix(); + let matrix = new Matrix(); matrix.setRotate(degrees, px, py); - var ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); + let ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); return ret; } ``` -有关调用Android和Java的API的更多信息,参见[Work with Java](https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/Rhino/Scripting_Java)。 \ No newline at end of file +关于脚本化 Java 的更多信息, 参阅 [Scripting Java - 脚本化 Java](scriptingJava) 章节. \ No newline at end of file diff --git a/api/recorder.md b/api/recorder.md new file mode 100644 index 0000000..af7385e --- /dev/null +++ b/api/recorder.md @@ -0,0 +1,66 @@ +# 记录器 (Recorder) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 22, 2022.

+ +--- + +记录器用于计时. + +> 注: 为避免与 Timers (定时器) 混淆, 本条目不采用 "计时器" 定义. + +--- + +

recorder

+ +--- + +## [@] recorder + +### recorder() + +### recorder(key) + +### recorder(key, timestamp) + +### recorder(func) + +### recorder(func, thisType) + +## [m] save + +### save() + +### save(key) + +### save(key, timestamp) + +## [m] load + +### load() + +### load(key) + +### load(key, timestamp) + +## [m] isLessThan + +### isLessThan(key, compare) + +## [m] isGreaterThan + +### isGreaterThan(key, compare) + +## [m] has + +### has(key) + +## [m] remove + +### remove(key) + +## [m] clear + +### clear() \ No newline at end of file diff --git a/api/runtime.md b/api/runtime.md new file mode 100644 index 0000000..50a413b --- /dev/null +++ b/api/runtime.md @@ -0,0 +1,52 @@ +# 运行时 (Runtime) + +--- + +

此章节待补充或完善...

+

Marked by SuperMonster003 on Oct 31, 2022.

+ +--- + +## runtime.requestPermissions(permissions) + +* `permissions` {Array} 权限的字符串数组 + +动态申请安卓的权限. 例如: + +``` +//请求GPS权限 +runtime.requestPermissions(["access_fine_location"]); +``` + +尽管安卓有很多权限, 但必须写入Manifest才能动态申请, 为了防止权限的滥用, 目前Auto.js只能额外申请两个权限: + +* `access_fine_location` GPS权限 +* `record_audio` 录音权限 + +您可以通过APK编辑器来增加Auto.js以及Auto.js打包的应用的权限. + +安卓所有的权限列表参见[Permissions Overview](https://developer.android.com/guide/topics/permissions/overview/). (并没有用) + +## runtime.loadJar(path) + +* `path` {string} jar文件路径 + +加载目标jar文件, 加载成功后将可以使用该Jar文件的类. + +``` +// 加载jsoup.jar +runtime.loadJar("./jsoup.jar"); +// 使用jsoup解析html +importClass(org.jsoup.Jsoup); +log(Jsoup.parse(files.read("./test.html"))); +``` + +(jsoup是一个Java实现的解析Html DOM的库, 可以在[Jsoup](https://jsoup.org/download/)下载/) + +## runtime.loadDex(path) + +* `path` {string} dex文件路径 + +加载目标dex文件, 加载成功后将可以使用该dex文件的类. + +因为加载jar实际上是把jar转换为dex再加载的, 因此加载dex文件会比jar文件快得多. 可以使用Android SDK的build tools的dx工具把jar转换为dex. diff --git a/api/scriptingJava.md b/api/scriptingJava.md new file mode 100644 index 0000000..607feb8 --- /dev/null +++ b/api/scriptingJava.md @@ -0,0 +1,262 @@ +# 脚本化 Java + +Rhino 引擎提供了脚本化 Java 的便捷性. + +> 注: 此章节参考并修改自 [Auto.js Pro](https://pro.autojs.org/) 及 [Scripting Java](http://udn.realityripple.com/docs/Mozilla/Projects/Rhino/Scripting_Java/). + +> 注: ECMA 标准并未包含与 Java 的交互, 本章节所有功能均作为扩展功能而非语言标准使用. + +## 访问 Java 包和类 + +Rhino 定义了顶层变量 `Packages`, 可用于访问顶层 Java 包 (如 [ java / com ] 等). + +```js +typeof Packages === 'object'; // true +typeof Packages.java === 'object'; // true +``` + +为了便于访问, Rhino 提供了顶层包名前缀的快捷访问方式. + +```js +Packages.java === java; // true +``` + +> 注: AutoJs6 顶层化的包名前缀包括 [ android / androidx / java / javax / org / com / net / de / ezy / kotlin / okhttp3 ]. + +顶层方法 `importPackage` 及 `importClass` 可以像 `import` 语句一样导入包或类. + +```js +importPackage(java.io); +typeof File === 'object'; // true +``` + +上述示例相当于 Java 的导入声明语句 `import java.io.*;` + +```js +importClass(java.io.File); +typeof File === 'object'; // true +``` + +上述示例相当于 Java 的导入声明语句 `import java.io.File;` + +```js +const File = java.io.File; +typeof File === 'object'; // true +``` + +上述示例也可使用解构赋值方式导入 File 类: `const {File} = java.io;` + +> 注: 配合 TypeScript Declarations 的 IDE 可能对解构赋值变量无法进行类型识别和代码智能提示. +> 因此建议使用原始变量声明方式导入需要使用的类. +> 对于 importClass 和 importPackage, 目前还未能实现类型识别和代码智能提示 (截至 2022 年 7 月). + +## 访问扩展的包及类 + +AutoJs6 的项目扩展库及项目依赖包均可直接访问. + +```js +/* 依赖包: implementation("joda-time:joda-time:2.10.14") */ +typeof org.joda.time.LocalDateTime.now; // "function" + +/* 扩展库: implementation(project(":libs:org.opencv-4.5.5")) */ +typeof org.opencv.core.Point; // "function" +``` + +## 与 Java 协作 + +Rhino 提供了在 JavaScript 代码中 [ 创建 Java 类 / 调用 Java 方法 / 访问变量 ] 等能力. + +### Java 类及方法 + +```js +let builder = new java.lang.StringBuilder(); +builder.append('foo'); +builder.append('bar'); +builder.toString(); // "foobar" +``` + +### Java 类的静态方法及字段 + +```js +java.lang.Math.PI; // 3.141592653589793 +java.lang.Math.cos(0); // 1 +``` + +### Java 重载方法签名 + +```js +new java.io.File("foo").listFiles.toString(); + +/* 代码结果 (共 5 行) */ + +// function listFiles() {/* +// java.io.File[] listFiles(java.io.FileFilter) +// java.io.File[] listFiles(java.io.FilenameFilter) +// java.io.File[] listFiles() +// */} +``` + +### JavaBean 属性 + +读写方法符合以下命名规范的类称为 JavaBean: + +```java +/* 读方法 */ +getXyz(): Type +/* 写方法 */ +setXyz(Type value): void +``` + +以下 JavaBean 示例 (Student) 定义了 age 和 sex 属性: + +```java +public class Student { + + private int mAge; + + public int getAge() { return mAge; } + public void setAge(int anAge) { mAge = anAge; } + + public String getSex() { return "male"; } + +}; +``` + +其中 age 属性可读写, 而 sex 属性只读. + +在 JavaScript 代码中可通过实例成员访问属性及方法: + +```js +let stu = new Student(); +stu.sex; // "male" - 相当于 stu.getSex(); +stu.age = 33; /* 相当于 stu.setAge(33); */ +stu.age; // 33 +stu.getAge(); // 33 +``` + +同样地, 对于属性类型为 boolean 类型的 JavaBean: + +```java +public class Student { + + private boolean mMale; + + public boolean isMale() { return mMale; } + public String setMale(boolean value) { mMale = value; } + +}; +``` + +JavaScript 使用方式: + +```js +let stu = new Student(); +stu.male; // false - 相当于 stu.isMale(); +stu.male = true; /* 相当于 stu.setMale(true); */ +stu.male; // true +stu.isMale(); // true +``` + +## 实现 Java 接口 + +### 函数转换 + +```js +"ui"; + +ui.layout( + + ' + + ''; + return ( + '
' + aside + + '
' + + '
' + + '
' + + '
' + ); + } + + /** + * Cover Page + * @returns {String} Cover page + */ + function cover() { + var SL = ', 100%, 85%'; + var bgc = + 'linear-gradient(to left bottom, ' + + 'hsl(' + (Math.floor(Math.random() * 255) + SL) + ') 0%,' + + 'hsl(' + (Math.floor(Math.random() * 255) + SL) + ') 100%)'; + + return ( + '
' + + '
' + + '
' + + '
' + ); + } + + /** + * Render tree + * @param {Array} toc Array of TOC section links + * @param {String} tpl TPL list + * @return {String} Rendered tree + */ + function tree(toc, tpl) { + if (tpl === void 0) tpl = '
    {inner}
'; + + if (!toc || !toc.length) { + return ''; + } + + var innerHTML = ''; + toc.forEach(function (node) { + var title = node.title.replace(/(<([^>]+)>)/g, ''); + innerHTML += '
  • ' + (node.title) + '
  • '; + if (node.children) { + innerHTML += tree(node.children, tpl); + } + }); + return tpl.replace('{inner}', innerHTML); + } + + function helper(className, content) { + return ('

    ' + (content.slice(5).trim()) + '

    '); + } + + function theme(color) { + return (''); + } + + /** + * Gen toc tree + * @link https://github.com/killercup/grock/blob/5280ae63e16c5739e9233d9009bc235ed7d79a50/styles/solarized/assets/js/behavior.coffee#L54-L81 + * @param {Array} toc List of TOC elements + * @param {Number} maxLevel Deep level + * @return {Array} Headlines + */ + function genTree(toc, maxLevel) { + var headlines = []; + var last = {}; + + toc.forEach(function (headline) { + var level = headline.level || 1; + var len = level - 1; + + if (level > maxLevel) { + return; + } + + if (last[len]) { + last[len].children = (last[len].children || []).concat(headline); + } else { + headlines.push(headline); + } + + last[level] = headline; + }); + + return headlines; + } + + var cache$1 = {}; + var re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g; + + function lower(string) { + return string.toLowerCase(); + } + + function slugify(str) { + if (typeof str !== 'string') { + return ''; + } + + var slug = str + .trim() + .replace(/[A-Z]+/g, lower) + .replace(/<[^>]+>/g, '') + .replace(re, '') + .replace(/\s/g, '-') + .replace(/-+/g, '-') + .replace(/^(\d)/, '_$1'); + var count = cache$1[slug]; + + count = hasOwn.call(cache$1, slug) ? count + 1 : 0; + cache$1[slug] = count; + + if (count) { + slug = slug + '-' + count; + } + + return slug; + } + + slugify.clear = function () { + cache$1 = {}; + }; + + function replace(m, $1) { + return ( + '' +
+            $1 +
+            '' + ); + } + + function emojify(text) { + return text + .replace(/:\+1:/g, ':thumbsup:') + .replace(/:-1:/g, ':thumbsdown:') + .replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g, function (m) { + return m.replace(/:/g, '__colon__'); + }, + ) + .replace(/:(\w+?):/gi, (window.emojify) || replace) + .replace(/__colon__/g, ':'); + } + + /** + * Converts a colon formatted string to a object with properties. + * + * This is process a provided string and look for any tokens in the format + * of `:name[=value]` and then convert it to a object and return. + * An example of this is ':include :type=code :fragment=demo' is taken and + * then converted to: + * + * ``` + * { + * include: '', + * type: 'code', + * fragment: 'demo' + * } + * ``` + * + * @param {string} str The string to parse. + * + * @return {object} The original string and parsed object, { str, config }. + */ + function getAndRemoveConfig(str) { + if (str === void 0) str = ''; + + var config = {}; + + if (str) { + str = str + .replace(/^('|")/, '') + .replace(/('|")$/, '') + .replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g, function (m, key, value) { + if (key.indexOf(':') === -1) { + config[key] = (value && value.replace(/"/g, '')) || true; + return ''; + } + + return m; + }) + .trim(); + } + + return { str: str, config: config }; + } + + /** + * Remove the tag from sidebar when the header with link, details see issue 1069 + * @param {string} str The string to deal with. + * + * @return {string} str The string after delete the element. + */ + function removeAtag(str) { + if (str === void 0) str = ''; + + return str.replace(/(<\/?a.*?>)/gi, ''); + } + + var imageCompiler = function (ref) { + var renderer = ref.renderer; + var contentBase = ref.contentBase; + var router = ref.router; + + return (renderer.image = function (href, title, text) { + var url = href; + var attrs = []; + + var ref = getAndRemoveConfig(title); + var str = ref.str; + var config = ref.config; + title = str; + + if (config['no-zoom']) { + attrs.push('data-no-zoom'); + } + + if (title) { + attrs.push(('title="' + title + '"')); + } + + if (config.size) { + var ref$1 = config.size.split('x'); + var width = ref$1[0]; + var height = ref$1[1]; + if (height) { + attrs.push(('width="' + width + '" height="' + height + '"')); + } else { + attrs.push(('width="' + width + '"')); + } + } + + if (config.class) { + attrs.push(('class="' + (config.class) + '"')); + } + + if (config.id) { + attrs.push(('id="' + (config.id) + '"')); + } + + if (!isAbsolutePath(href)) { + url = getPath(contentBase, getParentPath(router.getCurrentPath()), href); + } + + if (attrs.length > 0) { + return ('' + text + ''); + } + + return ('' + text + ''); + }); + }; + + var prism = createCommonjsModule(function (module) { + /* ********************************************** + Begin prism-core.js + ********************************************** */ + + /// + + var _self = (typeof window !== 'undefined') + ? window // if in browser + : ( + (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) + ? self // if in worker + : {} // if in node js + ); + + /** + * Prism: Lightweight, robust, elegant syntax highlighting + * + * @license MIT + * @author Lea Verou + * @namespace + * @public + */ + var Prism = (function (_self) { + + // Private helper vars + var lang = /\blang(?:uage)?-([\w-]+)\b/i; + var uniqueId = 0; + + + var _ = { + /** + * By default, Prism will attempt to highlight all code elements (by calling {@link Prism.highlightAll}) on the + * current page after the page finished loading. This might be a problem if e.g. you wanted to asynchronously load + * additional languages or plugins yourself. + * + * By setting this value to `true`, Prism will not automatically highlight all code elements on the page. + * + * You obviously have to change this value before the automatic highlighting started. To do this, you can add an + * empty Prism object into the global scope before loading the Prism script like this: + * + * ```js + * window.Prism = window.Prism || {}; + * Prism.manual = true; + * // add a new