AutoJs6

- Overview - 综述
- About - 关于文档
- Progress - 文档部署进度
- Changelog - 文档更新日志

- Manual - AutoJs6 使用手册
- Q & A - 疑难解答

- Global - 全局对象
- Automator - 自动化
- AutoJs6 - 本体应用
- App - 通用应用
- Color - 颜色
- Image - 图像
- OCR - 光学字符识别
- Barcode - 条码
- QR Code - 二维码
- Keys - 按键
- Device - 设备
- Storage - 储存
- File - 文件
- Engine - 引擎
- Task - 任务
- Module - 模块
- Plugins - 插件
- Toast - 消息浮动框
- Notice - 消息通知
- Console - 控制台
- Shell
- Shizuku
- Media - 多媒体
- Sensor - 传感器
- Recorder - 记录器
- Timer - 定时器
- Thread - 线程
- Continuation - 协程
- Event - 事件监听
- Dialog - 对话框
- Floaty - 悬浮窗
- Canvas - 画布
- UI - 用户界面
- Web - 万维网
- HTTP
- Base64
- Crypto - 密文
- Internationalization - 国际化
- Standardization - 标准化
- E4X

- UiSelector - 选择器
- UiObject - 控件节点
- UiObjectCollection - 控件集合
- UiObjectActions - 控件节点行为
- WebSocket
- EventEmitter - 事件发射器
- ImageWrapper - 包装图像类
- App - 应用枚举类
- Color - 颜色类
- Version - 版本工具类
- Polyfill - 代码填泥
- Arrayx - Array 扩展
- Numberx - Number 扩展
- Mathx - Math 扩展

- Exceptions - 异常
- Intent - 意图
- Runtime - 运行时
- Context - 上下文
- Activity - 活动

- Scripting Java - 脚本化 Java
- Android API Level - 安卓 API 级别
- Color Table - 颜色列表

- Glossaries - 术语
- HttpHeader - HTTP 标头
- HttpRequestMethods - HTTP 请求方法
- MimeType - MIME 类型
- NotificationChannel - 通知渠道

- Data Types - 数据类型
- Omnipotent Types - 全能类型
- Storage - 存储类
- AndroidBundle
- AndroidRect
- CryptoCipherOptions
- CryptoKey
- CryptoKeyPair
- ConsoleBuildOptions
- HttpRequestBuilderOptions
- HttpRequestHeaders
- HttpResponseBody
- HttpResponseHeaders
- HttpResponse
- InjectableWebClient
- InjectableWebView
- NoticeOptions
- NoticeChannelOptions
- NoticePresetConfiguration
- NoticeBuilder
- Okhttp3HttpUrl
- OcrOptions
- Okhttp3Request
- OpencvPoint
- OpencvRect
- OpencvSize

- GitHub - 应用项目地址
- GitHub - 文档项目地址

# 综述 (Overview)#

AutoJs6: 安卓平台 JavaScript 自动化工具.

- 脚本语言: JavaScript
- 脚本引擎: Rhino
- 支持特性: ES5 (全部), ES6 (部分)

扩展阅读:

- 了解 JavaScript
  - MDN - JavaScript 基础
  - JavaScript.info - JavaScript 教程
- 查看 Rhino 引擎兼容性列表
  - Rhino ES2015 Support
- 使用 PC (个人计算机) 开发
  - AutoJs6 VSCode Extension
- 使用 Node.js 开发
  - Auto.js Pro
- 使用 TypeScript 开发
  - Auto.js DevTools

阅读文档:

- 宽屏设备网页阅读
  - 点击左侧边栏条目 - 阅读相关章节
- 移动设备网页阅读
  - 点击左下方抽屉按钮 - 展开侧边栏并点击条目 - 阅读相关章节
- AutoJs6 应用内阅读
  - 点击首页 "文档" 标签 - 点击条目 - 阅读相关章节
  - 点击首页右上方 "搜索" 图标 - 在当前页面检索内容
  - 文档页面左上方的导航链接可实现页面跳转:
    - 点击 "索引" - 跳转至章节索引页面
    - 点击 "查看全部" - 所有章节内容在同一页面列出
  - 阅读文档时, "文档" 标签可作为快捷按钮使用:
    - 点击 "文档" 标签 - 返回至当前页面顶部
    - 长按 "文档" 标签 - 跳转至章节索引页面
- 启动器快速启动阅读
  - AutoJs6 设置页面 - 启动器快捷方式 - 点击 "文档" 图标
  - 将 "文档" 快捷方式添加到启动器 (俗称 "桌面")
  - 在启动器点击 "文档" 图标即可阅读文档
  - 部分高版本安卓系统支持长按 AutoJs6 应用图标直接激活快捷方式或拖放添加到启动器

> 注: 初次阅读文档或首次使用 AutoJs6 建议从 关于文档 开始.

# 关于文档 (About)#

AutoJs6 文档, 包含模块 API 使用方法及用例.
项目复刻 (Fork) 自 hyb1996/AutoJs-Docs (GitHub).
项目地址: SuperMonster003/AutoJs6-Documentation (GitHub).

## 文档阅读示例#

### 基础#

#### device.height#

device 表示全局对象 (这里同时也是一个模块).
".height" 表示访问 device 对象的 height 成员变量.
如 console.log(device.height) 表示在控制台打印当前设备的高度数值.

#### colors.rgb(red, green, blue)#

colors 与 device 类似, 表示全局对象.
"rgb" 表示方法名称, ".rgb()" 表示调用 colors 的 rgb 方法, 括号内的 red 等表示方法参数.
如 console.log(colors.rgb(255, 128, 64)) 表示在控制台打印一个 RGB 分别为 255, 128 和 64 的颜色数值.

> 注: 绝大多数情况, 文档不对 "函数" 与 "方法" 做明确区分.

### 临时作用域对象#

通常每个章节都以某个对象作为主题.

例如上述 `colors.rgb(red, green, blue)` 位于 Color - 颜色 这个章节.
其中 colors 称为此章节的 "临时作用域对象",
它可能是一个对象, 函数, 甚至 "类", 在文档中使用 `橙色粗体` 表示.

列举其后续的相关方法及属性时, 将不再重复书写对象本身:

colors

[m] rgb

rgb(red, green, blue)

... ...

上述 `rgb` 表示 `colors.rgb`.

### 参数类型#

#### colors.rgb(red, green, blue)#

- red { number }
- green { number }
- blue { number }

参数后的 "{}" 内包含其类型.
上述示例表示需要传入三个 number 类型的参数.
如 colors.rgb(255, 128, 64) 合法, 而 colors.rgb("abc", 128, 64) 将可能导致非预期结果或出现异常.

> 注: 点击类型对应的超链接 (如有) 可跳转至类型详情页面.

### 返回值类型#

#### colors.rgb(red, green, blue)#

- red { number }
- green { number }
- blue { number }
- returns { number }

returns 后的 "{}" 内包含返回值类型.
上述示例表示 colors.rgb 方法调用后将返回 number 类型数据.

### 属性类型#

#### colors.RED#

- { number }

属性类型包裹在一对花括号中.
上述示例表示 colors 的 RED 属性是 number 类型数据.

对象字面量形式的类型则用一对双花括号表示:

- properties { { name: string; age: number }}

多行形式:

- properties {{
  - name: string;
  - age: number;
  - laugh(decibel?: number);
- }}

一个符合上述示例期望的变量:

    let o = { name: "David", age: 13 };

可存取的属性在读取时如果有非 undefined 默认值, 则以一对方括号表示:

- [ `1200` ] { number }

上述示例表示一个默认值为 1200 的可存取属性.

以一对双方括号表示常量:

- [[ 0.5 ]] { number }

上述示例表示一个值为 0.5 的常量属性.

### 方法签名#

形如上述 返回值类型 小节的示例,
包含 [ 方法名称 + 参数类型 + 返回值类型 ] 的标志符, 称为 "方法签名".

> 注: 上述 "方法签名" 定义只用于辅助读者对文档的理解, 并不保证名词解释的合理性.

### 方法描述#

#### colors.rgb(red, green, blue)#

- red - R (红色) 通道数值 [ A ]
- green - G (绿色) 通道数值 [ A ]
- blue - B (蓝色) 通道数值 [ A ]
- @return - 颜色数值 [ B ]

获取 R/G/B 通道组合后的颜色数值. [ C ]

    [ D ]
    colors.rgb(255, 128, 64); // -32704
    colors.rgb(255, 128, 64) === 0xFFFF8040 - Math.pow(2, 32); // true
    colors.rgb(255, 128, 64) === colors.toInt("#FFFF8040"); // true
    colors.rgb(255, 128, 64) === colors.toInt("#FF8040"); // true

上述示例包含的字母标注含义:

- [ A ] - 参数描述
- [ B ] - 方法返回值描述
- [ C ] - 方法描述
- [ D ] - 方法使用示例

### 可变参数#

#### files.join(parent, ...child)#

上述示例的 child 参数是 "可变参数", 也称为 "可变长参数" 或 "变长参数".
可变参数可传入任意个 (包括 0 个) 参数:

    let p = files.getSdcardPath();
    files.join(p); /* 0 个可变参数 */
    files.join(p, 'a'); /* 1 个可变参数 */
    files.join(p, 'a', 'b', 'c', 'd'); /* 4 个可变参数 */

文档采用 JSDoc 标准标注可变参数, 需额外注意 JSDoc 的尾数组标识代表容器, 用于容纳展开后的参数:

    /**
     * @param {number} x
     * @param {number} y
     * @param {...number[]} others
     */
    function sum(x, y, others) {
        /* ... */
    }

上述示例 others 参数为可变参数, 其中 "...number[]" 代表 others 期望的参数类型为 number, 而非 number[], 最后的 "[]" 代表 "..." 的容器, "..." 与 "[]" 是配对出现的.

    /**
     * @param {number} x
     * @param {number} y
     * @param {...number[][]} others
     */
    function sum(x, y, others) {
        /* ... */
    }

上述示例 others 期望的参数类型为 number[], 而非 number[][], 同样最后的 "[]" 代表 "..." 的容器.

    /**
     * @param {number} x
     * @param {number} y
     * @param {...number} others
     */
    function sum(x, y, others) {
        /* ... */
    }

上述示例 others 的参数类型标识方法 "...number" 也是合法的, 它其实是 "...number[]" 的省略形式.
文档为了避免歧义, 将全部采用完整写法.

作为强调, "...(SomeType)[]" 这样的可变参数表示方法, 需要把 "..." 和 "[]" 视为一个整体, 中间部分才是期望的参数类型.

### 可选参数#

#### device.vibrate(text, delay?)#

上述示例的 delay 参数是可选的 (以 "?" 标注).
因此以下调用方式均被支持:

    device.vibrate("hello", 2e3); /* 两秒钟延迟. */
    device.vibrate("hello"); /* 无延迟. */

可选参数描述时会以 "[]" 标注:

- [ delay ] { number }

如果可选参数包含默认值, 则会以 "=" 标注:

- [ delay = 0 ] { number }

详见下述 参数默认值

### 参数默认值#

#### device.vibrate(text, delay?)#

- text { string } - 需转换为摩斯码的文本
- [ delay = 0 ] { number } - 振动延迟
- returns { void }

上述示例的 delay 参数是可选的 (以 "?" 标注) 且包含默认值 (以 "=" 标注).
因此以下两种调用方式等效:

    device.vibrate("hello");
    device.vibrate("hello", 0);

> 注: 上述示例的方法签名 (含默认值标注) 在 TypeScript 中并不合法, 此类签名仅限在文档中使用.

> 注: 以 "=" 标注的参数一定是可选的, 此时参数的 "?" 标注可能被省略, 尤其在重载签名拆写的情况下.
> 详情参阅下文的 "方法重载".

### 方法重载#

`Overload 1/17`

#### pickup(selector, compass, resultType)#

`Overload 2/17`

#### pickup(selector, compass)#

`Overload 3/17`

#### pickup(selector, resultType)#

... ...

`Overload 16/17`

#### pickup(root, selector)#

`Overload 17/17`

#### pickup()#

包含 "Overload m/n" 标签的方法, 表示重载方法的序数及总量.
如 "Overload 2/3" 表示当前方法签名描述第 2 个重载方法, 总计 3 个,
而 "Overload 5-6/17" 表示当前方法签名涵盖第 5 及 第 6 个重载方法, 总计 17 个.

重载方法可被简化:

    /* 拆写. */
    device.vibrate(text)
    device.vibrate(text, delay)

    /* 合写 (简化). */
    device.vibrate(text, delay?)

    /* 可选参数通常会标注默认值. */
    device.vibrate(text, delay?)
    · [ delay = 0 ] { number }

    /* 即使没有 "?" 标注 (针对拆写). */
    device.vibrate(text, delay)
    · [ delay = 0 ] { number }

多数情况下, 文档采用拆写的方式描述重载方法.

### 方法全局化#

`Global`

#### images.requestScreenCapture(landscape)#

包含 "Global" 标签的方法, 表示支持全局化使用, 可省略模块对象调用.
因此以下两种调用方式等效:

    images.requestScreenCapture(false);
    requestScreenCapture(false);

### 方法标签#

用于简便表示方法的特性:

- `Global`: 方法全局化 (可省略模块对象直接调用).
- `Overload 2/3`: 方法重载 [ 第 2 个, 共 3 个 ].
- `Non-UI`: 方法不能在 UI 模式下调用.
- `6.2.0`: 方法对 AutoJs6 的版本要求 [ 不低于 6.2.0 ].
- `[6.2.0]`: 与原同名方法相比, 方法的功能, 结果, 签名或使用方式发生变更的起始版本.
- `API>=29`: 方法对 API 级别 的要求 [ 不低于 29 ], 当不满足时不抛出异常.
- `API>=29!`: 方法对 API 级别 的要求 [ 不低于 29 ], 当不满足时将抛出异常.
- `A11Y`: 方法依赖无障碍服务.
- `A11Y?`: 方法可能会依赖无障碍服务.
- `Async`: 异步执行的方法.
- `Async?`: 可能异步执行的方法 (通过参数控制).
- `Getter`: 仅取值属性, 即使用 Getter 定义的对象属性.
- `Setter`: 仅存值属性, 即使用 Setter 定义的对象属性.
- `Getter/Setter`: 可存值且可取值属性, 即同时使用 Setter 及 Getter 定义的对象属性.
- `Enum`: 枚举类.
- `CONSTANT`: 常量.
- `READONLY`: 只读属性或方法.
- `DEPRECATED`: 已弃用的属性或方法. 表示不推荐使用, 通常会有替代属性或替代方法.
- `ABANDONED`: 已废弃的属性或方法. 表示不再提供功能支持, 使用后功能将无效.
- `xProto`: 针对原型的内置对象扩展.
- `xObject`: 针对对象的内置对象扩展.
- `xAlias`: 内置对象扩展时使用不同的方法或属性名称 (别名).

### 对象标签#

用于简便表示对象的属性:

- [m]: 普通对象方法或类静态成员方法.
  - 例如在 `images` 作为 临时作用域对象 时:
  - `[m] captureScreen` 代表 `images.captureScreen` 方法.
- [m+]: 具有扩展属性的对象方法.
  - 如 auto 本身是一个方法 (或称函数), waitFor 是 auto 的一个扩展方法.
  - 以下两种调用方式均可用: `auto()` 及 `auto.waitFor()`.
- [p]: 普通对象属性或类静态成员属性或接口变量属性.
  - 例如在 `device` 作为 临时作用域对象 时:
  - `[p] height` 代表 `device.height` 属性, 而非方法.
  - 此标签对 [ Getter / Setter / "类" 属性 / 对象属性 / 方法扩展属性 ] 等不作区分.
- [p+]: 具有扩展属性的对象属性.
  - 如 autojs 是一个对象, version 是 autojs 的扩展属性,
  - 支持 `autojs.version.xxx` 这样的访问方式,
  - 因此 version 属性将被标记为 `[p+]`.
- [I]: Java 接口.
- [C]: Java 类或 JavaScript 构造函数.
- [c]: Java 类的构造方法.
- [m!]: 抽象方法 (针对接口及抽象类).
- [m=]: 包含默认实现的抽象方法 (针对接口).
- [m#]: 类的实例成员方法.
  - 类的静态成员方法用 [m] 标签标记.
  - 例如对于类 `B`, 它有一个实例 `b` (可能通过 `new B()` 等方式获得),
  - `[m#] foo` 和 `[m] bar` 的调用方式分别为
  - `b.foo()` 和 `B.bar()`.
- [p#]: 类的实例成员属性.
  - 类的静态成员属性依然用 [p] 标签标记.
  - 例如对于类 `F`, 它有一个实例 `f` (可能通过 `new F()` 等方式获得),
  - `[p#] foo` 和 `[p] bar` 的调用方式分别为
  - `f.foo` 和 `F.bar`.
- [@]: 代表 临时作用域对象 自身.
  - 例如在同一个章节中
    - `[@] apple` [1]
    - `apple(c)` [2]
    - `[m] getColor` [3]
    - `getColor()` [4]
    - `[@] banana` [5]
    - `[m] banana` [6]
    - `banana(x)` [7]
    - `banana(x, y)` [8]
  - 这个章节有两个 临时作用域对象, apple 和 banana, 对应 `[1]` 和 `[5]`.
  - `[2]` 代表 apple 自身可被调用, 且调用方式为 `apple(c)`, 其中 "c" 为参数.
  - `[3]` 代表 apple 的一个方法, 名称为 "getColor",
  - 由 `[4]` 得知, 其调用方式为 `apple.getColor()`.
  - 注意 `[6]` 与 `[2]` 不同:
  - `[6]` 代表 banana 的一个方法, 名称为 "banana",
  - 由 `[7]` 和 `[8]` 得知, 其调用方式有两种,
  - `banana.banana(x)` 和 `banana.banana(x, y)`.

### 成员访问#

成员访问用 "." 表示调用关系, 包括 "类" 静态成员访问, 对象成员访问等.
而实例成员访问则需要 "类" 的实例才能访问, 用 "#" 表示调用关系.
例如 JavaScript 的 Number 本身是一个 "类", 可用的成员访问方式如下:

    Number(2); /* 作为普通函数使用, 无成员访问. */
    Number.EPSILON; /* "类" 静态成员访问, 用 "Number.EPSILON" 标识, 标签为 "[p]". */
    new Number(2); /* 创建 "类" 实例, 无成员访问. */
    new Number(2).toFixed(0); /* 实例成员访问, 用 "Number#toFixed(number)" 标识, 标签为 "[m#]". */

实例成员访问示例:

UiObject

[m#] bounds()

    /* 正确访问示例 */

    let w = pickup(/.+/); /* w 是 UiObject 的实例. */
    if (w !== null) {
        console.log(w.bounds()); /* 访问 UiObject 实例的 bounds 方法. */
    }

    /* 错误访问示例 */

    importClass(org.autojs.autojs.core.automator.UiObject);
    console.log(UiObject.bounds()); /* 访问的是类 UiObject 的静态方法 bounds. */

### 模板参数#

#### foo.bar(a, b)#

- a { T }
- b { number }
- returns { T }
- template { T }

template 标签指示了一个模板参数 `T`, 这个参数可以代表任意一个类型, 如 `string`.

示例 `foo.bar(a, b)` 中, 返回值与参数 `a` 的类型均为 `T`, 因此两者的类型相同.

例如当参数 `a` 传入 `string` 类型时, 返回值也为 `string` 类型:

    typeof foo.bar('hello', 3); // string

> 参阅: 泛型

## 声明#

当前项目 (文档) 及 AutoJs6 (App) 均为二次开发.
相对于 原始 App, 二次开发的 App 中会增加或修改部分模块功能.
相对于 原始文档, 二次开发的文档将进行部分增删或重新编写.
开发者无法保证对 API 的完全理解及文档的无纰漏撰写.
如有任何不当之处, 欢迎提交 Issue 或 PR.

# 疑难解答 (Q & A)#

## AutoJs6#

### AutoJs6 功能简介#

AutoJs6 是 Android 平台支持无障碍服务的 JavaScript 自动化工具.

可用作 JavaScript IDE, 支持 [ 代码补全 / 变量重命名 / 代码格式化 ] 等.

AutoJs6 封装了丰富的 JavaScript 模块, 提供丰富功能, 内置实用工具:

功能

- 图像处理 / 文字识别
- 自动化操作 / 控件操作 / 应用操作
- UI 交互 / 对话框交互 / 悬浮窗控件 / 画布控件
- 多线程编程 / 协程 / 异步编程 / 事件监听
- 文件处理 / 多媒体处理
- 定时任务 / 消息通知
- HTTP 请求
- Shell 语句
- 国际化
- ... ...

工具

- 设备信息 / 传感器信息 / 控件信息
- Base64 编解码 / 密文生成
- 数学运算 / 颜色转换
- ... ...

### AutoJs6 如何使用#

详见 AutoJs6 使用手册 章节.

### AutoJs6 是否免费#

AutoJs6 永久免费, 它是基于开源版本 (Auto.js 4.1.1 alpha2) 二次开发的, 将保持开源免费.

### AutoJs6 目标#

开源版本 (Auto.js 4.1.1 alpha2) 是非常好的学习资料, AutoJs6 之所以存在, 恰恰是因为站在巨人的肩膀上.

AutoJs6 的目标是对开源版本 (Auto.js 4.1.1 alpha2) 进行完善及扩展.

### AutoJs6 特色#

AutoJs6 对以下功能进行了十足的打磨:

- 夜间模式
- 多语言

同时对已有模块进行了精心优化及扩展:

- 颜色 (colors)
- 选择器 (UiSelector)
- 控件节点 (UiObject)
- ... ...

其中尤其具备 AutoJs6 特色的, 当属 pickup 选择器 及 compass 控件罗盘.

关于 AutoJs6 的更多内容, 可参阅 项目更新日志.

## 文档#

### 文档格式不统一#

AutoJs6 文档是在开源版本文档的基础上进行更新和修改的, 目前仅完成部分章节的更新, 未更新的章节依然保留原始文档内容, 因此会存在新旧不同的文档编写格式.
因文档编写需要耗费巨量的时间及精力, 文档更新速度会相对缓慢.
当全部章节完成编写及更新后, 文档将实现格式统一.

### 不支持夜间模式#

使用 AutoJs6 查看文档时, 若开启夜间模式后文档依然是亮色主题, 需检查 WebView (或 Google Chrome 等浏览器) 的版本条件:

- API 级别 29 (安卓 10) [Q] 及以上: 版本不低于 76
- API 级别 28 (安卓 9) [P] 及以下: 版本不低于 105

### 内容难以理解#

对于存在阅读障碍的文档内容, 可尝试暂时略过, 继续阅读后续内容.
当完整阅读一个章节或小节后, 可能对之前略过内容的进一步理解有所帮助.
也可提交反馈至 GitHub 项目页面, 开发者可能会根据提交的反馈适当调整文档内容.

## 图像#

### OCR 特性#

AutoJs6 的 OCR 特性是基于 Google ML Kit 的 文字识别 API 及 Baidu PaddlePaddle 的 Paddle Lite 实现的.

> 注:
> AutoJs6 基于 MLKit 引擎的 OCR 实现源码 参考自 TonyJiangWJ 的 Auto.js 项目.
> AutoJs6 基于 Paddle Lite 引擎的 OCR 实现源码 源自 TonyJiangWJ 的 GitHub PR.

> 参阅: 光学字符识别 (OCR) 模块

### 区域截图#

AutoJs6 不支持区域截图.

可通过 images.captureScreen 截取屏幕后使用 images.clip 等方法做进一步处理.

## 定时任务#

### 定时运行脚本#

脚本右侧菜单 -> 定时任务, 即可定时运行脚本.
需保持 AutoJs6 后台运行, 包括 [ 自启动白名单 / 忽略电池优化 / 忽略后台活动限制 / 系统多任务保留 ] 等.
在设备关屏情况下, 可使用 `device.wakeUp()` 唤醒屏幕.
但 AutoJs6 暂未提供解锁功能, 因此可能需要根据设备自行设计解锁代码.

### 定时任务获取外部参数#

若脚本由 intent (如网络状态变化等特定事件) 触发启动, 可通过 `engines.myEngine().execArgv.intent` 获取 intent, 进而获取外部参数.

## 脚本执行差异#

同样的脚本, 在不同环境 (如设备或系统等) 可能出现执行结果差异, 甚至出现异常而无法正常运行.

### 不同的系统版本#

AutoJs6 可以安装在 `Andoird API 24 (7.0) [N]` 及以上的操作系统.

然而不同操作系统 `API` 是有区别的, 有些 `API` 在某个系统版本之后 (甚至之前) 才能使用.

下面列出几个 AutoJs6 中受系统版本影响的方法或属性:

- notice 模块的渠道相关功能只能在 `Android API 26 (8.0) [O]` 及以上起作用
- device.imei 只能在 `Android API 29 (10) [Q]` 及以下获取到设备 IMEI 值
- UiSelector#imeEnter 只能在 `Android API 30 (11) [R]` 及以上才能起作用
- UiSelector#dragStart 只能在 `Android API 32 (12.1) [S_V2]` 及以上才能起作用
- UiSelector#showTextSuggestions 只能在 `Android API 33 (13) [TIRAMISU]` 及以上才能起作用
- ... ...

### 不同的设备厂商#

因不同设备厂商对操作系统进行了不同程度的定制和修改, 一些 `API` 可能发生变更.

下表列出了部分厂商及操作系统的信息 (排序无先后):

厂商或品牌 | 操作系统
--- | ---
魅族 (MEIZU) | Flyme OS
欧珀 (OPPO / Realme) | ColorOS
小米 (XiaoMi / Redmi / BlackShark) | MIUI
一加 (OnePlus) | 氢OS / Oxygen OS
维沃 (VIVO / IQOO) | Funtouch OS / OriginOS
华为 (Huawei / Honor) | EMUI / HarmonyOS
联想 (Lenovo) | ZUI
酷派 (Coolpad) | CoolOS
卓易 (Droi) | Freeme OS
锤子科技 (Smartisan) | Smartisan OS
中兴 (ZTE / 天机 / 远航 / Axon) | MyOS
努比亚 (Nubia / 红魔) | REDMAGIC OS
Google Pixel | 原生
AVD (安卓虚拟机) | 原生
索尼 (Sony / XPERIA) | 类原生
三星 (Samsung) | 类原生
黑莓 (BlackBerry) | 类原生
LG | 类原生
摩托罗拉 (Motorola) | 类原生
诺基亚 (Nokia) | 类原生 (仅限部分机型)
华硕 (ASUS / ZenFone / ROG Phone) | 类原生
宏达电 (HTC) | 类原生

由此可见, 想要在众多不同的操作系统中实现完全无差别且无异常的脚本执行效果, 难度是巨大的.

往往需要在实际操作系统中进行功能测试并编写额外的兼容代码, 甚至可能需要查询定制操作系统的开放 `API` 文档 (如果有的话).

> 注: 表格中的信息可能与实际存在出入, 仅供参考.

### 不同的 Auto.js 应用#

不同的 Auto.js 应用对 [ JavaScript 封装模块 / Java 包名及类名 ] 等进行了不同程度的 [ 增添 / 修改 / 删减 ], 因此同样的脚本很难在不同 Auto.js 应用上达到同样的运行效果, 甚至出现无法运行的情况.

有以下几种可能的解决方案:

- 继续使用之前编写脚本代码的 Auto.js 应用
- 修改脚本代码以适应新 Auto.js 应用
- 在脚本代码中加入不同 Auto.js 应用的检测, 在对应分支编写兼容代码

### 不同的 AutoJs6 版本#

随着 AutoJs6 版本的更新, 一些 `API` 可能出现 [ 新增 / 修改 / 废弃 / 移除 ] 等操作.

当升级 AutoJs6 后, 某个或某些 `API` 出现异常时, 可查询应用文档并定位到相关章节, 根据文档的提示排查并解决上述问题.

如问题仍未解决, 可在项目的 GitHub 议题页面提交 反馈.

## 打包应用#

AutoJs6 打包功能尚不完善, 打包应用与 AutoJs6 主应用可能有较大的功能和界面差异.

AutoJs6 开发者暂不考虑参与打包功能相关的开发工作, 目前以 LZX284 为主要贡献者进行打包功能的开发及维护, 后续将继续由其他开发者贡献相关代码.

### 图片等资源共同打包及多脚本打包#

上述需求需使用 "项目" 功能.

点击 AutoJs6 主页面 "+" 图标, 选择项目, 填写信息后可新建一个项目.
项目支持存放多个 [ 脚本 / 模块 / 资源文件 ].
项目工具栏的 APK 打包图标, 点击可打包一个项目.

例如:
脚本读取同目录 `1.png`: `images.read("./1.png")`.
UI 脚本图片控件引用同目录 `2.png`: `<img src="file://2.png"/>`.
AutoJs6 内置模块支持相对路径引用, 其他情况可能需借助 `files.path()` 转换为绝对路径.

### 打包应用不显示主界面#

需使用 "项目" 功能.
新建项目后, 在项目目录 `project.json` 文件中增加以下条目:

    {
      "launchConfig": {
        "hideLogs": true
      }
    }

例如:

    {
      "name": "First-Project",
      "versionName": "1.0.0",
      "versionCode": 1,
      "packageName": "org.autojs.example.first",
      "main": "main.js",
      "launchConfig": {
        "hideLogs": true
      }
    }

## 代码转换#

AutoJs6 支持直接调用 [ Java / Android / 扩展库 ] 等 API.
对于 AutoJs6 没有内置的功能, 可进行 Java 脚本化, 即直接参照 Java (或 Kotlin 等) 源码, 转换为 JavaScript 代码.
例如:

    import android.graphics.Bitmap;
    import android.graphics.Matrix;

    public static Bitmap rotate(Bitmap src, int degrees, float px, float py) {
        if (degrees == 0) return src;
        Matrix matrix = new Matrix();
        matrix.setRotate(degrees, px, py);
        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;
        let matrix = new Matrix();
        matrix.setRotate(degrees, px, py);
        let ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
        return ret;
    }

关于脚本化 Java 的更多信息, 参阅 Scripting Java - 脚本化 Java 章节.

## 反馈#

如有任何问题或建议, 可在 GitHub 项目议题页面发起新的反馈.

关于 应用文档 的反馈:
http://docs-issues.autojs6.com

关于 AutoJs6 的反馈:
http://issues.autojs6.com

# 全局对象 (Global)#

在 JavaScript 中, 几乎一切都是对象.
此处的全局 "对象" 包括 [ 变量 / 方法 / 构造器 ] 等.
全局对象随处可用, 包括 ECMA 标准内置对象 (如 [ Number / RegExp / String ] 等).

AutoJs6 的内置模块均支持全局使用, 如 `app`, `images`, `device` 等.

为便于使用, 一些 AutoJs6 模块中的方法也被全局化,
如 `images.captureScreen()`, `dialogs.alert()`, `app.launch()` 等.
全局化方法均以 `Global` 标签标注.

脚本文件可直接运行使用, 也可作为模块被导入使用 (`require` 方法).
当作为模块使用时, `exports` 和 `module` 可作为全局对象使用.
另在 UI 模式下也有一些专属全局对象, 如 `activity`.

## 覆写保护#

AutoJs6 对部分全局对象及内置模块增加了覆写保护.
以下全局声明或赋值将导致异常或非预期结果:

    /* 以全局对象 selector 为例. */

    /* 声明无效. */
    let selector = 1; /* 异常: 变量 selector 重复声明. */
    const selector = 1; /* 同上. */
    var selector = 1; /* 同上. */

    /* 覆写无效 (非严格模式). */
    selector = 1;
    typeof selector; // "function" - 静默失败, 覆写未生效.

    /* 覆写无效 (严格模式). */
    "use strict";
    selector = 1; /* 异常: 无法修改只读属性: selector. */

局部作用域不受上述情况影响:

    (function () {
        let selector = 1;
        return typeof selector;
    })(); // "number"

截至目前 (2022/10) 受覆写保护的对象有:

    selector
    continuation

global

## [@] global#

global 为 AutoJs6 的默认顶级作用域对象, 可作为全局对象使用:

    typeof global; // "object"
    typeof global.sleep; // "function"

另, 访问顶级作用域对象也可通过以下代码:

    runtime.topLevelScope;

`runtime.topLevelScope` 本身有 `global` 属性, 因此全局对象 `global` 也一样拥有:

    typeof runtime.topLevelScope.global; // "object"

    global.global === global; // true
    global.global.global.global === global; // true

global 对象可以增加属性, 也可以覆写甚至删除属性 (部分被保护):

    global.hello = "hello";
    delete global.hello;

global 对象本身是可被覆写的:

    typeof global; // "object"
    global = 3;
    typeof global; // "number"

如果 global 对象被意外重写 (虽然概率很低),
可通过 `runtime.topLevelScope` 访问或还原:

    global = 3; /* 覆写 global 对象. */
    typeof global; // "number"
    typeof global.sleep; // "undefined"
    typeof runtime.topLevelScope.sleep; // "function"

    global = runtime.topLevelScope; /* 还原 global 对象. */
    typeof global; // "object"
    typeof global.sleep; // "function"

## [m] sleep#

### sleep(millis)#

`Global` `Overload 1/3` `Non-UI`

- millis { number } - 休眠时间 (毫秒)
- returns { void }

使当前线程休眠一段时间.

    /* 休眠 9 秒钟. */
    sleep(9000);
    /* 休眠 9 秒钟 (使用科学计数法). */
    sleep(9e3);

### sleep(millisMin, millisMax)#

`6.2.0` `Global` `Overload 2/3` `Non-UI`

- millisMin { number } - 休眠时间下限 (毫秒)
- millisMax { number } - 休眠时间上限 (毫秒)
- returns { void }

使当前线程休眠一段时间, 该时间随机落在 millisMin 和 millisMax 之间.

    /* 随机休眠 3 - 5 秒钟. */
    sleep(3e3, 5e3);

### sleep(millis, bounds)#

`6.2.0` `Global` `Overload 3/3` `Non-UI`

- millis { number } - 休眠时间 (毫秒)
- bounds { NumberString | string } - 浮动值
- returns { void }

使当前线程休眠一段时间, 该时间随机落在 millis ± bounds 之间.
bounds 参数为 数字字符串 类型 (如 "12"), 或在字符串开头附加 "±" 明确参数含义 (如 "±12").

    /* 随机休眠 3 - 5 秒钟 (即 4 ± 1 秒钟). */
    sleep(4e3, "1e3");
    sleep(4e3, "±1e3"); /* 同上. */

## [m+] toast#

toast 模块的全局化对象, 参阅 消息浮动框 (Toast) 模块章节.

## [m] toastLog#

显示消息浮动框并在控制台打印消息.
相当于以下代码组合:

    toast(text, ...args);
    console.log(text);

因此, 方法重载与 toast 完全一致.

> 注: 虽然 toast 方法异步, 但 console.log 方法同步, 因此 toastLog 方法也为同步.

### toastLog(text)#

`Global` `Overload 1/4`

- text { string } - 消息内容
- returns { void }

> 参阅: toast(text)

### toastLog(text, isLong)#

`Global` `Overload 2/4`

- text { string } - 消息内容 isLong = false { `'long'` | `'l'` | `'short'` | `'s'` | boolean } - 是否以较长时间显示
- returns { void }

> 参阅: toast(text, isLong)

### toastLog(text, isLong, isForcible)#

`Global` `Overload 3/4`

- text { string } - 消息内容 isLong = false { `'long'` | `'l'` | `'short'` | `'s'` | boolean } - 是否以较长时间显示 isForcible = false { `'forcible'` | `'f'` | boolean } - 是否强制覆盖显示
- returns { void }

> 参阅: toast(text, isLong, isForcible)

### toastLog(text, isForcible)#

`Global` `Overload 4/4`

- text { string } - 消息内容
- isForcible { `'forcible'` | `'f'` } - 强制覆盖显示 (字符标识)
- returns { void }

> 参阅: toast(text, isForcible)

## [m+] notice#

notice 模块的全局化对象, 参阅 消息通知 (Notice) 模块章节.

## [m] random#

### random()#

`Global` `Overload 1/2`

- returns { number }

与 Math.random() 相同, 返回落在 [0, 1) 区间的随机数字.

### random(min, max)#

`Global` `Overload 2/2`

- min { number } - 随机数下限
- max { number } - 随机数上限
- returns { number }

返回落在 [min, max] 区间的随机数字.

> 注: random(min, max) 右边界闭合, 而 random() 右边界开放.

## [m] wait#

### wait(condition)#

`6.2.0` `Global` `Overload 1/6` `A11Y?` `Non-UI`

- condition { (() => any) | PickupSelector } - 结束等待条件
- returns { boolean }

阻塞等待, 直到条件满足.
默认等待时间为 10 秒, 条件检查间隔为 200 毫秒.
若超时, 放弃等待, 并返回特定的条件超时结果 (如 false).
若超时之前条件得以满足, 结束等待, 并返回特定的条件满足结果 (如 true).

> 注: 不同于 while 和 for 等循环语句的 "条件",
> 该方法的条件是结束等待条件, 只要不满足条件, 就一直等待.
> 而循环语句的条件, 是只要满足条件, 就一直循环.

等待条件支持函数及选择器.

函数示例, 等待设备屏幕关闭:

    wait(function () {
        return device.isScreenOff();
    });

    /* 使用箭头函数. */
    wait(() => device.isScreenOff());

    /* 使用 bind. */
    wait(device.isScreenOff.bind(device));

    /* 对结果分支处理. */
    if (wait(() => device.isScreenOff())) {
        console.log("等待屏幕关闭成功");
    } else {
        console.log("等待屏幕关闭超时");
    }

选择器示例, 等待文本为 "立即开始" 的控件出现:

    /* 以下三种方式为 Pickup 选择器的不同格式, 效果相同. */
    wait('立即开始');
    wait(content('立即开始')); /* 同上. */
    wait({ content: '立即开始' }); /* 同上. */

    /* 函数方式. */
    wait(() => content('立即开始').exists());
    wait(() => pickup('立即开始', '?')); /* 同上. */

    /* wait 返回结果的简单应用. */
    wait('立即开始') && toast('OK');
    wait('立即开始') ? toast('√') : toast('×');

等待条件的满足与否, 与函数返回值有关.
例如当函数返回 true 时, 等待条件即满足.

下面列出不满足条件的几种返回值:
[ false / null / undefined / NaN ]
除此之外的返回值均视为满足条件 (包括空字符串和数字 0 等).

一种常见的错误用例, 即函数条件缺少返回值:

    wait(() => {
        if (device.isScreenOff()) {
            console.log("屏幕已成功关闭");
        }
    });

上述示例中, 等待条件永远无法满足, 因函数一直返回 undefined.

添加合适的返回值即可修正:

    wait(() => {
        if (device.isScreenOff()) {
            console.log("屏幕已成功关闭");
            return true;
        }
    });

> 参阅: pickup

### wait(condition, limit)#

`6.2.0` `Global` `Overload 2/6` `A11Y?` `Non-UI`

- condition { (() => any) | PickupSelector } - 结束等待条件
- limit { number } - 等待条件检测限制
- returns { boolean }

wait(condition) 增加条件检测限制.
达到限制后, 表示等待超时, 并放弃等待.
限制分为 "次数限制" (limit < 100) 和 "时间限制" (limit >= 100).

    /* 等待屏幕关闭, 最多检测屏幕状态 20 次. */
    wait(() => device.isScreenOff(), 20); /* limit < 100, 视为次数限制. */
    /* 等待屏幕关闭, 最多检测屏幕状态 5 秒钟. */
    wait(() => device.isScreenOff(), 5e3); /* limit >= 100, 视为时间限制. */

### wait(condition, limit, interval)#

`6.2.0` `Global` `Overload 3/6` `A11Y?` `Non-UI`

- condition { (() => any) | PickupSelector } - 结束等待条件
- limit { number } - 等待条件检测限制
- interval { number } - 等待条件检测间隔
- returns { boolean }

wait(condition, limit) 增加条件检测间隔.
只要条件不满足, wait() 方法会持续检测, 直到条件满足或达到检测限制.
interval 参数用于设置条件检测之间的间歇时长, 默认为 200 毫秒.

    检查条件 (不满足) - 间歇 - 检查条件 (不满足) - 间歇 - 检查条件...

    /* 等待屏幕关闭, 最多检测屏幕状态 20 次, 每次检查间歇 3 秒钟. */
    wait(() => device.isScreenOff(), 20, 3e3);
    /* 等待屏幕关闭, 最多检测屏幕状态 20 次, 并采用不间断检测 (无间歇). */
    wait(() => device.isScreenOff(), 20, 0);

> 注: 在最后一次条件检查之后, 将不再发生间歇.
> 包括条件满足或达到检测限制.

> 例如在第三次检查时, 条件满足:
> 检查 (×) - 间歇 - 检查 (×) - 间歇 - 检查 (√) - 立即结束 wait()

### wait(condition, callback)#

`6.2.0` `Global` `Overload 4/6` `A11Y?` `Non-UI`

- condition { (() => T) | PickupSelector } - 结束等待条件
- callback {{
  - then(result?: T)?: R
  - else(result?: T)?: R
- }} - 等待结束回调对象
- returns { R extends void ? boolean : R }
- template T, R

wait(condition) 增加回调对象.

回调对象集合了两个方法, then 与 else 分别对应等待成功与等待失败的情况:

    wait(() => device.isScreenOff(), {
        then: () => console.log("等待屏幕关闭成功"),
        else: () => console.log("等待屏幕关闭超时"),
    });

两种方法都将最后一次检查结果作为实参, 可在方法体内直接使用:

    /* 等待一个落在 99.99 到 100 区间的随机数. */
    wait(() => {
        let num = Math.random() * 100;
        return num > 99.99 && num;
    }, {
        then(o) {
            console.log(`获取随机数成功, 数字是: ${o}`);
        },
        else() {
            console.log("获取 99.99 到 100 的随机数超时");
        },
    });

> 注: else 回调方法的参数只能是 [ false / null / undefined / NaN ],
> 因此 else 的参数几乎不会用到.

需特别注意, 回调方法的返回值具有穿透性.
在回调方法内使用 return 语句, 将直接影响 wait() 的返回值 (undefined 除外).

上述示例中, then 和 else 回调都没有返回值, 因此 wait() 返回值是 boolean 类型, 表示等待条件是否满足.
下述示例在回调函数中增加了返回值 (非 undefined), 则 wait() 也将返回这个值.

    let result = wait(() => {
        let num = Math.random() * 100;
        return num > 99.99 && num;
    }, {
        then(o) {
            console.log(`获取随机数成功`);
            return o;
        },
        else() {
            console.log("获取 99.99 到 100 的随机数超时");
            return NaN;
        },
    });
    result; /* 一个数字 (如 99.99732126036437) 或 NaN. */

上述示例如果等待条件满足, 则返回 then 的返回值 (number 类型),
等待条件超时, 则返回 else 的返回值 (NaN, 也为 number 类型).

如果去掉 else 的返回语句, 则等待条件超时后, wait() 将返回 false (boolean 类型).

如需对 wait() 的返回值做进一步处理, 则建议两个回调方法的返回值类型一致:

    wait(() => {
        let num = Math.random() * 100;
        return num > 99.99 && num;
    }, {
        then(o) {
            return [ o - 1, o, o + 1 ];
        },
        else() {
            /* 即使等待条件超时, 也可调用 forEach 方法. */
            return [];
        },
    }).forEach(x => console.log(x));

### wait(condition, limit, callback)#

`6.2.0` `Global` `Overload 5/6` `A11Y?` `Non-UI`

- condition { (() => T) | PickupSelector } - 结束等待条件
- limit { number } - 等待条件检测限制
- callback {{
  - then(result?: T)?: R
  - else(result?: T)?: R
- }} - 等待结束回调对象
- returns { R extends void ? boolean : R }
- template T, R

wait(condition, callback) 增加条件检测限制.

> 参阅: wait(condition, limit)

### wait(condition, limit, interval, callback)#

`6.2.0` `Global` `Overload 6/6` `A11Y?` `Non-UI`

- condition { (() => T) | PickupSelector } - 结束等待条件
- limit { number } - 等待条件检测限制
- interval { number } - 等待条件检测间隔
- callback {{
  - then(result?: T)?: R
  - else(result?: T)?: R
- }} - 等待结束回调对象
- returns { R extends void ? boolean : R }
- template T, R

wait(condition, limit, callback) 增加条件检测间隔.

> 参阅: wait(condition, limit, interval)

## [m] waitForActivity#

等待指定名称的 Activity 出现 (前置).
此方法相当于 `wait(() => currentActivity() === activityName, ...args)`,
因此其所有重载方法的结构与 wait 一致.
为节约篇幅, 将仅列出方法签名等重要信息.

### waitForActivity(activityName)#

`6.2.0` `Global` `Overload 1/6` `A11Y?` `Non-UI`

- activityName { string } - 目标活动名称
- returns { boolean }

> 参阅:wait(condition)

### waitForActivity(activityName, limit)#

`6.2.0` `Global` `Overload 2/6` `A11Y?` `Non-UI`

- activityName { string } - 目标活动名称
- limit { number } - 等待条件检测限制
- returns { boolean }

> 参阅:wait(condition, limit)

### waitForActivity(activityName, limit, interval)#

`6.2.0` `Global` `Overload 3/6` `A11Y?` `Non-UI`

- activityName { string } - 目标活动名称
- limit { number } - 等待条件检测限制
- interval { number } - 等待条件检测间隔
- returns { boolean }

> 参阅:wait(condition, limit, interval)

### waitForActivity(activityName, callback)#

`6.2.0` `Global` `Overload 4/6` `A11Y?` `Non-UI`

- activityName { string } - 目标活动名称
- callback {{
  - then(result?: T)?: R
  - else(result?: T)?: R
- }} - 等待结束回调对象
- returns { R extends void ? boolean : R }
- template T, R

> 参阅: wait(condition, callback)

### waitForActivity(activityName, limit, callback)#

`6.2.0` `Global` `Overload 5/6` `A11Y?` `Non-UI`

- activityName { string } - 目标活动名称
- limit { number } - 等待条件检测限制
- callback {{
  - then(result?: T)?: R
  - else(result?: T)?: R
- }} - 等待结束回调对象
- returns { R extends void ? boolean : R }
- template T, R

> 参阅: wait(condition, limit, callback)

### waitForActivity(activityName, limit, interval, callback)#

`6.2.0` `Global` `Overload 6/6` `A11Y?` `Non-UI`

- activityName { string } - 目标活动名称
- limit { number } - 等待条件检测限制
- interval { number } - 等待条件检测间隔
- callback {{
  - then(result?: T)?: R
  - else(result?: T)?: R
- }} - 等待结束回调对象
- returns { R extends void ? boolean : R }
- template T, R

> 参阅: wait(condition, limit, interval, callback)

## [m] waitForPackage#

等待指定包名的应用出现 (前置).
此方法相当于 `wait(() => currentPackage() === packageName, ...args)`,
因此其所有重载方法的结构与 wait 一致.
为节约篇幅, 将仅列出方法签名等重要信息.

### waitForPackage(packageName)#

`6.2.0` `Global` `Overload 1/6` `A11Y?` `Non-UI`

- packageName { string } - 目标应用包名
- returns { boolean }

> 参阅:wait(condition)

### waitForPackage(packageName, limit)#

`6.2.0` `Global` `Overload 2/6` `A11Y?` `Non-UI`

- packageName { string } - 目标应用包名
- limit { number } - 等待条件检测限制
- returns { boolean }

> 参阅:wait(condition, limit)

### waitForPackage(packageName, limit, interval)#

`6.2.0` `Global` `Overload 3/6` `A11Y?` `Non-UI`

- packageName { string } - 目标应用包名
- limit { number } - 等待条件检测限制
- interval { number } - 等待条件检测间隔
- returns { boolean }

> 参阅:wait(condition, limit, interval)

### waitForPackage(packageName, callback)#

`6.2.0` `Global` `Overload 4/6` `A11Y?` `Non-UI`

- packageName { string } - 目标应用包名
- callback {{
  - then(result?: T)?: R
  - else(result?: T)?: R
- }} - 等待结束回调对象
- returns { R extends void ? boolean : R }
- template T, R

> 参阅: wait(condition, callback)

### waitForPackage(packageName, limit, callback)#

`6.2.0` `Global` `Overload 5/6` `A11Y?` `Non-UI`

- packageName { string } - 目标应用包名
- limit { number } - 等待条件检测限制
- callback {{
  - then(result?: T)?: R
  - else(result?: T)?: R
- }} - 等待结束回调对象
- returns { R extends void ? boolean : R }
- template T, R

> 参阅: wait(condition, limit, callback)

### waitForPackage(packageName, limit, interval, callback)#

`6.2.0` `Global` `Overload 6/6` `A11Y?` `Non-UI`

- packageName { string } - 目标应用包名
- limit { number } - 等待条件检测限制
- interval { number } - 等待条件检测间隔
- callback {{
  - then(result?: T)?: R
  - else(result?: T)?: R
- }} - 等待结束回调对象
- returns { R extends void ? boolean : R }
- template T, R

> 参阅: wait(condition, limit, interval, callback)

## [m] exit#

停止脚本运行.

### exit()#

`Global` `Overload 1/2`

- returns { void }

通过抛出 `ScriptInterruptedException` 异常实现脚本停止.
因此用 `try` 包裹 `exit()` 语句将会使脚本继续运行片刻:

    try {
        log('exit now');
        exit();
        log("after"); /* 控制台不会打印 "after". */
    } catch (e) {
        e.javaException instanceof ScriptInterruptedException; // true
    }
    while (true) log("hello"); /* 控制台将打印一定数量的 "hello". */

如果编写的脚本对 "是否停止" 的状态十分敏感,
即要求 exit() 之后的代码一定不被执行,
则可通过附加状态判断实现上述需求:

    if (!isStopped()) {
        // 其他代码...
    }

因此上述示例如果加上状态判断, "hello" 将不会被打印:

    try {
        log('exit now');
        exit();
    } catch (_) {
        // Ignored.
    }
    if (!isStopped()) {
        while (true) {
            /* 控制台不会打印 "hello". */
            log("hello");
        }
    }

除了 isStopped, 还可通过 `threads` 或 `engines` 模块获取停止状态:

    /* threads. */
    if (!threads.currentThread().isInterrupted()) {
        // 其他代码...
    }

    /* engines. */
    if (!engines.myEngine().isStopped()) {
        // 其他代码...
    }

### exit(e)#

`Global` `Overload 2/2`

- e { OmniThrowable } - 异常参数
- returns { void }

停止脚本运行并抛出异常参数指定的异常.

    let arg = 'hello';
    try {
        if (typeof arg !== "number") {
            throw Error('arg 参数非 number 类型');
        }
    } catch (e) {
        exit(e);
    }

OmniThrowable 支持字符串参数, 可将字符串参数作为异常消息传入 `exit` 方法中:

    let buttonText = '点此开始';
    if (!pickup(buttonText)) {
        exit(`"${buttonText}" 按钮不存在.`);
    }

## [m] stop#

### stop()#

`Global` - returns { void }

停止脚本运行.

exit() 的别名方法.

> 注: stop 方法不存在 exit(e) 对应的重载方法.

## [m] isStopped#

### isStopped()#

`Global` `DEPRECATED`

- returns { boolean }

检测脚本主线程是否已中断.

即 `runtime.isInterrupted()`.

## [m] isShuttingDown#

### isShuttingDown()#

`Global` `DEPRECATED`

- returns { boolean }

检测脚本主线程是否已中断.

因方法名称易造成歧义及混淆, 因此被弃用, 建议使用 isStopped() 或 `runtime.isInterrupted()` 替代.

## [m] isRunning#

### isRunning()#

`Global` - returns { boolean }

检测脚本主线程是否未被中断.

即 `!runtime.isInterrupted()`.

## [m] notStopped#

### notStopped()#

`Global` `DEPRECATED`

- returns { boolean }

检测脚本主线程是否未被中断.

因方法名称易造成歧义及混淆, 因此被弃用, 建议使用 isRunning() 或 `!runtime.isInterrupted()` 替代.

## [m] requiresApi#

### requiresApi(api)#

`Global` - api { number } - 安卓 API 级别

- returns { void }

脚本运行的最低 API 级别要求.

例如要求脚本运行不低于 Android API 30 (11) [R]:

    requiresApi(30);
    requiresApi(util.versionCodes.R.apiLevel); /* 同上. */
    requiresApi(android.os.Build.VERSION_CODES.R); /* 同上. */

若 API 级别不符合要求, 脚本抛出异常并停止继续执行.

> 参阅:

> - Android API Level - 安卓 API 级别
> - util.versionCodes

## [m] requiresAutojsVersion#

### requiresAutojsVersion(versionName)#

`Global` `Overload 1/2`

- versionName { string } - AutoJs6 版本名称
- returns { void }

脚本运行的最低 AutoJs6 版本要求 (版本名称).

    requiresAutojsVersion("6.2.0");

可通过 `autojs.versionName` 查看 AutoJs6 版本名称.

> 参阅: autojs.versionName

### requiresAutojsVersion(versionCode)#

`Global` `Overload 2/2`

- versionCode { number } - AutoJs6 版本号
- returns { void }

脚本运行的最低 AutoJs6 版本要求 (版本号).

    requiresAutojsVersion(1024);

可通过 `autojs.versionCode` 查看 AutoJs6 版本号.

> 参阅: autojs.versionCode

## [m] importPackage#

### importPackage(...pkg)#

`Global` - pkg { ...( string | object ) } - 需导入的 Java 包

- returns { void }

    /* 导入一个 Java 包. */

    importPackage(java.lang);
    importPackage('java.lang'); /* 同上. */

    /* 导入多个 Java 包. */

    importPackage(java.io);
    importPackage(java.lang);
    importPackage(java.util);

    importPackage(java.io, java.lang, java.util); /* 同上. */

> 参阅: 访问 Java 包和类

## [m] importClass#

### importClass(...cls)#

`Global` - cls { ...( string | object ) } - 需导入的 Java 类

- returns { void }

    /* 导入一个 Java 类. */

    importClass(java.lang.Integer);
    importClass('java.lang.Integer'); /* 同上. */

    /* 导入多个 Java 类. */

    importClass(java.io.File);
    importClass(java.lang.Integer);
    importClass(java.util.HashMap);

    importClass(
        java.io.File,
        java.lang.Integer,
        java.util.HashMap,
    ); /* 同上. */

> 参阅: 访问 Java 包和类

## [m] currentPackage#

### currentPackage()#

`Global` `A11Y`

- returns { string }

获取最近一次监测到的应用包名, 并视为当前正在运行的应用包名.

## [m] currentActivity#

### currentActivity()#

`Global` `A11Y`

- returns { string }

获取最近一次监测到的活动名称, 并视为当前正在运行的活动名称.

## [m] setClip#

### setClip(text)#

`Global` - text { string } - 剪贴板内容

- returns { void }

设置系统剪贴板内容.

> 参阅: getClip

## [m] getClip#

### getClip()#

`Global` - returns { string } - 系统剪贴板内容

需额外留意, 自 Android API 29 (10) [Q] 起, 剪贴板数据的访问将受到限制:

为更好地保护用户隐私权, 除默认输入法及当前获取焦点的前置应用外, 均无法访问剪贴板数据.

    setClip("test");

    /* 安卓 10 以下: 打印 "test". */
    /* 安卓 10 及以上: 若 AutoJs6 前置, 打印 "test", 否则打印空字符串. */
    console.log(getClip());

> 参阅: setClip

> 参阅: Android Docs

## [m] selector#

### selector()#

`Global` - returns { UiSelector }

构建一个 "空" 选择器.

## [m] pickup#

拾取选择器, 简称拾取器, 是高度封装的混合形式选择器, 用于在筛选控件及处理结果过程中实现快捷操作.
支持 [ 选择器多形式混合 / 控件罗盘 / 结果筛选 / 参化调用 ] 等.

参阅 UiSelector.pickup.

## [m] detect#

控件探测.

探测相当于对控件进行一系列组合操作 (罗盘定位, 结果筛选, 参化调用, 回调处理).

参阅 UiObject#detect.

## [m] existsAll#

### existsAll(...selectors)#

`Global` - selectors { ...PickupSelector[] } - 混合选择器参数

- returns { boolean } - 选择器全部满足 "存在" 条件

提供的选择器参数全部满足 "存在" 条件, 即 `selector.exists() === true`.

例如要求当前活动窗口中同时存在以下三个选择器对应的控件:

1. contentMatch(/^开始.*/)
2. descMatch(/descriptions?/)
3. content('点击继续')

    console.log(existsAll(contentMatch(/^开始.*/), descMatch(/descriptions?/), content('点击继续'))); /* e.g. true */

因混合选择器参数支持对 content 系列选择器的简化, 因此上述示例也可改写为以下形式:

    console.log(existsAll(/^开始.*/, descMatch(/descriptions?/), '点击继续')); /* e.g. true */

此方法对应的传统的逻辑判断形式:

    console.log(contentMatch(/^开始.*/).exists()
        && descMatch(/descriptions?/).exists()
        && content('点击继续').exists()); /* e.g. true */

## [m] existsOne#

### existsOne(...selectors)#

`Global` - selectors { ...PickupSelector[] } - 混合选择器参数

- returns { boolean } - 选择器任一满足 "存在" 条件

提供的选择器参数任一满足 "存在" 条件, 即 `selector.exists() === true`.

例如要求当前活动窗口中存在任意一个以下选择器对应的控件:

1. contentMatch(/^开始.*/)
2. descMatch(/descriptions?/)
3. content('点击继续')

    console.log(existsOne(contentMatch(/^开始.*/), descMatch(/descriptions?/), content('点击继续'))); /* e.g. true */

因混合选择器参数支持对 content 系列选择器的简化, 因此上述示例也可改写为以下形式:

    console.log(existsOne(/^开始.*/, descMatch(/descriptions?/), '点击继续')); /* e.g. true */

此方法对应的传统的逻辑判断形式:

    console.log(contentMatch(/^开始.*/).exists()
        || descMatch(/descriptions?/).exists()
        || content('点击继续').exists()); /* e.g. true */

## [m] cX#

横坐标标度.

### cX()#

`6.2.0` `Global` `Overload 1/5`

- returns { number }

无参时, 返回当前设备宽度.

    console.log(cX() === device.width); // true

### cX(x, base)#

`6.2.0` `Global` `Overload 2/4`

- x { number } - 绝对坐标值
- [ base = 720 ] { number } - 坐标值基数
- returns { number }

由基数换算后得到的横坐标值.

例如在一个设备宽度为 `1096` 的设备上的 `100` 像素, 在其他不同宽度的设备上将转换为不同的值:

    /* 在宽度为 1096 像素的设备上. */
    cX(100, 1096); // 100

    /* 在宽度为 1080 像素的设备上. */
    cX(100, 1096); // 99

    /* 在宽度为 720 像素的设备上. */
    cX(100, 1096); // 66

    /* 在宽度为 540 像素的设备上. */
    cX(100, 1096); // 49

上述示例的 `1096` 为基数, 默认基数为 `720`, 如需设置默认基数, 可使用以下方法:

    cX(100); /* 相当于 cX(100, 720) . */
    setScaleBaseX(1096);
    cX(100); /* 相当于 cX(100, 1096) . */

默认基数只能修改最多一次.

### cX(x, isRatio)#

`6.2.0` `Global` `Overload 3/4`

- x { number } - 绝对坐标值或屏幕宽度百分比
- [ isRatio = 'auto' ] { `'auto'` | boolean } - 是否将 `x` 参数强制作为百分比
- returns { number }

`isRatio` 参数默认为 `auto`, 即由 `x` 参数的范围自动决定 `x` 是否视为百分比,
即当参数 `x` 满足 `-1 < x < 1` 时, `x` 将视为屏幕宽度百分比, 否则将视为绝对坐标值.

`isRatio` 参数为 `true` 时, `x` 参数将强制视为百分比, 如 `cX(2, true)` 意味着两倍屏幕宽度, `2` 的意义不再是像素值.

`isRatio` 参数为 `false` 时, `x` 参数将强制视为绝对坐标值, 如 `cX(0.5, false)` 意味着 `0.5` 像素值, 其意义不再是百分比.

### cX(x)#

`6.2.0` `Global` `Overload 4/4`

- x { number } - 绝对坐标值或屏幕宽度百分比
- returns { number }

当参数 `x` 满足 `-1 < x < 1` 时, 相当于 `cX(x, /* isRatio = */ true)`, 即 `x` 将视为屏幕宽度百分比.

当参数 `x` 满足 `x <= -1 | x >= 1` 时, 相当于 `cX(x, /* base = */ 720)`, 即 `x` 将视为绝对坐标值, 另 `base` 参数可能由 `setScaleBaseX` 等方法修改, `720` 为其默认值.

## [m] cY#

横坐标标度.

### cY()#

`6.2.0` `Global` `Overload 1/5`

- returns { number }

无参时, 返回当前设备高度.

    console.log(cY() === device.width); // true

### cY(y, base)#

`6.2.0` `Global` `Overload 2/4`

- y { number } - 绝对坐标值
- [ base = 1280 ] { number } - 坐标值基数
- returns { number }

由基数换算后得到的纵坐标值.

例如在一个设备高度为 `2560` 的设备上的 `100` 像素, 在其他不同高度的设备上将转换为不同的值:

    /* 在高度为 2560 像素的设备上. */
    cY(100, 2560); // 100

    /* 在高度为 1920 像素的设备上. */
    cY(100, 2560); // 75

    /* 在高度为 1280 像素的设备上. */
    cY(100, 2560); // 50

    /* 在高度为 960 像素的设备上. */
    cY(100, 2560); // 38

上述示例的 `2560` 为基数, 默认基数为 `1280`, 如需设置默认基数, 可使用以下方法:

    cY(100); /* 相当于 cY(100, 1280) . */
    setScaleBaseY(2560);
    cY(100); /* 相当于 cY(100, 2560) . */

默认基数只能修改最多一次.

### cY(y, isRatio)#

`6.2.0` `Global` `Overload 3/4`

- y { number } - 绝对坐标值或屏幕高度百分比
- [ isRatio = 'auto' ] { `'auto'` | boolean } - 是否将 `y` 参数强制作为百分比
- returns { number }

`isRatio` 参数默认为 `auto`, 即由 `y` 参数的范围自动决定 `y` 是否视为百分比,
即当参数 `y` 满足 `-1 < y < 1` 时, `y` 将视为屏幕高度百分比, 否则将视为绝对坐标值.

`isRatio` 参数为 `true` 时, `y` 参数将强制视为百分比, 如 `cY(2, true)` 意味着两倍屏幕高度, `2` 的意义不再是像素值.

`isRatio` 参数为 `false` 时, `y` 参数将强制视为绝对坐标值, 如 `cY(0.5, false)` 意味着 `0.5` 像素值, 其意义不再是百分比.

### cY(y)#

`6.2.0` `Global` `Overload 4/4`

- y { number } - 绝对坐标值或屏幕高度百分比
- returns { number }

当参数 `y` 满足 `-1 < y < 1` 时, 相当于 `cY(y, /* isRatio = */ true)`, 即 `y` 将视为屏幕高度百分比.

当参数 `y` 满足 `y <= -1 | y >= 1` 时, 相当于 `cY(y, /* base = */ 1280)`, 即 `y` 将视为绝对坐标值, 另 `base` 参数可能由 `setScaleBaseY` 等方法修改, `1280` 为其默认值.

## [m] cYx#

以横坐标度量的纵坐标标度.

与设备高度无关, 与设备宽度相关的坐标标度.

如 `cYx(0.5, '9:16')` 对于以下 5 个设备 (以分辨率区分) 得到的结果是完全一致的:

    1. 1080 × 1920
    4. 1080 × 2160
    5. 1080 × 2340
    3. 1080 × 2520
    2. 1080 × 2560

因为所有设备宽度相同, `cYx` 的结果是高度无关的.

计算结果:

    1080 * 0.5 * 16 / 9; // 960

设想如下场景, 某个应用页面是可以向下滚动窗口显示更多内容的, 在屏幕上半部分有一个按钮 `BTN`, 距离屏幕上边缘 `H` 距离, 另一台设备与当前设备屏幕宽度相同, 但高度更大, 相当于屏幕纵向变长, 此时按钮 `BTN` 距离屏幕上边缘依然是 `H` 距离, 仅仅是屏幕下方显示了更多内容.
因此可使用 `cYx` 标度表示按钮 `BTN` 的位置, 如 `cYx(0.2, 1080 / 1920)` 或 `cYx(0.2, 9 / 16)` 或 `cYx(0.2, '9:16')`.

上述示例的 `0.2` 是一个相对值, 是相对于当前设备屏幕高度的, 因此第 2 个参数对应设备宽高比例值.
如果使用绝对坐标值 (`Y` 坐标值), 如 `384`, 则第 2 个参数对应的是设备屏幕宽度值:

第 1 个参数 | 第 2 个参数 | 示例
--- | --- | ---
Y 坐标百分比 | 设备宽高比 | cYx(0.2, '9:16')
Y 坐标值 | 设备宽度值 | cYx(384, 1096)

### cYx(coordinateY, baseX)#

`6.2.0` `Global` `Overload [1(A)]/3`

- coordinateY { number } - 纵坐标值
- [ baseX = 720 ] { number } - 横坐标基数
- returns { number }

由横坐标基数换算后得到的纵坐标值.

例如在一个设备宽度为 `1096` 设备上的 `512` 像素高度, 在其他不同宽度的设备上将转换为不同的值:

    /* 在宽度为 1096 像素的设备上. */
    cYx(512, 1096); // 512

    /* 在宽度为 1080 像素的设备上. */
    cYx(512, 1096); // 505

    /* 在宽度为 720 像素的设备上. */
    cYx(512, 1096); // 336

    /* 在宽度为 540 像素的设备上. */
    cYx(512, 1096); // 252

上述示例的 `1096` 为基数, 默认基数为 `720`, 如需设置默认基数, 可使用以下方法:

    cYx(512); /* 相当于 cYx(512, 720) . */
    setScaleBaseX(1096);
    cYx(512); /* 相当于 cYx(512, 1096) . */

默认基数只能修改最多一次.

### cYx(percentY, ratio)#

`6.2.0` `Global` `Overload [1(B)]/3`

- percentY { number } - 纵坐标百分比
- [ ratio = '9:16' ] { number | string } - 设备宽高比
- returns { number }

由设备宽高比换算后得到的新纵坐标值.

例如在一个设备宽度与高度分别为 `1096` 和 `2560` 的设备上的 `512` 像素高度, 即 `0.2` 倍的屏幕高度, 在其他不同宽度的设备上将转换为不同的值:

    /* 在宽度为 1096 像素的设备上. */
    cYx(0.2, 1096 / 2560); // 512

    /* 在宽度为 1080 像素的设备上. */
    cYx(0.2, 1096 / 2560); // 505

    /* 在宽度为 720 像素的设备上. */
    cYx(0.2, 1096 / 2560); // 336

    /* 在宽度为 540 像素的设备上. */
    cYx(0.2, 1096 / 2560); // 252

上述示例的 `1096 / 2560` 为基数, 默认基数为 `720 / 1280`, 如需设置默认基数, 可使用以下方法:

    cYx(0.2); /* 相当于 cYx(0.2, 720 / 1280) . */
    setScaleBases(1096, 2560);
    cYx(0.2); /* 相当于 cYx(0.2, 1096 / 2560) . */

默认基数只能修改最多一次.

### cYx(y, isRatio)#

`6.2.0` `Global` `Overload 2/3`

- y { number } - 绝对坐标值或屏幕高度百分比
- [ isRatio = 'auto' ] { `'auto'` | boolean } - 是否将 `y` 参数强制作为百分比
- returns { number }

`isRatio` 参数默认为 `auto`, 即由 `y` 参数的范围自动决定 `y` 是否视为百分比,
即当参数 `y` 满足 `-1 < y < 1` 时, `y` 将视为屏幕高度百分比, 否则将视为绝对坐标值.

`isRatio` 参数为 `true` 时, `y` 参数将强制视为百分比, 如 `cYx(2, true)` 意味着两倍屏幕高度, `2` 的意义不再是像素值.

`isRatio` 参数为 `false` 时, `y` 参数将强制视为绝对坐标值, 如 `cYx(0.5, false)` 意味着 `0.5` 像素值, 其意义不再是百分比.

### cYx(y)#

`6.2.0` `Global` `Overload 3/3`

- y { number } - 绝对坐标值或屏幕高度百分比
- returns { number }

当参数 `y` 满足 `-1 < y < 1` 时, 相当于 `cY(y, /* isRatio = */ true)`, 即 `y` 将视为屏幕高度百分比.

当参数 `y` 满足 `y <= -1 | y >= 1` 时, 相当于 `cY(y, /* base = */ 720)`, 即 `y` 将视为绝对坐标值, 另 `base` 参数可能由 `setScaleBaseX` 等方法修改, `720` 为其默认值.

    cYx(0.3); /* 相当于 cYx(0.3, '9:16') . */
    cYx(384); /* 相当于 cYx(384, 720) . */

## [m] cXy#

以纵坐标度量的横坐标标度.

与设备宽度无关, 与设备高度相关的坐标标度.

如 `cXy(0.5, '9:16')` 对于以下 5 个设备 (以分辨率区分) 得到的结果是完全一致的:

    1. 1080 × 1920
    4. 1096 × 1920
    5. 720 × 1920
    3. 540 × 1920
    2. 960 × 1920

因为所有设备高度相同, `cXy` 的结果是宽度无关的.

计算结果:

    1920 * 0.5 * 9 / 16; // 540

设想如下场景, 某个应用页面是可以向右滚动窗口显示更多内容的, 在屏幕左半部分有一个按钮 `BTN`, 距离屏幕左边缘 `W` 距离, 另一台设备与当前设备屏幕高度相同, 但宽度更大, 相当于屏幕横向变长, 此时按钮 `BTN` 距离屏幕左边缘依然是 `W` 距离, 仅仅是屏幕右方显示了更多内容.
因此可使用 `cXy` 标度表示按钮 `BTN` 的位置, 如 `cXy(0.2, 1080 / 1920)` 或 `cXy(0.2, 9 / 16)` 或 `cXy(0.2, '9:16')`.

上述示例的 `0.2` 是一个相对值, 是相对于当前设备屏幕宽度的, 因此第 2 个参数对应设备宽高比例值.
如果使用绝对坐标值 (`X` 坐标值), 如 `384`, 则第 2 个参数对应的是设备屏幕高度值:

第 1 个参数 | 第 2 个参数 | 示例
--- | --- | ---
X 坐标百分比 | 设备宽高比 | cXy(0.2, '9:16')
X 坐标值 | 设备高度值 | cXy(384, 2560)

### cXy(coordinateX, baseY)#

`6.2.0` `Global` `Overload [1(A)]/3`

- coordinateX { number } - 横坐标值
- [ baseY = 1280 ] { number } - 纵坐标基数
- returns { number }

由纵坐标基数换算后得到的横坐标值.

例如在一个设备高度为 `2560` 设备上的 `512` 像素宽度, 在其他不同高度的设备上将转换为不同的值:

    /* 在高度为 2560 像素的设备上. */
    cXy(512, 2560); // 512

    /* 在高度为 1920 像素的设备上. */
    cXy(512, 2560); // 384

    /* 在高度为 1280 像素的设备上. */
    cXy(512, 2560); // 256

    /* 在高度为 960 像素的设备上. */
    cXy(512, 2560); // 192

上述示例的 `2560` 为基数, 默认基数为 `1280`, 如需设置默认基数, 可使用以下方法:

    cXy(512); /* 相当于 cXy(512, 1280) . */
    setScaleBaseY(2560);
    cXy(512); /* 相当于 cXy(512, 2560) . */

默认基数只能修改最多一次.

### cXy(percentX, ratio)#

`6.2.0` `Global` `Overload [1(B)]/3`

- percentX { number } - 横坐标百分比
- [ ratio = '9:16' ] { number | string } - 设备宽高比
- returns { number }

由设备宽高比换算后得到的新横坐标值.

例如在一个设备高度与宽度分别为 `1096` 和 `2560` 的设备上的 `548` 像素宽度, 即 `0.5` 倍的屏幕宽度, 在其他不同高度的设备上将转换为不同的值:

    /* 在高度为 2560 像素的设备上. */
    cXy(0.5, 1096 / 2560); // 548

    /* 在高度为 1920 像素的设备上. */
    cXy(0.5, 1096 / 2560); // 411

    /* 在高度为 1280 像素的设备上. */
    cXy(0.5, 1096 / 2560); // 274

    /* 在高度为 960 像素的设备上. */
    cXy(0.5, 1096 / 2560); // 206

上述示例的 `1096 / 2560` 为基数, 默认基数为 `720 / 1280`, 如需设置默认基数, 可使用以下方法:

    cXy(0.5); /* 相当于 cXy(0.5, 720 / 1280) . */
    setScaleBases(1096, 2560);
    cXy(0.5); /* 相当于 cXy(0.5, 1096 / 2560) . */

默认基数只能修改最多一次.

### cXy(x, isRatio)#

`6.2.0` `Global` `Overload 2/3`

- x { number } - 绝对坐标值或屏幕宽度百分比
- [ isRatio = 'auto' ] { `'auto'` | boolean } - 是否将 `x` 参数强制作为百分比
- returns { number }

`isRatio` 参数默认为 `auto`, 即由 `x` 参数的范围自动决定 `x` 是否视为百分比,
即当参数 `x` 满足 `-1 < x < 1` 时, `x` 将视为屏幕宽度百分比, 否则将视为绝对坐标值.

`isRatio` 参数为 `true` 时, `x` 参数将强制视为百分比, 如 `cXy(2, true)` 意味着两倍屏幕宽度, `2` 的意义不再是像素值.

`isRatio` 参数为 `false` 时, `x` 参数将强制视为绝对坐标值, 如 `cXy(0.5, false)` 意味着 `0.5` 像素值, 其意义不再是百分比.

### cXy(x)#

`6.2.0` `Global` `Overload 3/3`

- x { number } - 绝对坐标值或屏幕宽度百分比
- returns { number }

当参数 `x` 满足 `-1 < x < 1` 时, 相当于 `cY(x, /* isRatio = */ true)`, 即 `x` 将视为屏幕宽度百分比.

当参数 `x` 满足 `x <= -1 | x >= 1` 时, 相当于 `cY(x, /* base = */ 720)`, 即 `x` 将视为绝对坐标值, 另 `base` 参数可能由 `setScaleBaseX` 等方法修改, `720` 为其默认值.

    cXy(0.3); /* 相当于 cXy(0.3, '9:16') . */
    cXy(384); /* 相当于 cXy(384, 720) . */

## [m+] species#

### species(o)#

`Global`

- o { any } - 任意对象
- returns { string }

查看任意对象的 "种类", 如 `Object`, `Array`, `Number`, `String`, `RegExp` 等.

内部实现代码摘要:

    Object.prototype.toString.call(o).slice('[Object\x20'.length, ']'.length * -1);

示例:

    species('xyz'); // String
    species(20); // Number
    species(20n); // BigInt
    species(true); // Boolean
    species(undefined); // Undefined
    species(null); // Null
    species(() => null); // Function
    species({ a: 'Apple' }); // Object
    species([ 5, 10, 15 ]); // Array
    species(/^\d{8,11}$/); // RegExp
    species(new Date()); // Date
    species(new TypeError()); // Error
    species(new Map()); // Map
    species(new Set()); // Set
    species(<text/>); // XML
    species(org.autojs.autojs6); // JavaPackage
    species(org.autojs.autojs6.R); // JavaClass

如需判断某个对象是否为特定的 "种类", 可使用形如 `species.isXxx` 的扩展方法:

    species.isObject(23); // false
    species.isNumber(23); // true
    species.isRegExp(/test$/); // true

### [m] isArray#

#### isArray(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Array`.

### [m] isArrayBuffer#

#### isArrayBuffer(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `ArrayBuffer`.

### [m] isBigInt#

#### isBigInt(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `BigInt`.

### [m] isBoolean#

#### isBoolean(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Boolean`.

### [m] isContinuation#

#### isContinuation(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Continuation`.

### [m] isDataView#

#### isDataView(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `DataView`.

### [m] isDate#

#### isDate(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Date`.

### [m] isError#

#### isError(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Error`.

### [m] isFloat32Array#

#### isFloat32Array(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Float32Array`.

### [m] isFloat64Array#

#### isFloat64Array(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Float64Array`.

### [m] isFunction#

#### isFunction(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Function`.

### [m] isHTMLDocument#

#### isHTMLDocument(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `HTMLDocument`.

### [m] isInt16Array#

#### isInt16Array(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Int16Array`.

### [m] isInt32Array#

#### isInt32Array(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Int32Array`.

### [m] isInt8Array#

#### isInt8Array(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Int8Array`.

### [m] isJavaObject#

#### isJavaObject(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `JavaObject`.

### [m] isJavaPackage#

#### isJavaPackage(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `JavaPackage`.

### [m] isMap#

#### isMap(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Map`.

### [m] isNamespace#

#### isNamespace(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Namespace`.

### [m] isNull#

#### isNull(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Null`.

### [m] isNumber#

#### isNumber(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Number`.

### [m] isObject#

#### isObject(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Object`.

### [m] isQName#

#### isQName(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `QName`.

### [m] isRegExp#

#### isRegExp(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `RegExp`.

### [m] isSet#

#### isSet(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Set`.

### [m] isString#

#### isString(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `String`.

### [m] isUint16Array#

#### isUint16Array(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Uint16Array`.

### [m] isUint32Array#

#### isUint32Array(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Uint32Array`.

### [m] isUint8Array#

#### isUint8Array(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Uint8Array`.

### [m] isUint8ClampedArray#

#### isUint8ClampedArray(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Uint8ClampedArray`.

### [m] isUndefined#

#### isUndefined(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Undefined`.

### [m] isWeakMap#

#### isWeakMap(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `WeakMap`.

### [m] isWeakSet#

#### isWeakSet(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `WeakSet`.

### [m] isWindow#

#### isWindow(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `Window`.

### [m] isXML#

#### isXML(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `XML`.

### [m] isXMLList#

#### isXMLList(o)#

- o { any } - 任意对象
- returns { boolean }

判断对象的 "种类" 是否为 `XMLList`.

## [p] WIDTH#

`6.2.0` `Global` `Getter`

- <get> number

device.width 的别名属性.

## [p] HEIGHT#

`6.2.0` `Global` `Getter`

- <get> number

device.height 的别名属性.

## [p+] R#

在代码中使用 R 类的子类中的静态整数可访问 应用资源, 详情参阅 资源 ID 术语.

### [p+] anim#

`6.2.0` `Global`

动画资源.

定义了预先确定的动画.
补间动画保存在 `res/anim/` 中, 可通过 `R.anim` 属性访问.
帧动画保存在 `res/drawable/` 中, 可通过 `R.drawable` 属性访问.

    'ui';

    ui.layout(<vertical id="main">
        <vertical width="100" height="100" bg="#00695C"></vertical>
    </vertical>);

    const AnimationUtils = android.view.animation.AnimationUtils;

    const mContentContainer = ui.main;
    const mSlideDownAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_down);
    mSlideDownAnimation.setDuration(2000);
    mContentContainer.startAnimation(mSlideDownAnimation);

### [p+] array#

`6.2.0` `Global`

静态资源.

提供数组的 XML 资源.

    dialogs.build({
        title: R.string.text_pinch_to_zoom,
        items: R.array.values_editor_pinch_to_zoom_strategy,
        itemsSelectMode: 'single',
        itemsSelectedIndex: defSelectedIndex,
        positive: 'OK',
    }).on('single_choice', function (idx, item) {
        toastLog(`${idx}: ${item}`);
    }).show();

### [p+] bool#

`6.2.0` `Global`

静态资源.

包含布尔值的 XML 资源.

    console.log(context.getResources().getBoolean(R.bool.pref_auto_check_for_updates));

### [p+] color#

`6.2.0` `Global`

静态资源.

包含颜色值 (十六进制颜色) 的 XML 资源.

    console.log(colors.toString(context.getColor(R.color.console_view_warn), 6)); // #1976D2

### [p+] dimen#

`6.2.0` `Global`

静态资源.

包含尺寸值 (及度量单位) 的 XML 资源.

    console.log(context.getResources().getDimensionPixelSize(R.dimen.textSize_item_property)); // e.g. 28

### [p+] drawable#

`6.2.0` `Global`

可绘制资源.

使用位图或 XML 定义各种图形.
保存在 `res/drawable/` 中, 可通过 `R.drawable` 属性访问.

    /* 绘制一个淡绿色的铃铛图标. */

    'ui';

    ui.layout(<vertical bg="#FFFFFF">
        <img id="img" tint="#9CCC65"/>
    </vertical>);

    ui.img.setImageResource(R.drawable.ic_ali_notification);

### [p+] id#

`6.2.0` `Global`

静态资源.

为应用资源和组件提供唯一标识符的 XML 资源.

    'ui';

    ui.layout(<vertical bg="#FFFFFF">
        <text id="txt" size="30"/>
    </vertical>);

    let childCount = ui.txt.getRootView().findViewById(R.id.action_bar_root).getChildCount(); // e.g 2
    ui.txt.setText(`Child count is ${childCount}`);

### [p+] integer#

`6.2.0` `Global`

静态资源.

包含整数值的 XML 资源.

    console.log(context.getResources().getInteger(R.integer.layout_node_info_view_decoration_line)); // 2

### [p+] layout#

`6.2.0` `Global`

布局资源.

定义应用界面的布局.
保存在 `res/layout/` 中, 可通过 `R.layout` 属性访问.

    'ui';

    activity.setContentView(R.layout.activity_log);

### [p+] menu#

`6.2.0` `Global`

菜单资源.

定义应用菜单的内容.
保存在 `res/menu/` 中, 可通过 `R.menu` 属性访问.

    'ui';

    ui.layout(<vertical bg="#FFFFFF">
        <text id="txt" size="30"/>
    </vertical>);

    const PopupMenu = android.widget.PopupMenu;

    let childCount = ui.txt.getRootView().findViewById(R.id.action_bar_root).getChildCount(); // e.g 2
    ui.txt.setText(`Child count is ${childCount}`);

    let popupMenu = new PopupMenu(context, ui.txt);
    popupMenu.inflate(R.menu.menu_script_options);
    popupMenu.show();

### [p+] plurals#

`6.2.0` `Global`

静态资源.

定义资源复数形式.

    console.log(context.getResources().getQuantityString(
        R.plurals.text_already_stop_n_scripts,
        new java.lang.Integer(1),
        new java.lang.Integer(1))); // e.g. 1 script stopped
    console.log(context.getResources().getQuantityString(
        R.plurals.text_already_stop_n_scripts,
        new java.lang.Integer(3),
        new java.lang.Integer(3))); // e.g. 3 scripts stopped

### [p+] string#

`6.2.0` `Global`

字符串资源.

定义字符串.
保存在 `res/values/` 中, 可通过 `R.string` 属性访问.

    console.log(context.getString(R.string.app_name)); // AutoJs6

### [p+] strings#

`6.2.0` `Global`

字符串资源.

同 R.string.
因 `TypeScript Declarations (TS 声明文件)` 中, `string` 为保留关键字, 不能作为类名使用, 为了使 `IDE` 实现智能补全, 特提供 `R.strings` 别名类.

    console.log(context.getString(R.strings.app_name)); // AutoJs6
    console.log(context.getString(R.string.app_name)); /* 同上, 但 IDE 无法智能补全. */

### [p+] style#

`6.2.0` `Global`

样式资源.

定义界面元素的外观和格式.
保存在 `res/values/` 中, 可通过 `R.style` 属性访问.

    'ui';

    const MaterialDialog = com.afollestad.materialdialogs.MaterialDialog;
    const ContextThemeWrapper = android.view.ContextThemeWrapper;

    new MaterialDialog.Builder(new ContextThemeWrapper(activity, R.style.Material3DarkTheme))
        .title('Hello')
        .content('This is a test for showing a dialog with material 3 dark theme.')
        .positiveText('OK')
        .onPositive(() => ui.finish())
        .cancelable(false)
        .build()
        .show();

# 自动化 (Automator)#

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

Marked by SuperMonster003 on Oct 22, 2022.

## 简易自动化 (SimpleActionAutomator)#

待补充...

## 高权限自动化 (RootAutomator)#

待补充...

## 自动化配置 (AutomatorConfiguration)#

待补充...

## 选择器 (UiSelector)#

UiSelector (选择器), 亦可看作是 控件节点 的条件筛选器, 用于通过附加不同的条件, 筛选出一个或一组活动窗口中的 `控件节点`, 并做进一步处理, 如 [ 执行 控件行为 (点击, 长按, 设置文本等) / 判断位置 / 获取文本内容 / 获取控件特定状态 / 在 控件层级 中进行 罗盘 导航 ] 等.

详情参阅 选择器 (UiSelector) 章节.

## 控件节点 (UiObject)#

UiObject 通常被称为 [ 控件 / 节点 / 控件节点 ], 可看做是一个通过安卓无障碍服务包装的 AccessibilityNodeInfo 对象, 代表一个当前活动窗口中的节点, 通过此节点可收集控件信息或执行控件行为, 进而实现一系列自动化操作.

详情参阅 控件节点 (UiObject) 章节.

## 控件集合 (UiObjectCollection)#

UiObjectCollection 代表 控件节点 (UiObject) 的对象集合.

详情参阅 控件集合 (UiObjectCollection) 章节.

## 控件节点行为 (UiObjectActions)#

UiObjectActions 是一个 Java 接口, 代表 控件节点 (UiObject) 的行为集合.

详情参阅 控件节点行为 (UiObjectActions) 章节.

# 基于坐标的触摸模拟#

本章节介绍了一些使用坐标进行点击、滑动的函数. 这些函数有的需要安卓7.0以上, 有的需要root权限.

要获取要点击的位置的坐标, 可以在开发者选项中开启"指针位置".

基于坐标的脚本通常会有分辨率的问题, 这时可以通过`setScreenMetrics()`函数来进行自动坐标放缩. 这个函数会影响本章节的所有点击、长按、滑动等函数. 通过设定脚本设计时的分辨率, 使得脚本在其他分辨率下自动放缩坐标.

控件和坐标也可以相互结合. 一些控件是无法点击的(clickable为false), 无法通过`.click()`函数来点击, 这时如果安卓版本在7.0以上或者有root权限, 就可以通过以下方式来点击：

    //获取这个控件
    var widget = id("xxx").findOne();
    //获取其中心位置并点击
    click(widget.bounds().centerX(), widget.bounds().centerY());
    //如果用root权限则用Tap

## setScreenMetrics(width, height)#

- width { number } 屏幕宽度, 单位像素
- height { number } 屏幕高度, 单位像素

设置脚本坐标点击所适合的屏幕宽高. 如果脚本运行时, 屏幕宽度不一致会自动放缩坐标.

例如在1920*1080的设备中, 某个操作的代码为

    setScreenMetrics(1080, 1920);
    click(800, 200);
    longClick(300, 500);

那么在其他设备上AutoJs会自动放缩坐标以便脚本仍然有效. 例如在540 * 960的屏幕中`click(800, 200)`实际上会点击位置(400, 100).

# 安卓7.0以上的触摸和手势模拟#

注意以下命令只有Android7.0及以上才有效

## click(x, y)#

- `x` { number } 要点击的坐标的x值
- `y` { number } 要点击的坐标的y值

模拟点击坐标(x, y), 并返回是否点击成功. 只有在点击执行完成后脚本才继续执行.

一般而言, 只有点击过程(大约150毫秒)中被其他事件中断(例如用户自行点击)才会点击失败.

使用该函数模拟连续点击时可能有点击速度过慢的问题, 这时可以用`press()`函数代替.

## longClick(x, y)#

- `x` { number } 要长按的坐标的x值
- `y` { number } 要长按的坐标的y值

模拟长按坐标(x, y), 并返回是否成功. 只有在长按执行完成（大约600毫秒）时脚本才会继续执行.

一般而言, 只有长按过程中被其他事件中断(例如用户自行点击)才会长按失败.

## press(x, y, duration)#

- `x` { number } 要按住的坐标的x值
- `y` { number } 要按住的坐标的y值
- `duration` { number } 按住时长, 单位毫秒

模拟按住坐标(x, y), 并返回是否成功. 只有按住操作执行完成时脚本才会继续执行.

如果按住时间过短, 那么会被系统认为是点击；如果时长超过500毫秒, 则认为是长按.

一般而言, 只有按住过程中被其他事件中断才会操作失败.

一个连点器的例子如下：

    //循环100次
    for(var i = 0; i < 100; i++){
      //点击位置(500, 1000), 每次用时1毫秒
      press(500, 1000, 1);
    }

## swipe(x1, y1, x2, y2, duration)#

- `x1` { number } 滑动的起始坐标的x值
- `y1` { number } 滑动的起始坐标的y值
- `x2` { number } 滑动的结束坐标的x值
- `y2` { number } 滑动的结束坐标的y值
- `duration` { number } 滑动时长, 单位毫秒

模拟从坐标(x1, y1)滑动到坐标(x2, y2), 并返回是否成功. 只有滑动操作执行完成时脚本才会继续执行.

一般而言, 只有滑动过程中被其他事件中断才会滑动失败.

## gesture(duration, [x1, y1], [x2, y2], ...)#

- `duration` { number } 手势的时长
- [x, y] ... 手势滑动路径的一系列坐标

模拟手势操作. 例如`gesture(1000, [0, 0], [500, 500], [500, 1000])`为模拟一个从(0, 0)到(500, 500)到(500, 100)的手势操作, 时长为2秒.

## gestures([delay1, duration1, [x1, y1], [x2, y2], ...], [delay2, duration2, [x3, y3], [x4, y4], ...], ...)#

同时模拟多个手势. 每个手势的参数为[delay, duration, 坐标], delay为延迟多久(毫秒)才执行该手势；duration为手势执行时长；坐标为手势经过的点的坐标. 其中delay参数可以省略, 默认为0.

例如手指捏合：

    gestures([0, 500, [800, 300], [500, 1000]],
             [0, 500, [300, 1500], [500, 1000]]);

# RootAutomator#

RootAutomator是一个使用root权限来模拟触摸的对象, 用它可以完成触摸与多点触摸, 并且这些动作的执行没有延迟.

一个脚本中最好只存在一个RootAutomator, 并且保证脚本结束退出他. 可以在exit事件中退出RootAutomator, 例如：

    var ra = new RootAutomator();
    events.on('exit', function(){
      ra.exit();
    });
    //执行一些点击操作
    ...

注意以下命令需要root权限

## RootAutomator.tap(x, y[, id])#

- `x` { number } 横坐标
- `y` { number } 纵坐标
- `id` { number } 多点触摸id, 可选, 默认为1, 可以通过setDefaultId指定.

点击位置(x, y). 其中id是一个整数值, 用于区分多点触摸, 不同的id表示不同的"手指", 例如：

    var ra = new RootAutomator();
    //让"手指1"点击位置(100, 100)
    ra.tap(100, 100, 1);
    //让"手指2"点击位置(200, 200);
    ra.tap(200, 200, 2);
    ra.exit();

如果不需要多点触摸, 则不需要id这个参数. 多点触摸通常用于手势或游戏操作, 例如模拟双指捏合、双指上滑等.

某些情况下可能存在tap点击无反应的情况, 这时可以用`RootAutomator.press()`函数代替.

## RootAutomator.swipe(x1, x2, y1, y2[, duration, id])#

- `x1` { number } 滑动起点横坐标
- `y1` { number } 滑动起点纵坐标
- `x2` { number } 滑动终点横坐标
- `y2` { number } 滑动终点纵坐标
- `duration` { number } 滑动时长, 单位毫秒, 默认值为300
- `id` { number } 多点触摸id, 可选, 默认为1

模拟一次从(x1, y1)到(x2, y2)的时间为duration毫秒的滑动.

## RootAutomator.press(x, y, duration[, id])#

- `x` { number } 横坐标
- `y` { number } 纵坐标
- `duration` { number } 按下时长
- `id` { number } 多点触摸id, 可选, 默认为1

模拟按下位置(x, y), 时长为duration毫秒.

## RootAutomator.longPress(x, y[\, id])#

- `x` { number } 横坐标
- `y` { number } 纵坐标
- `duration` { number } 按下时长
- `id` { number } 多点触摸id, 可选, 默认为1

模拟长按位置(x, y).

以上为简单模拟触摸操作的函数. 如果要模拟一些复杂的手势, 需要更底层的函数.

## RootAutomator.touchDown(x, y[, id])#

- `x` { number } 横坐标
- `y` { number } 纵坐标
- `id` { number } 多点触摸id, 可选, 默认为1

模拟手指按下位置(x, y).

## RootAutomator.touchMove(x, y[, id])#

- `x` { number } 横坐标
- `y` { number } 纵坐标
- `id` { number } 多点触摸id, 可选, 默认为1

模拟移动手指到位置(x, y).

## RootAutomator.touchUp([id])#

- `id` { number } 多点触摸id, 可选, 默认为1

模拟手指弹起.

# 使用root权限点击和滑动的简单命令#

注意：本章节的函数在后续版本很可能有改动！请勿过分依赖本章节函数的副作用. 推荐使用`RootAutomator`代替本章节的触摸函数.

以下函数均需要root权限, 可以实现任意位置的点击、滑动等.

- 这些函数通常首字母大写以表示其特殊的权限.
- 这些函数均不返回任何值.
- 并且, 这些函数的执行是异步的、非阻塞的, 在不同机型上所用的时间不同. 脚本不会等待动作执行完成才继续执行. 因此最好在每个函数之后加上适当的sleep来达到期望的效果.

例如:

    Tap(100, 100);
    sleep(500);

注意, 动作的执行可能无法被停止, 例如：

    for(var i = 0; i < 100; i++){
      Tap(100, 100);
    }

这段代码执行后可能会出现在任务管理中停止脚本后点击仍然继续的情况. 因此, 强烈建议在每个动作后加上延时：

    for(var i = 0; i < 100; i++){
      Tap(100, 100);
      sleep(500);
    }

## Tap(x, y)#

- x, y { number } 要点击的坐标.

点击位置(x, y), 您可以通过"开发者选项"开启指针位置来确定点击坐标.

## Swipe(x1, y1, x2, y2, [duration])#

- x1, y1 { number } 滑动起点的坐标
- x2, y2 { number } 滑动终点的坐标
- duration { number } 滑动动作所用的时间

滑动. 从(x1, y1)位置滑动到(x2, y2)位置.

# 基于控件的操作#

基于控件的操作指的是选择屏幕上的控件, 获取其信息或对其进行操作. 对于一般软件而言, 基于控件的操作对不同机型有很好的兼容性；但是对于游戏而言, 由于游戏界面并不是由控件构成, 无法采用本章节的方法, 也无法使用本章节的函数. 有关游戏脚本的编写, 请参考《基于坐标的操作》.

基于控件的操作依赖于无障碍服务, 因此最好在脚本开头使用`auto()`函数来确保无障碍服务已经启用. 如果运行到某个需要权限的语句无障碍服务并没启动, 则会抛出异常并跳转到无障碍服务界面. 这样的用户体验并不好, 因为需要重新运行脚本, 后续会加入等待无障碍服务启动并让脚本继续运行的函数.

您也可以在脚本开头使用`"auto";`表示这个脚本需要无障碍服务, 但是不推荐这种做法, 因为这个标记必须在脚本的最开头(前面不能有注释或其他语句、空格等), 我们推荐使用`auto()`函数来确保无障碍服务已启用.

## auto([mode])#

- `mode` { string } 模式

检查无障碍服务是否已经启用, 如果没有启用则抛出异常并跳转到无障碍服务启用界面；同时设置无障碍模式为mode. mode的可选值为：

- `fast` 快速模式. 该模式下会启用控件缓存, 从而选择器获取屏幕控件更快. 对于需要快速的控件操作的脚本可以使用该模式, 一般脚本则没有必要使用该函数.
- `normal` 正常模式, 默认.

如果不加mode参数, 则为正常模式.

建议使用`auto.waitFor()`和`auto.setMode()`代替该函数, 因为`auto()`函数如果无障碍服务未启动会停止脚本；而`auto.waitFor()`则会在在无障碍服务启动后继续运行.

示例：

    auto("fast");

示例2：

    auto();

## auto.waitFor()#

检查无障碍服务是否已经启用, 如果没有启用则跳转到无障碍服务启用界面, 并等待无障碍服务启动；当无障碍服务启动后脚本会继续运行.

因为该函数是阻塞的, 因此除非是有协程特性, 否则不能在ui模式下运行该函数, 建议在ui模式下使用`auto()`函数.

## auto.setMode(mode)#

- `mode` { string } 模式

设置无障碍模式为mode. mode的可选值为：

- `fast` 快速模式. 该模式下会启用控件缓存, 从而选择器获取屏幕控件更快. 对于需要快速的控件查看和操作的脚本可以使用该模式, 一般脚本则没有必要使用该函数.
- `normal` 正常模式, 默认.

## auto.setFlags(flags)#

[v4.1.0新增]

- `flags` { string } | { Array } 一些标志, 来启用和禁用某些特性, 包括：
  - `findOnUiThread` 使用该特性后, 选择器搜索时会在主进程进行. 该特性用于解决线程安全问题导致的次生问题, 不过目前貌似已知问题并不是线程安全问题.
  - `useUsageStats` 使用该特性后, 将会以"使用情况统计"服务的结果来检测当前正在运行的应用包名（需要授予"查看使用情况统计"权限). 如果觉得currentPackage()返回的结果不太准确, 可以尝试该特性.
  - `useShell` 使用该特性后, 将使用shell命令获取当前正在运行的应用的包名、活动名称, 但是需要root权限.

启用有关automator的一些特性. 例如：

    auto.setFlags(["findOnUiThread", "useShell"]);

## auto.service#

[v4.1.0新增]

- AccessibilityService

获取无障碍服务. 如果无障碍服务没有启动, 则返回`null`.

参见AccessibilityService.

## auto.windows#

[v4.1.0新增]

- { Array }

当前所有窗口(AccessibilityWindowInfo)的数组, 可能包括状态栏、输入法、当前应用窗口, 弹出窗口、悬浮窗、分屏应用窗口等. 可以分别获取每个窗口的布局信息.

该函数需要Android 5.0以上才能运行.

## auto.root#

[v4.1.0新增]

- { UiObject }

当前窗口的布局根元素. 如果无障碍服务未启动或者WindowFilter均返回false, 则会返回`null`.

如果不设置windowFilter, 则当前窗口即为活跃的窗口（获取到焦点、正在触摸的窗口）；如果设置了windowFilter, 则获取的是过滤的窗口中的第一个窗口.

如果系统是Android5.0以下, 则始终返回当前活跃的窗口的布局根元素.

## auto.rootInActiveWindow#

[v4.1.0新增]

- { UiObject }

当前活跃的窗口（获取到焦点、正在触摸的窗口）的布局根元素. 如果无障碍服务未启动则为`null`.

## auto.setWindowFilter(filter)#

[v4.1.0新增]

- `filter` { Function } 参数为窗口(AccessibilityWindowInfo), 返回值为Boolean的函数.

设置窗口过滤器. 这个过滤器可以决定哪些窗口是目标窗口, 并影响选择器的搜索. 例如, 如果想要选择器在所有窗口（包括状态栏、输入法等）中搜索, 只需要使用以下代码：

    auto.setWindowFilter(function(window){
        //不管是如何窗口, 都返回true, 表示在该窗口中搜索
        return true;
    });

又例如, 当前使用了分屏功能, 屏幕上有Auto.js和QQ两个应用, 但我们只想选择器对QQ界面进行搜索, 则：

    auto.setWindowFilter(function(window){
        // 对于应用窗口, 他的title属性就是应用的名称, 因此可以通过title属性来判断一个应用
        return window.title == "QQ";
    });

选择器默认是在当前活跃的窗口中搜索, 不会搜索诸如悬浮窗、状态栏之类的, 使用WindowFilter则可以控制搜索的窗口.

需要注意的是, 如果WindowFilter返回的结果均为false, 则选择器的搜索结果将为空.

另外setWindowFilter函数也会影响`auto.windowRoots`的结果.

该函数需要Android 5.0以上才有效.

## auto.windowRoots#

[v4.1.0新增]

- { Array }

返回当前被WindowFilter过滤的窗口的布局根元素组成的数组.

如果系统是Android5.0以下, 则始终返回当前活跃的窗口的布局根元素的数组.

# SimpleActionAutomator#

SimpleActionAutomator提供了一些模拟简单操作的函数, 例如点击文字、模拟按键等. 这些函数可以直接作为全局函数使用.

## click(text[, i])#

- `text` { string } 要点击的文本
- `i` { number } 如果相同的文本在屏幕中出现多次, 则i表示要点击第几个文本, i从0开始计算

返回是否点击成功. 当屏幕中并未包含该文本, 或者该文本所在区域不能点击时返回false, 否则返回true.

该函数可以点击大部分包含文字的按钮. 例如微信主界面下方的"微信", "联系人", "发现", "我"的按钮.
通常与while同时使用以便点击按钮直至成功. 例如:

    while(!click("扫一扫"));

当不指定参数i时则会尝试点击屏幕上出现的所有文字text并返回是否全部点击成功.

i是从0开始计算的, 也就是, `click("啦啦啦", 0)`表示点击屏幕上第一个"啦啦啦", `click("啦啦啦", 1)`表示点击屏幕上第二个"啦啦啦".

> 文本所在区域指的是, 从文本处向其父视图寻找, 直至发现一个可点击的部件为止.

## click(left, top, bottom, right)#

- `left` { number } 要点击的长方形区域左边与屏幕左边的像素距离
- `top` { number } 要点击的长方形区域上边与屏幕上边的像素距离
- `bottom` { number } 要点击的长方形区域下边与屏幕下边的像素距离
- `right` { number } 要点击的长方形区域右边与屏幕右边的像素距离

注意, 该函数一般只用于录制的脚本中使用, 在自己写的代码中使用该函数一般不要使用该函数.

点击在指定区域的控件. 当屏幕中并未包含与该区域严格匹配的区域, 或者该区域不能点击时返回false, 否则返回true.

有些按钮或者部件是图标而不是文字（例如发送朋友圈的照相机图标以及QQ下方的消息、联系人、动态图标）, 这时不能通过`click(text, i)`来点击, 可以通过描述图标所在的区域来点击. left, bottom, top, right描述的就是点击的区域.

至于要定位点击的区域, 可以在悬浮窗使用布局分析工具查看控件的bounds属性.

通过无障碍服务录制脚本会生成该语句.

## longClick(text[, i]))#

- `text` { string } 要长按的文本
- `i` { number } 如果相同的文本在屏幕中出现多次, 则i表示要长按第几个文本, i从0开始计算

返回是否点击成功. 当屏幕中并未包含该文本, 或者该文本所在区域不能点击时返回false, 否则返回true.

当不指定参数i时则会尝试点击屏幕上出现的所有文字text并返回是否全部长按成功.

## scrollUp([i])#

- `i` { number } 要滑动的控件序号

找到第i+1个可滑动控件上滑或左滑. 返回是否操作成功. 屏幕上没有可滑动的控件时返回false.

另外不加参数时`scrollUp()`会寻找面积最大的可滑动的控件上滑或左滑, 例如微信消息列表等.

参数为一个整数i时会找到第i + 1个可滑动控件滑动. 例如`scrollUp(0)`为滑动第一个可滑动控件.

## scrollDown([i])#

- `i` { number } 要滑动的控件序号

找到第i+1个可滑动控件下滑或右滑. 返回是否操作成功. 屏幕上没有可滑动的控件时返回false.

另外不加参数时`scrollUp()`会寻找面积最大的可滑动的控件下滑或右滑.

参数为一个整数i时会找到第i + 1个可滑动控件滑动. 例如`scrollUp(0)`为滑动第一个可滑动控件.

## setText([i, ]text)#

- i { number } 表示要输入的为第i + 1个输入框
- text { string } 要输入的文本

返回是否输入成功. 当找不到对应的文本框时返回false.

不加参数i则会把所有输入框的文本都置为text. 例如`setText("测试")`.

这里的输入文本的意思是, 把输入框的文本置为text, 而不是在原来的文本上追加.

## input([i, ]text)#

- i { number } 表示要输入的为第i + 1个输入框
- text { string } 要输入的文本

返回是否输入成功. 当找不到对应的文本框时返回false.

不加参数i则会把所有输入框的文本追加内容text. 例如`input("测试")`.

# AutoJs6 本体应用#

autojs 全局对象主要包含与 AutoJs6 应用本身相关的属性及方法, 如获取 AutoJs6 的 [ Root 状态 / 语言标签 / 权限状态 ] 等.

autojs

## [m] getLanguage#

### getLanguage()#

`6.2.0`

- returns { java.util.Locale }

获取 AutoJs6 `语言` 设置选项.

此方法返回一个 java.util.Locale 对象, 如需返回其标签名, 如 `en-US`, `zh-CN` 等, 可使用 `autojs.getLanguage().toLanguageTag()` 或直接使用 autojs.getLanguageTag() 方法.

    console.log(autojs.getLanguage().getDisplayName()); /* e.g. 日本語 */

## [m] getLanguageTag#

### getLanguageTag()#

`6.2.0`

- returns { string }

获取 AutoJs6 语言设置选项.

此方法返回 IETF 语言标签, 相当于 `autojs.getLanguage().toLanguageTag()`:

    console.log(autojs.getLanguageTag()); /* e.g. en-US */

此方法可用于设定 i18n 对象的区域:

    i18n.setLocale(autojs.getLanguageTag());

## [m] isRootAvailable#

### isRootAvailable()#

`6.2.0`

- returns { boolean }

获取 AutoJs6 的 Root 权限有效性.

    console.log(autojs.isRootAvailable()); // e.g. true

注意上述示例的检测结果取决于 AutoJs6 的 `强制 Root 权限检查` 设置.
此设置可通过 AutoJs6 应用设置修改, 或 setRootMode 方法携带 `isWriteIntoPreference` 参数实现修改.

## [m] getRootMode#

### getRootMode()#

`6.2.0`

- returns { RootMode }

获取 AutoJs6 的 Root 权限状态.

     /* 是否为 '自动检测 Root 权限' 状态. */
    console.log(autojs.getRootMode() === RootMode.AUTO_DETECT);
    /* 是否为 '强制 Root 模式' 状态. */
    console.log(autojs.getRootMode() === RootMode.FORCE_ROOT);
    /* 是否为 '强制非 Root 模式' 状态. */
    console.log(autojs.getRootMode() === RootMode.FORCE_NON_ROOT);

## [m] setRootMode#

### setRootMode(rootMode, isWriteIntoPreference?)#

`6.2.0` `Overload [1-2]/2`

- rootMode { RootMode | number | boolean | 'auto' | 'root' | 'non-root' } - Root 模式参数
- [ isWriteIntoPreference = `false` ] { boolean } - 是否写入应用设置
- returns { void }

设置 AutoJs6 的 Root 模式.

默认情况下, AutoJs6 将根据 `su` 二进制名称特征来判断是否具有 Root 权限. 但有时设备可能使用了非常规 Root 方式或 Root 权限检测结果出现异常, 此时可设置 `强制 Root 模式` 或 `强制非 Root 模式` 来改变 AutoJs6 对 Root 权限的检测结果.

以设置 '强制 Root 模式' 为例:

    autojs.setRootMode(RootMode.FORCE_ROOT);
    autojs.setRootMode('root'); /* 同上. */
    autojs.setRootMode(1); /* 同上. */
    autojs.setRootMode(true); /* 同上. */

上述示例设置的 Root 模式, 将影响 isRootAvailable 的结果, 使其固定返回 `true`.
如果设置为 `RootMode.FORCE_NON_ROOT`, isRootAvailable 将固定返回 `false`.
如果设置为 `RootMode.AUTO_DETECT`, isRootAvailable 将根据 AutoJs6 是否具有 `su` 二进制名称特征决定其返回结果.

在没有特殊需求的情况下, 建议始终保持 Root 模式为 '自动' 模式.

需额外留意, Root 模式修改仅对当前 `运行时 (Runtime)` 有效, 当脚本结束时, 已设置的 Root 模式将自动还原为 '自动' 模式, 即 `RootMode.AUTO_DETECT`.

如需将保留修改的 Root 模式, 可使用 `isWriteIntoPreference` 参数, 修改将立即写入应用设置中:

    autojs.setRootMode(RootMode.FORCE_ROOT, true);

上述示例代码的效果, 等效于在 AutoJs6 应用中进行如下设置:

    [ AutoJs6 设置 ] - [ 强制 Root 权限检查 ] - [ 强制 Root 模式 ] # [ 选择 ]

## [m] canModifySystemSettings#

### canModifySystemSettings()#

`6.2.0`

- returns { boolean }

获取 AutoJs6 的 `修改系统设置` 权限状态.

    console.log(autojs.canModifySystemSettings()); // e.g. true

拥有 `修改系统设置` 后, AutoJs6 可以通过脚本修改部分系统设置, 如修改屏幕超时参数, 修改媒体音量值等.

## [m] canWriteSecureSettings#

### canWriteSecureSettings()#

`6.2.0`

- returns { boolean }

获取 AutoJs6 的 `修改安全设置` 权限状态.

    console.log(autojs.canWriteSecureSettings()); // e.g. true

拥有 `修改安全设置` 后, AutoJs6 可以通过脚本修改部分安全设置, 如修改屏幕常亮类别参数, 修改无障碍服务列表内容等.

## [m] canDisplayOverOtherApps#

### canDisplayOverOtherApps()#

`6.2.0`

- returns { boolean }

获取 AutoJs6 的 `显示在其他应用上层` 权限状态.

    console.log(autojs.canDisplayOverOtherApps()); // e.g. true

拥有 `显示在其他应用上层` 后, AutoJs6 可以使用悬浮窗工具, 并可通过脚本显示对话框或自定义浮动组件等.

## [p] versionName#

`6.2.0`

- { string }

获取版本名称.

    console.log(autojs.versionName); // e.g. 6.2.0-alpha9
    console.log(autojs.version.name); /* 同上. */

## [p] versionCode#

`6.2.0`

- { number }

获取版本号.

    console.log(autojs.versionCode); // e.g. 1545
    console.log(autojs.version.code); /* 同上. */

## [p] versionDate#

`6.2.0`

- { string }

获取版本日期.

    console.log(autojs.versionDate); // e.g. Dec 18, 2022
    console.log(autojs.version.date); /* 同上. */

## [p] themeColor#

`6.3.0` `Getter`

- <get> ThemeColor

获取 AutoJs6 的主题颜色实例.

    autojs.themeColor.getColorPrimary(); /* 获取 AutoJs6 主题色的主色色值. */

## [p+] version#

### [p] name#

`6.2.0`

- { string }

获取版本名称.

    console.log(autojs.version.name); // e.g. 6.2.0-alpha9
    console.log(autojs.versionName); /* 同上. */

### [p] code#

`6.2.0`

- { number }

获取版本号.

    console.log(autojs.version.code); // e.g. 1545
    console.log(autojs.versionCode); /* 同上. */

### [p] date#

`6.2.0`

- { string }

获取版本日期.

    console.log(autojs.version.date); // e.g. Dec 18, 2022
    console.log(autojs.versionDate); /* 同上. */

### [m] isEqual#

#### isEqual(otherVersion)#

`6.2.0`

- otherVersion { string | Version } - 待比较版本参数
- returns { boolean }

返回 AutoJs6 版本是否与参数对应的版本号等同.

    console.log(autojs.version.isEqual('6.2.0')); // e.g. true

### [m] isHigherThan#

#### isHigherThan(otherVersion)#

`6.2.0`

- otherVersion { string | Version } - 待比较版本参数
- returns { boolean }

返回 AutoJs6 版本是否高于待比较版本.

    console.log(autojs.version.isHigherThan('6.1.3')); // e.g. true

### [m] isLowerThan#

#### isLowerThan(otherVersion)#

`6.2.0`

- otherVersion { string | Version } - 待比较版本参数
- returns { boolean }

返回 AutoJs6 版本是否低于待比较版本.

    console.log(autojs.version.isLowerThan('6.2.0')); // e.g. true

### [m] isAtLeast#

#### isAtLeast(otherVersion)#

`6.2.0` `Overload 1/2`

- otherVersion { string | Version } - 待比较版本参数
- returns { boolean }

返回 AutoJs6 版本是否不低于 (即大于等于) 参数对应的版本号.

    console.log(autojs.version.isAtLeast('6.1.3')); // e.g. true

#### isAtLeast(otherVersion, ignoreSuffix)#

`6.2.0` `Overload 2/2`

- otherVersion { string | Version } - 待比较版本参数
- [ ignoreSuffix = `false` ] { boolean } - 是否忽略版本后缀
- returns { boolean }

返回 AutoJs6 版本是否不低于 (即大于等于) 参数对应的版本号且根据 `ignoreSuffix` 参数决定是否忽略版本后缀.

    console.log(autojs.version.name); // e.g. 6.2.0-alpha9
    console.log(autojs.version.isAtLeast('6.2.0')); // e.g. false
    console.log(autojs.version.isAtLeast('6.2.0', true)); // e.g. true

## [p+] R#

使用 R 类的子类中的静态整数可访问相应的应用资源, 如 `R.string` 访问字符串资源, `R.drawable` 访问可绘制资源等.

global.R 的别名属性, 参阅 全局对象 (Global) 章节.

# 通用应用 (App)#

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

Marked by SuperMonster003 on Oct 22, 2022.

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");

## app.intent(options)#

- `options` { Object } 选项, 包括：

  - `action` { string } 意图的Action, 指意图要完成的动作, 是一个字符串常量, 比如"android.intent.action.SEND". 当action以"android.intent.action"开头时, 可以省略前缀, 直接用"SEND"代替. 参见Actions.

  - `type` { string } 意图的MimeType, 表示和该意图直接相关的数据的类型, 表示比如"text/plain"为纯文本类型.

  - `data` { string } 意图的Data, 表示和该意图直接相关的数据, 是一个Uri, 可以是文件路径或者Url等. 例如要打开一个文件, action为"android.intent.action.VIEW", data为"file:///sdcard/1.txt".

  - `category` { Array } 意图的类别. 比较少用. 参见Categories.

  - `packageName` { string } 目标包名

  - `className` { string } 目标Activity或Service等组件的名称

  - `extras` { Object } 以键值对构成的这个Intent的Extras(额外信息). 提供该意图的其他信息, 例如发送邮件时的邮件标题、邮件正文. 参见Extras.

  - `flags` { Array } intent的标识, 字符串数组, 例如`["activity_new_task", "grant_read_uri_permission"]`. 参见Flags.

    [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或参考Android指南: Intent.

## 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参数的规范.

## app.parseUri(uri)#

[v4.1.0新增]

- `uri` { string } 一个代表Uri的字符串, 例如"file:///sdcard/1.txt", "https://www.autojs.org"
- 返回 { Uri } 一个代表Uri的对象, 参见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.

从一个文件路径创建一个uri对象. 需要注意的是, 在高版本Android上, 由于系统限制直接在Uri暴露文件的绝对路径, 因此返回的Uri会是诸如`content://...`的形式.

## app.getInstalledApps([options])#

[Pro 8.0.0新增]

- `options` { Object } 选项, 包括：
  - `get`: 指定返回的应用信息中包含的信息
    - `"activities"` 应用的Activity组件信息
    - `"configurations"` 应用的硬件配置
    - `"gids"` 应用的group id
    - `"instrumentation"` 应用的Instrumentation信息
    - `"intent_filters"` 应用的意图过滤
    - `"meta_data"` 应用的元信息（默认）
    - `"permissions"` 应用的权限信息
    - `"providers"` 应用的ContentProvider组件信息
    - `"receivers"` 应用的BroadcastReceiver组件信息
    - `"services"` 应用的Service组件信息
    - `"shared_library_files"` 应用的动态链接库文件信息
    - `"signatures"` 应用的签名信息（已弃用
    - `"signing_certificates"` 应用的签名信息
    - `"uri_permission_patterns"`
    - `"disabled_components"` 被卸载的但保留了数据的应用
    - `"disabled_until_used_components"` 禁用直到被使用的组件
    - `"uninstalled_packages"` 被卸载的但保留了数据的应用
  - `match`: 指定要匹配的应用列表
    - `"uninstalled_packages"` 被卸载的但保留了数据的应用
    - `"disabled_components"` 被禁用的组件
    - `"disabled_until_used_components"` 禁用直到被使用的组件
    - `"system_only"` 只匹配系统应用
    - `"factory_only"` 只匹配预装应用
    - `"apex"` APEX应用
- 返回 { Array\<ApplicationInfo> }

返回为当前用户安装的所有应用程序包的列表. 如果设置了match选项 `uninstalled_packages`, 则包括被删除但保留了数据的应用程序. 获取安装的应用列表.

返回值是ApplicationInfo对象的数组. 如果没有安装任何应用, 则返回一个空数组.

选项options的match选项用于指定要返回哪些应用程序, get选项用于指定返回的应用程序携带哪些信息.

    let apps = $app.getInstalledApps({
        matcg
    })

# 颜色 (Color)#

colors 模块可用于 [ 颜色模式转换 / 色彩空间转换 / 颜色分量合成及分解 ] 等.
同时包含一些颜色相关的工具, 如 [ 计算亮度值 / 相似度比较 ] 等.

colors 模块与 images 模块配合使用, 可完成更多图色方面的功能.

## 颜色表示#

AutoJs6 支持以下方式表示一个颜色:

- 颜色代码 (ColorHex)
  - 字面量
    - `#RGB` (如 `#F00` 表示红色, 相当于 `#FF0000`)
    - `#RRGGBB` (如 `#FF0000` 表示红色)
    - `#AARRGGBB` (如 `#80FF0000` 表示半透明红色)
  - 方法
    - colors.toHex (如 `colors.toHex(0xFF0000)` 表示红色对应的颜色字符串, 结果为 `#FF0000`)
    - colors.toFullHex (如 `colors.toFullHex(0xFF0000)` 表示红色对应的完全颜色字符串, 结果为 `#FFFF0000`)
    - ... ...
- 颜色整数 (ColorInt)
  - 字面量
    - `0xAARRGGBB` (如 `0x8000FF00` 在 `Java` 的 `Integer` 范围对应值表示半透明绿色)
  - 方法
    - colors.rgb (如 `colors.rgb(255, 0, 0)` 表示红色)
    - colors.argb (如 `colors.argb(128, 255, 0, 0)` 表示半透明红色)
    - colors.rgba (如 `colors.rgba(255, 0, 0, 128)` 表示半透明红色)
    - colors.hsv (如 `colors.hsv(0, 1, 1)` 表示红色)
    - colors.hsva (如 `colors.rgba(0, 1, 1, 0.5)` 表示半透明红色)
    - colors.hsl (如 `colors.hsl(0, 1, 0.5)` 表示红色)
    - colors.hsla (如 `colors.hsl(0, 1, 0.5, 0.5)` 表示半透明红色)
    - colors.toInt (如 `colors.toInt('#FF0000')` 表示红色对应的颜色整数, 结果为 `-65536`)
    - ... ...
  - 常量
    - colors.android.RED (Android 颜色列表 的红色颜色整数)
    - colors.android.BLACK (Android 颜色列表 的黑色颜色整数)
    - ... ...
    - colors.css.RED (Css 颜色列表 的红色颜色整数)
    - colors.css.BLACK (Css 颜色列表 的黑色颜色整数)
    - ... ...
    - colors.web.RED (Web 颜色列表 的红色颜色整数)
    - colors.web.BLACK (Web 颜色列表 的黑色颜色整数)
    - ... ...
    - colors.material.ORANGE (Material 颜色列表 的橙色颜色整数)
    - colors.material.ORANGE_300 (Material 颜色列表 的 300 色号橙色颜色整数)
    - ... ...
    - colors.RED (融合颜色列表 的红色颜色整数)
    - colors.BLACK (融合颜色列表 的黑色颜色整数)
    - colors.ORANGE (融合颜色列表 的橙色颜色整数)
    - ... ...
- 颜色分量数组 (ColorComponents)
  - 方法
    - colors.toRgb (颜色分量数组 `[R,G,B]`)
    - colors.toRgba (颜色分量数组 `[R,G,B,A]`)
    - colors.toArgb (颜色分量数组 `[A,R,G,B]`)
    - colors.toHsv (颜色分量数组 `[H,S,V]`)
    - colors.toHsva (颜色分量数组 `[H,S,V,A]`)
    - colors.toHsl (颜色分量数组 `[H,S,L]`)
    - colors.toHsla (颜色分量数组 `[H,S,L,A]`)
    - ... ...
- 颜色名称 (ColorName)
  - 常量
    - "red" (红色)
    - "black" (黑色)
    - "orange" (橙色)
    - ... ...

## 黑色与 0#

需特别留意, 黑色的颜色字符串为 `#000000`, 它是完全颜色字符串 `#FF000000` 的简写形式, 其 ARGB 分量表示为 `argb(255, 0, 0, 0)`.

因此黑色的颜色整数不是 `0`, 而是 `-16777216`.

颜色整数 `0` 对应的是完全透明色, 即 `#00000000`, 其 ARGB 分量表示为 `argb(0, 0, 0, 0)`.

colors

## [m] toInt#

### toInt(color)#

`6.2.0`

- color { OmniColor } - 颜色参数
- returns { ColorInt } - 颜色整数

将颜色参数转换为 颜色整数 (ColorInt).

    /* ColorHex - 颜色代码. */
    colors.toInt('#CC5500'); // -3386112
    colors.toInt('#C50'); // -3386112
    colors.toInt('#FFCC5500'); // -3386112

    /* ColorInt - 颜色整数. */
    colors.toInt(0xFFCC5500); // -3386112
    colors.toInt(colors.web.BURNT_ORANGE); // -3386112

    /* ColorName - 颜色名称. */
    colors.toInt('BURNT_ORANGE'); // -3386112
    colors.toInt('burnt-orange'); // -3386112

## [m] toHex#

### toHex(color)#

`6.2.0` `Overload 1/3`

- color { OmniColor } - 颜色参数
- returns { ColorHex } - 颜色代码

将颜色参数转换为 颜色代码 (ColorHex).

    /* ColorHex - 颜色代码. */
    colors.toHex('#CC5500'); // #CC5500
    colors.toHex('#C50'); // #CC5500
    colors.toHex('#DECC5500'); // #DECC5500
    colors.toHex('#FFCC5500'); /* #CC5500, A (alpha) 分量被省略. */

    /* ColorInt - 颜色整数. */
    colors.toHex(0xFFCC5500); // #CC5500
    colors.toHex(colors.web.BURNT_ORANGE); // #CC5500

    /* ColorName - 颜色名称. */
    colors.toHex('BURNT_ORANGE'); // #CC5500
    colors.toHex('burnt-orange'); // #CC5500

当 `A (alpha)` 分量为 `100% (255/255;100/100)` 时, `FF` 会自动省略,
如 `#FFC0C0C0` 将自动转换为 `#C0C0C0`, 此方法相当于 `toHex(color, 'auto')`.

### toHex(color, alpha)#

`6.2.0` `Overload 2/3`

- color { OmniColor } - 颜色参数
- [ alpha = `'auto'` ] { boolean | `'keep'` | `'none'` | `'auto'` } - A (alpha) 分量参数
- returns { ColorHex } - 颜色代码

将颜色参数转换为 颜色代码 (ColorHex), 并根据 `alpha` 参数决定颜色代码 `A (alpha)` 分量的显示状态.

`A (alpha)` 分量参数取值表:

取值 | 含义 | 默认
--- | --- | ---
'keep' / true | 强制显示 A 分量, 不论 A 分量是否为 0xFF |
'none' / false | 强制去除 A 分量, 只保留 R / G / B 分量 |
'auto' | 根据 A 分量是否为 0xFF 自动决定显示状态 | √

    let cA = '#AAC0C0C0';
    let cB = '#FFC0C0C0';
    let cC = '#C0C0C0';

    colors.toHex(cA, 'auto'); /* #AAC0C0C0, 'auto' 参数可省略. */
    colors.toHex(cB, 'auto'); /* #C0C0C0, 'auto' 参数可省略. */
    colors.toHex(cC, 'auto'); /* #C0C0C0, 'auto' 参数可省略. */

    /* cA 舍弃 A 分量. */
    colors.toHex(cA, false); // #C0C0C0
    colors.toHex(cA, 'none'); /* 同上. */

    /* cB 保留 A 分量. */
    colors.toHex(cB, true); // #FFC0C0C0
    colors.toHex(cB, 'keep'); /* 同上. */

    /* cC 强制显示 A 分量. */
    colors.toHex(cC, true); // #FFC0C0C0
    colors.toHex(cC, 'keep'); /* 同上. */

### toHex(color, length)#

`6.2.0` `Overload 3/3`

- color { OmniColor } - 颜色参数
- length { `8` | `6` | `3` } - Hex 代码长度参数
- returns { ColorHex } - 颜色代码

将颜色参数转换为 颜色代码 (ColorHex), 并根据 `length` 参数决定颜色代码的显示状态.

Hex 代码长度参数取值表:

取值 | 含义
--- | ---
8 | 强制显示 A 分量, 结果格式为 #AARRGGBB
6 | 强制去除 A 分量, 结果格式为 #RRGGBB
3 | 强制去除 A 分量, 结果格式为 #RGB

    let cA = '#AA9966CC';
    let cB = '#FF9966CC';
    let cC = '#9966CC';
    let cD = '#FAEBD7';

    /* 转换为 8 长度颜色代码, 强制保留 A 分量. */
    colors.toHex(cA, 8); // #AA9966CC
    colors.toHex(cB, 8); // #FF9966CC
    colors.toHex(cC, 8); // #FF9966CC
    colors.toHex(cD, 8); // #FFFAEBD7

    /* 转换为 6 长度颜色代码, 强制去除 A 分量. */
    colors.toHex(cA, 6); // #9966CC
    colors.toHex(cB, 6); // #9966CC
    colors.toHex(cC, 6); // #9966CC
    colors.toHex(cD, 6); // #FAEBD7

    /* 转换为 3 长度颜色代码, 强制去除 A 分量. */
    colors.toHex(cA, 3); // #96C
    colors.toHex(cB, 3); // #96C
    colors.toHex(cC, 3); // #96C
    colors.toHex(cD, 3); /* 抛出异常. */

## [m] toFullHex#

### toFullHex(color)#

`6.2.0`

- color { OmniColor } - 颜色参数
- returns { ColorHex } - 颜色代码的完整形式

将颜色参数强制转换为 颜色代码 (ColorHex) 的完整形式 (#AARRGGBB).

此方法为 colors.toHex(color, 8) 的别名方法.

    colors.toHex('#CC5500'); // #CC5500
    colors.toFullHex('#CC5500'); // #FFCC5500

## [m] build#

### build(color?)#

`6.3.0` `Overload [1-2]/4`

- [ color = `Colors.BLACK` ] { OmniColor } - 颜色参数
- returns { Color } - Color 实例

构建一个 Color 实例, 相当于 `new Color(color?)` 或 `Color(color?)`.

    colors.build('dark-orange') /* 以深橙色构建 Color 实例 */
        .setAlpha(0.85) /* 设置透明度 85%. */
        .removeBlue() /* 移除 B (blue) 分量. */
        .toHex(); // #D9FF8C00

    /* 构建空 Color 实例, 设置 HSLA 分量并转换为 Hex 代码. */
    colors.build().setHsla(0.25, 0.8, 0.64, 0.9).toHex(); // #E6A3ED5A

### build(red, green, blue, alpha?)#

`6.3.0` `Overload [3-4]/4`

- red { ColorComponent } - 颜色分量 - R (red)
- green { ColorComponent } - 颜色分量 - G (green)
- blue { ColorComponent } - 颜色分量 - B (blue)
- [ alpha = `1` ] { ColorComponent } - 颜色分量 - A (alpha)
- returns { ColorInt }

构建一个 Color 实例, 相当于 `new Color(red, green, blue, alpha?)`.

    colors.build(120, 60, 240).setAlpha(0.85).toHex(); // #D9783CF0
    colors.build(120, 60, 240, 0.85).toHex(); /* 同上. */
    colors.build().setRgba(120, 60, 240, 0.85).toHex(); /* 同上. */

## [m] summary#

### summary(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { string } - 颜色摘要

获取颜色摘要.

格式为 `hex($HEX), rgba($R,$G,$B/$A), int($INT)`.

其中, `A (alpha)` 分量将显示为 `0..1` 范围, 至少一位小数, 至多两位小数:

分量值 | 显示值
--- | ---
0 | 0.0
1 | 1.0
0.64 | 0.64
128 | 0.5
255 | 1.0
100 | 0.39

示例:

    // hex(#009688), rgba(0,150,136/1.0), int(-16738680)
    colors.summary('#009688');

    // hex(#BE009688), rgba(0,150,136/0.75), int(-1107257720)
    colors.summary('#BE009688');

    // hex(#FF0000), rgba(255,0,0/1.0), int(-65536)
    colors.summary('red');

    // hex(#6400008B), rgba(0,0,139/0.39), int(1677721739)
    colors.build('dark-blue').setAlpha(100).summary();

## [m] parseColor#

### parseColor(color)#

- color { string } - 颜色参数
- returns { ColorInt } - 颜色整数

将颜色参数转换为 颜色整数 (ColorInt).

类似 toInt, 但参数接受范围相对狭小且类型及数值要求更加严格.
parseColor 的颜色参数仅支持六位数及八位数颜色代码及部分颜色名称.

支持的颜色名称 (不区分大小写):

> 'aqua', 'black', 'blue', 'cyan', 'darkgray', 'darkgrey',

> 'fuchsia', 'gray', 'green', 'grey', 'lightgray',

> 'lightgrey', 'lime', 'magenta', 'maroon', 'navy', 'olive',

> 'purple', 'red', 'silver', 'teal', 'white', 'yellow'`.

下表列出部分 toInt 与 parseColor 传参后的结果对照:

参数 | toInt | parseColor
--- | --- | ---
'blue' | -16776961 | -16776961
'burnt-orange' | -3386112 | # 抛出异常 #
'#FFCC5500' | -3386112 | -3386112
'#CC5500' | -3386112 | -3386112
'#C50' | -3386112 | # 抛出异常 #
0xFFCC5500 | -3386112 | # 抛出异常 #
colors.web.BURNT_ORANGE | -3386112 | # 抛出异常 #

除非需要考虑多版本兼容, 否则建议始终使用 toInt 替代 parseColor.

## [m] toString#

### toString(color)#

`[6.2.0]` `Overload 1/3`

- color { OmniColor } - 颜色参数
- returns { ColorHex } - 颜色代码

将颜色参数转换为 颜色代码 (ColorHex).

toHex(color) 的别名方法.

### toString(color, alpha)#

`6.2.0` `Overload 2/3`

- color { OmniColor } - 颜色参数
- [ alpha = `'auto'` ] { boolean | `'keep'` | `'none'` | `'auto'` } - A (alpha) 分量参数
- returns { ColorHex } - 颜色代码

将颜色参数转换为 颜色代码 (ColorHex), 并根据 `alpha` 参数决定颜色代码 `A (alpha)` 分量的显示状态.

toHex(color, alpha) 的别名方法.

### toString(color, length)#

`6.2.0` `Overload 3/3`

- color { OmniColor } - 颜色参数
- length { `8` | `6` | `3` } - Hex 代码长度参数
- returns { ColorHex } - 颜色代码

将颜色参数转换为 颜色代码 (ColorHex), 并根据 `length` 参数决定颜色代码的显示状态.

toHex(color, length) 的别名方法.

## [m] alpha#

### alpha(color)#

`Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { IntRange[0..255] }

获取颜色的 `A (alpha)` 分量, 取值范围 `[0..255]`.

    colors.alpha('#663399'); // 255
    colors.alpha(colors.TRANSPARENT); // 0
    colors.alpha('#05060708'); // 5

### alpha(color, options)#

`6.3.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- options {{
  - [ max = `255` ]?: `1` | `255` - 范围最大值
- }} - 选项参数
- returns { IntRange[0..1] | IntRange[0..255] }

获取颜色的 `A (alpha)` 分量.

取值范围 `[0..1]` (`options.max` 为 `1`) 或 `[0..255]` (`options.max` 为 `255` 或不指定).

    colors.alpha('#663399', { max: 1 }); // 1
    colors.alpha('#663399', { max: 255 }); // 255
    colors.alpha('#663399'); /* 同上. */

    colors.alpha('#05060708', { max: 1 }); // 0.0196078431372549
    colors.alpha('#05060708', { max: 255 }); // 5
    colors.alpha('#05060708'); /* 同上. */

当 `options.max` 为 `1` 时, 相当于 colors.alphaDouble 方法.

## [m] alphaDouble#

### alphaDouble(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { Range[0..1] }

获取颜色的 `A (alpha)` 分量, 取值范围 `[0..1]`.

相当于 `colors.alpha(color, { max: 1 })`.

    colors.alphaDouble('#663399'); // 1
    colors.alphaDouble(colors.TRANSPARENT); // 0

    colors.alphaDouble('#05060708'); // 0.0196078431372549
    colors.alpha('#05060708', { max: 1 }); /* 同上. */

## [m] getAlpha#

### getAlpha(color)#

`6.3.0` `Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { IntRange[0..255] }

获取颜色的 `A (alpha)` 分量, 取值范围 `[0..255]`.

colors.alpha(color) 的别名方法.

### getAlpha(color, options)#

`6.3.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- options {{
  - [ max = `255` ]?: `1` | `255` - 范围最大值
- }} - 选项参数
- returns { IntRange[0..1] | IntRange[0..255] }

获取颜色的 `A (alpha)` 分量.

colors.alpha(color, options) 的别名方法.

## [m] getAlphaDouble#

### getAlphaDouble(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { Range[0..1] }

获取颜色的 `A (alpha)` 分量, 取值范围 `[0..1]`.

colors.alphaDouble(color) 的别名方法.

## [m] setAlpha#

### setAlpha(color, alpha)#

`6.3.0`

- color { OmniColor } - 颜色参数
- alpha { ColorComponent } - 颜色分量 - A (alpha)
- returns { ColorInt }

设置颜色的 `A (alpha)` 分量, 返回新颜色的颜色整数.

    colors.toHex(colors.setAlpha('#663399', 0x80)); // #80663399
    colors.toHex(colors.setAlpha('#663399', 0.5)); /* 同上, 0.5 解析为百分数分量, 即 50%. */

    colors.toHex(colors.setAlpha('#663399', 255)); // #FF663399
    colors.toHex(colors.setAlpha('#663399', 1)); /* 同上, 1 默认作为百分数分量, 即 100%. */

## [m] setAlphaRelative#

### setAlphaRelative(color, percentage)#

`6.3.1`

- color { OmniColor } - 颜色参数
- percentage { ColorComponent } - 相对百分数
- returns { ColorInt }

针对 `A (alpha)` 分量设置其相对百分比, 返回新颜色的颜色整数.

如当前颜色 `A (alpha)` 分量为 `80`, 希望设置 `A` 分量为 `50%` 相对量, 即 `40`:

    colors.setAlphaRelative(color, 0.5);
    colors.setAlphaRelative(color, '50%'); /* 效果同上. */

同样地, 如希望设置 `A` 分量为 `1.5` 倍相对量, 即 `120`:

    colors.setAlphaRelative(color, 1.5);
    colors.setAlphaRelative(color, '150%');

当设置的相对量超过 `255` 时, 将以 `255` 为最终值:

    colors.setAlphaRelative(color, 10); /* A 分量最终值为 255, 而非 800. */

特别地, 当原本颜色的 `A` 分量为 `0` 时, 无论如何设置相对量, `A` 分量均保持 `0` 值.

## [m] removeAlpha#

### removeAlpha(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { ColorInt }

去除颜色的 `A (alpha)` 分量, 返回新颜色的颜色整数.

    colors.toHex(colors.removeAlpha('#BE663399')); // #663399
    colors.toHex(colors.removeAlpha('#CC5500')); // #CC5500
    `

相当于 `colors.setAlpha(color, 0)`.

## [m] red#

### red(color)#

`Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { IntRange[0..255] }

获取颜色的 `R (red)` 分量, 取值范围 `[0..255]`.

    colors.red('#663399'); // 102
    colors.red(colors.TRANSPARENT); // 0
    colors.red('#05060708'); // 6

### red(color, options)#

`6.3.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- options {{
  - [ max = `255` ]?: `1` | `255` - 范围最大值
- }} - 选项参数
- returns { IntRange[0..1] | IntRange[0..255] }

获取颜色的 `R (red)` 分量.

取值范围 `[0..1]` (`options.max` 为 `1`) 或 `[0..255]` (`options.max` 为 `255` 或不指定).

    colors.red('#663399', { max: 1 }); // 0.4
    colors.red('#663399', { max: 255 }); // 102
    colors.red('#663399'); /* 同上. */

当 `options.max` 为 `1` 时, 相当于 colors.redDouble 方法.

## [m] redDouble#

### redDouble(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { Range[0..1] }

获取颜色的 `R (red)` 分量, 取值范围 `[0..1]`.

相当于 `colors.red(color, { max: 1 })`.

    colors.redDouble('#663399'); // 0.4

## [m] getRed#

### getRed(color)#

`6.3.0` `Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { IntRange[0..255] }

获取颜色的 `R (red)` 分量, 取值范围 `[0..255]`.

colors.red(color) 的别名方法.

### getRed(color, options)#

`6.3.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- options {{
  - [ max = `255` ]?: `1` | `255` - 范围最大值
- }} - 选项参数
- returns { IntRange[0..1] | IntRange[0..255] }

获取颜色的 `R (red)` 分量.

colors.red(color, options) 的别名方法.

## [m] getRedDouble#

### getRedDouble(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { Range[0..1] }

获取颜色的 `R (red)` 分量, 取值范围 `[0..1]`.

colors.redDouble(color) 的别名方法.

## [m] setRed#

### setRed(color, red)#

`6.3.0`

- color { OmniColor } - 颜色参数
- red { ColorComponent } - 颜色分量 - R (red)
- returns { ColorInt }

设置颜色的 `R (red)` 分量, 返回新颜色的颜色整数.

    colors.toHex(colors.setRed('#663399', 0x80)); // #803399
    colors.toHex(colors.setRed('#663399', 0.5)); /* 同上, 0.5 解析为百分数分量, 即 50%. */

    colors.toHex(colors.setRed('#663399', 255)); // #FF3399
    colors.toHex(colors.setRed('#663399', 1)); /* #013399, 不同上. 1 默认作为整数分量, 而非 100%. */

## [m] setRedRelative#

### setRedRelative(color, percentage)#

`6.3.1`

- color { OmniColor } - 颜色参数
- percentage { ColorComponent } - 相对百分数
- returns { ColorInt }

针对 `R (red)` 分量设置其相对百分比, 返回新颜色的颜色整数.

如当前颜色 `R (red)` 分量为 `80`, 希望设置 `R` 分量为 `50%` 相对量, 即 `40`:

    colors.setRedRelative(color, 0.5);
    colors.setRedRelative(color, '50%'); /* 效果同上. */

同样地, 如希望设置 `R` 分量为 `1.5` 倍相对量, 即 `120`:

    colors.setRedRelative(color, 1.5);
    colors.setRedRelative(color, '150%');

当设置的相对量超过 `255` 时, 将以 `255` 为最终值:

    colors.setRedRelative(color, 10); /* R 分量最终值为 255, 而非 800. */

特别地, 当原本颜色的 `R` 分量为 `0` 时, 无论如何设置相对量, `R` 分量均保持 `0` 值.

## [m] removeRed#

### removeRed(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { ColorInt }

去除颜色的 `R (red)` 分量, 返回新颜色的颜色整数.

    colors.toHex(colors.removeRed('#BE663399')); // #BE003399
    colors.toHex(colors.removeRed('#CC5500')); // #005500
    `

相当于 `colors.setRed(color, 0)`.

## [m] green#

### green(color)#

`Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { IntRange[0..255] }

获取颜色的 `G (green)` 分量, 取值范围 `[0..255]`.

    colors.green('#663399'); // 51
    colors.green(colors.TRANSPARENT); // 0
    colors.green('#05060708'); // 7

### green(color, options)#

`6.3.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- options {{
  - [ max = `255` ]?: `1` | `255` - 范围最大值
- }} - 选项参数
- returns { IntRange[0..1] | IntRange[0..255] }

获取颜色的 `G (green)` 分量.

取值范围 `[0..1]` (`options.max` 为 `1`) 或 `[0..255]` (`options.max` 为 `255` 或不指定).

    colors.green('#663399', { max: 1 }); // 0.2
    colors.green('#663399', { max: 255 }); // 51
    colors.green('#663399'); /* 同上. */

当 `options.max` 为 `1` 时, 相当于 colors.greenDouble 方法.

## [m] greenDouble#

### greenDouble(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { Range[0..1] }

获取颜色的 `G (green)` 分量, 取值范围 `[0..1]`.

相当于 `colors.green(color, { max: 1 })`.

    colors.greenDouble('#663399'); // 0.2

## [m] getGreen#

### getGreen(color)#

`6.3.0` `Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { IntRange[0..255] }

获取颜色的 `G (green)` 分量, 取值范围 `[0..255]`.

colors.green(color) 的别名方法.

## [m] setGreenRelative#

### setGreenRelative(color, percentage)#

`6.3.1`

- color { OmniColor } - 颜色参数
- percentage { ColorComponent } - 相对百分数
- returns { ColorInt }

针对 `G (green)` 分量设置其相对百分比, 返回新颜色的颜色整数.

如当前颜色 `G (green)` 分量为 `80`, 希望设置 `G` 分量为 `50%` 相对量, 即 `40`:

    colors.setGreenRelative(color, 0.5);
    colors.setGreenRelative(color, '50%'); /* 效果同上. */

同样地, 如希望设置 `G` 分量为 `1.5` 倍相对量, 即 `120`:

    colors.setGreenRelative(color, 1.5);
    colors.setGreenRelative(color, '150%');

当设置的相对量超过 `255` 时, 将以 `255` 为最终值:

    colors.setGreenRelative(color, 10); /* G 分量最终值为 255, 而非 800. */

特别地, 当原本颜色的 `G` 分量为 `0` 时, 无论如何设置相对量, `G` 分量均保持 `0` 值.

### getGreen(color, options)#

`6.3.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- options {{
  - [ max = `255` ]?: `1` | `255` - 范围最大值
- }} - 选项参数
- returns { IntRange[0..1] | IntRange[0..255] }

获取颜色的 `G (green)` 分量.

colors.green(color, options) 的别名方法.

## [m] getGreenDouble#

### getGreenDouble(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { Range[0..1] }

获取颜色的 `G (green)` 分量, 取值范围 `[0..1]`.

colors.greenDouble(color) 的别名方法.

## [m] setGreen#

### setGreen(color, green)#

`6.3.0`

- color { OmniColor } - 颜色参数
- green { ColorComponent } - 颜色分量 - G (green)
- returns { ColorInt }

设置颜色的 `G (green)` 分量, 返回新颜色的颜色整数.

    colors.toHex(colors.setGreen('#663399', 0x80)); // #668099
    colors.toHex(colors.setGreen('#663399', 0.5)); /* 同上, 0.5 解析为百分数分量, 即 50%. */

    colors.toHex(colors.setGreen('#663399', 255)); // #66FF99
    colors.toHex(colors.setGreen('#663399', 1)); /* #660199, 不同上. 1 默认作为整数分量, 而非 100%. */

## [m] removeGreen#

### removeGreen(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { ColorInt }

去除颜色的 `G (green)` 分量, 返回新颜色的颜色整数.

    colors.toHex(colors.removeGreen('#BE663399')); // #BE660099
    colors.toHex(colors.removeGreen('#CC5500')); // #CC0000
    `

相当于 `colors.setGreen(color, 0)`.

## [m] blue#

### blue(color)#

`Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { IntRange[0..255] }

获取颜色的 `B (blue)` 分量, 取值范围 `[0..255]`.

    colors.blue('#663399'); // 153
    colors.blue(colors.TRANSPARENT); // 0
    colors.blue('#05060708'); // 8

### blue(color, options)#

`6.3.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- options {{
  - [ max = `255` ]?: `1` | `255` - 范围最大值
- }} - 选项参数
- returns { IntRange[0..1] | IntRange[0..255] }

获取颜色的 `B (blue)` 分量.

取值范围 `[0..1]` (`options.max` 为 `1`) 或 `[0..255]` (`options.max` 为 `255` 或不指定).

    colors.blue('#663399', { max: 1 }); // 0.6
    colors.blue('#663399', { max: 255 }); // 153
    colors.blue('#663399'); /* 同上. */

当 `options.max` 为 `1` 时, 相当于 colors.blueDouble 方法.

## [m] blueDouble#

### blueDouble(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { Range[0..1] }

获取颜色的 `A (blue)` 分量, 取值范围 `[0..1]`.

相当于 `colors.blue(color, { max: 1 })`.

    colors.blueDouble('#663399'); // 0.6

## [m] getBlue#

### getBlue(color)#

`6.3.0` `Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { IntRange[0..255] }

获取颜色的 `B (blue)` 分量, 取值范围 `[0..255]`.

colors.blue(color) 的别名方法.

### getBlue(color, options)#

`6.3.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- options {{
  - [ max = `255` ]?: `1` | `255` - 范围最大值
- }} - 选项参数
- returns { IntRange[0..1] | IntRange[0..255] }

获取颜色的 `B (blue)` 分量.

colors.blue(color, options) 的别名方法.

## [m] getBlueDouble#

### getBlueDouble(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { Range[0..1] }

获取颜色的 `A (blue)` 分量, 取值范围 `[0..1]`.

colors.blueDouble(color) 的别名方法.

## [m] setBlue#

### setBlue(color, blue)#

`6.3.0`

- color { OmniColor } - 颜色参数
- blue { ColorComponent } - 颜色分量 - B (blue)
- returns { ColorInt }

设置颜色的 `B (blue)` 分量, 返回新颜色的颜色整数.

    colors.toHex(colors.setBlue('#663399', 0x80)); // #663380
    colors.toHex(colors.setBlue('#663399', 0.5)); /* 同上, 0.5 解析为百分数分量, 即 50%. */

    colors.toHex(colors.setBlue('#663399', 255)); // #6633FF
    colors.toHex(colors.setBlue('#663399', 1)); /* #663301, 不同上. 1 默认作为整数分量, 而非 100%. */

## [m] setBlueRelative#

### setBlueRelative(color, percentage)#

`6.3.1`

- color { OmniColor } - 颜色参数
- percentage { ColorComponent } - 相对百分数
- returns { ColorInt }

针对 `B (blue)` 分量设置其相对百分比, 返回新颜色的颜色整数.

如当前颜色 `B (blue)` 分量为 `80`, 希望设置 `B` 分量为 `50%` 相对量, 即 `40`:

    colors.setBlueRelative(color, 0.5);
    colors.setBlueRelative(color, '50%'); /* 效果同上. */

同样地, 如希望设置 `B` 分量为 `1.5` 倍相对量, 即 `120`:

    colors.setBlueRelative(color, 1.5);
    colors.setBlueRelative(color, '150%');

当设置的相对量超过 `255` 时, 将以 `255` 为最终值:

    colors.setBlueRelative(color, 10); /* B 分量最终值为 255, 而非 800. */

特别地, 当原本颜色的 `B` 分量为 `0` 时, 无论如何设置相对量, `B` 分量均保持 `0` 值.

## [m] removeBlue#

### removeBlue(color)#

`6.3.0`

- color { OmniColor } - 颜色参数
- returns { ColorInt }

去除颜色的 `B (blue)` 分量, 返回新颜色的颜色整数.

    colors.toHex(colors.removeBlue('#BE663399')); // #BE663300
    colors.toHex(colors.removeBlue('#CC5500')); // #CC5500
    `

相当于 `colors.setBlue(color, 0)`.

## [m] rgb#

### rgb(color)#

`[6.2.0]` `Overload 1/3`

- color { OmniColor } - 颜色参数
- returns { ColorInt }

获取 `color` 参数对应的 颜色整数 (ColorInt).

`color` 参数为颜色代码时, 支持情况如下:

格式 | 备注
--- | ---
#RRGGBB | 正常
#RGB | 正常
#AARRGGBB | A (alpha) 分量被忽略

方法调用结果的 `A (alpha)` 分量恒为 `255`, 意味着 `color` 参数中的 `A` 分量信息将被忽略.

    colors.rgb('#663399');
    colors.rgb('#DE663399'); /* 同上, A 分量被忽略. */

### rgb(red, green, blue)#

`6.2.0` `Overload 2/3`

- red { ColorComponent } - 颜色分量 - R (red)
- green { ColorComponent } - 颜色分量 - G (green)
- blue { ColorComponent } - 颜色分量 - B (blue)
- returns { ColorInt }

通过 颜色分量 获取 颜色整数 (ColorInt).

    colors.rgb(255, 128, 9);
    colors.rgb(0xFF, 0x80, 0x09); /* 同上. */
    colors.rgb('#FF8009'); /* 同上. */
    colors.rgb(1, 0.5, '3.53%'); /* 同上. */

### rgb(components)#

`6.2.0` `Overload 3/3`

- components { ColorComponents[] } - 颜色分量数组
- returns { ColorInt }

通过 颜色分量数组 获取 颜色整数 (ColorInt).

    colors.rgb([ 255, 128, 9 ]);
    colors.rgb([ 0xFF, 0x80, 0x09 ]); /* 同上. */
    colors.rgb([ 1, 0.5, '3.53%' ]); /* 同上. */

## [m] argb#

### argb(colorHex)#

`[6.2.0]` `Overload 1/3`

- colorHex { string } - 颜色代码
- returns { ColorInt }

获取 `colorHex` 颜色代码对应的 颜色整数 (ColorInt).

格式 | 备注
--- | ---
#RRGGBB | A (alpha) 分量为 0xFF
#RGB | A (alpha) 分量为 0xFF
#AARRGGBB | -

    colors.argb('#663399'); /* 相当于 argb('#FF663399') . */
    colors.argb('#DE663399'); /* 结果不同上. */

### argb(alpha, red, green, blue)#

`6.2.0` `Overload 2/3`

- alpha { ColorComponent } - 颜色分量 - A (alpha)
- red { ColorComponent } - 颜色分量 - R (red)
- green { ColorComponent } - 颜色分量 - G (green)
- blue { ColorComponent } - 颜色分量 - B (blue)
- returns { ColorInt }

通过 颜色分量 获取 颜色整数 (ColorInt).

    colors.argb(64, 255, 128, 9);
    colors.argb(0x40, 0xFF, 0x80, 0x09); /* 同上. */
    colors.argb('#40FF8009'); /* 同上. */
    colors.argb(0.25, 1, 0.5, '3.53%'); /* 同上. */

### argb(components)#

`6.2.0` `Overload 3/3`

- components { ColorComponents[] } - 颜色分量数组
- returns { ColorInt }

通过 颜色分量数组 获取 颜色整数 (ColorInt).

    colors.argb([ 64, 255, 128, 9 ]);
    colors.argb([ 0x40, 0xFF, 0x80, 0x09 ]); /* 同上. */
    colors.argb([ 0.25, 1, 0.5, '3.53%' ]); /* 同上. */

## [m] rgba#

### rgba(colorHex)#

`[6.2.0]` `Overload 1/3`

- colorHex { string } - 颜色代码
- returns { ColorInt }

获取 `colorHex` 颜色代码对应的 颜色整数 (ColorInt).

格式 | 备注
--- | ---
#RRGGBB | A (alpha) 分量为 0xFF
#RGB | A (alpha) 分量为 0xFF
#RRGGBBAA | -

    colors.rgba('#663399'); /* 相当于 rgba('#663399FF') . */
    colors.rgba('#663399FF'); /* 结果同上. */
    colors.rgba('#FF663399'); /* 结果不同上. */

注意区分 `colors.rgba` 与 `colors.argb`:

    colors.rgba('#11335577'); /* A (alpha) 分量为 0x77 . */
    colors.argb('#11335577'); /* A (alpha) 分量为 0x11 . */

### rgba(red, green, blue, alpha)#

`6.2.0` `Overload 2/3`

- red { ColorComponent } - 颜色分量 - R (red)
- green { ColorComponent } - 颜色分量 - G (green)
- blue { ColorComponent } - 颜色分量 - B (blue)
- alpha { ColorComponent } - 颜色分量 - A (alpha)
- returns { ColorInt }

通过 颜色分量 获取 颜色整数 (ColorInt).

    colors.rgba(255, 128, 9, 64);
    colors.rgba(0xFF, 0x80, 0x09, 0x40); /* 同上. */
    colors.rgba('#FF800940'); /* 同上. */
    colors.rgba(1, 0.5, '3.53%', 0.25); /* 同上. */

### rgba(components)#

`6.2.0` `Overload 3/3`

- components { ColorComponents[] } - 颜色分量数组
- returns { ColorInt }

通过 颜色分量数组 获取 颜色整数 (ColorInt).

    colors.rgba([ 255, 128, 9, 64 ]);
    colors.rgba([ 0xFF, 0x80, 0x09, 0x40 ]); /* 同上. */
    colors.rgba([ 1, 0.5, '3.53%', 0.25 ]); /* 同上. */

## [m] hsv#

### hsv(hue, saturation, value)#

`6.2.0` `Overload 1/2`

- hue { ColorComponent } - 颜色分量 - H (hue)
- saturation { ColorComponent } - 颜色分量 - S (saturation)
- value { ColorComponent } - 颜色分量 - V (value)
- returns { ColorInt }

通过 颜色分量 获取 颜色整数 (ColorInt).

    colors.hsv(90, 80, 64);
    colors.hsv(90, 0.8, 0.64); /* 同上. */
    colors.hsv(0.25, 0.8, 0.64); /* 同上. */
    colors.hsv('25%', '80%', '64%'); /* 同上. */

### hsv(components)#

`6.2.0` `Overload 2/2`

- components { ColorComponents[] } - 颜色分量数组
- returns { ColorInt }

通过 颜色分量数组 获取 颜色整数 (ColorInt).

    colors.hsv([ 90, 80, 64 ]);
    colors.hsv([ 90, 0.8, 0.64 ]); /* 同上. */
    colors.hsv([ 0.25, 0.8, 0.64 ]); /* 同上. */
    colors.hsv([ '25%', '80%', '64%' ]); /* 同上. */

## [m] hsva#

### hsva(hue, saturation, value, alpha)#

`6.2.0` `Overload 1/2`

- hue { ColorComponent } - 颜色分量 - H (hue)
- saturation { ColorComponent } - 颜色分量 - S (saturation)
- value { ColorComponent } - 颜色分量 - V (value)
- alpha { ColorComponent } - 颜色分量 - A (alpha)
- returns { ColorInt }

通过 颜色分量 获取 颜色整数 (ColorInt).

    colors.hsva(90, 80, 64, 64);
    colors.hsva(90, 0.8, 0.64, 0.25); /* 同上. */
    colors.hsva(0.25, 0.8, 0.64, 0.25); /* 同上. */
    colors.hsva('25%', '80%', '64%', '25%'); /* 同上. */

### hsva(components)#

`6.2.0` `Overload 2/2`

- components { ColorComponents[] } - 颜色分量数组
- returns { ColorInt }

通过 颜色分量数组 获取 颜色整数 (ColorInt).

    colors.hsva([ 90, 80, 64, 64 ]);
    colors.hsva([ 90, 0.8, 0.64, 0.25 ]); /* 同上. */
    colors.hsva([ 0.25, 0.8, 0.64, 0.25 ]); /* 同上. */
    colors.hsva([ '25%', '80%', '64%', '25%' ]); /* 同上. */

## [m] hsl#

### hsl(hue, saturation, lightness)#

`6.2.0` `Overload 1/2`

- hue { ColorComponent } - 颜色分量 - H (hue)
- saturation { ColorComponent } - 颜色分量 - S (saturation)
- lightness { ColorComponent } - 颜色分量 - L (lightness)
- returns { ColorInt }

通过 颜色分量 获取 颜色整数 (ColorInt).

    colors.hsl(90, 80, 64);
    colors.hsl(90, 0.8, 0.64); /* 同上. */
    colors.hsl(0.25, 0.8, 0.64); /* 同上. */
    colors.hsl('25%', '80%', '64%'); /* 同上. */

### hsl(components)#

`6.2.0` `Overload 2/2`

- components { ColorComponents[] } - 颜色分量数组
- returns { ColorInt }

通过 颜色分量数组 获取 颜色整数 (ColorInt).

    colors.hsl([ 90, 80, 64 ]);
    colors.hsl([ 90, 0.8, 0.64 ]); /* 同上. */
    colors.hsl([ 0.25, 0.8, 0.64 ]); /* 同上. */
    colors.hsl([ '25%', '80%', '64%' ]); /* 同上. */

## [m] hsla#

### hsla(hue, saturation, lightness, alpha)#

`6.2.0` `Overload 1/2`

- hue { ColorComponent } - 颜色分量 - H (hue)
- saturation { ColorComponent } - 颜色分量 - S (saturation)
- lightness { ColorComponent } - 颜色分量 - L (lightness)
- alpha { ColorComponent } - 颜色分量 - A (alpha)
- returns { ColorInt }

通过 颜色分量 获取 颜色整数 (ColorInt).

    colors.hsla(90, 80, 64, 64);
    colors.hsla(90, 0.8, 0.64, 0.25); /* 同上. */
    colors.hsla(0.25, 0.8, 0.64, 0.25); /* 同上. */
    colors.hsla('25%', '80%', '64%', '25%'); /* 同上. */

### hsla(components)#

`6.2.0` `Overload 2/2`

- components { ColorComponents[] } - 颜色分量数组
- returns { ColorInt }

通过 颜色分量数组 获取 颜色整数 (ColorInt).

    colors.hsla([ 90, 80, 64, 64 ]);
    colors.hsla([ 90, 0.8, 0.64, 0.25 ]); /* 同上. */
    colors.hsla([ 0.25, 0.8, 0.64, 0.25 ]); /* 同上. */
    colors.hsla([ '25%', '80%', '64%', '25%' ]); /* 同上. */

## [m] toRgb#

### toRgb(color)#

`6.2.0`

- color { OmniColor } - 颜色参数
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 RGB 颜色分量数组.

    let [ r, g, b ] = colors.toRgb('#663399');
    console.log(`R: ${r}, G: ${g}, B: ${b}`);

## [m] toRgba#

### toRgba(color)#

`6.2.0` `Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 RGBA 颜色分量数组.

    let [ r, g, b, a ] = colors.toRgba('#DE663399');
    console.log(`R: ${r}, G: ${g}, B: ${b}, A: ${a}`);

需留意上述示例的参数格式为 `#AARRGGBB`, 结果格式为 `[RR, GG, BB, AA]`.

### toRgba(color, options)#

`6.3.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- options {{
  - [ maxAlpha = `255` ]?: `1` | `255` - A (alpha) 分量的范围最大值
- }} - 选项参数
- returns { ColorComponents } - 颜色分量数组

根据 `options` 选项参数获取颜色参数的 RGBA 颜色分量数组.

    let [ r1, g1, b1, a1 ] = colors.toRgba('#DE663399');
    console.log(`R: ${r1}, G: ${g1}, B: ${b1}, A: ${a1}`); /* A 分量范围为 [0..255] . */

    let [ r2, g2, b2, a2 ] = colors.toRgba('#DE663399', { maxAlpha: 1 });
    console.log(`R: ${r2}, G: ${g2}, B: ${b2}, A: ${a2}`); /* A 分量范围为 [0..1] . */

## [m] toArgb#

### toArgb(color)#

`6.2.0` `Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 ARGB 颜色分量数组.

    let [ a, r, g, b ] = colors.toArgb('#DE663399');
    console.log(`A: ${a}, R: ${r}, G: ${g}, B: ${b}`);

### toArgb(color, options)#

`6.3.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- options {{
  - [ maxAlpha = `255` ]?: `1` | `255` - A (alpha) 分量的范围最大值
- }} - 选项参数
- returns { ColorComponents } - 颜色分量数组

根据 `options` 选项参数获取颜色参数的 ARGB 颜色分量数组.

    let [ a1, r1, g1, b1 ] = colors.toArgb('#DE663399');
    console.log(`A: ${a1}, R: ${r1}, G: ${g1}, B: ${b1}`); /* A 分量范围为 [0..255] . */

    let [ a2, r2, g2, b2 ] = colors.toArgb('#DE663399', { maxAlpha: 1 });
    console.log(`A: ${a2}, R: ${r2}, G: ${g2}, B: ${b2}`); /* A 分量范围为 [0..1] . */

## [m] toHsv#

### toHsv(color)#

`6.2.0` `Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 HSV 颜色分量数组.

    let [ h, s, v ] = colors.toHsv('#663399');
    console.log(`H: ${h}, S: ${s}, V: ${v}`);

### toHsv(red, green, blue)#

`6.2.0` `Overload 2/2`

- red { ColorComponent } - 颜色分量 - R (red)
- green { ColorComponent } - 颜色分量 - G (green)
- blue { ColorComponent } - 颜色分量 - B (blue)
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 HSV 颜色分量数组.

    let [ h, s, v ] = colors.toHsv(102, 51, 153);
    console.log(`H: ${h}, S: ${s}, V: ${v}`);

## [m] toHsva#

### toHsva(color)#

`6.2.0` `Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 HSVA 颜色分量数组.

其中 A (alpha) 分量范围恒为 `[0..1]`.

    let [ h, s, v, a ] = colors.toHsva('#BF663399');
    console.log(`H: ${h}, S: ${s}, V: ${v}, A: ${a}`);

### toHsva(red, green, blue, alpha)#

`6.2.0` `Overload 2/2`

- red { ColorComponent } - 颜色分量 - R (red)
- green { ColorComponent } - 颜色分量 - G (green)
- blue { ColorComponent } - 颜色分量 - B (blue)
- alpha { ColorComponent } - 颜色分量 - A (alpha)
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 HSVA 颜色分量数组.

其中 A (alpha) 分量范围恒为 `[0..1]`.

    let [ h, s, v, a ] = colors.toHsva(102, 51, 153, 191);
    console.log(`H: ${h}, S: ${s}, V: ${v}, A: ${a}`);

## [m] toHsl#

### toHsl(color)#

`6.2.0` `Overload 1/2`

- color { OmniColor } - 颜色参数
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 HSL 颜色分量数组.

    let [ h, s, l ] = colors.toHsl('#663399');
    console.log(`H: ${h}, S: ${s}, L: ${l}`);

### toHsl(red, green, blue)#

`6.2.0` `Overload 2/2`

- red { ColorComponent } - 颜色分量 - R (red)
- green { ColorComponent } - 颜色分量 - G (green)
- blue { ColorComponent } - 颜色分量 - B (blue)
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 HSL 颜色分量数组.

    let [ h, s, l ] = colors.toHsl(102, 51, 153);
    console.log(`H: ${h}, S: ${s}, L: ${l}`);

## [m] toHsla#

### toHsla(color)#

`6.2.0` `Overload 2/2`

- color { OmniColor } - 颜色参数
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 HSLA 颜色分量数组.

其中 A (alpha) 分量范围恒为 `[0..1]`.

    let [ h, s, l, a ] = colors.toHsla('#BF663399');
    console.log(`H: ${h}, S: ${s}, L: ${l}, A: ${a}`);

### toHsla(red, green, blue, alpha)#

`6.2.0` `Overload 1/2`

- red { ColorComponent } - 颜色分量 - R (red)
- green { ColorComponent } - 颜色分量 - G (green)
- blue { ColorComponent } - 颜色分量 - B (blue)
- alpha { ColorComponent } - 颜色分量 - A (alpha)
- returns { ColorComponents } - 颜色分量数组

获取颜色参数的 HSLA 颜色分量数组.

其中 A (alpha) 分量范围恒为 `[0..1]`.

    let [ h, s, l, a ] = colors.toHsla(102, 51, 153, 191);
    console.log(`H: ${h}, S: ${s}, L: ${l}, A: ${a}`);

## [m] isSimilar#

### isSimilar(colorA, colorB, threshold?, algorithm?)#

`[6.2.0]` `Overload [1-3]/4`

- colorA { OmniColor } - 颜色参数
- colorB { OmniColor } - 颜色参数
- [ threshold = `4` ] { IntRange[0..255] } - 颜色匹配阈值
- [ algorithm = `'diff'` ] { ColorDetectionAlgorithm } - 颜色检测算法
- returns { boolean } - 两个颜色是否相似

判断两个颜色是否相似.

不同阈值对结果的影响 (阈值越高, 条件越宽松, 阈值越低, 条件越严格):

    colors.isSimilar('orange', 'dark-orange', 5); /* false, 阈值较小, 条件相对严格. */
    colors.isSimilar('orange', 'dark-orange', 10); /* true, 阈值增大, 条件趋于宽松. */

不同 颜色检测算法 对结果的影响:

    colors.isSimilar('orange', 'dark-orange', 9, 'rgb+'); // false
    colors.isSimilar('orange', 'dark-orange', 9, 'diff'); // true
    colors.isSimilar('orange', 'dark-orange', 9, 'hs'); // true

    colors.isSimilar('orange', 'dark-orange', 8, 'rgb+'); // false
    colors.isSimilar('orange', 'dark-orange', 8, 'diff'); // false
    colors.isSimilar('orange', 'dark-orange', 8, 'hs'); // true

### isSimilar(colorA, colorB, options)#

`6.2.0` `Overload 4/4`

- colorA { OmniColor } - 颜色参数
- colorB { OmniColor } - 颜色参数
- options {{
  - [ similarity ≈ `0.9843` ]?: Range[0..1] - 颜色匹配相似度
  - [ threshold = `4` ]?: IntRange[0..255] - 颜色匹配阈值
  - [ algorithm = `'diff'` ]?: ColorDetectionAlgorithm - 颜色检测算法
- }} - 选项参数
- returns { boolean } - 两个颜色是否相似

判断两个颜色是否相似.

此方法将非必要参数集中于 `options` 对象中.

    colors.isSimilar('#010101', '#020202', { similarity: 0.95 }); // true

## [m] isEqual#

### isEqual(colorA, colorB, alphaMatters?)#

`6.2.0` `Overload[1-2]/2`

- colorA { OmniColor } - 颜色参数
- colorB { OmniColor } - 颜色参数
- [ alphaMatters = `false` ] { boolean } - 是否考虑 `A (alpha)` 分量
- returns { boolean } - 两个颜色是否相等

判断两个颜色是否相等, 比较时由 `alphaMatters` 参数决定是否考虑 `A (alpha)` 分量:

    /* Hex 代码. */
    colors.isEqual('#FF0000', '#FF0000'); // true
    colors.isEqual('#FF0000', '#F00'); /* 同上, 三位数简写形式. */
    /* 颜色整数. */
    colors.isEqual(-65536, 0xFF0000); // true
    /* 颜色名称. */
    colors.isEqual('red', 'RED'); /* true, 不区分大小写. */
    colors.isEqual('orange', 'Orange'); /* true, 不区分大小写. */
    colors.isEqual('dark-gray', 'DARK_GRAY'); /* true, 连字符与下划线均被支持. */
    /* 不同类型比较. */
    colors.isEqual('red', '#FF0000'); // true
    colors.isEqual('orange', '#FFA500'); // true
    /* A (alpha) 分量的不同情况. */
    colors.isEqual('#A1FF0000', '#A2FF0000'); /* true, 默认忽略 A 分量. */
    colors.isEqual('#A1FF0000', '#A2FF0000', true); /* false, 需考虑 A 分量. */

## [m] equals#

### equals(colorA, colorB)#

`DEPRECATED`

- colorA { number | string } - 颜色参数
- colorB { number | string } - 颜色参数
- returns { boolean } - 两个颜色是否相等 (忽略 `A (alpha)` 分量)

判断两个颜色是否相等, 比较时忽略 `A (alpha)` 分量:

    /* Hex 代码. */
    colors.equals('#FF0000', '#FF0000'); // true
    /* 颜色整数. */
    colors.equals(-65536, 0xFF0000); // true
    /* 颜色名称. */
    colors.equals('red', 'RED'); // true
    /* 不同类型比较. */
    colors.equals('red', '#FF0000'); // true
    /* A (alpha) 分量将被忽略. */
    colors.equals('#A1FF0000', '#A2FF0000'); // true

但以下示例将全部抛出异常:

    colors.equals('orange', '#FFA500'); /* 抛出异常. */
    colors.equals('dark-gray', '#444'); /* 抛出异常. */
    colors.equals('#FF0000', '#F00'); /* 抛出异常. */

上述示例对于 colors.isEqual 则全部返回 `true`.

除非需要考虑多版本兼容, 否则建议始终使用 `colors.isEqual` 替代 `colors.equals`.

## [m] luminance#

### luminance(color)#

`6.2.0`

- color { OmniColor } - 颜色参数
- returns { Range[0..1] } - 颜色亮度

获取颜色的 亮度 (Luminance), 取值范围 `[0..1]`.

    colors.luminance(colors.WHITE); // 1
    colors.luminance(colors.BLACK); // 0
    colors.luminance(colors.RED); // 0.2126
    colors.luminance(colors.GREEN); // 0.7152
    colors.luminance(colors.BLUE); // 0.0722
    colors.luminance(colors.YELLOW); // 0.9278

> 参阅: W3C Wiki

## [m] toColorStateList#

### toColorStateList(...color)#

`6.2.0`

- color { ...OmniColor[] } - 颜色参数
- returns { android.content.res.ColorStateList }

将一个或多个颜色参数转换为 ColorStateList 实例.

    colors.toColorStateList('red'); /* 包含单一颜色的 ColorStateList. */
    colors.toColorStateList('red', 'green', 'orange'); /* 包含多个颜色的 ColorStateList. */

## [m] setPaintColor#

### setPaintColor(paint, color)#

`6.2.0`

- paint { android.graphics.Paint } - 画笔参数
- color { OmniColor } - 颜色参数
- returns { void }

方法 `setPaintColor` 用于解决在 `Android API 29 (10) [Q]` 及以上系统中 `Paint#setColor(color)` 无法正常设置画笔颜色的问题.

    let paint = new android.graphics.Paint();

    /* 安卓 10 及以上系统无法正常设置颜色. */
    // paint.setColor(colors.toInt('blue'));

    /* 使用 colors 模块实现原始功能. */
    colors.setPaintColor(paint, 'blue');

画笔无法正常设置颜色的原因, 是 `Android API 29 (10) [Q]` 源码中 `setColor` 有以下两种方法签名:

    setColor(@ColorInt int color): void
    setColor(@ColorLong long color): void

JavaScript 语言不区分 `int` 和 `long`, 即只有 `setColor(color: number)`,
它会优先匹配 Java 的 `setColor(@ColorLong long color): void`.

`ColorLong` 颜色与 `ColorInt` 颜色不同在于, 前者包含了额外的 `ColorSpace` (颜色空间) 信息,
原有的 `ColorInt` 被当做 `ColorLong` 来解析, 导致颜色解析异常.

除上述 `colors.setPaintColor` 的方法外, 还有其他一些解决方案:

    /* A. 使用 paint.setArgb 方法. */
    paint.setARGB(
        colors.alpha(color),
        colors.red(color),
        colors.green(color),
        colors.blue(color),
    );

    /* 同上, 语法更简洁. */
    paint.setARGB.apply(paint, colors.toArgb(color));

    /* B. 将 ColorInt "打包" 为 ColorLong. */
    paint.setColor(android.graphics.Color.pack(colors.toInt(color)));

    /* C. 直接使用带 ColorSpace 信息的 ColorLong. */
    paint.setColor(android.graphics.Color.pack(
        colors.redDouble(color),
        colors.greenDouble(color),
        colors.blueDouble(color),
        colors.alphaDouble(color),
        android.graphics.ColorSpace.get(android.graphics.ColorSpace.Named.SRGB),
    ));

`colors.setPaintColor` 的大致源码:

    function setPaintColor(paint, color) {
        if (util.version.sdkInt >= util.versionCodes.Q) {
            paint.setARGB.apply(paint, colors.toArgb(color));
        } else {
            paint.setColor(colors.toInt(color));
        }
    }

## [p+] android#

`6.2.0`

Android 颜色列表 对象.

## [p+] css#

`6.2.0`

Css 颜色列表 对象.

## [p+] web#

`6.2.0`

Web 颜色列表 对象.

## [p+] material#

`6.2.0`

Material 颜色列表 对象.

## [p] BLACK#

`CONSTANT`

- [ `-16777216` ] { number }

◑ 黑 (`#000000` `rgb(0,0,0`) 的颜色整数.

## [p] BLUE#

`CONSTANT`

- [ `-16776961` ] { number }

◑ 蓝 (`#0000FF` `rgb(0,0,255`) 的颜色整数.

## [p] CYAN#

`CONSTANT`

- [ `-16711681` ] { number }

◑ 青 (`#00FFFF` `rgb(0,255,255`) 的颜色整数.

## [p] AQUA#

`6.2.0` `CONSTANT`

- [ `-16711681` ] { number }

◑ 青 (`#00FFFF` `rgb(0,255,255`) 的颜色整数.

## [p] DARK_GRAY#

`6.2.0` `CONSTANT`

- [ `-12303292` ] { number }

◑ 暗灰 (`#444444` `rgb(68,68,68`) 的颜色整数.

## [p] DARK_GREY#

`6.2.0` `CONSTANT`

- [ `-12303292` ] { number }

◑ 暗灰 (`#444444` `rgb(68,68,68`) 的颜色整数.

## [p] DKGRAY#

`CONSTANT`

- [ `-12303292` ] { number }

◑ 暗灰 (`#444444` `rgb(68,68,68`) 的颜色整数.

## [p] GRAY#

`CONSTANT`

- [ `-7829368` ] { number }

◑ 灰 (`#888888` `rgb(136,136,136`) 的颜色整数.

## [p] GREY#

`6.2.0` `CONSTANT`

- [ `-7829368` ] { number }

◑ 灰 (`#888888` `rgb(136,136,136`) 的颜色整数.

## [p] GREEN#

`CONSTANT`

- [ `-16711936` ] { number }

◑ 绿 (`#00FF00` `rgb(0,255,0`) 的颜色整数.

## [p] LIME#

`6.2.0` `CONSTANT`

- [ `-16711936` ] { number }

◑ 绿 (`#00FF00` `rgb(0,255,0`) 的颜色整数.

## [p] LIGHT_GRAY#

`6.2.0` `CONSTANT`

- [ `-3355444` ] { number }

◑ 亮灰 (`#CCCCCC` `rgb(204,204,204`) 的颜色整数.

## [p] LIGHT_GREY#

`6.2.0` `CONSTANT`

- [ `-3355444` ] { number }

◑ 亮灰 (`#CCCCCC` `rgb(204,204,204`) 的颜色整数.

## [p] LTGRAY#

`CONSTANT`

- [ `-3355444` ] { number }

◑ 亮灰 (`#CCCCCC` `rgb(204,204,204`) 的颜色整数.

## [p] MAGENTA#

`CONSTANT`

- [ `-65281` ] { number }

◑ 品红 / 洋红 (`#FF00FF` `rgb(255,0,255`) 的颜色整数.

## [p] FUCHSIA#

`6.2.0` `CONSTANT`

- [ `-65281` ] { number }

◑ 品红 / 洋红 (`#FF00FF` `rgb(255,0,255`) 的颜色整数.

## [p] MAROON#

`6.2.0` `CONSTANT`

- [ `-8388608` ] { number }

◑ 栗 (`#800000` `rgb(128,0,0`) 的颜色整数.

## [p] NAVY#

`6.2.0` `CONSTANT`

- [ `-16777088` ] { number }

◑ 海军蓝 / 藏青 (`#000080` `rgb(0,0,128`) 的颜色整数.

## [p] OLIVE#

`6.2.0` `CONSTANT`

- [ `-8355840` ] { number }

◑ 橄榄 (`#808000` `rgb(128,128,0`) 的颜色整数.

## [p] PURPLE#

`6.2.0` `CONSTANT`

- [ `-8388480` ] { number }

◑ 紫 (`#800080` `rgb(128,0,128`) 的颜色整数.

## [p] RED#

`CONSTANT`

- [ `-65536` ] { number }

◑ 红 (`#FF0000` `rgb(255,0,0`) 的颜色整数.

## [p] SILVER#

`6.2.0` `CONSTANT`

- [ `-4144960` ] { number }

◑ 银 (`#C0C0C0` `rgb(192,192,192`) 的颜色整数.

## [p] TEAL#

`6.2.0` `CONSTANT`

- [ `-16744320` ] { number }

◑ 鸭绿 / 凫绿 (`#008080` `rgb(0,128,128`) 的颜色整数.

## [p] WHITE#

`CONSTANT`

- [ `-1` ] { number }

◑ 白 (`#FFFFFF` `rgb(255,255,255`) 的颜色整数.

## [p] YELLOW#

`CONSTANT`

- [ `-256` ] { number }

◑ 黄 (`#FFFF00` `rgb(255,255,0)`) 的颜色整数.

## [p] ORANGE#

`6.2.0` `CONSTANT`

- [ `-23296` ] { number }

◑ 橙 (`#FFA500` `rgb(255,165,0)`) 的颜色整数.

## [p] TRANSPARENT#

`CONSTANT`

- [ `0` ] { number }

全透明 (`#00000000` `argb(0, 0, 0, 0)`) 的颜色整数.

## 融合颜色#

为节约篇幅, 本章节仅列出了常用的部分融合颜色, 融合颜色属性直接挂载于 colors 对象上, 使用 `colors.Xxx` 的形式访问:

    colors.toHex(colors.BLACK); /* 黑色. */
    colors.toHex(colors.ORANGE); /* 橙色. */
    colors.toHex(colors.PANSY); /* 三色堇紫色. */
    colors.toHex(colors.ALIZARIN_CRIMSON); /* 茜红色. */
    colors.toHex(colors.PURPLE_300); /* 材料紫色 (300 号). */

更多融合颜色, 参阅 融合颜色列表 小节.

# 图像 (Images)#

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

Marked by SuperMonster003 on Oct 22, 2022.

images模块提供了一些手机设备中常见的图片处理函数, 包括截图、读写图片、图片剪裁、旋转、二值化、找色找图等.

该模块分为两个部分, 找图找色部分和图片处理部分.

需要注意的是, image对象创建后尽量在不使用时进行回收, 同时避免循环创建大量图片. 因为图片是一种占用内存比较大的资源, 尽管Auto.js通过各种方式（比如图片缓存机制、垃圾回收时回收图片、脚本结束时回收所有图片）尽量降低图片资源的泄漏和内存占用, 但是糟糕的代码仍然可以占用大量内存.

Image对象通过调用`recycle()`函数来回收. 例如：

    // 读取图片
    var img = images.read("./1.png");
    //对图片进行操作
    ...
    // 回收图片
    img.recycle();

例外的是, `captureScreen()` 返回的图片不需要回收.

## 图片处理#

## 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

- 返回 { Image }

调整图片大小, 并返回调整后的图片. 例如把图片放缩为200*300：`images.resize(img, [200, 300])`.

参见Imgproc.resize.

## 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

- 返回 { Image }

放缩图片, 并返回放缩后的图片. 例如把图片变成原来的一半：`images.scale(img, 0.5, 0.5)`.

参见Imgproc.resize.

## images.rotate(img, degree[, x, y])#

[v4.1.0新增]

- `img` { Image } 图片
- `degree` { number } 旋转角度.
- `x` { number } 旋转中心x坐标, 默认为图片中点
- `y` { number } 旋转中心y坐标, 默认为图片中点
- 返回 { Image }

将图片逆时针旋转 degree 度, 返回旋转后的图片对象.

例如逆时针旋转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, 可选的值:

  - `BINARY`
  - `BINARY_INV`
  - `TRUNC`
  - `TOZERO`
  - `TOZERO_INV`
  - `OTSU`
  - `TRIANGLE`

- 返回 { Image }

将图片阈值化, 并返回处理后的图像. 可以用这个函数进行图片二值化. 例如：`images.threshold(img, 100, 255, "BINARY")`, 这个代码将图片中大于100的值全部变成255, 其余变成0, 从而达到二值化的效果. 如果img是一张灰度化图片, 这个代码将会得到一张黑白图片.

可以参考有关博客（比如threshold函数的使用）或者OpenCV文档threshold.

## 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）或者OpenCV文档adaptiveThreshold.

## images.cvtColor(img, code[, dstCn])#

[v4.1.0新增]

- `img` { Image } 图片
- `code` { string } 颜色空间转换的类型, 可选的值有一共有205个（参见ColorConversionCodes）, 这里只列出几个：
  - `BGR2GRAY` BGR转换为灰度
  - `BGR2HSV` BGR转换为HSV
  - ``
- `dstCn` { number } 目标图像的颜色通道数量, 如果不填写则根据其他参数自动决定.
- 返回 { Image }

对图像进行颜色空间转换, 并返回转换后的图像.

可以参考有关博客（比如颜色空间转换）或者OpenCV文档cvtColor.

## 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 }

对图像进行模糊（平滑处理）, 返回处理后的图像.

可以参考有关博客（比如实现图像平滑处理）或者OpenCV文档blur.

## images.medianBlur(img, size)#

[v4.1.0新增]

- `img` { Image } 图片
- `size` { number } 定义滤波器的大小, 正奇数, 如 3
- 返回 { Image }

对图像进行中值滤波, 返回处理后的图像.

可以参考有关博客（比如实现图像平滑处理）或者OpenCV文档blur.

## 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 }

对图像进行高斯模糊, 返回处理后的图像.

可以参考有关博客（比如实现图像平滑处理）或者OpenCV文档GaussianBlur.

## 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).

  - "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#

纵坐标.

# 光学字符识别 (OCR)#

ocr 模块用于识别图像中的文本.

AutoJs6 的 OCR 特性是基于 Google ML Kit 的 文字识别 API 及 Baidu PaddlePaddle 的 Paddle Lite 实现的.

ocr

## [@] ocr#

ocr 可作为全局对象使用:

    typeof ocr; // "function"
    typeof ocr.detect; // "function"
    typeof ocr.recognizeText; // "function"

### ocr(options?)#

`6.4.0` `Overload [1-2]/9`

- [ options ] { OcrOptions } - OCR 识别选项
- returns { string[] }

识别当前屏幕截图中包含的所有文本, 返回文本数组.

`ocr()` 相当于以下代码的整合:

    images.requestScreenCapture();
    let img = images.captureScreen();
    ocr(img);

同时也是 ocr.recognizeText(options?) 的别名方法.

### ocr(region)#

`6.4.0` `Overload 3/9`

- region { OmniRegion } - OCR 识别区域
- returns { string[] }

识别当前屏幕截图指定区域内包含的所有文本, 返回文本数组.

`ocr(region)` 相当于以下代码的整合:

    images.requestScreenCapture();
    let img = images.captureScreen();
    ocr(img, region);

同时也是 ocr({ region: region }) 的便捷方法,

以及 ocr.recognizeText(region) 的别名方法.

关于 OCR 区域参数 `region` 的更多用法, 参阅 OcrOptions#region 小节.

### ocr(img, options?)#

`6.3.0` `Overload [4-5]/9`

- img { ImageWrapper } - 包装图像对象
- [ options ] { OcrOptions } - OCR 识别选项
- returns { string[] }

识别图像包含的所有文本, 返回文本数组.

ocr.recognizeText(img, options?) 的别名方法.

    /* 申请屏幕截图权限. */
    images.requestScreenCapture();

    /* 截屏并获取包装图像对象. */
    let img = images.captureScreen();

    /* OCR 识别并获取结果, 结果为字符串数组. */
    let results = ocr(img);

    /* 结果过滤, 筛选出文本中可部分匹配 "app" 的结果, 如 "apple", "disappear" 等. */
    results.filter(text => text.includes('app'));

### ocr(img, region)#

`6.3.0` `Overload 6/9`

- img { ImageWrapper } - 包装图像对象
- region { OmniRegion } - OCR 识别区域
- returns { string[] }

识别指定区域内图像包含的所有文本, 返回文本数组.

ocr(img, { region: region }) 的便捷方法.

ocr.recognizeText(img, region) 的别名方法.

    /* 申请屏幕截图权限. */
    images.requestScreenCapture();

    /* 截屏并获取包装图像对象. */
    let img = images.captureScreen();

    /* 在区域 [ 0, 0, 100, 150 ] 内进行 OCR 识别并获取结果, 结果为字符串数组. */
    let results = ocr(img, [ 0, 0, 100, 150 ]);

    /* 结果过滤, 筛选出文本中可部分匹配 "app" 的结果, 如 "apple", "disappear" 等. */
    results.filter(text => text.includes('app'));

关于 OCR 区域参数 `region` 的更多用法, 参阅 OcrOptions#region 小节.

### ocr(imgPath, options?)#

`6.3.0` `Overload [7-8]/9`

- imgPath { string } - 图像路径
- [ options ] { OcrOptions } - OCR 识别选项
- returns { string[] }

识别指定路径对应图像包含的所有文本, 返回文本数组.

当指定路径无法解析为包装图像对象时, 将抛出 `TypeError` 异常.

ocr.recognizeText(imgPath, options?) 的别名方法.

    ocr('./picture.jpg'); /* 获取本地图像文件中的所有文本. */

### ocr(imgPath, region)#

`6.3.0` `Overload 9/9`

- imgPath { string } - 图像路径
- region { OmniRegion } - OCR 识别区域
- returns { string[] }

识别指定路径对应图像在指定区域内包含的所有文本, 返回文本数组.

当指定路径无法解析为包装图像对象时, 将抛出 `TypeError` 异常.

ocr(imgPath, { region: region }) 的便捷方法.

ocr.recognizeText(imgPath, region) 的别名方法.

    /* 获取本地图像文件在区域 [ 0, 0, 100, 150 ] 内的所有文本. */
    ocr('./picture.jpg', [ 0, 0, 100, 150 ]);

关于 OCR 区域参数 `region` 的更多用法, 参阅 OcrOptions#region 小节.

## [p] mode#

`6.3.4` `Getter/Setter`

- [ <get> = `'mlkit'` ] { OcrModeName }
- <set> { OcrModeName }

获取或设置 OCR 的工作模式名称.

    /* AutoJs6 OCR 默认采用 MLKit 工作模式. */
    console.log(ocr.mode); // "mlkit"

    ocr.mode = 'paddle'; /* 切换到 Paddle 工作模式. */
    console.log(ocr.mode); // "paddle"

    ocr.mode = 'mlkit'; /* 再次切换到 MLKit 工作模式. */
    console.log(ocr.mode); // "mlkit"

当使用不同的工作模式名称时, `ocr` 全局方法及其相关方法 (如 ocr.detect) 将使用不同的引擎, 进而可能获得不同的识别速度和结果.

> 注: 使用 Paddle 工作模式时, 建议开启 AutoJs6 的 "忽略电池优化" 开关, 并降低对 AutoJs6 节电及后台运行等方面的限制, 否则可能导致应用崩溃.

## [m] recognizeText#

用于识别图像中的全部文本.

`recognizeText` 方法与工作模式有关, 例如当工作模式为 `paddle` 时, `ocr.recognizeText(...)` 与 `ocr.paddle.recognizeText(...)` 等价.

`ocr.recognizeText(...)` 相关方法均可简写为 `ocr(...)`.

### recognizeText(options?)#

`6.4.0` `Overload [1-2]/9`

- [ options ] { OcrOptions } - OCR 识别选项
- returns { string[] }

识别当前屏幕截图中包含的所有文本, 返回文本数组.

`ocr.recognizeText()` 相当于以下代码的整合:

    images.requestScreenCapture();
    let img = images.captureScreen();
    ocr.recognizeText(img);

`ocr.recognizeText(options?)` 与 `ocr(options?)` 等价.

### recognizeText(region)#

`6.4.0` `Overload 3/9`

- region { OmniRegion } - OCR 识别区域
- returns { string[] }

识别当前屏幕截图指定区域内包含的所有文本, 返回文本数组.

`ocr.recognizeText(region)` 相当于以下代码的整合:

    images.requestScreenCapture();
    let img = images.captureScreen();
    ocr.recognizeText(img, region);

ocr.recognizeText({ region: region }) 的便捷方法.

`ocr.recognizeText(region)` 与 `ocr(region)` 等价.

关于 OCR 区域参数 `region` 的更多用法, 参阅 OcrOptions#region 小节.

### recognizeText(img, options?)#

`6.3.0` `Overload [4-5]/9`

- img { ImageWrapper } - 包装图像对象
- [ options ] { OcrOptions } - OCR 识别选项
- returns { string[] }

识别图像包含的所有文本, 返回文本数组.

`ocr.recognizeText(img, options?)` 与 `ocr(img, options?)` 等价.

    images.requestScreenCapture(); /* 申请屏幕截图权限. */
    let img = images.captureScreen(); /* 截屏并获取包装图像对象. */
    ocr.recognizeText(img).filter(text => text.includes('app')); /* 过滤结果. */

### recognizeText(img, region)#

`6.3.0` `Overload 6/9`

- img { ImageWrapper } - 包装图像对象
- region { OmniRegion } - OCR 识别区域
- returns { string[] }

识别指定区域内图像包含的所有文本, 返回文本数组.

ocr.recognizeText(img, { region: region }) 的便捷方法.

`ocr.recognizeText(img, region)` 与 `ocr(img, region)` 等价.

    images.requestScreenCapture(); /* 申请屏幕截图权限. */
    let img = images.captureScreen(); /* 截屏并获取包装图像对象. */
    ocr.recognizeText(img, [ 0, 0, 100, 150 ]).filter(text => text.includes('app')); /* 过滤结果. */

关于 OCR 区域参数 `region` 的更多用法, 参阅 OcrOptions#region 小节.

### recognizeText(imgPath, options?)#

`6.3.0` `Overload [7-8]/9`

- imgPath { string } - 图像路径
- [ options ] { OcrOptions } - OCR 识别选项
- returns { string[] }

识别指定路径对应图像包含的所有文本, 返回文本数组.

当指定路径无法解析为包装图像对象时, 将抛出 `TypeError` 异常.

`ocr.recognizeText(imgPath, options?)` 与 `ocr(imgPath, options?)` 等价.

    ocr.recognizeText('./picture.jpg'); /* 获取本地图像文件中的所有文本. */

### recognizeText(imgPath, region)#

`6.3.0` `Overload 9/9`

- imgPath { string } - 图像路径
- region { OmniRegion } - OCR 识别区域
- returns { string[] }

识别指定路径对应图像在指定区域内包含的所有文本, 返回文本数组.

当指定路径无法解析为包装图像对象时, 将抛出 `TypeError` 异常.

ocr.recognizeText(imgPath, { region: region }) 的便捷方法.

`ocr.recognizeText(imgPath, region)` 与 `ocr(imgPath, region)` 等价.

    /* 获取本地图像文件在区域 [ 0, 0, 100, 150 ] 内的所有文本. */
    ocr.recognizeText('./picture.jpg', [ 0, 0, 100, 150 ]);

关于 OCR 区域参数 `region` 的更多用法, 参阅 OcrOptions#region 小节.

## [m] detect#

用于识别图像中的全部文本.

`detect` 方法与工作模式有关, 例如当工作模式为 `paddle` 时, `ocr.detect(...)` 与 `ocr.paddle.detect(...)` 等价.

与 recognizeText 不同, `detect` 返回的结果包含更多信息, 包括 [ 文本标签, 置信度, 位置矩形 ] 等, `recognizeText` 精简了 `detect` 返回的结果, 仅包含文本标签数据.

### detect(options?)#

`6.4.0` `Overload [1-2]/9`

- [ options ] { OcrOptions } - OCR 识别选项
- returns { OcrResult[] }

识别当前屏幕截图中包含的所有文本, 返回 OcrResult 数组.

`ocr.detect()` 相当于以下代码的整合:

    images.requestScreenCapture();
    let img = images.captureScreen();
    ocr.detect(img);

### detect(region)#

`6.4.0` `Overload 3/9`

- region { OmniRegion } - OCR 识别区域
- returns { OcrResult[] }

识别当前屏幕截图指定区域内包含的所有文本, 返回 OcrResult 数组.

`ocr.detect(region)` 相当于以下代码的整合:

    images.requestScreenCapture();
    let img = images.captureScreen();
    ocr.detect(img, region);

同时也是 ocr.detect({ region: region }) 的便捷方法.

关于 OCR 区域参数 `region` 的更多用法, 参阅 OcrOptions#region 小节.

### detect(img, options?)#

`6.3.0` `Overload [4-5]/9`

- img { ImageWrapper } - 包装图像对象
- [ options ] { OcrOptions } - OCR 识别选项
- returns { OcrResult[] }

识别图像包含的所有文本, 返回 OcrResult 数组.

    /* 申请屏幕截图权限. */
    images.requestScreenCapture();

    /* 截屏并获取包装图像对象. */
    let img = images.captureScreen();

    /* 获取本地图像文件中的所有识别结果. */
    let result = ocr.detect(img);

    /* 筛选置信度高于 0.8 的结果. */
    result.filter(o => o.confidence >= 0.8);

### detect(img, region)#

`6.3.0` `Overload 6/9`

- img { ImageWrapper } - 包装图像对象
- region { OmniRegion } - OCR 识别区域
- returns { OcrResult[] }

识别指定路径对应图像在指定区域内包含的所有文本, 返回 OcrResult 数组.

ocr.detect(img, { region: region }) 的便捷方法.

    /* 申请屏幕截图权限. */
    images.requestScreenCapture();

    /* 截屏并获取包装图像对象. */
    let img = images.captureScreen();

    /* 获取本地图像文件在区域 [ 0, 0, 100, 150 ] 内的所有识别结果. */
    let result = ocr.detect(img, [ 0, 0, 100, 150 ]);

    /* 筛选置信度高于 0.8 的结果. */
    result.filter(o => o.confidence >= 0.8);

关于 OCR 区域参数 `region` 的更多用法, 参阅 OcrOptions#region 小节.

### detect(imgPath, options?)#

`6.3.0` `Overload [7-8]/9`

- imgPath { string } - 图像路径
- [ options ] { OcrOptions } - OCR 识别选项
- returns { OcrResult[] }

识别指定路径对应图像包含的所有文本, 返回 OcrResult 数组.

当指定路径无法解析为包装图像对象时, 将抛出 `TypeError` 异常.

    let result = ocr.detect('./picture.jpg'); /* 获取本地图像文件中的所有识别结果. */
    result.filter(o => o.confidence >= 0.8); /* 筛选置信度高于 0.8 的结果. */

### detect(imgPath, region)#

`6.3.0` `Overload 9/9`

- imgPath { string } - 图像路径
- region { OmniRegion } - OCR 识别区域
- returns { OcrResult[] }

识别指定路径对应图像在指定区域内包含的所有文本, 返回 OcrResult 数组.

当指定路径无法解析为包装图像对象时, 将抛出 `TypeError` 异常.

ocr.detect(imgPath, { region: region }) 的便捷方法.

    /* 获取本地图像文件在区域 [ 0, 0, 100, 150 ] 内的所有识别结果. */
    let result = ocr.detect('./picture.jpg', [ 0, 0, 100, 150 ]);

    /* 筛选置信度高于 0.8 的结果. */
    result.filter(o => o.confidence >= 0.8);

关于 OCR 区域参数 `region` 的更多用法, 参阅 OcrOptions#region 小节.

## [m] tap#

### tap(mode)#

`6.3.4`

- mode { OcrModeName } - OCR 工作模式
- returns { void }

用于切换 OCR 工作模式, 相当于 ocr.mode 的 setter 形式.

    ocr.tap('paddle');
    ocr.mode = 'paddle'; /* 同上. */

## [m] summary#

### summary()#

`6.4.0`

获取 AutoJs6 OCR 功能的摘要.

摘要中表述了 OCR 功能当前使用的工作模式, 以及全部可用的工作模式.

    /* e.g. [ OCR summary ]
     * Current mode: mlkit
     * Available modes: [ mlkit, paddle ]
     */
    console.log(ocr.summary());

## 工作模式与代码形式#

截止 2023 年 9 月, AutoJs6 的 ocr 支持两种工作模式, `mlkit` (默认) 及 `paddle`.

工作模式的获取或设置可通过 ocr.mode 实现.

下面以 `mlkit` 为例, 总结 `mlkit` 工作模式可用的全部代码形式.

1. ocr.mlkit.detect(...)
2. ocr.mlkit.recognizeText(...)
3. ocr.mlkit(...)
4. ocr.detect(...)
5. ocr.recognizeText(...)
6. ocr(...)

上述 6 种代码形式均可实现使用 `mlkit` 引擎进行光学字符识别.

其中, [ 3 ] 是 [ 2 ] 的简便写法, [ 6 ] 是 [ 5 ] 的简便写法.

另外, [ 4, 5, 6 ] 三种形式的条件, 是 OCR 工作模式为 `mlkit`, 即 `ocr.mode` 返回 `mlkit`. 否则需要调用 `ocr.mode = 'mlkit'` 切换工作模式.

下面再以 `paddle` 为例, 总结 `paddle` 工作模式可用的全部代码形式.

1. ocr.paddle.detect(...)
2. ocr.paddle.recognizeText(...)
3. ocr.paddle(...)
4. ocr.detect(...)
5. ocr.recognizeText(...)
6. ocr(...)

同样, [ 4, 5, 6 ] 三种形式的条件, 是 OCR 工作模式为 `paddle`, 即 `ocr.mode` 返回 `paddle`. 否则需要调用 `ocr.mode = 'paddle'` 切换工作模式.

由此可见, `ocr(...)` 和 `ocr.detect(...)` 等方法是动态变化的, 其功能取决于工作模式. 这种形式的优点是写法简单, 但可读性相对较差, 可能难以辨识 OCR 的具体工作引擎. 如需兼顾可读性, 则可使用 `ocr.mlkit(...)` 和 `ocr.mlkit.detect(...)` 等形式.

# 条码 (Barcode)#

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

Marked by SuperMonster003 on Oct 30, 2023.

barcode 模块用于识别图像中的条码.

barcode

    interface Barcode {

        (options?: DetectOptions): string | string[] | null;
        (isAll: boolean): string | string[] | null;
        (img: ImageWrapper | string, options?: DetectOptions): string | string[] | null;
        (img: ImageWrapper | string, isAll: boolean): string | string[] | null;

        detect(options?: DetectOptions): Barcode.Result | Barcode.Result[] | null;
        detect(isAll: boolean): Barcode.Result | Barcode.Result[] | null;
        detect(img: ImageWrapper | string, options?: DetectOptions): Barcode.Result | Barcode.Result[] | null;
        detect(img: ImageWrapper | string, isAll: boolean): Barcode.Result | Barcode.Result[] | null;

        detectAll(options?: DetectOptionsWithoutIsAll): Barcode.Result[];
        detectAll(img: ImageWrapper | string, options?: DetectOptionsWithoutIsAll): Barcode.Result[];

        recognizeText(options?: DetectOptions): string | string[] | null;
        recognizeText(isAll: boolean): string | string[] | null;
        recognizeText(img: ImageWrapper | string, options?: DetectOptions): string | string[] | null;
        recognizeText(img: ImageWrapper | string, isAll: boolean): string | string[] | null;

        recognizeTexts(options?: DetectOptionsWithoutIsAll): string[];
        recognizeTexts(img: ImageWrapper | string, options?: DetectOptionsWithoutIsAll): string[];

    }

# 二维码 (QR Code)#

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

Marked by SuperMonster003 on Oct 30, 2023.

qrcode 模块用于识别图像中的二维码.

qrcode

    interface QrCode {

        (options?: DetectOptions): string | string[] | null;
        (isAll: boolean): string | string[] | null;
        (img: ImageWrapper | string, options?: DetectOptions): string | string[] | null;
        (img: ImageWrapper | string, isAll: boolean): string | string[] | null;

        detect(options?: DetectOptions): QrCode.Result | QrCode.Result[] | null;
        detect(isAll: boolean): QrCode.Result | QrCode.Result[] | null;
        detect(img: ImageWrapper | string, options?: DetectOptions): QrCode.Result | QrCode.Result[] | null;
        detect(img: ImageWrapper | string, isAll: boolean): QrCode.Result | QrCode.Result[] | null;

        detectAll(options?: DetectOptionsWithoutIsAll): QrCode.Result[];
        detectAll(img: ImageWrapper | string, options?: DetectOptionsWithoutIsAll): QrCode.Result[];

        recognizeText(options?: DetectOptions): string | string[] | null;
        recognizeText(isAll: boolean): string | string[] | null;
        recognizeText(img: ImageWrapper | string, options?: DetectOptions): string | string[] | null;
        recognizeText(img: ImageWrapper | string, isAll: boolean): string | string[] | null;

        recognizeTexts(options?: DetectOptionsWithoutIsAll): string[];
        recognizeTexts(img: ImageWrapper | string, options?: DetectOptionsWithoutIsAll): string[];

    }

# 按键 (Keys)#

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

Marked by SuperMonster003 on Oct 22, 2022.

按键模拟部分提供了一些模拟物理按键的全局函数, 包括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

# 设备 (Device)#

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

Marked by SuperMonster003 on Oct 22, 2022.

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.

设备在工业设计中的名称.

## device.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()#

如果设备处于振动状态, 则取消振动.

# 存储 (Storages)#

storages 模块可用于保存 [ 简单数据 / 配置信息 / 列表清单 ] 等.
保存的数据在脚本间共享, 因此不适于敏感数据的存储.

保存数据时, 需要一个名称, 类似命名空间.
一个名称对应一个独立的本地存储.
但无法像 Web 开发中 LocalStorage 一样提供域名独立的存储, 因为脚本路径可能随时改变.

保存的数据仅在以下情况下会被删除:

- AutoJs6 应用被卸载或清除数据
- 使用 storages.remove / Storage#remove / Storage#clear 等方法删除

支持存入的数据类型:

- number
- boolean
- string
- null
- Array
- Object
- ... ...

具体的存入规则详见 Storage#put 小节.

存入时, 由 JSON.stringify 序列化数据为 string 类型后再存入,
读取时, 由 JSON.parse 还原为原本的数据类型.

storages

## [m] create#

### create(name)#

- name { string } - 存储名称
- returns { Storage }

以 `name` 参数为名称创建一个本地存储, 并返回 Storage 实例:

    /* 创建一个名为 fruit 的本地存储. */
    let sto = storages.create('fruit');

    /* 存入 "键值对" 数据. */
    sto.put('apple', 10);
    sto.put('banana', 20);

    /* 访问数据. */
    sto.get('apple'); // 10
    sto.get('banana'); // 20
    sto.get('cherry'); // undefined

不同的 `name` 参数可以创建不同的本地存储:

    let stoFruit = storages.create('fruit');
    let stoPhone = storages.create('phone');

    /* "键" 名均为 apple, 不同的本地存储之间数据独立. */
    stoFruit.put('apple', 7);
    stoPhone.put('apple', 3);

    /* 访问数据 */
    stoFruit.get('apple') // 7
    stoPhone.get('apple') // 3

如果 `name` 参数对应的本地存储已存在, 则返回一个本地存储副本:

    let sto = storages.create('fruit');
    sto.put('apple', 10);

    /* 名为 fruit 的本地存储已创建, 因此返回的是存储副本. */
    let stoCopied = storages.create('fruit');

    /* 虽然 stoCopied 没有存入 apple 数据, 但 fruit 本地存储中存在. */
    stoCopied.get('apple'); // 10

    /* 副本与原始的本地存储并非引用关系. */
    sto === stoCopied; // false

为保证数据安全及唯一性, `name` 参数应尽量具体:

    storages.create('project-publishing-schedule');

## [m] remove#

### remove(name)#

- name { string } - 存储名称
- returns { boolean } - name 参数对应的本地存储是否存在

清除名为 `name` 的本地存储包含的全部数据.

如果名为 `name` 的本地存储已存在, 返回 `true`, 否则返回 `false`.

    let sto = storages.create('fruit');
    sto.put('apple', 10);
    sto.get('apple'); // 10

    /* 相当于 storages.create('fruit').clear(); . */
    storages.remove('fruit'); // true

    /* 执行 remove 方法后, sto 对象将不存在任何存储数据. */
    sto.get('apple'); // undefined

    /* 但 sto 依然可以存放新的数据. */
    sto.put('banana', 20);
    sto.get('banana'); // 20

# 文件 (Files)#

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

Marked by SuperMonster003 on Oct 22, 2022.

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()#

关闭文件. 同时会被缓冲区内容输出到文件.

打开一个文件写入后, 不再使用时务必关闭, 否则文件可能会丢失

# 引擎 (Engines)#

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

Marked by SuperMonster003 on Oct 22, 2022.

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.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.name + "(" + 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对象.

    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对象.

    engines.execAutoFile("/sdcard/脚本/1.auto");

## engines.stopAll()#

停止所有正在运行的脚本. 包括当前脚本自身.

## engines.stopAllAndToast()#

停止所有正在运行的脚本并显示停止的脚本数量. 包括当前脚本自身.

## engines.myEngine()#

返回当前脚本的脚本引擎对象(ScriptEngine)

[v4.1.0新增] 特别的, 该对象可以通过`execArgv`来获取他的运行参数, 包括外部参数、intent等. 例如：

    log(engines.myEngine().execArgv);

普通脚本的运行参数通常为空, 通过定时任务的广播启动的则可以获取到启动的intent.

## engines.all()#

- 返回 { Array }

返回当前所有正在运行的脚本的脚本引擎ScriptEngine的数组.

    log(engines.all());

# ScriptExecution#

执行脚本时返回的对象, 可以通过他获取执行的引擎、配置等, 也可以停止这个执行.

要停止这个脚本的执行, 使用`exectuion.getEngine().forceStop()`.

## ScriptExecution.getEngine()#

返回执行该脚本的脚本引擎对象(ScriptEngine)

## ScriptExecution.getConfig()#

返回该脚本的运行配置(ScriptConfig)

# ScriptEngine#

脚本引擎对象.

## ScriptEngine.forceStop()#

停止脚本引擎的执行.

## ScriptEngine.cwd()#

- 返回 { string }

返回脚本执行的路径. 对于一个脚本文件而言为这个脚本所在的文件夹；对于其他脚本, 例如字符串脚本, 则为`null`或者执行时的设置值.

## ScriptEngine.getSource()#

- 返回 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 }

返回一个字符串数组表示脚本运行时模块寻找的路径.

# 任务 (Tasks)#

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

Marked by SuperMonster003 on Oct 22, 2022.

# 模块 (Module)#

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

Marked by SuperMonster003 on Oct 22, 2022.

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
      };
    };

# 插件 (Plugins)#

在 AutoJs6 中, 插件分为 [ 应用插件 / 项目插件 / 内置扩展插件 ].

plugins 模块主要用于插件及扩展模块的加载并使其功能生效.

## 应用插件#

应用插件通常是一个由开发者编写的可安装的 APK 文件, 安装插件后可由 AutoJs6 通过 plugins.load 方法加载并使用插件.

应用插件的使用步骤:

- 按需寻找或自行开发插件 (APK 格式)
- 安装到指定设备
- 脚本中将插件包名以字符串形式传入 plugins.load 方法并赋值给一个变量
- 这个变量即指向插件的导出对象 (module.exports)

加载及使用方式:

    let { exp } = plugins.load('org.autojs.autojs.plugin.demo');
    exp.test('hello');

## 项目插件#

项目插件是依附于项目的一组 JavaScript 模块, 它们位于项目根目录的 `plugins` 文件夹中, 由 AutoJs6 通过 plugins.load 方法加载并使用.

例如项目结构 (部分) 如下:

    ┌ modules ┬ moduleA.js
    │         └ moduleB.js
    │         ┌ pluginA.js
    ├ plugins ┼ pluginB.js
    │         └ pluginC.js
    └ main.js

对于此项目, `pluginA.js`, `pluginB.js` 及 `pluginC.js` 均称为项目插件, 加载方式如下:

    plugins.load('pluginA');
    plugins.load('pluginA.js'); /* 同上. */

## 内置扩展插件#

内置扩展插件相当于内置的项目插件, 它们是内置于 AutoJs6 软件中的, 可通过调用 plugins.extend 等方法选择性地启用部分或全部内置扩展插件, 也称作内置扩展模块.

加载方式:

    /* 启用 Array 内置扩展插件. */
    plugins.extend('Arrayx');

    /* 启用全部内置扩展插件. */
    plugins.extendAll();

截至 2023 年 2 月, AutoJs6 内置了以下扩展插件:

- Arrayx - Array 扩展
- Numberx - Number 扩展
- Mathx - Math 扩展

plugins

## [m] load#

### load(appPluginPackageName)#

`[6.2.0]` `Overload 1/2`

- packageName { string } - 应用插件的包名
- returns { any }

加载 应用插件.

    /* 一个可能的样例. */
    plugins.load('org.autojs.autojs.plugin.demo');

### load(projectPluginName)#

`6.2.0` `Overload 2/2`

- packageName { string } - 项目插件名称
- returns { any }

加载 项目插件.

## [m] extend#

### extend(...moduleNames)#

`6.2.0`

- moduleNames { ...ExtendModulesNames[] } - 内置扩展插件名称
- returns { void }

加载指定的 内置扩展插件.

    /* 启用 Array 内置扩展插件. */
    plugins.extend('Arrayx');

    /* 启用 Array 和 Number 内置扩展插件. */
    plugins.extend('Arrayx', 'Numberx');

## [m] extendAll#

### extendAll()#

`6.2.0`

- returns { void }

加载所有的 内置扩展插件.

    plugins.extendAll();

如需在所有脚本均自动加载所有内置扩展插件, 而无需每次使用 `plugins.extendAll()`, 可对 AutoJs6 进行如下设置:

    AutoJs6 应用设置 - 扩展性 - JavaScript 内置对象扩展 - [ 启用 ]

## [m] extendAllBut#

### extendAllBut(...moduleNames)#

`6.2.0`

- moduleNames { ...ExtendModulesNames[] } - 内置扩展插件名称
- returns { void }

加载所有的 内置扩展插件, 但排除指定的插件.

    plugins.extendAllBut('Mathx'); /* 加载除 Math 外的全部内置扩展插件. */

# 消息浮动框 (Toast)#

toast 模块用于 消息浮动框 的 [ 显示 / 消除 / 定制 ] 等.

部分操作系统的 toast 消息可能无法按队列依次显示, 新的 toast 消息直接覆盖之前的 toast 消息.

可能出现上述异常的操作系统:

- API 级别 28 (安卓 9) [P]
- 鸿蒙 (HarmonyOS) 2

部分机型需授予 "后台弹出页面" 权限才能正常显示 toast 消息.

可能依赖上述权限的设备及操作系统:

- 小米 (XiaoMi / Redmi / BlackShark) - MIUI
- 维沃 (VIVO / IQOO) - Funtouch OS / OriginOS
- 欧珀 (OPPO / Realme) - ColorOS

部分机型的 toast 消息正常显示依赖通知权限, 当未授予通知权限或通知被 `阻止 (block)` 时, toast 可能无法正常显示, 参阅 notice.isEnabled 小节.

toast

## [@] toast#

toast 可作为全局对象使用:

    typeof toast; // "function"
    typeof toast.dismissAll; // "function"

### toast(text)#

`Global` `Overload 1/4` `Async`

- text { string } - 消息内容
- returns { void }

显示一个消息浮动框.

消息框的显示默认是依次进行的:

    /* 显示消息框 2 秒钟. */
    toast("hello");
    /* 显示消息框 2 秒钟, 且在前一个消息框消失后才显示. */
    toast("world");
    /* 显示消息框 2 秒钟, 且在前一个消息框消失后才显示. */
    toast("hello world");

### toast(text, isLong)#

`Global` `Overload 2/4` `Async`

- text { string } - 消息内容
- isLong = false { `'long'` | `'l'` | `'short'` | `'s'` | boolean } - 是否以较长时间显示
- returns { void }

控制单个消息框显示时长:

    toast("hello", 'long'); /* 显示消息框 3.5 秒钟. */
    toast("hello", true); /* 同上. */

> 注: 仅有 [ 长 / 短 ] 两种时长, 此时长由安卓系统决定.
> 通常, 短时为 2 秒, 长时为 3.5 秒.

### toast(text, isLong, isForcible)#

`Global` `Overload 3/4` `Async`

- text { string } - 消息内容
- isLong = false { `'long'` | `'l'` | `'short'` | `'s'` | boolean } - 是否以较长时间显示
- isForcible = false { `'forcible'` | `'f'` | boolean } - 是否强制覆盖显示
- returns { void }

使用 "强制覆盖显示" 参数可立即显示消息框:

    toast("hello");
    /* 显示消息框 2 秒钟, 且立即显示, 前一个消息框 "hello" 被 "覆盖". */
    toast("world", "short", "forcible");

> 注: 强制覆盖仅对当前脚本有效, 对其他脚本及应用程序无效.

### toast(text, isForcible)#

`Global` `Overload 4/4` `Async`

- text { string } - 消息内容
- isForcible { `'forcible'` | `'f'` } - 强制覆盖显示 (字符标识)
- returns { void }

此方法相当于忽略 isLong 参数:

    toast("hello");
    /* 显示消息框 2 秒钟, 且立即显示, 前一个消息框 "hello" 被 "覆盖". */
    toast("world", "forcible");

> 注: 此方法的 isForcible 参数只能为具有明确意义的字符标识, 不能为 boolean 类型或其他类型, 否则 isForcible 将被视为 isLong.

## [m] dismissAll#

### dismissAll()#

`Global` - returns { void }

强制消除所有由 AutoJs6 产生的消息框, 包括正在显示的及等待显示的.

使用方式:

    toast.dismissAll(); /* 立即消除所有消息框. */

示例:

    toast("hello");
    toast("world");
    toast("of");
    toast("JavaScript");

    sleep(1e3);

    /* "hello" 显示 1 秒后消失, "world" 及其他消息框均不再显示. */
    /* 若无 sleep 语句, 由于 toast 是异步的, 上述消息框均不会显示. */
    toast.dismissAll();

    /* dismissAll 仅对已在队列中的消息框有效, 因此下述消息框正常显示. */
    toast("forcibly dismissed");

> 注: 强制取消显示仅对当前脚本有效, 对其他脚本及应用程序无效.

# 消息通知 (Notice)#

notice 模块用于创建并显示消息通知.

位于通知栏的消息, 可用于 [ 消息提醒 / 信息通信 / 执行操作 ] 等.

> 注: 不同安卓系统的通知表现可能存在较大差异, 与文档描述也可能存在出入.

## 简单操作#

显示一条通知:

    /* 内容为 hello (标题为空). */
    notice('hello');

    /* 标题为 new message, 内容为 hello. */
    notice('new message', 'hello');

    /* 标题为 new message, 内容为 hello. */
    notice({ title: 'new message', content: 'hello' });

    /* 标题为 new message, 内容为 hello. */
    notice(notice.getBuilder()
        .setContentTitle('new message')
        .setContentText('hello'));

显示两条独立的通知:

    notice('hello');
    notice('world');

显示两条可覆盖的通知:

    /* 方法 A: 通过指定相同的通知 ID 实现通知覆盖. */

    let id = 5; /* 任意 ID 均可, 用于区分不同的通知. */

    notice('hello', { notificationId: id }); /* 指定一个通知 ID. */
    sleep(1e3); /* 阻塞 1 秒. */
    notice('world', { notificationId: id }); /* 通知 ID 相同, 因此 1 秒后上一条通知被替代 (覆盖). */

    /* 方法 B: 通过 notice.config 配置 notice 的默认选项. */

    notice.config({ useDynamicDefaultNotificationId: false }); /* 禁用动态通知 ID 选项. */
    notice('hello');
    sleep(1e3); /* 阻塞 1 秒. */
    notice('world'); /* 1 秒后上一条通知被替代 (覆盖). */

显示一条定制通知:

    notice('hello', {
        bigContent: 'This is a message which says "hello"\n-- from AutoJs6', /* 设置长内容. */
        isSilent: true, /* 静音模式. */
        appendScriptName: 'content', /* 附加脚本名称到内容结尾. */
        intent: 'docs', /* 点击通知后跳转到 AutoJs6 的文档页面. */
        autoCancel: true, /* 点击通知后自动移除通知. */
    });

    /* 更多配置选项, 可参阅本章节后续内容. */

如果需要发送多条上述定制通知, 可将上述定制选项提取出来:

    /* 定义一个定制通知选项变量. */
    let options = {
        bigContent: 'This is a message which says "hello"\n-- from AutoJs6', /* 设置长内容. */
        isSilent: true, /* 静音模式. */
        appendScriptName: 'content', /* 附加脚本名称到内容结尾. */
        intent: 'docs', /* 点击通知后跳转到 AutoJs6 的文档页面. */
        autoCancel: true, /* 点击通知后自动移除通知. */
    };

    /* 以上述定制选项发送三条通知. */

    notice('hello', options);
    notice('world', options);
    notice('tour', options);

上述示例指定通知 ID 可实现通知覆盖:

    /* 指定一个通知 ID 为固定值. */
    options.notificationId = 20;

    /* 以上述定制选项发送三条可覆盖的通知. */

    notice('hello', options);
    sleep(1e3);
    notice('world', options); /* 1 秒后覆盖 'hello'. */
    sleep(1e3);
    notice('tour', options); /* 1 秒后覆盖 'world'. */

    /* options 可随时进行定制修改. */

    delete options.bigContent; /* 删除长内容. */
    sleep(1e3);
    notice('movie', options); /* 1 秒后覆盖 'tour' */

    options.intent = 'home'; /* 修改 intent 属性. */
    sleep(1e3);
    notice('here', options); /* 1 秒后覆盖 'movie' */

## 通知渠道#

`通知渠道 (Notification Channel)` 用于分类管理通知.

例如设置两个渠道, 水果和天气. 水果渠道用于发送与水果销量变化相关的通知, 天气渠道用于发送气象数据变化相关的通知.

不同渠道的通知可分别定制, 如是否弹出通知, 是否振动, 通知指示灯开关及颜色, 是否静音等. 渠道之间的设置是互相独立的.

> 更多通知渠道的内容, 参阅 通知渠道 术语章节.

> notice 模块的渠道相关方法, 参阅 notice.channel 小节.

### 创建渠道#

通知渠道使用 `渠道 ID (Channel ID)` 作为唯一标识.

以渠道 ID 名称 `'my_channel_id'` 为例, 当 ID 为 `'my_channel_id'` 的渠道从未创建时, `channel.create('my_channel_id', options)` 将创建一个新的渠道, 其 ID 为 `'my_channel_id'`.

    notice.channel.create('my_channel_id', {
        name: 'New message',
        description: 'Messages from David',
        importance: 3,
        enableLights: true,
        lightColor: 'blue',
        enableVibration: true,
    });

上述示例代码创建了一个新渠道, 并进行了渠道配置, 包括 [ 名称 (name) / 描述 (description) / 优先级 (importance) / 启动指示灯且设置为蓝色 / 启用振动 ].

创建渠道后, 使用渠道 ID 可以在渠道内显示通知:

    /* 简单通知. */
    notice('hello', { channelId: 'my_channel_id' });

    /* 设置一些选项. */
    notice('hello', {
        channelId: 'my_channel_id',
        isSilent: true, /* 静音模式. */
        intent: 'homepage', /* 点击通知后跳转到 AutoJs6 的主页页面. */
        autoCancel: true, /* 点击通知后自动移除通知. */
    });

### 修改渠道#

以渠道 ID 名称 `'my_channel_id'` 为例, 当 ID 为 `'my_channel_id'` 的渠道已存在 (且未经删除) 时, `channel.create('my_channel_id', options)` 将修改这个渠道的相关配置.

    notice.channel.create('my_channel_id', {
        name: 'New message',
        description: 'There is a new message from David',
        importance: 3,
    });

上述示例代码修改了渠道配置, 包括 [ 名称 (name) / 描述 (description) / 优先级 (importance) ].

需额外留意, 渠道的修改并非总是生效的, 需满足以下规则:

- 名称 (name) 允许修改
- 描述 (description) 允许修改
- 优先级 (importance) 需同时满足以下两个条件方可修改
  - 优先级降级修改
  - 用户从未修改当前渠道的优先级
- 除上述 [ 名称 / 描述 / 优先级 ] 外, 其他所有属性均无法修改

### 恢复渠道#

以渠道 ID 名称 `'my_channel_id'` 为例, 当 ID 为 `'my_channel_id'` 的渠道通过 `channel.remove()` 被删除时, `channel.create('my_channel_id', options)` 将重新恢复之前被删除的渠道 (反删除), 且附带之前渠道的所有配置.

这样的设计是防止应用通过代码的方式恶意篡改用户对通知渠道的配置.

### 渠道放权#

使用代码创建渠道时, 可自定义渠道的默认通知行为, 如指示灯颜色及是否振动等.

但渠道创建后, 将无法通过代码更改这些设置 (除上面提到的名称, 描述, 和受条件限制的优先级之外).

对于渠道的设置, 用户拥有最终控制权.

notice

## [@] notice#

notice 可作为全局对象使用:

    typeof notice; // "function"
    typeof notice.channel; // "object"
    typeof notice.getBuilder; // "function"

### notice(content)#

`6.3.0` `Global` `Overload 1/8`

- content { string } - 通知消息的内容
- returns { number } - 通知 ID

发送通知, 包含内容.

    notice('hello');

### notice(title, content)#

`6.3.0` `Global` `Overload 2/8`

- title { string } - 通知消息的标题
- content { string } - 通知消息的内容
- returns { number } - 通知 ID

发送通知, 包含标题及内容.

    notice('message', 'hello');

> 注: 第 1 个 (索引 0) 参数代表标题, 第 2 个 (索引 1) 参数代表内容.

### notice()#

`6.3.0` `Global` `Overload 3/8`

- returns { number } - 通知 ID

发送通知, 主要用于测试.

该测试通知包含标题及内容.

    // 以 AutoJs6 语言为 English 为例,
    // 标题为 Script notification,
    // 内容为 Notification from script.
    notice();

### notice(options)#

`6.3.0` `Global` `Overload 4/8`

- options { NoticeOptions } - 通知选项配置
- returns { number } - 通知 ID

发送通知, 并进行选项配置.

    notice({
        bigContent: 'This is a message which says "hello"\n-- from AutoJs6', /* 设置长内容. */
        isSilent: true, /* 静音模式. */
        appendScriptName: 'content', /* 附加脚本名称到内容结尾. */
        intent: 'settings', /* 点击通知后跳转到 AutoJs6 的设置页面. */
        autoCancel: true, /* 点击通知后自动移除通知. */
    });

更多配置选项, 可参阅 NoticeOptions 类型章节.

### notice(content, options)#

`6.3.0` `Global` `Overload 5/8`

- content { string } - 通知消息的内容
- options { NoticeOptions } - 通知选项配置
- returns { number } - 通知 ID

发送通知, 包含内容, 并进行选项配置.

与 `notice(options)` 类似, 但增加 `content` 参数.

    notice('hello', { isSilent: true });

> 注: 内容参数可能重复指定.
> 出现重复指定时, 按以下优先级处理:
> options.content > content

### notice(title, content, options)#

`6.3.0` `Global` `Overload 6/8`

- title { string } - 通知消息的标题
- content { string } - 通知消息的内容
- options { NoticeOptions } - 通知选项配置
- returns { number } - 通知 ID

发送通知, 包含标题及内容, 并进行选项配置.

与 `notice(options)` 类似, 但增加 `title` 和 `content` 参数.

    notice('message', 'hello', { isSilent: true });

> 注: 标题参数与内容参数可能重复指定.
> 出现重复指定时, 按以下优先级处理:
> options.title > title options.content > content

### notice(builder)#

`6.3.0` `Global` `Overload 7/8`

- builder { NoticeBuilder } - 通知构建器
- returns { number } - 通知 ID

使用 `通知构建器 (Notice Builder)` 发送通知.

参阅 getBuilder 小节.

### notice(builder, options)#

`6.3.0` `Global` `Overload 8/8`

- builder { NoticeBuilder } - 通知构建器
- options { NoticeOptions } - 通知选项配置
- returns { number } - 通知 ID

使用 `通知构建器 (Notice Builder)` 发送通知, 并进行选项配置.

    let notificationId = 12;
    let progress = 0;
    let progressMax = 100;

    let builder = notice.getBuilder()
        .setSilent(true)
        .setContentTitle('正在下载应用');

    while (progress < progressMax) {
        builder
            .setProgress(progressMax, progress, false)
            .setContentText(`已完成 ${progress}%`);
        notice(builder, { notificationId });
        sleep(50);
        progress += Mathx.randInt(1, 4);
    }
    builder
        .setContentText(`已完成 ${progressMax}%`)
        .setContentTitle('下载完成')
    notice(builder, { notificationId });

参阅 getBuilder 小节.

## [m] isEnabled#

### isEnabled()#

`6.3.0`

- returns { boolean }

检测 AutoJs6 的通知是否未被阻止 (not blocked).

通常的阻止 (block) 情况:

- 通知全局开关默认未开启或被用户关闭
- 通知权限 `Manifest.permission.POST_NOTIFICATIONS` 未授予或被撤回

    console.log(notice.isEnabled()); /* e.g. true */

部分机型的 toast 功能依赖通知权限, 如需在使用 toast 时检查通知权限是否被阻止, 可使用 `isEnabled` 或 `ensureEnabled` 方法:

    if (!notice.isEnabled()) {
        console.warn('通知被阻止, toast 可能无法正常显示');
    }
    toast('hello');

    notice.ensureEnabled();
    toast('hello');

结合 notice.launchSettings 可辅助用户跳转至通知设置页面:

    if (!notice.isEnabled()) {
        notice.launchSettings();
    }

## [m] ensureEnabled#

### ensureEnabled()#

`6.3.0`

- returns { void }

确保 AutoJs6 的通知未被阻止 (not blocked).

当通知被阻止时将抛出 `Exception` 异常.

## [m] launchSettings#

### launchSettings()#

`6.3.0`

- returns { void }

跳转至 AutoJs6 的通知设置页面.

    notice.launchSettings();

## [m] cancel#

### cancel(id)#

`6.3.0`

- id { number } - 通知 ID
- returns { void }

消除通知.

    let id = notice({ title: 'New message' });
    /* 2 秒后自动消除通知. */
    setTimeout(() => notice.cancel(id), 2e3);

## [m] getBuilder#

### getBuilder()#

`6.3.0`

- returns { NoticeBuilder }

获取一个简单通知构建器.

简单通知构建器包含以下默认设置:

- `setSmallIcon(R.drawable.autojs6_material)` # AutoJs6 应用图标作为 smallIcon
- `setPriority(NotificationCompat.PRIORITY_HIGH)` # 高优先级 (仅针对 Android 7.1 及以下)

构建器通常配合 `notice` 方法作为 第 1 个 (索引 0) 参数使用, 即 `notice(notice.getBuilder())`:

    let builder = notice.getBuilder();
    builder.setContentTitle('Weather condition');
    builder.setContentText('The sky is getting dark');
    notice(builder);

    /* 链式调用使代码更简洁. */

    notice(notice.getBuilder()
        .setContentTitle('Weather condition')
        .setContentText('The sky is getting dark'));

构建器可用于设置更多通知行为, 如 setStyle, setTimeoutAfter, setProgress 等.

但需要注意参数类型需严格符合要求, AutoJs6 内置的 全能类型 是不可用的.

关于通知构建器的更多用法, 参阅 NoticeBuilder 类型章节.

## [m] config#

config 方法用于修改默认配置, 即用于配置通知渠道与通知发送的默认行为.

例如 `notice('hello')` 会发送一个内容为 "hello" 的通知, 但其中隐含了许多默认的通知行为.

> 注: 初次使用 notice 模块时, 建议先跳过此小节内容, 待了解包括 channel 等在内的相关内容后再继续阅读当前小节.

例如, `isSilent` 默认为 `false`, 表示不进行强制静音.
通过 `notice.config` 可配置所有通知发送时, 默认启用强制静音:

    notice.config({ defaultIsSilent: true }); /* 通知发送时, 默认强制静音. */

执行上述示例代码后, `notice('hello')` 将会静音发送通知.

如果不执行上述代码, 则需要在每一个 notice 方法中加入 `isSilent` 选项设置:

    notice('hello', { isSilent: true });
    notice('message', { isSilent: true });
    notice('finished', { isSilent: true });
    /* ... ... */

因此, `notice.config` 适用于在同一个脚本或项目中, 有多次使用 `notice` 需求的场景.

> 注: notice.config 配置的是默认行为, 当通过参数明确指定了某个行为时, 默认行为将不会生效.

### config(preset)#

`6.3.0`

- preset { NoticePresetConfiguration } - 通知预设配置对象
- returns { void }

配置通知渠道与通知发送的默认行为.

    notice.config({
        useDynamicDefaultNotificationId: false, /* 禁用动态通知 ID. */
        useScriptNameAsDefaultChannelId: false, /* 禁用以脚本名称作为渠道 ID. */
        enableChannelInvalidModificationWarnings: false, /* 禁用渠道修改无效的警告消息. */
        defaultTitle: 'NEW MESSAGE', /* 修改默认通知标题. */
        /* ... ... */
    });

更多可用的默认行为配置, 参阅 NoticePresetConfiguration 类型章节.

## [p+] channel#

### [m] create#

`channel.create` 可用于 [ 创建 / 修改 / 恢复 ] 某个特定 `渠道 ID (Channel ID)` 的通知渠道.

详见 通知渠道 小节.

#### create(channelId)#

`6.3.0` `Overload 1/3`

- channelId { string | number } - 渠道 ID
- returns { string } - 渠道 ID

创建通知渠道, 并指定渠道 ID.

    let id = 'my_channel_id';
    notice.channel.create(id); /* 创建渠道. */
    notice('hello', { channelId: id }); /* 发送通知. */

#### create(channelId, options)#

`6.3.0` `Overload 2/3`

- channelId { string | number } - 渠道 ID
- options { NoticeChannelOptions } - 渠道创建选项
- returns { string } - 渠道 ID

创建通知渠道, 指定渠道 ID 并进行渠道配置.

    notice.channel.create('my_channel_id', {
        name: 'New message', /* 渠道名称. */
        description: 'Messages from David', /* 渠道描述. */
        importance: 3, /* 渠道优先级. */
        enableLights: true, /* 启用指示灯. */
        lightColor: 'blue', /* 设置指示灯颜色. */
        enableVibration: true, /* 启用振动. */
    });

更多渠道配置相关信息, 参阅 NoticeChannelOptions 类型章节.

> 注: 渠道 ID 可能重复指定.
> 出现重复指定时, 按以下优先级处理:
> options.id > channelId

#### create(options)#

`6.3.0` `Overload 3/3`

- options { NoticeChannelOptions } - 渠道创建选项
- returns { string } - 渠道 ID

创建通知渠道, 与 `create(channelId, options)` 方法类似, 但省略 `channelId` 参数.

如需指定渠道 ID, 可在 `options` 参数中使用 `id` 属性:

    notice.channel.create({ id: 'my_channel_id' });

当不指定 `id` 时, 渠道 ID 将使用当前运行脚本的脚本名称.

更多渠道配置相关信息, 参阅 NoticeChannelOptions 类型章节.

### [m] contains#

#### contains(channelId)#

`6.3.0`

- channelId { string | number } - 渠道 ID
- returns { boolean } - 当前渠道 ID 是否已被创建

返回指定 `渠道 ID (Channel ID)` 的 AutoJs6 渠道是否存在.

    notice.channel.contains('my_channel_id'); /* e.g. false */

### [m] remove#

#### remove(channelId)#

`6.3.0`

- channelId { string | number } - 渠道 ID
- returns { boolean } - 删除前, 当前渠道 ID 是否已被创建

根据 `渠道 ID (Channel ID)` 删除 AutoJs6 的渠道实例.

删除前, 若渠道已被创建且未被删除, 则返回 `true`, 否则返回 `false`.

    let id = 'my_channel_id';
    if (notice.channel.contains(id)) {
        notice.channel.remove(id); // true
    }

### [m] get#

#### get(channelId)#

`6.3.0`

- returns { android.app.NotificationChannel | null } - 渠道实例

根据 `渠道 ID (Channel ID)` 获取 AutoJs6 的渠道实例, 不存在时返回 `null`.

    let id = 'my_channel_id';
    if (notice.channel.contains(id)) {
        console.log(notice.channel.get(id)); /* 打印通知渠道信息. */
    } else {
        console.log(`ID "${id}" 对应的通知渠道不存在`);
    }

    /* 不使用 contains 也可以判断 Channel ID 的存在性. */

    let channel = notice.channel.get(id);
    if (channel !== null) {
        /* ... */
    }

### [m] getAll#

#### getAll()#

`6.3.0`

- returns { android.app.NotificationChannel[] } - 渠道实例数组

获取 AutoJs6 的所有通知渠道实例 (不包含已被删除的).

    console.log(`当前共计渠道 ${notice.channel.getAll().length} 个`);
    notice.channel.getAll().map(ch => ch.getId()); /* 获取所有渠道的 ID. */

# 控制台 (Console)#

AutoJs6 的控制台类似 Web 浏览器的调试控制台, 用于信息输出或辅助代码调试.

## 显示控制台#

AutoJs6 支持以下几种方式显示控制台:

- 点击 AutoJs6 应用主页右上区域 "日志" 图标 - 显示控制台 Activity 活动页面.
- 使用代码 `console.launch()` - 显示控制台 Activity 活动页面.
- 使用代码 `console.show()` - 显示控制台 `浮动窗口 (Floating Window)`.

## 模块作用#

console 模块的主要作用:

- 控制台日志内容的管理 - [ 按分级显示内容 / 内容清空 / 时间跟踪 / 栈追踪 / 存入文件 ] 等
  - console.log
  - console.setGlobalLogConfig
- 控制台浮动窗口的管理 - [ 窗口样式 / 文字样式 / 窗口显示与隐藏 / 窗口位置与尺寸 ] 等
  - console.show
- 控制台 Activity 活动窗口管理
  - console.launch

控制台浮动窗口的相关方法仅对浮动窗口有效, 而对 Activity 活动窗口无效:

    /* 浮动窗口日志文本字体大小修改为 23sp, */
    /* 但 Activity 活动窗口的日志字体不受影响. */

    console.setContentTextSize(23);
    console.show(); /* 浮动窗口日志字体 23sp. */

    console.launch(); /* Activity 活动窗口的日志字体仍为默认大小. */

## 浮动窗口#

使用 console.show 可显示控制台的浮动窗口.

- 浮动窗口右上区域有三个操作按钮
  - 最小化按钮 - 收起浮动窗口并显示一个浮动按钮
  - 空间状态配置按钮 - 显示或隐藏空间状态 (位置及尺寸) 配置按钮
  - 关闭按钮 - 隐藏浮动窗口
- 拖动标题栏区域也可实现浮动窗口的位置移动
- 日志显示区域支持双指缩放改变文本字体大小

如需使用代码配置浮动窗口的外观与样式, 参阅 console.build 小节.

一个简单的浮动窗口配置示例, 以便快速了解浮动窗口的配置方式:

- 尺寸 - 宽 80% 屏幕宽度, 高 60% 屏幕高度
- 位置 - X 坐标 10% 屏幕宽度, Y 坐标 15% 屏幕高度
- 标题 - HELLO WORLD
- 标题字号 - 18sp
- 标题背景颜色 - 900 号深橙色, 80% 透明度
- 日志字号 - 16sp
- 日志背景颜色 - 与标题背景颜色相同, 50% 透明度
- 浮动窗口在脚本结束后 6 秒钟自动隐藏

    /* 使用构建器方式. */

    console.build({
        size: [ 0.8, 0.6 ],
        position: [ 0.1, 0.15 ],
        title: 'HELLO WORLD',
        titleTextSize: 18,
        contentTextSize: 16,
        backgroundColor: 'deep-orange-900',
        titleBackgroundAlpha: 0.8,
        contentBackgroundAlpha: 0.5,
        exitOnClose: 6e3,
    }).show();

    /* 使用链式配置方式. */

    console
        .setSize(0.8, 0.6)
        .setPosition(0.1, 0.15)
        .setTitle('HELLO WORLD')
        .setTitleTextSize(18)
        .setContentTextSize(16)
        .setBackgroundColor('deep-orange-900')
        .setTitleBackgroundAlpha(0.8)
        .setContentBackgroundAlpha(0.5)
        .setExitOnClose(6e3)
        .show();

    /* 使用传统分步配置方式. */

    console.setSize(0.8, 0.6);
    console.setPosition(0.1, 0.15);
    console.setTitle('HELLO WORLD');
    console.setTitleTextSize(18);
    console.setContentTextSize(16);
    console.setBackgroundColor('deep-orange-900');
    console.setTitleBackgroundAlpha(0.8);
    console.setContentBackgroundAlpha(0.5);
    console.setExitOnClose(6e3);
    console.show();

console

## [m] show#

### show()#

`[6.3.0]`

- returns { this }

显示控制台浮动窗口.

窗口显示之前或之后, 均可设置浮动窗口的样式及空间状态.
如将窗口尺寸设置为 `500` × `800`:

    /* 在 show 之前设置尺寸. */

    console.setSize(500, 800);
    console.show();

    /* 在 show 之后设置尺寸. */

    console.show();
    console.setSize(500, 800);

    /* 上述两个示例均支持链式调用. */
    console.show().setSize(500, 800);
    console.setSize(500, 800).show();

## [m] isShowing#

### isShowing()#

`6.3.0`

- returns { boolean }

返回控制台浮动窗口是否未处于隐藏状态.

未隐藏状态包含以下情况:

- 浮动窗口展开显示
- 浮动窗口折叠显示 (最小化)

    console.show();
    console.isShowing(); // true

    console.collapse(); /* 折叠 (最小化) 浮动窗口. */
    console.isShowing(); /* 依然为 true. */

    console.hide(); /* 隐藏浮动窗口. */
    console.isShowing(); // false

## [m] hide#

### hide()#

`[6.3.0]`

- returns { this }

隐藏控制台浮动窗口.

窗口隐藏后, 其样式及空间状态均被保留, 即使在脚本结束后:

    console.show();
    console.setSize(500, 800);
    console.hide();

此时在另一个脚本运行如下代码:

    console.show();

显示浮动窗口后, 窗口尺寸依然为 `500` × `800`, 之前的窗口配置被还原.

如需在显示之前恢复窗口配置默认值, 可使用 `console.reset()`:

    console.reset();
    console.show();

## [m] reset#

### reset()#

`6.3.0`

- returns { this }

重置控制台浮动窗口的样式及空间状态, 恢复其默认值.

`reset` 方法在浮动窗口显示时也可使用:

    console.setSize(500, 800).show();
    setTimeout(console.reset, 2e3); /* 2 秒钟后重置. */

## [m] collapse#

### collapse()#

`6.3.0`

- returns { this }

折叠显示控制台浮动窗口, 即最小化窗口.

## [m] expand#

### expand()#

`6.3.0`

- returns { this }

展开显示控制台浮动窗口.

使用 console.show 显示窗口时, 默认为展开显示状态.

## [m] launch#

### launch()#

`6.3.0`

- returns { void }

启动控制台 Activity 活动窗口.

此方法相当于是 AutoJs6 首页右上区域点击 "日志" 图标的代码实现.

## [m] build#

### build(options)#

`6.3.0`

- options { ConsoleBuildOptions } - 构建器选项
- returns { { show(): void }}

构建控制台浮动窗口的配置.

构建后使用 `show` 方法显示控制台浮动窗口, 即 `console.build({ ... }).show()`.

构建器支持一次性配置多个浮动窗口样式选项:

    console.build({
        size: [ 0.8, 0.6 ], /* 窗口大小, 80% 屏幕宽度 × 60% 屏幕高度. */
        position: [ 0.1, 0.15 ], /* 窗口位置, X 坐标 10% 屏幕宽度, Y 坐标 15% 屏幕高度. */
        title: 'HELLO WORLD', /* 窗口标题文本. */
        titleTextSize: 18, /* 窗口标题字号, 单位为 sp. */
        contentTextSize: 16, /* 窗口日志字号, 单位 sp. */
        backgroundColor: 'deep-orange-900', /* 窗口标题及日志区域的背景色, 900 号深橙色. */
        titleBackgroundAlpha: 0.8, /* 窗口标题区域背景透明度, 90%. */
        contentBackgroundAlpha: 0.5, /* 窗口日志区域背景透明度, 50%. */
        exitOnClose: 6e3, /* 脚本运行结束时 6 秒钟后自动关闭窗口. */
    }).show(); /* 使用 show 方法显示浮动窗口. */

更多构建参数及使用方法, 参阅 ConsoleBuildOptions 类型章节.

## [m] setSize#

### setSize(width, height)#

`[6.3.0]`

- width { number } - 浮动窗口宽度值 (像素值/百分数)
- height { number } - 浮动窗口高度值 (像素值/百分数)
- returns { this }

设置控制台浮动窗口的尺寸.

    console.setSize(0.8, 700).show(); /* 80% 屏幕宽度, 700 像素高度. */
    console.build({ size: [ 0.8, 700 ] }).show(); /* 效果同上. */

## [m] setPosition#

### setPosition(x, y)#

`[6.3.0]`

- x { number } - 浮动窗口位置 X 坐标 (像素值/百分数)
- y { number } - 浮动窗口位置 Y 坐标 (像素值/百分数)
- returns { this }

设置控制台浮动窗口的位置.

    console.setPosition(0.1, 0.15).show(); /* X: 10% 屏幕宽度, Y: 15% 屏幕高度. */
    console.build({ position: [ 0.1, 0.15 ] }).show(); /* 效果同上. */

## [m] setExitOnClose#

### setExitOnClose(exitOnClose?)#

`6.3.0` `Overload 1/2`

- [ `true` ] exitOnClose { boolean } - 浮动窗口是否自动关闭
- returns { this }

设置控制台浮动窗口在脚本结束时是否自动关闭.

    console.setExitOnClose(true).show(); /* 自动关闭启用, 脚本结束后 5 秒钟自动关闭浮动窗口. */
    console.setExitOnClose().show(); /* 省略参数, 效果同上. */
    console.build({ exitOnClose: true }).show(); /* 效果同上. */

    console.setExitOnClose(false).show(); /* 禁用自动关闭. */
    console.build({ exitOnClose: false }).show(); /* 效果同上. */

### setExitOnClose(timeout)#

`6.3.0` `Overload 2/2`

- timeout { number } - 浮动窗口自动关闭的超时时间 (毫秒)
- returns { this }

设置控制台浮动窗口在脚本结束时自动关闭的超时时间, 单位为毫秒.

    console.setExitOnClose(6e3).show(); /* 脚本结束后 6 秒钟自动关闭浮动窗口. */
    console.build({ exitOnClose: 6e3 }).show(); /* 效果同上. */

## [m] setTitle#

### setTitle(title)#

`[6.3.0]`

- title { string } - 浮动窗口标题文本
- returns { this }

设置控制台浮动窗口的标题文本.

    console.setTitle('空调温度监测').show();
    console.build({ title: '空调温度监测' }).show(); /* 效果同上. */

## [m] setTitleTextSize#

### setTitleTextSize(size)#

`6.3.0`

- size { number } - 浮动窗口标题文本字体大小
- returns { this }

设置控制台浮动窗口的标题文本字体大小, 单位 `sp`.

    console.setTitleTextSize(20).show(); /* 设置标题字体大小为 20sp. */
    console.build({ titleTextSize: 20 }).show(); /* 效果同上. */

## [m] setTitleTextColor#

### setTitleTextColor(color)#

`6.3.0`

- color { OmniColor } - 浮动窗口标题文本字体颜色
- returns { this }

设置控制台浮动窗口的标题文本字体颜色.

    console.setTitleTextColor('dark-orange').show(); /* 设置标题字体颜色为深橙色. */
    console.build({ titleTextColor: 'dark-orange' }).show(); /* 效果同上. */

## [m] setTitleBackgroundColor#

### setTitleBackgroundColor(color)#

`6.3.0`

- color { OmniColor } - 浮动窗口标题显示区域背景颜色
- returns { this }

设置控制台浮动窗口的标题显示区域背景颜色.

    /* 设置标题显示区域背景颜色为深蓝色. */
    console.setTitleBackgroundColor('dark-blue').show();
    console.build({ titleBackgroundColor: 'dark-blue' }).show(); /* 效果同上. */

    /* 设置标题显示区域背景颜色为半透明深蓝色. */
    console.setTitleBackgroundColor(Color('dark-blue').setAlpha(0.5)).show();
    console.setTitleBackgroundColor('#8000008B').show(); /* 效果同上. */

    /* 透明度也可使用 setTitleBackgroundAlpha 单独设置. */
    console
        .setTitleBackgroundColor('dark-blue')
        .setTitleBackgroundAlpha(0.5)
        .show();

## [m] setTitleBackgroundAlpha#

### setTitleBackgroundAlpha(alpha)#

`6.3.0`

- alpha { number } - 浮动窗口标题显示区域背景颜色透明度
- returns { this }

设置控制台浮动窗口的标题显示区域背景颜色透明度.

    /* 设置标题显示区域背景颜色为半透明. */
    console.setTitleBackgroundAlpha(0.5).show();
    console.build({ titleBackgroundAlpha: 0.5 }).show(); /* 效果同上. */

    /* 设置标题显示区域背景颜色为半透明深蓝色. */
    console
        .setTitleBackgroundColor('dark-blue')
        .setTitleBackgroundAlpha(0.5)
        .show();

## [m] setTitleIconsTint#

### setTitleIconsTint(color)#

`6.3.0`

- color { OmniColor } - 浮动窗口操作按钮着色
- returns { this }

设置控制台浮动窗口的操作按钮着色.

    /* 设置操作按钮着色为绿色. */
    console.setTitleIconsTint('green').show();
    console.build({ titleIconsTint: 'green' }).show(); /* 效果同上. */

## [m] setContentTextSize#

### setContentTextSize(size: number)#

`6.3.0`

- param { number } - 浮动窗口日志文本字体大小
- returns { this }

设置控制台浮动窗口的日志文本字体大小, 单位 `sp`.

    /* 设置日志文本字体大小为 18sp. */
    console.setContentTextSize(18).show();
    console.build({ contentTextSize: 18 }).show(); /* 效果同上. */

## [m] setContentTextColor#

### setContentTextColor(colorMap)#

`6.3.0` `Overload 1/2`

- colorMap {{
  - verbose?: OmniColor;
  - log?: OmniColor;
  - info?: OmniColor;
  - warn?: OmniColor;
  - error?: OmniColor;
  - assert?: OmniColor;
- }} - 浮动窗口日志文本字体颜色表

设置控制台浮动窗口的日志文本字体颜色, 按日志等级设置一个或多个不同的字体颜色.

    /* 设置 LOG 等级日志字体颜色为深橙色. */
    console.setContentTextColor({ log: 'dark-orange' }).show();
    console.log('content text color test for console.log');

    /* 设置 ERROR 等级日志字体颜色为深红色. */
    console.setContentTextColor({ error: 'dark-red' }).show();
    console.error('content text color test for console.error');

    /* 设置多个不同等级日志的字体颜色. */
    console.setContentTextColor({
        verbose: 'gray',
        log: 'white',
        info: 'light-green',
        warn: 'light-blue',
        error: 'red',
    }).show();
    [ 'verbose', 'log', 'info', 'warn', 'error' ].forEach((fName) => {
        console[fName].call(console, `content text color test for console.${fName}`);
    });

### setContentTextColor(color)#

`6.3.0` `Overload 2/2`

- colors { OmniColor } - 浮动窗口日志文本字体统一颜色
- returns { this }

设置控制台浮动窗口的日志文本字体的统一颜色.

此方法设置颜色时不区分日志等级, 统一设置所有日志的文本颜色.

    /* 所有日志本文的颜色统一设置为深绿色. */
    console.setContentTextColor('dark-green').show();
    [ 'verbose', 'log', 'info', 'warn', 'error' ].forEach((fName) => {
        console[fName].call(console, `content text color test for console.${fName}`);
    });

## [m] setContentBackgroundColor#

### setContentBackgroundColor(color)#

`6.3.0`

- color { OmniColor } - 浮动窗口日志显示区域背景颜色
- returns { this }

设置控制台浮动窗口的日志显示区域背景颜色.

    /* 设置日志显示区域背景颜色为深蓝色. */
    console.setContentBackgroundColor('dark-blue').show();
    console.build({ contentBackgroundColor: 'dark-blue' }).show(); /* 效果同上. */

    /* 设置日志显示区域背景颜色为半透明深蓝色. */
    console.setContentBackgroundColor(Color('dark-blue').setAlpha(0.5)).show();
    console.setContentBackgroundColor('#8000008B').show(); /* 效果同上. */

    /* 透明度也可使用 setContentBackgroundAlpha 单独设置. */
    console
        .setContentBackgroundColor('dark-blue')
        .setContentBackgroundAlpha(0.5)
        .show();

## [m] setContentBackgroundAlpha#

### setContentBackgroundAlpha(alpha)#

`6.3.0`

- alpha { number } - 浮动窗口日志显示区域背景颜色透明度
- returns { this }

设置控制台浮动窗口的日志显示区域背景颜色透明度.

    /* 设置日志显示区域背景颜色为半透明. */
    console.setContentBackgroundAlpha(0.5).show();
    console.build({ contentBackgroundAlpha: 0.5 }).show(); /* 效果同上. */

    /* 设置日志显示区域背景颜色为半透明深蓝色. */
    console
        .setContentBackgroundColor('dark-blue')
        .setContentBackgroundAlpha(0.5)
        .show();

## [m] setTextSize#

### setTextSize(size)#

`6.3.0`

- size { number } - 浮动窗口标题及日志文本字体大小
- returns { this }

设置控制台浮动窗口的标题及日志文本字体大小, 单位 `sp`.

相当于 setTitleTextSize 和 setContentTextSize 的集成.

    /* 设置标题及日志文本字体大小为 18sp. */
    console.setTextSize(18).show();
    console.build({ textSize: 18 }).show(); /* 效果同上. */

## [m] setTextColor#

### setTextColor(color)#

`6.3.0`

- color OmniColor } - 浮动窗口标题及日志文本字体颜色
- returns { this }

设置控制台浮动窗口的标题及日志文本字体颜色.

对于日志文本, 不区分等级, 统一设置字体颜色.

相当于 setTitleTextColor 和 setContentTextColor 的集成.

    /* 所有标题及日志本文的颜色统一设置为浅蓝色. */
    console.setTextColor('light-blue').show();
    [ 'verbose', 'log', 'info', 'warn', 'error' ].forEach((fName) => {
        console[fName].call(console, ` text color test for console.${fName}`);
    });

## [m] setBackgroundColor#

### setBackgroundColor(color)#

`6.3.0`

- color { OmniColor } - 浮动窗口标题及日志显示区域背景颜色
- returns { this }

设置控制台浮动窗口的标题及日志显示区域背景颜色.

相当于 setTitleBackgroundColor 和 setContentBackgroundColor 的集成.

    /* 设置标题及日志显示区域背景颜色为浅黄色. */
    console.setBackgroundColor('light-yellow').show();
    console.build({ backgroundColor: 'light-yellow' }).show(); /* 效果同上. */

    /* 设置标题及日志显示区域背景颜色为半透明浅黄色. */
    console.setBackgroundColor(Color('light-yellow').setAlpha(0.5)).show();
    console.setBackgroundColor('#80FFFFE0').show(); /* 效果同上. */

    /* 透明度也可使用 backgroundAlpha 单独设置. */
    console
        .setBackgroundColor('light-yellow')
        .setBackgroundAlpha(0.5)
        .show();

## [m] setBackgroundAlpha#

### setBackgroundAlpha(alpha)#

`6.3.0`

- alpha { number } - 浮动窗口标题及日志显示区域背景颜色透明度
- returns { this }

设置控制台浮动窗口的标题及日志显示区域背景颜色透明度.

相当于 setTitleBackgroundAlpha 和 setContentBackgroundAlpha 的集成.

    /* 设置标题及日志显示区域背景颜色为半透明. */
    console.setBackgroundAlpha(0.5).show();
    console.build({ backgroundAlpha: 0.5 }).show(); /* 效果同上. */

    /* 设置标题及日志显示区域背景颜色为半透明浅黄色. */
    console
        .setBackgroundColor('light-yellow')
        .setBackgroundAlpha(0.5)
        .show();

## [m] verbose#

### verbose(data, ...args)#

`Global`

- data { string } - 可包含占位符的待格式化对象
- args { ...any[] } - 占位符替换参数
- returns { void }

输出参数内容到控制台.

主要用途: 测试消息 / 调试消息 / 重要性级别最低的消息

优先级: verbose < log < info < warn < error < assert

字体颜色:

- 浮动窗口 - [ ◑ ] - #E0E0E0
- Activity 活动窗口
  - 亮色主题 - [ ◑ ] - #DFC0C0C0
  - 暗色主题 - [ ◑ ] - #7F7F80

> 注: 此方法将自动添加末尾换行符.

## [m] log#

### log(data, ...args)#

`Global`

- data { string } - 可包含占位符的待格式化对象
- args { ...any[] } - 占位符替换参数
- returns { void }

输出参数内容到控制台.

主要用途: 普通消息

优先级: verbose < log < info < warn < error < assert

字体颜色:

- 浮动窗口 - [ ◑ ] - #FFFFFF
- Activity 活动窗口
  - 亮色主题 - [ ◑ ] - #CC000000
  - 暗色主题 - [ ◑ ] - #DFE0E0E0

> 注: 此方法将自动添加末尾换行符.

## [m] info#

### info(data, ...args)#

- data { string } - 可包含占位符的待格式化对象
- args { ...any[] } - 占位符替换参数
- returns { void }

输出参数内容到控制台.

主要用途: 重要消息 / 值得注意的消息

优先级: verbose < log < info < warn < error < assert

字体颜色:

- 浮动窗口 - [ ◑ ] - #DCEDC8
- Activity 活动窗口 - [ ◑ ] - #43A047

> 注: 此方法将自动添加末尾换行符.

## [m] warn#

### warn(data, ...args)#

`Global`

- data { string } - 可包含占位符的待格式化对象
- args { ...any[] } - 占位符替换参数
- returns { void }

输出参数内容到控制台.

主要用途: 警告消息 / 隐患消息

优先级: verbose < log < info < warn < error < assert

字体颜色:

- 浮动窗口 - [ ◑ ] - #B3E5FC
- Activity 活动窗口 - [ ◑ ] - #1976D2

> 注: 此方法将自动添加末尾换行符.

## [m] error#

### error(data, ...args)#

- data { string } - 可包含占位符的待格式化对象
- args { ...any[] } - 占位符替换参数
- returns { void }

输出参数内容到控制台.

主要用途: 错误消息 / 异常消息

优先级: verbose < log < info < warn < error < assert

字体颜色:

- 浮动窗口 - [ ◑ ] - #FFCDD2
- Activity 活动窗口 - [ ◑ ] - #C62828

> 注: 此方法将自动添加末尾换行符.

## [m] assert#

### assert(bool, message?)#

`6.3.0` `Overload [1-2]/4`

- bool { boolean } - 断言值
- [ message ] { string } - 断言失败时的消息
- returns { void }

断言 `bool` 参数为真.

断言失败时, 脚本停止运行, 输出失败消息及调用栈信息到控制台.

主要用途: 断言一个变量

优先级: verbose < log < info < warn < error < assert

字体颜色:

- 浮动窗口 - [ ◑ ] - #FCE4EC
- Activity 活动窗口 - [ ◑ ] - #E254FF

    console.assert(new Date().getSeconds() < 30, '断言失败, 当前时间秒数不小于 30');

> 注: 此方法将自动在控制台消息中添加末尾换行符.

### assert(func, message?)#

`6.3.0` `Overload [3-4]/4`

- func { () => boolean } - 断言值
- [ message ] { string } - 断言失败时的消息
- returns { void }

断言 `func` 参数的执行结果为真.

断言失败时, 脚本停止运行, 输出失败消息及调用栈信息到控制台.

主要用途: 断言一个函数

优先级: verbose < log < info < warn < error < assert

字体颜色:

- 浮动窗口 - [ ◑ ] - #FCE4EC
- Activity 活动窗口 - [ ◑ ] - #E254FF

    console.assert(function () {
        return new Date().getSeconds() < 30;
    }, '断言失败, 当前时间秒数不小于 30');

> 注: 此方法将自动在控制台消息中添加末尾换行符.

## [m] clear#

### clear()#

`6.3.0`

- returns { this }

清空控制台日志内容.

## [m] print#

### print(data, ...args)#

`Global` `DEPRECATED`

- data { string } - 可包含占位符的待格式化对象
- args { ...any[] } - 占位符替换参数
- returns { void }

等效于 console.log.

> 注: AutoJs6 的 `print` 方法在功能上更接近其他语言的 `printLn`, 而且在浏览器中, 全局方法 `print` 用于打印当前页面. 因此 `print` 全局方法被弃用, 不推荐使用.

## [m] printAllStackTrace#

### printAllStackTrace(e)#

`6.3.0`

- e { OmniThrowable } - 异常参数
- returns { void }

在控制台打印详细的栈追踪信息.

    try {
        null.toString()
    } catch (e) {
        /* 打印简单的错误消息. */
        /* 通常只有 1 行消息. */
        console.error(e.message);

        /* 使用 exit 方法抛出异常. */
        /* 通常有不到 10 行消息. */
        exit(e);

        /* 使用 console.printAllStackTrace 打印完整栈追踪信息. */
        /* 通常有几十行消息. */
        console.printAllStackTrace(e);
    }

## [m] trace#

### trace(message, level?)#

`6.3.0`

- message { string } - 追踪消息
- [ level = "debug" ] { `'verbose'` | `'debug'` | `'info'` | `'warn'` | `'error'` | number } - 消息输出等级
- returns { void }

输出当前位置调用栈的追踪信息到控制台.

`level` 参数接收由整形常量转化而来的字符串简化形式:

字符串 | 整形常量 | 简述
--- | --- | ---
'verbose' | Log.VERBOSE = 2 | 对应 console.verbose 输出等级.
'debug' | Log.DEBUG = 3 | 对应 console.log 输出等级.
'info' | Log.INFO = 4 | 对应 console.info 输出等级.
'warn' | Log.WARN = 5 | 对应 console.warn 输出等级.
'error' | Log.ERROR = 6 | 对应 console.error 输出等级.

    function printMessages() {
        console.trace('This is a "normal" message for test');
        console.trace('This is an "info" message for test', 'info');
        console.trace('This is a "warn" message for test', 'warn');
        console.trace('This is an "error" message for test', 'error');
        console.launch();
    }

    ({
        init() {
            this.intermediate();
        },
        intermediate() {
            printMessages();
        },
    }).init();

    // Error 等级的追踪信息输出样例:
    // 20:46:00.709/E: This is an "error" message for test
    //     at consoleTrace.js:5 (printMessages)
    //     at consoleTrace.js:14
    //     at consoleTrace.js:11
    //     at consoleTrace.js:9

## [m] time#

### time(label?)#

- [ label ] { string } - 计时标签
- returns { void }

启动计时器, 用以计算以 `label` 参数标记的时间间隔.

console.timeEnd 传入与 `label` 参数相同的值时, 计时器停止, 并输出时间间隔信息到控制台.

多次使用 time 方法传入相同 `label` 时, 将重置其关联的计时器.

    console.time('fruit');
    sleep(2e3);
    console.timeEnd('fruit');

## [m] timeEnd#

### timeEnd(label?)#

- [ label ] { string } - 计时标签
- returns { void }

与 console.time 配合使用, 用以计算以 `label` 参数标记的时间间隔.

`label` 关联的计时器不存在时, 打印 `NaNms`.

    console.time('fruit');
    sleep(2e3);
    console.timeEnd('fruit');

## [m] setGlobalLogConfig#

### setGlobalLogConfig(config)#

- config {{
  - [ file = `'android-log4j.log'` ]?: string - 待写入日志的文件路径, 支持绝对路径及相对路径
  - [ maxFileSize = `512 * 1024` ]?: number - 文件的分卷阈值容量 (单位为字节)
  - [ maxBackupSize = `5` ]?: number - 文件最大备份数量, 达到上限后将替换最旧文件
  - [ rootLevel = `'all'` ]?: `'all'` | `'off'` | `'debug'` | `'info'` | `'warn'` | `'error'` | `'fatal'` - 日志写入级别
  - [ filePattern = `'%d - [%p::%c::%C] - %m%n'` ]?: string - 日志写入格式, 参阅 PatternLayout
- }} - 日志输出至文件的配置选项
- returns { void }

设置将全局日志写入文件的配置选项.

该方法会影响所有脚本的日志记录.

    console.setGlobalLogConfig({
        file: `./log/${Date.now()}.log`,
        filePattern: '%d{yyyy-MM-dd/}%m%n',
        maxBackupSize: 16,
        maxFileSize: 384 << 10, /* 384 KB. */
    });

## [m] resetGlobalLogConfig#

### resetGlobalLogConfig()#

`6.3.1`

- returns { void }

重置全局日志写入配置.

此方法可重置 setGlobalLogConfig 的全部选项配置.

## [m] input#

### input(data, ...args)#

`ABANDONED`

- data { string } - 可包含占位符的待格式化对象
- args { ...any[] } - 占位符替换参数
- returns { void }

此方法已于 `6.3.1` 版本被废弃, 使用后将无任何效果.

## [m] rawInput#

### rawInput(data, ...args)#

`ABANDONED`

- data { string } - 可包含占位符的待格式化对象
- args { ...any[] } - 占位符替换参数
- returns { void }

此方法已于 `6.3.1` 版本被废弃, 使用后将无任何效果.

# Shell#

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

Marked by SuperMonster003 on Oct 22, 2022.

shell即Unix Shell, 在类Unix系统提供与操作系统交互的一系列命令.

很多程序可以用来执行shell命令, 例如终端模拟器.

在Auto.js大致等同于用adb执行命令"adb shell". 其实现包括两种方式：

- 通过`java.lang.Runtime.exec`执行(shell, Tap, Home等函数)
- 通过内嵌终端模拟器执行(RootAutomator, Shell等对象)

# shell函数#

## 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#

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命令.

## am命令#

am命令即Activity Manager命令, 用于管理应用程序活动、服务等.

以下命令均以"am "开头, 例如`shell('am start -p com.tencent.mm');`(启动微信)

### start [options] intent#

启动 intent 指定的 Activity(应用程序活动).
请参阅 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 参数的规范.
选项包括：

- --user user_id | current：指定要作为哪个用户运行；如果未指定, 则作为当前用户运行.

### force-stop package#

强行停止与 package（应用包名）关联的所有应用.

### kill [options] package#

终止与 package（应用包名）关联的所有进程. 此命令仅终止可安全终止且不会影响用户体验的进程.
选项包括：

- --user user_id | all | current：指定将终止其进程的用户；如果未指定, 则终止所有用户的进程.

### kill-all#

终止所有后台进程.

### broadcast [options] intent#

发出广播 intent. 请参阅 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 参数的规范.

### 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);

# 多媒体 (Media)#

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

Marked by SuperMonster003 on Oct 22, 2022.

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 }

返回当前音乐的播放进度(已经播放的时间), 单位毫秒.

# 传感器 (Sensors)#

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

Marked by SuperMonster003 on Oct 22, 2022.

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 传感器事件, 用于获取传感器数据变化时的所有信息
  - `ax` { number } x轴上的加速度, 单位m/s^2
  - `ay` { number } y轴上的加速度, 单位m/s^2
  - `az` { number } z轴上的加速度, 单位m/s^2 这里的x轴, y轴, z轴所属的坐标系统如下图(其中z轴垂直于设备屏幕表面):

  !

- `orientation` 方向传感器, 参数`(event, azimuth, pitch, roll)`:

  - `event` SensorEvent 传感器事件, 用于获取传感器数据变化时的所有信息
  - `azimuth` { number } 方位角, 从地磁指北方向线起, 依顺时针方向到y轴之间的水平夹角, 单位角度, 范围0~359
  - `pitch` { number } 绕x轴旋转的角度, 当设备水平放置时该值为0, 当设备顶部翘起时该值为正数, 当设备尾部翘起时该值为负数, 单位角度, 范围-180~180
  - `roll` { number } 绕y轴顺时针旋转的角度, 单位角度, 范围-90~90

- `gyroscope` 陀螺仪传感器, 参数`(event, wx, wy, wz)`:

  - `event` SensorEvent 传感器事件, 用于获取传感器数据变化时的所有信息
  - `wx` { number } 绕x轴的角速度, 单位弧度/s
  - `wy` { number } 绕y轴的角速度, 单位弧度/s
  - `wz` { number } 绕z轴的角速度, 单位弧度/s

- `magnetic_field` 磁场传感器, 参数`(event, bx, by, bz)`:

  - `event` SensorEvent 传感器事件, 用于获取传感器数据变化时的所有信息
  - `bx` { number } x轴上的磁场强度, 单位uT
  - `by` { number } y轴上的磁场强度, 单位uT
  - `bz` { number } z轴上的磁场强度, 单位uT

- `gravity` 重力传感器, 参数`(event, gx, gy, gz)`:

  - `event` 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 传感器事件, 用于获取传感器数据变化时的所有信息
  - `ax` { number } x轴上的线性加速度, 单位m/s^2
  - `ay` { number } y轴上的线性加速度, 单位m/s^2
  - `az` { number } z轴上的线性加速度, 单位m/s^2

- `ambient_temperature` 环境温度传感器, 大部分设备并不支持, 参数`(event, t)`:

  - `event` SensorEvent 传感器事件, 用于获取传感器数据变化时的所有信息
  - `t` { number } 环境温度, 单位摄氏度.

- `light` 光线传感器, 参数`(event, light)`:

  - `event` SensorEvent 传感器事件, 用于获取传感器数据变化时的所有信息
  - `light` { number } 环境光强度, 单位lux

- `pressure` 压力传感器, 参数`(event, p)`:

  - `event` SensorEvent 传感器事件, 用于获取传感器数据变化时的所有信息
  - `p` { number } 大气压, 单位hPa

- `proximity` 距离传感器, 参数`(event, distance)`:

  - `event` SensorEvent 传感器事件, 用于获取传感器数据变化时的所有信息
  - `distance` { number } 一般指设备前置摄像头旁边的距离传感器到前方障碍物的距离, 并且很多设备上这个值只有两种情况：当障碍物较近时该值为0, 当障碍物较远或在范围内没有障碍物时该值为5

- `relative_humidity` 湿度传感器, 大部分设备并不支持, 参数`(event, rh)`:

  - `event` 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

注册一个传感器监听并返回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.ignoresUnsupportedSensor = true;
    //无需null判断
    sensors.register("gravity").on("change", (gx, gy, gz)=>{
        log("重力加速度: %d, %d, %d", gx, gy, gz);
    });

更多信息, 参见SensorEventEmitter和sensors.ignoresUnsupportedSensor.

## sensors.unregister(emitter)#

- `emiiter` 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 高精度

当传感器精度改变时会触发的事件. 比较少用.

# 记录器 (Recorder)#

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

Marked by SuperMonster003 on Oct 22, 2022.

记录器用于计时.

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

recorder

## [@] recorder#

### recorder()#

### recorder(key)#

### recorder(key, timestamp)#

### recorder(func)#

### recorder(func, thisType)#

## [m] save#

### save()#

### save(key)#

### save(key, timestamp)#

## [m] load#

### load()#

### load(key)#

### load(key, timestamp)#

## [m] isLessThan#

### isLessThan(key, compare)#

## [m] isGreaterThan#

### isGreaterThan(key, compare)#

## [m] has#

### has(key)#

## [m] remove#

### remove(key)#

## [m] clear#

### clear()#

# 定时器 (Timers)#

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

Marked by SuperMonster003 on Oct 22, 2022.

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 对象.

# 线程 (Threads)#

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

Marked by SuperMonster003 on Oct 22, 2022.

threads模块提供了多线程支持, 可以启动新线程来运行脚本.

脚本主线程会等待所有子线程执行完成后才停止执行, 因此如果子线程中有死循环, 请在必要的时候调用`exit()`来直接停止脚本或`threads.shutDownAll()`来停止所有子线程.

通过`threads.start()`启动的所有线程会在脚本被强制停止时自动停止.

由于JavaScript自身没有多线程的支持, 因此您可能会遇到意料之外的问题.

## threads.start(action)#

- `action` { Function } 要在新线程执行的函数
- 返回 Thread

启动一个新线程并执行action.

例如:

    threads.start(function(){
        //在新线程执行的代码
        while(true){
            log("子线程");
        }
    });
    while(true){
        log("脚本主线程");
    }

通过该函数返回的Thread对象可以获取该线程的状态, 控制该线程的运行中. 例如:

    var thread = threads.start(function(){
        while(true){
            log("子线程");
        }
    });
    //停止线程执行
    thread.interrupt();

更多信息参见Thread.

## threads.shutDownAll()#

停止所有通过`threads.start()`启动的子线程.

## threads.currentThread()#

- 返回 Thread

返回当前线程.

## threads.disposable()#

- 返回 Disposable

新建一个Disposable对象, 用于等待另一个线程的某个一次性结果. 更多信息参见线程通信以及Disposable.

## threads.atomic([initialValue])#

- `initialValue` { number } 初始整数值, 默认为0
- 返回AtomicLong

新建一个整数原子变量. 更多信息参见线程安全以及AtomicLong.

## threads.lock()#

- 返回ReentrantLock

新建一个可重入锁. 更多信息参见线程安全以及ReentrantLock.

# 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().

区别在于, 该定时器会在该线程执行. 如果当前线程仍未开始执行或已经执行结束, 则抛出`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().

区别在于, 该定时器会在该线程执行. 如果当前线程仍未开始执行或已经执行结束, 则抛出`IllegalStateException`.

## Thread.setImmediate(callback[, ...args])#

参见timers.setImmediate().

区别在于, 该定时器会在该线程执行. 如果当前线程仍未开始执行或已经执行结束, 则抛出`IllegalStateException`.

## Thread.clearInterval(id)#

参见timers.clearInterval().

区别在于, 该定时器会在该线程执行. 如果当前线程仍未开始执行或已经执行结束, 则抛出`IllegalStateException`.

## Thread.clearTimeout(id)#

参见timers.clearTimeout().

区别在于, 该定时器会在该线程执行. 如果当前线程仍未开始执行或已经执行结束, 则抛出`IllegalStateException`.

## Thread.clearImmediate(id)#

参见timers.clearImmediate().

区别在于, 该定时器会在该线程执行. 如果当前线程仍未开始执行或已经执行结束, 则抛出`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`.

# 协程 (Continuation)#

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

Marked by SuperMonster003 on Oct 22, 2022.

# 事件监听 (Events)#

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

Marked by SuperMonster003 on Oct 22, 2022.

events模块提供了监听手机通知、按键、触摸的接口. 您可以用他配合自动操作函数完成自动化工作.

events本身是一个EventEmiiter, 但内置了一些事件、包括按键事件、通知事件、Toast事件等.

需要注意的是, 事件的处理是单线程的, 并且仍然在原线程执行, 如果脚本主体或者其他事件处理中有耗时操作、轮询等, 则事件将无法得到及时处理（会进入事件队列等待脚本主体或其他事件处理完成才执行）. 例如:

    auto();
    events.observeNotification();
    events.on('toast', function(t){
        //这段代码将得不到执行
        log(t);
    });
    while(true){
        //死循环
    }

## events.emitter()#

返回一个新的EventEmitter. 这个EventEmitter没有内置任何事件.

## events.observeKey()#

启用按键监听, 例如音量键、Home键. 按键监听使用无障碍服务实现, 如果无障碍服务未启用会抛出异常并提示开启.

只有这个函数成功执行后, `onKeyDown`, `onKeyUp`等按键事件的监听才有效.

该函数在安卓4.3以上才能使用.

## events.onKeyDown(keyName, listener)#

- `keyName` { string } 要监听的按键名称
- `listener` { Function } 按键监听器. 参数为一个KeyEvent.

注册一个按键监听函数, 当有keyName对应的按键被按下会调用该函数. 可用的按键名称参见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.

注册一个按键监听函数, 当有keyName对应的按键弹起会调用该函数. 可用的按键名称参见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

注册一个按键监听函数, 当有keyName对应的按键被按下时会调用该函数, 之后会注销该按键监听器.

也就是listener只有在onceKeyDown调用后的第一次按键事件被调用一次.

## events.onceKeyUp(keyName, listener)#

- `keyName` { string } 要监听的按键名称
- `listener` { Function } 按键监听器. 参数为一个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的函数

注册一个触摸监听函数. 相当于`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.observeNotification();
    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 通知对象

当有应用发出通知时会触发该事件, 参数为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#

## 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#

按键事件中所有可用的按键名称为：

- `volume_up` 音量上键
- `volume_down` 音量下键
- `home` 主屏幕键
- `back` 返回键
- `menu` 菜单键

# EventEmitter#

## 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);

# 对话框 (Dialogs)#

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

Marked by SuperMonster003 on Oct 22, 2022.

dialogs 模块提供了简单的对话框支持, 可以通过对话框和用户进行交互. 最简单的例子如下：

    alert("您好");

这段代码会弹出一个消息提示框显示"您好", 并在用户点击"确定"后继续运行. 稍微复杂一点的例子如下：

    var clear = confirm("要清除所有缓存吗?");
    if(clear){
        alert("清除成功!");
    }

`confirm()`会弹出一个对话框并让用户选择"是"或"否", 如果选择"是"则返回true.

需要特别注意的是, 对话框在ui模式下不能像通常那样使用, 应该使用回调函数或者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`

# 悬浮窗 (Floaty)#

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

Marked by SuperMonster003 on Oct 22, 2022.

floaty模块提供了悬浮窗的相关函数, 可以在屏幕上显示自定义悬浮窗, 控制悬浮窗大小、位置等.

悬浮窗在脚本停止运行时会自动关闭, 因此, 要保持悬浮窗不被关闭, 可以用一个空的setInterval来实现, 例如：

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

## floaty.window(layout)#

- `layout` { xml } | { View } 悬浮窗界面的XML或者View

指定悬浮窗的布局, 创建并显示一个悬浮窗, 返回一个`FloatyWindow`对象.

该悬浮窗自带关闭、调整大小、调整位置按键, 可根据需要调用`setAdjustEnabled()`函数来显示或隐藏.

其中layout参数可以是xml布局或者一个View, 更多信息参见ui模块的说明.

例子：

    var w = floaty.window(
        <frame gravity="center">
            <text id="text">悬浮文字</text>
        </frame>
    );
    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(
        <frame gravity="center">
            <text id="text">悬浮文字</text>
        </frame>
    );

    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.getWidth()#

返回悬浮窗宽度.

## 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(
        <frame gravity="center" bg="#44ffcc00"/>
    );

    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(
        <frame gravity="center" bg="#77ff0000">
            <text id="text">悬浮文字</text>
        </frame>
    );

    w.setSize(-1, -1);

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

## window.getWidth()#

返回悬浮窗宽度.

## window.getHeight()#

返回悬浮窗高度.

## window.close()#

关闭悬浮窗. 如果悬浮窗已经是关闭状态, 则此函数将不执行任何操作.

被关闭后的悬浮窗不能再显示.

## window.exitOnClose()#

使悬浮窗被关闭时自动结束脚本运行.

# 画布 (Canvas)#

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

Marked by SuperMonster003 on Oct 22, 2022.

canvas提供了使用画布进行2D画图的支持, 可用于简单的小游戏开发或者图片编辑. 使用canvas可以轻松地在一张图片或一个界面上绘制各种线与图形.

canvas的坐标系为平面直角坐标系, 以控件左上角为原点, 控件上边沿为x轴正方向, 控件左边沿为y轴正方向. 例如分辨率为1920*1080的屏幕上, canvas控件覆盖全屏, 画一条从屏幕左上角到屏幕右下角的线段为:

    canvas.drawLine(0, 0, 1080, 1920, paint);

canvas的绘制依赖于画笔Paint, 通过设置画笔的粗细、颜色、填充等可以改变绘制出来的图形. 例如绘制一个红色实心正方形为：

    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.getWidth()#

- 返回 { number }

返回画布当前图层的宽度.

## canvas.getHeight()#

- 返回 { number }

返回画布当前图层的高度.

## canvas.drawRGB(r, int g, int b)#

- `r` { number } 红色通道值
- `g` { number } 绿色通道值
- `b` { number } 蓝色通道值

将整个可绘制区域填充为r、g、b指定的颜色. 相当于 `canvas.drawColor(colors.rgb(r, g, b))`.

## canvas.drawARGB(a, r, g, b)#

- `a` { number } 透明通道值
- `r` { number } 红色通道值
- `g` { number } 绿色通道值
- `b` { number } 蓝色通道值

将整个可绘制区域填充为a、r、g、b指定的颜色. 相当于 `canvas.drawColor(colors.argb(a, r, g, b))`.

## canvas.drawColor(color)#

- `color` { number } 颜色值

将整个可绘制区域填充为color指定的颜色.

## canvas.drawColor(color, mode)#

- `color` { number } 颜色值
- `mode` { PorterDuff.Mode } Porter-Duff操作

将整个可绘制区域填充为color指定的颜色.

## canvas.drawPaint(paint)#

- `paint` { Paint } 画笔

将整个可绘制区域用paint指定的画笔填充. 相当于绘制一个无限大的矩形, 但是更快. 通过该方法可以绘制一个指定的着色器的图案.

## canvas.drawPoint(x, y, paint)#

- `x` { number } x坐标
- `y` { number } y坐标
- `paint` { Paint } 画笔

在可绘制区域绘制由坐标(x, y)指定的点. 点的形状由画笔的线帽决定（参见paint.setStrokeCap(cap)）. 点的大小由画笔的宽度决定（参见paint.setStrokeWidth(width)）.

> 如果画笔宽度为0, 则也会绘制1个像素至4个（若抗锯齿启用）.

相当于 `canvas.drawPoints([x, y], paint)`.

## canvas.drawPoints(pts, paint)#

- `pts` { Array } 点坐标数组 [x0, y0, x1, y1, x2, y2, ...]
- `paint` { Paint } 画笔

在可绘制区域绘制由坐标数组指定的多个点.

## canvas.drawLine(startX, startY, stopX, stopY, paint)#

- `startX` { number } 起点x坐标
- `startY` { number } 起点y坐标
- `endX` { number } 终点x坐标
- `endY` { number } 终点y坐标
- `paint` { Paint } 画笔

在可绘制区域绘制由起点坐标(startX, startY)和终点坐标(endX, endY)指定的线. 绘制时会忽略画笔的样式(Style). 也就是说, 即使样式设为“仅填充(FILL)”也会绘制. 退化为点的线（长度为0）不会被绘制.

## canvas.drawRect(r, paint)#

- `r` { Rect } 矩形边界
- `paint` { Paint } 画笔

在可绘制区域绘制由矩形边界r指定的矩形. 绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形.

## canvas.drawRect(left, top, right, bottom, android.graphics.Paint paint)#

- `left` { number } 矩形左边界x坐标
- `top` { number } 矩形上边界y坐标
- `right` { number } 矩形右边界x坐标
- `bottom` { number } 矩形下边界y坐标
- `paint` { Paint } 画笔

在可绘制区域绘制由矩形边界(left, top, right, bottom)指定的矩形. 绘制时画笔的样式(Style)决定了是否绘制矩形界线和填充矩形.

## canvas.drawOval(android.graphics.RectF oval, android.graphics.Paint paint)#

## canvas.drawOval(float left, float top, float right, float bottom, android.graphics.Paint paint)#

## canvas.drawCircle(float cx, float cy, float radius, android.graphics.Paint paint)#

## canvas.drawArc(android.graphics.RectF oval, float startAngle, float sweepAngle, boolean useCenter, android.graphics.Paint paint)#

## canvas.drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, android.graphics.Paint paint)#

## canvas.drawRoundRect(android.graphics.RectF rect, float rx, float ry, android.graphics.Paint paint)#

## canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, android.graphics.Paint paint)#

## canvas.drawPath(android.graphics.Path path, android.graphics.Paint paint)#

## canvas.drawBitmap(android.graphics.Bitmap bitmap, float left, float top, android.graphics.Paint paint)#

## canvas.drawText(java.lang.String text, float x, float y, android.graphics.Paint paint)#

## canvas.drawTextOnPath(java.lang.String text, android.graphics.Path path, float hOffset, float vOffset, android.graphics.Paint paint)#

## canvas.translate(dx, dy)#

- `dx` { number } 向x轴正方向平移的距离, 负数表示反方向平移
- `dy` { number } 向y轴正方向平移的距离, 负数表示反方向平移

平移指定距离.

## canvas.scale(sx, sy)#

- `sx` { number } 向x轴正方向平移的距离, 负数表示反方向平移
- `sy` { number } 向y轴正方向平移的距离, 负数表示反方向平移

以原点为中心, 将坐标系平移缩放指定倍数.

## canvas.scale(float sx, float sy, float px, float py)#

## canvas.rotate(float degrees)#

## canvas.rotate(float degrees, float px, float py)#

## canvas.skew(float sx, float sy)#

# 画笔#

# 变换矩阵#

# 路径#

# Porter-Duff操作#

# 着色器#

# 遮罩过滤器#

# 颜色过滤器#

# 路径特效#

# 区域#

# 用户界面 (UI)#

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

Marked by SuperMonster003 on Oct 22, 2022.

ui模块提供了编写用户界面的支持.

    给Android开发者或者高阶用户的提醒, Auto.js的UI系统来自于Android, 所有属性和方法都能在Android源码中找到. 如果某些代码或属性没有出现在Auto.js的文档中, 可以参考Android的文档.
    View: https://developer.android.google.cn/reference/android/view/View?hl=cn
    Widget: https://developer.android.google.cn/reference/android/widget/package-summary?hl=cn

带有ui的脚本的的最前面必须使用`"ui";`指定ui模式, 否则脚本将不会以ui模式运行. 正确示范:s

    "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(
        <vertical>
            <button text="第一个按钮"/>
            <button text="第二个按钮"/>
        </vertical>
    );

在这个例子中, 第3~6行的部分就是xml, 指定了界面的具体内容. 代码的第3行的标签`<vertical> ... </vertical>`表示垂直布局, 布局的标签通常以`<...>`开始, 以`</...>`结束, 两个标签之间的内容就是布局里面的内容, 例如`<frame> ... </frame>`. 在这个例子中第4, 5行的内容就是垂直布局(vertical)里面的内容. 代码的第4行是一个按钮控件(button), 控件的标签通常以`<...`开始, 以`/>`结束, 他们之间是控件的具体属性, 例如`<text ... />`. 在这个例子中`text="第一个按钮"`的部分就是按钮控件(button)的属性, 这个属性指定了这个按钮控件的文本内容(text)为"第一个按钮".

代码的第5行和第4行一样, 也是一个按钮控件, 只不过他的文本内容为"第二个按钮". 这两个控件在垂直布局中, 因此会纵向排列, 效果如图：

如果我们把这个例子的垂直布局(vertical)改成水平布局(horizontal), 也即：

    "ui";
    ui.layout(
        <horizontal>
            <button text="第一个按钮"/>
            <button text="第二个按钮"/>
        </horizontal>
    );

则这两个按钮会横向排列, 效果如图：

一个控件可以指定多个属性(甚至可以不指定任何属性), 用空格隔开即可；布局同样也可以指定属性, 例如:

    "ui";
    ui.layout(
        <vertical bg="#ff0000">
            <button text="第一个按钮" textSize="20sp"/>
            <button text="第二个按钮"/>
        </vertical>
    );

第三行`bg="#ff0000"`指定了垂直布局的背景色(bg)为"#ff0000", 这是一个RGB颜色, 表示红色(有关RGB的相关知识参见RGB颜色对照表). 第四行的`textSize="20sp"`则指定了按钮控件的字体大小(textSize)为"20sp", sp是一个字体单位, 暂时不用深入理会. 上述代码的效果如图：

一个界面便由一些布局和控件组成. 为了便于文档阅读, 我们再说明一下以下术语：

- 子视图, 子控件: 布局里面的控件是这个布局的子控件/子视图. 实际上布局里面不仅仅只能有控件, 还可以是嵌套的布局. 因此用子视图(Child View)更准确一些. 在上面的例子中, 按钮便是垂直布局的子控件.
- 父视图, 父布局：直接包含一个控件的布局是这个控件的父布局/父视图(Parent View). 在上面的例子中, 垂直布局便是按钮的父布局.

# 视图: View#

控件和布局都属于视图(View). 在这个章节中将介绍所有控件和布局的共有的属性和函数. 例如属性背景, 宽高等(所有控件和布局都能设置背景和宽高), 函数`click()`设置视图(View)被点击时执行的动作.

## attr(name, value)#

- `name` { string } 属性名称
- `value` { string } 属性的值

设置属性的值. 属性指定是View在xml中的属性. 例如可以通过语句`attr("text", "文本")`来设置文本控件的文本值.

    "ui";

    $ui.layout(
        <frame>
            <text id="example" text="Hello"/>
        </frame>
    );

    // 5秒后执行
    $ui.post(() => {
        // 修改文本
        $ui.example.attr("text", "Hello, Auto.js UI");
        // 修改背景
        $ui.example.attr("bg", "#ff00ff");
        // 修改高度
        $ui.example.attr("h", "500dp");
    }, 5000);

注意：并不是所有属性都能在js代码设置, 有一些属性只能在布局创建时设置, 例如style属性；还有一些属性虽然能在代码中设置, 但是还没支持；对于这些情况, 在Auto.js Pro 8.1.0+会抛出异常, 其他版本则不会抛出异常.

## attr(name)#

- `name` { string } 属性名称
- 返回 { string }

获取属性的值.

    "ui";

    $ui.layout(
        <frame>
            <text id="example" text="1"/>
        </frame>
    );

    plusOne();

    function plusOne() {
        // 获取文本
        let text = $ui.example.attr("text");
        // 解析为数字
        let num = parseInt(text);
        // 数字加1
        num++;
        // 设置文本
        $ui.example.attr("text", String(num));
        // 1秒后继续
        $ui.post(plusOne, 1000);
    }

## w#

View的宽度, 是属性`width`的缩写形式. 可以设置的值为`*`, `auto`和具体数值. 其中`*`表示宽度尽量填满父布局, 而`auto`表示宽度将根据View的内容自动调整(自适应宽度). 例如：

    "ui";
    ui.layout(
        <horizontal>
            <button w="auto" text="自适应宽度"/>
            <button w="*" text="填满父布局"/>
        </horizontal>
    );

在这个例子中, 第一个按钮为自适应宽度, 第二个按钮为填满父布局, 显示效果为：

如果不设置该属性, 则不同的控件和布局有不同的默认宽度, 大多数为`auto`.

宽度属性也可以指定一个具体数值. 例如`w="20"`, `w="20px"`等. 不加单位的情况下默认单位为dp, 其他单位包括px(像素), mm(毫米), in(英寸). 有关尺寸单位的更多内容, 参见尺寸的单位: Dimension.

    "ui";
    ui.layout(
        <horizontal>
            <button w="200" text="宽度200dp"/>
            <button w="100" text="宽度100dp"/>
        </horizontal>
    );

## h#

View的高度, 是属性`height`的缩写形式. 可以设置的值为`*`, `auto`和具体数值. 其中`*`表示宽度尽量填满父布局, 而`auto`表示宽度将根据View的内容自动调整(自适应宽度).

如果不设置该属性, 则不同的控件和布局有不同的默认高度, 大多数为`auto`.

宽度属性也可以指定一个具体数值. 例如`h="20"`, `h="20px"`等. 不加单位的情况下默认单位为dp, 其他单位包括px(像素), mm(毫米), in(英寸). 有关尺寸单位的更多内容, 参见尺寸的单位: Dimension.

## id#

View的id, 用来区分一个界面下的不同控件和布局, 一个界面的id在同一个界面下通常是唯一的, 也就是一般不存在两个View有相同的id. id属性也是连接xml布局和JavaScript代码的桥梁, 在代码中可以通过一个View的id来获取到这个View, 并对他进行操作(设置点击动作、设置属性、获取属性等). 例如：

    "ui";
    ui.layout(
        <frame>
            <button id="ok" text="确定"/>
        </frame>
    );
    //通过ui.ok获取到按钮控件
    toast(ui.ok.getText());

这个例子中有一个按钮控件"确定", id属性为"ok", 那么我们可以在代码中使用`ui.ok`来获取他, 再通过`getText()`函数获取到这个按钮控件的文本内容. 另外这个例子中使用帧布局(frame)是因为, 我们只有一个控件, 因此用于最简单的布局帧布局.

## gravity#

View的"重力". 用于决定View的内容相对于View的位置, 可以设置的值为:

- `left` 靠左
- `right` 靠右
- `top` 靠顶部
- `bottom` 靠底部
- `center` 居中
- `center_vertical` 垂直居中
- `center_horizontal` 水平居中

例如对于一个按钮控件, `gravity="right"`会使其中的文本内容靠右显示. 例如：

    "ui";
    ui.layout(
        <frame>
            <button gravity="right" w="*" h="auto" text="靠右的文字"/>
        </frame>
    );

显示效果为:

这些属性是可以组合的, 例如`gravity="right|bottom"`的View他的内容会在右下角.

## layout_gravity#

View在布局中的"重力", 用于决定View本身在他的父布局的位置, 可以设置的值和gravity属性相同. 注意把这个属性和gravity属性区分开来.

    "ui";
    ui.layout(
        <frame w="*" h="*">
            <button layout_gravity="center" w="auto" h="auto" text="居中的按钮"/>
            <button layout_gravity="right|bottom" w="auto" h="auto" text="右下角的按钮"/>
        </frame>
    );

在这个例子中, 我们让帧布局(frame)的大小占满整个屏幕, 通过给第一个按钮设置属性`layout_gravity="center"`来使得按钮在帧布局中居中, 通过给第二个按钮设置属性`layout_gravity="right|bottom"`使得他在帧布局中位于右下角. 效果如图：

要注意的是, layout_gravity的属性不一定总是生效的, 具体取决于布局的类别. 例如不能让水平布局中的第一个子控件靠底部显示(否则和水平布局本身相违背).

## margin#

margin为View和其他View的间距, 即外边距. margin属性包括四个值:

- `marginLeft` 左外边距
- `marginRight` 右外边距
- `marginTop` 上外边距
- `marginBottom` 下外边距

而margin属性本身的值可以有三种格式:

- `margin="marginAll"` 指定各个外边距都是该值. 例如`margin="10"`表示左右上下边距都是10dp.
- `margin="marginLeft marginTop marginRight marginBottom"` 分别指定各个外边距. 例如`margin="10 20 30 40"`表示左边距为10dp, 上边距为20dp, 右边距为30dp, 下边距为40dp
- `margin="marginHorizontal marginVertical"` 指定水平外边距和垂直外边距. 例如`margin="10 20"`表示左右边距为10dp, 上下边距为20dp.

用一个例子来具体理解外边距的含义：

    "ui";
    ui.layout(
        <horizontal>
            <button margin="30" text="距离四周30"/>
            <button text="普通的按钮"/>
        </horizontal>
    );

第一个按钮的margin属性指定了他的边距为30dp, 也就是他与水平布局以及第二个按钮的间距都是30dp, 其显示效果如图:

如果把`margin="30"`改成`margin="10 40"`那么第一个按钮的左右间距为10dp, 上下间距为40dp, 效果如图:

有关margin属性的单位, 参见尺寸的单位: Dimension.

## marginLeft#

View的左外边距. 如果该属性和margin属性指定的值冲突, 则在后面的属性生效, 前面的属性无效, 例如`margin="20" marginLeft="10"`的左外边距为10dp, 其他外边距为20dp.

    "ui";
    ui.layout(
        <horizontal>
            <button marginLeft="50" text="距离左边50"/>
            <button text="普通的按钮"/>
        </horizontal>
    );

第一个按钮指定了左外边距为50dp, 则他和他的父布局水平布局(horizontal)的左边的间距为50dp, 效果如图：

## marginRight#

View的右外边距. 如果该属性和margin属性指定的值冲突, 则在后面的属性生效, 前面的属性无效.

## marginTop#

View的上外边距. 如果该属性和margin属性指定的值冲突, 则在后面的属性生效, 前面的属性无效.

## marginBottom#

View的下外边距. 如果该属性和margin属性指定的值冲突, 则在后面的属性生效, 前面的属性无效.

## padding#

View和他的自身内容的间距, 也就是内边距. 注意和margin属性区分开来, margin属性是View之间的间距, 而padding是View和他自身内容的间距. 举个例子, 一个文本控件的padding也即文本控件的边缘和他的文本内容的间距, paddingLeft即文本控件的左边和他的文本内容的间距.

paddding属性的值同样有三种格式：

- `padding="paddingAll"` 指定各个内边距都是该值. 例如`padding="10"`表示左右上下内边距都是10dp.
- `padding="paddingLeft paddingTop paddingRight paddingBottom"` 分别指定各个内边距. 例如`padding="10 20 30 40"`表示左内边距为10dp, 上内边距为20dp, 右内边距为30dp, 下内边距为40dp
- `padding="paddingHorizontal paddingVertical"` 指定水平内边距和垂直内边距. 例如`padding="10 20"`表示左右内边距为10dp, 上下内边距为20dp.

用一个例子来具体理解内边距的含义：

    "ui";
    ui.layout(
        <frame w="*" h="*" gravity="center">
            <text padding="10 20 30 40" bg="#ff0000" w="auto" h="auto" text="HelloWorld"/>
        </frame>
    );

这个例子是一个居中的按钮(通过父布局的`gravity="center"`属性设置), 背景色为红色(`bg="#ff0000"`), 文本内容为"HelloWorld", 左边距为10dp, 上边距为20dp, 下边距为30dp, 右边距为40dp, 其显示效果如图：

## paddingLeft#

View的左内边距. 如果该属性和padding属性指定的值冲突, 则在后面的属性生效, 前面的属性无效.

## paddingRight#

View的右内边距. 如果该属性和padding属性指定的值冲突, 则在后面的属性生效, 前面的属性无效.

## paddingTop#

View的上内边距. 如果该属性和padding属性指定的值冲突, 则在后面的属性生效, 前面的属性无效.

## paddingBottom#

View的下内边距. 如果该属性和padding属性指定的值冲突, 则在后面的属性生效, 前面的属性无效.

## bg#

View的背景. 其值可以是一个链接或路径指向的图片, 或者RGB格式的颜色, 或者其他背景. 具体参见Drawables.

例如, `bg="#00ff00"`设置背景为绿色, `bg="file:///sdcard/1.png"`设置背景为图片"1.png", `bg="?attr/selectableItemBackground"`设置背景为点击时出现的波纹效果(可能需要同时设置`clickable="true"`才生效).

## alpha#

View的透明度, 其值是一个0~1之间的小数, 0表示完全透明, 1表示完全不透明. 例如`alpha="0.5"`表示半透明.

## foreground#

View的前景. 前景即在一个View的内容上显示的内容, 可能会覆盖掉View本身的内容. 其值和属性bg的值类似.

## minHeight#

View的最小高度. 该值不总是生效的, 取决于其父布局是否有足够的空间容纳.

例：`<text height="auto" minHeight="50"/>`

有关该属性的单位, 参见尺寸的单位: Dimension.

## minWidth#

View的最小宽度. 该值不总是生效的, 取决于其父布局是否有足够的空间容纳.

例：`<input width="auto" minWidth="50"/>`

有关该属性的单位, 参见尺寸的单位: Dimension.

## visibility#

View的可见性, 该属性可以决定View是否显示出来. 其值可以为：

- `gone` 不可见.
- `visible` 可见. 默认情况下View都是可见的.
- `invisible` 不可见, 但仍然占用位置.

## rotation#

View的旋转角度. 通过该属性可以让这个View顺时针旋转一定的角度. 例如`rotation="90"`可以让他顺时针旋转90度.

如果要设置旋转中心, 可以通过`transformPivotX`, `transformPivotY`属性设置. 默认的旋转中心为View的中心.

## transformPivotX#

View的变换中心坐标x. 用于View的旋转、放缩等变换的中心坐标. 例如`transformPivotX="10"`.

该坐标的坐标系以View的左上角为原点. 也就是x值为变换中心到View的左边的距离.

有关该属性的单位, 参见尺寸的单位: Dimension.

## transformPivotY#

View的变换中心坐标y. 用于View的旋转、放缩等变换的中心坐标. 例如`transformPivotY="10"`.

该坐标的坐标系以View的左上角为原点. 也就是y值为变换中心到View的上边的距离.

有关该属性的单位, 参见尺寸的单位: Dimension.

## style#

设置View的样式. 不同控件有不同的可选的内置样式. 具体参见各个控件的说明.

需要注意的是, style属性只支持安卓5.1及其以上.

# 文本控件: text#

文本控件用于显示文本, 可以控制文本的字体大小, 字体颜色, 字体等.

以下介绍该控件的主要属性和方法, 如果要查看他的所有属性和方法, 请阅读TextView.

## text#

设置文本的内容. 例如`text="一段文本"`.

## textColor#

设置字体的颜色, 可以是RGB格式的颜色(例如#ff00ff), 或者颜色名称(例如red, green等), 具体参见颜色.

示例, 红色字体：`<text text="红色字体" textColor="red"/>`

## textSize#

设置字体的大小, 单位一般是sp. 按照Material Design的规范, 正文字体大小为14sp, 标题字体大小为18sp, 次标题为16sp.

示例, 超大字体: `<text text="超大字体" textSize="40sp"/>`

## textStyle#

设置字体的样式, 比如斜体、粗体等. 可选的值为：

- bold 加粗字体
- italic 斜体
- normal 正常字体

可以用或("|")把他们组合起来, 比如粗斜体为"bold|italic".

例如, 粗体：`

## lines#

设置文本控件的行数. 即使文本内容没有达到设置的行数, 控件也会留出相应的宽度来显示空白行；如果文本内容超出了设置的行数, 则超出的部分不会显示.

另外在xml中是不能设置多行文本的, 要在代码中设置. 例如:

    "ui";
    ui.layout(
        <vertical>
            <text id="myText" line="3">
        </vertical>
    )
    //通过\n换行
    ui.myText.setText("第一行\n第二行\n第三行\n第四行");

## maxLines#

设置文本控件的最大行数.

## typeface#

设置字体. 可选的值为：

- `normal` 正常字体
- `sans` 衬线字体
- `serif` 非衬线字体
- `monospace` 等宽字体

示例, 等宽字体: `<text text="等宽字体" typeface="monospace"/>`

## ellipsize#

设置文本的省略号位置. 文本的省略号会在文本内容超出文本控件时显示. 可选的值为：

- `end` 在文本末尾显示省略号
- `marquee` 跑马灯效果, 文本将滚动显示
- `middle` 在文本中间显示省略号
- `none` 不显示省略号
- `start` 在文本开头显示省略号

## ems#

当设置该属性后,TextView显示的字符长度（单位是em）,超出的部分将不显示, 或者根据ellipsize属性的设置显示省略号.

例如, 限制文本最长为5em: `

## autoLink#

控制是否自动找到url和电子邮件地址等链接, 并转换为可点击的链接. 默认值为“none”.

设置该值可以让文本中的链接、电话等变成可点击状态.

可选的值为以下的值以其通过或("|")的组合：

- `all` 匹配所有连接、邮件、地址、电话
- `email` 匹配电子邮件地址
- `map` 匹配地图地址
- `none` 不匹配 (默认)
- `phone` 匹配电话号码
- `web` 匹配URL地址

示例：`<text autoLink="web|phone" text="百度: http://www.baidu.com 电信电话: 10000"/>`

# 按钮控件: button#

按钮控件是一个特殊的文本控件, 因此所有文本控件的函数的属性都适用于按钮控件.

除此之外, 按钮控件有一些内置的样式, 通过`style`属性设置, 包括：

- Widget.AppCompat.Button.Colored 带颜色的按钮
- Widget.AppCompat.Button.Borderless 无边框按钮
- Widget.AppCompat.Button.Borderless.Colored 带颜色的无边框按钮

这些样式的具体效果参见"示例/界面控件/按钮控件.js".

例如：`<button style="Widget.AppCompat.Button.Colored" text="漂亮的按钮"/>`

# 输入框控件: input#

输入框控件也是一个特殊的文本控件, 因此所有文本控件的函数的属性和函数都适用于按钮控件. 输入框控件有自己的属性和函数, 要查看所有这些内容, 阅读EditText.

对于一个输入框控件, 我们可以通过text属性设置他的内容, 通过lines属性指定输入框的行数；在代码中通过`getText()`函数获取输入的内容. 例如：

    "ui";
    ui.layout(
        <vertical padding="16">
            <text textSize="16sp" textColor="black" text="请输入姓名"/>
            <input id="name" text="小明"/>
            <button id="ok" text="确定"/>
        </vertical>
    );
    //指定确定按钮点击时要执行的动作
    ui.ok.click(function(){
        //通过getText()获取输入的内容
        var name = ui.name.getText();
        toast(name + "您好!");
    });

效果如图：

除此之外, 输入框控件有另外一些主要属性(虽然这些属性对于文本控件也是可用的但一般只用于输入框控件)：

## hint#

输入提示. 这个提示会在输入框为空的时候显示出来. 如图所示:

上面图片效果的代码为：

    "ui";
    ui.layout(
        <vertical>
            <input hint="请输入姓名"/>
        </vertical>
    )

## textColorHint#

指定输入提示的字体颜色.

## textSizeHint#

指定输入提示的字体大小.

## inputType#

指定输入框可以输入的文本类型. 可选的值为以下值及其用"|"的组合:

- `date` 用于输入日期.
- `datetime` 用于输入日期和时间.
- `none` 没有内容类型. 此输入框不可编辑.
- `number` 仅可输入数字.
- `numberDecimal` 可以与number和它的其他选项组合, 以允许输入十进制数(包括小数).
- `numberPassword` 仅可输入数字密码.
- `numberSigned` 可以与number和它的其他选项组合, 以允许输入有符号的数.
- `phone` 用于输入一个电话号码.
- `text` 只是普通文本.
- `textAutoComplete` 可以与text和它的其他选项结合, 以指定此字段将做自己的自动完成, 并适当地与输入法交互.
- `textAutoCorrect` 可以与text和它的其他选项结合, 以请求自动文本输入纠错.
- `textCapCharacters` 可以与text和它的其他选项结合, 以请求大写所有字符.
- `textCapSentences` 可以与text和它的其他选项结合, 以请求大写每个句子里面的第一个字符.
- `textCapWords` 可以与text和它的其他选项结合, 以请求大写每个单词里面的第一个字符.
- `textEmailAddress` 用于输入一个电子邮件地址.
- `textEmailSubject` 用于输入电子邮件的主题.
- `textImeMultiLine` 可以与text和它的其他选项结合, 以指示虽然常规文本视图不应为多行, 但如果可以, 则IME应提供多行支持.
- `textLongMessage` 用于输入长消息的内容.
- `textMultiLine` 可以与text和它的其他选项结合, 以便在该字段中允许多行文本. 如果未设置此标志, 则文本字段将被限制为单行.
- `textNoSuggestions` 可以与text及它的其他选项结合, 以指示输入法不应显示任何基于字典的单词建议.
- `textPassword` 用于输入密码.
- `textPersonName` 用于输入人名.
- `textPhonetic` 用于输入拼音发音的文本, 如联系人条目中的拼音名称字段.
- `textPostalAddress` 用于输入邮寄地址.
- `textShortMessage` 用于输入短的消息内容.
- `textUri` 用于输入一个URI.
- `textVisiblePassword` 用于输入可见的密码.
- `textWebEditText` 用于输入在web表单中的文本.
- `textWebEmailAddress` 用于在web表单里输入一个电子邮件地址.
- `textWebPassword` 用于在web表单里输入一个密码.
- `time` 用于输入时间.

例如, 想指定一个输入框的输入类型为小数数字, 为: `<input inputType="number|numberDecimal"/>`

## password#

指定输入框输入框是否为密码输入框. 默认为`false`.

例如：`<input password="true"/>`

## numeric#

指定输入框输入框是否为数字输入框. 默认为`false`.

例如：`<input numeric="true"/>`

## phoneNumber#

指定输入框输入框是否为电话号码输入框. 默认为`false`.

例如：`<input phoneNumber="true"/>`

## digits#

指定输入框可以输入的字符. 例如, 要指定输入框只能输入"1234567890+-", 为`<input digits="1234567890+-"/>`.

## singleLine#

指定输入框是否为单行输入框. 默认为`false`. 您也可以通过`lines="1"`来指定单行输入框.

例如：`<input singleLine="true"/>`

# 图片控件: img#

图片控件用于显示来自网络、本地或者内嵌数据的图片, 并可以指定图片以圆角矩形、圆形等显示. 但是不能用于显示gif动态图.

这里只介绍他的主要方法和属性, 如果要查看他的所有方法和属性, 阅读ImageView.

## src#

使用一个Uri指定图片的来源. 可以是图片的地址(http://....), 本地路径(file://....)或者base64数据("data:image/png;base64,...").

如果使用图片地址或本地路径, Auto.js会自动使用适当的缓存来储存这些图片, 减少下次加载的时间.

例如, 显示百度的logo:

    "ui";
    ui.layout(
        <frame>
            <img src="https://www.baidu.com/img/bd_logo1.png"/>
        </frame>
    );

再例如, 显示文件/sdcard/1.png的图片为 `<img src="file:///sdcard/1.png"/>`. 再例如, 使base64显示一张钱包小图片为：

    "ui";
    ui.layout(
        <frame>
            <img w="40" h="40" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEu0lEQVRoge3bW4iVVRQH8N+ZnDKxvJUGCSWUlXYle/ChiKAkIiu7UXQjonwNIopM8cHoAhkRGQXdfIiE0Ep8KalQoptRTiFFZiRlOo6TPuSk4zk97G9w5vidc77LPjNi84f1MN+391rrf9a+rL32N4xiFMcUjouo5zyciYPYH0FnBadiNiZiD2oR9JbGRdgiOFPDIXRhCWYU0Dcj6duV6BrQuyWxNaLowBcOO1Uv+7EKc4WINUIlabMq6dNI35eJzRHDWOzS2MEB6cd6XI/OQf07k2frkzat9HQnNkcUG7R2dECq2I53EtmePMvaf+MwcWqKu+RzuqhUcfcwcWqKTvmiXFQ2GDodRhQz0aN9ZHsSG0cVrkGf+GT7MG8YeeTCHeKS7sOdMR1stjcWxY2YH0nXh1gdSdf/E+2I8KVYigkl9ewVUsxNpT1qMzaKN4ejJxrtyEt7IuraE1EX2jOkp+JBnFxSzz68KuTqoyiK2BHuxDO4NpK+j/GoOAWF6BiH98Q/SHyCycPIIxMm4FPZCPTj30SynIFr+A7ThotMK4wXopA1Ym9gSiKv5Oj3bdKnFMpuS514E1fm6NMnbF098s3NS4QS0Ik5+hyBsoSXYkGO9jvxy6C/t+IPIYJZcBWW57AXFfMNrSo2kqqw2l4hvSzcIRTw1sm24FVxb5s4NcR0/JXBuUNYJttI6sDjsi1kvTgrGpsMjq3O4FQNa+SbNhWsyKj7I4wpzSYDbpFtKB/EOSn9ZwpRfx5Xp7yfhN0Z9FdxXxxKjTEe2zI4U8NnKf3PNrT2VcWTKe1eyGjjT+Eapm14IqMjNTyd0n9JSrsDwhmaEN2H8GMOO8viUjyMSfJVJh9O0bGoQdt1eFm2oVwve7UpC1ssX568KEXH6fghp54s8lRkrk7CjpxOrGqg6wQ8IKSKWXPpVtIt8ly+v4ATf2t+yqlgDl5SbCjXy8JIXFXweQEHqngxo43JeEw54l+JVLKaJeypRZzoFxavrIWG6cKPW2SO9+PCMkQHsLiA8fpIv5/DmUn4qaCtpWWIEiLzdUHj9XJA2H5uFRbBZriuoI1NSpatpio+nJtFvFvYd2c1sDsGvxfQ3a/knrwgMtm0qD8rPSprCuq8uRmhVqvanBbvm+EQfsNKIcnvTmnTiUdwQcq73oJ2L2v2stXx6vyCRr8RDuk/C8OMUK24J6VtBaekPG81zxuh0TTJhC7FhtUOHF+n61whGalvu8uRWVJFvgPEYOkqQzhLVSPPXLoYa4Xh3Stcls1NaTdb8Xx7ZxnCvSUIfy/kzWno0Pyzx3dL2C0695Hto7NGUhXy5Lzp3kLZKiqNpNTl2+YShgdIvyXbVck44TB/oKTNzWUIv13S+IDsFmpY84QvZAcwTbh4e04o18SwtbIM4dsiOTFYVgzSv7wN+m9vRqjV/PrA0JuCox1bhYNKQ7Qi3CcU1fpiedRG9AkLXhRfbxCnKlET0s21ifwaSWcPbopBdDDOwGtClTD2vCsq+/C68K8HmVDk7DhFyIsvFzKnGThN+689+oU9dptwQb5B+LB8dx4lMb7xqAhkJwo/xljhFFSfSdUc3mPrcbwj15P+pP0/QiR7hYSkGsHnUYziWMF/mXV4JVcZ8G0AAAAASUVORK5CYII="/>
        </frame>
    );

## tint#

图片着色, 其值是一个颜色名称或RGB颜色值. 使用该属性会将图片中的非透明区域都涂上同一颜色. 可以用于改变图片的颜色.

例如, 对于上面的base64的图片: `<img w="40" h="40" tint="red" src="data:image/png;base64,..."/>`, 则钱包图标颜色会变成红色.

## scaleType#

控制图片根据图片控件的宽高放缩时的模式. 可选的值为：

- `center` 在控件中居中显示图像, 但不执行缩放.
- `centerCrop` 保持图像的长宽比缩放图片, 使图像的尺寸 (宽度和高度) 等于或大于控件的相应尺寸 (不包括内边距padding)并且使图像在控件中居中显示.
- `centerInside` 保持图像的长宽比缩放图片, 使图像的尺寸 (宽度和高度) 小于视图的相应尺寸 (不包括内边距padding)并且图像在控件中居中显示.
- `fitCenter` 保持图像的长宽比缩放图片, 使图片的宽或高和控件的宽高相同并使图片在控件中居中显示
- `fitEnd` 保持图像的长宽比缩放图片, 使图片的宽或高和控件的宽高相同并使图片在控件中靠右下角显示
- `fitStart` 保持图像的长宽比缩放图片, 使图片的宽或高和控件的宽高相同并使图片在控件靠左上角显示
- `fitXY` 使图片和宽高和控件的宽高完全匹配, 但图片的长宽比可能不能保持一致
- `matrix` 绘制时使用图像矩阵进行缩放. 需要在代码中使用`setImageMatrix(Matrix)`函数才能生效.

默认的scaleType为`fitCenter`；除此之外最常用的是`fitXY`, 他能使图片放缩到控件一样的大小, 但图片可能会变形.

## radius#

图片控件的半径. 如果设置为控件宽高的一半并且控件的宽高相同则图片将剪切为圆形显示；否则图片为圆角矩形显示, 半径即为四个圆角的半径, 也可以通过`radiusTopLeft`, `radiusTopRight`, `radiusBottomLeft`, `radiusBottomRight`等属性分别设置四个圆角的半径.

例如, 圆角矩形的Auto.js图标：`<img w="100" h="100" radius="20" bg="white" src="http://www.autojs.org/assets/uploads/profile/3-profileavatar.png" />`

有关该属性的单位, 参见尺寸的单位: Dimension.

## radiusTopLeft#

图片控件的左上角圆角的半径. 有关该属性的单位, 参见尺寸的单位: Dimension.

## radiusTopRight#

图片控件的右上角圆角的半径. 有关该属性的单位, 参见尺寸的单位: Dimension.

## radiusBottomLeft#

图片控件的左下角圆角的半径. 有关该属性的单位, 参见尺寸的单位: Dimension.

## radiusBottomRight#

图片控件的右下角圆角的半径. 有关该属性的单位, 参见尺寸的单位: Dimension.

## borderWidth#

图片控件的边框宽度. 用于在图片外面显示一个边框, 边框会随着图片控件的外形(圆角等)改变而相应变化. 例如, 圆角矩形带灰色边框的Auto.js图标：`<img w="100" h="100" radius="20" borderWidth="5" borderColor="gray" bg="white" src="http://www.autojs.org/assets/uploads/profile/3-profileavatar.png" />`

## borderColor#

图片控件的边框颜色.

## circle#

指定该图片控件的图片是否剪切为圆形显示. 如果为`true`, 则图片控件会使其宽高保持一致(如果宽高不一致, 则保持高度等于宽度)并使圆形的半径为宽度的一半.

例如, 圆形的Auto.js图标：`<img w="100" h="100" circle="true" bg="white" src="http://www.autojs.org/assets/uploads/profile/3-profileavatar.png" />`

# 垂直布局: vertical#

垂直布局是一种比较简单的布局, 会把在它里面的控件按照垂直方向依次摆放, 如下图所示：

垂直布局:

—————

| 控件1 |

| 控件2 |

| 控件3 |

| ............ |

——————

## layout_weight#

垂直布局中的控件可以通过`layout_weight`属性来控制控件高度占垂直布局高度的比例. 如果为一个控件指定`layout_weight`, 则这个控件的高度=垂直布局剩余高度 * layout_weight / weightSum；如果不指定weightSum, 则weightSum为所有子控件的layout_weight之和. 所谓"剩余高度", 指的是垂直布局中减去没有指定layout_weight的控件的剩余高度. 例如:

    "ui";
    ui.layout(
        <vertical h="100dp">
            <text layout_weight="1" text="控件1" bg="#ff0000"/>
            <text layout_weight="1" text="控件2" bg="#00ff00"/>
            <text layout_weight="1" text="控件3" bg="#0000ff"/>
        </vertical>
    );

在这个布局中, 三个控件的layout_weight都是1, 也就是他们的高度都会占垂直布局高度的1/3, 都是33.3dp. 再例如：

    "ui";
    ui.layout(
        <vertical h="100dp">
            <text layout_weight="1" text="控件1" bg="#ff0000"/>
            <text layout_weight="2" text="控件2" bg="#00ff00"/>
            <text layout_weight="1" text="控件3" bg="#0000ff"/>
        </vertical>
    );

在这个布局中, 第一个控件高度为1/4, 第二个控件为2/4, 第三个控件为1/4. 再例如：

    "ui";
    ui.layout(
        <vertical h="100dp" weightSum="5">
            <text layout_weight="1" text="控件1" bg="#ff0000"/>
            <text layout_weight="2" text="控件2" bg="#00ff00"/>
            <text layout_weight="1" text="控件3" bg="#0000ff"/>
        </vertical>
    );

在这个布局中, 因为指定了weightSum为5, 因此第一个控件高度为1/5, 第二个控件为2/5, 第三个控件为1/5. 再例如：

    "ui";
    ui.layout(
        <vertical h="100dp">
            <text h="40dp" text="控件1" bg="#ff0000"/>
            <text layout_weight="2" text="控件2" bg="#00ff00"/>
            <text layout_weight="1" text="控件3" bg="#0000ff"/>
        </vertical>
    );

在这个布局中, 第一个控件并没有指定layout_weight, 而是指定高度为40dp, 因此不加入比例计算, 此时布局剩余高度为60dp. 第二个控件高度为剩余高度的2/3, 也就是40dp, 第三个控件高度为剩余高度的1/3, 也就是20dp.

垂直布局的layout_weight属性还可以用于控制他的子控件高度占满剩余空间, 例如：

    "ui";
    ui.layout(
        <vertical h="100dp">
            <text h="40dp" text="控件1" bg="#ff0000"/>
            <text h="40dp" text="控件2" bg="#00ff00"/>
            <text layout_weight="1" text="控件3" bg="#0000ff"/>
        </vertical>
    );

在这个布局中, 第三个控件的高度会占满除去控件1和控件2的剩余空间.

# 水平布局: horizontal#

水平布局是一种比较简单的布局, 会把在它里面的控件按照水平方向依次摆放, 如下图所示： 水平布局: ————————————————————————————

| 控件1 | 控件2 | 控件3 | ... |

————————————————————————————

## layout_weight#

水平布局中也可以使用layout_weight属性来控制子控件的宽度占父布局的比例. 和垂直布局中类似, 不再赘述.

# 线性布局: linear#

实际上, 垂直布局和水平布局都属于线性布局. 线性布局有一个orientation的属性, 用于指定布局的方向, 可选的值为`vertical`和`horizontal`.

例如`<linear orientation="vertical"></linear>`相当于`<vertical></vertical>`.

线性布局的默认方向是横向的, 因此, 一个没有指定orientation属性的线性布局就是横向布局.

# 帧布局: frame#

帧布局

# 相对布局: relative#

# 勾选框控件: checkbox#

# 选择框控件: radio#

# 选择框布局: radiogroup#

# 开关控件: Switch#

开关控件用于表示一个选项是否被选中.

## checked#

表示开关是否被选中. 可选的值为：

- `true` 打开开关
- `false` 关闭开关

## text#

对开关进行描述的文字.

# 进度条控件: progressbar#

# 拖动条控件: seekbar#

# 下来菜单控件: spinner#

# 时间选择控件: timepicker#

# 日期选择控件: datepicker#

# 浮动按钮控件: fab#

# 标题栏控件: toolbar#

# 卡片: card#

卡片控件是一个拥有圆角、阴影的控件.

## cardBackgroundColor#

卡片的背景颜色.

## cardCornerRadius#

卡片的圆角半径.

## cardElevation#

设置卡片在z轴上的高度, 来控制阴影的大小.

## contentPadding#

设置卡片的内边距. 该属性包括四个值：

- `contentPaddingLeft` 左内边距
- `contentPaddingRight` 右内边距
- `contentPaddingTop` 上内边距
- `contentPaddingBottom` 下内边距

## foreground#

使用`foreground="?selectableItemBackground"`属性可以为卡片添加点击效果.

# 抽屉布局: drawer#

# 列表: list#

# Tab: tab#

# ui#

## ui.layout(xml)#

- `xml` { XML } | { string } 布局XML或者XML字符串

将布局XML渲染为视图（View）对象, 并设置为当前视图.

## ui.layoutFile(xmlFile)#

- `xml` { string } 布局XML文件的路径

此函数和`ui.layout`相似, 只不过允许传入一个xml文件路径来渲染布局.

## ui.inflate(xml[, parent = null, attachToParent = false])#

- `xml` { string } | { XML } 布局XML或者XML字符串
- `parent` { View } 父视图
- `attachToParent` { boolean } 是否渲染的View加到父视图中, 默认为false
- 返回 { View }

将布局XML渲染为视图（View）对象. 如果该View将作为某个View的子View, 我们建议传入`parent`参数, 这样在渲染时依赖于父视图的一些布局属性能够正确应用.

此函数用于动态创建、显示View.

    "ui";

    $ui.layout(
        <linear id="container">
        </linear>
    );

    // 动态创建3个文本控件, 并加到container容器中
    // 这里仅为实例, 实际上并不推荐这种做法, 如果要展示列表,
    // 使用list组件；动态创建十几个、几十个View会让界面卡顿
    for (let i = 0; i < 3; i++) {
        let textView = $ui.inflate(
            <text textColor="#000000" textSize="14sp"/>
        , $ui.container);
        textView.attr("text", "文本控件" + i);
        $ui.container.addView(textView);
    }

# ui.registerWidget(name, widget)#

- `name` { string } 组件名称
- `widget` { Function } 组件

注册一个自定义组件. 参考示例->界面控件->自定义控件.

# ui.isUiThread()#

- 返回 { boolean }

返回当前线程是否是UI线程.

    "ui";

    log($ui.isUiThread()); // => true

    $threads.start(function () {
        log($ui.isUiThread()); // => false
    });

## ui.findView(id)#

- `id` { string } View的ID
- 返回 { View }

在当前视图中根据ID查找相应的视图对象并返回. 如果当前未设置视图或找不到此ID的视图时返回`null`.

一般我们都是通过`ui.xxx`来获取id为xxx的控件, 如果xxx是一个ui已经有的属性, 就可以通过`$ui.findView()`来获取这个控件.

## ui.finish()#

结束当前活动并销毁界面.

## ui.setContentView(view)#

- `view` { View }

将视图对象设置为当前视图.

## ui.post(callback[, delay = 0])#

- `callback` { Function } 回调函数
- `delay` { number } 延迟, 单位毫秒

将`callback`加到UI线程的消息循环中, 并延迟delay毫秒后执行（不能准确保证一定在delay毫秒后执行）.

此函数可以用于UI线程中延时执行动作（sleep不能在UI线程中使用）, 也可以用于子线程中更新UI.

    "ui";

    ui.layout(
        <frame>
            <text id="result"/>
        </frame>
    );

    ui.result.attr("text", "计算中");
    // 在子线程中计算1+ ... + 10000000
    threads.start({
        let sum = 0;
        for (let i = 0; i < 1000000; i++) {
            sum += i;
        }
        // 由于不能在子线程操作UI, 所以要抛到UI线程执行
        ui.post(() => {
            ui.result.attr("text", String(sum));
        });
    });

## ui.run(callback)#

- `callback` { Function } 回调函数
- 返回 callback的执行结果

将`callback`在UI线程中执行. 如果当前已经在UI线程中, 则直接执行`callback`；否则将`callback`抛到UI线程中执行（加到UI线程的消息循环的末尾）, 并等待callback执行结束(阻塞当前线程).

## ui.statusBarColor(color)#

- color { string } | { number } 颜色

设置当前界面的状态栏颜色.

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

## ui.useAndroidResources()#

启用使用Android的布局(layout)、绘图(drawable)、动画(anim)、样式(style)等资源的特性. 启用该特性后, 在project.json中进行以下配置, 就可以像写Android原生一样写界面：

    {
        // ...
        androidResources: {
            "resDir": "res",  // 资源文件夹
            "manifest": "AndroidManifest.xml" // AndroidManifest文件路径
        }
    }

res文件夹通常为以下结构：

    - res
        - layout  // 布局资源
        - drawable // 图片、形状等资源
        - menu // 菜单资源
        - values // 样式、字符串等资源
        // ...

可参考示例->复杂界面->Android原生界面.

# 尺寸的单位: Dimension#

# Drawables#

# 颜色#

(完善中...)

# 万维网 (Web)#

在应用里显示网页内容, 而不是打开独立的浏览器, 此时可使用 WebView 类, 以实现在 activity 中显示网页内容.

web 模块主要用于 WebView 网页的 [ 注入 (Inject) / 构建客户端 ] 等.

> 注: 与 http 模块不同, http 模块主要用于网络的请求与响应.

web

## [m] newInjectableWebView#

### newInjectableWebView(url?)#

`6.3.0` `Global` `Overload [1-2]/3` `UI`

- [ url ] { string } - 需要 WebView 加载的 URL
- returns { InjectableWebView }

新建并返回一个 InjectableWebView (可 注入 的 WebView) 实例.

    'ui';

    ui.layout(<vertical>
        <frame id="main"/>
    </vertical>);

    /* 创建一个 InjectableWebView 实例. */
    let webView = newInjectableWebView();
    /* 加载指定的 URL, "https://" 也可省略. */
    webView.loadUrl('https://www.github.com');
    /* 注入 JavaScript 脚本, 显示 alert 消息框. */
    webView.inject('alert("hello")');
    /* 附加视图对象到 id 为 main 的视图上. */
    ui.main.addView(webView);

上述示例也可使用 `setContentView` 实现更简单的内容加载 (但不包含代码注入):

    'ui';
    activity.setContentView(web.newInjectableWebView('www.github.com'));

除上述注入简单的 `alert` 消息框外, 还支持其他更多注入方式:

    /* 以给定的 URL 来替换当前的资源. */
    webView.inject('document.location.replace("https://www.jetbrains.com")');
    /* 替换整个 document 的内容. */
    webView.inject('document.write("hello")');
    /* 替换 body 元素为指定的 HTML 内容. */
    webView.inject('document.body.innerHTML = "<p>hello</p>"');
    /* 指定 body 元素的字体颜色. */
    webView.inject('document.body.style = "color:green"');
    /* 在文档末尾附加一个自定义元素. */
    webView.inject('let p = document.createElement("p"); p.innerHTML = "hello"; document.body.appendChild(p)');
    /* 使用回调方法获取内部信息. */
    webView.inject('navigator.userAgent', result => console.log(result));

如需对上述 `webView` 实例进行一些设置, 可通过 `webView.getSettings()` 获得 android.webkit.WebSettings 对象, 再进行个性化设置:

    let settings = webView.getSettings();

    /* 设置 WebView 默认字体大小, 默认值 16. */
    settings.setDefaultFontSize(18);
    /* 设置是否允许脚本自动弹出新窗口, 默认 false. */
    settings.setJavaScriptCanOpenWindowsAutomatically(true);
    /* 设置 WebView 是否支持使用屏幕控件或手势进行缩放, 默认 true. */
    settings.setSupportZoom(false);

    /* ... */

对于 `InjectableWebView`, 其内部已经对 WebView 进行了一些初始化设置, 包括:

    settings.setUseWideViewPort(true);
    settings.setBuiltInZoomControls(true);
    settings.setLoadWithOverviewMode(true);
    settings.setJavaScriptEnabled(true);
    settings.setJavaScriptCanOpenWindowsAutomatically(true);
    settings.setDomStorageEnabled(true);
    settings.setDisplayZoomControls(false);

> 注: 上述设置参考自 Auto.js 4.1.1 Alpha2 源码.

此外, `InjectableWebView` 内部还初始化了一个默认的 WebChromeClient 客户端:

    setWebChromeClient(new WebChromeClient());

> 注:
> WebChromeClient 中的 "Chrome" 与 Google Chrome 浏览器中的 "Chrome" 不同.
> WebView 中的 "Chrome" 指代 WebView 外面的装饰及 UI 部分.
> WebChromeClient 是 HTML/JavaScript 与 Android 客户端交互的中间件, 它将 WebView 中 JavaScript 产生的事件封装后传递到 Android 客户端, 从而避免一些可能的安全问题.
> 同时 WebChromeClient 也可以辅助 WebView 处理 JavaScript 对话框, 显示加载进度, 上传文件等.

在 WebView 中访问了多个网页时, 按返回键会立即关闭整个页面, 而不是回退到上一个历史网页.
如果希望在 WebView 里浏览历史网页, 可参考如下代码:

    ui.emitter.on('back_pressed', function (e) {
        if (webView.canGoBack()) {
            webView.goBack();
            e.consumed = true;
        }
    });

### newInjectableWebView(activity)#

`6.3.0` `Global` `Overload 3/3` `UI`

- activity { ScriptExecuteActivity } - 上下文对象, 默认为 UI 模式下的全局 activity 对象
- returns { InjectableWebView }

新建并返回一个 InjectableWebView (可 注入 的 WebView) 实例, 通过 `activity` 参数可传入不同的 `org.mozilla.javascript.Context` 上下文对象, 该对象主要用于执行 JavaScript 语句.

## [m] newInjectableWebClient#

### newInjectableWebClient()#

`6.3.0` `Global`

- returns { InjectableWebClient }

新建并返回一个 InjectableWebClient (可 注入 的 WebViewClient) 实例.

    /* 为 webView 对象重新设置一个新的客户端. */
    webView.setWebViewClient(newInjectableWebClient());

## [m] newWebSocket#

### newWebSocket(url)#

`6.3.1` `Global`

- url { string } - 请求的 URL 地址
- returns { WebSocket }

构建一个 WebSocket 实例.

相当于 `new WebSocket(url)`.

> 参阅: WebSocket 章节

# HTTP#

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

Marked by SuperMonster003 on Mar 21, 2023.

http 模块主要用于发送 HTTP 请求, 获取并解析 HTTP 响应.

> 注: 与 web 模块不同, web 模块主要用于 WebView 网页的注入及客户端构建.

http

## [m] get#

### get(url)#

`Overload 1/3`

- url { string } - 请求的 URL 地址 (默认使用 HTTP 协议)
- returns { HttpResponse } - 请求的响应实例

    let response = http.get('www.github.com');
    if (response.statusCode === 200) {
        console.log('请求成功');
    } else {
        console.log('请求失败');
    }

### get(url, options)#

`Overload 2/3`

- url { string } - 请求的 URL 地址 (默认使用 HTTP 协议)
- options { HttpRequestBuilderOptions } - 请求的构建选项
- returns { HttpResponse } - 请求的响应实例

### get(url, options, callback)#

`Overload 3/3` `Async`

- url { string } - 请求的 URL 地址 (默认使用 HTTP 协议)
- options { HttpRequestBuilderOptions } - 请求的构建选项
- callback { HttpRequestBuilderOptions } - 请求的响应回调
- returns { HttpResponse } - 请求的响应实例

- url { string } - 请求的 URL 地址
- `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));
    }

## [m] post#

### 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("登录失败");
    }

## [m] postJson#

### 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());

## [m] postMultipart#

### 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());

## [m] request#

### 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响应头信息.
- `method` { string } HTTP请求方法. 包括"GET", "POST", "PUT", "DELETE", "PATCH".
- `contentType` { string } HTTP头部信息中的"Content-Type", 表示HTTP请求的内容类型. 例如"text/plain", "application/json". 更多信息参见菜鸟教程：HTTP contentType.
- `body` { string } | { Array } | { Function } HTTP请求的内容. 可以是一个字符串, 也可以是一个字节数组；或者是一个以BufferedSink为参数的函数.

该函数是get, post, postJson等函数的基础函数. 因此除非是PUT, DELETE等请求, 或者需要更高定制的HTTP请求, 否则直接使用get, post, postJson等函数会更加方便.

## [m] buildRequest#

### buildRequest(url, options)#

... ...

# Base64#

Base64 是一种编码方式, 同时可作为一种数据的存储格式, 并非严格意义上的加密与解密.

base64 模块主要用于对数据进行 Base64 编码及解码.

> 注: 与 crypto 模块不同, crypto 模块主要用于信息的加密与解密.

> 参阅:
> Wikipedia (英) / Wikipedia (中)
> Base64 笔记 (from 阮一峰的网络日志)
> Base64 是加密算法吗 (from 稀土掘金)

## 可逆性#

Base64 是一种可逆的编码方式:

    console.log(base64.decode(base64.encode('orange'))); // orange
    console.log(base64.decode(base64.encode('简体中文'))); // 简体中文

但需注意参数的字符编码方式 (默认为 `UTF-8`), 当存在超出编码表示范围的字符时, 可逆性将遭到破坏:

    /* us-ascii 编码无法表示 "出" 和 "口". */
    console.log(base64.decode(base64.encode('E2 出口', 'us-ascii'), 'us-ascii')); // E2 ??

编解码使用了不同的编码方式, 也可能导致可逆性失效:

    /* utf-16 与 utf-8 不一致. */
    console.log(base64.decode(base64.encode('简体中文', 'utf-16'), 'utf-8')); // ��{�OSN-e�

base64

## [m] encode#

### encode(o, encoding?)#

- o { string | JsByteArray | ByteArray } - 待编码数据
- [ encoding = `'UTF_8'` ] { StandardCharset } - 字符编码
- returns { string }

对数据进行 Base64 编码.

    /* 字符串. */

    console.log(base64.encode('hello', 'iso-8859-1')); // aGVsbG8=
    console.log(base64.encode('hello', 'us-ascii')); // aGVsbG8=
    console.log(base64.encode('hello', 'utf-8')); // aGVsbG8=
    console.log(base64.encode('hello', 'utf-16')); // /v8AaABlAGwAbABv
    console.log(base64.encode('hello', 'utf-16be')); // AGgAZQBsAGwAbw==
    console.log(base64.encode('hello', 'utf-16le')); // aABlAGwAbABvAA==

    /* JavaScript 字节数组. */

    console.log(base64.encode([ 104, 101, 108, 108, 111 ], 'utf-8')); // aGVsbG8=

    /* Java 字节数组. */

    console.log(base64.encode(new java.lang.String('hello').getBytes(), 'utf-8')); // aGVsbG8=

    let jsArr = [ 104, 101, 108, 108, 111 ];
    let javaArr = util.java.array('byte', jsArr.length);
    jsArr.forEach((o, i) => javaArr[i] = o);
    console.log(base64.encode(javaArr, 'utf-8')); /* 效果同上. */

## [m] decode#

### decode(o, encoding?)#

- o { string | JsByteArray | ByteArray } - 待解码数据
- [ encoding = `'UTF_8'` ] { StandardCharset } - 字符编码
- returns { string }

对数据进行 Base64 解码.

    /* 字符串. */

    console.log(base64.decode('aGVsbG8=', 'iso-8859-1')); // hello
    console.log(base64.decode('aGVsbG8=', 'us-ascii')); // hello
    console.log(base64.decode('aGVsbG8=', 'utf-8')); // hello
    console.log(base64.decode('/v8AaABlAGwAbABv', 'utf-16')); // hello
    console.log(base64.decode('AGgAZQBsAGwAbw==', 'utf-16be')); // hello
    console.log(base64.decode('aABlAGwAbABvAA==', 'utf-16le')); // hello

    /* JavaScript 字节数组. */

    console.log(base64.decode([ 97, 71, 86, 115, 98, 71, 56, 61 ], 'utf-8')); // hello

    /* Java 字节数组. */

    console.log(base64.decode(new java.lang.String('aGVsbG8=').getBytes(), 'utf-8')); // hello

    let jsArr = [ 97, 71, 86, 115, 98, 71, 56, 61 ];
    let javaArr = util.java.array('byte', jsArr.length);
    jsArr.forEach((o, i) => javaArr[i] = o);
    console.log(base64.decode(javaArr, 'utf-8')); /* 效果同上. */

# 密文 (Crypto)#

crypto 模块提供 [ 对称加密 (如 AES) / 非对称加密 (如 RSA) / 消息摘要 (如 MD5, SHA) ] 等支持.

> 注: 本章节参考自 Auto.js Pro 文档.

crypto

## [m] digest#

### digest(message, algorithm)#

`6.3.2` `Overload 1/4`

- message { string } - 待获取摘要的消息
- algorithm { CryptoDigestAlgorithm } - 消息摘要算法
- returns { string | JsByteArray }

获取 `消息 (message)` 经指定 `算法 (algorithm)` 计算后的 `消息摘要 (message digest)`.

    /* 获取字符串 "hello" 的 MD5 摘要. */
    console.log(crypto.digest('hello', 'MD5')); // 5d41402abc4b2a76b9719d911017c592

    /* 获取字符串 "hello" 的 SHA-1 摘要. */
    console.log(crypto.digest('hello', 'SHA-1')); // aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d

### digest(message)#

`6.3.2` `Overload 2/4`

- message { string } - 待获取摘要的消息
- returns { string }

获取 `消息 (message)` 的 MD5 `消息摘要 (message digest)`.

相当于 `crypto.digest(message, 'MD5')`.

    /* 获取字符串 "hello" 的 MD5 摘要. */
    console.log(crypto.digest('hello')); // 5d41402abc4b2a76b9719d911017c592
    console.log(crypto.digest('hello', 'MD5')); /* 同上. */

### digest(message, algorithm, options)#

`6.3.2` `Overload 3/4`

- message { string } - 待获取摘要的消息
- algorithm { CryptoDigestAlgorithm } - 消息摘要算法
- options { CryptoDigestOptions } - 选项参数
- returns { string | JsByteArray }

获取 `消息 (message)` 经指定 `算法 (algorithm)` 计算后的 `消息摘要 (message digest)`.

通过 `options` 参数可指定输入消息的类型 (文件, Base64, Hex, 字符串) 及摘要的类型 (字节数组, Base64, Hex, 字符串) 等.

    /* 获取字符串 "hello" 的 MD5 摘要, 并输出其字节数组. */
    console.log(crypto.digest('hello', 'MD5', { output: 'bytes' })); // [93, 65, 64, 42, -68, 75, 42, 118, -71, 113, -99, -111, 16, 23, -59, -110]

    /* 获取字符串 "hello" 的 SHA-1 摘要, 并输出其 Base64 编码. */
    console.log(crypto.digest('hello', 'SHA-1', { output: 'base64' })); // qvTGHdzF6KLavt4PO0gs2a6pQ00=

    /* 获取 Base64 数据的 MD5 摘要. */
    console.log(crypto.digest('qvTGHdzF6KLavt4PO0gs2a6pQ00=', 'MD5', { input: 'base64' })); // 406f65cdc25d6a9db86c06e4bc19a2cf

    /* 获取文件的 MD5 摘要. */
    let str = 'hello';
    let path = files.path(`./tmp-${Date.now()}`);
    files.create(path);
    files.write(path, str);
    console.log(crypto.digest(path, 'MD5', { input: 'file' })); // 5d41402abc4b2a76b9719d911017c592
    files.remove(path);

### digest(message, options)#

`6.3.2` `Overload 4/4`

- message { string } - 待获取摘要的消息
- algorithm { CryptoDigestAlgorithm } - 消息摘要算法
- options { CryptoDigestOptions } - 选项参数
- returns { string | JsByteArray }

获取 `消息 (message)` 的 MD5 `消息摘要 (message digest)`.

通过 `options` 参数可指定输入消息的类型 (文件, Base64, Hex, 字符串) 及摘要的类型 (字节数组, Base64, Hex, 字符串) 等.

相当于 `crypto.digest(message, 'MD5', options)`.

    /* 获取字符串 "hello" 的 MD5 摘要, 并输出其字节数组. */
    console.log(crypto.digest('hello', { output: 'bytes' })); // [93, 65, 64, 42, -68, 75, 42, 118, -71, 113, -99, -111, 16, 23, -59, -110]

    /* 获取 Base64 数据的 MD5 摘要. */
    console.log(crypto.digest('qvTGHdzF6KLavt4PO0gs2a6pQ00=', { input: 'base64' })); // 406f65cdc25d6a9db86c06e4bc19a2cf

    /* 获取文件的 MD5 摘要. */
    let str = 'hello';
    let path = files.path(`./tmp-${Date.now()}`);
    files.create(path);
    files.write(path, str);
    console.log(crypto.digest(path, { input: 'file' })); // 5d41402abc4b2a76b9719d911017c592
    files.remove(path);

## [m] encrypt#

### encrypt(data, key, transformation, options?)#

`6.3.2` `Overload [1-2]/2`

- data { string | JsByteArray | ByteArray } - 待加密数据
- key { crypto.Key | java.security.Key } - 加密密钥
- transformation { CryptoCipherTransformation } - 密码转换名称
- [ options ] { CryptoCipherOptions } - 选项参数
- returns { string | JsByteArray }

数据加密.

#### 输入#

`data` 参数的性质可借助 `options.input` 属性进行区分, 详见 CryptoCipherOptions#input.

如果 `data` 参数为字节数组, 则 `options.input` 将被忽略. 因为字节数组可以唯一确定 `data` 的性质, 无需再通过 `options.input` 指定.

#### 输出#

`options.output` 指定输出的数据格式, 详见 CryptoCipherOptions#output.

特别地, 当 `options.output` 为 `'file'` 时, 输出格式为十六进制值. 因为加密后写入文件的数据通常是不可读的 (常被视作乱码), 因此最终的返回值类型没有采用 `'string'`, 而是 `'hex'`.

`options.output` 影响的其实仅仅是加密结果的表现形式, 这些形式之前通常可以互相转换. 真正影响加密结果的, 是加密过程.

#### 加密#

加密过程受 `key` 和 `transformation` 两个参数的影响. 详见 crypto.Key 及 CryptoCipherTransformation 小节.

为保证最大兼容性, `key` 参数同时支持原生的 `java.security.Key` 类型, 此时 `key` 将自动按照 `new crypto.Key(key.encoded)` 进行转换.

#### 示例#

AES 算法加密示例:

    let message = 'hello';

    /* 创建密钥实例. */
    /* 密钥长度需为 [ 16, 24, 32 ] 之一. */
    let key = new crypto.Key('a'.repeat(16));

    /* 加密数据, 输出格式保持默认, 即 bytes. */
    console.log(crypto.encrypt(message, key, 'AES')); // [-20, 97, -47, 124, 88, 10, 85, -42, -128, -117, 11, 98, -33, -85, 106, 13]

    /* 输出格式修改为 Base64. */
    console.log(crypto.encrypt(message, key, 'AES', { output: 'base64' })); // 7GHRfFgKVdaAiwti36tqDQ==

    /* AES 默认工作模式为 ECB, 默认填充方式为 PKCS5Padding, 结果同上. */
    console.log(crypto.encrypt(message, key, 'AES/ECB/PKCS5Padding', { output: 'base64' })); // 7GHRfFgKVdaAiwti36tqDQ==

RSA 算法加密示例:

    let message = 'hello';

    /* 生成 RSA 密钥对. */
    let key = crypto.generateKeyPair('RSA', 2048);

    /* 使用公钥加密, 转换名称为 RSA/ECB/PKCS1Padding. */
    console.log(crypto.encrypt(message, key.publicKey, 'RSA/ECB/PKCS1Padding')); /* 结果随机, 因为生成的密钥是不确定的. */

#### 可逆性#

加密与解密具有可逆性:

    let message = 'hello';
    let key = new crypto.Key('a'.repeat(16));
    let encrypted = crypto.encrypt(message, key, 'AES');

    /* 解密还原为 hello. */
    console.log(crypto.decrypt(encrypted, key, 'AES', { output: 'string' }));

## [m] decrypt#

### decrypt(data, key, transformation, options?)#

`6.3.2` `Overload [1-2]/2`

- data { string | JsByteArray | ByteArray } - 待解密数据
- key { crypto.Key | java.security.Key } - 解密密钥
- transformation { CryptoCipherTransformation } - 密码转换名称
- [ options ] { CryptoCipherOptions } - 选项参数
- returns { string | JsByteArray }

数据解密.

#### 输入#

`data` 参数的性质可借助 `options.input` 属性进行区分, 详见 CryptoCipherOptions#input.

如果 `data` 参数为字节数组, 则 `options.input` 将被忽略. 因为字节数组可以唯一确定 `data` 的性质, 无需再通过 `options.input` 指定.

#### 输出#

`options.output` 指定输出的数据格式, 详见 CryptoCipherOptions#output.

特别地, 当 `options.output` 为 `'file'` 时, 输出格式为十六进制值. 因为解密后写入文件的数据通常是不可读的 (常被视作乱码), 因此最终的返回值类型没有采用 `'string'`, 而是 `'hex'`.

`options.output` 影响的其实仅仅是解密结果的表现形式, 这些形式之前通常可以互相转换. 真正影响解密结果的, 是解密过程.

#### 解密#

解密过程受 `key` 和 `transformation` 两个参数的影响. 详见 crypto.Key 及 CryptoCipherTransformation 小节.

为保证最大兼容性, `key` 参数同时支持原生的 `java.security.Key` 类型, 此时 `key` 将自动按照 `new crypto.Key(key.encoded)` 进行转换.

#### 示例#

AES 算法解密示例:

    let ec = {
        bytes: [
            -20, 97, -47, 124, 88, 10, 85, -42, -128, -117, 11, 98, -33, -85, 106, 13,
        ],
        base64: '7GHRfFgKVdaAiwti36tqDQ==',
        hex: 'ec61d17c580a55d6808b0b62dfab6a0d',
    };

    /* 创建密钥实例. */
    /* 密钥长度需为 [ 16, 24, 32 ] 之一. */
    let key = new crypto.Key('a'.repeat(16));

    /* 解密数据, 输入为字节数组形式, 输出字符串形式. */
    console.log(crypto.decrypt(ec.bytes, key, 'AES', { input: 'bytes', output: 'string' })); // hello

    /* 解密数据, 输入为 Base64 形式, 输出字符串形式. */
    console.log(crypto.decrypt(ec.base64, key, 'AES', { input: 'base64', output: 'string' })); // hello

    /* 解密数据, 输入为十六进制值形式, 输出字符串形式. */
    console.log(crypto.decrypt(ec.hex, key, 'AES', { input: 'hex', output: 'string' })); // hello

RSA 算法解密示例:

参阅下文 `可逆性` 小节.

#### 可逆性#

加密与解密具有可逆性:

    let message = 'hello';

    /* 生成 RSA 密钥对. */
    let key = crypto.generateKeyPair('RSA', 2048);

    /* 使用公钥加密, 转换名称为 RSA/ECB/OAEPwithSHA-224andMGF1Padding. */
    let ec = crypto.encrypt(message, key.publicKey, 'RSA/ECB/OAEPwithSHA-224andMGF1Padding');

    /* 使用私钥解密, 转换名称同样为 RSA/ECB/PKCS1Padding. */
    let dc = crypto.decrypt(ec, key.privateKey, 'RSA/ECB/OAEPwithSHA-224andMGF1Padding', { output: 'string' });

    /* 解密还原为 message 变量的值, 即 'hello'. */
    console.log(dc); // hello
    console.log(dc === message); // true

## [m] generateKeyPair#

### generateKeyPair(algorithm, length?)#

`6.3.2` `Overload [1-2]/2`

- algorithm { CryptoKeyPairGeneratorAlgorithm } - 密钥对生成器算法
- [ length = `256` ] { number } - 密钥长度
- returns { CryptoKeyPair } - 密钥对

生成指定算法及长度的随机密钥对.

> 注: 不同算法对密钥长度有不同的要求.

如需生成固定密钥的密钥对, 可使用 crypto.KeyPair 直接构造.

密钥对通常会参与非对称加密算法的加解密过程:

    let message = 'hello';

    /* 生成 RSA 密钥对. */
    let key = crypto.generateKeyPair('RSA', 2048);

    /* 使用公钥加密, 转换名称为 RSA/ECB/OAEPwithSHA-224andMGF1Padding. */
    let ec = crypto.encrypt(message, key.publicKey, 'RSA/ECB/OAEPwithSHA-224andMGF1Padding');

    /* 使用私钥解密, 转换名称同样为 RSA/ECB/PKCS1Padding. */
    let dc = crypto.decrypt(ec, key.privateKey, 'RSA/ECB/OAEPwithSHA-224andMGF1Padding', { output: 'string' });

    /* 解密还原为 message 变量的值, 即 'hello'. */
    console.log(dc); // hello
    console.log(dc === message); // true

但需额外注意, 某些算法生成的密钥对, 仅仅用作密钥交换, 密钥交换后将确定一个对称密钥, 最终使用一个对称密钥算法 (如 DES) 进行加解密. 因此密钥对不一定参与非对称加解密过程.

上述情况典型的样例, 当属 Diffie-Hellman（迪菲-赫尔曼) 算法:

    let message = 'hello';

    /* 生成 Diffie-Hellman（迪菲-赫尔曼) 密钥对. */
    let keyPair = crypto.generateKeyPair('DiffieHellman');

    /* 借助 Diffie-Hellman 密钥对, 使用 DES 算法生成一个确定的密钥. */
    let key = keyPair.toKeySpec('DES');

    /* 使用上述密钥加密, 转换名称 (此处可视为算法名称) 也为 DES. */
    /* 除 DES 外, 凡是对称加密算法均被支持, 如 AES. */
    /* 但需保证与上述 toKeySpec 方法传入的参数一致. */
    let ec = crypto.encrypt(message, key, 'DES');

    /* 使用上述同一个密钥解密, 转换名称同样为 DES (与加密算法一致). */
    let dc = crypto.decrypt(ec, key, 'DES', { output: 'string' });

    /* 解密还原为 message 变量的值, 即 'hello'. */
    console.log(dc); // hello
    console.log(dc === message); // true

上述示例再次验证, 密钥对不一定会参与加解密过程. 例如 `DiffieHellman` 密钥对, 仅仅用于确定一个对称密钥.

## [C] Key#

### [c] (data, options?)#

`6.3.2` `Overload [1-2]/2`

- data { string | JsByteArray | ByteArray } - 用于生成密钥的数据
- [ options ] { CryptoCipherOptions & {
  - keyPair?: `'public'` | `'private'` - 指定密钥的公私性质, 用于非对称加密
- }} - 选项参数
- returns { CryptoKey }

生成一个自定义密钥.

关于自定义密钥的实例方法属性及相关示例, 参阅 CryptoKey 类型章节.

使用 `crypto.Key` 构造函数可以很方便地复制密钥数据:

    let key = new crypto.Key('string with length of thrity two');

    console.log(key);

    let copiedKeyA = new crypto.Key(base64.encode(key.data), { input: "base64" });

    console.log(copiedKeyA);

    let copiedKeyB = new crypto.Key(key.data, { input: "bytes" });

    console.log(copiedKeyB);

    let copiedKeyC = new crypto.Key(Crypto.toHex(key.data), { input: "hex" });

    console.log(copiedKeyC);

## [C] KeyPair#

### [c] (publicKeyData, privateKeyData, options?)#

`6.3.2` `Overload [1-2]/2`

- publicKeyData { string | JsByteArray | ByteArray } - 用于生成公钥的数据
- privateKeyData { string | JsByteArray | ByteArray } - 用于生成私钥的数据
- [ options ] { CryptoCipherOptions } - 选项参数
- returns { CryptoKeyPair }

生成一个固定密钥对.

如需生成一个随机密钥对, 可使用 crypto.generateKeyPair.

关于密钥对的实例方法属性及相关示例, 参阅 CryptoKeyPair 类型章节.

使用 `crypto.KeyPair` 构造函数可以很方便地复制密钥对数据:

    let keyPair = crypto.generateKeyPair('RSA');

    console.log(keyPair);

    let copiedKeyPairA = new crypto.KeyPair(
        base64.encode(keyPair.publicKey.data),
        base64.encode(keyPair.privateKey.data),
        { input: 'base64' },
    );

    console.log(copiedKeyPairA);

    let copiedKeyPairB = new crypto.KeyPair(
        keyPair.publicKey.data,
        keyPair.privateKey.data,
        { input: 'bytes' },
    );

    console.log(copiedKeyPairB);

    let copiedKeyPairC = new crypto.KeyPair(
        Crypto.toHex(keyPair.publicKey.data),
        Crypto.toHex(keyPair.privateKey.data),
        { input: "hex" },
    );

    console.log(copiedKeyPairC);

# 国际化 (Internationalization)#

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

Marked by SuperMonster003 on Oct 22, 2022.

# E4X#

> 注: E4X 已弃用.

> 尽管少数浏览器依然支持, 但随着件更新正逐步被废除, 应尽量避免使用.
> AutoJs6 使用 Rhino 引擎, 因此依然保持对 E4X 的支持.

> 本章节仅用于技术概念的归档及溯源, 不建议用于脚本编写.

ECMAScript for XML (E4X) 是对 ECMAScript 的扩展, 增加对 XML 的内在支持.
其目标是在访问 XML 文档时, 提供一种更直观且语法更简洁的的 DOM 接口, 成为处理 XML 文档的新方式.

    var sales = <sales vendor="John">
        <item type="peas" price="4" quantity="6"/>
        <item type="carrot" price="3" quantity="10"/>
        <item type="chips" price="5" quantity="3"/>
      </sales>;

    alert( sales.item.(@type == "carrot").@quantity );
    alert( sales.@vendor );
    for each( var price in sales..@price ) {
      alert( price );
    }
    delete sales.item[0];
    sales.item += <item type="oranges" price="4"/>;
    sales.item.(@type == "oranges").@quantity = 4;

> 参阅: Wikipedia (英) / Wikipedia (中)
> 替代: DOMParser / DOMSerializer