From f7beded5e5b0c65f295a727d9f3306edf72adb9a Mon Sep 17 00:00:00 2001 From: NEPDream Date: Sat, 20 Jul 2019 09:53:53 -0400 Subject: [PATCH] add auto.js doc --- AutoJs-Docs/LICENSE | 201 + AutoJs-Docs/README.md | 7 + AutoJs-Docs/api/_toc.md | 38 + AutoJs-Docs/api/all.md | 24 + AutoJs-Docs/api/app.md | 320 + AutoJs-Docs/api/canvas.md | 38 + AutoJs-Docs/api/console.md | 189 + .../api/coordinates-based-automation.md | 239 + AutoJs-Docs/api/device.md | 297 + AutoJs-Docs/api/dialogs.md | 521 ++ AutoJs-Docs/api/documentation.md | 69 + AutoJs-Docs/api/engines.md | 209 + AutoJs-Docs/api/events.md | 594 ++ AutoJs-Docs/api/files.md | 317 + AutoJs-Docs/api/floaty.md | 196 + AutoJs-Docs/api/globals.md | 198 + AutoJs-Docs/api/http.md | 240 + AutoJs-Docs/api/images.md | 855 ++ AutoJs-Docs/api/keys.md | 194 + AutoJs-Docs/api/media.md | 101 + AutoJs-Docs/api/modules.md | 47 + AutoJs-Docs/api/overview.md | 28 + AutoJs-Docs/api/qa.md | 77 + AutoJs-Docs/api/sensors.md | 225 + AutoJs-Docs/api/shell.md | 431 + AutoJs-Docs/api/storages.md | 70 + AutoJs-Docs/api/threads.md | 388 + AutoJs-Docs/api/timers.md | 94 + AutoJs-Docs/api/ui.md | 834 ++ AutoJs-Docs/api/util.md | 1206 +++ AutoJs-Docs/api/widgets-based-automation.md | 1078 +++ AutoJs-Docs/docs/all.html | 7137 +++++++++++++++++ AutoJs-Docs/docs/app.html | 371 + AutoJs-Docs/docs/assets/dnt_helper.js | 49 + AutoJs-Docs/docs/assets/fonts.css | 48 + .../assets/fonts/S6u9w4BMUTPHh6UVSwiPGQ.woff2 | Bin 0 -> 22820 bytes .../assets/fonts/S6uyw4BMUTPHjx4wXg.woff2 | Bin 0 -> 23316 bytes AutoJs-Docs/docs/assets/sh.css | 25 + AutoJs-Docs/docs/assets/sh_javascript.min.js | 1 + AutoJs-Docs/docs/assets/sh_main.js | 553 ++ AutoJs-Docs/docs/assets/style.css | 523 ++ AutoJs-Docs/docs/canvas.html | 119 + AutoJs-Docs/docs/console.html | 265 + .../docs/coordinates-based-automation.html | 298 + AutoJs-Docs/docs/device.html | 392 + AutoJs-Docs/docs/dialogs.html | 562 ++ AutoJs-Docs/docs/documentation.html | 139 + AutoJs-Docs/docs/engines.html | 271 + AutoJs-Docs/docs/events.html | 613 ++ AutoJs-Docs/docs/files.html | 402 + AutoJs-Docs/docs/floaty.html | 257 + AutoJs-Docs/docs/globals.html | 266 + AutoJs-Docs/docs/http.html | 306 + AutoJs-Docs/docs/images.html | 931 +++ AutoJs-Docs/docs/images/ex-gravity.png | Bin 0 -> 5583 bytes AutoJs-Docs/docs/images/ex-layout-gravity.png | Bin 0 -> 14420 bytes AutoJs-Docs/docs/images/ex-marginLeft.png | Bin 0 -> 11235 bytes AutoJs-Docs/docs/images/ex-padding.png | Bin 0 -> 5589 bytes AutoJs-Docs/docs/images/ex-properties.png | Bin 0 -> 12337 bytes AutoJs-Docs/docs/images/ex-w.png | Bin 0 -> 10815 bytes AutoJs-Docs/docs/images/ex1-horizontal.png | Bin 0 -> 9516 bytes AutoJs-Docs/docs/images/ex1-margin.png | Bin 0 -> 10866 bytes AutoJs-Docs/docs/images/ex1.png | Bin 0 -> 10499 bytes AutoJs-Docs/docs/images/ex2-margin.png | Bin 0 -> 10974 bytes AutoJs-Docs/docs/index.html | 129 + AutoJs-Docs/docs/keys.html | 295 + AutoJs-Docs/docs/media.html | 177 + AutoJs-Docs/docs/modules.html | 125 + AutoJs-Docs/docs/overview.html | 118 + AutoJs-Docs/docs/qa.html | 153 + AutoJs-Docs/docs/sensors.html | 321 + AutoJs-Docs/docs/shell.html | 473 ++ AutoJs-Docs/docs/storages.html | 155 + AutoJs-Docs/docs/threads.html | 414 + AutoJs-Docs/docs/timers.html | 170 + AutoJs-Docs/docs/ui.html | 744 ++ AutoJs-Docs/docs/util.html | 1222 +++ .../docs/widgets-based-automation.html | 1103 +++ AutoJs-Docs/docs/work-with-java.html | 83 + AutoJs-Docs/generator/.gitignore | 1 + AutoJs-Docs/generator/LICENSE | 18 + AutoJs-Docs/generator/README.md | 107 + AutoJs-Docs/generator/addon-verify.js | 115 + AutoJs-Docs/generator/auto_generate.py | 49 + AutoJs-Docs/generator/common.js | 45 + AutoJs-Docs/generator/generate.js | 105 + AutoJs-Docs/generator/html.js | 477 ++ AutoJs-Docs/generator/json.js | 597 ++ AutoJs-Docs/generator/npm-debug.log | 112 + AutoJs-Docs/generator/package.json | 16 + AutoJs-Docs/generator/preprocess.js | 61 + AutoJs-Docs/generator/type-parser.js | 101 + AutoJs-Docs/json/_toc.json | 345 + AutoJs-Docs/json/all.json | 14 + AutoJs-Docs/json/app.json | 619 ++ AutoJs-Docs/json/canvas.json | 43 + AutoJs-Docs/json/console.json | 490 ++ .../json/coordinates-based-automation.json | 814 ++ AutoJs-Docs/json/device.json | 686 ++ AutoJs-Docs/json/dialogs.json | 561 ++ AutoJs-Docs/json/documentation.json | 313 + AutoJs-Docs/json/engines.json | 443 + AutoJs-Docs/json/events.json | 977 +++ AutoJs-Docs/json/files.json | 1193 +++ AutoJs-Docs/json/floaty.json | 411 + AutoJs-Docs/json/globals.json | 449 ++ AutoJs-Docs/json/http.json | 325 + AutoJs-Docs/json/images.json | 2184 +++++ AutoJs-Docs/json/keys.json | 341 + AutoJs-Docs/json/media.json | 206 + AutoJs-Docs/json/modules.json | 14 + AutoJs-Docs/json/overview.json | 12 + AutoJs-Docs/json/qa.json | 48 + AutoJs-Docs/json/sensors.json | 154 + AutoJs-Docs/json/storages.json | 205 + AutoJs-Docs/json/threads.json | 381 + AutoJs-Docs/json/timers.json | 217 + AutoJs-Docs/json/ui.json | 598 ++ AutoJs-Docs/json/util.json | 1340 ++++ .../json/widgets-based-automation.json | 2631 ++++++ AutoJs-Docs/template.html | 49 + 121 files changed, 46402 insertions(+) create mode 100644 AutoJs-Docs/LICENSE create mode 100644 AutoJs-Docs/README.md create mode 100644 AutoJs-Docs/api/_toc.md create mode 100644 AutoJs-Docs/api/all.md create mode 100644 AutoJs-Docs/api/app.md create mode 100644 AutoJs-Docs/api/canvas.md create mode 100644 AutoJs-Docs/api/console.md create mode 100644 AutoJs-Docs/api/coordinates-based-automation.md create mode 100644 AutoJs-Docs/api/device.md create mode 100644 AutoJs-Docs/api/dialogs.md create mode 100644 AutoJs-Docs/api/documentation.md create mode 100644 AutoJs-Docs/api/engines.md create mode 100644 AutoJs-Docs/api/events.md create mode 100644 AutoJs-Docs/api/files.md create mode 100644 AutoJs-Docs/api/floaty.md create mode 100644 AutoJs-Docs/api/globals.md create mode 100644 AutoJs-Docs/api/http.md create mode 100644 AutoJs-Docs/api/images.md create mode 100644 AutoJs-Docs/api/keys.md create mode 100644 AutoJs-Docs/api/media.md create mode 100644 AutoJs-Docs/api/modules.md create mode 100644 AutoJs-Docs/api/overview.md create mode 100644 AutoJs-Docs/api/qa.md create mode 100644 AutoJs-Docs/api/sensors.md create mode 100644 AutoJs-Docs/api/shell.md create mode 100644 AutoJs-Docs/api/storages.md create mode 100644 AutoJs-Docs/api/threads.md create mode 100644 AutoJs-Docs/api/timers.md create mode 100644 AutoJs-Docs/api/ui.md create mode 100644 AutoJs-Docs/api/util.md create mode 100644 AutoJs-Docs/api/widgets-based-automation.md create mode 100644 AutoJs-Docs/docs/all.html create mode 100644 AutoJs-Docs/docs/app.html create mode 100644 AutoJs-Docs/docs/assets/dnt_helper.js create mode 100644 AutoJs-Docs/docs/assets/fonts.css create mode 100644 AutoJs-Docs/docs/assets/fonts/S6u9w4BMUTPHh6UVSwiPGQ.woff2 create mode 100644 AutoJs-Docs/docs/assets/fonts/S6uyw4BMUTPHjx4wXg.woff2 create mode 100644 AutoJs-Docs/docs/assets/sh.css create mode 100644 AutoJs-Docs/docs/assets/sh_javascript.min.js create mode 100644 AutoJs-Docs/docs/assets/sh_main.js create mode 100644 AutoJs-Docs/docs/assets/style.css create mode 100644 AutoJs-Docs/docs/canvas.html create mode 100644 AutoJs-Docs/docs/console.html create mode 100644 AutoJs-Docs/docs/coordinates-based-automation.html create mode 100644 AutoJs-Docs/docs/device.html create mode 100644 AutoJs-Docs/docs/dialogs.html create mode 100644 AutoJs-Docs/docs/documentation.html create mode 100644 AutoJs-Docs/docs/engines.html create mode 100644 AutoJs-Docs/docs/events.html create mode 100644 AutoJs-Docs/docs/files.html create mode 100644 AutoJs-Docs/docs/floaty.html create mode 100644 AutoJs-Docs/docs/globals.html create mode 100644 AutoJs-Docs/docs/http.html create mode 100644 AutoJs-Docs/docs/images.html create mode 100644 AutoJs-Docs/docs/images/ex-gravity.png create mode 100644 AutoJs-Docs/docs/images/ex-layout-gravity.png create mode 100644 AutoJs-Docs/docs/images/ex-marginLeft.png create mode 100644 AutoJs-Docs/docs/images/ex-padding.png create mode 100644 AutoJs-Docs/docs/images/ex-properties.png create mode 100644 AutoJs-Docs/docs/images/ex-w.png create mode 100644 AutoJs-Docs/docs/images/ex1-horizontal.png create mode 100644 AutoJs-Docs/docs/images/ex1-margin.png create mode 100644 AutoJs-Docs/docs/images/ex1.png create mode 100644 AutoJs-Docs/docs/images/ex2-margin.png create mode 100644 AutoJs-Docs/docs/index.html create mode 100644 AutoJs-Docs/docs/keys.html create mode 100644 AutoJs-Docs/docs/media.html create mode 100644 AutoJs-Docs/docs/modules.html create mode 100644 AutoJs-Docs/docs/overview.html create mode 100644 AutoJs-Docs/docs/qa.html create mode 100644 AutoJs-Docs/docs/sensors.html create mode 100644 AutoJs-Docs/docs/shell.html create mode 100644 AutoJs-Docs/docs/storages.html create mode 100644 AutoJs-Docs/docs/threads.html create mode 100644 AutoJs-Docs/docs/timers.html create mode 100644 AutoJs-Docs/docs/ui.html create mode 100644 AutoJs-Docs/docs/util.html create mode 100644 AutoJs-Docs/docs/widgets-based-automation.html create mode 100644 AutoJs-Docs/docs/work-with-java.html create mode 100644 AutoJs-Docs/generator/.gitignore create mode 100644 AutoJs-Docs/generator/LICENSE create mode 100644 AutoJs-Docs/generator/README.md create mode 100644 AutoJs-Docs/generator/addon-verify.js create mode 100644 AutoJs-Docs/generator/auto_generate.py create mode 100644 AutoJs-Docs/generator/common.js create mode 100644 AutoJs-Docs/generator/generate.js create mode 100644 AutoJs-Docs/generator/html.js create mode 100644 AutoJs-Docs/generator/json.js create mode 100644 AutoJs-Docs/generator/npm-debug.log create mode 100644 AutoJs-Docs/generator/package.json create mode 100644 AutoJs-Docs/generator/preprocess.js create mode 100644 AutoJs-Docs/generator/type-parser.js create mode 100644 AutoJs-Docs/json/_toc.json create mode 100644 AutoJs-Docs/json/all.json create mode 100644 AutoJs-Docs/json/app.json create mode 100644 AutoJs-Docs/json/canvas.json create mode 100644 AutoJs-Docs/json/console.json create mode 100644 AutoJs-Docs/json/coordinates-based-automation.json create mode 100644 AutoJs-Docs/json/device.json create mode 100644 AutoJs-Docs/json/dialogs.json create mode 100644 AutoJs-Docs/json/documentation.json create mode 100644 AutoJs-Docs/json/engines.json create mode 100644 AutoJs-Docs/json/events.json create mode 100644 AutoJs-Docs/json/files.json create mode 100644 AutoJs-Docs/json/floaty.json create mode 100644 AutoJs-Docs/json/globals.json create mode 100644 AutoJs-Docs/json/http.json create mode 100644 AutoJs-Docs/json/images.json create mode 100644 AutoJs-Docs/json/keys.json create mode 100644 AutoJs-Docs/json/media.json create mode 100644 AutoJs-Docs/json/modules.json create mode 100644 AutoJs-Docs/json/overview.json create mode 100644 AutoJs-Docs/json/qa.json create mode 100644 AutoJs-Docs/json/sensors.json create mode 100644 AutoJs-Docs/json/storages.json create mode 100644 AutoJs-Docs/json/threads.json create mode 100644 AutoJs-Docs/json/timers.json create mode 100644 AutoJs-Docs/json/ui.json create mode 100644 AutoJs-Docs/json/util.json create mode 100644 AutoJs-Docs/json/widgets-based-automation.json create mode 100644 AutoJs-Docs/template.html diff --git a/AutoJs-Docs/LICENSE b/AutoJs-Docs/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/AutoJs-Docs/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/AutoJs-Docs/README.md b/AutoJs-Docs/README.md new file mode 100644 index 000000000..ed11cb7e6 --- /dev/null +++ b/AutoJs-Docs/README.md @@ -0,0 +1,7 @@ +# AutoJs-Docs + +Auto.js文档。可在 https://hyb1996.github.io/AutoJs-Docs 浏览。 + +文档尚在完善中,可能有文档描述和代码实际行为有出入的情况。 + +模板、样式、generator来自[Node.js](https://github.com/nodejs/node/tree/master/doc)。 \ No newline at end of file diff --git a/AutoJs-Docs/api/_toc.md b/AutoJs-Docs/api/_toc.md new file mode 100644 index 000000000..96d32df9e --- /dev/null +++ b/AutoJs-Docs/api/_toc.md @@ -0,0 +1,38 @@ +@// 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) +* [Console - 控制台](console.html) +* [CoordinatesBasedAutomation - 基于坐标的操作](coordinates-based-automation.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) +* [WidgetsBasedAutomation - 基于控件的操作](widgets-based-automation.html) +* [Sensors - 传感器](sensors.html) +* [Shell - Shell命令](shell.html) +* [Storages - 本地存储](storages.html) +* [Threads - 多线程](threads.html) +* [Timers - 定时器](timers.html) +* [UI - 用户界面](ui.html) +* [Work with Java - 调用Java API](https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/Rhino/Scripting_Java) + +
+ +* [GitHub项目 & Issue提交](https://github.com/hyb1996/NoRootScriptDroid) +* [Auto.js交流社区](http://autojs.org) diff --git a/AutoJs-Docs/api/all.md b/AutoJs-Docs/api/all.md new file mode 100644 index 000000000..52745483a --- /dev/null +++ b/AutoJs-Docs/api/all.md @@ -0,0 +1,24 @@ +@include overview +@include qa +@include app +@include console +@include coordinates-based-automation +@include device +@include dialogs +@include engines +@include events +@include floaty +@include files +@include globals +@include http +@include images +@include keys +@include media +@include modules +@include widgets-based-automation +@include sensors +@include shell +@include storages +@include threads +@include timers +@include ui \ No newline at end of file diff --git a/AutoJs-Docs/api/app.md b/AutoJs-Docs/api/app.md new file mode 100644 index 000000000..2ee9b5f41 --- /dev/null +++ b/AutoJs-Docs/api/app.md @@ -0,0 +1,320 @@ +# App + +app模块提供一系列函数,用于使用其他应用、与其他应用交互。例如发送意图、打开文件、发送邮件等。 + +同时提供了方便的进阶函数startActivity和sendBroadcast,用他们可完成app模块没有内置的和其他应用的交互。 + +## app.versionCode +* {number} + +当前软件版本号,整数值。例如160, 256等。 + +如果在Auto.js中运行则为Auto.js的版本号;在打包的软件中则为打包软件的版本号。 + +``` +toastLog(app.versionCode); +``` + +## app.versionName +* {string} + +当前软件的版本名称,例如"3.0.0 Beta"。 + +如果在Auto.js中运行则为Auto.js的版本名称;在打包的软件中则为打包软件的版本名称。 + +``` +toastLog(app.verionName); +``` + +## app.autojs.versionCode +* {number} + +Auto.js版本号,整数值。例如160, 256等。 + +## app.autojs.versionName +* {string} + +Auto.js版本名称,例如"3.0.0 Beta"。 + +## app.launchApp(appName) +* `appName` {string} 应用名称 + +通过应用名称启动应用。如果该名称对应的应用不存在,则返回false; 否则返回true。如果该名称对应多个应用,则只启动其中某一个。 + +该函数也可以作为全局函数使用。 + +``` +launchApp("Auto.js"); +``` + +## app.launch(packageName) +* `packageName` {string} 应用包名 + +通过应用包名启动应用。如果该包名对应的应用不存在,则返回false;否则返回true。 + +该函数也可以作为全局函数使用。 + +``` +//启动微信 +launch("com.tencent.mm"); +``` + +## app.launchPackage(packageName) +* `packageName` {string} 应用包名 + +相当于`app.launch(packageName)`。 + +## app.getPackageName(appName) +* `appName` {string} 应用名称 + +获取应用名称对应的已安装的应用的包名。如果该找不到该应用,返回null;如果该名称对应多个应用,则只返回其中某一个的包名。 + +该函数也可以作为全局函数使用。 + +``` +var name = getPackageName("QQ"); //返回"com.tencent.mobileqq" +``` + +## app.getAppName(packageName) +* `packageName` {string} 应用包名 + +获取应用包名对应的已安装的应用的名称。如果该找不到该应用,返回null。 + +该函数也可以作为全局函数使用。 + +``` +var name = getAppName("com.tencent.mobileqq"); //返回"QQ" +``` + +## app.openAppSetting(packageName) +* `packageName` {string} 应用包名 + +打开应用的详情页(设置页)。如果找不到该应用,返回false; 否则返回true。 + +该函数也可以作为全局函数使用。 + +## app.viewFile(path) +* `path` {string} 文件路径 + +用其他应用查看文件。文件不存在的情况由查看文件的应用处理。 + +如果找不出可以查看该文件的应用,则抛出`ActivityNotException`。 + +``` +//查看文本文件 +app.viewFile("/sdcard/1.txt"); +``` + +## app.editFile(path) +* `path` {string} 文件路径 + +用其他应用编辑文件。文件不存在的情况由编辑文件的应用处理。 + +如果找不出可以编辑该文件的应用,则抛出`ActivityNotException`。 + +``` +//编辑文本文件 +app.editFile("/sdcard/1.txt/); +``` + +## app.uninstall(packageName) +* `packageName` {string} 应用包名 + +卸载应用。执行后会会弹出卸载应用的提示框。如果该包名的应用未安装,由应用卸载程序处理,可能弹出"未找到应用"的提示。 +``` +//卸载QQ +app.uninstall("com.tencent.mobileqq"); +``` + +## app.openUrl(url) +* `url` {string} 网站的Url,如果不以"http://"或"https://"开头则默认是"http://"。 + +用浏览器打开网站url。 + +如果没有安装浏览器应用,则抛出`ActivityNotException`。 + +## app.sendEmail(options) +* `options` {Object} 发送邮件的参数。包括: + * `email` {string} | {Array} 收件人的邮件地址。如果有多个收件人,则用字符串数组表示 + * `cc` {string} | {Array} 抄送收件人的邮件地址。如果有多个抄送收件人,则用字符串数组表示 + * `bcc` {string} | {Array} 密送收件人的邮件地址。如果有多个密送收件人,则用字符串数组表示 + * `subject` {string} 邮件主题(标题) + * `text` {string} 邮件正文 + * `attachment` {string} 附件的路径。 + +根据选项options调用邮箱应用发送邮件。这些选项均是可选的。 + +如果没有安装邮箱应用,则抛出`ActivityNotException`。 + +``` +//发送邮件给10086@qq.com和10001@qq.com。 +app.sendEmail({ + email: ["10086@qq.com", "10001@qq.com"], + subject: "这是一个邮件标题", + text: "这是邮件正文" +}); +``` + +## app.startActivity(name) +* `name` {string} 活动名称,可选的值为: + * `console` 日志界面 + * `settings` 设置界面 + +启动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)。 + + * `type` {string} 意图的MimeType,表示和该意图直接相关的数据的类型,表示比如"text/plain"为纯文本类型。 + + * `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)。 + + * `packageName` {string} 目标包名 + + * `className` {string} 目标Activity或Service等组件的名称 + + * `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)。 + + **[v4.1.0新增]** + + * `root` {Boolea} 是否以root权限启动、发送该intent。使用该参数后,不能使用`context.startActivity()`等方法,而应该直接使用诸如`app.startActivity({...})`的方法。 + + **[v4.1.0新增]** + +根据选项,构造一个意图Intent对象。 + +例如: +``` +//打开应用来查看图片文件 +var i = app.intent({ + action: "VIEW", + type: "image/png", + data: "file:///sdcard/1.png" +}); +context.startActivity(i); +``` + +需要注意的是,除非应用专门暴露Activity出来,否则在没有root权限的情况下使用intent是无法跳转到特定Activity、应用的特定界面的。例如我们能通过Intent跳转到QQ的分享界面,是因为QQ对外暴露了分享的Activity;而在没有root权限的情况下,我们无法通过intent跳转到QQ的设置界面,因为QQ并没有暴露这个Activity。 + +但如果有root权限,则在intent的参数加上`"root": true`即可。例如使用root权限跳转到Auto.js的设置界面为: +``` +app.startActivity({ + packageName: "org.autojs.autojs", + className: "org.autojs.autojs.ui.settings.SettingsActivity_", + root: true +}); +``` + +另外,关于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)。 + + +## app.startActivity(options) +* `options` {Object} 选项 + +根据选项构造一个Intent,并启动该Activity。 + +``` +app.startActivity({ + action: "SEND", + type: "text/plain", + data: "file:///sdcard/1.txt" +}); +``` + +## app.sendBroadcast(options) +* `options` {Object} 选项 + +根据选项构造一个Intent,并发送该广播。 + +## app.startService(options) +* `options` {Object} 选项 + +根据选项构造一个Intent,并启动该服务。 + +## app.sendBroadcast(name) +**[v4.1.0新增]** +* `name` {string} 特定的广播名称,包括: + * `inspect_layout_hierarchy` 布局层次分析 + * `inspect_layout_bounds` 布局范围 + +发送以上特定名称的广播可以触发Auto.js的布局分析,方便脚本调试。这些广播在Auto.js发送才有效,在打包的脚本上运行将没有任何效果。 + +``` +app.sendBroadcast("inspect_layout_bounds"); +``` + +## app.intentToShell(options) + +**[v4.1.0新增]** + +* `options` {Object} 选项 + +根据选项构造一个Intent,转换为对应的shell的intent命令的参数。 + +例如: +``` +shell("am start " + app.intentToShell({ + packageName: "org.autojs.autojs", + className: "org.autojs.autojs.ui.settings.SettingsActivity_" +}), true); +``` + +参见[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字符串并返回相应的Uri对象。即使Uri格式错误,该函数也会返回一个Uri对象,但之后如果访问该对象的scheme, path等值可能因解析失败而返回`null`。 + +需要注意的是,在高版本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)。 + +从一个文件路径创建一个uri对象。需要注意的是,在高版本Android上,由于系统限制直接在Uri暴露文件的绝对路径,因此返回的Uri会是诸如`content://...`的形式。 \ No newline at end of file diff --git a/AutoJs-Docs/api/canvas.md b/AutoJs-Docs/api/canvas.md new file mode 100644 index 000000000..256ccb9b1 --- /dev/null +++ b/AutoJs-Docs/api/canvas.md @@ -0,0 +1,38 @@ +# Canvas + +canvas提供了使用画布进行2D画图的支持,可用于简单的小游戏开发或者图片编辑。使用canvas可以轻松地在一张图片或一个界面上绘制各种线与图形。 + +canvas的坐标系为平面直角坐标系,以屏幕左上角为原点,屏幕上边为x轴正方向,屏幕左边为y轴正方向。例如分辨率为1920*1080的屏幕上,画一条从屏幕左上角到屏幕右下角的线段为: + +``` +canvas.drawLine(0, 0, 1080, 1920, paint); +``` + +canvas的绘制依赖于画笔Paint, 通过设置画笔的粗细、颜色、填充等可以改变绘制出来的图形。例如绘制一个红色实心正方形为: +``` +var paint = new Paint(); +//设置画笔为填充,则绘制出来的图形都是实心的 +paint.setStyle(Paint.STYLE.FILL); +//设置画笔颜色为红色 +paint.setColor(colors.RED); +//绘制一个从坐标(0, 0)到坐标(100, 100)的正方形 +canvas.drawRect(0, 0, 100, 100, paint); +``` + +如果要绘制正方形的边框,则通过设置画笔的Style来实现: +``` +var paint = new Paint(); +//设置画笔为描边,则绘制出来的图形都是轮廓 +paint.setStyle(Paint.STYLE.STROKE); +//设置画笔颜色为红色 +paint.setColor(colors.RED); +//绘制一个从坐标(0, 0)到坐标(100, 100)的正方形 +canvas.drawRect(0, 0, 100, 100, paint); +``` + +结合画笔canvas可以绘制基本图形、图片等。 + +## canvas.drawARGB(a, r, g, b) + +## canvas.draw + \ No newline at end of file diff --git a/AutoJs-Docs/api/console.md b/AutoJs-Docs/api/console.md new file mode 100644 index 000000000..96bca1883 --- /dev/null +++ b/AutoJs-Docs/api/console.md @@ -0,0 +1,189 @@ +# Console + +> Stability: 2 - Stable + +控制台模块提供了一个和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())。 + +``` +const count = 5; +console.log('count: %d', count); +// 打印: count: 5 到 stdout +console.log('count:', count); +// 打印: count: 5 到 stdout +``` + +详见 util.format()。 + +该函数也可以作为全局函数使用。 + +## console.verbose([data][, ...args]) +* `data` {any} +* `...args` {any} + +与console.log类似,但输出结果以灰色字体显示。输出优先级低于log,用于输出观察性质的信息。 + +## console.info([data][, ...args]) +* `data` {any} +* `...args` {any} + +与console.log类似,但输出结果以绿色字体显示。输出优先级高于log, 用于输出重要信息。 + +## console.warn([data][, ...args]) +* `data` {any} +* `...args` {any} + +与console.log类似,但输出结果以蓝色字体显示。输出优先级高于info, 用于输出警告信息。 + +## console.error([data][, ...args]) +* `data` {any} +* `...args` {any} + +与console.log类似,但输出结果以红色字体显示。输出优先级高于warn, 用于输出错误信息。 + +## console.assert(value, message) +* value {any} 要断言的布尔值 +* message {string} value为false时要输出的信息 + +断言。如果value为false则输出错误信息message并停止脚本运行。 + +``` +var a = 1 + 1; +console.assert(a == 2, "加法出错啦"); +``` +## console.time([label]) +**[v4.1.0新增]** +* `label` {String} 计时器标签,可省略 + +启动一个定时器,用以计算一个操作的持续时间。 +定时器由一个唯一的 `label` 标识。 +当调用 `console.timeEnd()` 时,可以使用相同的 `label` 来停止定时器,并以毫秒为单位将持续时间输出到控制台。 +重复启动同一个标签的定时器会覆盖之前启动同一标签的定时器。 + +## console.timeEnd(label) +**[v4.1.0新增]** +* `label` {String} 计时器标签 + +停止之前通过调用 `console.time()` 启动的定时器,并打印结果到控制台。 +调用 `console.timeEnd()` 后定时器会被删除。如果不存在标签指定的定时器则会打印 `NaNms`。 +```js +console.time('求和'); +var sum = 0; +for(let i = 0; i < 100000; i++){ + sum += i; +} +console.timeEnd('求和'); +// 打印 求和: xxx ms +``` + +## console.trace([data][, ...args]) +**[v4.1.0新增]** +* `data` {any} +* `...args` {any} + +与console.log类似,同时会打印出调用这个函数所在的调用栈信息(即当前运行的文件、行数等信息)。 + +```js +console.trace('Show me'); +// 打印: (堆栈跟踪会根据被调用的跟踪的位置而变化) +// Show me +// at :7 +``` + +## console.input(data[, ...args]) +* `data` {any} +* `...args` {any} + +与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串用eval计算后返回。 + +**部分机型可能会有控制台不显示输入框的情况,属于bug。** + +例如: +``` +var n = console.input("请输入一个数字:"); +//输入123之后: +toast(n + 1); +//显示124 +``` + +## console.rawInput(data[, ...args]) +* `data` {any} +* `...args` {any} + +与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串直接返回。 + +部分机型可能会有控制台不显示输入框的情况,属于bug。 + +例如: +``` +var n = console.rawInput("请输入一个数字:"); +//输入123之后: +toast(n + 1); +//显示1231 +``` + +## console.setSize(w, h) +* `w` {number} 宽度 +* `h` {number} 高度 + +设置控制台的大小,单位像素。 +``` +console.show(); +//设置控制台大小为屏幕的四分之一 +console.setSize(device.width / 2, device.height / 2); +``` + +## console.setPosition(x, y) +* `x` {number} 横坐标 +* `y` {number} 纵坐标 + +设置控制台的位置,单位像素。 + +``` +console.show(); +console.setPosition(100, 100); +``` + +## console.setGlobalLogConfig(config) +**[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) + +设置日志保存的路径和配置。例如把日志保存到"/sdcard/1.txt": + +``` +console.setGlobalLogConfig({ + "file": "/sdcard/1.txt" +}); +``` + +注意该函数会影响所有脚本的日志记录。 + +## print(text) +* text {string} | {Object} 要打印到控制台的信息 + +相当于`log(text)`。 + + diff --git a/AutoJs-Docs/api/coordinates-based-automation.md b/AutoJs-Docs/api/coordinates-based-automation.md new file mode 100644 index 000000000..0db662cbb --- /dev/null +++ b/AutoJs-Docs/api/coordinates-based-automation.md @@ -0,0 +1,239 @@ + +# 基于坐标的触摸模拟 + +> 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/AutoJs-Docs/api/device.md b/AutoJs-Docs/api/device.md new file mode 100644 index 000000000..b84a1da7b --- /dev/null +++ b/AutoJs-Docs/api/device.md @@ -0,0 +1,297 @@ +# Device + +> Stability: 2 - Stable + +device模块提供了与设备有关的信息与操作,例如获取设备宽高,内存使用率,IMEI,调整设备亮度、音量等。 + +此模块的部分函数,例如调整音量,需要"修改系统设置"的权限。如果没有该权限,会抛出`SecurityException`并跳转到权限设置界面。 + +## device.width +* {number} + +设备屏幕分辨率宽度。例如1080。 + +## device.height +* {number} + +设备屏幕分辨率高度。例如1920。 + +## device.buildId +* {string} + +Either a changelist number, or a label like "M4-rc20". + +修订版本号,或者诸如"M4-rc20"的标识。 + +## device.broad +* {string} + +The name of the underlying board, like "goldfish". + +设备的主板(?)型号。 + +## device.brand +* {string} + +The consumer-visible brand with which the product/hardware will be associated, if any. + +与产品或硬件相关的厂商品牌,如"Xiaomi", "Huawei"等。 + +## device.device +* {string} + +The name of the industrial design. + +设备在工业设计中的名称。 + +## deivce.model +* {string} + +The end-user-visible name for the end product. + +设备型号。 + +## device.product +* {string} + +The name of the overall product. + +整个产品的名称。 + +## device.bootloader +* {string} + +The system bootloader version number. + +设备Bootloader的版本。 + +## device.hardware +* {string} + +The name of the hardware (from the kernel command line or /proc). + +设备的硬件名称(来自内核命令行或者/proc)。 + +## device.fingerprint +* {string} + +A string that uniquely identifies this build. Do not attempt to parse this value. + +构建(build)的唯一标识码。 + +## device.serial +* {string} + +A hardware serial number, if available. Alphanumeric only, case-insensitive. + +硬件序列号。 + +## device.sdkInt +* {number} + +The user-visible SDK version of the framework; its possible values are defined in Build.VERSION_CODES. + +安卓系统API版本。例如安卓4.4的sdkInt为19。 + +## device.incremental +* {string} + +The internal value used by the underlying source control to represent this build. E.g., a perforce changelist number or a git hash. + +## device.release +* {string} + +The user-visible version string. E.g., "1.0" or "3.4b5". + +Android系统版本号。例如"5.0", "7.1.1"。 + +## device.baseOS +* {string} + +The base OS build the product is based on. + +## device.securityPatch +* {string} + +The user-visible security patch level. + +安全补丁程序级别。 + +## device.codename +* {string} + +The current development codename, or the string "REL" if this is a release build. + +开发代号,例如发行版是"REL"。 + +## device.getIMEI() +* {string} + +返回设备的IMEI. + +## device.getAndroidId() +* {string} + +返回设备的Android ID。 + +Android ID为一个用16进制字符串表示的64位整数,在设备第一次使用时随机生成,之后不会更改,除非恢复出厂设置。 + +## device.getMacAddress() +* {string} + +返回设备的Mac地址。该函数需要在有WLAN连接的情况下才能获取,否则会返回null。 + +**可能的后续修改**:未来可能增加有root权限的情况下通过root权限获取,从而在没有WLAN连接的情况下也能返回正确的Mac地址,因此请勿使用此函数判断WLAN连接。 + +## device.getBrightness() +* {number} + +返回当前的(手动)亮度。范围为0~255。 + +## device.getBrightnessMode() +* {number} + +返回当前亮度模式,0为手动亮度,1为自动亮度。 + +## device.setBrightness(b) +* `b` {number} 亮度,范围0~255 + +设置当前手动亮度。如果当前是自动亮度模式,该函数不会影响屏幕的亮度。 + +此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。 + +## device.setBrightnessMode(mode) +* `mode` {number} 亮度模式,0为手动亮度,1为自动亮度 + +设置当前亮度模式。 + +此函数需要"修改系统设置"的权限。如果没有该权限,会抛出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并跳转到权限设置界面。 + +## device.setNotificationVolume(volume) +* `volume` {number} 音量 + +设置当前通知音量。 + +此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。 + +## device.setAlarmVolume(volume) +* `volume` {number} 音量 + +设置当前闹钟音量。 + +此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。 + +## device.getBattery() +* {number} 0.0~100.0的浮点数 + +返回当前电量百分比。 + +## device.isCharging() +* {boolean} + +返回设备是否正在充电。 + +## device.getTotalMem() +* {number} + +返回设备内存总量,单位字节(B)。1MB = 1024 * 1024B。 + +## device.getAvailMem() +* {number} + +返回设备当前可用的内存,单位字节(B)。 + +## device.isScreenOn() +* 返回 {boolean} + +返回设备屏幕是否是亮着的。如果屏幕亮着,返回`true`; 否则返回`false`。 + +需要注意的是,类似于vivo xplay系列的息屏时钟不属于"屏幕亮着"的情况,虽然屏幕确实亮着但只能显示时钟而且不可交互,此时`isScreenOn()`也会返回`false`。 + +## device.wakeUp() + +唤醒设备。包括唤醒设备CPU、屏幕等。可以用来点亮屏幕。 + +## device.wakeUpIfNeeded() + +如果屏幕没有点亮,则唤醒设备。 + +## device.keepScreenOn([timeout]) +* `timeout` {number} 屏幕保持常亮的时间, 单位毫秒。如果不加此参数,则一直保持屏幕常亮。 + +保持屏幕常亮。 + +此函数无法阻止用户使用锁屏键等正常关闭屏幕,只能使得设备在无人操作的情况下保持屏幕常亮;同时,如果此函数调用时屏幕没有点亮,则会唤醒屏幕。 + +在某些设备上,如果不加参数timeout,只能在Auto.js的界面保持屏幕常亮,在其他界面会自动失效,这是因为设备的省电策略造成的。因此,建议使用比较长的时长来代替"一直保持屏幕常亮"的功能,例如`device.keepScreenOn(3600 * 1000)`。 + +可以使用`device.cancelKeepingAwake()`来取消屏幕常亮。 + +``` +//一直保持屏幕常亮 +device.keepScreenOn() +``` + +## device.keepScreenDim([timeout]) +* `timeout` {number} 屏幕保持常亮的时间, 单位毫秒。如果不加此参数,则一直保持屏幕常亮。 + +保持屏幕常亮,但允许屏幕变暗来节省电量。此函数可以用于定时脚本唤醒屏幕操作,不需要用户观看屏幕,可以让屏幕变暗来节省电量。 + +此函数无法阻止用户使用锁屏键等正常关闭屏幕,只能使得设备在无人操作的情况下保持屏幕常亮;同时,如果此函数调用时屏幕没有点亮,则会唤醒屏幕。 + +可以使用`device.cancelKeepingAwake()`来取消屏幕常亮。 + +## device.cancelKeepingAwake() + +取消设备保持唤醒状态。用于取消`device.keepScreenOn()`, `device.keepScreenDim()`等函数设置的屏幕常亮。 + +## device.vibrate(millis) +* `millis` {number} 震动时间,单位毫秒 + +使设备震动一段时间。 + +``` +//震动两秒 +device.vibrate(2000); +``` + +## device.cancelVibration() + +如果设备处于震动状态,则取消震动。 diff --git a/AutoJs-Docs/api/dialogs.md b/AutoJs-Docs/api/dialogs.md new file mode 100644 index 000000000..01fe500e0 --- /dev/null +++ b/AutoJs-Docs/api/dialogs.md @@ -0,0 +1,521 @@ +# Dialogs + +> Stability: 2 - Stable + +dialogs 模块提供了简单的对话框支持,可以通过对话框和用户进行交互。最简单的例子如下: +``` +alert("您好"); +``` +这段代码会弹出一个消息提示框显示"您好",并在用户点击"确定"后继续运行。稍微复杂一点的例子如下: +``` +var clear = confirm("要清除所有缓存吗?"); +if(clear){ + alert("清除成功!"); +} +``` +`confirm()`会弹出一个对话框并让用户选择"是"或"否",如果选择"是"则返回true。 + +需要特别注意的是,对话框在ui模式下不能像通常那样使用,应该使用回调函数或者[Promise](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise)的形式。理解这一点可能稍有困难。举个例子: +``` +"ui"; +//回调形式 + confirm("要清除所有缓存吗?", function(clear){ + if(clear){ + alert("清除成功!"); + } + }); +//Promise形式 +confirm("要清除所有缓存吗?") + .then(clear => { + if(clear){ + alert("清除成功!"); + } + }); +``` + +## dialogs.alert(title[, content, callback]) +* `title` {string} 对话框的标题。 +* `content` {string} 可选,对话框的内容。默认为空。 +* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 + +显示一个只包含“确定”按钮的提示对话框。直至用户点击确定脚本才继续运行。 + +该函数也可以作为全局函数使用。 +``` +alert("出现错误~", "出现未知错误,请联系脚本作者”); +``` + +在ui模式下该函数返回一个`Promise`。例如: +``` +"ui"; +alert("嘿嘿嘿").then(()=>{ + //当点击确定后会执行这里 +}); +``` + +## dialogs.confirm(title[, content, callback]) +* `title` {string} 对话框的标题。 +* `content` {string} 可选,对话框的内容。默认为空。 +* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 + +显示一个包含“确定”和“取消”按钮的提示对话框。如果用户点击“确定”则返回 `true` ,否则返回 `false` 。 + +该函数也可以作为全局函数使用。 + + +在ui模式下该函数返回一个`Promise`。例如: +``` +"ui"; +confirm("确定吗").then(value=>{ + //当点击确定后会执行这里, value为true或false, 表示点击"确定"或"取消" +}); +``` + +## dialogs.rawInput(title[, prefill, callback]) +* `title` {string} 对话框的标题。 +* `prefill` {string} 输入框的初始内容,可选,默认为空。 +* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 + +显示一个包含输入框的对话框,等待用户输入内容,并在用户点击确定时将输入的字符串返回。如果用户取消了输入,返回null。 + +该函数也可以作为全局函数使用。 + +``` +var name = rawInput("请输入您的名字", "小明"); +alert("您的名字是" + name); +``` +在ui模式下该函数返回一个`Promise`。例如: +``` +"ui"; +rawInput("请输入您的名字", "小明").then(name => { + alert("您的名字是" + name); +}); +``` +当然也可以使用回调函数,例如: +``` +rawInput("请输入您的名字", "小明", name => { + alert("您的名字是" + name); +}); +``` + +## dialogs.input(title[, prefill, callback]) +等效于 `eval(dialogs.rawInput(title, prefill, callback))`, 该函数和rawInput的区别在于,会把输入的字符串用eval计算一遍再返回,返回的可能不是字符串。 + +可以用该函数输入数字、数组等。例如: +``` +var age = dialogs.input("请输入您的年龄", "18"); +// new Date().getYear() + 1900 可获取当前年份 +var year = new Date().getYear() + 1900 - age; +alert("您的出生年份是" + year); +``` +在ui模式下该函数返回一个`Promise`。例如: +``` +"ui"; +dialogs.input("请输入您的年龄", "18").then(age => { + var year = new Date().getYear() + 1900 - age; + alert("您的出生年份是" + year); +}); +``` +## dialogs.prompt(title[, prefill, callback]) +相当于 `dialogs.rawInput()`; + +## dialogs.select(title, items, callback) +* `title` {string} 对话框的标题。 +* `items` {Array} 对话框的选项列表,是一个字符串数组。 +* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 + +显示一个带有选项列表的对话框,等待用户选择,返回用户选择的选项索引(0 ~ item.length - 1)。如果用户取消了选择,返回-1。 + +``` +var options = ["选项A", "选项B", "选项C", "选项D"] +var i = dialogs.select("请选择一个选项", options); +if(i >= 0){ + toast("您选择的是" + options[i]); +}else{ + toast("您取消了选择"); +} +``` +在ui模式下该函数返回一个`Promise`。例如: +``` +"ui"; +dialogs.select("请选择一个选项", ["选项A", "选项B", "选项C", "选项D"]) + .then(i => { + toast(i); + }); +``` + +## dialogs.singleChoice(title, items[, index, callback]) +* `title` {string} 对话框的标题。 +* `items` {Array} 对话框的选项列表,是一个字符串数组。 +* `index` {number} 对话框的初始选项的位置,默认为0。 +* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 + +显示一个单选列表对话框,等待用户选择,返回用户选择的选项索引(0 ~ item.length - 1)。如果用户取消了选择,返回-1。 + +在ui模式下该函数返回一个`Promise`。 + +## dialogs.multiChoice(title, items[, indices, callback]) +* `title` {string} 对话框的标题。 +* `items` {Array} 对话框的选项列表,是一个字符串数组。 +* `indices` {Array} 选项列表中初始选中的项目索引的数组,默认为空数组。 +* `callback` {Function} 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。 + +显示一个多选列表对话框,等待用户选择,返回用户选择的选项索引的数组。如果用户取消了选择,返回`[]`。 + +在ui模式下该函数返回一个`Promise`。 + +## dialogs.build(properties) +* `properties` {Object} 对话框属性,用于配置对话框。 +* 返回 {Dialog} + +创建一个可自定义的对话框,例如: +``` +dialogs.build({ + //对话框标题 + title: "发现新版本", + //对话框内容 + content: "更新日志: 新增了若干了BUG", + //确定键内容 + positive: "下载", + //取消键内容 + negative: "取消", + //中性键内容 + neutral: "到浏览器下载", + //勾选框内容 + checkBoxPrompt: "不再提示" +}).on("positive", ()=>{ + //监听确定键 + toast("开始下载...."); +}).on("neutral", ()=>{ + //监听中性键 + app.openUrl("https://www.autojs.org"); +}).on("check", (checked)=>{ + //监听勾选框 + log(checked); +}).show(); +``` + +选项properties可供配置的项目为: +* `title` {string} 对话框标题 +* `titleColor` {string} | {number} 对话框标题的颜色 +* `buttonRippleColor` {string} | {number} 对话框按钮的波纹效果颜色 +* `icon` {string} | {Image} 对话框的图标,是一个URL或者图片对象 +* `content` {string} 对话框文字内容 +* `contentColor`{string} | {number} 对话框文字内容的颜色 +* `contentLineSpacing`{number} 对话框文字内容的行高倍数,1.0为一倍行高 +* `items` {Array} 对话框列表的选项 +* `itemsColor` {string} | {number} 对话框列表的选项的文字颜色 +* `itemsSelectMode` {string} 对话框列表的选项选择模式,可以为: + * `select` 普通选择模式 + * `single` 单选模式 + * `multi` 多选模式 +* `itemsSelectedIndex` {number} | {Array} 对话框列表中预先选中的项目索引,如果是单选模式为一个索引;多选模式则为数组 +* `positive` {string} 对话框确定按钮的文字内容(最右边按钮) +* `positiveColor` {string} | {number} 对话框确定按钮的文字颜色(最右边按钮) +* `neutral` {string} 对话框中立按钮的文字内容(最左边按钮) +* `neutralColor` {string} | {number} 对话框中立按钮的文字颜色(最左边按钮) +* `negative` {string} 对话框取消按钮的文字内容(确定按钮左边的按钮) +* `negativeColor` {string} | {number} 对话框取消按钮的文字颜色(确定按钮左边的按钮) +* `checkBoxPrompt` {string} 勾选框文字内容 +* `checkBoxChecked` {boolean} 勾选框是否勾选 +* `progress` {Object} 配置对话框进度条的对象: + * `max` {number} 进度条的最大值,如果为-1则为无限循环的进度条 + * `horizontal` {boolean} 如果为true, 则对话框无限循环的进度条为水平进度条 + * `showMinMax` {boolean} 是否显示进度条的最大值和最小值 +* `cancelable` {boolean} 对话框是否可取消,如果为false,则对话框只能用代码手动取消 +* `canceledOnTouchOutside` {boolean} 对话框是否在点击对话框以外区域时自动取消,默认为true +* `inputHint` {string} 对话框的输入框的输入提示 +* `inputPrefill` {string} 对话框输入框的默认输入内容 + + +通过这些选项可以自定义一个对话框,并通过监听返回的Dialog对象的按键、输入事件来实现交互。下面是一些例子。 + +模拟alert对话框: +``` +dialogs.build({ + title: "你好", + content: "今天也要元气满满哦", + positive: "好的" +}).show(); +``` + +模拟confirm对话框: +``` +dialogs.build({ + title: "你好", + content: "请问你是笨蛋吗?", + positive: "是的", + negative: "我是大笨蛋" +}).on("positive", ()=>{ + alert("哈哈哈笨蛋"); +}).on("negative", ()=>{ + alert("哈哈哈大笨蛋"); +}).show(); +``` + +模拟单选框: +``` +dialogs.build({ + title: "单选", + items: ["选项1", "选项2", "选项3", "选项4"], + itemsSelectMode: "single", + itemsSelectedIndex: 3 +}).on("single_choice", (index, item)=>{ + toast("您选择的是" + item); +}).show(); +``` + +"处理中"对话框: +``` +var d = dialogs.build({ + title: "下载中...", + progress: { + max: -1 + }, + cancelable: false +}).show(); + +setTimeout(()=>{ + d.dismiss(); +}, 3000); +``` + +输入对话框: +``` +dialogs.build({ + title: "请输入您的年龄", + inputPrefill: "18" +}).on("input", (input)=>{ + var age = parseInt(input); + toastLog(age); +}).show(); +``` + +使用这个函数来构造对话框,一个明显的不同是需要使用回调函数而不能像dialogs其他函数一样同步地返回结果;但也可以通过threads模块的方法来实现。例如显示一个输入框并获取输入结果为: +``` +var input = threads.disposable(); +dialogas.build({ + title: "请输入您的年龄", + inputPrefill: "18" +}).on("input", text => { + input.setAndNotify(text); +}).show(); +var age = parseInt(input.blockedGet()); +tosatLog(age); +``` + +# Dialog + +`dialogs.build()`返回的对话框对象,内置一些事件用于响应用户的交互,也可以获取对话框的状态和信息。 + +## 事件: `show` +* `dialog` {Dialog} 对话框 + +对话框显示时会触发的事件。例如: +``` +dialogs.build({ + title: "标题" +}).on("show", (dialog)=>{ + toast("对话框显示了"); +}).show(); +``` + +## 事件: `cancel` +* `dialog` {Dialog} 对话框 + +对话框被取消时会触发的事件。一个对话框可能按取消按钮、返回键取消或者点击对话框以外区域取消。例如: +``` +dialogs.build({ + title: "标题", + positive: "确定", + negative: "取消" +}).on("cancel", (dialog)=>{ + toast("对话框取消了"); +}).show(); +``` + +## 事件: `dismiss` +* `dialog` {Dialog} 对话框 + +对话框消失时会触发的事件。对话框被取消或者手动调用`dialog.dismiss()`函数都会触发该事件。例如: +``` +var d = dialogs.build({ + title: "标题", + positive: "确定", + negative: "取消" +}).on("dismiss", (dialog)=>{ + toast("对话框消失了"); +}).show(); + +setTimeout(()=>{ + d.dismiss(); +}, 5000); +``` + +## 事件: `positive` +* `dialog` {Dialog} 对话框 + +确定按钮按下时触发的事件。例如: +``` +var d = dialogs.build({ + title: "标题", + positive: "确定", + negative: "取消" +}).on("positive", (dialog)=>{ + toast("你点击了确定"); +}).show(); +``` + +## 事件: `negative` +* `dialog` {Dialog} 对话框 + +取消按钮按下时触发的事件。例如: +``` +var d = dialogs.build({ + title: "标题", + positive: "确定", + negative: "取消" +}).on("negative", (dialog)=>{ + toast("你点击了取消"); +}).show(); +``` + +## 事件: `neutral` +* `dialog` {Dialog} 对话框 + +中性按钮按下时触发的事件。例如: +``` +var d = dialogs.build({ + title: "标题", + positive: "确定", + negative: "取消", + neutral: "稍后提示" +}).on("positive", (dialog)=>{ + toast("你点击了稍后提示"); +}).show(); +``` + +## 事件: `any` +* `dialog` {Dialog} 对话框 +* `action` {string} 被点击的按钮,可能的值为: + * `positive` 确定按钮 + * `negative` 取消按钮 + * `neutral` 中性按钮 + +任意按钮按下时触发的事件。例如: +``` +var d = dialogs.build({ + title: "标题", + positive: "确定", + negative: "取消", + neutral: "稍后提示" +}).on("any", (action, dialog)=>{ + if(action == "positive"){ + toast("你点击了确定"); + }else if(action == "negative"){ + toast("你点击了取消"); + } +}).show(); +``` + +## 事件: `item_select` +* `index` {number} 被选中的项目索引,从0开始 +* `item` {Object} 被选中的项目 +* `dialog` {Dialog} 对话框 + +对话框列表(itemsSelectMode为"select")的项目被点击选中时触发的事件。例如: +``` +var d = dialogs.build({ + title: "请选择", + positive: "确定", + negative: "取消", + items: ["A", "B", "C", "D"], + itemsSelectMode: "select" +}).on("item_select", (index, item, dialog)=>{ + toast("您选择的是第" + (index + 1) + "项, 选项为" + item); +}).show(); +``` + +## 事件: `single_choice` +* `index` {number} 被选中的项目索引,从0开始 +* `item` {Object} 被选中的项目 +* `dialog` {Dialog} 对话框 + +对话框单选列表(itemsSelectMode为"singleChoice")的项目被选中并点击确定时触发的事件。例如: +``` +var d = dialogs.build({ + title: "请选择", + positive: "确定", + negative: "取消", + items: ["A", "B", "C", "D"], + itemsSelectMode: "singleChoice" +}).on("item_select", (index, item, dialog)=>{ + toast("您选择的是第" + (index + 1) + "项, 选项为" + item); +}).show(); +``` + +## 事件: `multi_choice` +* `indices` {Array} 被选中的项目的索引的数组 +* `items` {Array} 被选中的项目的数组 +* `dialog` {Dialog} 对话框 + +对话框多选列表(itemsSelectMode为"multiChoice")的项目被选中并点击确定时触发的事件。例如: +``` +var d = dialogs.build({ + title: "请选择", + positive: "确定", + negative: "取消", + items: ["A", "B", "C", "D"], + itemsSelectMode: "multiChoice" +}).on("item_select", (indices, items, dialog)=>{ + toast(util.format("您选择的项目为%o, 选项为%o", indices, items); +}).show(); +``` + +## 事件: `input` +* `text` {string} 输入框的内容 +* `dialog` {Dialog} 对话框 + +带有输入框的对话框当点击确定时会触发的事件。例如: +``` +dialogs.build({ + title: "请输入", + positive: "确定", + negative: "取消", + inputPrefill: "" +}).on("input", (text, dialog)=>{ + toast("你输入的是" + text); +}).show(); +``` + +## 事件: `input_change` +* `text` {string} 输入框的内容 +* `dialog` {Dialog} 对话框 + +对话框的输入框的文本发生变化时会触发的事件。例如: +``` +dialogs.build({ + title: "请输入", + positive: "确定", + negative: "取消", + inputPrefill: "" +}).on("input_change", (text, dialog)=>{ + toast("你输入的是" + text); +}).show(); +``` + +## dialog.getProgress() +* 返回 {number} + +获取当前进度条的进度值,是一个整数 + +## dialog.getMaxProgress() +* 返回 {number} + +获取当前进度条的最大进度值,是一个整数 + +## dialog.getActionButton(action) +* `action` {string} 动作,包括: + * `positive` + * `negative` + * `neutral` diff --git a/AutoJs-Docs/api/documentation.md b/AutoJs-Docs/api/documentation.md new file mode 100644 index 000000000..40264196d --- /dev/null +++ b/AutoJs-Docs/api/documentation.md @@ -0,0 +1,69 @@ +# 关于本文档 + + + +本文档为Auto.js的文档,解释了Auto.js各个模块的API的使用方法、作用和例子。 + +文档借助Node.js的文档构建工具生成,并在github上开源(https://github.com/hyb1996/AutoJs-Docs ),目前由开发者维护。 + +## API稳定性 + +由于Auto.js处于活跃的更新和开发状态,API可能随时有变动,我们用Stability来标记模块、函数的稳定性。这些标记包括: + +```txt +Stability: 0 - Deprecated + +弃用的函数、模块或特性,在未来的更新中将很快会被移除或更改。应该在脚本中移除对这些函数的使用,以免后续出现意料之外的问题。 +``` + +```txt +Stability: 1 - Experimental + +实验性的函数、模块或特性,在未来的更新中可能会更改或移除。应该谨慎使用这些函数或模块,或者仅用作临时或试验用途。 +``` + +```txt +Stability: 2 - Stable + +稳定的函数、模块或特性,在未来的更新中这些模块已有的函数一般不会被更改,会保证后向兼容性。 +``` + +## 如何阅读本文档 + +先看一个例子,下面是[基于控件的操作模拟](coordinates-based-automation.html)的章节中input函数的部分说明。 + +## input([i, ]text) +* `i` {number} 表示要输入的为第i + 1个输入框 +* `text` {string} 要输入的文本 + + +input表示函数名,括号内的`[i, ]text`为函数的参数。下面是参数列表,"number"表示参数i的类型为数值,"string"表示参数text的类型为字符串。 + +例如input(1, "啦啦啦"),执行这个语句会在屏幕上的第2个输入框处输入"啦啦啦"。 + +方括号[ ]表示参数为可选参数。也就是说,可以省略i直接调用input。例如input("嘿嘿嘿"),按照文档,这个语句会在屏幕上所有输入框输入"嘿嘿嘿"。 + +调用有可选参数的函数时请不要写上方括号。 + +我们再看第二个例子。图片和图色处理中detectsColor函数的部分说明。 + +## 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空间的色调值。 + +同样地,`[, threshold = 16, algorithm = "rgb"]`为可选参数,并且,等于号=后面的值为参数的默认值。也就是如果不指定该参数,则该参数将会为这个值。 + +例如 `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 diff --git a/AutoJs-Docs/api/engines.md b/AutoJs-Docs/api/engines.md new file mode 100644 index 000000000..a667da2bd --- /dev/null +++ b/AutoJs-Docs/api/engines.md @@ -0,0 +1,209 @@ +# Engines + +> Stability: 2 - Stable + +engines模块包含了一些与脚本环境、脚本运行、脚本引擎有关的函数,包括运行其他脚本,关闭脚本等。 + +例如,获取脚本所在目录: +``` +toast(engines.myEngine().cwd()); +``` + +## engines.execScript(name, script[, config]) +* `name` {string} 要运行的脚本名称。这个名称和文件名称无关,只是在任务管理中显示的名称。 +* `script` {string} 要运行的脚本内容。 +* `config` {Object} 运行配置项 + * `delay` {number} 延迟执行的毫秒数,默认为0 + * `loopTimes` {number} 循环运行次数,默认为1。0为无限循环。 + * `interval` {number} 循环运行时两次运行之间的时间间隔,默认为0 + * `path` {Array} | {string} 指定脚本运行的目录。这些路径会用于require时寻找模块文件。 + +在新的脚本环境中运行脚本script。返回一个[ScriptExectuion](#engines_scriptexecution)对象。 + +所谓新的脚本环境,指定是,脚本中的变量和原脚本的变量是不共享的,并且,脚本会在新的线程中运行。 + +最简单的例子如下: +``` +engines.execScript("hello world", "toast('hello world')"); +``` + +如果要循环运行,则: +``` +//每隔3秒运行一次脚本,循环10次 +engines.execScript("hello world", "toast('hello world')", { + loopTimes: 10, + interval: 3000 +}); +``` + +用字符串来编写脚本非常不方便,可以结合 `Function.toString()`的方法来执行特定函数: + +``` +function helloWorld(){ + //注意,这里的变量和脚本主体的变量并不共享 + toast("hello world"); +} +engines.execScript("hello world", "helloWorld();\n" + helloWorld.toString()); +``` + +如果要传递变量,则可以把这些封装成一个函数: +``` +function exec(action, args){ + args = args || {}; + engines.execScript(action.name, action + "(" + JSON.stringify(args) + ");\n" + action.toString()); +} + +//要执行的函数,是一个简单的加法 +function add(args){ + toast(args.a + args.b); +} + +//在新的脚本环境中执行 1 + 2 +exec(add, {a: 1, b:2}); +``` + +## engines.execScriptFile(path[, config]) +* `path` {string} 要运行的脚本路径。 +* `config` {Object} 运行配置项 + * `delay` {number} 延迟执行的毫秒数,默认为0 + * `loopTimes` {number} 循环运行次数,默认为1。0为无限循环。 + * `interval` {number} 循环运行时两次运行之间的时间间隔,默认为0 + * `path` {Array} | {string} 指定脚本运行的目录。这些路径会用于require时寻找模块文件。 + +在新的脚本环境中运行脚本文件path。返回一个[ScriptExecution](#ScriptExecution)对象。 + +``` +engines.execScriptFile("/sdcard/脚本/1.js"); +``` + +## engines.execAutoFile(path[, config]) +* `path` {string} 要运行的录制文件路径。 +* `config` {Object} 运行配置项 + * `delay` {number} 延迟执行的毫秒数,默认为0 + * `loopTimes` {number} 循环运行次数,默认为1。0为无限循环。 + * `interval` {number} 循环运行时两次运行之间的时间间隔,默认为0 + * `path` {Array} | {string} 指定脚本运行的目录。这些路径会用于require时寻找模块文件。 + +在新的脚本环境中运行录制文件path。返回一个[ScriptExecution](#ScriptExecution)对象。 + +``` +engines.execAutoFile("/sdcard/脚本/1.auto"); +``` + +## engines.stopAll() + +停止所有正在运行的脚本。包括当前脚本自身。 + +## engines.stopAllAndToast() + +停止所有正在运行的脚本并显示停止的脚本数量。包括当前脚本自身。 + +## engines.myEngine() + +返回当前脚本的脚本引擎对象([ScriptEngine](#engines_scriptengine)) + +**[v4.1.0新增]** +特别的,该对象可以通过`execArgv`来获取他的运行参数,包括外部参数、intent等。例如: +``` +log(engines.myEngine().execArgv); +``` + +普通脚本的运行参数通常为空,通过定时任务的广播启动的则可以获取到启动的intent。 + +## engines.all() +* 返回 {Array} + +返回当前所有正在运行的脚本的脚本引擎[ScriptEngine](#engines_scriptengine)的数组。 + +``` +log(engines.all()); +``` + +# ScriptExecution + +执行脚本时返回的对象,可以通过他获取执行的引擎、配置等,也可以停止这个执行。 + +要停止这个脚本的执行,使用`exectuion.getEngine().forceStop()`. + +## ScriptExecution.getEngine() + +返回执行该脚本的脚本引擎对象([ScriptEngine](#engines_scriptengine)) + +## ScriptExecution.getConfig() + +返回该脚本的运行配置([ScriptConfig](#engines_scriptconfig)) + +# ScriptEngine + +脚本引擎对象。 + +## ScriptEngine.forceStop() + +停止脚本引擎的执行。 + +## ScriptEngine.cwd() +* 返回 {string} + +返回脚本执行的路径。对于一个脚本文件而言为这个脚本所在的文件夹;对于其他脚本,例如字符串脚本,则为`null`或者执行时的设置值。 + +## ScriptEngine.getSource() +* 返回 [ScriptSource](#engines_scriptsource) + +返回当前脚本引擎正在执行的脚本对象。 + +``` +log(engines.myEngine().getSource()); +``` + +## ScriptEngine.emit(eventName[, ...args]) +* `eventName` {string} 事件名称 +* `...args` {any} 事件参数 + +向该脚本引擎发送一个事件,该事件可以在该脚本引擎对应的脚本的events模块监听到并在脚本主线程执行事件处理。 + +例如脚本receiver.js的内容如下: + +``` +//监听say事件 +events.on("say", function(words){ + toastLog(words); +}); +//保持脚本运行 +setInterval(()=>{}, 1000); +``` + +同一目录另一脚本可以启动他并发送该事件: +``` +//运行脚本 +var e = engines.execScriptFile("./receiver.js"); +//等待脚本启动 +sleep(2000); +//向该脚本发送事件 +e.getEngine().emit("say", "你好"); +``` + +# ScriptConfig +脚本执行时的配置。 + +## delay +* {number} + +延迟执行的毫秒数 + +## interval +* {number} + +循环运行时两次运行之间的时间间隔 + +## loopTimes +* {number} + +循环运行次数 + +## getPath() +* 返回 {Array} + +返回一个字符串数组表示脚本运行时模块寻找的路径。 + + + diff --git a/AutoJs-Docs/api/events.md b/AutoJs-Docs/api/events.md new file mode 100644 index 000000000..51e521e75 --- /dev/null +++ b/AutoJs-Docs/api/events.md @@ -0,0 +1,594 @@ +# Events + +> Stability: 2 - Stable + +events模块提供了监听手机通知、按键、触摸的接口。您可以用他配合自动操作函数完成自动化工作。 + +events本身是一个[EventEmiiter](#events_eventemitter), 但内置了一些事件、包括按键事件、通知事件、Toast事件等。 + +需要注意的是,事件的处理是单线程的,并且仍然在原线程执行,如果脚本主体或者其他事件处理中有耗时操作、轮询等,则事件将无法得到及时处理(会进入事件队列等待脚本主体或其他事件处理完成才执行)。例如: +``` +auto(); +events.observeNotification(); +events.on('toast', function(t){ + //这段代码将得不到执行 + log(t); +}); +while(true){ + //死循环 +} +``` + +## events.emitter() + +返回一个新的[EventEmitter](#events_eventemitter)。这个EventEmitter没有内置任何事件。 + +## events.observeKey() + +启用按键监听,例如音量键、Home键。按键监听使用无障碍服务实现,如果无障碍服务未启用会抛出异常并提示开启。 + +只有这个函数成功执行后, `onKeyDown`, `onKeyUp`等按键事件的监听才有效。 + +该函数在安卓4.3以上才能使用。 + +## events.onKeyDown(keyName, listener) +* `keyName` {string} 要监听的按键名称 +* `listener` {Function} 按键监听器。参数为一个[KeyEvent](#events_keyevent)。 + +注册一个按键监听函数,当有keyName对应的按键被按下会调用该函数。可用的按键名称参见[Keys](#events_keys)。 + +例如: +``` +//启用按键监听 +events.observeKey(); +//监听音量上键按下 +events.onKeyDown("volume_up", function(event){ + toast("音量上键被按下了"); +}); +//监听菜单键按下 +events.onKeyDown("menu", function(event){ + toast("菜单键被按下了"); + exit(); +}); +``` + +## events.onKeyUp(keyName, listener) +* `keyName` {string} 要监听的按键名称 +* `listener` {Function} 按键监听器。参数为一个[KeyEvent](#events_keyevent)。 + +注册一个按键监听函数,当有keyName对应的按键弹起会调用该函数。可用的按键名称参见[Keys](#events_keys)。 + +一次完整的按键动作包括了按键按下和弹起。按下事件会在手指按下一个按键的"瞬间"触发, 弹起事件则在手指放开这个按键时触发。 + +例如: +``` +//启用按键监听 +events.observeKey(); +//监听音量下键弹起 +events.onKeyDown("volume_down", function(event){ + toast("音量上键弹起"); +}); +//监听Home键弹起 +events.onKeyDown("home", function(event){ + toast("Home键弹起"); + exit(); +}); +``` + +## events.onceKeyDown(keyName, listener) +* `keyName` {string} 要监听的按键名称 +* `listener` {Function} 按键监听器。参数为一个[KeyEvent](#events_keyevent) + +注册一个按键监听函数,当有keyName对应的按键被按下时会调用该函数,之后会注销该按键监听器。 + +也就是listener只有在onceKeyDown调用后的第一次按键事件被调用一次。 + +## events.onceKeyUp(keyName, listener) +* `keyName` {string} 要监听的按键名称 +* `listener` {Function} 按键监听器。参数为一个[KeyEvent](#events_keyevent) + +注册一个按键监听函数,当有keyName对应的按键弹起时会调用该函数,之后会注销该按键监听器。 + +也就是listener只有在onceKeyUp调用后的第一次按键事件被调用一次。 + +## events.removeAllKeyDownListeners(keyName) +* `keyName` {string} 按键名称 + +删除该按键的KeyDown(按下)事件的所有监听。 + +## events.removeAllKeyUpListeners(keyName) +* `keyName` {string} 按键名称 + +删除该按键的KeyUp(弹起)事件的所有监听。 + +## events.setKeyInterceptionEnabled([key, ]enabled) +* `enabled` {boolean} +* `key` {string} 要屏蔽的按键 + +设置按键屏蔽是否启用。所谓按键屏蔽指的是,屏蔽原有按键的功能,例如使得音量键不再能调节音量,但此时仍然能通过按键事件监听按键。 + +如果不加参数key则会屏蔽所有按键。 + +例如,调用`events.setKeyInterceptionEnabled(true)`会使系统的音量、Home、返回等键不再具有调节音量、回到主页、返回的作用,但此时仍然能通过按键事件监听按键。 + +该函数通常于按键监听结合,例如想监听音量键并使音量键按下时不弹出音量调节框则为: +``` +events.setKeyInterceptionEnabled("volume_up", true); +events.observeKey(); +events.onKeyDown("volume_up", ()=>{ + log("音量上键被按下"); +}); +``` + +只要有一个脚本屏蔽了某个按键,该按键便会被屏蔽;当脚本退出时,会自动解除所有按键屏蔽。 + +## events.observeTouch() + +启用屏幕触摸监听。(需要root权限) + +只有这个函数被成功执行后, 触摸事件的监听才有效。 + +没有root权限调用该函数则什么也不会发生。 + +## events.setTouchEventTimeout(timeout) +* `timeout` {number} 两个触摸事件的最小间隔。单位毫秒。默认为10毫秒。如果number小于0,视为0处理。 + +设置两个触摸事件分发的最小时间间隔。 + +例如间隔为10毫秒的话,前一个触摸事件发生并被注册的监听器处理后,至少要过10毫秒才能分发和处理下一个触摸事件,这10毫秒之间的触摸将会被忽略。 + +建议在满足需要的情况下尽量提高这个间隔。一个简单滑动动作可能会连续触发上百个触摸事件,如果timeout设置过低可能造成事件拥堵。强烈建议不要设置timeout为0。 + +## events.getTouchEventTimeout() + +返回触摸事件的最小时间间隔。 + +## events.onTouch(listener) +* `listener` {Function} 参数为[Point](images.html#images_point)的函数 + +注册一个触摸监听函数。相当于`on("touch", listener)`。 + +例如: +``` +//启用触摸监听 +events.observeTouch(); +//注册触摸监听器 +events.onTouch(function(p){ + //触摸事件发生时, 打印出触摸的点的坐标 + log(p.x + ", " + p.y); +}); +``` + +## events.removeAllTouchListeners() + +删除所有事件监听函数。 + +## 事件: 'key' +* `keyCode` {number} 键值 +* `event` {KeyEvent} 事件 + +当有按键被按下或弹起时会触发该事件。 +例如: +``` +auto(); +events.observeKey(); +events.on("key", function(keyCode, event){ + //处理按键事件 +}); +``` +其中监听器的参数KeyCode包括: +* `keys.home` 主页键 +* `keys.back` 返回键 +* `keys.menu` 菜单键 +* `keys.volume_up` 音量上键 +* `keys.volume_down` 音量下键 + +例如: +``` +auto(); +events.observeKey(); +events.on("key", function(keyCode, event){ + if(keyCode == keys.menu && event.getAction() == event.ACTION_UP){ + toast("菜单键按下"); + } +}); +``` + + +## 事件: 'key_down' +* `keyCode` {number} 键值 +* `event` {KeyEvent} 事件 + +当有按键被按下时会触发该事件。 +``` +auto(); +events.observeKey(); +events.on("key_down", function(keyCode, event){ + //处理按键按下事件 +}); +``` + +## 事件: 'key_up' +* `keyCode` {number} 键值 +* `event` {KeyEvent} 事件 + +当有按键弹起时会触发该事件。 +``` +auto(); +events.observeKey(); +events.on("key_up", function(keyCode, event){ + //处理按键弹起事件 +}); +``` +## 事件: 'exit` + +当脚本正常或者异常退出时会触发该事件。事件处理中如果有异常抛出,则立即中止exit事件的处理(即使exit事件有多个处理函数)并在控制台和日志中打印该异常。 + +一个脚本停止运行时,会关闭该脚本的所有悬浮窗,触发exit事件,之后再回收资源。如果exit事件的处理中有死循环,则后续资源无法得到及时回收。 +此时脚本会停留在任务列表,如果在任务列表中关闭,则会强制结束exit事件的处理并回收后续资源。 + +``` +log("开始运行") +events.on("exit", function(){ + log("结束运行"); +}); +log("即将结束运行"); +``` + +## events.observeNotification() +开启通知监听。例如QQ消息、微信消息、推送等通知。 + +通知监听依赖于通知服务,如果通知服务没有运行,会抛出异常并跳转到通知权限开启界面。(有时即使通知权限已经开启通知服务也没有运行,这时需要关闭权限再重新开启一次) + +例如: +``` +events.obverseNotification(); +events.onNotification(function(notification){ + log(notification.getText()); +}); +``` + +## events.observeToast() +开启Toast监听。 + +Toast监听依赖于无障碍服务,因此此函数会确保无障碍服务运行。 + +## 事件: 'toast' +* `toast` {Object} + * `getText()` 获取Toast的文本内容 + * `getPackageName()` 获取发出Toast的应用包名 + +当有应用发出toast(气泡消息)时会触发该事件。但Auto.js软件本身的toast除外。 + +例如,要记录发出所有toast的应用: +``` +events.observeToast(); +events.onToast(function(toast){ + log("Toast内容: " + toast.getText() + " 包名: " + toast.getPackageName()); +}); +``` + +## 事件: 'notification' +* `notification` [Notification](#events_notification) 通知对象 + +当有应用发出通知时会触发该事件,参数为[Notification](#events_notification)。 + +例如: +``` +events.observeNotification(); +events.on("notification", function(n){ + log("收到新通知:\n 标题: %s, 内容: %s, \n包名: %s", n.getTitle(), n.getText(), n.getPackageName()); +}); +``` + +# Notification + +通知对象,可以获取通知详情,包括通知标题、内容、发出通知的包名、时间等,也可以对通知进行操作,比如点击、删除。 + +## Notification.number +* {number} + +通知数量。例如QQ连续收到两条消息时number为2。 + +## Notification.when +* {number} + +通知发出时间的时间戳,可以用于构造`Date`对象。例如: +``` +events.observeNotification(); +events.on("notification", function(n){ + log("通知时间为}" + new Date(n.when)); +}); +``` + +## Notification.getPackageName() +* 返回 {string} + +获取发出通知的应用包名。 + +## Notification.getTitle() +* 返回 {string} + +获取通知的标题。 + +## Notification.getText() +* 返回 {string} + +获取通知的内容。 + +## Notification.click() + +点击该通知。例如对于一条QQ消息,点击会进入具体的聊天界面。 + +## Notification.delete() + +删除该通知。该通知将从通知栏中消失。 + + +# KeyEvent + +> Stability: 2 - Stable + +## KeyEvent.getAction() + +返回事件的动作。包括: +* `KeyEvent.ACTION_DOWN` 按下事件 +* `KeyEvent.ACTION_UP` 弹起事件 + +## KeyEvent.getKeyCode() + +返回按键的键值。包括: +* `KeyEvent.KEYCODE_HOME` 主页键 +* `KeyEvent.KEYCODE_BACK` 返回键 +* `KeyEvent.KEYCODE_MENU` 菜单键 +* `KeyEvent.KEYCODE_VOLUME_UP` 音量上键 +* `KeyEvent.KEYCODE_VOLUME_DOWN` 音量下键 + +## KeyEvent.getEventTime() +* 返回 {number} + +返回事件发生的时间戳。 + +## KeyEvent.getDownTime() + +返回最近一次按下事件的时间戳。如果本身是按下事件,则与`getEventTime()`相同。 + +## KeyEvent.keyCodeToString(keyCode) + +把键值转换为字符串。例如KEYCODE_HOME转换为"KEYCODE_HOME"。 + +# keys + +> Stability: 2 - Stable + +按键事件中所有可用的按键名称为: +* `volume_up` 音量上键 +* `volume_down` 音量下键 +* `home` 主屏幕键 +* `back` 返回键 +* `menu` 菜单键 + + +# EventEmitter + +> Stability: 2 - Stable + +## EventEmitter.defaultMaxListeners + +每个事件默认可以注册最多 10 个监听器。 单个 EventEmitter 实例的限制可以使用 emitter.setMaxListeners(n) 方法改变。 所有 EventEmitter 实例的默认值可以使用 EventEmitter.defaultMaxListeners 属性改变。 + +设置 EventEmitter.defaultMaxListeners 要谨慎,因为会影响所有 EventEmitter 实例,包括之前创建的。 因而,调用 emitter.setMaxListeners(n) 优先于 EventEmitter.defaultMaxListeners。 + +注意,与Node.js不同,**这是一个硬性限制**。 EventEmitter 实例不允许添加更多的监听器,监听器超过最大数量时会抛出TooManyListenersException。 +``` +emitter.setMaxListeners(emitter.getMaxListeners() + 1); +emitter.once('event', () => { + // 做些操作 + emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0)); +}); +``` +## EventEmitter.addListener(eventName, listener) +* `eventName` {any} +* `listener` {Function} + +emitter.on(eventName, listener) 的别名。 + +## EventEmitter.emit(eventName[, ...args]) +* `eventName` {any} +* `args` {any} + +按监听器的注册顺序,同步地调用每个注册到名为 eventName 事件的监听器,并传入提供的参数。 + +如果事件有监听器,则返回 true ,否则返回 false。 + +## EventEmitter.eventNames() + +返回一个列出触发器已注册监听器的事件的数组。 数组中的值为字符串或符号。 +``` +const myEE = events.emitter(); +myEE.on('foo', () => {}); +myEE.on('bar', () => {}); + +const sym = Symbol('symbol'); +myEE.on(sym, () => {}); + +console.log(myEE.eventNames()); +// 打印: [ 'foo', 'bar', Symbol(symbol) ] +``` +## EventEmitter.getMaxListeners() + +返回 EventEmitter 当前的最大监听器限制值,该值可以通过 emitter.setMaxListeners(n) 设置或默认为 EventEmitter.defaultMaxListeners。 + +## EventEmitter.listenerCount(eventName) +* `eventName` {string} 正在被监听的事件名 + +返回正在监听名为 eventName 的事件的监听器的数量。 + +## EventEmitter.listeners(eventName) +* `eventName` {string} + +返回名为 eventName 的事件的监听器数组的副本。 +``` +server.on('connection', (stream) => { + console.log('someone connected!'); +}); +console.log(util.inspect(server.listeners('connection'))); +// 打印: [ [Function] ] +``` + +## EventEmitter.on(eventName, listener) +* `eventName` {any} 事件名 +* `listener` {Function} 回调函数 + +添加 listener 函数到名为 eventName 的事件的监听器数组的末尾。 不会检查 listener 是否已被添加。 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。 +``` +server.on('connection', (stream) => { + console.log('有连接!'); +}); +``` +返回一个 EventEmitter 引用,可以链式调用。 + +默认情况下,事件监听器会按照添加的顺序依次调用。 emitter.prependListener() 方法可用于将事件监听器添加到监听器数组的开头。 +``` +const myEE = events.emitter(); +myEE.on('foo', () => console.log('a')); +myEE.prependListener('foo', () => console.log('b')); +myEE.emit('foo'); +// 打印: +// b +// a +``` + + +## EventEmitter.once(eventName, listener)# +* `eventName` {any} 事件名 +* `listener` {Function} 回调函数 + +添加一个单次 listener 函数到名为 eventName 的事件。 下次触发 eventName 事件时,监听器会被移除,然后调用。 +``` +server.once('connection', (stream) => { + console.log('首次调用!'); +}); +``` +返回一个 EventEmitter 引用,可以链式调用。 + +默认情况下,事件监听器会按照添加的顺序依次调用。 emitter.prependOnceListener() 方法可用于将事件监听器添加到监听器数组的开头。 +``` +const myEE = events.emitter(); +myEE.once('foo', () => console.log('a')); +myEE.prependOnceListener('foo', () => console.log('b')); +myEE.emit('foo'); +// 打印: +// b +// a +``` + +## EventEmitter.prependListener(eventName, listener) +* `eventName` {any} 事件名 +* `listener` {Function} 回调函数 + +添加 listener 函数到名为 eventName 的事件的监听器数组的开头。 不会检查 listener 是否已被添加。 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。 +``` +server.prependListener('connection', (stream) => { + console.log('有连接!'); +}); +``` +返回一个 EventEmitter 引用,可以链式调用。 + +## EventEmitter.prependOnceListener(eventName, listener) +* `eventName` {any} 事件名 +* `listener` {Function} 回调函数 + +添加一个单次 listener 函数到名为 eventName 的事件的监听器数组的开头。 下次触发 eventName 事件时,监听器会被移除,然后调用。 +``` +server.prependOnceListener('connection', (stream) => { + console.log('首次调用!'); +}); +``` +返回一个 EventEmitter 引用,可以链式调用。 + +## EventEmitter.removeAllListeners(\[eventName\]) +* `eventName` {any} + +移除全部或指定 eventName 的监听器。 + +注意,在代码中移除其他地方添加的监听器是一个不好的做法,尤其是当 EventEmitter 实例是其他组件或模块创建的。 + +返回一个 EventEmitter 引用,可以链式调用。 + +## EventEmitter.removeListener(eventName, listener) +* `eventName` {any} +* `listener` {Function} + +从名为 eventName 的事件的监听器数组中移除指定的 listener。 +``` +const callback = (stream) => { + console.log('有连接!'); +}; +server.on('connection', callback); +// ... +server.removeListener('connection', callback); +``` +removeListener 最多只会从监听器数组里移除一个监听器实例。 如果任何单一的监听器被多次添加到指定 eventName 的监听器数组中,则必须多次调用 removeListener 才能移除每个实例。 + + +注意,一旦一个事件被触发,所有绑定到它的监听器都会按顺序依次触发。 这意味着,在事件触发后、最后一个监听器完成执行前,任何 removeListener() 或 removeAllListeners() 调用都不会从 emit() 中移除它们。 随后的事件会像预期的那样发生。 +``` +const myEmitter = events.emitter(); + +const callbackA = () => { + console.log('A'); + myEmitter.removeListener('event', callbackB); +}; + +const callbackB = () => { + console.log('B'); +}; + +myEmitter.on('event', callbackA); + +myEmitter.on('event', callbackB); + +// callbackA 移除了监听器 callbackB,但它依然会被调用。 +// 触发是内部的监听器数组为 [callbackA, callbackB] +myEmitter.emit('event'); +// 打印: +// A +// B + +// callbackB 被移除了。 +// 内部监听器数组为 [callbackA] +myEmitter.emit('event'); +// 打印: +// A +``` +因为监听器是使用内部数组进行管理的,所以调用它会改变在监听器被移除后注册的任何监听器的位置索引。 虽然这不会影响监听器的调用顺序,但意味着由 emitter.listeners() 方法返回的监听器数组副本需要被重新创建。 + +返回一个 EventEmitter 引用,可以链式调用。 + +## EventEmitter.setMaxListeners(n) +* `n` {number} + +默认情况下,如果为特定事件添加了超过 10 个监听器,则 EventEmitter 会打印一个警告。 此限制有助于寻找内存泄露。 但是,并不是所有的事件都要被限为 10 个。 emitter.setMaxListeners() 方法允许修改指定的 EventEmitter 实例的限制。 值设为 Infinity(或 0)表明不限制监听器的数量。 + +返回一个 EventEmitter 引用,可以链式调用。 + +# events.broadcast: 脚本间广播 + +脚本间通信除了使用engines模块提供的`ScriptEngine.emit()`方法以外,也可以使用events模块提供的broadcast广播。 + +events.broadcast本身是一个EventEmitter,但它的事件是在脚本间共享的,所有脚本都能发送和监听这些事件;事件处理会在脚本主线程执行(后续可能加入函数`onThisThread(eventName, ...args)`来提供在其他线程执行的能力)。 + +例如在一个脚本发送一个广播hello: +``` +events.broadcast.emit("hello", "小明"); +``` + +在其他脚本中监听并处理: +``` +events.broadcast.on("hello", function(name){ + toast("你好, " + name); +}); +//保持脚本运行 +setInterval(()=>{}, 1000); +``` diff --git a/AutoJs-Docs/api/files.md b/AutoJs-Docs/api/files.md new file mode 100644 index 000000000..864a453a4 --- /dev/null +++ b/AutoJs-Docs/api/files.md @@ -0,0 +1,317 @@ +# Files + +> Stability: 2 - Stable + +files模块提供了一些常见的文件处理,包括文件读写、移动、复制、删掉等。 + +一次性的文件读写可以直接使用`files.read()`, `files.write()`, `files.append()`等方便的函数,但如果需要频繁读写或随机读写,则使用`open()`函数打开一个文件对象来操作文件,并在操作完毕后调用`close()`函数关闭文件。 + +## files.isFile(path) +* `path` {string} 路径 +* 返回 {boolean} + +返回路径path是否是文件。 + +``` +log(files.isDir("/sdcard/文件夹/")); //返回false +log(files.isDir("/sdcard/文件.txt")); //返回true +``` + +## files.isDir(path) +* `path` {string} 路径 +* 返回 {boolean} + +返回路径path是否是文件夹。 + +``` +log(files.isDir("/sdcard/文件夹/")); //返回true +log(files.isDir("/sdcard/文件.txt")); //返回false +``` + +## files.isEmptyDir(path) +* `path` {string} 路径 +* 返回 {boolean} + +返回文件夹path是否为空文件夹。如果该路径并非文件夹,则直接返回`false`。 + +## files.join(parent, child) +* `parent` {string} 父目录路径 +* `child` {string} 子路径 +* 返回 {string} + +连接两个路径并返回,例如`files.join("/sdcard/", "1.txt")`返回"/sdcard/1.txt"。 + +## files.create(path) +* `path` {string} 路径 +* 返回 {boolean} + +创建一个文件或文件夹并返回是否创建成功。如果文件已经存在,则直接返回`false`。 + +``` +files.create("/sdcard/新文件夹/"); +``` + +## files.createWithDirs(path) +* `path` {string} 路径 +* 返回 {boolean} + +创建一个文件或文件夹并返回是否创建成功。如果文件所在文件夹不存在,则先创建他所在的一系列文件夹。如果文件已经存在,则直接返回`false`。 + +``` +files.createWithDirs("/sdcard/新文件夹/新文件夹/新文件夹/1.txt"); +``` + +## files.exists(path) +* `path` {string} 路径 +* 返回 {boolean} + +返回在路径path处的文件是否存在。 + +## files.ensureDir(path) +* `path` {string} 路径 + +确保路径path所在的文件夹存在。如果该路径所在文件夹不存在,则创建该文件夹。 + +例如对于路径"/sdcard/Download/ABC/1.txt",如果/Download/文件夹不存在,则会先创建Download,再创建ABC文件夹。 + +## files.read(path[, encoding = "utf-8"]) +* `path` {string} 路径 +* `encoding` {string} 字符编码,可选,默认为utf-8 +* 返回 {string} + +读取文本文件path的所有内容并返回。如果文件不存在,则抛出`FileNotFoundException`。 + +``` +log(files.read("/sdcard/1.txt")); +``` + +## files.readBytes(path) +* `path` {string} 路径 +* 返回 {byte[]} + +读取文件path的所有内容并返回一个字节数组。如果文件不存在,则抛出`FileNotFoundException`。 + +注意,该数组是Java的数组,不具有JavaScript数组的forEach, slice等函数。 + +一个以16进制形式打印文件的例子如下: +``` +var data = files.readBytes("/sdcard/1.png"); +var sb = new java.lang.StringBuilder(); +for(var i = 0; i < data.length; i++){ + sb.append(data[i].toString(16)); +} +log(sb.toString()); +``` + +## files.write(path, text[, encoding = "utf-8"]) +* `path` {string} 路径 +* `text` {string} 要写入的文本内容 +* `encoding` {string} 字符编码 + +把text写入到文件path中。如果文件存在则覆盖,不存在则创建。 + +``` +var text = "文件内容"; +//写入文件 +files.write("/sdcard/1.txt", text); +//用其他应用查看文件 +app.viewFile("/sdcard/1.txt"); +``` + +## files.writeBytes(path, bytes) +* `path` {string} 路径 +* `bytes` {byte[]} 字节数组,要写入的二进制数据 + +把bytes写入到文件path中。如果文件存在则覆盖,不存在则创建。 + +## files.append(path, text[, encoding = 'utf-8']) +* `path` {string} 路径 +* `text` {string} 要写入的文本内容 +* `encoding` {string} 字符编码 + +把text追加到文件path的末尾。如果文件不存在则创建。 +``` +var text = "追加的文件内容"; +files.append("/sdcard/1.txt", text); +files.append("/sdcard/1.txt", text); +//用其他应用查看文件 +app.viewFile("/sdcard/1.txt"); +``` + +## files.appendBytes(path, text[, encoding = 'utf-8']) +* `path` {string} 路径 +* `bytes` {byte[]} 字节数组,要写入的二进制数据 + +把bytes追加到文件path的末尾。如果文件不存在则创建。 + +## files.copy(fromPath, toPath) +* `fromPath` {string} 要复制的原文件路径 +* `toPath` {string} 复制到的文件路径 +* 返回 {boolean} + +复制文件,返回是否复制成功。例如`files.copy("/sdcard/1.txt", "/sdcard/Download/1.txt")`。 + +## files.move(fromPath, toPath) +* `fromPath` {string} 要移动的原文件路径 +* `toPath` {string} 移动到的文件路径 +* 返回 {boolean} + +移动文件,返回是否移动成功。例如`files.move("/sdcard/1.txt", "/sdcard/Download/1.txt")`会把1.txt文件从sd卡根目录移动到Download文件夹。 + +## files.rename(path, newName) +* `path` {string} 要重命名的原文件路径 +* `newName` {string} 要重命名的新文件名 +* 返回 {boolean} + +重命名文件,并返回是否重命名成功。例如`files.rename("/sdcard/1.txt", "2.txt")`。 + +## files.renameWithoutExtension(path, newName) +* `path` {string} 要重命名的原文件路径 +* `newName` {string} 要重命名的新文件名 +* 返回 {boolean} + +重命名文件,不包含拓展名,并返回是否重命名成功。例如`files.rename("/sdcard/1.txt", "2")`会把"1.txt"重命名为"2.txt"。 + +## files.getName(path) +* `path` {string} 路径 +* 返回 {string} + +返回文件的文件名。例如`files.getName("/sdcard/1.txt")`返回"1.txt"。 + +## files.getNameWithoutExtension(path) +* `path` {string} 路径 +* 返回 {string} + +返回不含拓展名的文件的文件名。例如`files.getName("/sdcard/1.txt")`返回"1"。 + +## files.getExtension(path) +* `path` {string} 路径 +* 返回 {string} + +返回文件的拓展名。例如`files.getExtension("/sdcard/1.txt")`返回"txt"。 + +## files.remove(path) +* `path` {string} 路径 +* 返回 {boolean} + +删除文件或**空文件夹**,返回是否删除成功。 + +## files.removeDir(path) +* `path` {string} 路径 +* `path` {string} 路径 +* 返回 {boolean} + +删除文件夹,如果文件夹不为空,则删除该文件夹的所有内容再删除该文件夹,返回是否全部删除成功。 + +## files.getSdcardPath() +* 返回 {string} + +返回SD卡路径。所谓SD卡,即外部存储器。 + +## files.cwd() +* 返回 {string} + +返回脚本的"当前工作文件夹路径"。该路径指的是,如果脚本本身为脚本文件,则返回这个脚本文件所在目录;否则返回`null`获取其他设定路径。 + +例如,对于脚本文件"/sdcard/脚本/1.js"运行`files.cwd()`返回"/sdcard/脚本/"。 + +## files.path(relativePath) +* `relativePath` {string} 相对路径 +* 返回 {string} + +返回相对路径对应的绝对路径。例如`files.path("./1.png")`,如果运行这个语句的脚本位于文件夹"/sdcard/脚本/"中,则返回`"/sdcard/脚本/1.png"`。 + +## files.listDir(path[, filter]) +* `path` {string} 路径 +* `filter` {Function} 过滤函数,可选。接收一个`string`参数(文件名),返回一个`boolean`值。 + +列出文件夹path下的满足条件的文件和文件夹的名称的数组。如果不加filter参数,则返回所有文件和文件夹。 + +列出sdcard目录下所有文件和文件夹为: +``` +var arr = files.listDir("/sdcard/"); +log(arr); +``` + +列出脚本目录下所有js脚本文件为: +``` +var dir = "/sdcard/脚本/"; +var jsFiles = files.listDir(dir, function(name){ + return name.endsWith(".js") && files.isFile(files.join(dir, name)); +}); +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} 文件读写的缓冲区大小。 + +打开一个文件。根据打开模式返回不同的文件对象。包括: +* "r": 返回一个ReadableTextFile对象。 +* "w", "a": 返回一个WritableTextFile对象。 + +对于"w"模式,如果文件并不存在,则会创建一个,已存在则会清空该文件内容;其他模式文件不存在会抛出FileNotFoundException。 + +# ReadableTextFile + +可读文件对象。 + +## ReadableTextFile.read() + +返回该文件剩余的所有内容的字符串。 + +## ReadableTextFile.read(maxCount) + +* `maxCount` {Number} 最大读取的字符数量 + +读取该文件接下来最长为maxCount的字符串并返回。即使文件剩余内容不足maxCount也不会出错。 + +## ReadableTextFile.readline() + +读取一行并返回(不包含换行符)。 + +## ReadableTextFile.readlines() + +读取剩余的所有行,并返回它们按顺序组成的字符串数组。 + +## close() + +关闭该文件。 + +**打开一个文件不再使用时务必关闭** + +# PWritableTextFile + +可写文件对象。 + +## PWritableTextFile.write(text) +* `text` {string} 文本 + +把文本内容text写入到文件中。 + +## PWritableTextFile.writeline(line) +* `text` {string} 文本 + +把文本line写入到文件中并写入一个换行符。 + +## PWritableTextFile.writelines(lines) +* `lines` {Array} 字符串数组 + +把很多行写入到文件中.... + +## PWritableTextFile.flush() + +把缓冲区内容输出到文件中。 + +## PWritableTextFile.close() + +关闭文件。同时会被缓冲区内容输出到文件。 + +**打开一个文件写入后,不再使用时务必关闭,否则文件可能会丢失** diff --git a/AutoJs-Docs/api/floaty.md b/AutoJs-Docs/api/floaty.md new file mode 100644 index 000000000..a8564eb50 --- /dev/null +++ b/AutoJs-Docs/api/floaty.md @@ -0,0 +1,196 @@ +# Floaty + +floaty模块提供了悬浮窗的相关函数,可以在屏幕上显示自定义悬浮窗,控制悬浮窗大小、位置等。 + +悬浮窗在脚本停止运行时会自动关闭,因此,要保持悬浮窗不被关闭,可以用一个空的setInterval来实现,例如: +``` +setInterval(()=>{}, 1000); +``` + +## floaty.window(layout) +* `layout` {xml} | {View} 悬浮窗界面的XML或者View + +指定悬浮窗的布局,创建并**显示**一个悬浮窗,返回一个`FloatyWindow`对象。 + +该悬浮窗自带关闭、调整大小、调整位置按键,可根据需要调用`setAdjustEnabled()`函数来显示或隐藏。 + +其中layout参数可以是xml布局或者一个View,更多信息参见ui模块的说明。 + +例子: +``` +var w = floaty.window( + + 悬浮文字 + +); +setTimeout(()=>{ + w.close(); +}, 2000); +``` +这段代码运行后将会在屏幕上显示悬浮文字,并在两秒后消失。 + +另外,因为脚本运行的线程不是UI线程,而所有对控件的修改操作需要在UI线程执行,此时需要用`ui.run`,例如: +``` +ui.run(function(){ + w.text.setText("文本"); +}); +``` + +有关返回的`FloatyWindow`对象的说明,参见下面的`FloatyWindow`章节。 + +## floaty.rawWindow(layout) +* `layout` {xml} | {View} 悬浮窗界面的XML或者View + +指定悬浮窗的布局,创建并**显示**一个原始悬浮窗,返回一个`FloatyRawWindow`对象。 + +与`floaty.window()`函数不同的是,该悬浮窗不会增加任何额外设施(例如调整大小、位置按钮),您可以根据自己需要编写任何布局。 + +而且,该悬浮窗支持完全全屏,可以覆盖状态栏,因此可以做护眼模式之类的应用。 + +``` +var w = floaty.rawWindow( + + 悬浮文字 + +); + +w.setPosition(500, 500); + +setTimeout(()=>{ + w.close(); +}, 2000); +``` +这段代码运行后将会在屏幕上显示悬浮文字,并在两秒后消失。 + +有关返回的`FloatyRawWindow`对象的说明,参见下面的`FloatyRawWindow`章节。 + +## floaty.closeAll() + +关闭所有本脚本的悬浮窗。 + +# FloatyWindow + +悬浮窗对象,可通过`FloatyWindow.{id}`获取悬浮窗界面上的元素。例如, 悬浮窗window上一个控件的id为aaa, 那么`window.aaa`即可获取到该控件,类似于ui。 + +## window.setAdjustEnabled(enabled) +* `enabled` {boolean} 是否启用悬浮窗调整(大小、位置) + +如果enabled为true,则在悬浮窗左上角、右上角显示可供位置、大小调整的标示,就像控制台一样; +如果enabled为false,则隐藏上述标示。 + +## window.setPosition(x, y) +* `x` {number} x +* `x` {number} y + +设置悬浮窗位置。 + +## window.getX() + +返回悬浮窗位置的X坐标。 + +## window.getY() + +返回悬浮窗位置的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.setTouchable(touchable) +* `touchable` {Boolean} 是否可触摸 + +设置悬浮窗是否可触摸,如果为true, 则悬浮窗将接收到触摸、点击等事件并且无法继续传递到悬浮窗下面;如果为false, 悬浮窗上的触摸、点击等事件将被直接传递到悬浮窗下面。处于安全考虑,被悬浮窗接收的触摸事情无法再继续传递到下层。 + +可以用此特性来制作护眼模式脚本。 +``` +var w = floaty.rawWindow( + +); + +w.setSize(-1, -1); +w.setTouchable(false); + +setTimeout(()=>{ + w.close(); +}, 4000); + +``` + +## window.setPosition(x, y) +* `x` {number} x +* `x` {number} y + +设置悬浮窗位置。 + +## window.getX() + +返回悬浮窗位置的X坐标。 + +## window.getY() + +返回悬浮窗位置的Y坐标。 + +## window.setSize(width, height) +* `width` {number} 宽度 +* `height` {number} 高度 + +设置悬浮窗宽高。 + +特别地,如果设置为-1,则为占满全屏;设置为-2则为根据悬浮窗内容大小而定。例如: +``` +var w = floaty.rawWindow( + + 悬浮文字 + +); + +w.setSize(-1, -1); + +setTimeout(()=>{ + w.close(); +}, 2000); + +``` + +## window.getWidht() + +返回悬浮窗宽度。 + +## window.getHeight() + +返回悬浮窗高度。 + +## window.close() + +关闭悬浮窗。如果悬浮窗已经是关闭状态,则此函数将不执行任何操作。 + +被关闭后的悬浮窗不能再显示。 + +## window.exitOnClose() + +使悬浮窗被关闭时自动结束脚本运行。 diff --git a/AutoJs-Docs/api/globals.md b/AutoJs-Docs/api/globals.md new file mode 100644 index 000000000..f3628955e --- /dev/null +++ b/AutoJs-Docs/api/globals.md @@ -0,0 +1,198 @@ +# 全局变量与函数 + +全局变量和函数在所有模块中均可使用。 但以下变量的作用域只在模块内,详见 [module](modules.html): +* 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/AutoJs-Docs/api/http.md b/AutoJs-Docs/api/http.md new file mode 100644 index 000000000..8660beb91 --- /dev/null +++ b/AutoJs-Docs/api/http.md @@ -0,0 +1,240 @@ +# HTTP + +> Stability: 2 - Stable + +http模块提供一些进行http请求的函数。 + +## http.get(url[, options, callback]) + +* `url` {string} 请求的URL地址,需要以"http://"或"https://"开头。如果url没有以"http://"开头,则默认为"http://"。 +* `options` {Object} 请求选项。参见[http.request()][]。 +* `callback` {Function} 回调函数,可选,其参数是一个[Response][]对象。如果不加回调函数,则该请求将阻塞、同步地执行。 + +对地址url进行一次HTTP GET 请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。 + +最简单GET请求如下: + +``` +console.show(); +var r = http.get("www.baidu.com"); +log("code = " + r.statusCode); +log("html = " + r.body.string()); +``` + +采用回调形式的GET请求如下: + +``` +console.show(); +http.get("www.baidu.com", {}, function(res, err){ + if(err){ + console.error(err); + return; + } + log("code = " + res.statusCode); + log("html = " + res.body.string()); +}); +``` + +如果要增加HTTP头部信息,则在options参数中添加,例如: +``` +console.show(); +var r = http.get("www.baidu.com", { + headers: { + 'Accept-Language': 'zh-cn,zh;q=0.5', + 'User-Agent': 'Mozilla/5.0(Macintosh;IntelMacOSX10_7_0)AppleWebKit/535.11(KHTML,likeGecko)Chrome/17.0.963.56Safari/535.11' + } +}); +log("code = " + r.statusCode); +log("html = " + r.body.string()); +``` + +一个请求天气并解析返回的天气JSON结果的例子如下: +``` +var city = "广州"; +var res = http.get("http://www.sojson.com/open/api/weather/json.shtml?city=" + city); +if(res.statusCode != 200){ + toast("请求失败: " + res.statusCode + " " + res.statusMessage); +}else{ + var weather = res.body.json(); + log(weather); + toast(util.format("温度: %s 湿度: %s 空气质量: %s", weather.data.wendu, + weather.data.shidu, weather.quality)); +} +``` + +## http.post(url, data[, options, callback]) + +* `url` {string} 请求的URL地址,需要以"http://"或"https://"开头。如果url没有以"http://"开头,则默认为"http://"。 +* `data` {string} | {Object} POST数据。 +* `options` {Object} 请求选项。 +* `callback` {Function} 回调,其参数是一个[Response][]对象。如果不加回调参数,则该请求将阻塞、同步地执行。 + +对地址url进行一次HTTP POST 请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。 + +其中POST数据可以是字符串或键值对。具体含义取决于options.contentType的值。默认为"application/x-www-form-urlencoded"(表单提交), 这种方式是JQuery的ajax函数的默认方式。 + +一个模拟表单提交登录淘宝的例子如下: + +``` +var url = "https://login.taobao.com/member/login.jhtml"; +var username = "你的用户名"; +var password = "你的密码"; +var res = http.post(url, { + "TPL_username": username, + "TPL_password": password +}); +var html = res.body.string(); +if(html.contains("页面跳转中")){ + toast("登录成功"); +}else{ + toast("登录失败"); +} +``` +## http.postJson(url[, data, options, callback]) +* `url` {string} 请求的URL地址,需要以"http://"或"https://"开头。如果url没有以"http://"开头,则默认为"http://"。 +* `data` {Object} POST数据。 +* `options` {Object} 请求选项。 +* `callback` {Function} 回调,其参数是一个[Response][]对象。如果不加回调参数,则该请求将阻塞、同步地执行。 + +以JSON格式向目标Url发起POST请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。 + +JSON格式指的是,将会调用`JSON.stringify()`把data对象转换为JSON字符串,并在HTTP头部信息中把"Content-Type"属性置为"application/json"。这种方式是AngularJS的ajax函数的默认方式。 + +一个调用图灵机器人接口的例子如下: + +``` +var url = "http://www.tuling123.com/openapi/api"; +r = http.postJson(url, { + key: "65458a5df537443b89b31f1c03202a80", + info: "你好啊", + userid: "1", +}); +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`对象。如果不加回调参数,则该请求将阻塞、同步地执行。 + +向目标地址发起类型为multipart/form-data的请求(通常用于文件上传等), 其中files参数是{name1: value1, name2: value2, ...}的键值对,value的格式可以是以下几种情况: +1. `string` +2. 文件类型,即open()返回的类型 +3. [fileName, filePath] +4. [fileName, mimeType, filePath] + +其中1属于非文件参数,2、3、4为文件参数。举个例子,最简单的文件上传的请求为: +``` +var res = http.postMultipart(url, { + file: open("/sdcard/1.txt") +}); +log(res.body.string()); +``` + +如果使用格式2,则代码为 +``` +var res = http.postMultipart(url, { + file: ["1.txt", "/sdcard/1.txt"] +}); +log(res.body.string()); +``` +如果使用格式3,则代码为 +``` +var res = http.postMultipart(url, { + file: ["1.txt", "text/plain", "/sdcard/1.txt"] +}); +log(res.body.string()); +``` +如果使用格式2的同时要附带非文件参数"appId=abcdefghijk",则为: +``` +var res = http.postMultipart(url, { + appId: "adcdefghijk", + file: open("/sdcard/1.txt") +}); +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发起一次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)为参数的函数。 + +该函数是get, post, postJson等函数的基础函数。因此除非是PUT, DELET等请求,或者需要更高定制的HTTP请求,否则直接使用get, post, postJson等函数会更加方便。 + +# Response + +HTTP请求的响应。 + +## Response.statusCode +* {number} + +当前响应的HTTP状态码。例如200(OK), 404(Not Found)等。 + +有关HTTP状态码的信息,参见[菜鸟教程:HTTP状态码](http://www.runoob.com/http/http-status-codes.html)。 + +## Response.statusMessage +* {string} + +当前响应的HTTP状态信息。例如"OK", "Bad Request", "Forbidden"。 + +有关HTTP状态码的信息,参见[菜鸟教程:HTTP状态码](http://www.runoob.com/http/http-status-codes.html)。 + +例子: +``` +var res = http.get("www.baidu.com"); +if(res.statusCode >= 200 && res.statusCode < 300){ + toast("页面获取成功!"); +}else if(res.statusCode == 404){ + toast("页面没找到哦..."); +}else{ + toast("错误: " + res.statusCode + " " + res.statusMessage); +} +``` + +## Response.headers +* {Object} + +当前响应的HTTP头部信息。该对象的键是响应头名称,值是各自的响应头值。 所有响应头名称都是小写的(吗)。 + +有关HTTP头部信息,参见[菜鸟教程:HTTP响应头信息](http://www.runoob.com/http/http-header-fields.html)。 + +例子: +``` +console.show(); +var res = http.get("www.qq.com"); +console.log("HTTP Headers:") +for(var headerName in res.headers){ + console.log("%s: %s", headerName, res.headers[headerName]); +} +``` + +## Response.body +* {Object} + +当前响应的内容。他有以下属性和函数: +* bytes() {Array} 以字节数组形式返回响应内容 +* string() {string} 以字符串形式返回响应内容 +* json() {Object} 把响应内容作为JSON格式的数据并调用JSON.parse,返回解析后的对象 +* contentType {string} 当前响应的内容类型 + +## Response.request +* {Request} +当前响应所对应的请求。参见[Request][]。 + +## Response.url +* {number} +当前响应所对应的请求URL。 + +## Response.method +* {string} +当前响应所对应的HTTP请求的方法。例如"GET", "POST", "PUT"等。 diff --git a/AutoJs-Docs/api/images.md b/AutoJs-Docs/api/images.md new file mode 100644 index 000000000..2343a125b --- /dev/null +++ b/AutoJs-Docs/api/images.md @@ -0,0 +1,855 @@ +# 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/AutoJs-Docs/api/keys.md b/AutoJs-Docs/api/keys.md new file mode 100644 index 000000000..87b46a74a --- /dev/null +++ b/AutoJs-Docs/api/keys.md @@ -0,0 +1,194 @@ +# Keys + +按键模拟部分提供了一些模拟物理按键的全局函数,包括Home、音量键、照相键等,有的函数依赖于无障碍服务,有的函数依赖于root权限。 + +一般来说,以大写字母开头的函数都依赖于root权限。执行此类函数时,如果没有root权限,则函数执行后没有效果,并会在控制台输出一个警告。 + +## back() +* 返回 {boolean} + +模拟按下返回键。返回是否执行成功。 +此函数依赖于无障碍服务。 + +## home() +* 返回 {boolean} + +模拟按下Home键。返回是否执行成功。 +此函数依赖于无障碍服务。 + +## powerDialog() +* 返回 {boolean} + +弹出电源键菜单。返回是否执行成功。 +此函数依赖于无障碍服务。 + +## notifications() +* 返回 {boolean} + +拉出通知栏。返回是否执行成功。 +此函数依赖于无障碍服务。 + +## quickSettings() +* 返回 {boolean} + +显示快速设置(下拉通知栏到底)。返回是否执行成功。 +此函数依赖于无障碍服务。 + +## recents() +* 返回 {boolean} + +显示最近任务。返回是否执行成功。 +此函数依赖于无障碍服务。 + +## splitScreen() +* 返回 {boolean} + +分屏。返回是否执行成功。 +此函数依赖于无障碍服务, 并且需要系统自身功能的支持。 + +## Home() +模拟按下Home键。 +此函数依赖于root权限。 + +## Back() +模拟按下返回键。 +此函数依赖于root权限。 + +## Power() +模拟按下电源键。 +此函数依赖于root权限。 + +## Menu() +模拟按下菜单键。 +此函数依赖于root权限。 + +## VolumeUp() +按下音量上键。 +此函数依赖于root权限。 + +## VolumeDown() +按键音量上键。 +此函数依赖于root权限。 + +## Camera() +模拟按下照相键。 + +## Up() +模拟按下物理按键上。 +此函数依赖于root权限。 + +## Down() +模拟按下物理按键下。 +此函数依赖于root权限。 + +## Left() +模拟按下物理按键左。 +此函数依赖于root权限。 + +## Right() +模拟按下物理按键右。 +此函数依赖于root权限。 + +## OK() +模拟按下物理按键确定。 +此函数依赖于root权限。 + +## Text(text) +* text {string} 要输入的文字,只能为英文或英文符号 +输入文字text。例如`Text("aaa");` + +## KeyCode(code) +* code {number} | 要按下的按键的数字代码或名称。参见下表。 +模拟物理按键。例如`KeyCode(29)`和`KeyCode("KEYCODE_A")`是按下A键。 + +# 附录: KeyCode对照表 + +KeyCode KeyEvent Value +* KEYCODE_MENU 1 +* KEYCODE_SOFT_RIGHT 2 +* KEYCODE_HOME 3 +* KEYCODE_BACK 4 +* KEYCODE_CALL 5 +* KEYCODE_ENDCALL 6 +* KEYCODE_0 7 +* KEYCODE_1 8 +* KEYCODE_2 9 +* KEYCODE_3 10 +* KEYCODE_4 11 +* KEYCODE_5 12 +* KEYCODE_6 13 +* KEYCODE_7 14 +* KEYCODE_8 15 +* KEYCODE_9 16 +* KEYCODE_STAR 17 +* KEYCODE_POUND 18 +* KEYCODE_DPAD_UP 19 +* KEYCODE_DPAD_DOWN 20 +* KEYCODE_DPAD_LEFT 21 +* KEYCODE_DPAD_RIGHT 22 +* KEYCODE_DPAD_CENTER 23 +* KEYCODE_VOLUME_UP 24 +* KEYCODE_VOLUME_DOWN 25 +* KEYCODE_POWER 26 +* KEYCODE_CAMERA 27 +* KEYCODE_CLEAR 28 +* KEYCODE_A 29 +* KEYCODE_B 30 +* KEYCODE_C 31 +* KEYCODE_D 32 +* KEYCODE_E 33 +* KEYCODE_F 34 +* KEYCODE_G 35 +* KEYCODE_H 36 +* KEYCODE_I 37 +* KEYCODE_J 38 +* KEYCODE_K 39 +* KEYCODE_L 40 +* KEYCODE_M 41 +* KEYCODE_N 42 +* KEYCODE_O 43 +* KEYCODE_P 44 +* KEYCODE_Q 45 +* KEYCODE_R 46 +* KEYCODE_S 47 +* KEYCODE_T 48 +* KEYCODE_U 49 +* KEYCODE_V 50 +* KEYCODE_W 51 +* KEYCODE_X 52 +* KEYCODE_Y 53 +* KEYCODE_Z 54 +* KEYCODE_COMMA 55 +* KEYCODE_PERIOD 56 +* KEYCODE_ALT_LEFT 57 +* KEYCODE_ALT_RIGHT 58 +* KEYCODE_SHIFT_LEFT 59 +* KEYCODE_SHIFT_RIGHT 60 +* KEYCODE_TAB 61 +* KEYCODE_SPACE 62 +* KEYCODE_SYM 63 +* KEYCODE_EXPLORER 64 +* KEYCODE_ENVELOPE 65 +* KEYCODE_ENTER 66 +* KEYCODE_DEL 67 +* KEYCODE_GRAVE 68 +* KEYCODE_MINUS 69 +* KEYCODE_EQUALS 70 +* KEYCODE_LEFT_BRACKET 71 +* KEYCODE_RIGHT_BRACKET 72 +* KEYCODE_BACKSLASH 73 +* KEYCODE_SEMICOLON 74 +* KEYCODE_APOSTROPHE 75 +* KEYCODE_SLASH 76 +* KEYCODE_AT 77 +* KEYCODE_NUM 78 +* KEYCODE_HEADSETHOOK 79 +* KEYCODE_FOCUS 80 +* KEYCODE_PLUS 81 +* KEYCODE_MENU 82 +* KEYCODE_NOTIFICATION 83 +* KEYCODE_SEARCH 84 +* TAG_LAST_ KEYCODE 85 + + + diff --git a/AutoJs-Docs/api/media.md b/AutoJs-Docs/api/media.md new file mode 100644 index 000000000..154fff583 --- /dev/null +++ b/AutoJs-Docs/api/media.md @@ -0,0 +1,101 @@ +# Media + +> Stability: 2 - Stable + +media模块提供多媒体编程的支持。目前仅支持音乐播放和媒体文件扫描。后续会结合UI加入视频播放等功能。 + +需要注意是,使用该模块播放音乐时是在后台异步播放的,在脚本结束后会自动结束播放,因此可能需要插入诸如`sleep()`的语句来使脚本保持运行。例如: +``` +//播放音乐 +media.playMusic("/sdcard/1.mp3"); +//让音乐播放完 +sleep(media.getMusicDuration()); +``` + +## media.scanFile(path) +* `path` {string} 媒体文件路径 + +扫描路径path的媒体文件,将它加入媒体库中;或者如果该文件以及被删除,则通知媒体库移除该文件。 + +媒体库包括相册、音乐库等,因此该函数可以用于把某个图片文件加入相册。 + +``` +//请求截图 +requestScreenCapture(false); +//截图 +var im = captureScreen(); +var path = "/sdcard/screenshot.png"; +//保存图片 +im.saveTo(path); +//把图片加入相册 +media.scanFile(path); +``` + +## media.playMusic(path[, volume, looping]) +* `path` {string} 音乐文件路径 +* `volume` {number} 播放音量,为0~1的浮点数,默认为1 +* `looping` {boolean} 是否循环播放,如果looping为`true`则循环播放,默认为`false` + +播放音乐文件path。该函数不会显示任何音乐播放界面。如果文件不存在或者文件不是受支持的音乐格式,则抛出`UncheckedIOException`异常。 + +``` +//播放音乐 +media.playMusic("/sdcard/1.mp3"); +//让音乐播放完 +sleep(media.getMusicDuration()); +``` + +如果要循环播放音乐,则使用looping参数: +``` +``` +//传递第三个参数为true以循环播放音乐 +media.playMusic("/sdcard/1.mp3", 1, true); +//等待三次播放的时间 +sleep(media.getMusicDuration() * 3); +``` +``` + +如果要使用音乐播放器播放音乐,调用`app.viewFile(path)`函数。 + +## media.musicSeekTo(msec) +* `msec` {number} 毫秒数,表示音乐进度 + +把当前播放进度调整到时间msec的位置。如果当前没有在播放音乐,则调用函数没有任何效果。 + +例如,要把音乐调到1分钟的位置,为`media.musicSeekTo(60 * 1000)`。 + +``` +//播放音乐 +media.playMusic("/sdcard/1.mp3"); +//调整到30秒的位置 +media.musicSeekTo(30 * 1000); +//等待音乐播放完成 +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 diff --git a/AutoJs-Docs/api/modules.md b/AutoJs-Docs/api/modules.md new file mode 100644 index 000000000..642a7d522 --- /dev/null +++ b/AutoJs-Docs/api/modules.md @@ -0,0 +1,47 @@ +# module (模块) + +> Stability: 2 - Stable + +Auto.js 有一个简单的模块加载系统。 在 Auto.js 中,文件和模块是一一对应的(每个文件被视为一个独立的模块)。 + +例子,假设有一个名为 foo.js 的文件: +``` +var circle = require('circle.js'); +console.log("半径为 4 的圆的面积是 %d", circle.area(4)); +``` +在第一行中,foo.js 加载了同一目录下的 circle.js 模块。 + +circle.js 文件的内容为: +``` +const PI = Math.PI; + +var circle = {}; + +circle.area = function (r) { + return PI * r * r; +}; + +circle.circumference = (r) => 2 * PI * r; + +module.exports = circle; +``` +circle.js 模块导出了 area() 和 circumference() 两个函数。 通过在特殊的 exports 对象上指定额外的属性,函数和对象可以被添加到模块的根部。 + +模块内的本地变量是私有的。 在这个例子中,变量 PI 是 circle.js 私有的,不会影响到加载他的脚本的变量环境。 + +module.exports属性可以被赋予一个新的值(例如函数或对象)。 + +如下,bar.js 会用到 square 模块,square 导出一个构造函数: +``` +const square = require('square.js'); +const mySquare = square(2); +console.log("正方形的面积是 %d", mySquare.area()); +square 模块定义在 square.js 中: + +// 赋值给 `exports` 不会修改模块,必须使用 `module.exports` +module.exports = function(width) { + return { + area: () => width ** 2 + }; +}; +``` diff --git a/AutoJs-Docs/api/overview.md b/AutoJs-Docs/api/overview.md new file mode 100644 index 000000000..f0a61670f --- /dev/null +++ b/AutoJs-Docs/api/overview.md @@ -0,0 +1,28 @@ +# 综述 + +Auto.js使用[JavaScript](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)作为脚本语言,目前使用[Rhino 1.7.7.2](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)。 + +本文档的章节大致上是以模块来分的,总体上可以分成"自动操作"类模块(控件操作、触摸模拟、按键模拟等)和其他类模块(设备、应用、界面等)。 + +"自动操作"的部分又可以大致分为基于控件和基于坐标的操作。基于坐标的操作是传统按键精灵、触摸精灵等脚本软件采用的方式,通过屏幕坐标来点击、长按指定位置模拟操作,从而到达目的。例如`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界面,和用户交互。 + +除此之外,Auto.js内置了对[Promise](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise)。 \ No newline at end of file diff --git a/AutoJs-Docs/api/qa.md b/AutoJs-Docs/api/qa.md new file mode 100644 index 000000000..d3dd512aa --- /dev/null +++ b/AutoJs-Docs/api/qa.md @@ -0,0 +1,77 @@ +# Q & A + +## 如何定时运行脚本 + +点击脚本右边的菜单按钮->更多->定时任务即可定时运行脚本,但是必须保持Auto.js后台运行(自启动白名单、电源管理白名单等)。同时,可以在脚本的开头使用`device.wakeUp()`来唤醒屏幕;但是,Auto.js没有解锁屏幕的功能,因此难以在有锁屏密码的设备上达到效果。 + +## 定时任何如何获取外部参数 + +如果一个脚本是用intent"启动"的,比如定时任务中的特定事件(网络状态变化等)触发而启动的,则可以通过`engines.myEngine().execArgv.intent`获取启动的intent,从而获取外部参数。 + +## 如何把图片和脚本一起打包,或者打包多个脚本 + +如果除了单脚本以外还有其他脚本、图片、音乐等资源一起打包,则需要使用项目功能。 + +点击Auto.js的"+"号,选择项目,填写项目名称、包名等信息以后,点击"√"即可新建一个项目。可以在项目中放多个脚本、模块、资源文件,点击项目工具栏的apk打包图标即可打包一个项目,点击工具栏可以重新配置项目。 + +例如,主脚本要读取同一文件夹下的图片1.png,再执行找图,则可以通过`images.read("./1.png")`来读取,其中"./1.png"表示同一目录1.png图片;ui中的图片控件要引用同一文件夹的2.png图片则为``。Auto.js内置的函数和模块都支持相对路径,但是,其他情况则需要使用`files.path()`函数来把相对路径转换为绝对路径。 + +## 如何使打包的应用不显示主界面 + +需要使用项目功能。新建项目后,修改项目下的`project.json`文件,增加以下条目: +``` +"launchConfig": { + "hideLogs": true +} +``` + +例如: + +``` +{ + "name": "项目名称", + "versionName": "1.0.0", + "versionCode": 1, + "packageName": "org.autojs.example", + "main": "main.js", + "launchConfig": { + "hideLogs": true + } +} +``` +"launchConfig"表示启动配置,"hideLogs"表示隐藏日志。 + +参见项目与项目配置。 + +## Auto.js自带的模块和函数中没有的功能如何实现 + +由于Auto.js支持直接调用Android的API,对于Auto.js没有内置的函数,可以直接通过修改Android代码为JavaScript代码实现。例如旋转图片的Android代码为: +``` +import android.graphics.Bitmap; +import android.graphics.Matrix; + +public static Bitmap rotate(final Bitmap src, + final int degrees, + final float px, + final float py) { + if (degrees == 0) return src; + Matrix matrix = new Matrix(); + matrix.setRotate(degrees, px, py); + Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); + return ret; +} +``` +转换为JavaScript的代码后为: +``` +importClass(android.graphics.Bitmap); +importClass(android.graphics.Matrix); + +function rotate(src, degrees, px, py){ + if (degrees == 0) return src; + var matrix = new Matrix(); + matrix.setRotate(degrees, px, py); + var 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 diff --git a/AutoJs-Docs/api/sensors.md b/AutoJs-Docs/api/sensors.md new file mode 100644 index 000000000..47e02e3bb --- /dev/null +++ b/AutoJs-Docs/api/sensors.md @@ -0,0 +1,225 @@ +# Sensors + +> Stability: 2 - Stable + +sensors模块提供了获取手机上的传感器的信息的支持,这些传感器包括距离传感器、光线光感器、重力传感器、方向传感器等。需要指出的是,脚本只能获取传感器的数据,**不能模拟或伪造传感器的数据和事件**,因此诸如模拟摇一摇的功能是无法实现的。 + +要监听一个传感器时,需要使用`sensors.register()`注册监听器,之后才能开始监听;不需要监听时则调用`sensors.unregister()`注销监听器。在脚本结束时会自动注销所有的监听器。同时,这种监听会使脚本保持运行状态,如果不注销监听器,脚本会一直保持运行状态。 + +例如,监听光线传感器的代码为: +``` +//光线传感器监听 +sensors.register("light").on("change", (event, light)=>{ + log("当前光强度为", light); +}); +``` + +要注意的是,每个传感器的数据并不相同,所以对他们调用`on()`监听事件时的回调函数参数也不是相同,例如光线传感器参数为`(event, light)`,加速度传感器参数为`(event, ax, ay, az)`。甚至在某些设备上的传感器参数有所增加,例如华为手机的距离传感器为三个参数,一般手机只有一个参数。 + +常用的传感器及其事件参数如下表: +* `accelerometer` 加速度传感器,参数`(event, ax, ay, az)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `ax` {number} x轴上的加速度,单位m/s^2 + * `ay` {number} y轴上的加速度,单位m/s^2 + * `az` {number} z轴上的加速度,单位m/s^2 + 这里的x轴,y轴,z轴所属的坐标系统如下图(其中z轴垂直于设备屏幕表面): + + !![axis_device](#images/axis_device.png) + +* `orientation` 方向传感器,参数`(event, azimuth, pitch, roll)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `azimuth` {number} 方位角,从地磁指北方向线起,依顺时针方向到y轴之间的水平夹角,单位角度,范围0~359 + * `pitch` {number} 绕x轴旋转的角度,当设备水平放置时该值为0,当设备顶部翘起时该值为正数,当设备尾部翘起时该值为负数,单位角度,范围-180~180 + * `roll` {number} 绕y轴顺时针旋转的角度,单位角度,范围-90~90 + +* `gyroscope` 陀螺仪传感器,参数`(event, wx, wy, wz)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `wx` {number} 绕x轴的角速度,单位弧度/s + * `wy` {number} 绕y轴的角速度,单位弧度/s + * `wz` {number} 绕z轴的角速度,单位弧度/s + +* `magnetic_field` 磁场传感器,参数`(event, bx, by, bz)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `bx` {number} x轴上的磁场强度,单位uT + * `by` {number} y轴上的磁场强度,单位uT + * `bz` {number} z轴上的磁场强度,单位uT + +* `gravity` 重力传感器,参数`(event, gx, gy, gz)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `gx` {number} x轴上的重力加速度,单位m/s^2 + * `gy` {number} y轴上的重力加速度,单位m/s^2 + * `gz` {number} z轴上的重力加速度,单位m/s^2 + +* `linear_acceleration` 线性加速度传感器,参数`(event, ax, ay, az)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `ax` {number} x轴上的线性加速度,单位m/s^2 + * `ay` {number} y轴上的线性加速度,单位m/s^2 + * `az` {number} z轴上的线性加速度,单位m/s^2 + +* `ambient_temperature` 环境温度传感器,大部分设备并不支持,参数`(event, t)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `t` {number} 环境温度,单位摄氏度。 + +* `light` 光线传感器,参数`(event, light)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `light` {number} 环境光强度,单位lux + +* `pressure` 压力传感器,参数`(event, p)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `p` {number} 大气压,单位hPa + +* `proximity` 距离传感器,参数`(event, distance)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `distance` {number} 一般指设备前置摄像头旁边的距离传感器到前方障碍物的距离,并且很多设备上这个值只有两种情况:当障碍物较近时该值为0,当障碍物较远或在范围内没有障碍物时该值为5 + +* `relative_humidity` 湿度传感器,大部分设备并不支持,参数`(event, rh)`: + * `event` [SensorEvent](#sensors_sensorevent) 传感器事件,用于获取传感器数据变化时的所有信息 + * `rh` {number} 相对湿度,范围为0~100(百分比) + + + +## sensors.register(sensorName[, delay]) +* `sensorName` {string} 传感器名称,常用的传感器名称如上面所述 +* `delay` {number} 传感器数据更新频率,可选,默认为`sensors.delay.normal`。可用的值如下: + * `sensors.delay.normal` 正常频率 + * `sensors.delay.ui` 适合于用户界面的更新频率 + * `sensors.delay.game` 适合于游戏的更新频率 + * `sensors.delay.fastest` 最快的更新频率】 +* 返回 [SensorEventEmiiter](#sensors_sensoreventemitter) + +注册一个传感器监听并返回[SensorEventEmitter](#sensors_sensoreventemitter)。 + +例如: +``` +console.show(); +//注册传感器监听 +var sensor = sensors.register("gravity"); +if(sensor == null){ + toast("不支持重力传感器"); + exit(); +} +//监听数据 +sensor.on("change", (gx, gy, gz)=>{ + log("重力加速度: %d, %d, %d", gx, gy, gz); +}); +``` + +可以通过delay参数来指定传感器数据的更新频率,例如: +``` +var sensor = sensors.register("gravity", sensors.delay.game); +``` + +另外,如果不支持`sensorName`所指定的传感器,那么该函数将返回`null`;但如果`sensors.ignoresUnsupportedSensor`的值被设置为`true`, 则该函数会返回一个不会分发任何传感器事件的[SensorEventEmitter](#sensors_sensoreventemitter)。 + +例如: +``` +sensors.ignoresUnsupportedSensor = true; +//无需null判断 +sensors.register("gravity").on("change", (gx, gy, gz)=>{ + log("重力加速度: %d, %d, %d", gx, gy, gz); +}); +``` + +更多信息,参见[SensorEventEmitter](#sensors_sensoreventemitter)和[sensors.ignoresUnsupportedSensor](#sensors_sensors_ignoresUnsupportedSensor)。 + +## sensors.unregister(emitter) +* `emiiter` [SensorEventEmitter](#sensors_sensoreventemitter) + +注销该传感器监听器。被注销的监听器将不再能监听传感器数据。 + +``` +//注册一个传感器监听器 +var sensor = sensors.register("gravity"); +if(sensor == null){ + exit(); +} +//2秒后注销该监听器 +setTimeout(()=> { + sensors.unregister(sensor); +}, 2000); +``` + +## sensors.unregisterAll() + +注销所有传感器监听器。 + +## sensors.ignoresUnsupportedSensor +* {boolean} + +表示是否忽略不支持的传感器。如果该值被设置为`true`,则函数`sensors.register()`即使对不支持的传感器也会返回一个无任何数据的虚拟传感器监听,也就是`sensors.register()`不会返回`null`从而避免非空判断,并且此时会触发`sensors`的"unsupported_sensor"事件。 + +``` +//忽略不支持的传感器 +sensors.ignoresUnsupportedSensor = true; +//监听有不支持的传感器时的事件 +sensors.on("unsupported_sensor", function(sensorName){ + toastLog("不支持的传感器: " + sensorName); +}); +//随便注册一个不存在的传感器。 +log(sensors.register("aaabbb")); +``` + +## 事件: 'unsupported_sensor' +* `sensorName` {string} 不支持的传感器名称 + +当`sensors.ignoresUnsupportedSensor`被设置为`true`并且有不支持的传感器被注册时触发该事件。事件参数的传感器名称。 + +# SensorEventEmitter + +注册传感器返回的对象,其本身是一个EventEmmiter,用于监听传感器事件。 +## 事件: 'change' +* `..args` {Any} 传感器参数 + +当传感器数据改变时触发该事件;该事件触发的最高频繁由`sensors.register()`指定的delay参数决定。 + +事件参数根据传感器类型不同而不同,具体参见本章最前面的列表。 + +一个监听光线传感器和加速度传感器并且每0.5秒获取一个数据并最终写入一个csv表格文件的例子如下: + +``` +//csv文件路径 +cosnt csvPath = "/sdcard/sensors_data.csv"; +//记录光线传感器的数据 +var light = 0; +//记录加速度传感器的数据 +var ax = 0; +var ay = 0; +var az = 0; +//监听光线传感器 +sensors.register("light", sensors.delay.fastest) + .on("change", l => { + light = l; + }); +//监听加速度传感器 +sensors.register("accelerometer", sensors.delay.fastest) + .on("change", (ax0, ay0, az0) => { + ax = ax0; + ay = ay0; + az = az0; + }); + +var file = open(csvPath, "w"); +//写csv表格头 +file.writeline("light,ax,ay,az") +//每0.5秒获取一次数据并写入文件 +setInterval(()=>{ + file.writeline(util.format("%d,%d,%d,%d", light, ax, ay, az)); +}, 500); +//10秒后退出并打开文件 +setTimeout(()=>{ + file.close(); + sensors.unregsiterAll(); + app.viewFile(csvPath); +}, 10 * 1000); + +``` + +## 事件: 'accuracy_change' +* `accuracy` {number} 表示传感器精度。为以下值之一: + * -1 传感器未连接 + * 0 传感器不可读 + * 1 低精度 + * 2 中精度 + * 3 高精度 + +当传感器精度改变时会触发的事件。比较少用。 diff --git a/AutoJs-Docs/api/shell.md b/AutoJs-Docs/api/shell.md new file mode 100644 index 000000000..dbe0c3858 --- /dev/null +++ b/AutoJs-Docs/api/shell.md @@ -0,0 +1,431 @@ +shell即Unix Shell,在类Unix系统提供与操作系统交互的一系列命令。 + +很多程序可以用来执行shell命令,例如终端模拟器。 + +在Auto.js大致等同于用adb执行命令"adb shell"。其实现包括两种方式: + +* 通过`java.lang.Runtime.exec`执行(shell, Tap, Home等函数) +* 通过内嵌终端模拟器执行(RootAutomator, Shell等对象) + +# shell函数 + +> Stability: 2 - Stable + +## shell(cmd[, root]) +* cmd {string} 要执行的命令 +* root {Boolean} 是否以root权限运行,默认为false。 + +一次性执行命令cmd, 并返回命令的执行结果。返回对象的其属性如下: +* code {number} 返回码。执行成功时为0,失败时为非0的数字。 +* result {string} 运行结果(stdout输出结果) +* error {string} 运行的错误信息(stderr输出结果)。例如执行需要root权限的命令但没有授予root权限会返回错误信息"Permission denied"。 + +示例(强制停止微信) : +``` +var result = shell("am force-stop com.tencent.mm", true); +log(result); +console.show(); +if(result.code == 0){ + toast("执行成功"); +}else{ + toast("执行失败!请到控制台查看错误信息"); +} +``` + +# Shell + +> Stability: 2 - Stable + +shell函数通过用来一次性执行单条命令并获取结果。如果有多条命令需要执行,用Shell对象的效率更高。这是因为,每次运行shell函数都会打开一个单独的shell进程并在运行结束后关闭他,这个过程需要一定的时间;而Shell对象自始至终使用同一个shell进程。 + +## new Shell(root) +* root {Boolean} 是否以root权限运行一个shell进程,默认为false。这将会影响其后使用该Shell对象执行的命令的权限 + +Shell对象的"构造函数"。 +``` +var sh = new Shell(true); +//强制停止微信 +sh.exec("am force-stop com.tencent.mm"); +sh.exit(); +``` + +## Shell.exec(cmd) +* `cmd` {string} 要执行的命令 + +执行命令cmd。该函数不会返回任何值。 + +注意,命令执行是"异步"的、非阻塞的。也就是不会等待命令完成后才继续向下执行。 + +尽管这样的设计使用起来有很多不便之处,但受限于终端模拟器,暂时没有解决方式;如果后续能找到解决方案,则将提供`Shell.execAndWaitFor`函数。 + +## Shell.exit() + +直接退出shell。正在执行的命令会被强制退出。 + +## Shell.exitAndWaitFor() + +执行"exit"命令并等待执行命令执行完成、退出shell。 + +此函数会执行exit命令来正常退出shell。 + +## Shell.setCallback(callback) +* callback {Object} 回调函数 + +设置该Shell的回调函数,以便监听Shell的输出。可以包括以下属性: +* onOutput {Function} 每当shell有新的输出时便会调用该函数。其参数是一个字符串。 +* onNewLine {Function} 每当shell有新的一行输出时便会调用该函数。其参数是一个字符串(不包括最后的换行符)。 + +例如: +``` +var sh = new Shell(); +sh.setCallback({ + onNewLine: function(line){ + //有新的一行输出时打印到控制台 + log(line); + } +}) +while(true){ + //循环输入命令 + var cmd = dialogs.rawInput("请输入要执行的命令,输入exit退出"); + if(cmd == "exit"){ + break; + } + //执行命令 + sh.exec(cmd); +} +sh.exit(); +``` + +# 附录: shell命令简介 + +以下关于shell命令的资料来自[AndroidStudio用户指南:Shell命令](https://developer.android.com/studio/command-line/adb.html#shellcommands)。 + +## am命令 + +am命令即Activity Manager命令,用于管理应用程序活动、服务等。 + +**以下命令均以"am "开头,例如`shell('am start -p com.tencent.mm');`(启动微信)** + +### start [options] intent +启动 intent 指定的 Activity(应用程序活动)。 +请参阅 [intent 参数的规范](#shell_intent)。 + +选项包括: + +* -D:启用调试。 +* -W:等待启动完成。 +* --start-profiler file:启动分析器并将结果发送到 file。 +* -P file:类似于 --start-profiler,但当应用进入空闲状态时分析停止。 +* -R count:重复 Activity 启动 count 次数。在每次重复前,将完成顶部 Activity。 +* -S:启动 Activity 前强行停止目标应用。 +* --opengl-trace:启用 OpenGL 函数的跟踪。 +* --user user_id | current:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。 + +### startservice [options] intent +启动 intent 指定的 Service(服务)。 +请参阅 [intent 参数的规范](#shell_intent)。 +选项包括: + +* --user user_id | current:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。 + +### force-stop package +强行停止与 package([应用包名](#应用包名))关联的所有应用。 + +### kill [options] package +终止与 package([应用包名](#应用包名))关联的所有进程。此命令仅终止可安全终止且不会影响用户体验的进程。 +选项包括: + +* --user user_id | all | current:指定将终止其进程的用户;如果未指定,则终止所有用户的进程。 + +### kill-all +终止所有后台进程。 + +### broadcast [options] intent +发出广播 intent。 +请参阅 [intent 参数的规范](#shell_intent)。 + +选项包括: + +* [--user user_id | all | current]:指定要发送到的用户;如果未指定,则发送到所有用户。 + +### instrument [options] component +使用 Instrumentation 实例启动监控。通常,目标 component 是表单 test_package/runner_class。 +选项包括: + +* -r:输出原始结果(否则对 report_key_streamresult 进行解码)。与 [-e perf true] 结合使用以生成性能测量的原始输出。 +* -e name value:将参数 name 设为 value。对于测试运行器,通用表单为 -e testrunner_flag value[,value...]。 +* -p file:将分析数据写入 file。 +* -w:先等待仪器完成,然后再返回。测试运行器需要使用此选项。 +* --no-window-animation:运行时关闭窗口动画。 +* --user user_id | current:指定仪器在哪个用户中运行;如果未指定,则在当前用户中运行。 +* profile start process file 启动 process 的分析器,将结果写入 file。 +* profile stop process 停止 process 的分析器。 + +### dumpheap [options] process file +转储 process 的堆,写入 file。 + +选项包括: + +* --user [user_id|current]:提供进程名称时,指定要转储的进程用户;如果未指定,则使用当前用户。 +* -n:转储原生堆,而非托管堆。 +* set-debug-app [options] package 将应用 package 设为调试。 + +选项包括: + +* -w:应用启动时等待调试程序。 +* --persistent:保留此值。 +* clear-debug-app 使用 set-debug-app 清除以前针对调试用途设置的软件包。 + +### monitor [options] 启动对崩溃或 ANR 的监控。 +选项包括: + +* --gdb:在崩溃/ANR 时在给定端口上启动 gdbserv。 +### screen-compat {on|off} package +控制 package 的屏幕兼容性模式。 + +### display-size [reset|widthxheight] +替换模拟器/设备显示尺寸。此命令对于在不同尺寸的屏幕上测试您的应用非常有用,它支持使用大屏设备模仿小屏幕分辨率(反之亦然)。 +示例: +``` +shell("am display-size 1280x800", true); + +``` +### display-density dpi +替换模拟器/设备显示密度。此命令对于在不同密度的屏幕上测试您的应用非常有用,它支持使用低密度屏幕在高密度环境环境上进行测试(反之亦然)。 +示例: +``` +shell("am display-density 480", true); +``` +### to-uri intent +将给定的 intent 规范以 URI 的形式输出。 +请参阅 [intent 参数的规范](#shell_intent)。 + +### to-intent-uri intent +将给定的 intent 规范以 intent:URI 的形式输出。 +请参阅 intent 参数的规范。 + +### intent参数的规范 +对于采用 intent 参数的 am 命令,您可以使用以下选项指定 intent: + +* -a action +指定 intent 操作,如“android.intent.action.VIEW”。此指定只能声明一次。 +* -d data_uri +指定 intent 数据 URI,如“content://contacts/people/1”。此指定只能声明一次。 +* -t mime_type +指定 intent MIME 类型,如“image/png”。此指定只能声明一次。 +* -c category +指定 intent 类别,如“android.intent.category.APP_CONTACTS”。 +* -n component +指定带有软件包名称前缀的组件名称以创建显式 intent,如“com.example.app/.ExampleActivity”。 +* -f flags +将标志添加到 setFlags() 支持的 intent。 +* --esn extra_key +添加一个 null extra。URI intent 不支持此选项。 +* -e|--es extra_key extra_string_value +添加字符串数据作为键值对。 +* --ez extra_key extra_boolean_value +添加布尔型数据作为键值对。 +* --ei extra_key extra_int_value +添加整数型数据作为键值对。 +* --el extra_key extra_long_value +添加长整型数据作为键值对。 +* --ef extra_key extra_float_value +添加浮点型数据作为键值对。 +* --eu extra_key extra_uri_value +添加 URI 数据作为键值对。 +* --ecn extra_key extra_component_name_value +添加组件名称,将其作为 ComponentName 对象进行转换和传递。 +* --eia extra_key extra_int_value[,extra_int_value...] +添加整数数组。 +* --ela extra_key extra_long_value[,extra_long_value...] +添加长整型数组。 +* --efa extra_key extra_float_value[,extra_float_value...] +添加浮点型数组。 +* --grant-read-uri-permission +包含标志 FLAG_GRANT_READ_URI_PERMISSION。 +* --grant-write-uri-permission +包含标志 FLAG_GRANT_WRITE_URI_PERMISSION。 +* --debug-log-resolution +包含标志 FLAG_DEBUG_LOG_RESOLUTION。 +* --exclude-stopped-packages +包含标志 FLAG_EXCLUDE_STOPPED_PACKAGES。 +* --include-stopped-packages +包含标志 FLAG_INCLUDE_STOPPED_PACKAGES。 +* --activity-brought-to-front +包含标志 FLAG_ACTIVITY_BROUGHT_TO_FRONT。 +* --activity-clear-top +包含标志 FLAG_ACTIVITY_CLEAR_TOP。 +* --activity-clear-when-task-reset +包含标志 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET。 +* --activity-exclude-from-recents +包含标志 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS。 +* --activity-launched-from-history +包含标志 FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY。 +* --activity-multiple-task +包含标志 FLAG_ACTIVITY_MULTIPLE_TASK。 +* --activity-no-animation +包含标志 FLAG_ACTIVITY_NO_ANIMATION。 +* --activity-no-history +包含标志 FLAG_ACTIVITY_NO_HISTORY。 +* --activity-no-user-action +包含标志 FLAG_ACTIVITY_NO_USER_ACTION。 +* --activity-previous-is-top +包含标志 FLAG_ACTIVITY_PREVIOUS_IS_TOP。 +* --activity-reorder-to-front +包含标志 FLAG_ACTIVITY_REORDER_TO_FRONT。 +* --activity-reset-task-if-needed +包含标志 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED。 +* --activity-single-top +包含标志 FLAG_ACTIVITY_SINGLE_TOP。 +* --activity-clear-task +包含标志 FLAG_ACTIVITY_CLEAR_TASK。 +* --activity-task-on-home +包含标志 FLAG_ACTIVITY_TASK_ON_HOME。 +* --receiver-registered-only +包含标志 FLAG_RECEIVER_REGISTERED_ONLY。 +* --receiver-replace-pending +包含标志 FLAG_RECEIVER_REPLACE_PENDING。 +* --selector +需要使用 -d 和 -t 选项以设置 intent 数据和类型。 +#### URI component package +如果不受上述某一选项的限制,您可以直接指定 URI、软件包名称和组件名称。当参数不受限制时,如果参数包含一个“:”(冒号),则此工具假定参数是一个 URI;如果参数包含一个“/”(正斜杠),则此工具假定参数是一个组件名称;否则,此工具假定参数是一个软件包名称。 + +## 应用包名 +所谓应用包名,是唯一确定应用的标识。例如微信的包名是"com.tencent.mm", QQ的包名是"com.tencent.mobileqq"。 +要获取一个应用的包名,可以通过函数`getPackageName(appName)`获取。参见帮助->其他一般函数。 + + +## pm命令 + +pm命令用于管理应用程序,例如卸载应用、冻结应用等。 +**以下命令均以"pm "开头,例如"shell(\"pm disable com.tencent.mm\");"(冻结微信)** + +### list packages [options] filter +输出所有软件包,或者,仅输出包名称包含 filter 中的文本的软件包。 +选项: + +* -f:查看它们的关联文件。 +* -d:进行过滤以仅显示已停用的软件包。 +* -e:进行过滤以仅显示已启用的软件包。 +* -s:进行过滤以仅显示系统软件包。 +* -3:进行过滤以仅显示第三方软件包。 +* -i:查看软件包的安装程序。 +* -u:也包括卸载的软件包。 +* --user user_id:要查询的用户空间。 + +### list permission-groups +输出所有已知的权限组。 +### list permissions [options] group +输出所有已知权限,或者,仅输出 group 中的权限。 +选项: + +* -g:按组加以组织。 +* -f:输出所有信息。 +* -s:简短摘要。 +* -d:仅列出危险权限。 +* -u:仅列出用户将看到的权限。 + +### list instrumentation [options] +列出所有测试软件包。 +选项: + +* -f:列出用于测试软件包的 APK 文件。 +* target_package:列出仅用于此应用的测试软件包。 + +### list features +输出系统的所有功能。 +### list libraries +输出当前设备支持的所有库。 +### list users +输出系统上的所有用户。 +### path package +输出给定 package 的 APK 的路径。 +### install [options] path +将软件包(通过 path 指定)安装到系统。 +选项: + +* -l:安装具有转发锁定功能的软件包。 +* -r:重新安装现有应用,保留其数据。 +* -t:允许安装测试 APK。 +* -i installer_package_name:指定安装程序软件包名称。 +* -s:在共享的大容量存储(如 sdcard)上安装软件包。 +* -f:在内部系统内存上安装软件包。 +* -d:允许版本代码降级。 +* -g:授予应用清单文件中列出的所有权限。 + +### uninstall [options] package + +从系统中卸载软件包。 +选项: + +* -k:移除软件包后保留数据和缓存目录。 +### clear package +删除与软件包关联的所有数据。 + +### enable package_or_component +启用给定软件包或组件(作为“package/class”写入)。 + +### disable package_or_component +停用给定软件包或组件(作为“package/class”写入)。 + +### disable-user [options] package_or_component + +选项: + +* --user user_id:要停用的用户。 +### grant package_name permission +向应用授予权限。在运行 Android 6.0(API 级别 23)及更高版本的设备上,可以是应用清单中声明的任何权限。在运行 Android 5.1(API 级别 22)和更低版本的设备上,必须是应用定义的可选权限。 + +### revoke package_name permission +从应用中撤销权限。在运行 Android 6.0(API 级别 23)及更高版本的设备上,可以是应用清单中声明的任何权限。在运行 Android 5.1(API 级别 22)和更低版本的设备上,必须是应用定义的可选权限。 + +### set-install-location location +更改默认安装位置。位置值: +* 0:自动—让系统决定最佳位置。 +* 1:内部—安装在内部设备存储上。 +* 2:外部—安装在外部介质上。 + +> 注:此命令仅用于调试目的;使用此命令会导致应用中断和其他意外行为。 + +### get-install-location +返回当前安装位置。返回值: +* 0 [auto]:让系统决定最佳位置。 +* 1 [internal]:安装在内部设备存储上 +* 2 [external]:安装在外部介质上 + +### set-permission-enforced permission [true|false] +指定是否应强制执行给定的权限。 + +### trim-caches desired_free_space +减少缓存文件以达到给定的可用空间。 + +### create-user user_name +使用给定的 user_name 创建新用户,输出新用户的标识符。 + +### remove-user user_id +移除具有给定的 user_id 的用户,删除与该用户关联的所有数据。 + +### get-max-users +输出设备支持的最大用户数。 + +## 其他命令 + +### 进行屏幕截图 +screencap 命令是一个用于对设备显示屏进行屏幕截图的 shell 实用程序。在 shell 中,此语法为: +``` +screencap filename +``` + +例如: +``` +$ shell("screencap /sdcard/screen.png"); +``` + +### 列表文件 +``` +ls filepath +``` +例如: +``` +log(shell("ls /system/bin").result); +``` diff --git a/AutoJs-Docs/api/storages.md b/AutoJs-Docs/api/storages.md new file mode 100644 index 000000000..5a0afb869 --- /dev/null +++ b/AutoJs-Docs/api/storages.md @@ -0,0 +1,70 @@ +# Storages + +> Stability: 2 - Stable + +storages模块提供了保存简单数据、用户配置等的支持。保存的数据除非应用被卸载或者被主动删除,否则会一直保留。 + +storages支持`number`, `boolean`, `string`等数据类型以及把`Object`, `Array`用`JSON.stringify`序列化存取。 + +storages保存的数据在脚本之间是共享的,任何脚本只要知道storage名称便可以获取到相应的数据,因此它不能用于敏感数据的储存。 +storages无法像Web开发中LocalStorage一样提供根据域名独立的存储,因为脚本的路径随时可能改变。 + +## storages.create(name) +* `name` {string} 本地存储名称 + +创建一个本地存储并返回一个`Storage`对象。不同名称的本地存储的数据是隔开的,而相同名称的本地存储的数据是共享的。 + +例如在一个脚本中,创建名称为ABC的存储并存入a=123: +``` +var storage = storages.create("ABC"); +storage.put("a", 123); +``` +而在另一个脚本中是可以获取到ABC以及a的值的: +``` +var storage = storages.create("ABC"); +log("a = " + storage.get("a")); +``` + +因此,本地存储的名称比较重要,尽量使用含有域名、作者邮箱等唯一信息的名称来避免冲突,例如: +``` +var storage = storages.create("2732014414@qq.com:ABC"); +``` + +## storages.remove(name) +* `name` {string} 本地存储名称 + +删除一个本地存储以及他的全部数据。如果该存储不存在,返回false;否则返回true。 + +# Storages + +## Storage.get(key[, defaultValue]) +* `key` {string} 键值 +* `defaultValue` {any} 可选,默认值 + +从本地存储中取出键值为key的数据并返回。 + +如果该存储中不包含该数据,这时若指定了默认值参数则返回默认值,否则返回undefined。 + +返回的数据可能是任意数据类型,这取决于使用`Storage.put`保存该键值的数据时的数据类型。 + +## Storage.put(key, value) +* `key` {string} 键值 +* `value` {any} 值 + +把值value保存到本地存储中。value可以是undefined以外的任意数据类型。如果value为undefined则抛出TypeError。 + +存储的过程实际上是使用JSON.stringify把value转换为字符串再保存,因此value必须是可JSON化的才能被接受。 + +## Storage.remove(key) +* `key` {string} 键值 + +移除键值为key的数据。不返回任何值。 + +## Storage.contains(key) +* `key` {string} 键值 + +返回该本地存储是否包含键值为key的数据。是则返回true,否则返回false。 + +## Storage.clear() + +移除该本地存储的所有数据。不返回任何值。 \ No newline at end of file diff --git a/AutoJs-Docs/api/threads.md b/AutoJs-Docs/api/threads.md new file mode 100644 index 000000000..2c07c1f9d --- /dev/null +++ b/AutoJs-Docs/api/threads.md @@ -0,0 +1,388 @@ +# Threads + +> Stability: 1 - Experiment + +threads模块提供了多线程支持,可以启动新线程来运行脚本。 + +脚本主线程会等待所有子线程执行完成后才停止执行,因此如果子线程中有死循环,请在必要的时候调用`exit()`来直接停止脚本或`threads.shutDownAll()`来停止所有子线程。 + +通过`threads.start()`启动的所有线程会在脚本被强制停止时自动停止。 + +由于JavaScript自身没有多线程的支持,因此您可能会遇到意料之外的问题。 + +## threads.start(action) +* `action` {Function} 要在新线程执行的函数 +* 返回 [Thread](#threads_thread) + +启动一个新线程并执行action。 + +例如: +``` +threads.start(function(){ + //在新线程执行的代码 + while(true){ + log("子线程"); + } +}); +while(true){ + log("脚本主线程"); +} +``` + +通过该函数返回的[Thread](#threads_thread)对象可以获取该线程的状态,控制该线程的运行中。例如: +``` +var thread = threads.start(function(){ + while(true){ + log("子线程"); + } +}); +//停止线程执行 +thread.interrupt(); +``` + +更多信息参见[Thread](#threads_thread)。 + +## threads.shutDownAll() + +停止所有通过`threads.start()`启动的子线程。 + +## threads.currentThread() +* 返回 [Thread](#threads_thread) + +返回当前线程。 + +## threads.disposable() +* 返回 [Disposable](#threads_disposable) + +新建一个Disposable对象,用于等待另一个线程的某个一次性结果。更多信息参见[线程通信](#threads_线程通信)以及[Disposable](#threads_disposable)。 + +## threads.atomic([initialValue]) +* `initialValue` {number} 初始整数值,默认为0 +* 返回[AtomicLong](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicLong.html) + +新建一个整数原子变量。更多信息参见[线程安全](#threads_线程安全)以及[AtomicLong](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicLong.html)。 + +## threads.lock() +* 返回[ReentrantLock](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html) + +新建一个可重入锁。更多信息参见[线程安全](#threads_线程安全)以及[ReentrantLock](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html)。 + +# Thread + +线程对象,`threads.start()`返回的对象,用于获取和控制线程的状态,与其他线程交互等。 + +Thread对象提供了和timers模块一样的API,例如`setTimeout()`, `setInterval()`等,用于在该线程执行相应的定时回调,从而使线程之间可以直接交互。例如: + +``` +var thread = threads.start(function(){ + //在子线程执行的定时器 + setInterval(function(){ + log("子线程:" + threads.currentThread()); + }, 1000); +}); + +log("当前线程为主线程:" + threads.currentThread()); + +//等待子线程启动 +thread.waitFor(); +//在子线程执行的定时器 +thread.setTimeout(function(){ + //这段代码会在子线程执行 + log("当前线程为子线程:" + threads.currentThread()); +}, 2000); + +sleep(30 * 1000); +thread.interrupt(); +``` + +## Thread.interrupt() + +中断线程运行。 + +## Thread.join([timeout]) +* `timeout` {number} 等待时间,单位毫秒 + +等待线程执行完成。如果timeout为0,则会一直等待直至该线程执行完成;否则最多等待timeout毫秒的时间。 + +例如: +``` +var sum = 0; +//启动子线程计算1加到10000 +var thread = threads.start(function(){ + for(var i = 0; i < 10000; i++){ + sum += i; + } +}); +//等待该线程完成 +thread.join(); +toast("sum = " + sum); +``` + +## isAlive() +* 返回 {boolean} + +返回线程是否存活。如果线程仍未开始或已经结束,返回`false`; 如果线程已经开始或者正在运行中,返回`true`。 + +## waitFor() + +等待线程开始执行。调用`threads.start()`以后线程仍然需要一定时间才能开始执行,因此调用此函数会等待线程开始执行;如果线程已经处于执行状态则立即返回。 + +``` +var thread = threads.start(function(){ + //do something +}); +thread.waitFor(); +thread.setTimeout(function(){ + //do something +}, 1000); +``` + +## Thread.setTimeout(callback, delay\[, ...args\]) + +参见[timers.setTimeout()](timers.html#timers_settimeout_callback_delay_args)。 + +区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出`IllegalStateException`。 + +``` +log("当前线程(主线程):" + threads.currentThread()); + +var thread = threads.start(function(){ + //设置一个空的定时来保持线程的运行状态 + setInterval(function(){}, 1000); +}); + +sleep(1000); +thread.setTimeout(function(){ + log("当前线程(子线程):" + threads.currentThread()); + exit(); +}, 1000); +``` + +## Thread.setInterval(callback, delay\[, ...args\]) + +参见[timers.setInterval()](timers.html#timers_setinterval_callback_delay_args)。 + +区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出`IllegalStateException`。 + + +## Thread.setImmediate(callback[, ...args]) + +参见[timers.setImmediate()](timers.html#timers_setimmediate_callback_delay_args)。 + +区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出`IllegalStateException`。 + +## Thread.clearInterval(id) + +参见[timers.clearInterval()](timers.html#timers_clearinterval_id)。 + +区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出`IllegalStateException`。 + +## Thread.clearTimeout(id) + +参见[timers.clearTimeout()](timers.html#timers_cleartimeout_id)。 + +区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出`IllegalStateException`。 + +## Thread.clearImmediate(id) + +参见[timers.clearImmediate()](timers.html#timers_clearimmediate_id)。 + +区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出`IllegalStateException`。 + +# 线程安全 + +线程安全问题是一个相对专业的编程问题,本章节只提供给有需要的用户。 + +引用维基百科的解释: +> 线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。 + +在Auto.js中,线程间变量在符合JavaScript变量作用域规则的前提下是共享的,例如全局变量在所有线程都能访问,并且保证他们在所有线程的可见性。但是,不保证任何操作的原子性。例如经典的自增"i++"将不是原子性操作。 + +Rhino和Auto.js提供了一些简单的设施来解决简单的线程安全问题,如锁`threads.lock()`, 函数同步锁`sync()`, 整数原子变量`threads.atomic()`等。 + +例如,对于多线程共享下的整数的自增操作(自增操作会导致问题,是因为自增操作实际上为`i = i + 1`,也就是先读取i的值, 把他加1, 再赋值给i, 如果两个线程同时进行自增操作,可能出现i的值只增加了1的情况),应该使用`threads.atomic()`函数来新建一个整数原子变量,或者使用锁`threads.lock()`来保证操作的原子性,或者用`sync()`来增加同步锁。 + +线程不安全的代码如下: +``` +var i = 0; +threads.start(function(){ + while(true){ + log(i++); + } +}); +while(true){ + log(i++); +} +``` + +此段代码运行后打开日志,可以看到日志中有重复的值出现。 + +使用`threads.atomic()`的线程安全的代码如下: + +``` +//atomic返回的对象保证了自增的原子性 +var i = threads.atomic(); +threads.start(function(){ + while(true){ + log(i.getAndIncrement()); + } +}); +while(true){ + log(i.getAndIncrement()); +} +``` + +或者: + +``` +//锁保证了操作的原子性 +var lock = threads.lock(); +var i = 0; +threads.start(function(){ + while(true){ + lock.lock(); + log(i++); + lock.unlock(); + } +}); +while(true){ + lock.lock(); + log(i++); + lock.unlock(); +} +``` + +或者: +``` +//sync函数会把里面的函数加上同步锁,使得在同一时刻最多只能有一个线程执行这个函数 +var i = 0; +var getAndIncrement = sync(function(){ + return i++; +}); +threads.start(function(){ + while(true){ + log(getAndIncrement()); + } +}); +while(true){ + log(getAndIncrement()); +} +``` + +另外,数组Array不是线程安全的,如果有这种复杂的需求,请用Android和Java相关API来实现。例如`CopyOnWriteList`, `Vector`等都是代替数组的线程安全的类,用于不同的场景。例如: +``` +var nums = new java.util.Vector(); +nums.add(123); +nums.add(456); +toast("长度为" + nums.size()); +toast("第一个元素为" + nums.get(0)); +``` +但很明显的是,这些类不像数组那样简便易用,也不能使用诸如`slice()`之类的方便的函数。在未来可能会加入线程安全的数组来解决这个问题。当然您也可以为每个数组的操作加锁来解决线程安全问题: +``` +var nums = []; +var numsLock = threads.lock(); +threads.start(function(){ + //向数组添加元素123 + numsLock.lock(); + nums.push(123); + log("线程: %s, 数组: %s", threads.currentThread(), nums); + numsLock.unlock(); +}); + +threads.start(function(){ + //向数组添加元素456 + numsLock.lock(); + nums.push(456); + log("线程: %s, 数组: %s", threads.currentThread(), nums); + numsLock.unlock(); +}); + +//删除数组最后一个元素 +numsLock.lock(); +nums.pop(); +log("线程: %s, 数组: %s", threads.currentThread(), nums); +numsLock.unlock(); +``` + +## sync(func) +* `func` {Function} 函数 +* 返回 {Function} + +给函数func加上同步锁并作为一个新函数返回。 + +``` +var i = 0; +function add(x){ + i += x; +} + +var syncAdd = sync(add); +syncAdd(10); +toast(i); +``` + +# 线程通信 + +Auto.js提供了一些简单的设施来支持简单的线程通信。`threads.disposable()`用于一个线程等待另一个线程的(一次性)结果,同时`Lock.newCondition()`提供了Condition对象用于一般的线程通信(await, signal)。另外,`events`模块也可以用于线程通信,通过指定`EventEmiiter`的回调执行的线程来实现。 + +使用`threads.disposable()`可以简单地等待和获取某个线程的执行结果。例如要等待某个线程计算"1+.....+10000": +``` +var sum = threads.disposable(); +//启动子线程计算 +threads.start(function(){ + var s = 0; + //从1加到10000 + for(var i = 1; i <= 10000; i++){ + s += i; + } + //通知主线程接收结果 + sum.setAndNotify(s); +}); +//blockedGet()用于等待结果 +toast("sum = " + sum.blockedGet()); +``` + +如果上述代码用`Condition`实现: +``` +//新建一个锁 +var lock = threads.lock(); +//新建一个条件,即"计算完成" +var complete = lock.newCondition(); +var sum = 0; +threads.start(function(){ + //从1加到10000 + for(var i = 1; i <= 10000; i++){ + sum += i; + } + //通知主线程接收结果 + lock.lock(); + complete.signal(); + lock.unlock(); +}); +//等待计算完成 +lock.lock(); +complete.await(); +lock.unlock(); +//打印结果 +toast("sum = " + sum); +``` + +如果上诉代码用`events`模块实现: +``` +//新建一个emitter, 并指定回调执行的线程为当前线程 +var sum = events.emitter(threads.currentThread()); +threads.start(function(){ + var s = 0; + //从1加到10000 + for(var i = 1; i <= 10000; i++){ + s += i; + } + //发送事件result通知主线程接收结果 + sum.emit('result', s); +}); +sum.on('result', function(s){ + toastLog("sum = " + s + ", 当前线程: " + threads.currentThread()); +}); +``` + +有关线程的其他问题,例如生产者消费者等问题,请用Java相关方法解决,例如`java.util.concurrent.BlockingQueue`。 \ No newline at end of file diff --git a/AutoJs-Docs/api/timers.md b/AutoJs-Docs/api/timers.md new file mode 100644 index 000000000..d161c985e --- /dev/null +++ b/AutoJs-Docs/api/timers.md @@ -0,0 +1,94 @@ +# Timers + +> Stability: 2 - Stable + +timers 模块暴露了一个全局的 API,用于在某个未来时间段调用调度函数。 因为定时器函数是全局的,所以使用该 API 无需调用 timers.*** + +Auto.js 中的计时器函数实现了与 Web 浏览器提供的定时器类似的 API,除了它使用了一个不同的内部实现,它是基于 Android Looper-Handler消息循环机制构建的。其实现机制与Node.js比较相似。 + +例如,要在5秒后发出消息"hello": +``` +setTimeout(function(){ + toast("hello") +}, 5000); +``` + +需要注意的是,这些定时器仍然是单线程的。如果脚本主体有耗时操作或死循环,则设定的定时器不能被及时执行,例如: +``` +setTimeout(function(){ + //这里的语句会在15秒后执行而不是5秒后 + toast("hello") +}, 5000); +//暂停10秒 +sleep(10000); +``` + +再如: +``` +setTimeout(function(){ + //这里的语句永远不会被执行 + toast("hello") +}, 5000); +//死循环 +while(true); +``` + +## setInterval(callback, delay\[, ...args\]) +* `callback` {Function} 当定时器到点时要调用的函数。 +* `delay` {number} 调用 callback 之前要等待的毫秒数。 +* `...args` {any} 当调用 callback 时要传入的可选参数。 + +预定每隔 delay 毫秒重复执行的 callback。 返回一个用于 clearInterval() 的 id。 + +当 delay 小于 0 时,delay 会被设为 0。 + +## setTimeout(callback, delay\[, ...args\]) +* `callback` {Function} 当定时器到点时要调用的函数。 +* `delay` {number} 调用 callback 之前要等待的毫秒数。 +* `...args` {any} 当调用 callback 时要传入的可选参数。 + +预定在 delay 毫秒之后执行的单次 callback。 返回一个用于 clearTimeout() 的 id。 + +callback 可能不会精确地在 delay 毫秒被调用。 Auto.js 不能保证回调被触发的确切时间,也不能保证它们的顺序。 回调会在尽可能接近所指定的时间上调用。 + +当 delay 小于 0 时,delay 会被设为 0。 + + + +## setImmediate(callback[, ...args]) +* `callback` {Function} 在Looper循环的当前回合结束时要调用的函数。 +* `...args` {any} 当调用 callback 时要传入的可选参数。 + +预定立即执行的 callback,它是在 I/O 事件的回调之后被触发。 返回一个用于 clearImmediate() 的 id。 + +当多次调用 setImmediate() 时,callback 函数会按照它们被创建的顺序依次执行。 每次事件循环迭代都会处理整个回调队列。 如果一个立即定时器是被一个正在执行的回调排入队列的,则该定时器直到下一次事件循环迭代才会被触发。 + +setImmediate()、setInterval() 和 setTimeout() 方法每次都会返回表示预定的计时器的id。 它们可用于取消定时器并防止触发。 + + +## clearInterval(id) +* `id` {number} 一个 setInterval() 返回的 id。 + +取消一个由 setInterval() 创建的循环定时任务。 + +例如: +``` +//每5秒就发出一次hello +var id = setInterval(function(){ + toast("hello"); +}, 5000); +//1分钟后取消循环 +setTimeout(function(){ + clearInterval(id); +}, 60 * 1000); +``` + +## clearTimeout(id) +* `id` {number} 一个 setTimeout() 返回的 id。 + +取消一个由 setTimeout() 创建的定时任务。 + +## clearImmediate(id) +* `id` {number} 一个 setImmediate() 返回的 id。 + +取消一个由 setImmediate() 创建的 Immediate 对象。 diff --git a/AutoJs-Docs/api/ui.md b/AutoJs-Docs/api/ui.md new file mode 100644 index 000000000..01ebc7cd1 --- /dev/null +++ b/AutoJs-Docs/api/ui.md @@ -0,0 +1,834 @@ +# 用户界面: UI + +ui模块提供了编写用户界面的支持。 + +带有ui的脚本的的最前面必须使用`"ui";`指定ui模式,否则脚本将不会以ui模式运行。正确示范: + +``` +"ui"; + +//脚本的其他代码 +``` + +字符串"ui"的前面可以有注释、空行和空格**[v4.1.0新增]**,但是不能有其他代码。 + +界面是由视图(View)组成的。View分成两种,控件(Widget)和布局(Layout)。控件(Widget)用来具体显示文字、图片、网页等,比如文本控件(text)用来显示文字,按钮控件(button)则可以显示一个按钮并提供点击效果,图片控件(img)则用来显示来自网络或者文件的图片,除此之外还有输入框控件(input)、进度条控件(progressbar)、单选复选框控件(checkbox)等;布局(Layout)则是装着一个或多个控件的"容器",用于控制在他里面的控件的位置,比如垂直布局(vertical)会把他里面的控件从上往下依次显示(即纵向排列),水平布局(horizontal)则会把他里面的控件从左往右依次显示(即横向排列),以及帧布局(frame),他会把他里面的控件直接在左上角显示,如果有多个控件,后面的控件会重叠在前面的控件上。 + +我们使用xml来编写界面,并通过`ui.layout()`函数指定界面的布局xml。举个例子: + +``` +"ui"; +ui.layout( + +