diff --git a/README.md b/README.md index 88e94c96..b9418a98 100644 --- a/README.md +++ b/README.md @@ -112,15 +112,24 @@ # v6.3.3 -###### 2023/07/12 +###### 2023/07/21 -* `修复` VSCode 插件在脚本字符总长度超过四位十进制数时无法解析数据的问题 _[`issue #91`](http://issues.autojs6.com/91)_ _[`issue #93`](http://issues.autojs6.com/93)_ +* `新增` 代码编辑器的代码注释功能 (by [抠脚本人](https://github.com/little-alei)) _[`pr #98`](http://pr.autojs6.com/98)_ +* `新增` auto.stateListener 用于无障碍服务连接状态监听 (by [抠脚本人](https://github.com/little-alei)) _[`pr #98`](http://pr.autojs6.com/98)_ +* `新增` UiObject 类型添加 nextSibling / lastChild / offset 等方法 (参阅 项目文档 > [控件节点](https://docs.autojs6.com/#/uiObjectType)) +* `修复` VSCode 插件在脚本字符总长度超过四位十进制数时无法解析数据的问题 _[`issue #91`](http://issues.autojs6.com/91)_ _[`issue #93`](http://issues.autojs6.com/93)_ _[`issue #100`](http://issues.autojs6.com/100)_ _[`issue #109`](http://issues.autojs6.com/109)_ * `修复` VSCode 插件无法正常保存文件的问题 _[`issue #92`](http://issues.autojs6.com/91)_ _[`issue #94`](http://issues.autojs6.com/93)_ -* `修复` 异步环境 (by [aiselp](https://github.com/aiselp)) _[`pr #75`](http://pr.autojs6.com/75)_ +* `修复` 浮动按钮菜单项 "管理无障碍服务" 点击后可能未发生页面跳转的问题 +* `修复` runtime.requestPermissions 方法丢失的问题 _[`issue #104`](http://issues.autojs6.com/104)_ +* `修复` events.emitter 不支持 MainThreadProxy 参数的问题 _[`issue #103`](http://issues.autojs6.com/103)_ +* `修复` 在 _[`pr #78`](http://pr.autojs6.com/78)_ 中存在的代码编辑器无法格式化代码的问题 +* `修复` 使用 JavaAdapter 时导致 ClassLoader 调用栈溢出的问题 _[`issue #99`](http://issues.autojs6.com/99)_ _[`issue #110`](http://issues.autojs6.com/110)_ * `优化` 调整模块作用域 (by [aiselp](https://github.com/aiselp)) _[`pr #75`](http://pr.autojs6.com/75)_ _[`pr #78`](http://pr.autojs6.com/78)_ -* `优化` 定时器调用性能 (by [aiselp](https://github.com/aiselp)) _[`pr #75`](http://pr.autojs6.com/75)_ _[`pr #78`](http://pr.autojs6.com/78)_ * `优化` 移除发行版本应用启动时的签名校验 (by [LZX284](https://github.com/LZX284)) _[`pr #81`](http://pr.autojs6.com/81)_ +* `优化` 在 _[`pr #98`](http://pr.autojs6.com/98)_ 基础上的编辑器代码注释功能的行为, 样式及光标位置处理 +* `优化` 在 _[`pr #98`](http://pr.autojs6.com/98)_ 基础上添加代码注释菜单项 * `优化` 适配 VSCode 插件 1.0.6 +* `优化` UiObject#parent 方法增加级数参数支持 (参阅 项目文档 > [控件节点](https://docs.autojs6.com/#/uiObjectType)) # v6.3.2 @@ -355,13 +364,13 @@ Gradle - Update version name and released date - Append related GitHub issues to changelog entries - README.md - - Update badges like [ android studio / rhino / ... ] - Latest changelog was synchronized by which in CHANGELOG.md - - The summary of the latest changelog for committing to Git - Changelog entries are not more than three + - The summary of the latest changelog for committing to Git + - Update badges like [ android studio / rhino / ... ] - Update android studio download links and version names - Remove the part like [ alpha / beta / ... ] of VERSION_NAME in version.properties - - Re-generate documentation by running the python script - Update dependencies information for Android Gradle Plugin in the top-level build.gradle.kts + - Re-generate documentation by running the python script - Build APK to determine the final VERSION_BUILD field ) diff --git a/app/src/main/assets/doc/CHANGELOG.md b/app/src/main/assets/doc/CHANGELOG.md index 440810b2..897072d0 100644 --- a/app/src/main/assets/doc/CHANGELOG.md +++ b/app/src/main/assets/doc/CHANGELOG.md @@ -6,15 +6,25 @@ # v6.3.3 -###### 2023/07/12 +###### 2023/07/21 -* `修复` VSCode 插件在脚本字符总长度超过四位十进制数时无法解析数据的问题 _[`issue #91`](http://issues.autojs6.com/91)_ _[`issue #93`](http://issues.autojs6.com/93)_ +* `新增` 代码编辑器的代码注释功能 (by [抠脚本人](https://github.com/little-alei)) _[`pr #98`](http://pr.autojs6.com/98)_ +* `新增` auto.stateListener 用于无障碍服务连接状态监听 (by [抠脚本人](https://github.com/little-alei)) _[`pr #98`](http://pr.autojs6.com/98)_ +* `新增` UiObject 类型添加 nextSibling / lastChild / offset 等方法 (参阅 项目文档 > [控件节点](https://docs.autojs6.com/#/uiObjectType)) +* `修复` VSCode 插件在脚本字符总长度超过四位十进制数时无法解析数据的问题 _[`issue #91`](http://issues.autojs6.com/91)_ _[`issue #93`](http://issues.autojs6.com/93)_ _[`issue #100`](http://issues.autojs6.com/100)_ _[`issue #109`](http://issues.autojs6.com/109)_ * `修复` VSCode 插件无法正常保存文件的问题 _[`issue #92`](http://issues.autojs6.com/91)_ _[`issue #94`](http://issues.autojs6.com/93)_ -* `修复` 异步环境 (by [aiselp](https://github.com/aiselp)) _[`pr #75`](http://pr.autojs6.com/75)_ +* `修复` 浮动按钮菜单项 "管理无障碍服务" 点击后可能未发生页面跳转的问题 +* `修复` runtime.requestPermissions 方法丢失的问题 _[`issue #104`](http://issues.autojs6.com/104)_ +* `修复` events.emitter 不支持 MainThreadProxy 参数的问题 _[`issue #103`](http://issues.autojs6.com/103)_ +* `修复` 在 _[`pr #78`](http://pr.autojs6.com/78)_ 中存在的代码编辑器无法格式化代码的问题 +* `修复` 使用 JavaAdapter 时导致 ClassLoader 调用栈溢出的问题 _[`issue #99`](http://issues.autojs6.com/99)_ _[`issue #110`](http://issues.autojs6.com/110)_ * `优化` 调整模块作用域 (by [aiselp](https://github.com/aiselp)) _[`pr #75`](http://pr.autojs6.com/75)_ _[`pr #78`](http://pr.autojs6.com/78)_ -* `优化` 定时器调用性能 (by [aiselp](https://github.com/aiselp)) _[`pr #75`](http://pr.autojs6.com/75)_ _[`pr #78`](http://pr.autojs6.com/78)_ * `优化` 移除发行版本应用启动时的签名校验 (by [LZX284](https://github.com/LZX284)) _[`pr #81`](http://pr.autojs6.com/81)_ -* `优化` 升级 Gradle 版本 8.2 -> 8.2.1 +* `优化` 在 _[`pr #98`](http://pr.autojs6.com/98)_ 基础上的编辑器代码注释功能的行为, 样式及光标位置处理 +* `优化` 在 _[`pr #98`](http://pr.autojs6.com/98)_ 基础上添加代码注释菜单项 +* `优化` 适配 VSCode 插件 1.0.6 +* `优化` UiObject#parent 方法增加级数参数支持 (参阅 项目文档 > [控件节点](https://docs.autojs6.com/#/uiObjectType)) +* `优化` 升级 Gradle 版本 8.2 -> 8.3-rc-1 # v6.3.2 diff --git a/app/src/main/assets/docs/404.html b/app/src/main/assets/docs/404.html index 0e433cee..37af334a 100644 --- a/app/src/main/assets/docs/404.html +++ b/app/src/main/assets/docs/404.html @@ -2,7 +2,7 @@ - 页面不存在 | AutoJs6 文档 - 6.3.2 + 页面不存在 | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@
-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/activity.html b/app/src/main/assets/docs/activity.html index 011aa5be..2e5d09fa 100644 --- a/app/src/main/assets/docs/activity.html +++ b/app/src/main/assets/docs/activity.html @@ -2,7 +2,7 @@ - 活动 (Activity) | AutoJs6 文档 - 6.3.2 + 活动 (Activity) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/all.html b/app/src/main/assets/docs/all.html index 6c7c72cd..f0a7e2eb 100644 --- a/app/src/main/assets/docs/all.html +++ b/app/src/main/assets/docs/all.html @@ -2,7 +2,7 @@ - 综述 (Overview) | AutoJs6 文档 - 6.3.2 + 综述 (Overview) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | @@ -4295,7 +4295,7 @@ species.isRegExp(/test$/); // true

[p+] anim#

6.2.0 Global

动画资源.

-

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

+

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

'ui';
 
 ui.layout(<vertical id="main">
@@ -4438,6 +4438,7 @@ 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)
diff --git a/app/src/main/assets/docs/androidBundleType.html b/app/src/main/assets/docs/androidBundleType.html
index 97a276e4..563b41cd 100644
--- a/app/src/main/assets/docs/androidBundleType.html
+++ b/app/src/main/assets/docs/androidBundleType.html
@@ -2,7 +2,7 @@
 
 
     
-    AndroidBundle | AutoJs6 文档 - 6.3.2
+    AndroidBundle | AutoJs6 文档 - 6.3.3
     
     
     
@@ -155,7 +155,7 @@
 
     
-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/androidRectType.html b/app/src/main/assets/docs/androidRectType.html index a448bf4e..39f95192 100644 --- a/app/src/main/assets/docs/androidRectType.html +++ b/app/src/main/assets/docs/androidRectType.html @@ -2,7 +2,7 @@ - AndroidRect | AutoJs6 文档 - 6.3.2 + AndroidRect | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/apiLevel.html b/app/src/main/assets/docs/apiLevel.html index 802392b6..88ccb361 100644 --- a/app/src/main/assets/docs/apiLevel.html +++ b/app/src/main/assets/docs/apiLevel.html @@ -2,7 +2,7 @@ - 安卓 API 级别 (Android API Level) | AutoJs6 文档 - 6.3.2 + 安卓 API 级别 (Android API Level) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/app.html b/app/src/main/assets/docs/app.html index 2308b679..6190f6db 100644 --- a/app/src/main/assets/docs/app.html +++ b/app/src/main/assets/docs/app.html @@ -2,7 +2,7 @@ - 通用应用 (App) | AutoJs6 文档 - 6.3.2 + 通用应用 (App) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/appType.html b/app/src/main/assets/docs/appType.html index a5704b4f..11b62742 100644 --- a/app/src/main/assets/docs/appType.html +++ b/app/src/main/assets/docs/appType.html @@ -2,7 +2,7 @@ - 应用枚举类 (App) | AutoJs6 文档 - 6.3.2 + 应用枚举类 (App) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/arrayx.html b/app/src/main/assets/docs/arrayx.html index 9866b173..a17666f6 100644 --- a/app/src/main/assets/docs/arrayx.html +++ b/app/src/main/assets/docs/arrayx.html @@ -2,7 +2,7 @@ - Arrayx (Array 扩展) | AutoJs6 文档 - 6.3.2 + Arrayx (Array 扩展) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/autojs.html b/app/src/main/assets/docs/autojs.html index b5d1377b..2adf278b 100644 --- a/app/src/main/assets/docs/autojs.html +++ b/app/src/main/assets/docs/autojs.html @@ -2,7 +2,7 @@ - AutoJs6 本体应用 | AutoJs6 文档 - 6.3.2 + AutoJs6 本体应用 | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/automator.html b/app/src/main/assets/docs/automator.html index 5ceeffea..573ef00b 100644 --- a/app/src/main/assets/docs/automator.html +++ b/app/src/main/assets/docs/automator.html @@ -2,7 +2,7 @@ - 自动化 (Automator) | AutoJs6 文档 - 6.3.2 + 自动化 (Automator) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/base64.html b/app/src/main/assets/docs/base64.html index bb3ef930..df6083e5 100644 --- a/app/src/main/assets/docs/base64.html +++ b/app/src/main/assets/docs/base64.html @@ -2,7 +2,7 @@ - Base64 | AutoJs6 文档 - 6.3.2 + Base64 | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/canvas.html b/app/src/main/assets/docs/canvas.html index b0415362..a616fc41 100644 --- a/app/src/main/assets/docs/canvas.html +++ b/app/src/main/assets/docs/canvas.html @@ -2,7 +2,7 @@ - 画布 (Canvas) | AutoJs6 文档 - 6.3.2 + 画布 (Canvas) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | diff --git a/app/src/main/assets/docs/changelog.html b/app/src/main/assets/docs/changelog.html index 868bb38c..c68fa73e 100644 --- a/app/src/main/assets/docs/changelog.html +++ b/app/src/main/assets/docs/changelog.html @@ -2,7 +2,7 @@ - 文档更新日志 (Changelog) | AutoJs6 文档 - 6.3.2 + 文档更新日志 (Changelog) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

-

AutoJs6 文档 - 6.3.2

+

AutoJs6 文档 - 6.3.3

索引 | @@ -169,6 +169,7 @@

目录

  • 文档更新日志 (Changelog)
      +
    • v1.1.6
    • v1.1.5
    • v1.1.4
    • v1.1.3
    • @@ -188,6 +189,12 @@

      文档更新日志 (Changelog)#

      +

      v1.1.6#

      +

      2023/07/21

      + +

      v1.1.5#

      2023/07/06

      diff --git a/app/src/main/assets/docs/color.html b/app/src/main/assets/docs/color.html index 4714c5e9..d67a9bf8 100644 --- a/app/src/main/assets/docs/color.html +++ b/app/src/main/assets/docs/color.html @@ -2,7 +2,7 @@ - 颜色 (Color) | AutoJs6 文档 - 6.3.2 + 颜色 (Color) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@
      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/colorTable.html b/app/src/main/assets/docs/colorTable.html index 6a0f2ceb..80a99229 100644 --- a/app/src/main/assets/docs/colorTable.html +++ b/app/src/main/assets/docs/colorTable.html @@ -2,7 +2,7 @@ - 颜色列表 (Color Table) | AutoJs6 文档 - 6.3.2 + 颜色列表 (Color Table) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/colorType.html b/app/src/main/assets/docs/colorType.html index 900f7b12..2a948a20 100644 --- a/app/src/main/assets/docs/colorType.html +++ b/app/src/main/assets/docs/colorType.html @@ -2,7 +2,7 @@ - Color (颜色类) | AutoJs6 文档 - 6.3.2 + Color (颜色类) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/console.html b/app/src/main/assets/docs/console.html index 45844db5..259c5a21 100644 --- a/app/src/main/assets/docs/console.html +++ b/app/src/main/assets/docs/console.html @@ -2,7 +2,7 @@ - 控制台 (Console) | AutoJs6 文档 - 6.3.2 + 控制台 (Console) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/consoleBuildOptionsType.html b/app/src/main/assets/docs/consoleBuildOptionsType.html index f28539ea..fdfd6fe2 100644 --- a/app/src/main/assets/docs/consoleBuildOptionsType.html +++ b/app/src/main/assets/docs/consoleBuildOptionsType.html @@ -2,7 +2,7 @@ - ConsoleBuildOptions | AutoJs6 文档 - 6.3.2 + ConsoleBuildOptions | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/context.html b/app/src/main/assets/docs/context.html index c07bde03..2964562c 100644 --- a/app/src/main/assets/docs/context.html +++ b/app/src/main/assets/docs/context.html @@ -2,7 +2,7 @@ - 上下文 (Context) | AutoJs6 文档 - 6.3.2 + 上下文 (Context) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/continuation.html b/app/src/main/assets/docs/continuation.html index 00fcdb9b..ec0ddad5 100644 --- a/app/src/main/assets/docs/continuation.html +++ b/app/src/main/assets/docs/continuation.html @@ -2,7 +2,7 @@ - 协程 (Continuation) | AutoJs6 文档 - 6.3.2 + 协程 (Continuation) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/coverpage.html b/app/src/main/assets/docs/coverpage.html index 4a0a6a03..9f2737d0 100644 --- a/app/src/main/assets/docs/coverpage.html +++ b/app/src/main/assets/docs/coverpage.html @@ -2,7 +2,7 @@ - [AutoJs6](http://project.autojs6.com) | AutoJs6 文档 - 6.3.2 + [AutoJs6](http://project.autojs6.com) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/crypto.html b/app/src/main/assets/docs/crypto.html index a2f4afc0..62ed789e 100644 --- a/app/src/main/assets/docs/crypto.html +++ b/app/src/main/assets/docs/crypto.html @@ -2,7 +2,7 @@ - 密文 (Crypto) | AutoJs6 文档 - 6.3.2 + 密文 (Crypto) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/cryptoCipherOptionsType.html b/app/src/main/assets/docs/cryptoCipherOptionsType.html index 6d9ded07..9416aa71 100644 --- a/app/src/main/assets/docs/cryptoCipherOptionsType.html +++ b/app/src/main/assets/docs/cryptoCipherOptionsType.html @@ -2,7 +2,7 @@ - CryptoCipherOptions | AutoJs6 文档 - 6.3.2 + CryptoCipherOptions | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/cryptoKeyPairType.html b/app/src/main/assets/docs/cryptoKeyPairType.html index 2a6efadd..70d1e2ee 100644 --- a/app/src/main/assets/docs/cryptoKeyPairType.html +++ b/app/src/main/assets/docs/cryptoKeyPairType.html @@ -2,7 +2,7 @@ - CryptoKeyPair | AutoJs6 文档 - 6.3.2 + CryptoKeyPair | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/cryptoKeyType.html b/app/src/main/assets/docs/cryptoKeyType.html index eeab75dc..83ef4844 100644 --- a/app/src/main/assets/docs/cryptoKeyType.html +++ b/app/src/main/assets/docs/cryptoKeyType.html @@ -2,7 +2,7 @@ - CryptoKey | AutoJs6 文档 - 6.3.2 + CryptoKey | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/dataTypes.html b/app/src/main/assets/docs/dataTypes.html index a61f6c07..3a3877eb 100644 --- a/app/src/main/assets/docs/dataTypes.html +++ b/app/src/main/assets/docs/dataTypes.html @@ -2,7 +2,7 @@ - 数据类型 (Data Types) | AutoJs6 文档 - 6.3.2 + 数据类型 (Data Types) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/device.html b/app/src/main/assets/docs/device.html index cbaf0ed9..c812b510 100644 --- a/app/src/main/assets/docs/device.html +++ b/app/src/main/assets/docs/device.html @@ -2,7 +2,7 @@ - 设备 (Device) | AutoJs6 文档 - 6.3.2 + 设备 (Device) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/dialogs.html b/app/src/main/assets/docs/dialogs.html index 4d7db366..cb8143da 100644 --- a/app/src/main/assets/docs/dialogs.html +++ b/app/src/main/assets/docs/dialogs.html @@ -2,7 +2,7 @@ - 对话框 (Dialogs) | AutoJs6 文档 - 6.3.2 + 对话框 (Dialogs) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/documentation.html b/app/src/main/assets/docs/documentation.html index e9216e92..5739b22c 100644 --- a/app/src/main/assets/docs/documentation.html +++ b/app/src/main/assets/docs/documentation.html @@ -2,7 +2,7 @@ - 关于文档 (About) | AutoJs6 文档 - 6.3.2 + 关于文档 (About) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/e4x.html b/app/src/main/assets/docs/e4x.html index 84681962..76fbc6fb 100644 --- a/app/src/main/assets/docs/e4x.html +++ b/app/src/main/assets/docs/e4x.html @@ -2,7 +2,7 @@ - E4X | AutoJs6 文档 - 6.3.2 + E4X | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/engines.html b/app/src/main/assets/docs/engines.html index 1be2780b..4707728c 100644 --- a/app/src/main/assets/docs/engines.html +++ b/app/src/main/assets/docs/engines.html @@ -2,7 +2,7 @@ - 引擎 (Engines) | AutoJs6 文档 - 6.3.2 + 引擎 (Engines) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/events.html b/app/src/main/assets/docs/events.html index ad15dc74..93be51f8 100644 --- a/app/src/main/assets/docs/events.html +++ b/app/src/main/assets/docs/events.html @@ -2,7 +2,7 @@ - 事件监听 (Events) | AutoJs6 文档 - 6.3.2 + 事件监听 (Events) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/exceptions.html b/app/src/main/assets/docs/exceptions.html index 3fbd45cc..f4eb6dbb 100644 --- a/app/src/main/assets/docs/exceptions.html +++ b/app/src/main/assets/docs/exceptions.html @@ -2,7 +2,7 @@ - 异常 (Exceptions) | AutoJs6 文档 - 6.3.2 + 异常 (Exceptions) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/files.html b/app/src/main/assets/docs/files.html index 9aab5c9d..9f5ecf12 100644 --- a/app/src/main/assets/docs/files.html +++ b/app/src/main/assets/docs/files.html @@ -2,7 +2,7 @@ - 文件 (Files) | AutoJs6 文档 - 6.3.2 + 文件 (Files) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/floaty.html b/app/src/main/assets/docs/floaty.html index 104b63c1..92047d37 100644 --- a/app/src/main/assets/docs/floaty.html +++ b/app/src/main/assets/docs/floaty.html @@ -2,7 +2,7 @@ - 悬浮窗 (Floaty) | AutoJs6 文档 - 6.3.2 + 悬浮窗 (Floaty) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/global.html b/app/src/main/assets/docs/global.html index 491ebb8b..7061cadc 100644 --- a/app/src/main/assets/docs/global.html +++ b/app/src/main/assets/docs/global.html @@ -2,7 +2,7 @@ - 全局对象 (Global) | AutoJs6 文档 - 6.3.2 + 全局对象 (Global) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | @@ -1976,7 +1976,7 @@ species.isRegExp(/test$/); // true

      [p+] anim#

      6.2.0 Global

      动画资源.

      -

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

      +

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

      'ui';
       
       ui.layout(<vertical id="main">
      @@ -2119,6 +2119,7 @@ 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)
      diff --git a/app/src/main/assets/docs/glossaries.html b/app/src/main/assets/docs/glossaries.html
      index 7c628fe8..c446120c 100644
      --- a/app/src/main/assets/docs/glossaries.html
      +++ b/app/src/main/assets/docs/glossaries.html
      @@ -2,7 +2,7 @@
       
       
           
      -    术语 (Glossaries) | AutoJs6 文档 - 6.3.2
      +    术语 (Glossaries) | AutoJs6 文档 - 6.3.3
           
           
           
      @@ -155,7 +155,7 @@
       
           
      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/http.html b/app/src/main/assets/docs/http.html index 672772c8..a1544886 100644 --- a/app/src/main/assets/docs/http.html +++ b/app/src/main/assets/docs/http.html @@ -2,7 +2,7 @@ - HTTP | AutoJs6 文档 - 6.3.2 + HTTP | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/httpHeaderGlossary.html b/app/src/main/assets/docs/httpHeaderGlossary.html index becc3110..1b89433f 100644 --- a/app/src/main/assets/docs/httpHeaderGlossary.html +++ b/app/src/main/assets/docs/httpHeaderGlossary.html @@ -2,7 +2,7 @@ - HTTP Header (HTTP 标头) | AutoJs6 文档 - 6.3.2 + HTTP Header (HTTP 标头) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/httpRequestBuilderOptionsType.html b/app/src/main/assets/docs/httpRequestBuilderOptionsType.html index 330dd1e0..2b621e28 100644 --- a/app/src/main/assets/docs/httpRequestBuilderOptionsType.html +++ b/app/src/main/assets/docs/httpRequestBuilderOptionsType.html @@ -2,7 +2,7 @@ - HttpRequestBuilderOptions | AutoJs6 文档 - 6.3.2 + HttpRequestBuilderOptions | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/httpRequestHeadersType.html b/app/src/main/assets/docs/httpRequestHeadersType.html index d2aff8de..7b0bcd1d 100644 --- a/app/src/main/assets/docs/httpRequestHeadersType.html +++ b/app/src/main/assets/docs/httpRequestHeadersType.html @@ -2,7 +2,7 @@ - HttpRequestHeaders | AutoJs6 文档 - 6.3.2 + HttpRequestHeaders | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/httpRequestMethodsGlossary.html b/app/src/main/assets/docs/httpRequestMethodsGlossary.html index 34ea1561..688cf75d 100644 --- a/app/src/main/assets/docs/httpRequestMethodsGlossary.html +++ b/app/src/main/assets/docs/httpRequestMethodsGlossary.html @@ -2,7 +2,7 @@ - HTTP Request Methods (HTTP 请求方法) | AutoJs6 文档 - 6.3.2 + HTTP Request Methods (HTTP 请求方法) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/httpResponseBodyType.html b/app/src/main/assets/docs/httpResponseBodyType.html index f3adab91..901b818e 100644 --- a/app/src/main/assets/docs/httpResponseBodyType.html +++ b/app/src/main/assets/docs/httpResponseBodyType.html @@ -2,7 +2,7 @@ - HttpResponseBodyType | AutoJs6 文档 - 6.3.2 + HttpResponseBodyType | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/httpResponseHeadersType.html b/app/src/main/assets/docs/httpResponseHeadersType.html index 5a67415f..35dd484f 100644 --- a/app/src/main/assets/docs/httpResponseHeadersType.html +++ b/app/src/main/assets/docs/httpResponseHeadersType.html @@ -2,7 +2,7 @@ - HttpResponseHeaders | AutoJs6 文档 - 6.3.2 + HttpResponseHeaders | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/httpResponseType.html b/app/src/main/assets/docs/httpResponseType.html index 0d62bd66..b2667a12 100644 --- a/app/src/main/assets/docs/httpResponseType.html +++ b/app/src/main/assets/docs/httpResponseType.html @@ -2,7 +2,7 @@ - HttpResponse | AutoJs6 文档 - 6.3.2 + HttpResponse | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/i18n.html b/app/src/main/assets/docs/i18n.html index e5d89ef6..89d99a23 100644 --- a/app/src/main/assets/docs/i18n.html +++ b/app/src/main/assets/docs/i18n.html @@ -2,7 +2,7 @@ - 国际化 (Internationalization) | AutoJs6 文档 - 6.3.2 + 国际化 (Internationalization) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/image.html b/app/src/main/assets/docs/image.html index e30a6dd8..10bd712f 100644 --- a/app/src/main/assets/docs/image.html +++ b/app/src/main/assets/docs/image.html @@ -2,7 +2,7 @@ - 图像 (Images) | AutoJs6 文档 - 6.3.2 + 图像 (Images) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/imageWrapper.html b/app/src/main/assets/docs/imageWrapper.html index 73275f7d..903d3803 100644 --- a/app/src/main/assets/docs/imageWrapper.html +++ b/app/src/main/assets/docs/imageWrapper.html @@ -2,7 +2,7 @@ - 包装图像类 (ImageWrapper) | AutoJs6 文档 - 6.3.2 + 包装图像类 (ImageWrapper) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/index.html b/app/src/main/assets/docs/index.html index 393866b9..0a464fab 100644 --- a/app/src/main/assets/docs/index.html +++ b/app/src/main/assets/docs/index.html @@ -2,7 +2,7 @@ - Index | AutoJs6 文档 - 6.3.2 + Index | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/injectableWebClientType.html b/app/src/main/assets/docs/injectableWebClientType.html index 904bef79..c5d1acb1 100644 --- a/app/src/main/assets/docs/injectableWebClientType.html +++ b/app/src/main/assets/docs/injectableWebClientType.html @@ -2,7 +2,7 @@ - InjectableWebClient | AutoJs6 文档 - 6.3.2 + InjectableWebClient | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/injectableWebViewType.html b/app/src/main/assets/docs/injectableWebViewType.html index b8dd71ee..6c405950 100644 --- a/app/src/main/assets/docs/injectableWebViewType.html +++ b/app/src/main/assets/docs/injectableWebViewType.html @@ -2,7 +2,7 @@ - InjectableWebView | AutoJs6 文档 - 6.3.2 + InjectableWebView | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/intentType.html b/app/src/main/assets/docs/intentType.html index 34bec768..9e246a2d 100644 --- a/app/src/main/assets/docs/intentType.html +++ b/app/src/main/assets/docs/intentType.html @@ -2,7 +2,7 @@ - 意图 (Intent) | AutoJs6 文档 - 6.3.2 + 意图 (Intent) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/keys.html b/app/src/main/assets/docs/keys.html index ff4e2e0f..40ebfc2b 100644 --- a/app/src/main/assets/docs/keys.html +++ b/app/src/main/assets/docs/keys.html @@ -2,7 +2,7 @@ - 按键 (Keys) | AutoJs6 文档 - 6.3.2 + 按键 (Keys) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/manual.html b/app/src/main/assets/docs/manual.html index f955af56..30f1e601 100644 --- a/app/src/main/assets/docs/manual.html +++ b/app/src/main/assets/docs/manual.html @@ -2,7 +2,7 @@ - AutoJs6 使用手册 (Manual) | AutoJs6 文档 - 6.3.2 + AutoJs6 使用手册 (Manual) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/mathx.html b/app/src/main/assets/docs/mathx.html index dc2b9f6e..828e8332 100644 --- a/app/src/main/assets/docs/mathx.html +++ b/app/src/main/assets/docs/mathx.html @@ -2,7 +2,7 @@ - Mathx (Math 扩展) | AutoJs6 文档 - 6.3.2 + Mathx (Math 扩展) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/media.html b/app/src/main/assets/docs/media.html index 5f3e71a1..25dd9b9e 100644 --- a/app/src/main/assets/docs/media.html +++ b/app/src/main/assets/docs/media.html @@ -2,7 +2,7 @@ - 多媒体 (Media) | AutoJs6 文档 - 6.3.2 + 多媒体 (Media) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/mimeTypeGlossary.html b/app/src/main/assets/docs/mimeTypeGlossary.html index cd1cf4df..62367eef 100644 --- a/app/src/main/assets/docs/mimeTypeGlossary.html +++ b/app/src/main/assets/docs/mimeTypeGlossary.html @@ -2,7 +2,7 @@ - MIME Type (MIME 类型) | AutoJs6 文档 - 6.3.2 + MIME Type (MIME 类型) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/modules.html b/app/src/main/assets/docs/modules.html index 72c1709f..38d0255d 100644 --- a/app/src/main/assets/docs/modules.html +++ b/app/src/main/assets/docs/modules.html @@ -2,7 +2,7 @@ - 模块 (Module) | AutoJs6 文档 - 6.3.2 + 模块 (Module) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/notice.html b/app/src/main/assets/docs/notice.html index fc4011cd..9a6dc02b 100644 --- a/app/src/main/assets/docs/notice.html +++ b/app/src/main/assets/docs/notice.html @@ -2,7 +2,7 @@ - 消息通知 (Notice) | AutoJs6 文档 - 6.3.2 + 消息通知 (Notice) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/noticeBuilderType.html b/app/src/main/assets/docs/noticeBuilderType.html index fcf49834..e7efc5a1 100644 --- a/app/src/main/assets/docs/noticeBuilderType.html +++ b/app/src/main/assets/docs/noticeBuilderType.html @@ -2,7 +2,7 @@ - NoticeBuilder | AutoJs6 文档 - 6.3.2 + NoticeBuilder | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/noticeChannelOptionsType.html b/app/src/main/assets/docs/noticeChannelOptionsType.html index a9802575..3ad25617 100644 --- a/app/src/main/assets/docs/noticeChannelOptionsType.html +++ b/app/src/main/assets/docs/noticeChannelOptionsType.html @@ -2,7 +2,7 @@ - NoticeChannelOptions | AutoJs6 文档 - 6.3.2 + NoticeChannelOptions | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/noticeOptionsType.html b/app/src/main/assets/docs/noticeOptionsType.html index f7c2e056..a0b5987f 100644 --- a/app/src/main/assets/docs/noticeOptionsType.html +++ b/app/src/main/assets/docs/noticeOptionsType.html @@ -2,7 +2,7 @@ - NoticeOptions | AutoJs6 文档 - 6.3.2 + NoticeOptions | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/noticePresetConfigurationType.html b/app/src/main/assets/docs/noticePresetConfigurationType.html index a100feb1..dcff94d1 100644 --- a/app/src/main/assets/docs/noticePresetConfigurationType.html +++ b/app/src/main/assets/docs/noticePresetConfigurationType.html @@ -2,7 +2,7 @@ - NoticePresetConfiguration | AutoJs6 文档 - 6.3.2 + NoticePresetConfiguration | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/notificationChannelGlossary.html b/app/src/main/assets/docs/notificationChannelGlossary.html index 1a750ac3..e1f6a765 100644 --- a/app/src/main/assets/docs/notificationChannelGlossary.html +++ b/app/src/main/assets/docs/notificationChannelGlossary.html @@ -2,7 +2,7 @@ - Notification Channel (通知渠道) | AutoJs6 文档 - 6.3.2 + Notification Channel (通知渠道) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/numberx.html b/app/src/main/assets/docs/numberx.html index 410c64e9..79fd4fad 100644 --- a/app/src/main/assets/docs/numberx.html +++ b/app/src/main/assets/docs/numberx.html @@ -2,7 +2,7 @@ - Numberx (Number 扩展) | AutoJs6 文档 - 6.3.2 + Numberx (Number 扩展) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/ocr.html b/app/src/main/assets/docs/ocr.html index 677f8eda..71d5af20 100644 --- a/app/src/main/assets/docs/ocr.html +++ b/app/src/main/assets/docs/ocr.html @@ -2,7 +2,7 @@ - 光学字符识别 (OCR) | AutoJs6 文档 - 6.3.2 + 光学字符识别 (OCR) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/ocrOptionsType.html b/app/src/main/assets/docs/ocrOptionsType.html index 2ff65ebb..a02ef4c2 100644 --- a/app/src/main/assets/docs/ocrOptionsType.html +++ b/app/src/main/assets/docs/ocrOptionsType.html @@ -2,7 +2,7 @@ - OcrOptions | AutoJs6 文档 - 6.3.2 + OcrOptions | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/okhttp3HttpUrlType.html b/app/src/main/assets/docs/okhttp3HttpUrlType.html index 73d4e275..e106cb32 100644 --- a/app/src/main/assets/docs/okhttp3HttpUrlType.html +++ b/app/src/main/assets/docs/okhttp3HttpUrlType.html @@ -2,7 +2,7 @@ - Okhttp3HttpUrlType | AutoJs6 文档 - 6.3.2 + Okhttp3HttpUrlType | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/okhttp3RequestType.html b/app/src/main/assets/docs/okhttp3RequestType.html index 89da778c..6300de40 100644 --- a/app/src/main/assets/docs/okhttp3RequestType.html +++ b/app/src/main/assets/docs/okhttp3RequestType.html @@ -2,7 +2,7 @@ - Okhttp3Request | AutoJs6 文档 - 6.3.2 + Okhttp3Request | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/omniTypes.html b/app/src/main/assets/docs/omniTypes.html index 50bee10d..ca980c77 100644 --- a/app/src/main/assets/docs/omniTypes.html +++ b/app/src/main/assets/docs/omniTypes.html @@ -2,7 +2,7 @@ - 全能类型 (Omnipotent Types) | AutoJs6 文档 - 6.3.2 + 全能类型 (Omnipotent Types) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/opencvPointType.html b/app/src/main/assets/docs/opencvPointType.html index 1c4423a3..8b957e64 100644 --- a/app/src/main/assets/docs/opencvPointType.html +++ b/app/src/main/assets/docs/opencvPointType.html @@ -2,7 +2,7 @@ - OpencvPoint | AutoJs6 文档 - 6.3.2 + OpencvPoint | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/opencvRectType.html b/app/src/main/assets/docs/opencvRectType.html index de73f23d..8220f2ec 100644 --- a/app/src/main/assets/docs/opencvRectType.html +++ b/app/src/main/assets/docs/opencvRectType.html @@ -2,7 +2,7 @@ - OpencvRect | AutoJs6 文档 - 6.3.2 + OpencvRect | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/opencvSizeType.html b/app/src/main/assets/docs/opencvSizeType.html index 09eb8c91..67e6ed81 100644 --- a/app/src/main/assets/docs/opencvSizeType.html +++ b/app/src/main/assets/docs/opencvSizeType.html @@ -2,7 +2,7 @@ - OpencvSize | AutoJs6 文档 - 6.3.2 + OpencvSize | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/overview.html b/app/src/main/assets/docs/overview.html index 77b909cf..a74e8671 100644 --- a/app/src/main/assets/docs/overview.html +++ b/app/src/main/assets/docs/overview.html @@ -2,7 +2,7 @@ - 综述 (Overview) | AutoJs6 文档 - 6.3.2 + 综述 (Overview) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/plugins.html b/app/src/main/assets/docs/plugins.html index 63531185..ebbd6d69 100644 --- a/app/src/main/assets/docs/plugins.html +++ b/app/src/main/assets/docs/plugins.html @@ -2,7 +2,7 @@ - 插件 (Plugins) | AutoJs6 文档 - 6.3.2 + 插件 (Plugins) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/polyfill.html b/app/src/main/assets/docs/polyfill.html index d62d2bf8..ffba2088 100644 --- a/app/src/main/assets/docs/polyfill.html +++ b/app/src/main/assets/docs/polyfill.html @@ -2,7 +2,7 @@ - 代码填泥 (Polyfill) | AutoJs6 文档 - 6.3.2 + 代码填泥 (Polyfill) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/progress.html b/app/src/main/assets/docs/progress.html index a2b40de6..fddc2e07 100644 --- a/app/src/main/assets/docs/progress.html +++ b/app/src/main/assets/docs/progress.html @@ -2,7 +2,7 @@ - 文档部署进度 (Progress) | AutoJs6 文档 - 6.3.2 + 文档部署进度 (Progress) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | @@ -315,7 +315,7 @@ Web - 万维网 -√ +> 80% HTTP diff --git a/app/src/main/assets/docs/qa.html b/app/src/main/assets/docs/qa.html index f2694ff4..224798da 100644 --- a/app/src/main/assets/docs/qa.html +++ b/app/src/main/assets/docs/qa.html @@ -2,7 +2,7 @@ - 疑难解答 (Q & A) | AutoJs6 文档 - 6.3.2 + 疑难解答 (Q & A) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/recorder.html b/app/src/main/assets/docs/recorder.html index ebcb23df..a3543e61 100644 --- a/app/src/main/assets/docs/recorder.html +++ b/app/src/main/assets/docs/recorder.html @@ -2,7 +2,7 @@ - 记录器 (Recorder) | AutoJs6 文档 - 6.3.2 + 记录器 (Recorder) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/runtime.html b/app/src/main/assets/docs/runtime.html index d1ea7f0d..40e360aa 100644 --- a/app/src/main/assets/docs/runtime.html +++ b/app/src/main/assets/docs/runtime.html @@ -2,7 +2,7 @@ - 运行时 (Runtime) | AutoJs6 文档 - 6.3.2 + 运行时 (Runtime) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/s13n.html b/app/src/main/assets/docs/s13n.html index 2d009250..835a38c4 100644 --- a/app/src/main/assets/docs/s13n.html +++ b/app/src/main/assets/docs/s13n.html @@ -2,7 +2,7 @@ - 标准化 (Standardization) | AutoJs6 文档 - 6.3.2 + 标准化 (Standardization) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/scriptingJava.html b/app/src/main/assets/docs/scriptingJava.html index 9b57f541..1dda24f4 100644 --- a/app/src/main/assets/docs/scriptingJava.html +++ b/app/src/main/assets/docs/scriptingJava.html @@ -2,7 +2,7 @@ - 脚本化 Java | AutoJs6 文档 - 6.3.2 + 脚本化 Java | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/sensors.html b/app/src/main/assets/docs/sensors.html index 5cf5880d..3d23aa28 100644 --- a/app/src/main/assets/docs/sensors.html +++ b/app/src/main/assets/docs/sensors.html @@ -2,7 +2,7 @@ - 传感器 (Sensors) | AutoJs6 文档 - 6.3.2 + 传感器 (Sensors) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/shell.html b/app/src/main/assets/docs/shell.html index fb711908..18c9c900 100644 --- a/app/src/main/assets/docs/shell.html +++ b/app/src/main/assets/docs/shell.html @@ -2,7 +2,7 @@ - Shell | AutoJs6 文档 - 6.3.2 + Shell | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/sidebar.html b/app/src/main/assets/docs/sidebar.html index c85ea693..ab9d091e 100644 --- a/app/src/main/assets/docs/sidebar.html +++ b/app/src/main/assets/docs/sidebar.html @@ -2,7 +2,7 @@ - Index | AutoJs6 文档 - 6.3.2 + Index | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/storageType.html b/app/src/main/assets/docs/storageType.html index b2fa7131..a5d86cbb 100644 --- a/app/src/main/assets/docs/storageType.html +++ b/app/src/main/assets/docs/storageType.html @@ -2,7 +2,7 @@ - Storage (存储类) | AutoJs6 文档 - 6.3.2 + Storage (存储类) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/storages.html b/app/src/main/assets/docs/storages.html index 3b772f5c..af96149f 100644 --- a/app/src/main/assets/docs/storages.html +++ b/app/src/main/assets/docs/storages.html @@ -2,7 +2,7 @@ - 存储 (Storages) | AutoJs6 文档 - 6.3.2 + 存储 (Storages) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/tasks.html b/app/src/main/assets/docs/tasks.html index 00f79efa..bcef075f 100644 --- a/app/src/main/assets/docs/tasks.html +++ b/app/src/main/assets/docs/tasks.html @@ -2,7 +2,7 @@ - 任务 (Tasks) | AutoJs6 文档 - 6.3.2 + 任务 (Tasks) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/threads.html b/app/src/main/assets/docs/threads.html index c06b967b..eec8a63d 100644 --- a/app/src/main/assets/docs/threads.html +++ b/app/src/main/assets/docs/threads.html @@ -2,7 +2,7 @@ - 线程 (Threads) | AutoJs6 文档 - 6.3.2 + 线程 (Threads) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/timers.html b/app/src/main/assets/docs/timers.html index ef68fdab..29de6728 100644 --- a/app/src/main/assets/docs/timers.html +++ b/app/src/main/assets/docs/timers.html @@ -2,7 +2,7 @@ - 定时器 (Timers) | AutoJs6 文档 - 6.3.2 + 定时器 (Timers) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/toast.html b/app/src/main/assets/docs/toast.html index 92bb3f32..ebd6f08e 100644 --- a/app/src/main/assets/docs/toast.html +++ b/app/src/main/assets/docs/toast.html @@ -2,7 +2,7 @@ - 消息浮动框 (Toast) | AutoJs6 文档 - 6.3.2 + 消息浮动框 (Toast) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/ui.html b/app/src/main/assets/docs/ui.html index fb506147..bd70f269 100644 --- a/app/src/main/assets/docs/ui.html +++ b/app/src/main/assets/docs/ui.html @@ -2,7 +2,7 @@ - 用户界面 (UI) | AutoJs6 文档 - 6.3.2 + 用户界面 (UI) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/uiObjectActionsType.html b/app/src/main/assets/docs/uiObjectActionsType.html index 589a4a92..fa5175a3 100644 --- a/app/src/main/assets/docs/uiObjectActionsType.html +++ b/app/src/main/assets/docs/uiObjectActionsType.html @@ -2,7 +2,7 @@ - 控件节点行为 (UiObjectActions) | AutoJs6 文档 - 6.3.2 + 控件节点行为 (UiObjectActions) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/uiObjectCollectionType.html b/app/src/main/assets/docs/uiObjectCollectionType.html index bce4d1bc..76fca32d 100644 --- a/app/src/main/assets/docs/uiObjectCollectionType.html +++ b/app/src/main/assets/docs/uiObjectCollectionType.html @@ -2,7 +2,7 @@ - 控件集合 (UiObjectCollection) | AutoJs6 文档 - 6.3.2 + 控件集合 (UiObjectCollection) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/uiObjectType.html b/app/src/main/assets/docs/uiObjectType.html index 696176ba..ee129df8 100644 --- a/app/src/main/assets/docs/uiObjectType.html +++ b/app/src/main/assets/docs/uiObjectType.html @@ -2,7 +2,7 @@ - 控件节点 (UiObject) | AutoJs6 文档 - 6.3.2 + 控件节点 (UiObject) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      [m#] parent#

      -

      parent()#

      -

      A11Y

      +

      parent(i?)#

      +

      [6.3.3] A11Y Overload [1-2]/2

      返回其父控件.

      +

      当指定级数 i 时, 返回其对应级数的父控件.

      +

      i0 时, 返回控件自身,
      i 为正整数时, 返回第 i 级父控件,
      i 为负数时, 将抛出异常.

      let w = pickup(/.+/);
       w.parent();
      +w.parent(1); /* 同上. */
       w.compass('p'); /* 同上. */
       detect(w, 'p'); /* 同上. */
       
       w.parent().parent().parent();
      +w.parent(3); /* 同上. */
       w.compass('p3'); /* 同上. */
       detect(w, 'p3'); /* 同上. */
       

      [m#] child#

      child(i)#

      -

      A11Y

      +

      [6.3.3] A11Y

      -

      返回其索引为 i 的子控件.

      +

      返回其索引为 i 的子控件.

      +

      i 为正整数或 0, 返回正数索引子控件,
      i 为负整数, 返回倒数索引子控件,

      let w = pickup(/.+/);
       w.child(3);
       w.compass('c3'); /* 同上. */
      @@ -712,7 +762,48 @@ detect(w, 'c3'); /* 同上. */
       w.child(3).child(1);
       w.compass('c3c1'); /* 同上. */
       detect(w, 'c3>1'); /* 同上. */
      +
      +w.child(-1); /* 最后一个子控件. */
      +
      +w.child(-2); /* 倒数第 2 个子控件. */
      +w.compass('c-2'); /* 同上. */
      +detect(w, 'c-2'); /* 同上. */
       
      +

      [m#] firstChild#

      +

      firstChild()#

      +

      6.3.3 A11Y

      + +

      返回第一个子控件.

      +

      相当于 child(0).

      +

      [m#] lastChild#

      +

      lastChild()#

      +

      6.3.3 A11Y

      + +

      返回最后一个子控件.

      +

      相当于 child(-1).

      +

      [m#] childCount#

      +

      childCount()#

      +

      A11Y

      + +

      返回当前节点的子控件数量.

      +

      别名属性或方法:

      +
        +
      • [m#] getChildCount
      • +
      +

      [m#] hasChildren#

      +

      hasChildren()#

      +

      6.2.0 A11Y

      + +

      返回当前节点是否有子节点.

      +

      相当于 childCount() > 0.

      [m#] children#

      children()#

      A11Y

      @@ -733,25 +824,100 @@ cc.forEach((w) => {
      let w = pickup({ filter: w => w.children().length > 5 });
       console.log(w.find().length); /* e.g. 20 */
       
      -

      [m#] childCount#

      -

      childCount()#

      -

      A11Y

      +

      [m#] sibling#

      +

      sibling(i)#

      +

      6.3.3 A11Y

      + +

      返回其索引为 i 的兄弟控件.

      +

      i 为正整数或 0, 返回正数索引兄弟控件,
      i 为负整数, 返回倒数索引兄弟控件,

      +

      iindexInParent() 相同时, 返回其自身.

      +
      let w = pickup(/.+/);
      +w.sibling(0); /* 第 1 (索引为 0) 的兄弟控件. */
      +w.compass('s0'); /* 同上. */
      +detect(w, 's0'); /* 同上. */
      +
      +w.sibling(-2); /* 倒数第 2 个兄弟控件. */
      +w.compass('s-2'); /* 同上. */
      +detect(w, 's-2'); /* 同上. */
      +
      +

      如需获取相邻的兄弟控件, 可使用 offset, 或使用 nextSiblingpreviousSibling.

      +

      [m#] firstSibling#

      +

      firstSibling()#

      +

      6.3.3 A11Y

      + +

      返回第一个兄弟控件 (可能为自身).

      +

      相当于 sibling(0).

      +

      [m#] lastSibling#

      +

      lastSibling()#

      +

      6.3.3 A11Y

      + +

      返回最后一个兄弟控件 (可能为自身).

      +

      相当于 sibling(-1).

      +

      [m#] offset#

      +

      offset(i)#

      +

      6.3.3 A11Y

      + +

      返回其索引偏移量为 i 的兄弟控件.

      +

      i 为正整数, 返回后向兄弟控件,
      i 为负整数, 返回前向兄弟控件,
      i0, 返回当前控件自身.

      +
      let w = pickup(/.+/);
      +w.offset(3);
      +w.compass('s>3'); /* 同上. */
      +detect(w, 's>3'); /* 同上. */
      +
      +w.offset(-2);
      +w.compass('s<2'); /* 同上. */
      +detect(w, 's<2'); /* 同上. */
      +
      +

      如需获取相邻的兄弟控件, 除 offset 外, 还可使用 nextSiblingpreviousSibling.

      +

      [m#] nextSibling#

      +

      nextSibling()#

      +

      6.3.3 A11Y

      + +

      返回后一个兄弟控件.

      +

      相当于 offset(1).

      +

      [m#] previousSibling#

      +

      previousSibling()#

      +

      6.3.3 A11Y

      + +

      返回前一个兄弟控件.

      +

      相当于 offset(-1).

      +

      [m#] siblingCount#

      +

      siblingCount()#

      +

      6.3.3 A11Y

      -

      返回当前节点的子控件数量.

      -

      别名属性或方法:

      -
        -
      • [m#] getChildCount
      • -
      -

      [m#] hasChildren#

      -

      hasChildren()#

      -

      6.2.0 A11Y

      +

      返回当前节点的兄弟控件总数量 (含自身).

      +

      siblingCount 返回一个总是大于等于 1 的数字.

      +

      [m#] isSingleton#

      +

      isSingleton()#

      +

      6.3.3 A11Y

      -

      返回当前节点是否有子节点.

      -

      相当于 childCount() > 0.

      +

      返回当前节点是否为独身节点, 即除自身外没有其他兄弟节点.

      +

      相当于 siblingCount() === 1.

      +

      [m#] siblings#

      +

      siblings()#

      +

      6.3.3 A11Y

      + +

      返回当前节点的兄弟控件集合 (含自身).

      [m#] indexInParent#

      indexInParent()#

      A11Y

      diff --git a/app/src/main/assets/docs/uiSelectorType.html b/app/src/main/assets/docs/uiSelectorType.html index cfdb1aeb..43c57d46 100644 --- a/app/src/main/assets/docs/uiSelectorType.html +++ b/app/src/main/assets/docs/uiSelectorType.html @@ -2,7 +2,7 @@ - 选择器 (UiSelector) | AutoJs6 文档 - 6.3.2 + 选择器 (UiSelector) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@
      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/util.html b/app/src/main/assets/docs/util.html index a93d2e72..46094d3e 100644 --- a/app/src/main/assets/docs/util.html +++ b/app/src/main/assets/docs/util.html @@ -2,7 +2,7 @@ - Util | AutoJs6 文档 - 6.3.2 + Util | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/versionType.html b/app/src/main/assets/docs/versionType.html index dff6fe9e..e0b3346d 100644 --- a/app/src/main/assets/docs/versionType.html +++ b/app/src/main/assets/docs/versionType.html @@ -2,7 +2,7 @@ - 版本工具类 (Version) | AutoJs6 文档 - 6.3.2 + 版本工具类 (Version) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/assets/docs/web.html b/app/src/main/assets/docs/web.html index a1bbcfc3..5b8c5f54 100644 --- a/app/src/main/assets/docs/web.html +++ b/app/src/main/assets/docs/web.html @@ -2,7 +2,7 @@ - 万维网 (Web) | AutoJs6 文档 - 6.3.2 + 万维网 (Web) | AutoJs6 文档 - 6.3.3 @@ -155,7 +155,7 @@

      -

      AutoJs6 文档 - 6.3.2

      +

      AutoJs6 文档 - 6.3.3

      索引 | diff --git a/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityService.kt b/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityService.kt index 2a412561..e26777e1 100644 --- a/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityService.kt +++ b/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityService.kt @@ -11,16 +11,13 @@ import android.view.accessibility.AccessibilityNodeInfo import org.autojs.autojs.core.accessibility.SimpleActionAutomator.Companion.AccessibilityEventCallback import org.autojs.autojs.core.automator.AccessibilityEventWrapper import org.autojs.autojs.event.EventDispatcher +import org.autojs.autojs.pref.Language import org.autojs.autojs.pref.Pref import java.util.* import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantLock -interface AccessibilityServiceCallback { - fun onConnected() - fun onDisconnected() -} /** * Created by Stardust on 2017/5/2. */ @@ -35,15 +32,13 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ private val eventExecutor by lazy { Executors.newSingleThreadExecutor() } - private fun eventNameToType(str: String): Int { + private fun eventNameToType(event: String): Int { return try { - val sb = StringBuilder() - sb.append("TYPE_") - val upperCase = str.uppercase(Locale.getDefault()) - sb.append(upperCase) - AccessibilityEvent::class.java.getField(sb.toString()).get(null) as Int + AccessibilityEvent::class.java.getField( + "TYPE_${event.uppercase(Language.getPrefLanguage().locale)}" + ).get(null) as Int } catch (unused: NoSuchFieldException) { - throw IllegalArgumentException("unknown event type: $str") + throw IllegalArgumentException("Unknown event: $event") } } @@ -166,7 +161,8 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ fun isNotRunning() = !isRunning() fun addDelegate(uniquePriority: Int, delegate: AccessibilityDelegate) { - // 用于记录eventTypes中的事件id + // @Hint by 抠脚本人 on Jul 10, 2023. + // ! 用于记录 eventTypes 中的事件 id. delegates[uniquePriority] = delegate val set = delegate.eventTypes if (set == null) { @@ -204,10 +200,12 @@ open class AccessibilityService : android.accessibilityservice.AccessibilityServ LOCK.unlock() } } + @JvmStatic fun clearAccessibilityEventCallback() { instance?.eventBox?.clear() } + fun setCallback(listener: AccessibilityServiceCallback) { callback = listener } diff --git a/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityServiceCallback.kt b/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityServiceCallback.kt new file mode 100644 index 00000000..092db2e4 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityServiceCallback.kt @@ -0,0 +1,9 @@ +package org.autojs.autojs.core.accessibility + +/** + * Created by 抠脚本人 on Jul 10, 2023. + */ +interface AccessibilityServiceCallback { + fun onConnected() + fun onDisconnected() +} \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityTool.kt b/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityTool.kt index f54852cf..aec67b23 100644 --- a/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityTool.kt +++ b/app/src/main/java/org/autojs/autojs/core/accessibility/AccessibilityTool.kt @@ -43,6 +43,10 @@ class AccessibilityTool(val context: Context) { enableIfNeeded().also { if (!it) launchSettings() } } + fun launchSettings() { + this@AccessibilityTool.launchSettings() + } + fun enableIfNeeded() = when { enableWithRootIfNeeded() -> true enableWithSecureIfNeeded() -> true diff --git a/app/src/main/java/org/autojs/autojs/core/accessibility/SimpleActionAutomator.kt b/app/src/main/java/org/autojs/autojs/core/accessibility/SimpleActionAutomator.kt index 006fed56..12b99eed 100644 --- a/app/src/main/java/org/autojs/autojs/core/accessibility/SimpleActionAutomator.kt +++ b/app/src/main/java/org/autojs/autojs/core/accessibility/SimpleActionAutomator.kt @@ -182,7 +182,9 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge @ScriptInterface fun ensureService() = accessibilityBridge.ensureServiceEnabled() - //todo:优化实现方式 + // @Created by 抠脚本人 on Jul 10, 2023. + // TODO by 抠脚本人 on Jul 10, 2023. + // ! 优化实现方式 // TODO by SuperMonster003 on Jul 12, 2023. // ! Ref to Auto.js Pro fun registerEvent(eventName: String, callback: AccessibilityEventCallback) { @@ -190,6 +192,7 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge AccessibilityService.instance?.addAccessibilityEventCallback(eventName, callback) } + // @Created by 抠脚本人 on Jul 10, 2023. fun removeEvent(eventName: String) { AccessibilityService.instance?.removeAccessibilityEventCallback(eventName) } @@ -251,12 +254,17 @@ class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge } companion object { + val accessibilityDelegateCounter = AtomicInteger(1000) val TAG: String = SimpleActionAutomator::class.java.name + /** + * Created by 抠脚本人 on Jul 10, 2023. + */ interface AccessibilityEventCallback { fun onAccessibilityEvent(event: AccessibilityEventWrapper) } + } } diff --git a/app/src/main/java/org/autojs/autojs/core/automator/AccessibilityEventWrapper.kt b/app/src/main/java/org/autojs/autojs/core/automator/AccessibilityEventWrapper.kt index 5e1bea68..f72a42d4 100644 --- a/app/src/main/java/org/autojs/autojs/core/automator/AccessibilityEventWrapper.kt +++ b/app/src/main/java/org/autojs/autojs/core/automator/AccessibilityEventWrapper.kt @@ -4,7 +4,7 @@ import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo /** - * Created by 抠脚本人 on Jul 10, 2023. + * Created by 抠脚本人 (https://github.com/little-alei) on Jul 10, 2023. */ class AccessibilityEventWrapper(event: AccessibilityEvent) { val raw = event diff --git a/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt b/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt index bcaf88f7..53952f3c 100644 --- a/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt +++ b/app/src/main/java/org/autojs/autojs/core/automator/UiObject.kt @@ -40,15 +40,18 @@ open class UiObject( private val bounds by lazy { AccessibilityNodeInfoHelper.getBoundsInScreen(this) } - constructor(info: Any?, allocator: AccessibilityNodeInfoAllocator, indexInParent: Int) : this( - info, - allocator, - 0, - indexInParent - ) + constructor( + info: Any?, + allocator: AccessibilityNodeInfoAllocator, + indexInParent: Int, + ) : this(info, allocator, 0, indexInParent) @JvmOverloads - constructor(info: Any?, depth: Int = 0, indexInParent: Int = -1) : this(info, null, depth, indexInParent) + constructor( + info: Any?, + depth: Int = 0, + indexInParent: Int = -1, + ) : this(info, null, depth, indexInParent) open fun parent(): UiObject? = try { super.getParent()?.let { node -> @@ -63,37 +66,63 @@ open class UiObject( null.also { e.printStackTrace() } } - open fun child(i: Int): UiObject? = try { - super.getChild(i)?.run { UiObject(unwrap(), depth + 1, i) } - } catch (e: IllegalStateException) { - // FIXME: 2017/5/5 - null.also { e.printStackTrace() } + fun parent(i: Int): UiObject? = if (i < 0) throw Exception("i < 0") else compass("p$i") + + open fun child(i: Int): UiObject? { + if (i < 0) { + return (i + childCount).takeIf { it >= 0 }?.let { child(it) } + } + return try { + super.getChild(i)?.run { UiObject(unwrap(), depth + 1, i) } + } catch (e: IllegalStateException) { + // FIXME: 2017/5/5 + null.also { e.printStackTrace() } + } } + // @Deprecated by SuperMonster003 on Jul 20, 2023. + // ! Author: 抠脚本人 + // ! Reason: Replaced with offset(i). @Deprecated("Deprecated in Java", ReplaceWith("offset(i)")) open fun brother(i: Int): UiObject? = offset(i) open fun offset(i: Int): UiObject? = try { - parent()?.child(indexInParent + i) + if (i == 0) this + else parent()?.child(indexInParent + i) } catch (e: ArrayIndexOutOfBoundsException) { null.also { e.printStackTrace() } } open fun sibling(i: Int): UiObject? = try { - parent()?.child(i) + if (i == indexInParent) this + else parent()?.child(i) } catch (e: ArrayIndexOutOfBoundsException) { null.also { e.printStackTrace() } } - open fun nextSibling() = sibling(1) + fun siblingCount() = parent()?.childCount ?: 1 - open fun previousSibling() = sibling(-1) + fun isSingleton() = siblingCount() == 1 + + fun firstSibling() = sibling(0) + + fun lastSibling() = sibling(-1) + + open fun nextSibling() = offset(1) + + open fun previousSibling() = offset(-1) open fun childCount() = childCount fun hasChildren() = childCount > 0 - fun children(): UiObjectCollection = List(childCount) { child(it) }.let { UiObjectCollection.of(it) } + fun firstChild() = child(0) + + fun lastChild() = child(childCount - 1) + + fun children() = List(childCount) { child(it) }.let { UiObjectCollection.of(it) } + + fun siblings() = List(siblingCount()) { sibling(it) }.let { UiObjectCollection.of(it) } fun indexInParent() = indexInParent diff --git a/app/src/main/java/org/autojs/autojs/core/crypto/Crypto.kt b/app/src/main/java/org/autojs/autojs/core/crypto/Crypto.kt index c4e22bce..68c069ab 100644 --- a/app/src/main/java/org/autojs/autojs/core/crypto/Crypto.kt +++ b/app/src/main/java/org/autojs/autojs/core/crypto/Crypto.kt @@ -30,9 +30,9 @@ import javax.crypto.spec.SecretKeySpec /** * Created by SuperMonster003 on Jun 15, 2023. */ -// @Reference to com.stardust.autojs.core.cypto.Crypto.class on Jun 15, 2023. +// @Reference to com.stardust.autojs.core.cypto.Crypto.class from Auto.js Pro 9.3.11 on Jun 15, 2023. // ! There is a strong possibility that "cypto" is a typo. -// @Reference to Auto.js Pro 9.3.11 module __$crypto__.js on Jun 15, 2023. +// @Reference to module __$crypto__.js from Auto.js Pro 9.3.11 on Jun 15, 2023. object Crypto { private val scriptRuntime by lazy { AutoJs.instance.runtime } diff --git a/app/src/main/java/org/autojs/autojs/core/image/capture/ScreenCapturer.java b/app/src/main/java/org/autojs/autojs/core/image/capture/ScreenCapturer.java index 450c654c..a304f9b3 100644 --- a/app/src/main/java/org/autojs/autojs/core/image/capture/ScreenCapturer.java +++ b/app/src/main/java/org/autojs/autojs/core/image/capture/ScreenCapturer.java @@ -44,7 +44,7 @@ public class ScreenCapturer { public static final int ORIENTATION_LANDSCAPE = Configuration.ORIENTATION_LANDSCAPE; public static final int ORIENTATION_PORTRAIT = Configuration.ORIENTATION_PORTRAIT; - // @Reference to TonyJiangWJ/Auto.js on May 19, 2022. + // @Reference to TonyJiangWJ/Auto.js (https://github.com/TonyJiangWJ/Auto.js) on May 19, 2022. // ! Snippet: // ! private final ConcurrentHashMap registeredRuntimes = new ConcurrentHashMap<>(); private static final List mScriptRuntimes = Collections.synchronizedList(new ArrayList<>()); diff --git a/app/src/main/java/org/autojs/autojs/core/image/capture/ScreenCapturerForegroundService.java b/app/src/main/java/org/autojs/autojs/core/image/capture/ScreenCapturerForegroundService.java index 977680f3..109e9281 100644 --- a/app/src/main/java/org/autojs/autojs/core/image/capture/ScreenCapturerForegroundService.java +++ b/app/src/main/java/org/autojs/autojs/core/image/capture/ScreenCapturerForegroundService.java @@ -44,7 +44,7 @@ public class ScreenCapturerForegroundService extends Service { ForegroundServiceUtils.startForeground(new ForegroundServiceCreator.Builder(this) .setClassName(sClassName) .setIntent( - // @Reference to TonyJiangWJ/Auto.js on Apr 10, 2022 + // @Reference to TonyJiangWJ/Auto.js (https://github.com/TonyJiangWJ/Auto.js) on Apr 10, 2022 ScreenCaptureRequestActivity.getIntent(this) ) .setNotificationId(NOTIFICATION_ID) diff --git a/app/src/main/java/org/autojs/autojs/core/looper/Loopers.kt b/app/src/main/java/org/autojs/autojs/core/looper/Loopers.kt index bc409ddd..c52e0558 100644 --- a/app/src/main/java/org/autojs/autojs/core/looper/Loopers.kt +++ b/app/src/main/java/org/autojs/autojs/core/looper/Loopers.kt @@ -1,114 +1,112 @@ package org.autojs.autojs.core.looper +import android.os.Handler import android.os.Looper import android.os.MessageQueue +import android.os.MessageQueue.IdleHandler import android.util.Log import org.autojs.autojs.lang.ThreadCompat import org.autojs.autojs.rhino.AutoJsContext import org.autojs.autojs.runtime.ScriptRuntime +import org.autojs.autojs.runtime.api.Threads +import org.autojs.autojs.runtime.api.Timers import org.autojs.autojs.runtime.exception.ScriptInterruptedException import org.mozilla.javascript.Context -import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.CopyOnWriteArrayList /** * Created by Stardust on 2017/7/29. - * Transformed by aiselp on Jul 4, 2023. + * Modified by SuperMonster003 as of Jul 12, 2023. + * Transformed by SuperMonster003 on Jul 12, 2023. */ -/** - * update by aiselp on 2023/6/4 - * 调整内容: - * 使此类只负责单loop线程生命周期管理,移除繁琐的调用链 - * 调整timer由此类创建 - * 通过向此类添加AsyncTask以监听线程退出事件 - */ -class Loopers(val runtime: ScriptRuntime) { - @Deprecated("使用AsyncTask代替") +// @Overruled by SuperMonster003 on Jul 12, 2023. +// ! Author: aiselp +// ! Related PR: +// ! http://pr.autojs6.com/75 +// ! Reason: +// ! Sorry but my current capabilities are not sufficient +// ! to fully understand everything from above pull request(s), +// ! so most of the code will remain as is. :) +class Loopers(runtime: ScriptRuntime) : IdleHandler { interface LooperQuitHandler { fun shouldQuit(): Boolean } - open class AsyncTask(private val describe: String) { - private val allBind = ConcurrentLinkedQueue() - var isEnd: Boolean = false - private set - - //线程即将退出时调用,返回true阻止线程退出,只要有一个task返回true线程就不会退出 - open fun onFinish(loopers: Loopers): Boolean { - return true - } - - fun end() { - isEnd = true - } - - //线程正在退出,这里应该结束任务的执行,回收资源 - open fun onStop(loopers: Loopers) {} - override fun toString(): String { - return "AsyncTask: $describe" + private val waitWhenIdle: ThreadLocal = object : ThreadLocal() { + override fun initialValue(): Boolean { + return Looper.myLooper() == Looper.getMainLooper() } } - - private var waitWhenIdle: Boolean + private val waitIds: ThreadLocal> = object : ThreadLocal>() { + override fun initialValue(): HashSet { + return HashSet() + } + } + private val maxWaitId: ThreadLocal = object : ThreadLocal() { + override fun initialValue(): Int { + return 0 + } + } + private val looperQuitHandlers = ThreadLocal>() @Volatile private var mServantLooper: Looper? = null + private val mTimers: Timers private var mMainLooperQuitHandler: LooperQuitHandler? = null - private val allTasks = ConcurrentLinkedQueue() - val mTimer: Timer - val myLooper: Looper + private val mMainHandler: Handler + private val mMainLooper: Looper? + private val mThreads: Threads + private val mMainMessageQueue: MessageQueue init { + mTimers = runtime.timers + mThreads = runtime.threads prepare() - myLooper = Looper.myLooper()!! - mTimer = Timer(runtime, myLooper) - waitWhenIdle = myLooper == Looper.getMainLooper() + mMainLooper = Looper.myLooper() + mMainHandler = Handler(Looper.getMainLooper()) + mMainMessageQueue = Looper.myQueue() } - fun createAndAddAsyncTask(describe: String): AsyncTask { - val task = AsyncTask(describe) - allTasks.add(task) - return task - } - - fun addAsyncTask(task: AsyncTask) { - synchronized(myLooper) { - allTasks.add(task) + fun addLooperQuitHandler(handler: LooperQuitHandler) { + var handlers = looperQuitHandlers.get() + if (handlers == null) { + handlers = CopyOnWriteArrayList() + looperQuitHandlers.set(handlers) } + handlers.add(handler) } - fun removeAsyncTask(task: AsyncTask) { - synchronized(myLooper) { - allTasks.remove(task) - mTimer.post(EMPTY_RUNNABLE) - } - } - - private fun checkTask(): Boolean { - allTasks.removeAll(allTasks.filter { it.isEnd }.toSet()) - for (task in allTasks) { - if (task.onFinish(this)) return true - } - return false + fun removeLooperQuitHandler(handler: LooperQuitHandler): Boolean { + val handlers = looperQuitHandlers.get() + return handlers != null && handlers.remove(handler) } private fun shouldQuitLooper(): Boolean { - synchronized(myLooper) { - if (Thread.currentThread().isInterrupted) return true - if (mTimer.hasPendingCallbacks()) return false - //检查是否有运行中的线程 - if (checkTask()) return false - if (waitWhenIdle) return false - if ((Context.getCurrentContext() as AutoJsContext).hasPendingContinuation()) { - return false - } + if (Thread.currentThread().isInterrupted) { return true } + if (mTimers.hasPendingCallbacks()) { + return false + } + if (waitWhenIdle.get() || !waitIds.get().isEmpty()) { + return false + } + if ((Context.getCurrentContext() as AutoJsContext).hasPendingContinuation()) { + return false + } + val handlers = looperQuitHandlers.get() ?: return true + for (handler in handlers) { + if (!handler.shouldQuit()) { + return false + } + } + return true } private fun initServantThread() { + val lock = this@Loopers as Object ThreadCompat { Looper.prepare() - val lock = this@Loopers as Object mServantLooper = Looper.myLooper() synchronized(lock) { lock.notifyAll() } Looper.loop() @@ -119,7 +117,7 @@ class Loopers(val runtime: ScriptRuntime) { get() { if (mServantLooper == null) { initServantThread() - val lock = this as java.lang.Object + val lock = this@Loopers as Object synchronized(lock) { try { lock.wait() @@ -131,56 +129,65 @@ class Loopers(val runtime: ScriptRuntime) { return mServantLooper!! } - @Deprecated("使用AsyncTask代替") - fun waitWhenIdle(b: Boolean) { - waitWhenIdle = b - } - - fun recycle() { - Log.d(LOG_TAG, "recycle") - for (task in allTasks.filter { !it.isEnd }) { - try { - task.onStop(this) - } catch (e: Exception) { - Log.w(LOG_TAG, e) - } - } + private fun quitServantLooper() { mServantLooper?.quit() } - @Deprecated("使用AsyncTask代替") + fun waitWhenIdle(): Int { + val id = maxWaitId.get() + Log.d(LOG_TAG, "waitWhenIdle: $id") + maxWaitId.set(id + 1) + waitIds.get().add(id) + return id + } + + fun doNotWaitWhenIdle(waitId: Int) { + Log.d(LOG_TAG, "doNotWaitWhenIdle: $waitId") + waitIds.get().remove(waitId) + } + + fun waitWhenIdle(b: Boolean) { + waitWhenIdle.set(b) + } + + fun recycle() { + quitServantLooper() + mMainMessageQueue.removeIdleHandler(this) + } + fun setMainLooperQuitHandler(mainLooperQuitHandler: LooperQuitHandler?) { mMainLooperQuitHandler = mainLooperQuitHandler } - private fun prepare() { - if (Looper.myLooper() == null) LooperHelper.prepare() - Looper.myQueue().addIdleHandler(MessageQueue.IdleHandler { - if (this == runtime.loopers) { - Log.d(LOG_TAG, "main looper queueIdle") - if (shouldQuitLooper() && - mMainLooperQuitHandler != null && - mMainLooperQuitHandler!!.shouldQuit() - ) { - Log.d(LOG_TAG, "main looper quit") - Looper.myLooper()!!.quitSafely() - } - } else { - Log.d(LOG_TAG, "looper queueIdle $this") - if (shouldQuitLooper()) { - Log.d(LOG_TAG, "looper quit $this") - Looper.myLooper()!!.quitSafely() - } + override fun queueIdle(): Boolean { + val l = Looper.myLooper() ?: return true + if (l == mMainLooper) { + Log.d(LOG_TAG, "main looper queueIdle") + if (shouldQuitLooper() && !mThreads.hasRunningThreads() && mMainLooperQuitHandler != null && mMainLooperQuitHandler!!.shouldQuit()) { + Log.d(LOG_TAG, "main looper quit") + l.quit() } - return@IdleHandler true - }) + } else { + Log.d(LOG_TAG, "looper queueIdle: $l") + if (shouldQuitLooper()) { + l.quit() + } + } + return true + } + + fun prepare() { + if (Looper.myLooper() == null) { + LooperHelper.prepare() + } + Looper.myQueue().addIdleHandler(this) } fun notifyThreadExit(thread: TimerThread) { Log.d(LOG_TAG, "notifyThreadExit: $thread") - //当子线程退成时,主线程需要检查自身是否退出(主线程在所有子线程执行完成后才能退出,如果主线程已经执行完任务仍然要等待所有子线程), - //此时通过向主线程发送一个空的Runnable,主线程执行完这个Runnable后会触发IdleHandler,从而检查自身是否退出 - //mHandler.post(EMPTY_RUNNABLE) + // 当子线程退成时,主线程需要检查自身是否退出(主线程在所有子线程执行完成后才能退出,如果主线程已经执行完任务仍然要等待所有子线程), + // 此时通过向主线程发送一个空的Runnable,主线程执行完这个Runnable后会触发IdleHandler,从而检查自身是否退出 + mMainHandler.post(EMPTY_RUNNABLE) } companion object { diff --git a/app/src/main/java/org/autojs/autojs/core/looper/Timer.kt b/app/src/main/java/org/autojs/autojs/core/looper/Timer.kt index b3e02adc..f40e1cb4 100644 --- a/app/src/main/java/org/autojs/autojs/core/looper/Timer.kt +++ b/app/src/main/java/org/autojs/autojs/core/looper/Timer.kt @@ -3,134 +3,119 @@ package org.autojs.autojs.core.looper import android.os.Handler import android.os.Looper import android.os.SystemClock +import android.util.SparseArray +import org.autojs.autojs.concurrent.VolatileBox import org.autojs.autojs.runtime.ScriptRuntime -import org.mozilla.javascript.BaseFunction -import org.mozilla.javascript.Context -import org.mozilla.javascript.Scriptable -import org.mozilla.javascript.Undefined -import java.util.concurrent.ConcurrentHashMap -import kotlin.random.Random +import kotlin.math.max /** * Created by Stardust on 2017/12/27. - * Transformed by aiselp on Jun 4, 2023. + * Modified by SuperMonster003 as of Jul 12, 2023. + * Transformed by SuperMonster003 on Jul 12, 2023. */ -class Timer( - runtime: ScriptRuntime, - looper: Looper -) { - private val myLooper: Looper = looper - private val mHandlerCallbacks = ConcurrentHashMap() +// @Overruled by SuperMonster003 on Jul 12, 2023. +// ! Author: aiselp +// ! Related PR: +// ! http://pr.autojs6.com/75 +// ! http://pr.autojs6.com/78 +// ! Reason: +// ! Sorry but my current capabilities are not sufficient +// ! to fully understand everything from above pull request(s), +// ! so most of the code will remain as is. :) +class Timer @JvmOverloads constructor(runtime: ScriptRuntime, maxCallbackMillisForAllThread: VolatileBox, private val looper: Looper? = Looper.myLooper()) { + + private val mHandlerCallbacks = SparseArray() + private var mCallbackMaxId = 0 private val mRuntime: ScriptRuntime = runtime - private val mHandler: Handler = Handler(looper) - private val isUiLoop: Boolean = looper == Looper.getMainLooper() - private val context: Context? by lazy { Context.getCurrentContext() } + private val mHandler = looper?.let { Handler(it) } ?: Handler() + private var mMaxCallbackUptimeMillis: Long = 0 + private val mMaxCallbackMillisForAllThread: VolatileBox = maxCallbackMillisForAllThread - constructor(runtime: ScriptRuntime) : this(runtime, Looper.myLooper()!!) - - fun setTimeout(callback: Any, delay: Long, vararg args: Any?): Int { - val id = createTimerId() + fun setTimeout(callback: Any, delay: Long, vararg args: Array): Int { + mCallbackMaxId++ + val id = mCallbackMaxId val r = Runnable { - callFunction(callback, null, args) + callFunction(callback, args) mHandlerCallbacks.remove(id) } - mHandlerCallbacks[id] = r + mHandlerCallbacks.put(id, r) postDelayed(r, delay) return id } - private fun callFunction(callback: Any, thisArg: Any?, args: Any?) { - val func = callback as BaseFunction - val map: Array = - (args as? Array<*>)?.map { Context.javaToJS(it, callback.parentScope) } - ?.toTypedArray() ?: emptyArray() + private fun callFunction(callback: Any, args: Array) { try { - func.call( - context ?: Context.enter(), func.parentScope, - thisArg as? Scriptable ?: Undefined.SCRIPTABLE_UNDEFINED, map - ) + mRuntime.bridges.callFunction(callback, null, args) } catch (e: Exception) { - if (isUiLoop) { + if (Looper.myLooper() == Looper.getMainLooper()) { mRuntime.exit(e) - } else throw e - } finally { - context ?: Context.exit() + } else { + throw e + } } } - @Synchronized - private fun createTimerId(): Int { - var id: Int - do { - id = Random.nextInt() - } while (mHandlerCallbacks.containsKey(id)) - mHandlerCallbacks[id] = EMPTY_RUNNABLE - return id - } + fun clearTimeout(id: Int) = clearCallback(id) - fun setInterval(listener: Any, interval: Long, vararg args: Any?): Int { - val id = createTimerId() - val r: Runnable = object : Runnable { + fun setInterval(listener: Any, interval: Long, vararg args: Any): Int { + mCallbackMaxId++ + val id = mCallbackMaxId + val r = object : Runnable { override fun run() { - if (mHandlerCallbacks[id] == null) return - callFunction(listener, null, args) + mHandlerCallbacks[id] ?: return + callFunction(listener, args) postDelayed(this, interval) } } - mHandlerCallbacks[id] = r + mHandlerCallbacks.put(id, r) postDelayed(r, interval) return id } fun postDelayed(r: Runnable, interval: Long) { - synchronized(myLooper) { - val uptime = SystemClock.uptimeMillis() + interval - mHandler.postAtTime(r, uptime) - } + val uptime = SystemClock.uptimeMillis() + interval + mHandler.postAtTime(r, uptime) + mMaxCallbackUptimeMillis = mMaxCallbackUptimeMillis.coerceAtLeast(uptime) + synchronized(mMaxCallbackMillisForAllThread) { mMaxCallbackMillisForAllThread.set(max(mMaxCallbackMillisForAllThread.get(), uptime)) } } + // @Reference to aiselp (https://github.com/aiselp) on Jul 18, 2023. fun post(r: Runnable) { - synchronized(myLooper) { - mHandler.post(r) + looper?.let { + synchronized(it) { + mHandler.post(r) + } } } - fun clearInterval(id: Int): Boolean = clearCallback(id) - fun clearImmediate(id: Int): Boolean = clearCallback(id) - fun clearTimeout(id: Int): Boolean = clearCallback(id) + fun clearInterval(id: Int) = clearCallback(id) - fun setImmediate(listener: Any, vararg args: Any?): Int { - val id = createTimerId() + fun setImmediate(listener: Any, vararg args: Any): Int { + mCallbackMaxId++ + val id = mCallbackMaxId val r = Runnable { - callFunction(listener, null, args) + callFunction(listener, args) mHandlerCallbacks.remove(id) } - mHandlerCallbacks[id] = r - post(r) + mHandlerCallbacks.put(id, r) + postDelayed(r, 0) return id } + fun clearImmediate(id: Int) = clearCallback(id) private fun clearCallback(id: Int): Boolean { val callback = mHandlerCallbacks[id] if (callback != null) { mHandler.removeCallbacks(callback) mHandlerCallbacks.remove(id) - if (mHandlerCallbacks.isEmpty()) mHandler.post(EMPTY_RUNNABLE) return true } return false } - fun hasPendingCallbacks(): Boolean { - return mHandlerCallbacks.size > 0 - } + fun hasPendingCallbacks() = mMaxCallbackUptimeMillis > SystemClock.uptimeMillis() - fun removeAllCallbacks() { - mHandler.removeCallbacksAndMessages(null) - } + fun removeAllCallbacks() = mHandler.removeCallbacksAndMessages(null) - companion object { - private val EMPTY_RUNNABLE = Runnable {} - } } \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/core/looper/TimerThread.kt b/app/src/main/java/org/autojs/autojs/core/looper/TimerThread.kt index d30c089d..084073aa 100644 --- a/app/src/main/java/org/autojs/autojs/core/looper/TimerThread.kt +++ b/app/src/main/java/org/autojs/autojs/core/looper/TimerThread.kt @@ -1,45 +1,58 @@ package org.autojs.autojs.core.looper +import android.os.Handler import android.os.Looper import androidx.annotation.CallSuper +import org.autojs.autojs.concurrent.VolatileBox import org.autojs.autojs.engine.RhinoJavaScriptEngine import org.autojs.autojs.lang.ThreadCompat import org.autojs.autojs.runtime.ScriptRuntime import org.autojs.autojs.runtime.exception.ScriptInterruptedException +import org.autojs.autojs.util.StringUtils.str +import org.autojs.autojs6.R import org.mozilla.javascript.Context import java.util.concurrent.ConcurrentHashMap /** * Created by Stardust on 2017/12/27. - * Transformed by aiselp on Jun 4, 2023. + * Modified by SuperMonster003 as of Jul 12, 2023. + * Transformed by SuperMonster003 on Jul 12, 2023. */ -open class TimerThread(private val mRuntime: ScriptRuntime, private val mTarget: Runnable) : - ThreadCompat(mTarget) { +// @Overruled by SuperMonster003 on Jul 12, 2023. +// ! Author: aiselp +// ! Related PR: +// ! http://pr.autojs6.com/75 +// ! Reason: +// ! Sorry but my current capabilities are not sufficient +// ! to fully understand everything from above pull request(s), +// ! so most of the code will remain as is. :) +open class TimerThread( + private val scriptRuntime: ScriptRuntime, + private val maxCallbackUptimeMillisForAllThreads: VolatileBox, + private val target: Runnable +) : ThreadCompat(target) { + private var mTimer: Timer? = null private var mRunning = false private val mRunningLock = Object() - private val mAsyncTask = Loopers.AsyncTask("TimerThread") - var loopers: Loopers? = null - - init { - mRuntime.loopers.addAsyncTask(mAsyncTask) - } override fun run() { - loopers = Loopers(mRuntime) - mTimer = loopers!!.mTimer - sTimerMap[currentThread()] = mTimer!! - (mRuntime.engines.myEngine() as RhinoJavaScriptEngine).enterContext() + scriptRuntime.loopers.prepare() + mTimer = Timer(scriptRuntime, maxCallbackUptimeMillisForAllThreads).also { + sTimerMap[currentThread()] = it + } + (scriptRuntime.engines.myEngine() as? RhinoJavaScriptEngine)?.enterContext() notifyRunning() - mTimer!!.post(mTarget) + Looper.myLooper()?.let { + Handler(it).post(target) + } ?: Handler().post(target) try { Looper.loop() } catch (e: Throwable) { if (!ScriptInterruptedException.causedByInterrupted(e)) { - mRuntime.console.error(currentThread().toString() + ": ", e) + scriptRuntime.console.error("${currentThread()}: $e") } } finally { - //mRuntime.console.log("TimerThread exit"); onExit() mTimer = null Context.exit() @@ -61,69 +74,49 @@ open class TimerThread(private val mRuntime: ScriptRuntime, private val mTarget: @CallSuper protected open fun onExit() { - mRuntime.loopers.removeAsyncTask(mAsyncTask) - mRuntime.loopers.notifyThreadExit(this) - } - - fun setTimeout(callback: Any, delay: Long, vararg args: Any?): Int { - return timer.setTimeout(callback, delay, *args as Array) - } - - fun setTimeout(callback: Any): Int { - return setTimeout(callback, 1) + scriptRuntime.loopers.notifyThreadExit(this) } val timer: Timer get() { - checkNotNull(mTimer) { "thread is not alive" } - return mTimer as Timer + checkNotNull(mTimer) { str(R.string.error_thread_is_not_alive) } + return mTimer!! } - fun clearTimeout(id: Int): Boolean { - return timer.clearTimeout(id) - } + fun setTimeout(callback: Any, delay: Long, vararg args: Array) = timer.setTimeout(callback, delay, *args) - fun setInterval(listener: Any?, interval: Long, vararg args: Any?): Int { - return timer.setInterval(listener!!, interval, *args as Array) - } + fun clearTimeout(id: Int) = timer.clearTimeout(id) - fun setInterval(listener: Any?): Int { - return setInterval(listener, 1) - } + fun setInterval(listener: Any, interval: Long, vararg args: Array) = timer.setInterval(listener, interval, *args) - fun clearInterval(id: Int): Boolean { - return timer.clearInterval(id) - } + fun clearInterval(id: Int) = timer.clearInterval(id) - fun setImmediate(listener: Any, vararg args: Any?): Int { - return timer.setImmediate(listener, *args as Array) - } + fun setImmediate(listener: Any, vararg args: Array) = timer.setImmediate(listener, *args) - fun clearImmediate(id: Int): Boolean { - return timer.clearImmediate(id) - } + fun clearImmediate(id: Int) = timer.clearImmediate(id) @Throws(InterruptedException::class) fun waitFor() { synchronized(mRunningLock) { - if (mRunning) return - mRunningLock.wait() + if (!mRunning) { + mRunningLock.wait() + } } } - override fun toString(): String { - return "Thread[$name,$priority]" - } + override fun toString() = "Thread[$name,$priority]" companion object { + private val sTimerMap = ConcurrentHashMap() @JvmStatic - fun getTimerForThread(thread: Thread): Timer? { - return sTimerMap[thread] - } + fun getTimerForThread(thread: Thread) = sTimerMap[thread] - val timerForCurrentThread: Timer? + @JvmStatic + val timerForCurrentThread get() = getTimerForThread(currentThread()) + } + } \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/core/permission/Permissions.java b/app/src/main/java/org/autojs/autojs/core/permission/Permissions.java new file mode 100644 index 00000000..6a605b5a --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/core/permission/Permissions.java @@ -0,0 +1,44 @@ +package org.autojs.autojs.core.permission; + +import android.content.Context; +import android.content.Intent; + +import java.util.ArrayList; + +import static android.content.pm.PackageManager.PERMISSION_DENIED; + +public class Permissions { + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + + static final int REQUEST_CODE = 18777; + + public static String[] getPermissionsNeedToRequest(Context context, String[] permissions) { + ArrayList list = new ArrayList<>(); + for (String permission : permissions) { + if (!permission.startsWith("android.permission.")) { + permission = "android.permission." + permission.toUpperCase(); + } + if (context.checkSelfPermission(permission) == PERMISSION_DENIED) { + list.add(permission); + } + } + return list.toArray(EMPTY_STRING_ARRAY); + } + + public static void requestPermissions(PermissionRequestProxyActivity activity, String[] permissions, OnRequestPermissionsResultCallback callback) { + if (callback != null) { + activity.addRequestPermissionsCallback((code, p, grantResults) -> { + activity.removeRequestPermissionsCallback(callback); + callback.onRequestPermissionsResult(code, p, grantResults); + }); + } + activity.requestPermissions(permissions, REQUEST_CODE); + } + + public static void requestPermissions(Context context, String[] permissions) { + context.startActivity(new Intent(context, PermissionRequestActivity.class) + .putExtra(PermissionRequestActivity.EXTRA_PERMISSIONS, permissions) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } +} diff --git a/app/src/main/java/org/autojs/autojs/core/ui/JsViewHelper.kt b/app/src/main/java/org/autojs/autojs/core/ui/JsViewHelper.kt index 4d22c2f1..1cac63c4 100644 --- a/app/src/main/java/org/autojs/autojs/core/ui/JsViewHelper.kt +++ b/app/src/main/java/org/autojs/autojs/core/ui/JsViewHelper.kt @@ -6,7 +6,7 @@ import org.autojs.autojs.core.ui.inflater.util.Ids /** * Created by Stardust on 2017/5/14. - * Transformed by 抠脚本人 on Jul 10, 2023. + * Transformed by 抠脚本人 (https://github.com/little-alei) on Jul 10, 2023. */ object JsViewHelper { @JvmStatic diff --git a/app/src/main/java/org/autojs/autojs/core/ui/dialog/JsDialogBuilder.java b/app/src/main/java/org/autojs/autojs/core/ui/dialog/JsDialogBuilder.java index 440cd3bf..d9e8022d 100644 --- a/app/src/main/java/org/autojs/autojs/core/ui/dialog/JsDialogBuilder.java +++ b/app/src/main/java/org/autojs/autojs/core/ui/dialog/JsDialogBuilder.java @@ -21,8 +21,7 @@ public class JsDialogBuilder extends MaterialDialog.Builder { private final Timer mTimer; private final Loopers mLoopers; private JsDialog mDialog; - private volatile Loopers.AsyncTask task; - + private volatile int mWaitId = -1; public JsDialogBuilder(Context context, ScriptRuntime runtime) { super(context); @@ -56,14 +55,14 @@ public class JsDialogBuilder extends MaterialDialog.Builder { } }); dismissListener(dialog -> { - mTimer.postDelayed(() -> mLoopers.removeAsyncTask(task), 0); + mTimer.postDelayed(() -> mLoopers.doNotWaitWhenIdle(mWaitId), 0); emit("dismiss", dialog); }); cancelListener(dialog -> emit("cancel", dialog)); } public void onShowCalled() { - mTimer.postDelayed(() -> task = mLoopers.createAndAddAsyncTask("js-dialog"), 0); + mTimer.postDelayed(() -> mWaitId = mLoopers.waitWhenIdle(), 0); } public JsDialog getDialog() { diff --git a/app/src/main/java/org/autojs/autojs/core/ui/widget/JsTextViewLegacy.kt b/app/src/main/java/org/autojs/autojs/core/ui/widget/JsTextViewLegacy.kt index d3808aa3..96a743a5 100644 --- a/app/src/main/java/org/autojs/autojs/core/ui/widget/JsTextViewLegacy.kt +++ b/app/src/main/java/org/autojs/autojs/core/ui/widget/JsTextViewLegacy.kt @@ -8,7 +8,7 @@ import androidx.appcompatlegacy.widget.AppCompatTextView * Created by SuperMonster003 on Mar 20, 2022. * Transformed by SuperMonster003 on May 22, 2023. */ -// @Reference to TonyJiangWJ/Auto.js on Mar 20, 2022 +// @Reference to TonyJiangWJ/Auto.js (https://github.com/TonyJiangWJ/Auto.js) on Mar 20, 2022 class JsTextViewLegacy : AppCompatTextView { constructor(context: Context) : super(context) diff --git a/app/src/main/java/org/autojs/autojs/core/ui/xml/XmlConverter.kt b/app/src/main/java/org/autojs/autojs/core/ui/xml/XmlConverter.kt index aa3154b0..ce4f7e13 100644 --- a/app/src/main/java/org/autojs/autojs/core/ui/xml/XmlConverter.kt +++ b/app/src/main/java/org/autojs/autojs/core/ui/xml/XmlConverter.kt @@ -83,7 +83,7 @@ object XmlConverter { .map(arrayOf("webview", "web"), JsWebView::class.java.name) .map( "text", - // @Reference to TonyJiangWJ/Auto.js on Mar 20, 2022 + // @Reference to TonyJiangWJ/Auto.js (https://github.com/TonyJiangWJ/Auto.js) on Mar 20, 2022 when (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { true -> JsTextViewLegacy::class.java.name else -> JsTextView::class.java.name diff --git a/app/src/main/java/org/autojs/autojs/core/web/WebSocket.kt b/app/src/main/java/org/autojs/autojs/core/web/WebSocket.kt index 46b860be..2a4f014f 100644 --- a/app/src/main/java/org/autojs/autojs/core/web/WebSocket.kt +++ b/app/src/main/java/org/autojs/autojs/core/web/WebSocket.kt @@ -14,7 +14,7 @@ val runtime = AutoJs.instance.runtime /** * Created by SuperMonster003 on Apr 30, 2023. */ -// @Reference to kkevsekk1/AutoX on Apr 30, 2023. +// @Reference to kkevsekk1/AutoX (https://github.com/kkevsekk1/AutoX) on Apr 30, 2023. class WebSocket @JvmOverloads constructor(val client: OkHttpClient, val url: String, isInCurrentThread: Boolean = true) : EventEmitter( runtime.bridges, runtime.timers.timerForCurrentThread.takeIf { isInCurrentThread } ), okhttp3.WebSocket { diff --git a/app/src/main/java/org/autojs/autojs/engine/RhinoJavaScriptEngine.kt b/app/src/main/java/org/autojs/autojs/engine/RhinoJavaScriptEngine.kt index 92067d89..20fe2251 100644 --- a/app/src/main/java/org/autojs/autojs/engine/RhinoJavaScriptEngine.kt +++ b/app/src/main/java/org/autojs/autojs/engine/RhinoJavaScriptEngine.kt @@ -5,8 +5,8 @@ import android.view.View import org.autojs.autojs.core.automator.UiObjectCollection import org.autojs.autojs.core.ui.ViewExtras import org.autojs.autojs.engine.module.AssetAndUrlModuleSourceProvider -import org.autojs.autojs.engine.module.ScopeRequire import org.autojs.autojs.execution.ExecutionConfig +import org.autojs.autojs.pio.PFiles import org.autojs.autojs.pio.UncheckedIOException import org.autojs.autojs.project.ScriptConfig import org.autojs.autojs.rhino.RhinoAndroidHelper @@ -18,6 +18,7 @@ import org.mozilla.javascript.Context import org.mozilla.javascript.Script import org.mozilla.javascript.Scriptable import org.mozilla.javascript.ScriptableObject +import org.mozilla.javascript.commonjs.module.RequireBuilder import org.mozilla.javascript.commonjs.module.provider.SoftCachingModuleScriptProvider import java.io.File import java.io.IOException @@ -118,10 +119,13 @@ open class RhinoJavaScriptEngine(private val mAndroidContext: android.content.Co private fun initRequireBuilder(context: Context, scope: Scriptable) { val provider = AssetAndUrlModuleSourceProvider( - mAndroidContext, MODULES_PATH, - listOf(File("/").toURI()) + mAndroidContext, MODULES_ROOT_PATH, listOf(File(File.separator).toURI()) ) - ScopeRequire(context, scope, SoftCachingModuleScriptProvider(provider)).install(scope) + RequireBuilder() + .setModuleScriptProvider(SoftCachingModuleScriptProvider(provider)) + .setSandboxed(true) + .createRequire(context, scope) + .install(scope) } protected fun createScope(context: Context): TopLevelScope { @@ -164,8 +168,11 @@ open class RhinoJavaScriptEngine(private val mAndroidContext: android.content.Co const val SOURCE_FILE_INIT = "init.js" const val SOURCE_NAME_INIT = "" + const val MODULES_ROOT_PATH = "modules" + const val JS_BEAUTIFY_PATH = "js-beautify" - private const val MODULES_PATH = "modules" + @JvmField + val JS_BEAUTIFY_FILE = PFiles.join(JS_BEAUTIFY_PATH, "beautify.js") private var sInitScript: Script? = null private val sContextEngineMap = ConcurrentHashMap() diff --git a/app/src/main/java/org/autojs/autojs/engine/module/AssetAndUrlModuleSourceProvider.kt b/app/src/main/java/org/autojs/autojs/engine/module/AssetAndUrlModuleSourceProvider.kt index 329c2e0c..02fac406 100644 --- a/app/src/main/java/org/autojs/autojs/engine/module/AssetAndUrlModuleSourceProvider.kt +++ b/app/src/main/java/org/autojs/autojs/engine/module/AssetAndUrlModuleSourceProvider.kt @@ -1,160 +1,131 @@ package org.autojs.autojs.engine.module -import android.content.ContentResolver import android.content.Context import android.net.Uri -import com.google.gson.Gson import okhttp3.OkHttpClient import okhttp3.Request import org.autojs.autojs.engine.encryption.ScriptEncryption.decrypt +import org.autojs.autojs.runtime.ScriptRuntime import org.autojs.autojs.script.EncryptedScriptFileHeader import org.autojs.autojs.script.EncryptedScriptFileHeader.isValidFile import org.mozilla.javascript.commonjs.module.provider.ModuleSource -import org.mozilla.javascript.commonjs.module.provider.ModuleSourceProviderBase import java.io.ByteArrayInputStream import java.io.File +import java.io.File.separator +import java.io.FileNotFoundException +import java.io.IOException import java.io.InputStream import java.io.InputStreamReader +import java.io.Reader import java.net.URI +import java.net.URISyntaxException +import java.net.URLConnection +import java.nio.charset.Charset +import java.nio.charset.StandardCharsets +/** + * Created by Stardust on 2017/5/9. + * Transformed by SuperMonster003 on Jul 14, 2023. + */ +// @Inspired by aiselp (https://github.com/aiselp) on Jul 14, 2023. +// ! Related PR: +// ! http://pr.autojs6.com/75 +// ! http://pr.autojs6.com/78 +// @Hint by SuperMonster003 on Jul 17, 2023. +// ! Project-structured directories with package.json +// ! was not yet adapted, +// ! as it doesn't seem to matter as much. :) class AssetAndUrlModuleSourceProvider( - context: Context, - assetDirPath: String, - list: List? = null -) : ModuleSourceProviderBase() { - val mContext = context - private val okHttpClient = OkHttpClient.Builder().followRedirects(true).build() - private val contentResolver: ContentResolver = context.contentResolver - private val moduleSources: ArrayList = arrayListOf(mBaseURI, npmModuleSource) + private val context: Context, + private val assetDirPath: String, + list: List +) : UrlModuleSourceProvider(list, null) { - companion object { - val mBaseURI: URI = URI.create("file:/android_asset/modules") - val npmModuleSource: URI = URI.create("file:/android_asset/modules/npm") - } + private val mOkHttpClient by lazy { OkHttpClient.Builder().followRedirects(true).build() } + private val mAssetBaseURI = URI.create("file:///android_asset$separator$assetDirPath") + private val mAssetManager = context.assets + private val mJavaScriptExtensionName = ".js" + private val mRegexUrl = "^(https?|ftp|file)://[-a-zA-Z\\d+&@#/%?=~_|!:,.;]*[-a-zA-Z\\d+&@#/%=~_|]".toRegex() - // 初始化脚本以及启动文件只会从此方法加载模块,子模块加载没有以"./"或"../"开头的模块也会从此方法加载 + @Throws(IOException::class, URISyntaxException::class) override fun loadFromPrivilegedLocations(moduleId: String, validator: Any?): ModuleSource? { - // println("加载私有模块:$moduleId") - val uri = if (moduleId.startsWith("/")) { - File(moduleId).toURI() - } else if (moduleId.startsWith("http://") || moduleId.startsWith("https://")) { - URI.create(moduleId) - } else null - if (uri != null) { - return loadFromUri(uri, File(uri.path).parentFile?.toURI(), validator) - } - for (baseUri in moduleSources) { - val sourceUri = URI.create("$baseUri/$moduleId") - val moduleSource = loadFromUri(sourceUri, baseUri, validator) - if (moduleSource != null) { - return moduleSource + return when (moduleId.matches(mRegexUrl)) { + true -> loadFromURL(moduleId, validator) + else -> try { + loadFromAsset(moduleId, validator) + } catch (e: Exception) { + return when (moduleId.startsWith(separator)) { + true -> loadFromFile(File(moduleId), validator = validator) + else -> null + } } } - return null } - // 这里处理node_module目录的模块 - override fun loadFromFallbackLocations(moduleId: String, validator: Any?): ModuleSource? { - return super.loadFromFallbackLocations(moduleId, validator) - } - - // 子模块以相对路径加载时调用此方法 - override fun loadFromUri(uri: URI, base: URI?, validator: Any?): ModuleSource? { - var uri = uri - if (uri.scheme == null) uri = File(uri.path).toURI() - // println("加载模块:$uri") - if (uri.scheme == "http" || uri.scheme == "https") { - return loadFromHttp(uri, base, validator) + private fun loadFromAsset(moduleId: String, validator: Any?): ModuleSource? { + val moduleIdWithExtension = when (moduleId.endsWith(mJavaScriptExtensionName, true)) { + true -> moduleId + else -> moduleId + mJavaScriptExtensionName } - val moduleSource = loadAt(uri, base, validator) ?: loadAt( - File(uri.path + ".js").toURI(), base, validator - ) - if (moduleSource != null) { - return moduleSource - } - // 尝试从目录加载 - // 尝试读取package.json指定的文件 - val mainFile: URI? = try { - val packageFile = File(uri.path, "package.json") - val json = Gson().fromJson>( - InputStreamReader(packageFile.inputStream()), - Map::class.java + return try { + createModuleSource( + mAssetManager.open("$assetDirPath$separator$moduleIdWithExtension"), + URI("$mAssetBaseURI$separator$moduleIdWithExtension"), + mAssetBaseURI, + validator, ) - val main = json["main"] as String - packageFile.toURI().resolve(main) - } catch (e: Exception) { - null + } catch (_: FileNotFoundException) { + super.loadFromPrivilegedLocations(moduleId, validator) } - val main: URI = mainFile ?: File(uri.path, "index.js").toURI() - return loadAt(main, uri, validator) } - private fun loadAt(uri: URI, base: URI?, validator: Any?): ModuleSource? { - if (uri.scheme == "http" || uri.scheme == "https") { - return loadFromHttp(uri, base, validator) - } + private fun loadFromURL(url: String, validator: Any?): ModuleSource? { return try { - val inputStream = if (uri.path.startsWith("/android_asset/")) { - mContext.assets.open(uri.path.replace("/android_asset/", "")) - } else contentResolver.openInputStream(Uri.parse(uri.toString())) - if (inputStream != null) { - createModuleEncryptionSource(inputStream, uri, base, validator) - } else null - } catch (e: Exception) { - null - } - } - - private fun createModuleEncryptionSource( - inputStream: InputStream, - uri: URI, - base: URI?, - validator: Any?, - ): ModuleSource { - val bytes = ByteArray(inputStream.available()) - inputStream.read(bytes) - inputStream.close() - val i = if (isValidFile(bytes)) { - val clearText = decrypt(bytes, EncryptedScriptFileHeader.BLOCK_SIZE, bytes.size) - ByteArrayInputStream(clearText) - } else ByteArrayInputStream(bytes) - return createModuleSource(i, uri, base, validator) - } - - private fun loadFromHttp(uri: URI, base: URI?, validator: Any?): ModuleSource? { - return try { - Request.Builder().url(uri.toString()).build().let { request -> - val response = okHttpClient.newCall(request).execute() + Request.Builder().url(url).build().let { request -> + val response = mOkHttpClient.newCall(request).execute() if (!response.isSuccessful) { - response.close() - return null + return null.also { response.close() } } - response.body?.let { - val charset = it.contentType()?.charset()?.toString() ?: "utf-8" - return createModuleSource(it.byteStream(), uri, base, validator, charset) + response.body.let { + return createModuleSource(it.byteStream(), URI.create(url), null, validator, it.contentType()?.charset()) } } } catch (e: Exception) { - null + null.also { ScriptRuntime.popException(e.message) } } } - private fun createModuleSource( - inputStream: InputStream, - uri: URI, - base: URI?, - validator: Any?, - charset: String? = null - ): ModuleSource { - val id = if (uri.scheme == "file") { - URI.create(uri.path) - } else uri - return ModuleSource( - InputStreamReader(inputStream, charset ?: "utf-8"), - null, - id, - base, - validator - ) + private fun loadFromFile(file: File, parentFile: File? = file.parentFile, validator: Any?): ModuleSource? { + return loadFromFile(file.toURI(), parentFile?.toURI(), validator) } + + private fun loadFromFile(uri: URI, parent: URI?, validator: Any?): ModuleSource? { + val inputStream = context.contentResolver.openInputStream(Uri.parse(uri.toString())) ?: return null + return try { + createModuleSource(inputStream, uri, parent, validator) + } catch (e: FileNotFoundException) { + null.also { ScriptRuntime.popException(e.message) } + } + } + + private fun createModuleSource(stream: InputStream, uri: URI, base: URI?, validator: Any?, charset: Charset? = null): ModuleSource { + val streamReader = InputStreamReader(stream, charset ?: StandardCharsets.UTF_8) + return ModuleSource(streamReader, null, uri, base, validator) + } + + @Throws(IOException::class) + override fun getReader(urlConnection: URLConnection): Reader { + val stream = urlConnection.getInputStream() + val bytes = ByteArray(stream.available()).also { + stream.read(it) + stream.close() + } + return if (isValidFile(bytes)) { + val clearText = decrypt(bytes, EncryptedScriptFileHeader.BLOCK_SIZE, bytes.size) + InputStreamReader(ByteArrayInputStream(clearText)) + } else { + InputStreamReader(ByteArrayInputStream(bytes)) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/engine/module/ScopeRequire.kt b/app/src/main/java/org/autojs/autojs/engine/module/ScopeRequire.kt deleted file mode 100644 index bb38955e..00000000 --- a/app/src/main/java/org/autojs/autojs/engine/module/ScopeRequire.kt +++ /dev/null @@ -1,261 +0,0 @@ -package org.autojs.autojs.engine.module - -import org.mozilla.javascript.* -import org.mozilla.javascript.commonjs.module.ModuleScope -import org.mozilla.javascript.commonjs.module.ModuleScript -import org.mozilla.javascript.commonjs.module.ModuleScriptProvider - -import java.io.File -import java.net.URI -import java.net.URISyntaxException -import java.util.concurrent.ConcurrentHashMap - -open class ScopeRequire( - cx: Context, private val nativeScope: Scriptable, - private val moduleScriptProvider: ModuleScriptProvider, private val preExec: Script?, - private val postExec: Script?, private val sandboxed: Boolean = true -) : BaseFunction() { - private var paths: Scriptable? = null - private var mainModuleId: String? = null - private var mainExports: Scriptable? = null - - // Modules that completed loading; visible to all threads - private val exportedModuleInterfaces: MutableMap = ConcurrentHashMap() - private val loadLock = Any() - - constructor(cx: Context, nativeScope: Scriptable, moduleScriptProvider: ModuleScriptProvider) - : this(cx, nativeScope, moduleScriptProvider, null, null, false) - - init { - prototype = getFunctionPrototype(nativeScope) - if (!sandboxed) { - paths = cx.newArray(nativeScope, 0) - defineReadOnlyProperty(this, "paths", paths) - } else paths = null - } - - - fun requireMain(cx: Context, mainModuleId: String): Scriptable? { - if (this.mainModuleId != null) { - if (this.mainModuleId != mainModuleId) { - throw IllegalStateException("Main module already set to " + this.mainModuleId) - } - return mainExports - } - val moduleScript: ModuleScript? = try { - moduleScriptProvider.getModuleScript(cx, mainModuleId, null, null, paths) - } catch (x: RuntimeException) { - throw x - } catch (x: Exception) { - throw RuntimeException(x) - } - if (moduleScript != null) { - mainExports = getExportedModuleInterface( - cx, mainModuleId, - null, null, true - ) - } else if (!sandboxed) { - var mainUri: URI? = try { - URI(mainModuleId) - } catch (_: URISyntaxException) { - null - } - if (mainUri == null || !mainUri.isAbsolute) { - val file = File(mainModuleId) - if (!file.isFile) { - throw ScriptRuntime.throwError( - cx, nativeScope, - "Module \"$mainModuleId\" not found." - ) - } - mainUri = file.toURI() - } - mainExports = getExportedModuleInterface( - cx, mainUri.toString(), - mainUri, null, true - ) - } - this.mainModuleId = mainModuleId - return mainExports - } - - fun install(scope: Scriptable?) { - putProperty(scope, "require", this) - } - - override fun call(cx: Context, scope: Scriptable, thisObj: Scriptable, args: Array?): Any { - if (args == null || args.isEmpty()) { - throw ScriptRuntime.throwError( - cx, scope, - "require() needs one argument" - ) - } - var id = Context.jsToJava(args[0], String::class.java) as String - var uri: URI? = null - var base: URI? = null - if (id.startsWith("./") || id.startsWith("../")) { - if (thisObj !is ModuleScope) { - throw ScriptRuntime.throwError( - cx, scope, - "Can't resolve relative module ID \"" + id + - "\" when require() is used outside of a module" - ) - } - base = thisObj.base - val current = thisObj.uri - uri = current.resolve(id) - if (base == null) { - id = uri.toString() - } else { - id = base.relativize(current).resolve(id).toString() - if (id[0] == '.') { - if (sandboxed) { - throw ScriptRuntime.throwError( - cx, scope, - "Module \"$id\" is not contained in sandbox." - ) - } - id = uri.toString() - } - } - } - return (getExportedModuleInterface(cx, id, uri, base, false))!! - } - - override fun construct(cx: Context, scope: Scriptable, args: Array): Scriptable { - throw ScriptRuntime.throwError( - cx, scope, - "require() can not be invoked as a constructor" - ) - } - - private fun getExportedModuleInterface( - cx: Context, id: String, uri: URI?, base: URI?, isMain: Boolean - ): Scriptable? { - // Check if the requested module is already completely loaded - var exports = exportedModuleInterfaces[id] - if (exports != null) { - if (isMain) { - throw IllegalStateException("Attempt to set main module after it was loaded") - } else - return exports - } - var threadLoadingModules: MutableMap? = - loadingModuleInterfaces.get() as? MutableMap - exports = threadLoadingModules?.get(id) - if (exports != null) return exports - - synchronized(loadLock) { - exports = exportedModuleInterfaces[id] - if (exports != null) return exports - - val moduleScript: ModuleScript = getModule(cx, id, uri, base) - if (sandboxed && !moduleScript.isSandboxed) { - throw ScriptRuntime.throwError( - cx, nativeScope, ("Module \"$id\" is not contained in sandbox.") - ) - } - exports = cx.newObject(nativeScope) - val outermostLocked: Boolean = threadLoadingModules == null - if (outermostLocked) { - threadLoadingModules = HashMap() - loadingModuleInterfaces.set(threadLoadingModules) - } - - threadLoadingModules?.set(id, exports!!) - try { - val newExports: Scriptable = executeModuleScript( - cx, id, exports, - moduleScript, isMain - ) - if (exports !== newExports) { - threadLoadingModules?.put(id, newExports) - exports = newExports - } - } catch (e: RuntimeException) { - threadLoadingModules?.remove(id) - throw e - } finally { - if (outermostLocked) { - exportedModuleInterfaces.putAll((threadLoadingModules!!)) - loadingModuleInterfaces.set(null) - } - } - } - return exports - } - - private fun executeModuleScript( - cx: Context, id: String, - exports: Scriptable?, moduleScript: ModuleScript, isMain: Boolean - ): Scriptable { - val moduleObject = cx.newObject(nativeScope) as ScriptableObject - val uri = moduleScript.uri - val base = moduleScript.base - defineReadOnlyProperty(moduleObject, "id", id) - if (!sandboxed) { - defineReadOnlyProperty(moduleObject, "uri", uri.toString()) - } - val executionScope: Scriptable = ModuleScope(nativeScope, uri, base) - executionScope.put("__filename", executionScope, File(uri.path).path) - executionScope.put("__dirname", executionScope, File(uri.path).parent) - executionScope.put("exports", executionScope, exports) - executionScope.put("module", executionScope, moduleObject) - moduleObject.put("exports", moduleObject, exports) - install(executionScope) - if (isMain) { - defineReadOnlyProperty(this, "main", moduleObject) - } - //创建新作用域 - val funScope = cx.newObject(executionScope) - funScope.parentScope = executionScope - - executeOptionalScript(preExec, cx, funScope) - moduleScript.script.exec(cx, funScope) - executeOptionalScript(postExec, cx, funScope) - return ScriptRuntime.toObject( - cx, nativeScope, - getProperty(moduleObject, "exports") - ) - } - - private fun getModule(cx: Context, id: String, uri: URI?, base: URI?): ModuleScript { - try { - return moduleScriptProvider.getModuleScript(cx, id, uri, base, paths) - ?: throw ScriptRuntime.throwError( - cx, nativeScope, ("Module \"$id\" not found.") - ) - } catch (e: RuntimeException) { - throw e - } catch (e: Exception) { - throw Context.throwAsScriptRuntimeEx(e) - } - } - - override fun getFunctionName() = "require" - override fun getArity() = 1 - override fun getLength() = 1 - - companion object { - private const val serialVersionUID = 1L - - private val loadingModuleInterfaces = ThreadLocal>() - private fun executeOptionalScript( - script: Script?, cx: Context, - executionScope: Scriptable - ) { - script?.exec(cx, executionScope) - } - - private fun defineReadOnlyProperty( - obj: ScriptableObject, - name: String, value: Any? - ) { - putProperty(obj, name, value) - obj.setAttributes( - name, READONLY or - PERMANENT - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/engine/module/UrlModuleSourceProvider.java b/app/src/main/java/org/autojs/autojs/engine/module/UrlModuleSourceProvider.java new file mode 100644 index 00000000..82b1a7e2 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/engine/module/UrlModuleSourceProvider.java @@ -0,0 +1,360 @@ +package org.autojs.autojs.engine.module; + +import org.mozilla.javascript.commonjs.module.provider.DefaultUrlConnectionExpiryCalculator; +import org.mozilla.javascript.commonjs.module.provider.ModuleSource; +import org.mozilla.javascript.commonjs.module.provider.ModuleSourceProviderBase; +import org.mozilla.javascript.commonjs.module.provider.ParsedContentType; +import org.mozilla.javascript.commonjs.module.provider.UrlConnectionExpiryCalculator; +import org.mozilla.javascript.commonjs.module.provider.UrlConnectionSecurityDomainProvider; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.Iterator; +import java.util.List; + + +/** + * A URL-based script provider that can load modules against a set of base + * privileged and fallback URIs. It is deliberately not named "URI provider" + * but a "URL provider" since it actually only works against those URIs that + * are URLs (and the JRE has a protocol handler for them). It creates cache + * validators that are suitable for use with both file: and http: URL + * protocols. Specifically, it is able to use both last-modified timestamps and + * ETags for cache revalidation, and follows the HTTP cache expiry calculation + * model, and allows for fallback heuristic expiry calculation when no server + * specified expiry is provided. + * + * @author Attila Szegedi + * @version $Id: UrlModuleSourceProvider.java,v 1.4 2011/04/07 20:26:12 hannes%helma.at Exp $ + */ +public class UrlModuleSourceProvider extends ModuleSourceProviderBase { + private static final long serialVersionUID = 1L; + + private final Iterable privilegedUris; + private final Iterable fallbackUris; + private final UrlConnectionSecurityDomainProvider + urlConnectionSecurityDomainProvider; + private final UrlConnectionExpiryCalculator urlConnectionExpiryCalculator; + + /** + * Creates a new module script provider that loads modules against a set of + * privileged and fallback URIs. It will use a fixed default cache expiry + * of 60 seconds, and provide no security domain objects for the resource. + * + * @param privilegedUris an iterable providing the privileged URIs. Can be + * null if no privileged URIs are used. + * @param fallbackUris an iterable providing the fallback URIs. Can be + * null if no fallback URIs are used. + */ + public UrlModuleSourceProvider(Iterable privilegedUris, + Iterable fallbackUris) { + this(privilegedUris, fallbackUris, + new DefaultUrlConnectionExpiryCalculator(), null); + } + + /** + * Creates a new module script provider that loads modules against a set of + * privileged and fallback URIs. It will use the specified heuristic cache + * expiry calculator and security domain provider. + * + * @param privilegedUris an iterable providing the privileged URIs. Can be + * null if no privileged URIs are used. + * @param fallbackUris an iterable providing the fallback URIs. Can be + * null if no fallback URIs are used. + * @param urlConnectionExpiryCalculator the calculator object for heuristic + * calculation of the resource expiry, used when no expiry is provided by + * the server of the resource. Can be null, in which case the maximum age + * of cached entries without validation will be zero. + * @param urlConnectionSecurityDomainProvider object that provides security + * domain objects for the loaded sources. Can be null, in which case the + * loaded sources will have no security domain associated with them. + */ + public UrlModuleSourceProvider(Iterable privilegedUris, + Iterable fallbackUris, + UrlConnectionExpiryCalculator urlConnectionExpiryCalculator, + UrlConnectionSecurityDomainProvider urlConnectionSecurityDomainProvider) { + this.privilegedUris = privilegedUris; + this.fallbackUris = fallbackUris; + this.urlConnectionExpiryCalculator = urlConnectionExpiryCalculator; + this.urlConnectionSecurityDomainProvider = + urlConnectionSecurityDomainProvider; + } + + @Override + protected ModuleSource loadFromPrivilegedLocations( + String moduleId, Object validator) + throws IOException, URISyntaxException { + return loadFromPathList(moduleId, validator, privilegedUris); + } + + @Override + protected ModuleSource loadFromFallbackLocations( + String moduleId, Object validator) + throws IOException, URISyntaxException { + return loadFromPathList(moduleId, validator, fallbackUris); + } + + private ModuleSource loadFromPathList(String moduleId, + Object validator, Iterable paths) + throws IOException, URISyntaxException { + if (paths == null) { + return null; + } + for (URI path : paths) { + final ModuleSource moduleSource = loadFromUri( + path.resolve(moduleId), path, validator); + if (moduleSource != null) { + return moduleSource; + } + } + return null; + } + + @Override + protected ModuleSource loadFromUri(URI uri, URI base, Object validator) + throws IOException, URISyntaxException { + // We expect modules to have a ".js" file name extension ... + URI fullUri = new URI(uri + ".js"); + ModuleSource source = loadFromActualUri(fullUri, base, validator); + // ... but for compatibility we support modules without extension, + // or ids with explicit extension. + return source != null ? + source : loadFromActualUri(uri, base, validator); + } + + protected ModuleSource loadFromActualUri(URI uri, URI base, Object validator) + throws IOException { + final URL url = new URL(base == null ? null : base.toURL(), uri.toString()); + final long request_time = System.currentTimeMillis(); + final URLConnection urlConnection = openUrlConnection(url); + final URLValidator applicableValidator; + if (validator instanceof final URLValidator uriValidator) { + applicableValidator = uriValidator.appliesTo(uri) ? uriValidator : + null; + } else { + applicableValidator = null; + } + if (applicableValidator != null) { + applicableValidator.applyConditionals(urlConnection); + } + try { + urlConnection.connect(); + if (applicableValidator != null && + applicableValidator.updateValidator(urlConnection, + request_time, urlConnectionExpiryCalculator)) { + close(urlConnection); + return NOT_MODIFIED; + } + + return new ModuleSource(getReader(urlConnection), + getSecurityDomain(urlConnection), uri, base, + new URLValidator(uri, urlConnection, request_time, + urlConnectionExpiryCalculator)); + } catch (FileNotFoundException e) { + return null; + } catch (RuntimeException | IOException e) { + close(urlConnection); + throw e; + } + } + + protected Reader getReader(URLConnection urlConnection) + throws IOException { + return new InputStreamReader(urlConnection.getInputStream(), + getCharacterEncoding(urlConnection)); + } + + protected String getCharacterEncoding(URLConnection urlConnection) { + final ParsedContentType pct = new ParsedContentType( + urlConnection.getContentType()); + final String encoding = pct.getEncoding(); + if (encoding != null) { + return encoding; + } + final String contentType = pct.getContentType(); + if (contentType != null && contentType.startsWith("text/")) { + return "8859_1"; + } + return "utf-8"; + } + + protected Object getSecurityDomain(URLConnection urlConnection) { + return urlConnectionSecurityDomainProvider == null ? null : + urlConnectionSecurityDomainProvider.getSecurityDomain( + urlConnection); + } + + private void close(URLConnection urlConnection) { + try { + urlConnection.getInputStream().close(); + } catch (IOException e) { + onFailedClosingUrlConnection(urlConnection, e); + } + } + + /** + * Override if you want to get notified if the URL connection fails to + * close. Does nothing by default. + * + * @param urlConnection the connection + * @param cause the cause it failed to close. + */ + protected void onFailedClosingUrlConnection(URLConnection urlConnection, + IOException cause) { + } + + /** + * Can be overridden in subclasses to customize the URL connection opening + * process. By default, just calls {@link URL#openConnection()}. + * + * @param url the URL + * @return a connection to the URL. + * @throws IOException if an I/O error occurs. + */ + protected URLConnection openUrlConnection(URL url) throws IOException { + return url.openConnection(); + } + + @Override + protected boolean entityNeedsRevalidation(Object validator) { + return !(validator instanceof URLValidator) + || ((URLValidator) validator).entityNeedsRevalidation(); + } + + private static class URLValidator implements Serializable { + private static final long serialVersionUID = 1L; + + private final URI uri; + private final long lastModified; + private final String entityTags; + private long expiry; + + public URLValidator(URI uri, URLConnection urlConnection, + long request_time, UrlConnectionExpiryCalculator + urlConnectionExpiryCalculator) { + this.uri = uri; + this.lastModified = urlConnection.getLastModified(); + this.entityTags = getEntityTags(urlConnection); + expiry = calculateExpiry(urlConnection, request_time, + urlConnectionExpiryCalculator); + } + + boolean updateValidator(URLConnection urlConnection, long request_time, + UrlConnectionExpiryCalculator urlConnectionExpiryCalculator) + throws IOException { + boolean isResourceChanged = isResourceChanged(urlConnection); + if (!isResourceChanged) { + expiry = calculateExpiry(urlConnection, request_time, + urlConnectionExpiryCalculator); + } + return isResourceChanged; + } + + private boolean isResourceChanged(URLConnection urlConnection) + throws IOException { + if (urlConnection instanceof HttpURLConnection) { + return ((HttpURLConnection) urlConnection).getResponseCode() == + HttpURLConnection.HTTP_NOT_MODIFIED; + } + return lastModified != urlConnection.getLastModified(); + } + + private long calculateExpiry(URLConnection urlConnection, + long request_time, UrlConnectionExpiryCalculator + urlConnectionExpiryCalculator) { + if ("no-cache".equals(urlConnection.getHeaderField("Pragma"))) { + return 0L; + } + final String cacheControl = urlConnection.getHeaderField( + "Cache-Control"); + if (cacheControl != null) { + if (cacheControl.contains("no-cache")) { + return 0L; + } + final int max_age = getMaxAge(cacheControl); + if (-1 != max_age) { + final long response_time = System.currentTimeMillis(); + final long apparent_age = Math.max(0, response_time - + urlConnection.getDate()); + final long corrected_received_age = Math.max(apparent_age, + urlConnection.getHeaderFieldInt("Age", 0) * 1000L); + final long response_delay = response_time - request_time; + final long corrected_initial_age = corrected_received_age + + response_delay; + final long creation_time = response_time - + corrected_initial_age; + return max_age * 1000L + creation_time; + } + } + final long explicitExpiry = urlConnection.getHeaderFieldDate( + "Expires", -1L); + if (explicitExpiry != -1L) { + return explicitExpiry; + } + return urlConnectionExpiryCalculator == null ? 0L : + urlConnectionExpiryCalculator.calculateExpiry(urlConnection); + } + + private int getMaxAge(String cacheControl) { + final int maxAgeIndex = cacheControl.indexOf("max-age"); + if (maxAgeIndex == -1) { + return -1; + } + final int eq = cacheControl.indexOf('=', maxAgeIndex + 7); + if (eq == -1) { + return -1; + } + final int comma = cacheControl.indexOf(',', eq + 1); + final String strAge; + if (comma == -1) { + strAge = cacheControl.substring(eq + 1); + } else { + strAge = cacheControl.substring(eq + 1, comma); + } + try { + return Integer.parseInt(strAge); + } catch (NumberFormatException e) { + return -1; + } + } + + private String getEntityTags(URLConnection urlConnection) { + final List etags = urlConnection.getHeaderFields().get("ETag"); + if (etags == null || etags.isEmpty()) { + return null; + } + final StringBuilder b = new StringBuilder(); + final Iterator it = etags.iterator(); + b.append(it.next()); + while (it.hasNext()) { + b.append(", ").append(it.next()); + } + return b.toString(); + } + + boolean appliesTo(URI uri) { + return this.uri.equals(uri); + } + + void applyConditionals(URLConnection urlConnection) { + if (lastModified != 0L) { + urlConnection.setIfModifiedSince(lastModified); + } + if (entityTags != null && entityTags.length() > 0) { + urlConnection.addRequestProperty("If-None-Match", entityTags); + } + } + + boolean entityNeedsRevalidation() { + return System.currentTimeMillis() > expiry; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/model/autocomplete/CodeCompletion.kt b/app/src/main/java/org/autojs/autojs/model/autocomplete/CodeCompletion.kt index 2a2a00ce..c0e8aadb 100644 --- a/app/src/main/java/org/autojs/autojs/model/autocomplete/CodeCompletion.kt +++ b/app/src/main/java/org/autojs/autojs/model/autocomplete/CodeCompletion.kt @@ -2,7 +2,7 @@ package org.autojs.autojs.model.autocomplete /** * Created by Stardust on 2018/2/3. - * Transformed by 抠脚本人 on Jul 11, 2023. + * Transformed by 抠脚本人 (https://github.com/little-alei) on Jul 11, 2023. */ class CodeCompletion { val hint: String diff --git a/app/src/main/java/org/autojs/autojs/model/autocomplete/CodeCompletions.kt b/app/src/main/java/org/autojs/autojs/model/autocomplete/CodeCompletions.kt index 60b82405..19387b77 100644 --- a/app/src/main/java/org/autojs/autojs/model/autocomplete/CodeCompletions.kt +++ b/app/src/main/java/org/autojs/autojs/model/autocomplete/CodeCompletions.kt @@ -2,7 +2,7 @@ package org.autojs.autojs.model.autocomplete /** * Created by Stardust on 2017/9/27. - * Transformed by 抠脚本人 on Jul 11, 2023. + * Transformed by 抠脚本人 (https://github.com/little-alei) on Jul 11, 2023. */ class CodeCompletions(val from: Int, private val mCompletions: List) { fun size(): Int { diff --git a/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginResponseHandler.java b/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginResponseHandler.java index fd1d3a75..d98d3437 100644 --- a/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginResponseHandler.java +++ b/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginResponseHandler.java @@ -162,7 +162,7 @@ public class DevPluginResponseHandler implements Handler { File file = new File(WorkingDirectoryUtils.getPath(), name); PFiles.ensureDir(file.getPath()); PFiles.write(file, script); - ViewUtils.showToast(mContext, R.string.text_script_save_successfully, true); + ViewUtils.showToast(mContext, R.string.text_script_save_succeeded, true); } @SuppressWarnings("ResultOfMethodCallIgnored") @@ -180,7 +180,7 @@ public class DevPluginResponseHandler implements Handler { }; Consumer stringConsumer = dest -> ViewUtils - .showToast(mContext, mContext.getString(R.string.text_project_save_success) + "\n" + dest); + .showToast(mContext, mContext.getString(R.string.text_project_save_succeeded) + "\n" + dest); Consumer throwableConsumer = err -> ViewUtils .showToast(mContext, mContext.getString(R.string.text_project_save_error) + "\n" + err.getMessage()); diff --git a/app/src/main/java/org/autojs/autojs/rhino/AndroidClassLoader.kt b/app/src/main/java/org/autojs/autojs/rhino/AndroidClassLoader.kt index 65d1846c..c105586b 100644 --- a/app/src/main/java/org/autojs/autojs/rhino/AndroidClassLoader.kt +++ b/app/src/main/java/org/autojs/autojs/rhino/AndroidClassLoader.kt @@ -29,6 +29,8 @@ import java.io.IOException */ class AndroidClassLoader(private val parent: ClassLoader, private val cacheDir: File) : ClassLoader(), GeneratedClassLoader { + private val mDexClassLoaders = HashMap() + init { if (cacheDir.exists()) { deleteFilesOfDir(cacheDir) @@ -52,7 +54,7 @@ class AndroidClassLoader(private val parent: ClassLoader, private val cacheDir: throw FileNotFoundException(str(R.string.file_not_exist_or_readable, path)) } return DexClassLoader(path, cacheDir.path, null, parent).also { - dexClassLoaders[path] = it + mDexClassLoaders[path] = it } } @@ -99,7 +101,7 @@ class AndroidClassLoader(private val parent: ClassLoader, private val cacheDir: @Throws(ClassNotFoundException::class) public override fun loadClass(name: String, resolve: Boolean): Class<*> { findLoadedClass(name)?.let { return it } - for (dex in dexClassLoaders.values) try { + for (dex in mDexClassLoaders.values) try { dex.loadClass(name)?.let { return it } } catch (e: Exception) { e.printStackTrace() @@ -114,7 +116,7 @@ class AndroidClassLoader(private val parent: ClassLoader, private val cacheDir: * @param aClass ignored */ override fun linkClass(aClass: Class<*>?) { - //doesn't make sense on android + // doesn't make sense on android } /** @@ -182,7 +184,6 @@ class AndroidClassLoader(private val parent: ClassLoader, private val cacheDir: companion object { private val TAG = AndroidClassLoader::class.java.simpleName - private val dexClassLoaders = HashMap() } diff --git a/app/src/main/java/org/autojs/autojs/runtime/ScriptRuntime.java b/app/src/main/java/org/autojs/autojs/runtime/ScriptRuntime.java index 745f74c9..b61c30e7 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/ScriptRuntime.java +++ b/app/src/main/java/org/autojs/autojs/runtime/ScriptRuntime.java @@ -7,7 +7,9 @@ import android.os.Build; import android.os.Looper; import android.util.Log; +import org.autojs.autojs.core.permission.Permissions; import org.autojs.autojs.AutoJs; +import org.autojs.autojs.annotation.ScriptInterface; import org.autojs.autojs.annotation.ScriptVariable; import org.autojs.autojs.concurrent.VolatileDispose; import org.autojs.autojs.core.accessibility.AccessibilityBridge; @@ -449,7 +451,7 @@ public class ScriptRuntime { } } - public void load(String ...path) { + public void load(String... path) { load(f -> isJarFile(f) || isDexFile(f), path); } @@ -529,17 +531,18 @@ public class ScriptRuntime { } }); - // 清空无障碍事件 + // @Hint by 抠脚本人 on Jul 10, 2023. + // ! 清空无障碍事件. ignoresException(AccessibilityService::clearAccessibilityEventCallback); ignoresException(RootUtils::resetRuntimeOverriddenRootModeState); ignoresException(ImageWrapper::recycleAll); - // 清除 interrupt 状态 + /* 清除 interrupt 状态. */ ignoresException(ThreadCompat::interrupted); - // 浮动窗口需要第一时间关闭 - // 以免出现恶意脚本全屏浮动窗口遮蔽屏幕并且在 exit 中写死循环的问题 + /* 浮动窗口需要第一时间关闭. */ + /* 以免出现恶意脚本全屏浮动窗口遮蔽屏幕并且在 exit 中写死循环的问题. */ ignoresException(floaty::closeAll); ignoresException(() -> events.emit("exit"), "exception on exit: %s"); @@ -639,4 +642,13 @@ public class ScriptRuntime { } } + @ScriptInterface + public void requestPermissions(String[] permissions) { + Context context = uiHandler.getContext(); + permissions = Permissions.getPermissionsNeedToRequest(context, permissions); + if (permissions.length == 0) + return; + Permissions.requestPermissions(context, permissions); + } + } diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/Events.java b/app/src/main/java/org/autojs/autojs/runtime/api/Events.java index bf74b403..d615fe25 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/Events.java +++ b/app/src/main/java/org/autojs/autojs/runtime/api/Events.java @@ -10,6 +10,7 @@ import android.view.KeyEvent; import androidx.annotation.NonNull; +import org.autojs.autojs.annotation.ScriptInterface; import org.autojs.autojs.core.accessibility.AccessibilityBridge; import org.autojs.autojs.core.accessibility.AccessibilityNotificationObserver; import org.autojs.autojs.core.accessibility.AccessibilityService; @@ -21,6 +22,7 @@ import org.autojs.autojs.core.eventloop.EventEmitter; import org.autojs.autojs.core.inputevent.InputEventObserver; import org.autojs.autojs.core.inputevent.TouchObserver; import org.autojs.autojs.core.looper.Loopers; +import org.autojs.autojs.core.looper.MainThreadProxy; import org.autojs.autojs.core.looper.Timer; import org.autojs.autojs.core.notification.Notification; import org.autojs.autojs.core.notification.NotificationListenerService; @@ -95,6 +97,11 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver return new EventEmitter(mBridges, timer); } + @SuppressWarnings("unused") + @ScriptInterface + public EventEmitter emitter(MainThreadProxy mainThreadProxy) { + return new EventEmitter(mBridges, mScriptRuntime.timers.getMainTimer()); + } public void observeKey() { if (mListeningKey) diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/MlKitOCR.kt b/app/src/main/java/org/autojs/autojs/runtime/api/MlKitOCR.kt index f2064cfb..9a2df074 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/MlKitOCR.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/MlKitOCR.kt @@ -10,7 +10,7 @@ import org.autojs.autojs.core.image.ImageWrapper /** * Created by SuperMonster003 on Mar 18, 2023. */ -// @Reference to TonyJiangWJ/Auto.js on Mar 18, 2023. +// @Reference to TonyJiangWJ/Auto.js (https://github.com/TonyJiangWJ/Auto.js) on Mar 18, 2023. class MlKitOCR { private var recognizer: TextRecognizer? = null diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/OcrResult.kt b/app/src/main/java/org/autojs/autojs/runtime/api/OcrResult.kt index b86ac7af..b5776cd4 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/OcrResult.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/OcrResult.kt @@ -4,6 +4,7 @@ import android.graphics.Rect import kotlin.math.abs // @Reference to com.baidu.paddle.lite.ocr.OcrResult on Mar 18, 2023. +// ! https://github.com/PaddlePaddle/PaddleOCR class OcrResult(@JvmField val label: String, @JvmField val confidence: Float, @JvmField val bounds: Rect) : Comparable { override fun compareTo(other: OcrResult): Int { diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/Sensors.java b/app/src/main/java/org/autojs/autojs/runtime/api/Sensors.java index 5e823b79..90c0e28d 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/Sensors.java +++ b/app/src/main/java/org/autojs/autojs/runtime/api/Sensors.java @@ -10,6 +10,7 @@ import androidx.annotation.NonNull; import org.autojs.autojs.core.eventloop.EventEmitter; import org.autojs.autojs.core.looper.Loopers; +import org.autojs.autojs.pref.Language; import org.autojs.autojs.runtime.ScriptBridges; import org.autojs.autojs.runtime.ScriptRuntime; import org.autojs.autojs.tool.MapBuilder; @@ -22,7 +23,7 @@ import java.util.Set; /** * Created by Stardust on 2018/2/5. */ -public class Sensors extends EventEmitter { +public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler { public class SensorEventEmitter extends EventEmitter implements SensorEventListener { @@ -82,13 +83,6 @@ public class Sensors extends EventEmitter { private final ScriptBridges mScriptBridges; private final SensorEventEmitter mNoOpSensorEventEmitter; private final ScriptRuntime mScriptRuntime; - private final Loopers.AsyncTask mAsyncTask = new Loopers.AsyncTask("Sensors"){ - @Override - public boolean onFinish(@NonNull Loopers loopers) { - return !mSensorEventEmitters.isEmpty(); - } - }; - public Sensors(Context context, ScriptRuntime runtime) { super(runtime.bridges); @@ -96,7 +90,7 @@ public class Sensors extends EventEmitter { mScriptBridges = runtime.bridges; mNoOpSensorEventEmitter = new SensorEventEmitter(runtime.bridges); mScriptRuntime = runtime; - runtime.loopers.addAsyncTask(mAsyncTask); + runtime.loopers.addLooperQuitHandler(this); } public SensorEventEmitter register(String sensorName) { @@ -128,8 +122,13 @@ public class Sensors extends EventEmitter { return emitter; } + @Override + public boolean shouldQuit() { + return mSensorEventEmitters.isEmpty(); + } + public Sensor getSensor(String sensorName) { - sensorName = sensorName.toUpperCase(); + sensorName = sensorName.toUpperCase(Language.getPrefLanguage().getLocale()); Integer type = SENSORS.get(sensorName); type = type == null ? getSensorTypeByReflect(sensorName) : type; return type == null ? null : mSensorManager.getDefaultSensor(type); @@ -160,6 +159,6 @@ public class Sensors extends EventEmitter { } mSensorEventEmitters.clear(); } - mScriptRuntime.loopers.removeAsyncTask(mAsyncTask); + mScriptRuntime.loopers.removeLooperQuitHandler(this); } } diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/Threads.kt b/app/src/main/java/org/autojs/autojs/runtime/api/Threads.kt index d3be8e14..6a850839 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/Threads.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/Threads.kt @@ -1,15 +1,11 @@ package org.autojs.autojs.runtime.api import org.autojs.autojs.concurrent.VolatileDispose -import org.autojs.autojs.core.looper.Loopers import org.autojs.autojs.core.looper.MainThreadProxy import org.autojs.autojs.core.looper.TimerThread import org.autojs.autojs.runtime.ScriptRuntime -import org.autojs.autojs.runtime.exception.ScriptInterruptedException -import org.mozilla.javascript.BaseFunction -import org.mozilla.javascript.Context -import java.util.concurrent.Executors -import java.util.concurrent.ThreadFactory +import org.autojs.autojs.util.StringUtils.str +import org.autojs.autojs6.R import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.locks.ReentrantLock @@ -17,91 +13,58 @@ import java.util.concurrent.locks.ReentrantLock * Created by Stardust on 2017/12/3. */ class Threads(private val mRuntime: ScriptRuntime) { + private val mThreads = HashSet() - val mainThread: Thread = Thread.currentThread() private val mMainThreadProxy = MainThreadProxy(Thread.currentThread(), mRuntime) private var mSpawnCount = 0 - private var mTaskCount = AtomicLong(0) private var mExit = false - private val looperTask = Loopers.AsyncTask("AsyncTaskThreadPool") - private val threadPool = Executors.newFixedThreadPool(20, ThreadFactory { - val thread = Thread(fun() { - Context.enter() - try { - it.run() - } finally { - Context.exit() - } - }) - thread.name = mainThread.name + " (AsyncThread)" - thread - }) - fun currentThread(): Any { - val thread = Thread.currentThread() - return if (thread === mainThread) mMainThreadProxy else thread - } + val mainThread: Thread = Thread.currentThread() - fun runTaskForThreadPool(runnable: BaseFunction) { - if (mTaskCount.addAndGet(1) == 1L) mRuntime.loopers.addAsyncTask(looperTask) - threadPool.execute { - try { - runnable.call( - Context.getCurrentContext(), runnable.parentScope, runnable, - emptyArray() - ) - } catch (e: Throwable) { - if (!ScriptInterruptedException.causedByInterrupted(e)) { - mRuntime.console.error("$this: ", e) - } - } finally { - if (mTaskCount.addAndGet(-1) == 0L) { - mRuntime.loopers.removeAsyncTask(looperTask) - } - } - } + fun currentThread(): Any = Thread.currentThread().let { thread -> + if (thread === mainThread) mMainThreadProxy else thread } fun start(runnable: Runnable): TimerThread { val thread = createThread(runnable) synchronized(mThreads) { - check(!mExit) { "script exiting" } - mThreads.add(thread) - thread.name = mainThread.name + " (Spawn-" + mSpawnCount + ")" - mSpawnCount++ - thread.start() + check(!mExit) { str(R.string.error_script_is_on_exiting) } + thread.let { + mThreads.add(it) + it.name = "${mainThread.name} (Spawn-$mSpawnCount)" + mSpawnCount++ + it.start() + } } return thread } private fun createThread(runnable: Runnable): TimerThread { - return object : TimerThread(mRuntime, runnable) { + val millis = mRuntime.timers.maxCallbackUptimeMillisForAllThreads + return object : TimerThread(mRuntime, millis, runnable) { + override fun onExit() { synchronized(mThreads) { mThreads.remove(currentThread()) } super.onExit() } + } } - fun disposable(): VolatileDispose<*> { - return VolatileDispose() - } + fun disposable() = VolatileDispose() - fun atomic(value: Long): AtomicLong { - return AtomicLong(value) - } + fun atomic(value: Long) = AtomicLong(value) fun atomic() = AtomicLong() fun lock() = ReentrantLock() fun shutDownAll() { - threadPool.shutdownNow() synchronized(mThreads) { - for (thread in mThreads) { - thread.interrupt() + mThreads.apply { + forEach { it.interrupt() } + clear() } - mThreads.clear() } } @@ -112,7 +75,6 @@ class Threads(private val mRuntime: ScriptRuntime) { } } - fun hasRunningThreads(): Boolean { - synchronized(mThreads) { return mThreads.isNotEmpty() } - } + fun hasRunningThreads(): Boolean = synchronized(mThreads) { return mThreads.isNotEmpty() } + } \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/Timers.java b/app/src/main/java/org/autojs/autojs/runtime/api/Timers.java index 56e5628a..a8ee8b81 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/Timers.java +++ b/app/src/main/java/org/autojs/autojs/runtime/api/Timers.java @@ -1,6 +1,9 @@ package org.autojs.autojs.runtime.api; import android.os.Looper; +import android.os.SystemClock; + +import org.autojs.autojs.concurrent.VolatileBox; import org.autojs.autojs.core.looper.Timer; import org.autojs.autojs.core.looper.TimerThread; import org.autojs.autojs.runtime.ScriptRuntime; @@ -8,24 +11,26 @@ import org.autojs.autojs.runtime.ScriptRuntime; /** * Created by Stardust on 2017/7/21. */ - public class Timers { - private static final String LOG_TAG = "Timers"; + private final VolatileBox mMaxCallbackUptimeMillisForAllThreads = new VolatileBox<>(0L); + private final Threads mThreads; + private final Timer mMainTimer; + private final Timer mUiTimer; - //private VolatileBox mMaxCallbackUptimeMillisForAllThreads = new VolatileBox<>(0L); - private Threads mThreads; - private Timer mUiTimer; - private ScriptRuntime mRuntime; public Timers(ScriptRuntime runtime) { - mUiTimer = new Timer(runtime, Looper.getMainLooper()); + mMainTimer = new Timer(runtime, mMaxCallbackUptimeMillisForAllThreads); + mUiTimer = new Timer(runtime, mMaxCallbackUptimeMillisForAllThreads, Looper.getMainLooper()); mThreads = runtime.threads; - mRuntime = runtime; } public Timer getMainTimer() { - return mRuntime.loopers.getMTimer(); + return mMainTimer; + } + + VolatileBox getMaxCallbackUptimeMillisForAllThreads() { + return mMaxCallbackUptimeMillisForAllThreads; } public Timer getTimerForCurrentThread() { @@ -34,27 +39,19 @@ public class Timers { public Timer getTimerForThread(Thread thread) { if (thread == mThreads.getMainThread()) { - return mRuntime.loopers.getMTimer(); + return mMainTimer; } Timer timer = TimerThread.getTimerForThread(thread); if (timer == null && Looper.myLooper() == Looper.getMainLooper()) { return mUiTimer; } - if (timer == null) { - return mRuntime.loopers.getMTimer(); - } else { - return timer; - } + return timer; } public int setTimeout(Object callback, long delay, Object... args) { return getTimerForCurrentThread().setTimeout(callback, delay, args); } - public int setTimeout(Object callback) { - return setTimeout(callback, 1); - } - public boolean clearTimeout(int id) { return getTimerForCurrentThread().clearTimeout(id); } @@ -63,10 +60,6 @@ public class Timers { return getTimerForCurrentThread().setInterval(listener, interval, args); } - public int setInterval(Object listener) { - return setInterval(listener, 1); - } - public boolean clearInterval(int id) { return getTimerForCurrentThread().clearInterval(id); } @@ -79,8 +72,17 @@ public class Timers { return getTimerForCurrentThread().clearImmediate(id); } + public boolean hasPendingCallbacks() { + // 如果是脚本主线程,则检查所有子线程中的定时回调。mFutureCallbackUptimeMillis用来记录所有子线程中定时最久的一个。 + if (mThreads.getMainThread() == Thread.currentThread()) { + return mMaxCallbackUptimeMillisForAllThreads.get() > SystemClock.uptimeMillis(); + } + // 否则检查当前线程的定时回调 + return getTimerForCurrentThread().hasPendingCallbacks(); + } + public void recycle() { - mRuntime.loopers.getMTimer().removeAllCallbacks(); + mMainTimer.removeAllCallbacks(); } } diff --git a/app/src/main/java/org/autojs/autojs/script/JsBeautifier.java b/app/src/main/java/org/autojs/autojs/script/JsBeautifier.java index 5a2e38cd..2bdf950d 100644 --- a/app/src/main/java/org/autojs/autojs/script/JsBeautifier.java +++ b/app/src/main/java/org/autojs/autojs/script/JsBeautifier.java @@ -1,5 +1,8 @@ package org.autojs.autojs.script; +import static org.autojs.autojs.engine.RhinoJavaScriptEngine.JS_BEAUTIFY_PATH; +import static org.autojs.autojs.engine.RhinoJavaScriptEngine.JS_BEAUTIFY_FILE; + import android.content.Context; import android.view.View; @@ -37,15 +40,12 @@ public class JsBeautifier { private Function mJsBeautifyFunction; private org.mozilla.javascript.Context mScriptContext; private Scriptable mScriptable; - private final String mBeautifyJsPath; - private final String mBeautifyJsDir; + private View mView; - public JsBeautifier(View view, String beautifyJsDirPath) { + public JsBeautifier(View view) { mContext = view.getContext(); mView = view; - mBeautifyJsDir = beautifyJsDirPath; - mBeautifyJsPath = PFiles.join(beautifyJsDirPath, "beautify.js"); } public void beautify(final String code, final Callback callback) { @@ -82,8 +82,11 @@ public class JsBeautifier { importerTopLevel.initStandardObjects(mScriptContext, false); mScriptable = importerTopLevel; } - AssetAndUrlModuleSourceProvider provider = new AssetAndUrlModuleSourceProvider(mContext, mBeautifyJsDir, - Collections.singletonList(new File("/").toURI())); + AssetAndUrlModuleSourceProvider provider = new AssetAndUrlModuleSourceProvider( + mContext, + JS_BEAUTIFY_PATH, + Collections.singletonList(new File(File.separator).toURI()) + ); new RequireBuilder() .setModuleScriptProvider(new SoftCachingModuleScriptProvider(provider)) .setSandboxed(false) @@ -111,7 +114,7 @@ public class JsBeautifier { private void compile() { try { enterContext(); - InputStream is = mContext.getAssets().open(mBeautifyJsPath); + InputStream is = mContext.getAssets().open(JS_BEAUTIFY_FILE); mJsBeautifyFunction = (Function) mScriptContext.evaluateString(mScriptable, PFiles.read(is), "", 1, null); } catch (IOException e) { exitContext(); @@ -119,7 +122,7 @@ public class JsBeautifier { } } - public void shutdown(){ + public void shutdown() { mExecutor.shutdownNow(); mView = null; } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditorMenu.java b/app/src/main/java/org/autojs/autojs/ui/edit/EditorMenu.java index 190dc5e1..b9c26bde 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditorMenu.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditorMenu.java @@ -153,13 +153,11 @@ public class EditorMenu { private void importJavaPackageOrClass() { mEditor.getSelection() .observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> - new ClassSearchDialogBuilder(mContext) - .setQuery(s) - .itemClick((dialog, item, pos) -> showClassSearchingItem(dialog, item)) - .title(R.string.text_find_java_classes) - .show() - ); + .subscribe(s -> new ClassSearchDialogBuilder(mContext) + .setQuery(s) + .itemClick((dialog, item, pos) -> showClassSearchingItem(dialog, item)) + .title(R.string.text_find_java_classes) + .show()); } private void showClassSearchingItem(MaterialDialog dialog, ClassSearchingItem item) { @@ -217,10 +215,12 @@ public class EditorMenu { .title(R.string.text_pinch_to_zoom) .items(R.array.values_editor_pinch_to_zoom_strategy) .itemsCallbackSingleChoice(defSelectedIndex, (dialog, itemView, which, text) -> { - String newKey = itemKeys.get(which); - if (!Objects.equals(newKey, itemKey)) { - Pref.putString(key, newKey); - mEditorView.editor.notifyPinchToZoomStrategyChanged(newKey); + if (mEditorView.editor != null) { + String newKey = itemKeys.get(which); + if (!Objects.equals(newKey, itemKey)) { + Pref.putString(key, newKey); + mEditorView.editor.notifyPinchToZoomStrategyChanged(newKey); + } } return true; }) @@ -283,6 +283,9 @@ public class EditorMenu { if (itemId == R.id.action_clear) { return tryDoing(() -> mEditor.setText("")); } + if (itemId == R.id.action_comment) { + return tryDoing(mEditor.commentHelper::handle); + } if (itemId == R.id.action_beautify) { return tryDoing(mEditorView::beautifyCode); } @@ -359,12 +362,9 @@ public class EditorMenu { private void findOrReplace() { mEditor.getSelection() .observeOn(AndroidSchedulers.mainThread()) - .subscribe(s -> - new FindOrReplaceDialogBuilder(mContext, mEditorView) - .setQueryIfNotEmpty(s) - .show() - ); - + .subscribe(s -> new FindOrReplaceDialogBuilder(mContext, mEditorView) + .setQueryIfNotEmpty(s) + .show()); } private void copyAll() { diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.kt b/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.kt index 20493262..a3c87095 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.kt +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.kt @@ -198,7 +198,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar } } - fun setRestoredText(text: String?) { + fun setRestoredText(text: String) { mRestoredText = text editor!!.text = text } @@ -231,7 +231,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar private fun setInitialText(text: String) { if (mRestoredText != null) { - editor!!.text = mRestoredText + editor!!.text = mRestoredText!! mRestoredText = null return } @@ -281,19 +281,32 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar private fun setUpFunctionsKeyboard() { mFunctionsKeyboardHelper = FunctionsKeyboardHelper.with(context as Activity) .setContent(editor) - .setFunctionsTrigger(mShowFunctionsButton) + + // @Hint by SuperMonster003 on Jul 20, 2023. + // ! Note the order in which setFunctionsView and setFunctionsTrigger are called. + // ! zh-CN: 需留意 setFunctionsView 与 setFunctionsTrigger 的先后顺序. .setFunctionsView(mFunctionsKeyboard) + .setFunctionsTrigger(mShowFunctionsButton) + .setEditView(editor!!.codeEditText) .build() - //todo:不清楚作用,暂时注释掉 + + // @ArchivedTodo by 抠脚本人 on Jul 10, 2023. + // ! 不清楚作用, 暂时注释掉. // @Hint by SuperMonster003 on Jul 12, 2023. + // ! The click event callback is registered here + // ! so that the properties name of the functional keyboard + // ! can implement the functionality of the interface: + // ! Click: auto-completion, with parentheses, periods, etc. as appropriate. + // ! Long-press: display the method, property or module equivalent in a floating window (if exists). + // ! zh-CN: // ! 此处的点击事件回调注册是为了使功能键盘智能提示的属性可以实现其接口对应的功能: // ! 点击: 自动补全, 并根据情况添加括号或句点符号等. // ! 长按: 以浮动窗口形式展示 [方法/属性/模块] 对应的文档内容 (如果存在的话). mFunctionsKeyboard!!.setClickCallback(this) + mShowFunctionsButton!!.setOnLongClickListener { - editor!!.beautifyCode() - true + true.also { editor!!.beautifyCode() } } } @@ -321,7 +334,11 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar }) editText.textSize = getEditorTextSize(pxToSp(context, editText.textSize).toInt()).toFloat() } - editor.addCursorChangeCallback { line: String, cursor: Int -> autoComplete(line, cursor) } + editor.addCursorChangeCallback(object : CodeEditor.CursorChangeCallback { + override fun onCursorChange(line: String, cursor: Int) { + autoComplete(line, cursor) + } + }) editor.layoutDirection = LAYOUT_DIRECTION_LTR } } @@ -507,7 +524,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar fun setTextSize(value: Int) { setEditorTextSize(value) editor!!.codeEditText.textSize = value.toFloat() - editor!!.setLastTextSize(value) + editor!!.lastTextSize = value } private fun selectEditorTheme(themes: List) { @@ -529,7 +546,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar } @Throws(CheckedPatternSyntaxException::class) - fun find(keywords: String?, usingRegex: Boolean) { + fun find(keywords: String, usingRegex: Boolean) { editor!!.find(keywords, usingRegex) showSearchToolbar(false) } @@ -544,13 +561,13 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar } @Throws(CheckedPatternSyntaxException::class) - fun replace(keywords: String?, replacement: String?, usingRegex: Boolean) { + fun replace(keywords: String, replacement: String, usingRegex: Boolean) { editor!!.replace(keywords, replacement, usingRegex) showSearchToolbar(true) } @Throws(CheckedPatternSyntaxException::class) - fun replaceAll(keywords: String?, replacement: String?, usingRegex: Boolean) { + fun replaceAll(keywords: String, replacement: String, usingRegex: Boolean) { editor!!.replaceAll(keywords, replacement, usingRegex) } @@ -584,21 +601,52 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar override fun onHintClick(completions: CodeCompletions, pos: Int) { val completion = completions[pos] - //todo:增加行注释 - if (completion.insertText=="/") { - editor!!.commentLine() - } else editor!!.insert(completion.insertText) + + // @Overruled by SuperMonster003 on Jul 12, 2023. + // ! Author: 抠脚本人 + // ! Related PR: + // ! http://pr.autojs6.com/98 + // ! Reason: + // ! In any case, only the simplest input functions should be realized + // ! when clicking on the keys of the function keyboard. + // ! zh-CN: 在任何情况下, 单击功能键盘的按键时, 均应实现且仅实现最简单的输入功能. + // ! + // @ArchivedTodo by 抠脚本人 on Jul 11, 2023. + // ! 增加行注释 + // if (completion.insertText == "/") { + // editor!!.commentLine() + // } else editor!!.insert(completion.insertText) + + editor!!.insert(completion.insertText) } override fun onHintLongClick(completions: CodeCompletions, pos: Int) { - val completion = completions[pos] - //todo:增加块注释 - if (completion.insertText=="/") { - editor!!.commentBlock() - return + + // @Overruled by SuperMonster003 on Jul 12, 2023. + // ! Author: 抠脚本人 + // ! Related PR: + // ! http://pr.autojs6.com/98 + // ! Reason: + // ! Given the confusion caused by combinations of + // ! block comments and certain syntactic of RegEx, + // ! multi-line comments are also commented with double slashes. + // ! zh-CN: 鉴于块注释与正则表达式的某些句法组合造成混淆, 多行注释也采用双斜杠注释方式. + // ! + // val completion = completions[pos] + // @ArchivedTodo by 抠脚本人 on Jul 10, 2023. + // ! 增加块注释 + // if (completion.insertText == "/") { + // editor!!.commentBlock() + // return + // } + + completions[pos].let { + when { + // @Inspired by 抠脚本人 (https://github.com/little-alei) on Jul 13, 2023. + it.insertText == "/" -> editor!!.commentHelper.handle() + it.url != null -> showManual(it.url, it.hint) + } } - if (completion.url == null) return - showManual(completion.url, completion.hint) } private fun showManual(urlSuffix: String, title: String) { @@ -654,6 +702,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar override fun onRestoreInstanceState(state: Parcelable) { val bundle = state as Bundle + @Suppress("DEPRECATION") val superData = bundle.getParcelable("super_data") scriptExecutionId = bundle.getInt("script_execution_id", ScriptExecution.NO_ID) @@ -662,7 +711,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar } fun destroy() { - editor?.destroy() + editor!!.destroy() mAutoCompletion?.shutdown() } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditText.kt b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditText.kt index 46110c9c..c6ff2625 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditText.kt +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditText.kt @@ -136,7 +136,6 @@ class CodeEditText : AppCompatEditText { } private fun drawLineHighlights(canvas: Canvas) { - val currentLine = currentLine val debugHighlightLine = mDebuggingLine if (debugHighlightLine != currentLine) { // 绘制当前行高亮 @@ -165,6 +164,7 @@ class CodeEditText : AppCompatEditText { setPadding(gutterWidth.toInt(), 0, 0, 0) } } + // 该方法中内联了很多函数来提高效率 但是 这是必要的吗??? // 绘制文本着色 private fun drawText(canvas: Canvas) { @@ -352,7 +352,7 @@ class CodeEditText : AppCompatEditText { } override fun setSelection(index: Int) { - super.setSelection(index.coerceAtMost(text!!.length).coerceAtLeast(0)) + super.setSelection(index.coerceIn(0, text?.length ?: 0)) } override fun onSaveInstanceState(): Parcelable { @@ -402,7 +402,7 @@ class CodeEditText : AppCompatEditText { mTouchValid = false } if (event.action == MotionEvent.ACTION_UP) { - //当触摸有效时, 对那一行设置断点或取消断点 + // 当触摸有效时, 对那一行设置断点或取消断点 if (mTouchValid) { if (!removeBreakpoint(mTouchedLine)) { addBreakpoint(mTouchedLine) diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java deleted file mode 100644 index 66978284..00000000 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java +++ /dev/null @@ -1,579 +0,0 @@ -package org.autojs.autojs.ui.edit.editor; - -import static org.autojs.autojs.util.StringUtils.key; - -import android.content.Context; -import android.graphics.Canvas; -import android.text.Editable; -import android.text.Layout; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; - -import androidx.annotation.NonNull; - -import com.afollestad.materialdialogs.MaterialDialog; - -import org.autojs.autojs.pref.Pref; -import org.autojs.autojs.script.JsBeautifier; -import org.autojs.autojs.ui.edit.theme.Theme; -import org.autojs.autojs.util.ClipboardUtils; -import org.autojs.autojs.util.DisplayUtils; -import org.autojs.autojs.util.StringUtils; -import org.autojs.autojs.util.ViewUtils; -import org.autojs.autojs6.R; - -import java.util.LinkedHashMap; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -import io.reactivex.Observable; - -/** - * Copyright 2018 WHO<980008027@qq.com> - *

      - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

      - * http://www.apache.org/licenses/LICENSE-2.0 - *

      - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *

      - * Modified by project: https://github.com/980008027/JsDroidEditor - */ -public class CodeEditor extends HVScrollView { - - private CodeEditText mCodeEditText; - private TextViewUndoRedo mTextViewRedoUndo; - private JavaScriptHighlighter mJavaScriptHighlighter; - private ScaleGestureDetector mScaleGestureDetector; - private ScaleGestureDetector mScaleGestureDetectorForChangeTextSize; - private Theme mTheme; - private JsBeautifier mJsBeautifier; - private MaterialDialog mProcessDialog; - - private CharSequence mReplacement = ""; - private String mKeywords; - private Matcher mMatcher; - private int mFoundIndex = -1; - - private double mLastScaleFactor = 1; - private int mLastTextSize = 0; - - private int mMinTextSize = 0; - private int mMaxTextSize = 0; - - public CodeEditor(Context context) { - super(context); - init(); - } - - public CodeEditor(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public void setLastTextSize(int size) { - mLastTextSize = size; - } - - private void init() { - // setFillViewport(true); - inflate(getContext(), R.layout.code_editor, this); - mCodeEditText = findViewById(R.id.code_edit_text); - mCodeEditText.addTextChangedListener(new AutoIndent(mCodeEditText)); - mLastTextSize = Pref.getEditorTextSize((int) DisplayUtils.pxToSp(getContext(), mCodeEditText.getTextSize())); - mMinTextSize = Integer.parseInt(getContext().getString(R.string.text_text_size_min_value)); - mMaxTextSize = Integer.parseInt(getContext().getString(R.string.text_text_size_max_value)); - mTextViewRedoUndo = new TextViewUndoRedo(mCodeEditText); - mJavaScriptHighlighter = new JavaScriptHighlighter(mTheme, mCodeEditText); - mJsBeautifier = new JsBeautifier(this, "js-beautify"); - mScaleGestureDetectorForChangeTextSize = new ScaleGestureDetector(getContext(), getSimpleOnScaleGestureListener()); - applyScaleGesture(); - } - - private void applyScaleGesture() { - applyScaleGesture(null); - } - - private void applyScaleGesture(String key) { - if (key == null) { - String defKey = key(R.string.default_key_editor_pinch_to_zoom_strategy); - key = Pref.getString(key(R.string.key_editor_pinch_to_zoom_strategy), defKey); - } - if (Objects.equals(key, key(R.string.key_editor_pinch_to_zoom_change_text_size))) { - mScaleGestureDetector = mScaleGestureDetectorForChangeTextSize; - } else if (Objects.equals(key, key(R.string.key_editor_pinch_to_zoom_scale_view))) { - - // TODO by SuperMonster003 on Oct 17, 2022. - - } else if (Objects.equals(key, key(R.string.key_editor_pinch_to_zoom_disable))) { - mScaleGestureDetector = null; - } - } - - @NonNull - private ScaleGestureDetector.SimpleOnScaleGestureListener getSimpleOnScaleGestureListener() { - return new ScaleGestureDetector.SimpleOnScaleGestureListener() { - @Override - public boolean onScale(@NonNull ScaleGestureDetector detector) { - double currentFactor = Math.floor(detector.getScaleFactor() * 10) / 10; - if (currentFactor > 0 && mLastScaleFactor != currentFactor) { - int currentTextSize = mLastTextSize + (currentFactor > mLastScaleFactor ? 1 : -1); - mLastTextSize = Math.max(mMinTextSize, Math.min(mMaxTextSize, currentTextSize)); - mCodeEditText.setTextSize(mLastTextSize); - - mLastScaleFactor = currentFactor; - } - return super.onScale(detector); - } - - @Override - public boolean onScaleBegin(@NonNull ScaleGestureDetector detector) { - // TODO by SuperMonster003 on Oct 16, 2022. - // ! Show a floating text size changing bar. - - return super.onScaleBegin(detector); - } - - @Override - public void onScaleEnd(@NonNull ScaleGestureDetector detector) { - // TODO by SuperMonster003 on Oct 16, 2022. - // ! Dismiss a floating text size changing bar in 2 seconds. - - mLastScaleFactor = 1.0; - Pref.setEditorTextSize(mLastTextSize); - super.onScaleEnd(detector); - } - }; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (mScaleGestureDetector == null) { - return super.onTouchEvent(ev); - } - mScaleGestureDetector.onTouchEvent(ev); - return !mScaleGestureDetector.isInProgress() && super.onTouchEvent(ev); - } - - public Observable getLineCount() { - return Observable.just(mCodeEditText.getLayout().getLineCount()); - } - - public void copyLine() { - Layout layout = mCodeEditText.getLayout(); - int line = LayoutHelper.getLineOfChar(layout, mCodeEditText.getSelectionStart()); - if (line >= 0 && line < layout.getLineCount()) { - Editable text = mCodeEditText.getText(); - CharSequence lineText = null; - if (text != null) { - lineText = text.subSequence(layout.getLineStart(line), layout.getLineEnd(line)); - } - ClipboardUtils.setClip(getContext(), lineText); - ViewUtils.showSnack(this, R.string.text_already_copied_to_clip, false); - } - } - - - public void deleteLine() { - Layout layout = mCodeEditText.getLayout(); - int line = LayoutHelper.getLineOfChar(layout, mCodeEditText.getSelectionStart()); - if (line >= 0 && line < layout.getLineCount()) { - Editable text = mCodeEditText.getText(); - if (text != null) { - text.replace(layout.getLineStart(line), layout.getLineEnd(line), ""); - } - } - } - - public void jumpToStart() { - mCodeEditText.setSelection(0); - } - - public void jumpToEnd() { - Editable text = mCodeEditText.getText(); - if (text != null) { - mCodeEditText.setSelection(text.length()); - } - } - - public void jumpToLineStart() { - Layout layout = mCodeEditText.getLayout(); - int line = LayoutHelper.getLineOfChar(layout, mCodeEditText.getSelectionStart()); - if (line >= 0 && line < layout.getLineCount()) { - mCodeEditText.setSelection(layout.getLineStart(line)); - } - } - - public void jumpToLineEnd() { - Layout layout = mCodeEditText.getLayout(); - int line = LayoutHelper.getLineOfChar(layout, mCodeEditText.getSelectionStart()); - if (line >= 0 && line < layout.getLineCount()) { - mCodeEditText.setSelection(layout.getLineEnd(line) - 1); - } - } - - public void setTheme(Theme theme) { - mTheme = theme; - setBackgroundColor(mTheme.getBackgroundColor()); - mJavaScriptHighlighter.setTheme(theme); - Editable text = mCodeEditText.getText(); - if (text != null) { - mJavaScriptHighlighter.updateTokens(text.toString()); - } - mCodeEditText.setTheme(mTheme); - invalidate(); - } - - public boolean isTextChanged() { - return mTextViewRedoUndo.isTextChanged(); - } - - public boolean canUndo() { - return mTextViewRedoUndo.canUndo(); - } - - public boolean canRedo() { - return mTextViewRedoUndo.canRedo(); - } - - @Override - protected void onScrollChanged(int l, int t, int oldl, int oldt) { - super.onScrollChanged(l, t, oldl, oldt); - mCodeEditText.postInvalidate(); - } - - public CodeEditText getCodeEditText() { - return mCodeEditText; - } - - public void setInitialText(String text) { - mCodeEditText.setText(text); - mTextViewRedoUndo.setDefaultText(text); - } - - public void jumpTo(int line, int col) { - Layout layout = mCodeEditText.getLayout(); - if (line >= 0 && (layout == null || line < layout.getLineCount())) { - mCodeEditText.setSelection(mCodeEditText.getLayout().getLineStart(line) + col); - } - } - - public void setReadOnly(boolean readOnly) { - mCodeEditText.setEnabled(!readOnly); - } - - public void setRedoUndoEnabled(boolean enabled) { - mTextViewRedoUndo.setEnabled(enabled); - } - - public void setProgress(boolean progress) { - if (mProcessDialog != null) { - mProcessDialog.dismiss(); - } - mProcessDialog = !progress ? null : new MaterialDialog.Builder(getContext()) - .content(R.string.text_processing) - .progress(true, 0) - .cancelable(false) - .show(); - } - - public void setText(String text) { - mCodeEditText.setText(text); - } - - public void addCursorChangeCallback(CursorChangeCallback callback) { - mCodeEditText.addCursorChangeCallback(callback); - } - - public void removeCursorChangeCallback(CursorChangeCallback callback) { - mCodeEditText.removeCursorChangeCallback(callback); - } - - public void undo() { - mTextViewRedoUndo.undo(); - } - - public void redo() { - mTextViewRedoUndo.redo(); - } - - public void find(String keywords, boolean usingRegex) throws CheckedPatternSyntaxException { - if (usingRegex) { - try { - Editable text = mCodeEditText.getText(); - if (text != null) { - mMatcher = Pattern.compile(keywords).matcher(text); - } - } catch (PatternSyntaxException e) { - throw new CheckedPatternSyntaxException(e); - } - mKeywords = null; - } else { - mKeywords = keywords; - mMatcher = null; - } - findNext(); - } - - public void replace(String keywords, String replacement, boolean usingRegex) throws CheckedPatternSyntaxException { - mReplacement = replacement == null ? "" : replacement; - find(keywords, usingRegex); - } - - public void replaceAll(String keywords, String replacement, boolean usingRegex) throws CheckedPatternSyntaxException { - if (!usingRegex) { - keywords = Pattern.quote(keywords); - } - Editable codeEditTextText = mCodeEditText.getText(); - String text = null; - if (codeEditTextText != null) { - text = codeEditTextText.toString(); - } - try { - if (text != null) { - text = text.replaceAll(keywords, replacement); - } - } catch (PatternSyntaxException e) { - throw new CheckedPatternSyntaxException(e); - } - setText(text); - } - - public void findNext() { - int foundIndex; - if (mMatcher == null) { - if (mKeywords == null) { - return; - } - Editable text = mCodeEditText.getText(); - if (text != null) { - foundIndex = StringUtils.indexOf(text, mKeywords, mFoundIndex + 1); - } else { - foundIndex = -1; - } - if (foundIndex >= 0) - mCodeEditText.setSelection(foundIndex, foundIndex + mKeywords.length()); - } else if (mMatcher.find(mFoundIndex + 1)) { - foundIndex = mMatcher.start(); - mCodeEditText.setSelection(foundIndex, foundIndex + mMatcher.group().length()); - } else { - foundIndex = -1; - } - if (foundIndex < 0 && mFoundIndex >= 0) { - mFoundIndex = -1; - findNext(); - } else { - mFoundIndex = foundIndex; - } - } - - public void findPrev() { - if (mMatcher != null) { - ViewUtils.showToast(getContext(), R.string.error_regex_find_prev, true); - return; - } - int len = mCodeEditText.getText().length(); - if (mFoundIndex <= 0) { - mFoundIndex = len; - } - int index = mCodeEditText.getText().toString().lastIndexOf(mKeywords, mFoundIndex - 1); - if (index < 0) { - if (mFoundIndex != len) { - mFoundIndex = len; - findPrev(); - } - return; - } - mFoundIndex = index; - mCodeEditText.setSelection(index, index + mKeywords.length()); - } - - public void replaceSelection() { - mCodeEditText.getText().replace(mCodeEditText.getSelectionStart(), mCodeEditText.getSelectionEnd(), mReplacement); - } - - public void beautifyCode() { - setProgress(true); - int pos = mCodeEditText.getSelectionStart(); - mJsBeautifier.beautify(mCodeEditText.getText().toString(), new JsBeautifier.Callback() { - @Override - public void onSuccess(String beautifiedCode) { - setProgress(false); - mCodeEditText.setText(beautifiedCode); - // 格式化后恢复光标位置 - mCodeEditText.setSelection(pos); - } - - @Override - public void onException(Exception e) { - setProgress(false); - e.printStackTrace(); - } - }); - } - - public void commentLine() { - //如果没有选中,则添加文本/,否则选中的行前加// - String selectionText = getSelectionRaw(); - if (selectionText.equals("")) { - insert("/"); - } else { - String[] lines = selectionText.split("\\n"); - StringBuilder commentedText = new StringBuilder(); - //处理取消注释 - if (lines[0].startsWith("//")) { - for (String line : lines) { - commentedText.append(line.substring(2)).append("\n"); - } - } else { - for (String line : lines) { - commentedText.append("//").append(line).append("\n"); - } - } - mReplacement = commentedText.toString().replaceAll("\\n$", ""); - replaceSelection(); - } - } - - public void commentBlock() { - String selectionText = getSelectionRaw(); - if (!selectionText.isEmpty()) { - String regex = "/\\*([^*]|\\*+[^*/])*\\*/"; - if (selectionText.matches(regex)) { - // 取消块注释 - mReplacement = selectionText.substring(2, selectionText.length() - 2); - } else { - // 增加块注释 - mReplacement = "/*" + selectionText + "*/"; - } - replaceSelection(); - } - } - - public void insert(String insertText) { - int selection = Math.max(mCodeEditText.getSelectionStart(), 0); - mCodeEditText.getText().insert(selection, insertText); - } - - public void insert(int line, String insertText) { - int selection = mCodeEditText.getLayout().getLineStart(line); - mCodeEditText.getText().insert(selection, insertText); - } - - public void moveCursor(int dCh) { - mCodeEditText.setSelection(mCodeEditText.getSelectionStart() + dCh); - } - - public String getText() { - return mCodeEditText.getText().toString(); - } - - public String getSelectionRaw() { - int s = mCodeEditText.getSelectionStart(); - int e = mCodeEditText.getSelectionEnd(); - if (s == e) { - return ""; - } - return mCodeEditText.getText().toString().substring(s, e); - } - - public Observable getSelection() { - return Observable.just(getSelectionRaw()); - } - - - public void markTextAsSaved() { - mTextViewRedoUndo.markTextAsUnchanged(); - } - - public LinkedHashMap getBreakpoints() { - return mCodeEditText.getBreakpoints(); - } - - public void setDebuggingLine(int line) { - mCodeEditText.setDebuggingLine(line); - } - - public void setBreakpointChangeListener(BreakpointChangeListener listener) { - mCodeEditText.setBreakpointChangeListener(listener); - } - - public void addOrRemoveBreakpoint(int line) { - if (!mCodeEditText.removeBreakpoint(line)) { - mCodeEditText.addBreakpoint(line); - } - } - - public void addOrRemoveBreakpointAtCurrentLine() { - Layout layout = mCodeEditText.getLayout(); - int line = LayoutHelper.getLineOfChar(layout, mCodeEditText.getSelectionStart()); - if (line >= 0 && line < layout.getLineCount()) { - addOrRemoveBreakpoint(line); - } - } - - public void removeAllBreakpoints() { - mCodeEditText.removeAllBreakpoints(); - } - - public void destroy() { - mJavaScriptHighlighter.shutdown(); - mJsBeautifier.shutdown(); - } - - @Override - protected void onDraw(Canvas canvas) { - int codeWidth = getWidth() - getPaddingLeft() - getPaddingRight(); - int codeHeight = getHeight() - getPaddingTop() - getPaddingBottom(); - if (mCodeEditText.getMinWidth() != codeWidth || mCodeEditText.getMinWidth() != codeWidth) { - mCodeEditText.setMinWidth(codeWidth); - mCodeEditText.setMinHeight(codeHeight); - invalidate(); - } - super.onDraw(canvas); - } - - public static class Breakpoint { - - public int line; - public boolean enabled = true; - - public Breakpoint(int line) { - this.line = line; - } - } - - public interface BreakpointChangeListener { - void onBreakpointChange(int line, boolean enabled); - - void onAllBreakpointRemoved(int count); - } - - public void notifyPinchToZoomStrategyChanged(String newKey) { - applyScaleGesture(newKey); - } - - public static class CheckedPatternSyntaxException extends Exception { - - public CheckedPatternSyntaxException(PatternSyntaxException cause) { - super(cause); - } - - } - - public interface CursorChangeCallback { - - void onCursorChange(String line, int ch); - - } - -} diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.kt b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.kt new file mode 100644 index 00000000..f7e23e56 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.kt @@ -0,0 +1,634 @@ +package org.autojs.autojs.ui.edit.editor + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Canvas +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.ScaleGestureDetector +import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener +import com.afollestad.materialdialogs.MaterialDialog +import io.reactivex.Observable +import org.autojs.autojs.pref.Pref.getEditorTextSize +import org.autojs.autojs.pref.Pref.getString +import org.autojs.autojs.pref.Pref.setEditorTextSize +import org.autojs.autojs.script.JsBeautifier +import org.autojs.autojs.ui.edit.theme.Theme +import org.autojs.autojs.util.ClipboardUtils.setClip +import org.autojs.autojs.util.DisplayUtils.pxToSp +import org.autojs.autojs.util.StringUtils.indexOf +import org.autojs.autojs.util.StringUtils.key +import org.autojs.autojs.util.ViewUtils.showSnack +import org.autojs.autojs.util.ViewUtils.showToast +import org.autojs.autojs6.R +import org.autojs.autojs6.databinding.CodeEditorBinding +import java.util.regex.Matcher +import java.util.regex.Pattern +import java.util.regex.PatternSyntaxException +import kotlin.math.floor + +/** + * Copyright 2018 WHO<980008027@qq.com> + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Modified by project: https://github.com/980008027/JsDroidEditor + */ +/** + * Modified by SuperMonster003 as of Jul 16, 2023. + * Transformed by SuperMonster003 on Jul 16, 2023. + */ +class CodeEditor : HVScrollView { + + val binding = CodeEditorBinding.inflate(LayoutInflater.from(context), this, true) + + val codeEditText: CodeEditText = binding.codeEditText.also { + it.addTextChangedListener(AutoIndent(it)) + lastTextSize = getEditorTextSize(pxToSp(it.context, it.textSize).toInt()) + } + + val lineCount + get() = Observable.just(codeEditText.layout.lineCount) + + val isTextChanged + get() = mTextViewRedoUndo.isTextChanged + + val selection: Observable + get() = Observable.just(selectionText) + + val breakpoints: LinkedHashMap + get() = codeEditText.breakpoints + + var lastTextSize = 0 + + var text: String + get() = codeEditText.text?.toString() ?: "" + set(text) { + codeEditText.setText(text) + } + + @JvmField + val commentHelper = CommentHelper() + + private val simpleOnScaleGestureListener + get() = object : SimpleOnScaleGestureListener() { + override fun onScale(detector: ScaleGestureDetector): Boolean { + val currentFactor = floor((detector.scaleFactor * 10).toDouble()) / 10 + if (currentFactor > 0 && mLastScaleFactor != currentFactor) { + val currentTextSize = lastTextSize + if (currentFactor > mLastScaleFactor) 1 else -1 + lastTextSize = currentTextSize.coerceIn(mMinTextSize, mMaxTextSize).also { + codeEditText.textSize = it.toFloat() + } + mLastScaleFactor = currentFactor + } + return super.onScale(detector) + } + + // TODO by SuperMonster003 on Oct 16, 2022. + // ! Show a floating text size changing bar. + + // override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { + // return super.onScaleBegin(detector) + // } + + override fun onScaleEnd(detector: ScaleGestureDetector) { + // TODO by SuperMonster003 on Oct 16, 2022. + // ! Dismiss a floating text size changing bar in 2 seconds. + mLastScaleFactor = 1.0 + setEditorTextSize(lastTextSize) + super.onScaleEnd(detector) + } + } + + private val selectionText: String + get() { + val s = codeEditText.selectionStart + val e = codeEditText.selectionEnd + return if (s == e) "" else codeEditText.text?.substring(s, e) ?: "" + } + + private var mMinTextSize = context.getString(R.string.text_text_size_min_value).toInt() + private var mMaxTextSize = context.getString(R.string.text_text_size_max_value).toInt() + private var mTextViewRedoUndo = TextViewUndoRedo(codeEditText) + + private var mTheme: Theme? = null + private var mJavaScriptHighlighter = JavaScriptHighlighter(mTheme, codeEditText) + + private var mJsBeautifier = JsBeautifier(this) + private var mScaleGestureDetectorForChangeTextSize = ScaleGestureDetector(context, simpleOnScaleGestureListener) + private var mScaleGestureDetector: ScaleGestureDetector? = null + private var mProcessDialog: MaterialDialog? = null + private var mReplacement: CharSequence = "" + private var mKeywords: String? = null + private var mMatcher: Matcher? = null + private var mFoundIndex = -1 + private var mLastScaleFactor = 1.0 + + constructor(context: Context?) : super(context) + + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + + init { + applyScaleGesture() + } + + private fun applyScaleGesture(key: String? = null) { + var niceKey = key + if (niceKey == null) { + val defKey = key(R.string.default_key_editor_pinch_to_zoom_strategy) + niceKey = getString(key(R.string.key_editor_pinch_to_zoom_strategy), defKey) + } + when (niceKey) { + key(R.string.key_editor_pinch_to_zoom_change_text_size) -> { + mScaleGestureDetector = mScaleGestureDetectorForChangeTextSize + } + key(R.string.key_editor_pinch_to_zoom_scale_view) -> { + // TODO by SuperMonster003 on Oct 17, 2022. + } + key(R.string.key_editor_pinch_to_zoom_disable) -> { + mScaleGestureDetector = null + } + } + } + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(ev: MotionEvent): Boolean { + return mScaleGestureDetector?.let { + it.onTouchEvent(ev) + !it.isInProgress && super.onTouchEvent(ev) + } ?: super.onTouchEvent(ev) + } + + override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) { + super.onScrollChanged(l, t, oldl, oldt) + codeEditText.postInvalidate() + } + + fun copyLine() { + val layout = codeEditText.layout + val line = LayoutHelper.getLineOfChar(layout, codeEditText.selectionStart) + if (line >= 0 && line < layout.lineCount) { + val text = codeEditText.text + var lineText: CharSequence? = null + if (text != null) { + lineText = text.subSequence(layout.getLineStart(line), layout.getLineEnd(line)) + } + setClip(context, lineText) + showSnack(this, R.string.text_already_copied_to_clip, false) + } + } + + private fun getCoveredLinesText(): CharSequence { + val layout = codeEditText.layout + val lineStart = LayoutHelper.getLineOfChar(layout, codeEditText.selectionStart) + val lineEnd = LayoutHelper.getLineOfChar(layout, codeEditText.selectionEnd) + if (lineStart >= 0 && lineStart < layout.lineCount) { + if (lineEnd >= 0 && lineEnd < layout.lineCount) { + return text.subSequence(layout.getLineStart(lineStart), layout.getLineEnd(lineEnd)) + } + } + return "" + } + + private fun replaceSelectedLines(feature: Regex, transform: (MatchResult) -> CharSequence) { + val text = codeEditText.text ?: return + val layout = codeEditText.layout + val lineStart = LayoutHelper.getLineOfChar(layout, codeEditText.selectionStart) + val lineEnd = LayoutHelper.getLineOfChar(layout, codeEditText.selectionEnd) + val newText = getCoveredLinesText().split("\n").joinToString("\n") { it.replace(feature, transform) } + text.replace(layout.getLineStart(lineStart), layout.getLineEnd(lineEnd), newText) + } + + fun deleteLine() { + val text = codeEditText.text ?: return + val layout = codeEditText.layout + val line = LayoutHelper.getLineOfChar(layout, codeEditText.selectionStart) + if (line >= 0 && line < layout.lineCount) { + text.replace(layout.getLineStart(line), layout.getLineEnd(line), "") + } + } + + fun jumpToStart() { + codeEditText.setSelection(0) + } + + fun jumpToEnd() { + val text = codeEditText.text + if (text != null) { + codeEditText.setSelection(text.length) + } + } + + fun jumpToLineStart() { + val layout = codeEditText.layout + val line = LayoutHelper.getLineOfChar(layout, codeEditText.selectionStart) + if (line >= 0 && line < layout.lineCount) { + codeEditText.setSelection(layout.getLineStart(line)) + } + } + + fun jumpToLineEnd() { + val layout = codeEditText.layout + val line = LayoutHelper.getLineOfChar(layout, codeEditText.selectionStart) + if (line >= 0 && line < layout.lineCount) { + codeEditText.setSelection(layout.getLineEnd(line) - 1) + } + } + + fun setTheme(theme: Theme?) { + mTheme = theme + setBackgroundColor(mTheme!!.backgroundColor) + mJavaScriptHighlighter.setTheme(theme) + val text = codeEditText.text + if (text != null) { + mJavaScriptHighlighter.updateTokens(text.toString()) + } + codeEditText.setTheme(mTheme!!) + invalidate() + } + + fun canUndo(): Boolean { + return mTextViewRedoUndo.canUndo() + } + + fun canRedo(): Boolean { + return mTextViewRedoUndo.canRedo() + } + + fun setInitialText(text: String?) { + codeEditText.setText(text) + mTextViewRedoUndo.setDefaultText(text) + } + + fun jumpTo(line: Int, col: Int) { + val layout = codeEditText.layout + if (line >= 0 && (layout == null || line < layout.lineCount)) { + codeEditText.setSelection(codeEditText.layout.getLineStart(line) + col) + } + } + + fun setReadOnly(readOnly: Boolean) { + codeEditText.isEnabled = !readOnly + } + + fun setRedoUndoEnabled(enabled: Boolean) { + mTextViewRedoUndo.isEnabled = enabled + } + + fun setProgress(progress: Boolean) { + if (mProcessDialog != null) { + mProcessDialog!!.dismiss() + } + mProcessDialog = if (!progress) null else MaterialDialog.Builder(context) + .content(R.string.text_processing) + .progress(true, 0) + .cancelable(false) + .show() + } + + fun addCursorChangeCallback(callback: CursorChangeCallback?) { + codeEditText.addCursorChangeCallback(callback!!) + } + + fun removeCursorChangeCallback(callback: CursorChangeCallback?) { + codeEditText.removeCursorChangeCallback(callback!!) + } + + fun undo() { + mTextViewRedoUndo.undo() + } + + fun redo() { + mTextViewRedoUndo.redo() + } + + @Throws(CheckedPatternSyntaxException::class) + fun find(keywords: String, usingRegex: Boolean) { + if (usingRegex) { + try { + val text = codeEditText.text + if (text != null) { + mMatcher = Pattern.compile(keywords).matcher(text) + } + } catch (e: PatternSyntaxException) { + throw CheckedPatternSyntaxException(e) + } + mKeywords = null + } else { + mKeywords = keywords + mMatcher = null + } + findNext() + } + + @Throws(CheckedPatternSyntaxException::class) + fun replace(keywords: String, replacement: String, usingRegex: Boolean) { + mReplacement = replacement + find(keywords, usingRegex) + } + + @Throws(CheckedPatternSyntaxException::class) + fun replaceAll(keywords: String, replacement: String?, usingRegex: Boolean) { + var niceKeywords = keywords + if (!usingRegex) { + niceKeywords = Pattern.quote(niceKeywords) + } + val codeEditTextText = codeEditText.text + var text: String? = null + if (codeEditTextText != null) { + text = codeEditTextText.toString() + } + try { + if (text != null) { + text = text.replace(niceKeywords.toRegex(), replacement!!) + } + } catch (e: PatternSyntaxException) { + throw CheckedPatternSyntaxException(e) + } + if (text != null) { + this.text = text + } + } + + fun findNext() { + val foundIndex: Int + if (mMatcher == null) { + if (mKeywords == null) { + return + } + val text = codeEditText.text + foundIndex = if (text != null) { + indexOf(text, mKeywords!!, mFoundIndex + 1) + } else { + -1 + } + if (foundIndex >= 0) codeEditText.setSelection(foundIndex, foundIndex + mKeywords!!.length) + } else if (mMatcher!!.find(mFoundIndex + 1)) { + foundIndex = mMatcher!!.start() + codeEditText.setSelection(foundIndex, foundIndex + mMatcher!!.group().length) + } else { + foundIndex = -1 + } + if (foundIndex < 0 && mFoundIndex >= 0) { + mFoundIndex = -1 + findNext() + } else { + mFoundIndex = foundIndex + } + } + + fun findPrev() { + if (mMatcher != null) { + showToast(context, R.string.error_regex_find_prev, true) + return + } + val len = codeEditText.text!!.length + if (mFoundIndex <= 0) { + mFoundIndex = len + } + val index = codeEditText.text.toString().lastIndexOf(mKeywords!!, mFoundIndex - 1) + if (index < 0) { + if (mFoundIndex != len) { + mFoundIndex = len + findPrev() + } + return + } + mFoundIndex = index + codeEditText.setSelection(index, index + mKeywords!!.length) + } + + fun replaceSelection() { + codeEditText.text?.replace(codeEditText.selectionStart, codeEditText.selectionEnd, mReplacement) + } + + fun beautifyCode() { + setProgress(true) + val pos = codeEditText.selectionStart + mJsBeautifier.beautify(codeEditText.text.toString(), object : JsBeautifier.Callback { + override fun onSuccess(beautifiedCode: String) { + codeEditText.setText(beautifiedCode) + // @Hint by 抠脚本人 on Jul 11, 2023. + // ! 格式化后恢复光标位置 + codeEditText.setSelection(pos) + setProgress(false) + showToast(context, R.string.text_formatting_completed) + } + + override fun onException(e: Exception) { + setProgress(false) + showToast(context, R.string.text_failed_to_format, true) + e.printStackTrace() + } + }) + } + + fun insert(insertText: String?) { + val selection = codeEditText.selectionStart.coerceAtLeast(0) + codeEditText.text!!.insert(selection, insertText) + } + + fun insert(line: Int, insertText: String?) { + val selection = codeEditText.layout.getLineStart(line) + codeEditText.text!!.insert(selection, insertText) + } + + fun moveCursor(dCh: Int) { + codeEditText.setSelection(codeEditText.selectionStart + dCh) + } + + fun markTextAsSaved() { + mTextViewRedoUndo.markTextAsUnchanged() + } + + fun setDebuggingLine(line: Int) { + codeEditText.debuggingLine = line + } + + fun setBreakpointChangeListener(listener: BreakpointChangeListener?) { + codeEditText.breakpointChangeListener = listener + } + + private fun addOrRemoveBreakpoint(line: Int) { + if (!codeEditText.removeBreakpoint(line)) { + codeEditText.addBreakpoint(line) + } + } + + fun addOrRemoveBreakpointAtCurrentLine() { + val layout = codeEditText.layout + val line = LayoutHelper.getLineOfChar(layout, codeEditText.selectionStart) + if (line >= 0 && line < layout.lineCount) { + addOrRemoveBreakpoint(line) + } + } + + fun removeAllBreakpoints() { + codeEditText.removeAllBreakpoints() + } + + fun destroy() { + mJavaScriptHighlighter.shutdown() + mJsBeautifier.shutdown() + } + + override fun onDraw(canvas: Canvas) { + val codeWidth = width - paddingLeft - paddingRight + val codeHeight = height - paddingTop - paddingBottom + if (codeEditText.minWidth != codeWidth || codeEditText.minWidth != codeWidth) { + codeEditText.minWidth = codeWidth + codeEditText.minHeight = codeHeight + invalidate() + } + super.onDraw(canvas) + } + + fun notifyPinchToZoomStrategyChanged(newKey: String?) { + applyScaleGesture(newKey) + } + + class Breakpoint(@JvmField var line: Int) { + @JvmField + var enabled = true + } + + class CheckedPatternSyntaxException(cause: PatternSyntaxException?) : Exception(cause) + + interface BreakpointChangeListener { + fun onBreakpointChange(line: Int, enabled: Boolean) + fun onAllBreakpointRemoved(count: Int) + } + + interface CursorChangeCallback { + fun onCursorChange(line: String, cursor: Int) + } + + inner class CommentHelper : CodeEditorCommentHelper { + + private val prefix = "//" + + override fun handle() = toggle() + + override fun comment() { + var selectionEnd = codeEditText.selectionEnd + val insetPosition = getProperWhitespaceAmount() + var hasEverMatched = false + + @Suppress("RegExpSimplifiable") + replaceSelectedLines(Regex("^(\\s{$insetPosition})(\\s*\\S+)")) { matchResult -> + "$prefix\u0020" + .also { selectionEnd += it.length } + .also { hasEverMatched = true } + .let { "${matchResult.groupValues[1]}$it${matchResult.groupValues[2]}" } + } + + @Suppress("ControlFlowWithEmptyBody") + if (!hasEverMatched) { + // FIXME by SuperMonster003 on Jul 20, 2023. + // ! Behaves abnormally for empty line(s). + // replaceSelectedLines(Regex(".*")) { matchResult -> + // prefix + // .also { selectionEnd += it.length } + // .let { "$it${matchResult.value}" } + // } + } + + codeEditText.setSelection(selectionEnd) + } + + override fun uncomment() { + var selectionEnd = codeEditText.selectionEnd + + replaceSelectedLines(Regex("(\\s*)($prefix\\s?)(.*)")) { matchResult -> + selectionEnd -= matchResult.groupValues[2].length + "${matchResult.groupValues[1]}${matchResult.groupValues[3]}" + } + + codeEditText.setSelection(selectionEnd) + } + + override fun isCommented(): Boolean { + var atLeastOneWithPrefix = false + val allMatched = getCoveredLinesText().split("\n").all { + it.matches(Regex("\\s*")) || it + .contains(Regex("^\\s*$prefix")) + .also { atLeastOneWithPrefix = true } + } + return allMatched and atLeastOneWithPrefix + } + + private fun getProperWhitespaceAmount(): Int { + var result = Int.MAX_VALUE + val threshold = 0 + getCoveredLinesText().split("\n").forEach { + Regex("^\\s*(?=\\S+)").find(it)?.let { matchResult -> + val len = matchResult.value.length + if (len < result) result = len + if (result == threshold) return threshold + } + } + return if (result == Int.MAX_VALUE) threshold else result + } + + } + + // @Archived by SuperMonster003 on Jul 12, 2023. + // ! Author: 抠脚本人 + // ! Reason: Replaced with CommentHelper class. + // fun commentLine() { + // // 如果没有选中,则添加文本/,否则选中的行前加// + // val selectionText: String = getSelectionRaw() + // if (selectionText == "") { + // insert("/") + // } else { + // val lines = selectionText.split("\\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + // val commentedText = StringBuilder() + // // 处理取消注释 + // if (lines[0].startsWith("//")) { + // for (line in lines) { + // commentedText.append(line.substring(2)).append("\n") + // } + // } else { + // for (line in lines) { + // commentedText.append("//").append(line).append("\n") + // } + // } + // mReplacement = commentedText.toString().replace("\\n$".toRegex(), "") + // replaceSelection() + // } + // } + + // @Archived by SuperMonster003 on Jul 12, 2023. + // ! Author: 抠脚本人 + // ! Reason: Replaced with CommentHelper class. + // fun commentBlock() { + // val selectionText: String = getSelectionRaw() + // if (!selectionText.isEmpty()) { + // val regex = "/\\*([^*]|\\*+[^*/])*\\*/" + // mReplacement = if (selectionText.matches(regex)) { + // // 取消块注释 + // selectionText.substring(2, selectionText.length - 2) + // } else { + // // 增加块注释 + // "/*$selectionText*/" + // } + // replaceSelection() + // } + // } + +} \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditorCommentHelper.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditorCommentHelper.java new file mode 100644 index 00000000..1c81d6b5 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditorCommentHelper.java @@ -0,0 +1,21 @@ +package org.autojs.autojs.ui.edit.editor; + +public interface CodeEditorCommentHelper { + + void handle(); + + void comment(); + + void uncomment(); + + boolean isCommented(); + + default void toggle() { + if (isCommented()) { + uncomment(); + } else { + comment(); + } + } + +} diff --git a/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java b/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java index 536f60f7..9ad72e5d 100644 --- a/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java +++ b/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java @@ -186,7 +186,7 @@ public class CircularMenu implements Recorder.OnStateChangedListener, LayoutInsp mSettingsDialog = new CircularMenuOperationDialogBuilder(mContext) .item(R.drawable.ic_accessibility_black_48dp, R.string.text_manage_a11y_service, v1 -> { dismissSettingsDialog(); - getAccessibilityTool().getService().enable(); + getAccessibilityTool().getService().launchSettings(); }) .item(R.drawable.ic_text_fields_black_48dp, mContext.getString(R.string.text_latest_package) + ":\n" + getRunningPackage(), v1 -> { dismissSettingsDialog(); diff --git a/app/src/main/java/org/autojs/autojs/ui/floating/LayoutFloatyWindow.kt b/app/src/main/java/org/autojs/autojs/ui/floating/LayoutFloatyWindow.kt index 7e23a25c..884f92c8 100644 --- a/app/src/main/java/org/autojs/autojs/ui/floating/LayoutFloatyWindow.kt +++ b/app/src/main/java/org/autojs/autojs/ui/floating/LayoutFloatyWindow.kt @@ -17,7 +17,7 @@ import org.autojs.autojs.util.ClipboardUtils import org.autojs.autojs.util.ViewUtils import org.autojs.autojs6.R -abstract class LayoutFloatyWindow(private val rootNode: NodeInfo?, private val context: Context, private val isServiceRelied: Boolean) : FullScreenFloatyWindow(){ +abstract class LayoutFloatyWindow(private val rootNode: NodeInfo?, private val context: Context, private val isServiceRelied: Boolean) : FullScreenFloatyWindow() { private lateinit var mServiceContext: Context private lateinit var mActions: LinkedHashMap @@ -29,12 +29,22 @@ abstract class LayoutFloatyWindow(private val rootNode: NodeInfo?, private val c private val mNodeInfoDialog by lazy { AppLevelThemeDialogBuilder(mServiceContext) .customView(mNodeInfoView, false) - .positiveText("生成") - .onPositive { _, _ -> - ViewUtils.showToast(context, "TODO") - val selector = mNodeInfoView.getCheckedDate().joinToString(".") - if (selector.isNotEmpty()) ClipboardUtils.setClip(context, selector) - } + + // @Overruled by SuperMonster003 on Jul 21, 2023. + // ! Author: 抠脚本人 + // ! Related PR: + // ! http://pr.autojs6.com/98 + // ! Reason: + // ! Pending processing. + // ! zh-CN: 将于后续版本继续处理. + // ! + // .positiveText("生成") + // .onPositive { _, _ -> + // ViewUtils.showToast(context, "TODO") + // val selector = mNodeInfoView.getCheckedDate().joinToString(".") + // if (selector.isNotEmpty()) ClipboardUtils.setClip(context, selector) + // } + .build() .also { it.window!!.setType(FloatyWindowManger.getWindowType()) } } diff --git a/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/LayoutHierarchyView.java b/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/LayoutHierarchyView.java index 8e6790ae..b51e97e9 100644 --- a/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/LayoutHierarchyView.java +++ b/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/LayoutHierarchyView.java @@ -36,7 +36,8 @@ public class LayoutHierarchyView extends MultiLevelListView { public interface OnItemLongClickListener { void onItemLongClick(View view, NodeInfo nodeInfo); } - private final Map nodeMap = new LinkedHashMap<>(); + + private final Map nodeMap = new LinkedHashMap<>(); private Adapter mAdapter; private OnItemLongClickListener mOnItemLongClickListener; private final AdapterView.OnItemLongClickListener mOnItemLongClickListenerProxy = new AdapterView.OnItemLongClickListener() { @@ -121,25 +122,34 @@ public class LayoutHierarchyView extends MultiLevelListView { } private void drawListItem(NodeInfo info, Boolean draw) { - ArrayList list = new ArrayList<>(); - NodeInfo currentInfo = info; - while (true) { - currentInfo = currentInfo.getParent(); - if (currentInfo == null) break; - ViewHolder vh = nodeMap.get(currentInfo); - list.add(vh); - } - //todo:选用能适应深色模式的字体颜色 - //fixme:列表滑动时listview数据错乱 - if (draw) { - for (ViewHolder vh : list) { - vh.nameView.setTextColor(Color.RED); // 设置字体颜色为红色 - } - } else { - for (ViewHolder vh : list) { - vh.nameView.setTextColor(Color.BLACK); // 设置字体颜色为红色 - } - } + + // @Overruled by SuperMonster003 on Jul 21, 2023. + // ! Author: 抠脚本人 + // ! Related PR: + // ! http://pr.autojs6.com/98 + // ! Reason: + // ! Pending processing. + // ! zh-CN: 将于后续版本继续处理. + // ! + // ArrayList list = new ArrayList<>(); + // NodeInfo currentInfo = info; + // while (true) { + // currentInfo = currentInfo.getParent(); + // if (currentInfo == null) break; + // ViewHolder vh = nodeMap.get(currentInfo); + // list.add(vh); + // } + // // TODO: 选用能适应深色模式的字体颜色 + // // FIXME: 列表滑动时 ListView 数据错乱 + // if (draw) { + // for (ViewHolder vh : list) { + // vh.nameView.setTextColor(Color.RED); // 设置字体颜色为红色 + // } + // } else { + // for (ViewHolder vh : list) { + // vh.nameView.setTextColor(Color.BLACK); // 设置字体颜色为红色 + // } + // } } private void initPaint() { @@ -221,10 +231,8 @@ public class LayoutHierarchyView extends MultiLevelListView { } } - private class Adapter extends MultiLevelListAdapter { - @Override protected List getSubObjects(Object object) { return ((NodeInfo) object).getChildren(); @@ -252,9 +260,21 @@ public class LayoutHierarchyView extends MultiLevelListView { } else { viewHolder = (ViewHolder) convertView.getTag(); } - nodeMap.put(nodeInfo,viewHolder); - //对于id,desc,text,clickable,longClickable不为空的显示额外信息 - viewHolder.nameView.setText(extraInfo(nodeInfo)); + nodeMap.put(nodeInfo, viewHolder); + + // @Overruled by SuperMonster003 on Jul 12, 2023. + // ! Author: 抠脚本人 + // ! Related PR: + // ! http://pr.autojs6.com/98 + // ! Reason: + // ! Pending processing. + // ! zh-CN: 将于后续版本继续处理. + // ! + // @Hint by 抠脚本人 on Jul 10, 2023. + // ! 对于 id, desc, text, clickable, longClickable 不为空的显示额外信息 + // viewHolder.nameView.setText(extraInfo(nodeInfo)); + viewHolder.nameView.setText(simplifyClassName(nodeInfo.getClassName())); + viewHolder.nodeInfo = nodeInfo; if (viewHolder.infoView.getVisibility() == VISIBLE) viewHolder.infoView.setText(getItemInfoDsc(itemInfo)); @@ -306,7 +326,7 @@ public class LayoutHierarchyView extends MultiLevelListView { if (nodeInfo.getLongClickable()) { info.add("longClickable"); } - //字符串拼接 + // 字符串拼接 String others = String.join(", ", info); if (!others.isEmpty()) { return extra + " [" + others + "]"; diff --git a/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/NodeInfoView.kt b/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/NodeInfoView.kt index a08702c0..9c45df77 100644 --- a/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/NodeInfoView.kt +++ b/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/NodeInfoView.kt @@ -22,7 +22,9 @@ import java.lang.reflect.Field * Modified by SuperMonster003 as of Dec 1, 2021. */ class NodeInfoView : RecyclerView { - //todo:调整数据结构,对话框关闭后根据已勾选的属性,生成选择器 + + // TODO by 抠脚本人 on Jul 12, 2023. + // ! 调整数据结构, 对话框关闭后根据已勾选的属性, 生成选择器 private val data = Array(FIELDS.size + 1) { Array(2) { "" } } constructor(context: Context) : super(context) @@ -75,7 +77,8 @@ class NodeInfoView : RecyclerView { } fun getCheckedDate(): Array { - //todo:数据增加checked属性,区分已选中项目 + // TODO by 抠脚本人 on Jul 12, 2023. + // ! 数据增加 checked 属性, 区分已选中项目 val checkedArr = data.filter { it[0] == "id" || it[0] == "text" } return Array(checkedArr.size) { dataToFx(checkedArr[it]) @@ -95,7 +98,7 @@ class NodeInfoView : RecyclerView { override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.apply { data[position].let { - //attrChecked.isChecked = false + // attrChecked.isChecked = false attrName.text = it[0] attrValue.text = it[1] } @@ -109,7 +112,7 @@ class NodeInfoView : RecyclerView { } internal inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - //val attrChecked: CheckBox = itemView.findViewById(R.id.generate) + // val attrChecked: CheckBox = itemView.findViewById(R.id.generate) val attrName: TextView = itemView.findViewById(R.id.name) val attrValue: TextView = itemView.findViewById(R.id.value) diff --git a/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/OnNodeInfoSelectListener.kt b/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/OnNodeInfoSelectListener.kt index 1f65773d..bec291c9 100644 --- a/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/OnNodeInfoSelectListener.kt +++ b/app/src/main/java/org/autojs/autojs/ui/floating/layoutinspector/OnNodeInfoSelectListener.kt @@ -4,6 +4,7 @@ import org.autojs.autojs.core.accessibility.NodeInfo /** * Created by Stardust on 2017/3/10. + * Transformed by 抠脚本人 on Jul 10, 2023. */ fun interface OnNodeInfoSelectListener { fun onNodeSelect(info: NodeInfo) diff --git a/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java b/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java index 1d238169..854e0d66 100644 --- a/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java @@ -292,8 +292,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa mProgressDialog.dismiss(); mProgressDialog = null; new MaterialDialog.Builder(this) - .title(R.string.text_build_successfully) - .content(getString(R.string.format_build_successfully, outApk.getPath())) + .title(R.string.text_build_succeeded) + .content(getString(R.string.format_build_succeeded, outApk.getPath())) .positiveText(R.string.text_install) .negativeText(R.string.text_cancel) .onPositive((dialog, which) -> IntentUtils.installApkOrToast(BuildActivity.this, outApk.getPath(), AppFileProvider.AUTHORITY)) diff --git a/app/src/main/java/org/autojs/autojs/util/ResourceMonitor.java b/app/src/main/java/org/autojs/autojs/util/ResourceMonitor.java index 386ab660..3079f364 100644 --- a/app/src/main/java/org/autojs/autojs/util/ResourceMonitor.java +++ b/app/src/main/java/org/autojs/autojs/util/ResourceMonitor.java @@ -14,7 +14,7 @@ public final class ResourceMonitor { private static final String LOG_TAG = "ResourceMonitor"; - // @Reference to TonyJiangWJ/Auto.js on Nov 22, 2021 + // @Reference to TonyJiangWJ/Auto.js (https://github.com/TonyJiangWJ/Auto.js) on Nov 22, 2021 private static class LockedResource { private ReentrantLock lock; private SparseArray resource; diff --git a/app/src/main/res/layout/editor_view.xml b/app/src/main/res/layout/editor_view.xml index 3a2c4eba..9f7a3384 100644 --- a/app/src/main/res/layout/editor_view.xml +++ b/app/src/main/res/layout/editor_view.xml @@ -69,7 +69,7 @@ android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:background="?selectableItemBackgroundBorderless" - android:contentDescription="@string/fun" + android:contentDescription="@string/text_function" android:padding="6dp" android:src="@drawable/ic_ali_fx" app:tint="#222329" /> diff --git a/app/src/main/res/layout/node_info_view_item.xml b/app/src/main/res/layout/node_info_view_item.xml index 6280f231..18205a2f 100644 --- a/app/src/main/res/layout/node_info_view_item.xml +++ b/app/src/main/res/layout/node_info_view_item.xml @@ -10,11 +10,20 @@ android:orientation="horizontal" android:padding="8dp"> - + + + + + + + + + + + + + + + + خدمة مقدمة AutoJs6 انقر لإطلاق AutoJs6 خدمة مقدمة AutoJs6 - نجح بناء.\nمسار الملف: %s + نجح بناء.\nمسار الملف: %s تصحيح [%s] chars: %s\nالخطوط: %s\nبحجم: %s تم تنزيل الملف إلى %s @@ -260,7 +260,7 @@ عمل مهمة البث بناء APK - نجح بناء + نجح بناء يلغي إلغاء لا يمكن قراءة الملف @@ -525,7 +525,7 @@ وصول وسائل الإعلام المشروع مع الوصول إلى وسائل الإعلام ، لن يطالب التحذير الأمني ​​لتسجيل الشاشة. فشل في حفظ المشروع - تم حفظ المشروع + تم حفظ المشروع مستعجل يترك انتهى التسجيل @@ -585,7 +585,7 @@ حفظ في تسجيل السيناريو البرنامج النصي - نجح النصي المحفوظ (هناك حاجة إلى تحديث) + نجح النصي المحفوظ (هناك حاجة إلى تحديث) يبحث يختار في حالة وجود مقطع (موجود) @@ -674,6 +674,10 @@ عنوان URL لمستودع امتداد VSCode لا يمكن إنشاء الاتصال أدنى إصدار من امتداد VSCode + وظيفة + اكتمل التنسيق + فشل التنسيق + تعليق المجموع: عنصر واحد diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index e5a5d88c..62025a2f 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -156,7 +156,7 @@ AutoJs6 foreground service Click to launch AutoJs6 AutoJs6 foreground service - Build succeeded.\nFile path: %s + Build succeeded.\nFile path: %s Debug [ %s ] Chars: %d\nLines: %d\nSize: %s File downloaded to %s @@ -258,7 +258,7 @@ Action Broadcast task Build APK - Build succeeded + Build succeeded Cancel Cancel Can\'t read file @@ -523,7 +523,7 @@ Project media access With project media access, security warning for screen recording will not prompt. Failed to save project - Project saved + Project saved Prompt Quit Recording finished @@ -583,7 +583,7 @@ Save to Script recording Script running - Script saved succeeded (refresh is needed) + Script saved succeeded (refresh is needed) Search Select If clip exists (exists) @@ -672,6 +672,10 @@ Repository URL of VSCode extension Connection can\'t be established Minimum version of VSCode extension + Function + Formatting completed + Failed to format + Comment Total: %d item diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 653c4187..1e4d03a1 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -158,7 +158,7 @@ Servicio de primer plano de AutoJs6 Haga clic para iniciar AutoJs6 Servicio de primer plano de AutoJs6 - La construcción se ha realizado con éxito.\nRuta del archivo: %s + La construcción se ha realizado con éxito.\nRuta del archivo: %s Depuración [ %s ] Caracteres: %d\nLíneas: %d\nTamaño: %s Archivo descargado en %s @@ -260,7 +260,7 @@ Acción Tarea de difusión Construir APK - Construir con éxito + Construir con éxito Cancelar Cancel No se puede leer el archivo @@ -525,7 +525,7 @@ Acceso a los medios del proyecto Con el acceso a los medios del proyecto, la advertencia de seguridad para la grabación de la pantalla no aparecerá. No se ha podido guardar el proyecto - Proyecto guardado + Proyecto guardado Indicación Salga de Grabación terminada @@ -585,7 +585,7 @@ Guardar en Grabación del script Script en ejecución - Guión guardado con éxito (es necesario refrescar) + Guión guardado con éxito (es necesario refrescar) Buscar en Seleccione Si el clip existe (exists) @@ -674,6 +674,10 @@ URL del repositorio de la extensión VSCode No se puede establecer la conexión Versión mínima de la extensión VSCode + Función + Formateo completado + Error de formato + Comente Total: %d artículo diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2ce4c09d..48eb0359 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -158,7 +158,7 @@ Service d\'avant-plan AutoJs6 Cliquez pour lancer AutoJs6 Service AutoJs6 au premier plan - Construction réussie.\nPlateau du fichier: %s + Construction réussie.\nPlateau du fichier: %s Debug [ %s ] Chars: %d\nLines: %d\nSize: %s Fichier téléchargé sur %s @@ -260,7 +260,7 @@ Action Tâche de diffusion Construire un APK - Construction réussie + Construction réussie Annulation Annul Impossible de lire le fichier @@ -525,7 +525,7 @@ Accès aux médias du projet Avec l\'accès aux médias du projet, l\'avertissement de sécurité pour l\'enregistrement d\'écran ne sera pas demandé. Fail to save project - Projet enregistré + Projet enregistré Prompt Quit Enregistrement terminé @@ -585,7 +585,7 @@ Save to Enregistrement de scripts Script running - Script enregistré réussi (un rafraîchissement est nécessaire) + Script enregistré réussi (un rafraîchissement est nécessaire) Recherche Select Si le clip existe (exists) @@ -674,6 +674,10 @@ URL du dépôt de l\'extension VSCode La connexion ne peut pas être établie Version minimale de l\'extension VSCode + Fonction + Formatage terminé + Échec du formatage + Commentaire Total: %d article diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 4160037a..7ca70a82 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -158,7 +158,7 @@ AutoJs6 のフォアグラウンド サービス クリックすると、AutoJs6 が起動します AutoJs6 のフォアグラウンド・サービス - ビルドに成功しました.\nファイル パス: %s + ビルドに成功しました.\nファイル パス: %s デバッグ [ %s ] 文字数: %d\n行数 %d\nサイズ: %s ファイルは %s にダウンロードされました @@ -260,7 +260,7 @@ 動作 タスクの実行 APKのビルド - ビルドに成功しました + ビルドに成功しました 取消 取消 ファイルを読めません @@ -525,7 +525,7 @@ プロジェクト・メディア・アクセス プロジェクトのメディアアクセス権を使用すると、画面録画のセキュリティ警告が表示されなくなります プロジェクトの保存に失敗しました - プロジェクトの保存 + プロジェクトの保存 プロンプト 終了 記録終了 @@ -585,7 +585,7 @@ 保存先 スクリプトの記録 スクリプトの実行 - スクリプトの保存に成功 (リフレッシュが必要です) + スクリプトの保存に成功 (リフレッシュが必要です) 検索 選択 クリップが存在する場合 (exists) @@ -674,6 +674,10 @@ VSCode 拡張機能のリポジトリURL 接続が確立できない VSCode 拡張機能の最小バージョン + 関数 + フォーマット完了 + フォーマット失敗 + コメント 合計: %d 件 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 2e2d940c..117e33b0 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -158,7 +158,7 @@ AutoJs6 전경 서비스 AutoJs6 을 시작하려면 클릭하십시오 AutoJs6 전경 서비스 - 빌드가 성공했습니다.\n파일 경로: %s + 빌드가 성공했습니다.\n파일 경로: %s 디버그 [%s] 숯: %d\n라인: %d\n크기: %s 파일은 %s 로 다운로드했습니다 @@ -260,7 +260,7 @@ 동작 방송 작업 APK 빌드 - 빌드가 성공했습니다 + 빌드가 성공했습니다 취소 취소 파일을 읽을 수 없다 @@ -525,7 +525,7 @@ 프로젝트 미디어 액세스 프로젝트 미디어 액세스를 사용하면 화면 기록에 대한 보안 경고가 중요하지 않습니다. 프로젝트를 저장하지 못했습니다 - 프로젝트 저장 + 프로젝트 저장 즉각적인 그만두다 녹음 완료 @@ -585,7 +585,7 @@ 저장 스크립트 녹음 스크립트 실행 - 스크립트 저장 성공 (새로 고침이 필요합니다) + 스크립트 저장 성공 (새로 고침이 필요합니다) 검색 고르다 클립이 존재하는 경우 (exists) @@ -674,6 +674,10 @@ VSCode 확장의 리포지토리 URL 연결을 설정할 수 없습니다 VSCode 확장의 최소 버전 + 기능 + 포맷 완료 + 포맷 실패 + 논평 총: %d 항목 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 9cdbb7f9..6509ae0d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -158,7 +158,7 @@ Служба AutoJs6 в фоновом режиме Нажмите для запуска AutoJs6 Фоновая служба AutoJs6 - Сборка прошла успешно.\nПуть к файлу: %s + Сборка прошла успешно.\nПуть к файлу: %s Отладка [ %s ] Заголовки: %d\nСтроки: %d\nРазмер: %s Файл загружен на %s @@ -260,7 +260,7 @@ Действие Трансляция задачи Сборка APK - Сборка прошла успешно + Сборка прошла успешно Отменить Отмен Невозможно прочитать файл @@ -525,7 +525,7 @@ Доступ к носителям проекта При наличии доступа к медиафайлам проекта предупреждение о безопасности при записи экрана не выдается. Не удалось сохранить проект - Проект сохранен + Проект сохранен Запрос Выйти Запись завершена @@ -585,7 +585,7 @@ Сохранить в Запись сценария Выполнение сценария - Сценарий сохранен успешно (требуется обновление) + Сценарий сохранен успешно (требуется обновление) Поиск Выберите Если клип существует (существует) @@ -674,6 +674,10 @@ URL репозитория расширения VSCode Соединение не может быть установлено Минимальная версия расширения VSCode + Функция + Форматирование завершено + Не удалось отформатировать + Комментарий Всего: %d элемент diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 612f7fad..04fa4f18 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -158,7 +158,7 @@ AutoJs6 前台服務 點擊返回 AutoJs6 主界面 AutoJs6 前台服務 - 構建成功.\n文件路徑: %s + 構建成功.\n文件路徑: %s 調試 [ %s ] 字符數: %d\n行數: %d\n大小: %s 文件已下載至 %s @@ -260,7 +260,7 @@ 動作 (Action) 廣播觸發任務 打包應用 - 打包成功 + 打包成功 取消 取消 無法讀取文件 @@ -525,7 +525,7 @@ 投影媒體權限 被授予 \"投影媒體權限\" 後, 錄製屏幕的安全提示窗口將不再彈出. 項目保存失敗 - 項目已保存 + 項目已保存 提示 放棄 錄製完成 @@ -585,7 +585,7 @@ 保存到 錄製腳本 腳本運行 - 保存成功 (需刷新目錄) + 保存成功 (需刷新目錄) 搜索 選擇 判斷控件存在 (exists) @@ -674,6 +674,10 @@ VSCode 插件項目地址 無法建立連接 VSCode 插件最低版本 + 函數 + 格式化已完成 + 格式化失敗 + 註釋 共計: %d 項 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 0e5e1380..5164c26b 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -158,7 +158,7 @@ AutoJs6 前臺服務 點選返回 AutoJs6 主介面 AutoJs6 前臺服務 - 構建成功.\n檔案路徑: %s + 構建成功.\n檔案路徑: %s 除錯 [ %s ] 字元數: %d\n行數: %d\n大小: %s 檔案已下載至 %s @@ -260,7 +260,7 @@ 動作 (Action) 廣播觸發任務 打包應用 - 打包成功 + 打包成功 取消 取消 無法讀取檔案 @@ -525,7 +525,7 @@ 投影媒體許可權 被授予 \"投影媒體許可權\" 後, 錄製螢幕的安全提示視窗將不再彈出. 專案儲存失敗 - 專案已儲存 + 專案已儲存 提示 放棄 錄製完成 @@ -585,7 +585,7 @@ 儲存到 錄製指令碼 指令碼執行 - 儲存成功 (需重新整理目錄) + 儲存成功 (需重新整理目錄) 搜尋 選擇 判斷控制元件存在 (exists) @@ -674,6 +674,10 @@ VSCode 外掛專案地址 無法建立連線 VSCode 外掛最低版本 + 函式 + 格式化已完成 + 格式化失敗 + 註釋 共計: %d 項 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index f39f5b6a..6f43ed79 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -154,7 +154,7 @@ AutoJs6 前台服务 点击返回 AutoJs6 主界面 AutoJs6 前台服务 - 构建成功.\n文件路径: %s + 构建成功.\n文件路径: %s 调试 [ %s ] 字符数: %d\n行数: %d\n大小: %s 文件已下载至 %s @@ -256,7 +256,7 @@ 动作 (Action) 广播触发任务 打包应用 - 打包成功 + 打包成功 取消 取消 无法读取文件 @@ -521,7 +521,7 @@ 投影媒体权限 被授予 \"投影媒体权限\" 后, 录制屏幕的安全提示窗口将不再弹出. 项目保存失败 - 项目已保存 + 项目已保存 提示 放弃 录制完成 @@ -581,7 +581,7 @@ 保存到 录制脚本 脚本运行 - 保存成功 (需刷新目录) + 保存成功 (需刷新目录) 搜索 选择 判断控件存在 (exists) @@ -670,6 +670,10 @@ VSCode 插件项目地址 无法建立连接 VSCode 插件最低版本 + 函数 + 格式化已完成 + 格式化失败 + 注释 共计: %d 项 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 34ba73f4..84dee0bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -205,7 +205,7 @@ AutoJs6 foreground service Click to launch AutoJs6 AutoJs6 foreground service - Build succeeded.\nFile path: %s + Build succeeded.\nFile path: %s Debug [ %s ] com.example.script%d %.1fKB/%.1fKB @@ -440,7 +440,7 @@ android.intent.action. Broadcast task Build APK - Build succeeded + Build succeeded Cancel Cancel Can\'t read file @@ -460,6 +460,7 @@ Close Code Beautify code + Comment Command copied to clipboard Compatibility Compatibility with Java classes for version 4.x @@ -712,7 +713,7 @@ Project media access With project media access, security warning for screen recording will not prompt. Failed to save project - Project saved + Project saved Prompt Quit Recording finished @@ -778,7 +779,7 @@ Save to Script recording Script running - Script saved succeeded (refresh is needed) + Script saved succeeded (refresh is needed) Search Select If clip exists (exists) @@ -880,7 +881,9 @@ Colon must follow a valid IP address Repeated dot symbol Repeated colon symbol - 函数 + Function + Formatting completed + Failed to format Total: %d item diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e4743973..6881fc8a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Jul 12 09:21:49 CST 2023 +#Fri Jul 21 20:25:37 CST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-rc-1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/libs/org.opencv-4.5.5/build.gradle.kts b/libs/org.opencv-4.5.5/build.gradle.kts index 4eb84a8a..cc0c2efa 100644 --- a/libs/org.opencv-4.5.5/build.gradle.kts +++ b/libs/org.opencv-4.5.5/build.gradle.kts @@ -1,5 +1,5 @@ // @Reference -// ! Current library (OpenCV) was referred to TonyJiangWJ/Auto.js on Mar 18, 2022. +// ! Current library (OpenCV) was referred to TonyJiangWJ/Auto.js (https://github.com/TonyJiangWJ/Auto.js) on Mar 18, 2022. "opencv-4.5.5.aar" .let { path -> diff --git a/version.properties b/version.properties index c30b3bbe..b4b29b29 100644 --- a/version.properties +++ b/version.properties @@ -1,10 +1,9 @@ -#Wed Jul 12 14:08:52 CST 2023 -BUILD_TIME=1689142132084 +#Fri Jul 21 21:40:34 CST 2023 +BUILD_TIME=1689946834216 COMPILE_SDK_VERSION=33 JAVA_VERSION=20 MIN_SDK_VERSION=24 TARGET_SDK_VERSION=33 -#2006/2020 -VERSION_BUILD=1999 +VERSION_BUILD=2046 VERSION_NAME=6.3.3 -VSCODE_EXT_REQUIRED_VERSION=1.0.8 +VSCODE_EXT_REQUIRED_VERSION=1.0.6