diff --git a/README.md b/README.md index 7478c8e2..5c7abe11 100644 --- a/README.md +++ b/README.md @@ -29,17 +29,17 @@ [Auto.js](https://github.com/hyb1996/Auto.js) 是一款 Android 平台支持 [无障碍服务](https://developer.android.com/guide/topics/ui/accessibility/service?hl=zh-cn) 的 JavaScript 自动化工具软件. -Auto.js 由 [hyb1996](https://github.com/hyb1996) 于 Jan 27, 2017 初次发布, 于 Mar 13, 2020 停止维护, 最终版本名称为 `4.1.1 alpha2`. +Auto.js 由 [hyb1996](https://github.com/hyb1996) 于 `2017/01/27` 初次发布, 于 `2020/03/13` 停止维护, 最终版本名称为 `4.1.1 alpha2`. -AutoJs6 在 Auto.js 最终项目的基础上, 于 Dec 1, 2021 进行二次开发, 继续保持开源免费. +AutoJs6 在 Auto.js 最终项目的基础上, 于 `2021/12/01` 进行二次开发, 继续保持开源免费. 基于 Auto.js 二次开发的开源项目 (以开发时间排序, 仅部分列举): | 项目名称 | 应用名称 | 开发者 | 开发时间 | |:-----------------------------------------------------:|:----------------------:|:-----------------------------------------------------:|:------------:| -| [Auto.js](https://github.com/TonyJiangWJ/Auto.js) | Auto.js M | [TonyJiangWJ](https://github.com/TonyJiangWJ) | Nov 21, 2019 | -| [AutoX](https://github.com/kkevsekk1/AutoX) | Autox.js / Autox.js v6 | [kkevsekk1](https://github.com/kkevsekk1) | Jul 24, 2020 | -| [AutoJs6](https://github.com/SuperMonster003/AutoJs6) | AutoJs6 | [SuperMonster003](https://github.com/SuperMonster003) | Dec 1, 2021 | +| [Auto.js](https://github.com/TonyJiangWJ/Auto.js) | Auto.js M | [TonyJiangWJ](https://github.com/TonyJiangWJ) | `2019/11/21` | +| [AutoX](https://github.com/kkevsekk1/AutoX) | Autox.js / Autox.js v6 | [kkevsekk1](https://github.com/kkevsekk1) | `2020/07/24` | +| [AutoJs6](https://github.com/SuperMonster003/AutoJs6) | AutoJs6 | [SuperMonster003](https://github.com/SuperMonster003) | `2021/12/01` | ****** @@ -133,19 +133,20 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 Dec 1, 2021 进行二次开发 # v6.4.0 -###### 2023/10/25 +###### 2023/10/30 * `新增` ocr 模块支持 Paddle Lite 引擎 (by [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`pr #120`](http://pr.autojs6.com/120)_ * `新增` 打包功能支持内置插件与外部插件两种打包方式 (by [LZX284](https://github.com/LZX284)) _[`pr #151`](http://pr.autojs6.com/151)_ * `新增` WebSocket 模块 (参阅 项目文档 > [WebSocket](https://docs.autojs6.com/#/webSocketType)) * `新增` barcode / qrcode 模块 (参阅 项目文档 > [条码](https://docs.autojs6.com/#/barcode) / [二维码](https://docs.autojs6.com/#/qrcode)) -* `新增` shizuku 模块 (参阅 项目文档 > [Shizuku](https://docs.autojs6.com/#/shizuku)) +* `新增` shizuku 模块 (参阅 项目文档 > [Shizuku](https://docs.autojs6.com/#/shizuku)) 及主页抽屉权限开关 * `新增` device.rotation / device.orientation 等方法 * `新增` 内部 Java 类支持 class 静态属性访问 * `新增` 支持在安卓系统设置页面选择并切换应用语言 (安卓 13 及以上) * `新增` 支持设置页面添加或长按应用图标激活 [应用快捷方式](https://developer.android.com/guide/topics/ui/shortcuts?hl=zh-cn) , 可启动文档和设置等页面 * `修复` 重新合并部分 PR (by [aiselp](https://github.com/aiselp)) 以解决部分脚本无法正常结束运行的问题 _[`pr #75`](http://pr.autojs6.com/75)_ _[`pr #78`](http://pr.autojs6.com/78)_ * `修复` 打包应用无法使用 AutoJs6 新增 API 的问题 (by [LZX284](https://github.com/LZX284)) _[`pr #151`](http://pr.autojs6.com/151)_ +* `修复` 打包应用在系统夜间模式下的样式异常 * `修复` VSCode 插件保存文件到本地时文件扩展名信息丢失的问题 * `修复` 使用协程特性运行项目产生未捕获异常致使应用崩溃的问题 * `修复` 重启或退出应用时浮动按钮无法记录其位置状态信息的问题 @@ -164,9 +165,12 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 Dec 1, 2021 进行二次开发 * `优化` 日志活动页面发送功能支持条目数量过大时自动截取并作出提示 * `优化` ocr 模块同时兼容 Google MLKit 及 Paddle Lite 引擎 (参阅 项目文档 > [光学字符识别](https://docs.autojs6.com/#/ocr?id=p-mode)) * `优化` 提升无障碍服务自动启动的成功概率 +* `优化` Kotlin 注解处理由 kapt 迁移至 KSP +* `优化` 构建工具支持 IntelliJ Idea EAP 版本 * `优化` 构建工具自适应 Java 发行版本以尽量避免 "无效的发行版本" 问题 * `优化` 构建工具优化 IDE 及相关插件的版本退级逻辑并增加版本预测能力 -* `优化` 部分 Kotlin 注解处理由 kapt 迁移至 KSP +* `优化` 适配 VSCode 插件 1.0.7 +* `优化` 部分依赖或本地库版本调整 _[`CHANGELOG.md`](http://project.autojs6.com/blob/master/app/src/main/assets/doc/CHANGELOG.md#v640)_ # v6.3.3 @@ -239,12 +243,12 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 Dec 1, 2021 进行二次开发 #### Android Studio 准备 -下载 `Android Studio Giraffe | 2022.3.1 Patch 2` 版本 (按需选择其一): +下载 `Android Studio Hedgehog | 2023.1.1 RC 1` 版本 (按需选择其一): -- [android-studio-2022.3.1.20-windows.exe](https://redirector.gvt1.com/edgedl/android/studio/install/2022.3.1.20/android-studio-2022.3.1.20-windows.exe) (1.1 GB) -- [android-studio-2022.3.1.20-windows.zip](https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2022.3.1.20/android-studio-2022.3.1.20-windows.zip) (1.1 GB) +- [android-studio-2023.1.1.23-windows.exe](https://redirector.gvt1.com/edgedl/android/studio/install/2023.1.1.23/android-studio-2023.1.1.23-windows.exe) (1.1 GB) +- [android-studio-2023.1.1.23-windows.zip](https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2023.1.1.23/android-studio-2023.1.1.23-windows.zip) (1.1 GB) -> 注: 上述版本发布时间为 2023 年 9 月 28 日. 如需下载其他版本, 或上述链接已失效, 可访问 [Android Studio 发行版本归档](https://developer.android.com/studio/archive?hl=en) 页面. +> 注: 上述版本发布时间为 2023 年 10 月 17 日. 如需下载其他版本, 或上述链接已失效, 可访问 [Android Studio 发行版本归档](https://developer.android.com/studio/archive?hl=en) 页面. 安装或解压上述文件, 运行 Android Studio 软件 (如 `"D:\android-studio\bin\studio64.exe"`). @@ -267,11 +271,32 @@ Android SDK (安卓软件开发工具包) 待相关资源下载并安装完毕, 点击 `Finish (完成)` 按钮. 上述 `Android SDK Location (安卓软件开发工具包位置)` 处将自动完成路径填写, SDK 准备工作随即完成. +#### Android SDK Tools 准备 + +AutoJs6 需要使用部分 SDK 工具 (如 NDK 及 CMake). + +> 注: 如果计算机系统已安装 AutoJs6 全部所需的 Android SDK Tools, 则可跳过此小节内容. + +在 Android Studio 软件中使用快捷键 `CTRL + ALT + S` 打开设置页面: + +```text +Appearance & Behavior (外观与表现) -> +System Settings (系统设置) -> +Android SDK (安卓软件开发工具包) -> +SDK Tools (SDK 工具) (位于右侧窗口) +``` + +勾选 `Show Package Details (显示包详情)`, 依次点击 NDK 及 CMake, 确保相应版本的工具已勾选 (截至 2023 年 10 月 30 日, NDK 所需版本为 `21.1.6352462`, CMake 所需版本为 `3.10.2`). + +SDK 工具的版本信息位于 AutoJs6 项目根目录的 `version.properties` 文件中. + #### JDK 准备 -AutoJs6 项目依赖的 `JDK (Java 开发工具包)` 发行版本不低于 `17`. +AutoJs6 项目依赖的 `JDK (Java 开发工具包)` 发行版本不低于 `17`, 但建议不低于 `19`. -> 注: 如果计算机系统已安装 JDK 且版本不低于 17, 则可跳过此小节内容. +需额外留意, 截至 2023 年 10 月 30 日, AutoJs6 暂不支持 JDK 最新版本 `21`. + +> 注: 如果计算机系统已安装 JDK 且版本满足上述要求, 则可跳过此小节内容. JDK 可使用 IDE 直接下载, 或访问 [Oracle 网站](https://www.oracle.com/java/technologies/downloads/) 下载. @@ -348,6 +373,7 @@ Gradle - [Ant-Forest](https://github.com/SuperMonster003/Ant-Forest) - 蚂蚁森林能量自动收取脚本 by [SuperMonster003](https://github.com/SuperMonster003) - [autojs](https://github.com/e1399579/autojs) - Auto.js 实用脚本 by [e1399579](https://github.com/e1399579) - [autojsDemo](https://github.com/snailuncle/autojsDemo) - Auto.js 演示示例 by [snailuncle](https://github.com/snailuncle) +- [autojs 相关仓库](https://github.com/topics/autojs) - GitHub 与 autojs 话题相关的全部仓库 ****** @@ -355,21 +381,23 @@ Gradle ****** -感谢每一位参与 AutoJs6 项目开发的贡献人员. +感谢每一位参与 AutoJs6 项目开发的贡献人员. | 贡献人员 | 提交数 | 最近提交 | |:-----------------------------------------------------:|:-------------------------------------------------------------------------------:|:------------:| -| [TonyJiangWJ](https://github.com/TonyJiangWJ) | [3](https://github.com/SuperMonster003/AutoJs6/commits?author=TonyJiangWJ) | Aug 29, 2023 | -| [SuperMonster003](https://github.com/SuperMonster003) | [33](https://github.com/SuperMonster003/AutoJs6/commits?author=SuperMonster003) | Jul 21, 2023 | -| [LZX284](https://github.com/LZX284) (Ai) | [4](https://github.com/SuperMonster003/AutoJs6/commits?author=LZX284) | Jul 20, 2023 | -| [little-alei](https://github.com/little-alei) (抠脚本人) | [12](https://github.com/SuperMonster003/AutoJs6/commits?author=little-alei) | Jul 12, 2023 | -| [aiselp](https://github.com/aiselp) | [6](https://github.com/SuperMonster003/AutoJs6/pulls?q=is%3Apr+author%3Aaiselp) | Jun 14, 2023 | -| [LYS86](https://github.com/LYS86) (LYS) | [2](https://github.com/SuperMonster003/AutoJs6/commits?author=LYS86) | Jun 3, 2023 | +| [LZX284](https://github.com/LZX284) (Ai) | [13](https://github.com/SuperMonster003/AutoJs6/commits?author=LZX284) | `2023/10/09` | +| [TonyJiangWJ](https://github.com/TonyJiangWJ) | [3](https://github.com/SuperMonster003/AutoJs6/commits?author=TonyJiangWJ) | `2023/08/29` | +| [little-alei](https://github.com/little-alei) (抠脚本人) | [12](https://github.com/SuperMonster003/AutoJs6/commits?author=little-alei) | `2023/07/12` | +| [aiselp](https://github.com/aiselp) | [6](https://github.com/SuperMonster003/AutoJs6/pulls?q=is%3Apr+author%3Aaiselp) | `2023/06/14` | +| [LYS86](https://github.com/LYS86) (LYS) | [2](https://github.com/SuperMonster003/AutoJs6/commits?author=LYS86) | `2023/06/03` | -> 数据统计区间: Dec 1, 2021 - Oct 3, 2023. -> 数据条目按 "最近提交" 列排序 (降序). -> 新发起的暂未处理的 Pull Request, 将在合并处理后加入数据统计. -> 部分贡献人员在 [GitHub Contributors](https://github.com/SuperMonster003/AutoJs6/graphs/contributors) 未能正常出现, 其提交记录为空, 仍可通过 [Pull Request](https://github.com/SuperMonster003/AutoJs6/pulls) 查看贡献记录. +数据更新于 `2023/10/25`. + +数据条目按 `最近提交` 降序排序. + +新发起的暂未处理的 Pull Request, 将在合并处理后加入数据统计. + +部分贡献人员在 [GitHub Contributors](https://github.com/SuperMonster003/AutoJs6/graphs/contributors) 未能正常出现, 其提交记录为空, 仍可通过 [Pull Request](https://github.com/SuperMonster003/AutoJs6/pulls) 查看贡献记录. [//]: # ( # --------------------------------------------------------------# @@ -377,7 +405,7 @@ Gradle # --------------------------------------------------------------# - CHANGELOG.md - Update entries for AutoJs6 by checking all changed files - - Update entries for Gradle plugins + - Update entries for Gradle plugins [ implementation ] - Update version name and released date - Append related GitHub issues to changelog entries - README.md @@ -388,6 +416,9 @@ Gradle - Update android studio download links and version names - Remove the part like [ alpha / beta / ... ] of VERSION_NAME in version.properties - Update dependencies information for Android Gradle Plugin in the top-level build.gradle.kts + - Update dependencies TypeScript declarations. - Re-generate documentation by running the python script + - Check the two-way versions for AutoJs6 and VSCode ext, then publish the ext to Microsoft + - Run Gradle task "app:assembleInrtRelease" - Build APK to determine the final VERSION_BUILD field ) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1331aa68..e2759af8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,7 +29,6 @@ val templateName = "template" plugins { id("com.android.application") id("com.google.devtools.ksp") - id("org.jetbrains.kotlin.kapt") /* kotlin("kapt") */ id("org.jetbrains.kotlin.android") /* kotlin("android") */ } @@ -161,9 +160,7 @@ dependencies /* Annotations */ { // Android Annotations implementation("org.androidannotations:androidannotations-api:4.8.0") implementation("androidx.annotation:annotation:1.7.0") - // FIXME by SuperMonster003 on Aug 17, 2023. - // ! Failed to migrate it from kapt to KSP. - kapt("org.androidannotations:androidannotations:4.8.0") + ksp("org.androidannotations:androidannotations:4.8.0") // JCIP Annotations implementation("net.jcip:jcip-annotations:1.0") @@ -177,7 +174,7 @@ dependencies /* AppCompat */ { // ! Comment with related code will be removed since Jan 5, 2024. // // @Hint by SuperMonster003 on Oct 5, 2023. - // ! It look like that bugs below in version 1.5.x has gone away since 1.6.x. + // ! It looks like that bugs below in version 1.5.x has gone away since 1.6.x. // implementation("androidx.appcompat:appcompat") { // version { // strictly("1.4.2") @@ -361,29 +358,32 @@ android { gradle.taskGraph.whenReady(object : Action { override fun execute(taskGraph: TaskExecutionGraph) { - val task = project.getTasksByName("${buildTypeRelease}InrtRelease", true).firstOrNull() ?: return - task.doLast { - copy { - val src = "build/outputs/apk/$flavorNameInrt/$buildTypeRelease" - val dst = "src/main/assets" - val ext = Utils.FILE_EXTENSION_APK + val taskName = "$buildActionAssemble${flavorNameInrt.uppercaseFirstChar()}${buildTypeRelease.uppercaseFirstChar()}" + project.getTasksByName(taskName, true) + .firstOrNull() + ?.doLast { + copy { + val src = "build/outputs/apk/$flavorNameInrt/$buildTypeRelease" + val dst = "src/main/assets" + val ext = Utils.FILE_EXTENSION_APK - if (!file(src).isDirectory) { - return@copy + if (!file(src).isDirectory) { + return@copy + } + + from(src); into(dst) + + val verName = versionName?.replace(Regex("\\s"), "-")?.lowercase() + val srcFileName = "$flavorNameInrt-v$verName-universal.$ext" /* e.g. inrt-v6.4.0-beta-universal.apk */ + val dstFileName = "$templateName.$ext" + val isOverridden = file(File(dst, dstFileName)).exists() + include(srcFileName) + rename(srcFileName, dstFileName) + println("Source: ${file(File(src, srcFileName))}") + println("Destination: ${file(File(dst, dstFileName))}${if (isOverridden) " [overridden]" else ""}") } - - from(src); into(dst) - - val verName = versionName?.replace(Regex("\\s"), "-")?.lowercase() - val srcFileName = "$flavorNameInrt-v$verName-universal.$ext" /* e.g. inrt-v6.4.0-beta-universal.apk */ - val dstFileName = "$templateName.$ext" - val isOverridden = file(File(dst, dstFileName)).exists() - include(srcFileName) - rename(srcFileName, dstFileName) - println("Source: ${file(File(src, srcFileName))}") - println("Destination: ${file(File(dst, dstFileName))}${if (isOverridden) " [overridden]" else ""}") } - } + ?: println("$taskName doesn't exist in project ${project.name}") } }) } @@ -409,8 +409,6 @@ android { targetCompatibility = versions.javaVersion } - // Utils.Version(ANDROID_GRADLE_PLUGIN_VERSION) < Utils.Version("8.0") - // @Legacy packagingOptions { ... } packaging { arrayOf( @@ -615,18 +613,37 @@ class Versions(filePath: String) { val appVersionCode = properties["VERSION_BUILD"].let { it as String }.toInt() val vscodeExtRequiredVersion = properties["VSCODE_EXT_REQUIRED_VERSION"] as String - private val javaVersionMinSupported = properties["JAVA_VERSION_MIN_SUPPORTED"] + private val currentVersionInt = JavaVersion.current().majorVersion.toInt() + + private val javaVersionMinSupported: Int = properties["JAVA_VERSION_MIN_SUPPORTED"] .let { it as String }.toInt() .also { - if (!JavaVersion.current().isCompatibleWith(JavaVersion.toVersion(it))) { - throw GradleException("Current Gradle JDK version ${JavaVersion.current()} does not meet the minimum requirement which $it is needed.") + if (currentVersionInt < it) { + throw GradleException( + "Current Gradle JDK version ${JavaVersion.current()} does not meet " + + "the minimum requirement which $it is needed." + ) } } - private val javaVersionMinSuggested = properties["JAVA_VERSION_MIN_SUGGESTED"] + private val javaVersionMinSuggested: Int = properties["JAVA_VERSION_MIN_SUGGESTED"] .let { it as String }.toInt() .also { - if (!JavaVersion.current().isCompatibleWith(JavaVersion.toVersion(it))) { - logger.error("It is recommended to upgrade current Gradle JDK version ${JavaVersion.current()} to $it or higher.") + if (currentVersionInt < it) { + logger.error( + "It is recommended to upgrade current Gradle JDK version ${JavaVersion.current()} " + + "to $it or higher (but lower than $javaVersionMinRadical)." + ) + } + } + private val javaVersionMinRadical: Int = properties["JAVA_VERSION_MIN_RADICAL"] + .let { it as String }.toInt() + .also { + if (it in currentVersionInt downTo 1) { + logger.error( + "It is recommended to downgrade current Gradle JDK version $currentVersionInt " + + "to ${it - 1}${if (it - 1 > javaVersionMinSuggested) " or lower (but not lower than $javaVersionMinSuggested)" else ""}, " + + "as Gradle may be not compatible with JDK $it${if (currentVersionInt > it) " (and above)" else ""} for now." + ) } } private val javaVersionRaw = properties["JAVA_VERSION"] as String @@ -643,16 +660,18 @@ class Versions(filePath: String) { ) val javaVersion: JavaVersion by lazy { - var versionInt = javaVersionRaw.toInt() + var niceVersionInt = javaVersionRaw.toInt() + var isFallback = false - while (versionInt > javaVersionMinSupported) { - if (JvmTarget.values().any { it.name.contains(Regex("_$versionInt$")) }) { + while (niceVersionInt > javaVersionMinSupported) { + if (JvmTarget.values().any { it.name.contains(Regex("_$niceVersionInt$")) }) { break } - versionInt -= 1 + niceVersionInt -= 1 + isFallback = true } - if (!JavaVersion.toVersion("$versionInt").isCompatibleWith(JavaVersion.toVersion(javaVersionRaw))) { + if (isFallback) { javaVersionInfoSuffix += " [fallback]" } @@ -660,18 +679,18 @@ class Versions(filePath: String) { val platformType = gradle.extra["platformType"] as String javaVersionCeilMap[platformType]!![platformVersion]?.let { ceil: Int -> - if (versionInt > ceil) { - versionInt = ceil + if (niceVersionInt > ceil) { + niceVersionInt = ceil javaVersionInfoSuffix += " [coerced]" } } - if (!JavaVersion.current().isCompatibleWith(JavaVersion.toVersion(versionInt))) { - versionInt = JavaVersion.current().majorVersion.toInt() + if (niceVersionInt > currentVersionInt) { + niceVersionInt = currentVersionInt javaVersionInfoSuffix += " [consistent]" } - JavaVersion.toVersion(versionInt.toString()) + JavaVersion.toVersion(niceVersionInt) } private var isBuildNumberAutoIncremented = false diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cb10a9f2..84ff26f5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,6 +53,9 @@ + + + @@ -149,6 +152,7 @@ + + android:value="true"/> - + + diff --git a/app/src/main/assets/doc/CHANGELOG.md b/app/src/main/assets/doc/CHANGELOG.md index a94c6c45..7c42ced9 100644 --- a/app/src/main/assets/doc/CHANGELOG.md +++ b/app/src/main/assets/doc/CHANGELOG.md @@ -6,19 +6,20 @@ # v6.4.0 -###### 2023/10/25 +###### 2023/10/30 * `新增` ocr 模块支持 Paddle Lite 引擎 (by [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`pr #120`](http://pr.autojs6.com/120)_ * `新增` 打包功能支持内置插件与外部插件两种打包方式 (by [LZX284](https://github.com/LZX284)) _[`pr #151`](http://pr.autojs6.com/151)_ * `新增` WebSocket 模块 (参阅 项目文档 > [WebSocket](https://docs.autojs6.com/#/webSocketType)) * `新增` barcode / qrcode 模块 (参阅 项目文档 > [条码](https://docs.autojs6.com/#/barcode) / [二维码](https://docs.autojs6.com/#/qrcode)) -* `新增` shizuku 模块 (参阅 项目文档 > [Shizuku](https://docs.autojs6.com/#/shizuku)) +* `新增` shizuku 模块 (参阅 项目文档 > [Shizuku](https://docs.autojs6.com/#/shizuku)) 及主页抽屉权限开关 * `新增` device.rotation / device.orientation 等方法 * `新增` 内部 Java 类支持 class 静态属性访问 * `新增` 支持在安卓系统设置页面选择并切换应用语言 (安卓 13 及以上) * `新增` 支持设置页面添加或长按应用图标激活 [应用快捷方式](https://developer.android.com/guide/topics/ui/shortcuts?hl=zh-cn) , 可启动文档和设置等页面 * `修复` 重新合并部分 PR (by [aiselp](https://github.com/aiselp)) 以解决部分脚本无法正常结束运行的问题 _[`pr #75`](http://pr.autojs6.com/75)_ _[`pr #78`](http://pr.autojs6.com/78)_ * `修复` 打包应用无法使用 AutoJs6 新增 API 的问题 (by [LZX284](https://github.com/LZX284)) _[`pr #151`](http://pr.autojs6.com/151)_ +* `修复` 打包应用在系统夜间模式下的样式异常 * `修复` VSCode 插件保存文件到本地时文件扩展名信息丢失的问题 * `修复` 使用协程特性运行项目产生未捕获异常致使应用崩溃的问题 * `修复` 重启或退出应用时浮动按钮无法记录其位置状态信息的问题 @@ -37,9 +38,11 @@ * `优化` 日志活动页面发送功能支持条目数量过大时自动截取并作出提示 * `优化` ocr 模块同时兼容 Google MLKit 及 Paddle Lite 引擎 (参阅 项目文档 > [光学字符识别](https://docs.autojs6.com/#/ocr?id=p-mode)) * `优化` 提升无障碍服务自动启动的成功概率 +* `优化` Kotlin 注解处理由 kapt 迁移至 KSP +* `优化` 构建工具支持 IntelliJ Idea EAP 版本 * `优化` 构建工具自适应 Java 发行版本以尽量避免 "无效的发行版本" 问题 * `优化` 构建工具优化 IDE 及相关插件的版本退级逻辑并增加版本预测能力 -* `优化` 部分 Kotlin 注解处理由 kapt 迁移至 KSP +* `优化` 适配 VSCode 插件 1.0.7 * `依赖` 附加 Rikka Shizuku 版本 13.1.5 * `依赖` 附加 MLKit Barcode Scanning 版本 17.2.0 * `依赖` 升级 OpenCV 版本 4.5.5 -> 4.8.0 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ)) @@ -48,7 +51,6 @@ * `依赖` 升级 Apache Commons Lang3 版本 3.12.0 -> 3.13.0 * `依赖` 升级 Glide 版本 4.15.1 -> 4.16.0 * `依赖` 升级 Android Analytics 版本 14.3.0 -> 14.4.0 -* `依赖` 升级 Androidx AppCompat 版本 1.4.2 -> 1.6.0-alpha05 * `依赖` 升级 Androidx WebKit 版本 1.7.0 -> 1.8.0 * `依赖` 升级 Androidx Preference 版本 1.2.0 -> 1.2.1 * `依赖` 升级 Androidx Annotation 版本 1.6.0 -> 1.7.0 diff --git a/app/src/main/assets/docs/404.html b/app/src/main/assets/docs/404.html index 37af334a..525edf4f 100644 --- a/app/src/main/assets/docs/404.html +++ b/app/src/main/assets/docs/404.html @@ -2,7 +2,7 @@ - 页面不存在 | AutoJs6 文档 - 6.3.3 + 页面不存在 | AutoJs6 文档 - 6.4.0 @@ -42,6 +42,8 @@
  • Color - 颜色
  • Image - 图像
  • OCR - 光学字符识别
  • +
  • Barcode - 条码
  • +
  • QR Code - 二维码
  • Keys - 按键
  • Device - 设备
  • Storage - 储存
  • @@ -54,6 +56,7 @@
  • Notice - 消息通知
  • Console - 控制台
  • Shell
  • +
  • Shizuku
  • Media - 多媒体
  • Sensor - 传感器
  • Recorder - 记录器
  • @@ -80,7 +83,9 @@
  • UiObject - 控件节点
  • UiObjectCollection - 控件集合
  • UiObjectActions - 控件节点行为
  • -
  • ImageWrapper - 包装图像类
  • +
  • WebSocket
  • +
  • EventEmitter - 事件发射器
  • +
  • ImageWrapper - 包装图像类
  • App - 应用枚举类
  • Color - 颜色类
  • Version - 版本工具类
  • @@ -155,7 +160,7 @@
    -

    AutoJs6 文档 - 6.3.3

    +

    AutoJs6 文档 - 6.4.0

    索引 | diff --git a/app/src/main/assets/docs/activity.html b/app/src/main/assets/docs/activity.html index 2e5d09fa..ea937ce5 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.3 + 活动 (Activity) | AutoJs6 文档 - 6.4.0 @@ -42,6 +42,8 @@

  • Color - 颜色
  • Image - 图像
  • OCR - 光学字符识别
  • +
  • Barcode - 条码
  • +
  • QR Code - 二维码
  • Keys - 按键
  • Device - 设备
  • Storage - 储存
  • @@ -54,6 +56,7 @@
  • Notice - 消息通知
  • Console - 控制台
  • Shell
  • +
  • Shizuku
  • Media - 多媒体
  • Sensor - 传感器
  • Recorder - 记录器
  • @@ -80,7 +83,9 @@
  • UiObject - 控件节点
  • UiObjectCollection - 控件集合
  • UiObjectActions - 控件节点行为
  • -
  • ImageWrapper - 包装图像类
  • +
  • WebSocket
  • +
  • EventEmitter - 事件发射器
  • +
  • ImageWrapper - 包装图像类
  • App - 应用枚举类
  • Color - 颜色类
  • Version - 版本工具类
  • @@ -155,7 +160,7 @@
    -

    AutoJs6 文档 - 6.3.3

    +

    AutoJs6 文档 - 6.4.0

    索引 | diff --git a/app/src/main/assets/docs/all.html b/app/src/main/assets/docs/all.html index f0a7e2eb..5c17a785 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.3 + 综述 (Overview) | AutoJs6 文档 - 6.4.0 @@ -42,6 +42,8 @@

  • Color - 颜色
  • Image - 图像
  • OCR - 光学字符识别
  • +
  • Barcode - 条码
  • +
  • QR Code - 二维码
  • Keys - 按键
  • Device - 设备
  • Storage - 储存
  • @@ -54,6 +56,7 @@
  • Notice - 消息通知
  • Console - 控制台
  • Shell
  • +
  • Shizuku
  • Media - 多媒体
  • Sensor - 传感器
  • Recorder - 记录器
  • @@ -80,7 +83,9 @@
  • UiObject - 控件节点
  • UiObjectCollection - 控件集合
  • UiObjectActions - 控件节点行为
  • -
  • ImageWrapper - 包装图像类
  • +
  • WebSocket
  • +
  • EventEmitter - 事件发射器
  • +
  • ImageWrapper - 包装图像类
  • App - 应用枚举类
  • Color - 颜色类
  • Version - 版本工具类
  • @@ -155,7 +160,7 @@
    -

    AutoJs6 文档 - 6.3.3

    +

    AutoJs6 文档 - 6.4.0

    索引 | @@ -687,10 +692,7 @@

  • [p] versionName
  • [p] versionCode
  • [p] versionDate
  • -
  • [p] themeColor -
  • +
  • [p] themeColor
  • [p+] version
  • returns 后的 "{}" 内包含返回值类型.
    上述示例表示 colors.rgb 方法调用后将返回 number 类型数据.

    +

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

    +

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

    +

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

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

    参阅: 泛型

    @@ -2605,9 +2639,9 @@ console.log(UiObject.bounds()); /* 访问的是类 UiObject 的静态方法 boun

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

    图像#

    OCR 特性#

    -

    AutoJs6 的 OCR 特性是基于 Google ML Kit文字识别 API 实现的.

    +

    AutoJs6 的 OCR 特性是基于 Google ML Kit文字识别 APIBaidu PaddlePaddlePaddle Lite 实现的.

    -

    注: AutoJs6 的 OCR 实现源码 参考自 TonyJiangWJ开源项目.

    +

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

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

    @@ -2753,8 +2787,8 @@ console.log(UiObject.bounds()); /* 访问的是类 UiObject 的静态方法 boun

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

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

    打包应用#

    -

    AutoJs6 使用 Auto.js 最终免费版打包插件, 因此打包应用与 AutoJs6 主应用有较大的功能和界面差异.

    -

    AutoJs6 暂时不考虑对打包应用做进一步开发, 甚至可能永久搁置.

    +

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

    +

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

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

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

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

    @@ -4281,13 +4315,13 @@ species.isRegExp(/test$/); // true

    [p] WIDTH#

    6.2.0 Global Getter

    device.width 的别名属性.

    [p] HEIGHT#

    6.2.0 Global Getter

    device.height 的别名属性.

    [p+] R#

    @@ -4951,10 +4985,9 @@ console.log(autojs.version.code); /* 同上. */ console.log(autojs.version.date); /* 同上. */

    [p] themeColor#

    -

    themeColor#

    6.3.0 Getter

    获取 AutoJs6 的主题颜色实例.

    autojs.themeColor.getColorPrimary(); /* 获取 AutoJs6 主题色的主色色值. */
    @@ -5407,6 +5440,10 @@ context.startActivity(i);
     
     
     
    +

    黑色与 0#

    +

    需特别留意, 黑色的颜色字符串为 #000000, 它是完全颜色字符串 #FF000000 的简写形式, 其 ARGB 分量表示为 argb(255, 0, 0, 0).

    +

    因此黑色的颜色整数不是 0, 而是 -16777216.

    +

    颜色整数 0 对应的是完全透明色, 即 #00000000, 其 ARGB 分量表示为 argb(0, 0, 0, 0).


    colors

    @@ -5606,8 +5643,8 @@ colors.build().setHsla(0.25, 0.8, 0.64, 0.9).toHex(); // #E6A3ED5A colors.build(120, 60, 240, 0.85).toHex(); /* 同上. */ colors.build().setRgba(120, 60, 240, 0.85).toHex(); /* 同上. */
    -

    [m] digest#

    -

    digest(color)#

    +

    [m] summary#

    +

    summary(color)#

    6.3.0

    @@ -5299,7 +5304,29 @@ pickup(w, text('abc'), 's>1', 'bounds', (o) => { } }); /* 如果没有异常, pickup 结果为原本的拾取结果. */ -

    筛选器类型#

    +
    +

    选择器行为#

    +

    通常执行控件行为时, 按以下过程进行:

    +
    构建选择器 - 筛选 (查找) - 对结果 (控件或集合) 执行行为
    +
    +

    而选择器行为的过程:

    +
    构建选择器 - 执行行为
    +
    +

    执行原理#

    +

    选择器行为隐含默认的筛选过程, 即 untilFind.

    +

    例如 text('abc').click(), 相当于 text('abc').untilFind().click().

    +

    谨慎使用#

    +

    与选择器行为相关的全局方法, 均不建议使用. 原因如下.

    +
      +
    1. 潜在的永久阻塞风险

      +

      untilFind 方法具有阻塞特性, 意味着此方法可能导致脚本 永久阻塞.
      如上述示例, text('abc') 不存在时, 脚本将持续阻塞.

      +
    2. +
    3. 全局行为缺少针对性

      +

      paste() 为例.
      当使用全局方法 paste() 时, 相当于 untilFind().paste(), untilFind() 前无筛选条件, 因此 untilFind() 将得到窗口全部控件的集合.
      这样的集合往往有几十甚至几百个控件, 再执行 paste() 时, 集合中的所有控件全部执行一次 paste().
      这样的操作往往是非预期且耗时的, 因此不建议使用 paste() 这样的全局方法, 推荐使用具体且尽量可控的筛选器筛选出特定的控件或集合, 再有针对性地执行 paste() 操作.

      +
    4. +
    +
    +

    筛选器类型#

    xxxStartsWith#

    前缀匹配筛选器.

    筛选条件为 字符串 类型, 匹配对应控件属性串值的前缀.

    @@ -5327,7 +5354,7 @@ descContains('app'); /* 不可匹配 w. */

    xxxMatches#

    正则全匹配筛选器.

    筛选条件为 字符串 类型或 正则表达式 类型, 按正则表达式规则完全匹配控件属性串值.

    -

    正则表达式类型#

    +

    正则表达式类型#

    筛选条件为正则表达式类型时, 效果等同于 JavaScript 的 RegExp.prototype.test, 但依照起止位置做完全匹配, 相当于自动添加匹配起始位置的 ^ 与匹配结束位置的 $.

    w.desc(); // splendid
     /* 相当于 descMatch(/^s.*did$/) . */
    @@ -5349,7 +5376,7 @@ descMatches(/(?:)/); /* 不可匹配 w. */
     /* 相当于 descMatch(/^spl\.?.+$/) . */
     descMatches(new RegExp('spl\\.?.+$')); /* 不可匹配 w. */
     
    -

    字符串类型#

    +

    字符串类型#

    筛选条件为字符串类型时, 相当于 JavaScript 的 RegExp.prototype.constructor 构造函数的 pattern (模式) 参数, 但依照起止位置做完全匹配, 相当于自动添加匹配起始位置的 ^ 与匹配结束位置的 $.

    如字符串 'abc' 按照正则表达式 /^abc$/ 处理,
    字符串 '\\d+' 按照正则表达式 /^\d+$/ 处理.

    w.desc(); // splendid
    @@ -5387,7 +5414,7 @@ xxxMatches(/.*word.*/); /* 或 xxxMatches('.*word.*') . */
     

    xxxMatch#

    正则匹配筛选器.

    筛选条件为 字符串 类型或 正则表达式 类型, 按正则表达式规则匹配控件属性串值.

    -

    正则表达式类型#

    +

    正则表达式类型#

    筛选条件为正则表达式类型时, 效果等同于 JavaScript 的 RegExp.prototype.test.

    w.desc(); // splendid
     descMatch(/s.*did/); /* 可匹配 w. */
    @@ -5406,7 +5433,7 @@ descMatch(new RegExp('autojs6', 'i')); /* 可匹配 w. */
     

    注: 截至 2022 年 12 月, 支持的修饰符仅包含 'i'.

    -

    字符串类型#

    +

    字符串类型#

    筛选条件为字符串类型时, 相当于 JavaScript 的 RegExp.prototype.constructor 构造函数的 pattern (模式) 参数.

    如字符串 'abc' 按照正则表达式 /abc/ 处理,
    字符串 '\\d+' 按照正则表达式 /\d+/ 处理.

    w.desc(); // splendid
    @@ -5425,26 +5452,7 @@ descMatch('.+'); /* 可匹配 w, 与 descMatch('') 效果相同.
     /* 相当于 descMatch(/spl\.?.+$/) . */
     descMatch('spl\\.?.+$'); /* 可匹配 w. */
     
    -

    选择器行为#

    -

    通常执行控件行为时, 按以下过程进行:

    -
    构建选择器 - 筛选 (查找) - 对结果 (控件或集合) 执行行为
    -
    -

    而选择器行为的过程:

    -
    构建选择器 - 执行行为
    -
    -

    原理#

    -

    选择器行为隐含默认的筛选过程, 即 untilFind.

    -

    例如 text('abc').click(), 相当于 text('abc').untilFind().click().

    -

    不推荐使用#

    -
      -
    1. 潜在的永久阻塞风险

      -

      untilFind 方法具有阻塞特性, 意味着此方法可能导致脚本 永久阻塞.
      如上述示例, text('abc') 不存在时, 脚本将持续阻塞.

      -
    2. -
    3. 全局行为缺少针对性

      -

      paste() 为例.
      当使用全局方法 paste() 时, 相当于 untilFind().paste(), untilFind() 前无筛选条件, 因此 untilFind() 将得到窗口全部控件的集合.
      这样的集合往往有几十甚至几百个控件, 再执行 paste() 时, 集合中的所有控件全部执行一次 paste().
      这样的操作往往是非预期且耗时的, 因此不建议使用 paste() 这样的全局方法, 推荐使用具体且尽量可控的筛选器筛选出特定的控件或集合, 再有针对性地执行 paste() 操作.

      -
    4. -
    -
    +
    diff --git a/app/src/main/assets/docs/util.html b/app/src/main/assets/docs/util.html index 46094d3e..89867c00 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.3 + Util | AutoJs6 文档 - 6.4.0 @@ -42,6 +42,8 @@
  • Color - 颜色
  • Image - 图像
  • OCR - 光学字符识别
  • +
  • Barcode - 条码
  • +
  • QR Code - 二维码
  • Keys - 按键
  • Device - 设备
  • Storage - 储存
  • @@ -54,6 +56,7 @@
  • Notice - 消息通知
  • Console - 控制台
  • Shell
  • +
  • Shizuku
  • Media - 多媒体
  • Sensor - 传感器
  • Recorder - 记录器
  • @@ -80,7 +83,9 @@
  • UiObject - 控件节点
  • UiObjectCollection - 控件集合
  • UiObjectActions - 控件节点行为
  • -
  • ImageWrapper - 包装图像类
  • +
  • WebSocket
  • +
  • EventEmitter - 事件发射器
  • +
  • ImageWrapper - 包装图像类
  • App - 应用枚举类
  • Color - 颜色类
  • Version - 版本工具类
  • @@ -155,7 +160,7 @@
    -

    AutoJs6 文档 - 6.3.3

    +

    AutoJs6 文档 - 6.4.0

    索引 | diff --git a/app/src/main/assets/docs/versionType.html b/app/src/main/assets/docs/versionType.html index e0b3346d..589b99ea 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.3 + 版本工具类 (Version) | AutoJs6 文档 - 6.4.0 @@ -42,6 +42,8 @@

  • Color - 颜色
  • Image - 图像
  • OCR - 光学字符识别
  • +
  • Barcode - 条码
  • +
  • QR Code - 二维码
  • Keys - 按键
  • Device - 设备
  • Storage - 储存
  • @@ -54,6 +56,7 @@
  • Notice - 消息通知
  • Console - 控制台
  • Shell
  • +
  • Shizuku
  • Media - 多媒体
  • Sensor - 传感器
  • Recorder - 记录器
  • @@ -80,7 +83,9 @@
  • UiObject - 控件节点
  • UiObjectCollection - 控件集合
  • UiObjectActions - 控件节点行为
  • -
  • ImageWrapper - 包装图像类
  • +
  • WebSocket
  • +
  • EventEmitter - 事件发射器
  • +
  • ImageWrapper - 包装图像类
  • App - 应用枚举类
  • Color - 颜色类
  • Version - 版本工具类
  • @@ -155,7 +160,7 @@
    -

    AutoJs6 文档 - 6.3.3

    +

    AutoJs6 文档 - 6.4.0

    索引 | diff --git a/app/src/main/assets/docs/web.html b/app/src/main/assets/docs/web.html index 5b8c5f54..c0428ea2 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.3 + 万维网 (Web) | AutoJs6 文档 - 6.4.0 @@ -42,6 +42,8 @@

  • Color - 颜色
  • Image - 图像
  • OCR - 光学字符识别
  • +
  • Barcode - 条码
  • +
  • QR Code - 二维码
  • Keys - 按键
  • Device - 设备
  • Storage - 储存
  • @@ -54,6 +56,7 @@
  • Notice - 消息通知
  • Console - 控制台
  • Shell
  • +
  • Shizuku
  • Media - 多媒体
  • Sensor - 传感器
  • Recorder - 记录器
  • @@ -80,7 +83,9 @@
  • UiObject - 控件节点
  • UiObjectCollection - 控件集合
  • UiObjectActions - 控件节点行为
  • -
  • ImageWrapper - 包装图像类
  • +
  • WebSocket
  • +
  • EventEmitter - 事件发射器
  • +
  • ImageWrapper - 包装图像类
  • App - 应用枚举类
  • Color - 颜色类
  • Version - 版本工具类
  • @@ -155,7 +160,7 @@
    -

    AutoJs6 文档 - 6.3.3

    +

    AutoJs6 文档 - 6.4.0

    索引 | @@ -299,69 +304,14 @@ webView.setWebViewClient(newInjectableWebClient());

    newWebSocket(url)#

    6.3.1 Global

    -

    新建并返回一个 WebSocket 实例.

    -
    console.setExitOnClose(7e3).show();
    -
    -let ws = web.newWebSocket('wss://echo.websocket.events');
    -
    -ws
    -    .on('open', (res, ws) => {
    -        console.log('WebSocket 已连接');
    -    })
    -    .on('message', (message, ws) => {
    -        console.log('接收到消息');
    -        // if (message instanceof okio.ByteString) {
    -        //     console.log(`消息类型: ByteString`);
    -        // } else if (typeof message === 'string') {
    -        //     console.log(`消息类型: String`);
    -        // } else {
    -        //     throw TypeError('Should never happen');
    -        // }
    -    })
    -    .on('text', (text, ws) => {
    -        console.info('接收到文本消息:');
    -        console.info(`text: ${text}`);
    -    })
    -    .on('bytes', (bytes, ws) => {
    -        console.info('接收到字节数组消息:');
    -        console.info(`utf8: ${bytes.utf8()}`);
    -        console.info(`base64: ${bytes.base64()}`);
    -        console.info(`md5: ${bytes.md5()}`);
    -        console.info(`hex: ${bytes.hex()}`);
    -    })
    -    .on('closing', (code, reason, ws) => {
    -        console.log('WebSocket 关闭中');
    -    })
    -    .on('closed', (code, reason, ws) => {
    -        console.log('WebSocket 已关闭');
    -        console.log(`code: ${code}`);
    -        if (reason) console.log(`reason: ${reason}`);
    -    })
    -    .on('failure', (err, res, ws) => {
    -        console.error('WebSocket 连接失败');
    -        console.error(err);
    -    });
    -
    -/* 发送文本消息. */
    -ws.send('Hello WebSocket');
    -
    -/* 发送字节数组消息. */
    -ws.send(new okio.ByteString(new java.lang.String('Hello WebSocket').getBytes()));
    -
    -setTimeout(() => {
    -    console.log('断开 WebSocket');
    -
    -    /* code 支持的范围 [1000,5000). */
    -    /* 1000 表示成功操作或常规的 Socket 关闭操作. */
    -    /* 参阅 https://github.com/Luka967/websocket-close-codes */
    -    ws.close(1000, 'Closed by user');
    -}, 8e3);
    -
    -setTimeout(() => console.log('Exit'), 9e3);
    -
    +

    构建一个 WebSocket 实例.

    +

    相当于 new WebSocket(url).

    +
    +

    参阅: WebSocket 章节

    +
    diff --git a/app/src/main/assets/docs/webSocketType.html b/app/src/main/assets/docs/webSocketType.html new file mode 100644 index 00000000..df83ec22 --- /dev/null +++ b/app/src/main/assets/docs/webSocketType.html @@ -0,0 +1,605 @@ + + + + + WebSocket | AutoJs6 文档 - 6.4.0 + + + + + + + + + + +
    +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + +
    + + + +
    + +
    +
    +

    AutoJs6 文档 - 6.4.0

    +
    +

    + 索引 | + 查看全部 +

    +
    +
    +
    + + + +
    +

    WebSocket#

    +
    +

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

    +

    Marked by SuperMonster003 on Oct 30, 2023.

    + +
    +

    WebSocket 类主要用于构建一个 OkHttp3 WebSocket 接口实现类的实例, 以便完成基于 WebSocket 协议 的网络请求.

    +
    +

    注: WebSocket 不同于 Socket.
    WebSocket 是应用层的网络传输协议. 而 Socket 并非协议, 是位于应用层和传输控制层之间的一组接口, 是对 TCP/IP 协议的封装.

    +
    +

    一个流程相对完备的 WebSocket 示例:

    +
    console.setExitOnClose(7e3).show();
    +
    +let ws = new WebSocket('wss://echo.websocket.events');
    +
    +ws
    +    .on(WebSocket.EVENT_OPEN, (res, ws) => {
    +        console.log('WebSocket 已连接');
    +    })
    +    .on(WebSocket.EVENT_MESSAGE, (message, ws) => {
    +        console.log('接收到消息');
    +        // if (message instanceof okio.ByteString) {
    +        //     console.log(`消息类型: ByteString`);
    +        // } else if (typeof message === 'string') {
    +        //     console.log(`消息类型: String`);
    +        // } else {
    +        //     throw TypeError('Should never happen');
    +        // }
    +    })
    +    .on(WebSocket.EVENT_TEXT, (text, ws) => {
    +        console.info('接收到文本消息:');
    +        console.info(`text: ${text}`);
    +    })
    +    .on(WebSocket.EVENT_BYTES, (bytes, ws) => {
    +        console.info('接收到字节数组消息:');
    +        console.info(`utf8: ${bytes.utf8()}`);
    +        console.info(`base64: ${bytes.base64()}`);
    +        console.info(`md5: ${bytes.md5()}`);
    +        console.info(`hex: ${bytes.hex()}`);
    +    })
    +    .on(WebSocket.EVENT_CLOSING, (code, reason, ws) => {
    +        console.log('WebSocket 关闭中');
    +    })
    +    .on(WebSocket.EVENT_CLOSED, (code, reason, ws) => {
    +        console.log('WebSocket 已关闭');
    +        console.log(`code: ${code}`);
    +        if (reason) console.log(`reason: ${reason}`);
    +    })
    +    .on(WebSocket.EVENT_FAILURE, (err, res, ws) => {
    +        console.error('WebSocket 连接失败');
    +        console.error(err);
    +    });
    +
    +/* 发送文本消息. */
    +ws.send('Hello WebSocket');
    +
    +/* 发送字节数组消息. */
    +ws.send(new okio.ByteString(new java.lang.String('Hello WebSocket').getBytes()));
    +
    +setTimeout(() => {
    +    console.log('断开 WebSocket');
    +    ws.close('由用户断开连接');
    +}, 8e3);
    +
    +
    +

    WebSocket

    + +
    +

    [C] WebSocket#

    +
    +

    WebSocket 类继承自 EventEmitter 类.

    +

    因此 WebSocket 实例拥有继承而来的 on, once, emit, eventNames, addListener, removeListener 等方法, 详情参阅 事件发射器 (EventEmitter) 章节.

    +
    +

    注:
    特别地, on 和 once 方法在子类进行了 覆写 (override), 其返回值类型被具体化为 WebSocket, 以便于链式调用.
    为节约篇幅, 本章节仅列举了 on 方法的相关文档, once 方法与 on 的用法相同.

    +
    +

    [c] (url)#

    +

    6.3.4 Global

    + +

    构建一个 WebSocket 实例.

    +
    +

    注: 构建实例时, 已经隐含客户端建立连接的过程.

    +
    +

    以下示例建立一个 WebSocket 连接, 并在 5 秒钟后主动断开连接.

    +
    let ws = new WebSocket('wss://echo.websocket.events');
    +setTimeout(() => {
    +    console.log('断开 WebSocket');
    +    ws.close(WebSocket.CODE_CLOSE_NORMAL, 'Closed by user');
    +}, 5e3);
    +
    +

    [m] send#

    +

    send(text)#

    +

    Overload 1/2

    +

    Attempts to enqueue text to be UTF-8 encoded and sent as a the data of a text (type 0x1) message. +This method returns true if the message was enqueued. Messages that would overflow the outgoing message buffer will be rejected and trigger a graceful shutdown of this web socket. This method returns false in that case, and in any other case where this web socket is closing, closed, or canceled. +This method returns immediately.

    +
    let ws = new WebSocket('wss://echo.websocket.events');
    +ws.send('Hello WebSocket');
    +ws.exitOnClose();
    +
    +

    send(bytes)#

    +

    Overload 2/2

    +

    Attempts to enqueue bytes to be sent as a the data of a binary (type 0x2) message. +This method returns true if the message was enqueued. Messages that would overflow the outgoing message buffer (16 MiB) will be rejected and trigger a graceful shutdown of this web socket. This method returns false in that case, and in any other case where this web socket is closing, closed, or canceled. +This method returns immediately.

    +
    let ws = new WebSocket('wss://echo.websocket.events');
    +ws.send(new okio.ByteString(new java.lang.String('Hello WebSocket').getBytes()));
    +ws.exitOnClose();
    +
    +

    [m] close#

    +

    close(code?, reason?)#

    +

    Overload [1-3]/4

    +
      +
    • [ code = WebSocket.CODE_CLOSE_NORMAL [1000] ] { number } - 状态码
    • +
    • [ reason = null ] { string } - 连接关闭原因
    • +
    • returns { boolean } - 优雅关闭是否已经启动
    • +
    +

    尝试启动 WebSocket 优雅关闭, 此时已排队的报文将在 WebSocket 断开前被传送.

    +
    +

    注: 相对应地, cancel 则会立即释放资源, 而丢弃所有排队的报文.

    +
    +

    如果调用 close 时启动了优雅关闭, 返回 true.
    如果调用 close 时, 优雅关闭已经启动, 或 WebSocket 已关闭或取消, 返回 false.

    +

    参数 code 可选, 代表状态码, 通过状态码可以获取或判断连接关闭的原因. 其范围为 [1000..5000).

    +

    参数 reason 可选, 代表关闭原因, 方便用户直接通过阅读字符串获取连接关闭的原因.

    +

    以下调用方式均被支持 (其中 ws 代表一个 WebSocket 实例):

    +
    ws.close(); /* 默认状态码, 无具体关闭原因. */
    +ws.close(WebSocket.CODE_CLOSE_NORMAL); /* 指定状态码, 无具体关闭原因. */
    +ws.close(WebSocket.CODE_CLOSE_NORMAL, '用户正常关闭'); /* 指定状态码, 指定具体关闭原因. */
    +
    +

    close(reason)#

    +

    Overload 4/4

    +
      +
    • reason { string } - 连接关闭原因
    • +
    • returns { boolean } - 优雅关闭是否已经启动
    • +
    +

    相当于 close(WebSocket.CODE_CLOSE_NORMAL, reason).

    +

    [m] exitOnClose#

    +

    exitOnClose()#

    +

    Overload 1/2

    +

    ... ...

    +

    exitOnClose(timeout)#

    +

    Overload 2/2

    +

    ... ...

    +

    [m] cancel#

    +

    cancel()#

    +
      +
    • returns { void }
    • +
    +

    立即强制释放该 WebSocket 所占用的资源, 并丢弃所有排队的报文.

    +
    +

    注: 相对应地, close 则会在释放资源之前将排队的报文完成传送.

    +
    +

    [m] queueSize#

    +

    queueSize()#

    +
    +

    Returns the size in bytes of all messages enqueued to be transmitted to the server. This doesn't include framing overhead. If compression is enabled, uncompressed messages size is used to calculate this value. It also doesn't include any bytes buffered by the operating system or network intermediaries. This method returns 0 if no messages are waiting in the queue. If may return a nonzero value after the web socket has been canceled; this indicates that enqueued messages were not transmitted.

    +

    [m] on#

    +

    on(eventName, callback)#

    +
    +

    注册一个 WebSocket 相关的事件监听器, 当事件名称与 eventName 参数一致时, 触发执行回调函数 callback.

    +

    ... ...

    +

    不同事件名称, 其对应监听回调函数的参数也不同 (给出具体对应的在 [p] 文档内).

    +

    ... ...

    +

    [m] rebuild#

    +

    rebuild(maxRebuildTimes?)#

    +

    Overload [1-2]/2

    +
      +
    • maxRebuildTimes { number } - 最大连接重建次数
    • +
    • returns { void }
    • +
    +

    ... ...

    +

    [m] request#

    +

    request()#

    +
    +

    Returns the original request that initiated this web socket.

    +

    [p] EVENT_OPEN#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 事件名称常量.

    +
      +
    • 事件触发: 远程对等端接受网络套接字, 并且可以开始信息传输.
    • +
    • 事件监听: WebSocket#on
    • +
    +
    let ws = new WebSocket('wss://echo.websocket.events');
    +ws.on(WebSocket.EVENT_OPEN, (res, ws) => console.log('WebSocket 已连接'));
    +ws.exitOnClose();
    +
    +

    [p] EVENT_MESSAGE#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 事件名称常量, 用于 xxx 事件.

    +

    Invoked when a text (type 0x1) message has been received.

    +

    [p] EVENT_TEXT#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 事件名称常量, 用于 xxx 事件.

    +

    Invoked when a text (type 0x1) message has been received.

    +

    [p] EVENT_BYTES#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 事件名称常量, 用于 xxx 事件.

    +

    Invoked when a text (type 0x1) message has been received.

    +

    [p] EVENT_CLOSING#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 事件名称常量, 用于 xxx 事件.

    +

    Invoked when the remote peer has indicated that no more incoming messages will be transmitted.

    +

    [p] EVENT_CLOSED#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 事件名称常量, 用于 xxx 事件.

    +

    Invoked when both peers have indicated that no more messages will be transmitted and the connection has been successfully released. No further calls to this listener will be made.

    +

    [p] EVENT_FAILURE#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 事件名称常量, 用于 xxx 事件.

    +

    Invoked when a web socket has been closed due to an error reading from or writing to the network. Both outgoing and incoming messages may have been lost. No further calls to this listener will be made.

    +

    [p] EVENT_MAX_REBUILDS#

    +

    6.3.4 CONSTANT

    +
      +
    • [ max_rebuilds ] { string }
    • +
    +

    WebSocket 事件名称常量, 用于 xxx 事件.

    +

    [p] CODE_CLOSE_NORMAL#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示成功操作或常规的 Socket 关闭操作.

    +

    [p] CODE_CLOSE_GOING_AWAY#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示终端正在处于移除状态, 服务端或客户端即将不可用.

    +

    [p] CODE_CLOSE_PROTOCOL_ERROR#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示终端因协议错误或无效帧而即将终止连接.

    +

    [p] CODE_CLOSE_UNSUPPORTED#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示终端因帧数据类型不支持而即将终止连接.

    +

    [p] CODE_CLOSED_NO_STATUS#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示不包含错误原因, 仅代表已经关闭的状态.

    +

    [p] CODE_CLOSE_ABNORMAL#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示异常关闭 (如浏览器关闭).

    +

    [p] CODE_UNSUPPORTED_PAYLOAD#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示终端接收到不一致的报文 (如异常格式的 UTF-8).

    +

    [p] CODE_POLICY_VIOLATION#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示终端因收到了违反其策略的报文而即将终止连接.

    +

    [p] CODE_CLOSE_TOO_LARGE#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示终端因无法处理长度过大的报文而即将终止连接.

    +

    [p] CODE_MANDATORY_EXTENSION#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示终端因期望与服务端进行扩展协商而即将终止连接.

    +

    [p] CODE_SERVER_ERROR#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示服务端因发生内部错误而即将终止连接.

    +

    [p] CODE_SERVICE_RESTART#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示服务端正在重启过程中.

    +

    [p] CODE_TRY_AGAIN_LATER#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示服务端临时拒绝了终端请求.

    +

    [p] CODE_BAD_GATEWAY#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示网关服务器接收到无效的请求.

    +

    [p] CODE_TLS_HANDSHAKE_FAIL#

    +

    6.3.4 CONSTANT

    + +

    WebSocket 状态码, 表示 TLS 握手失败 (如服务端证书未通过验证等).

    + +
    +
    +
    + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/modules/__device__.js b/app/src/main/assets/modules/__device__.js index c654f6af..406d9e8a 100644 --- a/app/src/main/assets/modules/__device__.js +++ b/app/src/main/assets/modules/__device__.js @@ -1,3 +1,5 @@ +// noinspection JSUnusedLocalSymbols + /* Overwritten protection. */ let { util } = global; diff --git a/app/src/main/assets/modules/__shizuku__.js b/app/src/main/assets/modules/__shizuku__.js index 498845b2..197f1c5c 100644 --- a/app/src/main/assets/modules/__shizuku__.js +++ b/app/src/main/assets/modules/__shizuku__.js @@ -19,17 +19,11 @@ module.exports = function (scriptRuntime, scope) { const shizuku = function (cmd) { return rtShizuku.execCommand(cmd); }; - return Object.assign(shizuku, ShizukuCtor.prototype); + return Object.assign(Object.setPrototypeOf(shizuku, rtShizuku), ShizukuCtor.prototype); }; ShizukuCtor.prototype = { constructor: ShizukuCtor, - ensureService() { - rtShizuku.ensureService(); - }, - execCommand() { - return rtShizuku.execCommand.apply(rtShizuku, arguments); - }, }; return ShizukuCtor; diff --git a/app/src/main/java/org/autojs/autojs/core/console/ConsoleView.java b/app/src/main/java/org/autojs/autojs/core/console/ConsoleView.java index ee041cc7..ae133214 100644 --- a/app/src/main/java/org/autojs/autojs/core/console/ConsoleView.java +++ b/app/src/main/java/org/autojs/autojs/core/console/ConsoleView.java @@ -2,6 +2,8 @@ package org.autojs.autojs.core.console; import android.annotation.SuppressLint; import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; @@ -57,29 +59,53 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener public ConsoleView(Context context) { super(context); - init(); + init(null); } public ConsoleView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); - init(); + init(attrs); } public ConsoleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - init(); + init(attrs); } @SuppressLint("ClickableViewAccessibility") - private void init() { + private void init(@Nullable AttributeSet attrs) { inflate(getContext(), R.layout.console_view, this); - for (Map.Entry map : getLogLevelMap().entrySet()) { + Map logLevelColorResIdMap = getLogLevelMap(); + Map logLevelColorIntMap = getLogLevelMap(); + + if (attrs != null) { + TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ConsoleView); + + logLevelColorIntMap.put(Log.VERBOSE, ta.getColor(R.styleable.ConsoleView_color_verbose, Color.TRANSPARENT)); + logLevelColorIntMap.put(Log.DEBUG, ta.getColor(R.styleable.ConsoleView_color_debug, Color.TRANSPARENT)); + logLevelColorIntMap.put(Log.INFO, ta.getColor(R.styleable.ConsoleView_color_info, Color.TRANSPARENT)); + logLevelColorIntMap.put(Log.WARN, ta.getColor(R.styleable.ConsoleView_color_warn, Color.TRANSPARENT)); + logLevelColorIntMap.put(Log.ERROR, ta.getColor(R.styleable.ConsoleView_color_error, Color.TRANSPARENT)); + logLevelColorIntMap.put(Log.ASSERT, ta.getColor(R.styleable.ConsoleView_color_assert, Color.TRANSPARENT)); + + ta.recycle(); + } + + for (Map.Entry map : logLevelColorResIdMap.entrySet()) { int logLevel = map.getKey(); int colorResKey = map.getValue(); mColors.put(logLevel, getContext().getColor(colorResKey)); } + for (Map.Entry map : logLevelColorIntMap.entrySet()) { + int logLevel = map.getKey(); + int colorInt = map.getValue(); + if (colorInt != Color.TRANSPARENT) { + mColors.put(logLevel, colorInt); + } + } + ScaleGestureDetector mScaleGestureDetector = new ScaleGestureDetector(getContext(), getSimpleOnScaleGestureListener()); mLogListRecyclerView = findViewById(R.id.log_list); @@ -166,24 +192,28 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener adapter.setDebugTextColor(color); } } + public void setInfoTextColor(int color) { Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter(); if (adapter != null) { adapter.setInfoTextColor(color); } } + public void setWarnTextColor(int color) { Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter(); if (adapter != null) { adapter.setWarnTextColor(color); } } + public void setErrorTextColor(int color) { Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter(); if (adapter != null) { adapter.setErrorTextColor(color); } } + public void setAssertTextColor(int color) { Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter(); if (adapter != null) { @@ -319,7 +349,7 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener public void showEditText() { post(() -> { mWindow.requestWindowFocus(); - //mInputContainer.setVisibility(VISIBLE); + // mInputContainer.setVisibility(VISIBLE); mEditText.requestFocus(); }); } @@ -359,7 +389,7 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener if (textSize > 0) { textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize); } else { - textSize = DisplayUtils.pxToSp(getContext(), textView.getTextSize()); + textSize = DisplayUtils.pxToSp(textView.getTextSize()); } textView.setClickable(false); textView.setLongClickable(false); diff --git a/app/src/main/java/org/autojs/autojs/core/console/FloatingConsoleView.kt b/app/src/main/java/org/autojs/autojs/core/console/FloatingConsoleView.kt index 64ab2abb..31fe20d0 100644 --- a/app/src/main/java/org/autojs/autojs/core/console/FloatingConsoleView.kt +++ b/app/src/main/java/org/autojs/autojs/core/console/FloatingConsoleView.kt @@ -19,12 +19,12 @@ class FloatingConsoleView : ConsoleView { } override fun getLogLevelMap(): MutableMap = MapBuilder() - .put(Log.VERBOSE,R.color.floating_console_view_verbose) - .put(Log.DEBUG,R.color.floating_console_view_debug) - .put(Log.INFO,R.color.floating_console_view_info) - .put(Log.WARN,R.color.floating_console_view_warn) - .put(Log.ERROR,R.color.floating_console_view_error) - .put(Log.ASSERT,R.color.floating_console_view_assert) + .put(Log.VERBOSE, R.color.floating_console_view_verbose) + .put(Log.DEBUG, R.color.floating_console_view_debug) + .put(Log.INFO, R.color.floating_console_view_info) + .put(Log.WARN, R.color.floating_console_view_warn) + .put(Log.ERROR, R.color.floating_console_view_error) + .put(Log.ASSERT, R.color.floating_console_view_assert) .build() } \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/core/console/GlobalConsole.java b/app/src/main/java/org/autojs/autojs/core/console/GlobalConsole.java index 807ea19b..080acb66 100644 --- a/app/src/main/java/org/autojs/autojs/core/console/GlobalConsole.java +++ b/app/src/main/java/org/autojs/autojs/core/console/GlobalConsole.java @@ -8,8 +8,6 @@ import static android.util.Log.VERBOSE; import static android.util.Log.WARN; import static android.util.Log.d; -import android.annotation.SuppressLint; - import androidx.annotation.NonNull; import org.apache.log4j.Level; @@ -24,11 +22,10 @@ import java.util.Locale; /** * Created by Stardust on 2017/10/22. */ -@SuppressLint("ConstantLocale") public class GlobalConsole extends ConsoleImpl { private static final String TAG = GlobalConsole.class.getSimpleName(); - private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()); private static final Logger LOGGER = Logger.getLogger(GlobalConsole.class); + private final SimpleDateFormat mDateFormat = new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()); public GlobalConsole(UiHandler uiHandler) { super(uiHandler); @@ -38,7 +35,7 @@ public class GlobalConsole extends ConsoleImpl { @NonNull public String println(int level, @NonNull CharSequence charSequence) { String log = String.format(Locale.getDefault(), "%s/%s: %s", - DATE_FORMAT.format(new Date()), getLevelChar(level), charSequence); + mDateFormat.format(new Date()), getLevelChar(level), charSequence); LOGGER.log(toLog4jLevel(level), log); d(TAG, log); super.println(level, log); diff --git a/app/src/main/java/org/autojs/autojs/core/eventloop/EventEmitter.java b/app/src/main/java/org/autojs/autojs/core/eventloop/EventEmitter.java index f1c2668e..0afacbcf 100644 --- a/app/src/main/java/org/autojs/autojs/core/eventloop/EventEmitter.java +++ b/app/src/main/java/org/autojs/autojs/core/eventloop/EventEmitter.java @@ -31,7 +31,6 @@ public class EventEmitter { } } - private class Listeners { private final CopyOnWriteArrayList mListenerWrappers = new CopyOnWriteArrayList<>(); @@ -134,8 +133,9 @@ public class EventEmitter { public boolean emit(String eventName, Object... args) { Listeners listeners = mListenersMap.get(eventName); - if (listeners == null || listeners.empty()) + if (listeners == null || listeners.empty()) { return false; + } listeners.emit(args); return true; } @@ -146,8 +146,9 @@ public class EventEmitter { public int listenerCount(String eventName) { Listeners listeners = mListenersMap.get(eventName); - if (listeners == null) + if (listeners == null) { return 0; + } return listeners.count(); } @@ -177,8 +178,9 @@ public class EventEmitter { public EventEmitter removeListener(String eventName, Object listener) { Listeners listeners = mListenersMap.get(eventName); - if (listeners != null) + if (listeners != null) { listeners.remove(listener); + } return this; } @@ -195,5 +197,4 @@ public class EventEmitter { return defaultMaxListeners; } - } 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 a304f9b3..c2acadf8 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 @@ -20,12 +20,12 @@ import android.view.OrientationEventListener; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.autojs.autojs.lang.ThreadCompat; import org.autojs.autojs.runtime.ScriptRuntime; +import org.autojs.autojs.runtime.api.ScreenMetrics; import org.autojs.autojs.runtime.exception.ScriptException; import org.autojs.autojs.runtime.exception.ScriptInterruptedException; import org.autojs.autojs.util.ForegroundServiceUtils; -import org.autojs.autojs.lang.ThreadCompat; -import org.autojs.autojs.runtime.api.ScreenMetrics; import java.util.ArrayList; import java.util.Collections; diff --git a/app/src/main/java/org/autojs/autojs/core/notification/NotificationListenerService.kt b/app/src/main/java/org/autojs/autojs/core/notification/NotificationListenerService.kt index b861c792..7a1f8444 100644 --- a/app/src/main/java/org/autojs/autojs/core/notification/NotificationListenerService.kt +++ b/app/src/main/java/org/autojs/autojs/core/notification/NotificationListenerService.kt @@ -45,6 +45,7 @@ class NotificationListenerService : android.service.notification.NotificationLis } companion object { + @JvmStatic var instance: NotificationListenerService? = null private set } diff --git a/app/src/main/java/org/autojs/autojs/core/ui/widget/JsConsoleView.kt b/app/src/main/java/org/autojs/autojs/core/ui/widget/JsConsoleView.kt index 35e74d9c..e1764b63 100644 --- a/app/src/main/java/org/autojs/autojs/core/ui/widget/JsConsoleView.kt +++ b/app/src/main/java/org/autojs/autojs/core/ui/widget/JsConsoleView.kt @@ -4,6 +4,7 @@ import android.content.Context import android.util.AttributeSet import android.util.Log import org.apache.log4j.Logger +import org.autojs.autojs.AutoJs import org.autojs.autojs.core.console.ConsoleImpl import org.autojs.autojs.core.console.ConsoleView import org.autojs.autojs.core.console.GlobalConsole @@ -22,22 +23,24 @@ class JsConsoleView : ConsoleView { constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - // init { - // setConsole(object : GlobalConsole(UiHandler(context)) { - // override fun println(level: Int, charSequence: CharSequence): String { - // val log = String.format(Locale.getDefault(), "%s", charSequence) - // LOGGER.log(toLog4jLevel(level), log) - // Log.d(TAG, log) - // super.println(level, log) - // return log - // } - // }) - // } - // - // override fun onNewLog(logEntry: ConsoleImpl.LogEntry?) { - // if (logEntry != null) { - // ViewUtils.showToast(context, logEntry.content.toString()) - // } - // } + init { + // setConsole(object : GlobalConsole(UiHandler(context)) { + // override fun println(level: Int, charSequence: CharSequence): String { + // val log = String.format(Locale.getDefault(), "%s", charSequence) + // LOGGER.log(toLog4jLevel(level), log) + // Log.d(TAG, log) + // super.println(level, log) + // return log + // } + // }) + setConsole(AutoJs.instance.globalConsole) + } + + override fun onNewLog(logEntry: ConsoleImpl.LogEntry?) { + // if (logEntry != null) { + // ViewUtils.showToast(context, logEntry.content.toString()) + // } + super.onNewLog(logEntry) + } } \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java b/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java index 706579f1..603cebd2 100644 --- a/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java +++ b/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java @@ -48,7 +48,7 @@ public class TaskerScriptEditActivity extends BaseActivity { ViewUtils.showToast(TaskerScriptEditActivity.this, ex.getMessage(), true); finish(); }); - setToolbarAsBack(mEditorView.getName()); + setToolbarAsBack(mEditorView.name); } public static void edit(Activity activity, String title, String summary, String content) { @@ -61,9 +61,7 @@ public class TaskerScriptEditActivity extends BaseActivity { @Override public void finish() { CodeEditor editor = mEditorView.editor; - if (editor != null) { - setResult(RESULT_OK, new Intent().putExtra(EXTRA_CONTENT, editor.getText())); - } + setResult(RESULT_OK, new Intent().putExtra(EXTRA_CONTENT, editor.getText())); TaskerScriptEditActivity.super.finish(); } diff --git a/app/src/main/java/org/autojs/autojs/inrt/SettingsActivity.kt b/app/src/main/java/org/autojs/autojs/inrt/SettingsActivity.kt index 44899e85..f633e592 100644 --- a/app/src/main/java/org/autojs/autojs/inrt/SettingsActivity.kt +++ b/app/src/main/java/org/autojs/autojs/inrt/SettingsActivity.kt @@ -1,23 +1,24 @@ package org.autojs.autojs.inrt import android.os.Bundle -import androidx.annotation.Nullable import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import org.autojs.autojs6.R /** * Created by Stardust on 2017/12/8. + * Modified by SuperMonster003 as of Oct 27, 2023. */ class SettingsActivity : AppCompatActivity() { - override fun onCreate(@Nullable savedInstanceState: Bundle?) { + + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setupViews() } private fun setupViews() { setContentView(R.layout.activity_settings_inrt) - fragmentManager.beginTransaction().replace(R.id.fragment_setting, PreferenceFragment()).commit() + supportFragmentManager.beginTransaction().replace(R.id.fragment_setting, PreferenceFragment()).commit() val toolbar = findViewById(R.id.toolbar) toolbar.setTitle(R.string.text_settings) setSupportActionBar(toolbar) @@ -25,14 +26,10 @@ class SettingsActivity : AppCompatActivity() { supportActionBar?.setDisplayHomeAsUpEnabled(true) } - class PreferenceFragment : androidx.preference.PreferenceFragment() { - - override fun onCreate(@Nullable savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - } - + class PreferenceFragment : androidx.preference.PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.preference_inrt) } } + } diff --git a/app/src/main/java/org/autojs/autojs/inrt/SplashActivity.kt b/app/src/main/java/org/autojs/autojs/inrt/SplashActivity.kt index 4347e73e..c147a832 100644 --- a/app/src/main/java/org/autojs/autojs/inrt/SplashActivity.kt +++ b/app/src/main/java/org/autojs/autojs/inrt/SplashActivity.kt @@ -34,11 +34,9 @@ class SplashActivity : AppCompatActivity() { } private fun main() { - checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.READ_PHONE_STATE) + checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) } - private fun runScript() { Thread { try { diff --git a/app/src/main/java/org/autojs/autojs/permission/ShizukuPermission.kt b/app/src/main/java/org/autojs/autojs/permission/ShizukuPermission.kt new file mode 100644 index 00000000..fd068b69 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/permission/ShizukuPermission.kt @@ -0,0 +1,42 @@ +package org.autojs.autojs.permission + +import android.content.Context +import org.autojs.autojs.runtime.api.WrappedShizuku +import org.autojs.autojs.ui.main.drawer.PermissionItemHelper +import org.autojs.autojs.util.ViewUtils +import org.autojs.autojs6.R +import rikka.shizuku.Shizuku + +class ShizukuPermission(override val context: Context) : PermissionItemHelper { + + override fun has() = WrappedShizuku.hasPermission() + + override fun request() { + when { + !WrappedShizuku.isInstalled() -> ViewUtils.showToast(context, R.string.error_shizuku_is_not_installed) + Shizuku.isPreV11() -> ViewUtils.showToast(context, R.string.error_shizuku_version_is_not_supported) + else -> { + try { + if (!Shizuku.shouldShowRequestPermissionRationale()) { + WrappedShizuku.requestPermission() + return + } + } catch (e: Throwable) { + e.printStackTrace() + } + WrappedShizuku.config(true) ?: ViewUtils.showToast(context, R.string.error_failed_to_grant_shizuku_access) + } + } + } + + override fun revoke() { + when { + !WrappedShizuku.isInstalled() -> ViewUtils.showToast(context, R.string.error_shizuku_is_not_installed) + Shizuku.isPreV11() -> ViewUtils.showToast(context, R.string.error_shizuku_version_is_not_supported) + else -> WrappedShizuku.config(false) ?: ViewUtils.showToast(context, R.string.error_failed_to_revoke_shizuku_access) + } + } + + + +} \ No newline at end of file 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 8d01dfaf..d1dff4a6 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 @@ -104,8 +104,9 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver } public void observeKey() { - if (mListeningKey) + if (mListeningKey) { return; + } AccessibilityService service = getAccessibilityService(); // @Dubious by SuperMonster003 on Feb 11, 2022. @@ -129,8 +130,9 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver } public void observeTouch() { - if (mTouchObserver != null) + if (mTouchObserver != null) { return; + } ensureHandler(); mLoopers.waitWhenIdle(true); mTouchObserver = new TouchObserver(InputEventObserver.initObserver(mContext)); @@ -155,11 +157,13 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver } private void ensureKeyInterceptor() { - if (mKeyInterceptor != null) + if (mKeyInterceptor != null) { return; + } mKeyInterceptor = event -> { - if (mInterceptsAllKey) + if (mInterceptsAllKey) { return true; + } String keyName = KeyEvent.keyCodeToString(event.getKeyCode()).substring(8).toLowerCase(Language.getPrefLanguage().getLocale()); return mInterceptedKeys.contains(keyName); }; @@ -169,12 +173,12 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver private AccessibilityService getAccessibilityService() { mScriptRuntime.ensureAccessibilityServiceEnabled(); AccessibilityService service = mAccessibilityBridge.getService(); - if (service == null) + if (service == null) { throw new ScriptException(mContext.getString(R.string.error_no_accessibility_service)); + } return service; } - public Events onKeyDown(String keyName, Object listener) { on(PREFIX_KEY_DOWN + keyName, listener); return this; @@ -224,21 +228,35 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver } public void observeNotification() { - if (mListeningNotification) + if (mListeningNotification) { return; + } mListeningNotification = true; ensureHandler(); mLoopers.waitWhenIdle(true); - if (NotificationListenerService.Companion.getInstance() == null) { - mContext.startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)); - throw new ScriptException(mContext.getString(R.string.text_notification_service_disabled)); + if (NotificationListenerService.getInstance() != null) { + NotificationListenerService.getInstance().addListener(this); + return; } - NotificationListenerService.Companion.getInstance().addListener(this); + Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + throw new ScriptException(mContext.getString(R.string.text_notification_service_disabled)); + } + + public void removeNotificationObserver() { + mAccessibilityBridge.getNotificationObserver().removeNotificationListener(this); + if (NotificationListenerService.getInstance() != null) { + NotificationListenerService.getInstance().removeListener(this); + } + mListeningNotification = false; + mLoopers.waitWhenIdle(mListeningToast); } public void observeToast() { - if (mListeningToast) + if (mListeningToast) { return; + } mAccessibilityBridge.ensureServiceEnabled(); mListeningToast = true; ensureHandler(); @@ -246,6 +264,14 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver mAccessibilityBridge.getNotificationObserver().addToastListener(this); } + public void removeToastObserver() { + mAccessibilityBridge.getNotificationObserver().removeToastListener(this); + if (NotificationListenerService.getInstance() != null) { + NotificationListenerService.getInstance().removeListener(this); + } + mListeningToast = false; + mLoopers.waitWhenIdle(mListeningNotification); + } public Events onNotification(Object listener) { on("notification", listener); @@ -271,11 +297,10 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver mTouchObserver.stop(); } if (mListeningNotification) { - mAccessibilityBridge.getNotificationObserver().removeNotificationListener(this); - mAccessibilityBridge.getNotificationObserver().removeToastListener(this); - if (NotificationListenerService.Companion.getInstance() != null) { - NotificationListenerService.Companion.getInstance().removeListener(this); - } + removeNotificationObserver(); + } + if (mListeningToast) { + removeToastObserver(); } if (mKeyInterceptor != null) { AccessibilityService service = mAccessibilityBridge.getService(); @@ -284,6 +309,7 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver } mKeyInterceptor = null; } + mLoopers.waitWhenIdle(false); } @Override @@ -313,7 +339,6 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver public void onNotification(@NonNull final Notification notification) { mHandler.post(() -> emit("notification", notification)); - } @Override diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/ScreenMetrics.kt b/app/src/main/java/org/autojs/autojs/runtime/api/ScreenMetrics.kt index 158ac34f..b4f1278c 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/ScreenMetrics.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/ScreenMetrics.kt @@ -24,34 +24,34 @@ class ScreenMetrics(private var designWidth: Int, private var designHeight: Int) @JvmOverloads fun scaleX(x: Int, width: Int = designWidth) = when { - width == 0 || !isInitialized -> x + width == 0 || !mIsInitialized -> x else -> x * deviceScreenWidth / width } @JvmOverloads fun scaleY(y: Int, height: Int = designHeight) = when { - height == 0 || !isInitialized -> y + height == 0 || !mIsInitialized -> y else -> y * deviceScreenHeight / height } @JvmOverloads fun rescaleX(x: Int, width: Int = designWidth) = when { - width == 0 || !isInitialized -> x + width == 0 || !mIsInitialized -> x else -> x * width / deviceScreenWidth } @JvmOverloads fun rescaleY(y: Int, height: Int = designHeight) = when { - height == 0 || !isInitialized -> y + height == 0 || !mIsInitialized -> y else -> y * height / deviceScreenHeight } companion object { - private var mWindowManager: WindowManager? = null - private var mResources: Resources? = null + var resources: Resources? = null - private var isInitialized = false + private var mWindowManager: WindowManager? = null + private var mIsInitialized = false @Suppress("DEPRECATION") @JvmStatic @@ -60,7 +60,7 @@ class ScreenMetrics(private var designWidth: Int, private var designHeight: Int) @JvmStatic val orientation: Int - get() = mResources?.configuration?.orientation ?: ORIENTATION_PORTRAIT + get() = resources?.configuration?.orientation ?: ORIENTATION_PORTRAIT @JvmStatic val isScreenPortrait: Boolean @@ -74,7 +74,7 @@ class ScreenMetrics(private var designWidth: Int, private var designHeight: Int) @Suppress("DEPRECATION") val deviceScreenWidth: Int get() { - mResources?.displayMetrics?.widthPixels?.takeIf { it > 0 }?.let { return it } + resources?.displayMetrics?.widthPixels?.takeIf { it > 0 }?.let { return it } val metricsLegacy = DisplayMetrics() val display = mWindowManager?.defaultDisplay?.apply { getRealMetrics(metricsLegacy) } @@ -89,7 +89,7 @@ class ScreenMetrics(private var designWidth: Int, private var designHeight: Int) @Suppress("DEPRECATION") val deviceScreenHeight: Int get() { - mResources?.displayMetrics?.heightPixels?.takeIf { it > 0 }?.let { return it } + resources?.displayMetrics?.heightPixels?.takeIf { it > 0 }?.let { return it } val metricsLegacy = DisplayMetrics() val display = mWindowManager?.defaultDisplay?.apply { getRealMetrics(metricsLegacy) } @@ -111,9 +111,9 @@ class ScreenMetrics(private var designWidth: Int, private var designHeight: Int) @JvmStatic fun init(activity: Activity) { + resources = activity.resources mWindowManager = activity.windowManager - mResources = activity.resources - isInitialized = true + mIsInitialized = true } private fun toOriAwarePoint(a: Int, b: Int) = arrayOf(minOf(a, b), maxOf(a, b)) diff --git a/app/src/main/java/org/autojs/autojs/runtime/api/WrappedShizuku.kt b/app/src/main/java/org/autojs/autojs/runtime/api/WrappedShizuku.kt index 94ed803c..45d578b7 100644 --- a/app/src/main/java/org/autojs/autojs/runtime/api/WrappedShizuku.kt +++ b/app/src/main/java/org/autojs/autojs/runtime/api/WrappedShizuku.kt @@ -1,15 +1,19 @@ package org.autojs.autojs.runtime.api import android.content.ComponentName +import android.content.Intent import android.content.ServiceConnection import android.content.pm.PackageManager import android.os.IBinder import android.util.Log import org.autojs.autojs.App import org.autojs.autojs.annotation.ScriptInterface +import org.autojs.autojs.app.GlobalAppContext import org.autojs.autojs.core.shizuku.IUserService import org.autojs.autojs.core.shizuku.UserService +import org.autojs.autojs.util.ViewUtils import org.autojs.autojs6.BuildConfig +import org.autojs.autojs6.R import rikka.shizuku.Shizuku import org.autojs.autojs.runtime.api.AbstractShell.Result as ShellResult @@ -23,6 +27,9 @@ object WrappedShizuku { private val TAG: String = WrappedShizuku::class.java.simpleName + private val context + get() = GlobalAppContext.get() + private val mRequestCode = "shizuku-request-code".hashCode() private val mUserServiceConnection: ServiceConnection = object : ServiceConnection { @@ -36,7 +43,7 @@ object WrappedShizuku { } override fun onServiceDisconnected(componentName: ComponentName) { - // ... ... + Log.d(TAG, "onServiceDisconnected: ${componentName.className}") } } @@ -46,16 +53,16 @@ object WrappedShizuku { .debuggable(BuildConfig.DEBUG) .version(BuildConfig.VERSION_CODE) - private val mBinderReceivedListener = { - // if (Shizuku.isPreV11()) { - // binding.text1.setText("Shizuku pre-v11 is not supported") - // } else { - // binding.text1.setText("Binder received") - // } + private val mBinderReceivedListener = Shizuku.OnBinderReceivedListener { + if (Shizuku.isPreV11()) { + Log.d(TAG, "Shizuku pre-v11 is not supported") + } else { + Log.d(TAG, "Binder received") + } } - private val mBinderDeadListener = { - // binding.text1.setText("Binder dead") + private val mBinderDeadListener = Shizuku.OnBinderDeadListener { + Log.d(TAG, "Binder dead") } private val mRequestPermissionResultListener: (requestCode: Int, grantResult: Int) -> Unit = { requestCode: Int, grantResult: Int -> @@ -71,53 +78,65 @@ object WrappedShizuku { Shizuku.addRequestPermissionResultListener(mRequestPermissionResultListener) } + internal fun bindUserServiceIfNeeded() { + if (hasPermission()) { + bindUserService() + } + } + private fun bindUserService() { - val res = StringBuilder() try { - if (Shizuku.getVersion() < 10) { - res.append("requires Shizuku API 10") - } else { + if (!Shizuku.isPreV11()) { Shizuku.bindUserService(mUserServiceArgs, mUserServiceConnection) } - } catch (tr: Throwable) { - tr.printStackTrace() - res.append(tr) + } catch (e: Throwable) { + e.printStackTrace() } - // binding.text3.setText(res.toString().trim { it <= ' ' }) } private fun unbindUserService() { - val res = StringBuilder() try { - if (Shizuku.getVersion() < 10) { - res.append("requires Shizuku API 10") - } else { + if (!Shizuku.isPreV11()) { Shizuku.unbindUserService(mUserServiceArgs, mUserServiceConnection, true) } - } catch (tr: Throwable) { - tr.printStackTrace() - res.append(tr) + } catch (_: Throwable) { + // Ignored. } - // binding.text3.setText(res.toString().trim { it <= ' ' }) } - internal fun checkShizukuPermission() = try { - when { - Shizuku.isPreV11() -> false - Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED -> true.also { bindUserService() } - Shizuku.shouldShowRequestPermissionRationale() -> false - else -> false.also { Shizuku.requestPermission(mRequestCode) } - } + @ScriptInterface + fun isInstalled() = getLaunchIntent() != null + + @ScriptInterface + fun hasPermission() = try { + Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED } catch (e: Throwable) { false.also { e.printStackTrace() } } + @ScriptInterface + fun requestPermission() = Shizuku.requestPermission(mRequestCode) + + @ScriptInterface + @JvmOverloads + fun config(isRequest: Boolean? = null): Intent? { + return getLaunchIntent()?.also { + context.startActivity(it) + val message = when (isRequest) { + true -> "${context.getString(R.string.text_grant_autojs6_access_in_shizuku_app)} (${context.getString(R.string.text_shizuku_service_may_need_to_be_run_first)})" + false -> context.getString(R.string.text_revoke_autojs6_access_in_shizuku_app) + else -> null + } + ViewUtils.showToast(context, message, true) + } + } + @ScriptInterface fun execCommand(cmd: String): ShellResult { ensureService() return try { ShellResult.fromJson(service!!.execCommand(cmd)) - } catch (e: Exception) { + } catch (e: Throwable) { ShellResult().apply { code = 1 error = e.message ?: "" @@ -133,7 +152,7 @@ object WrappedShizuku { @ScriptInterface fun ensureService() { - service ?: throw IllegalStateException("Shizuku service is not available") + service ?: throw IllegalStateException(context.getString(R.string.error_unable_to_use_shizuku_service)) } internal fun onDestroy() { @@ -143,4 +162,10 @@ object WrappedShizuku { Shizuku.removeRequestPermissionResultListener(mRequestPermissionResultListener) } + private fun getLaunchIntent(): Intent? { + return context.packageManager.getLaunchIntentForPackage(org.autojs.autojs.util.App.SHIZUKU.packageName)?.apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/service/NotificationService.kt b/app/src/main/java/org/autojs/autojs/service/NotificationService.kt index e06f9c44..77175999 100644 --- a/app/src/main/java/org/autojs/autojs/service/NotificationService.kt +++ b/app/src/main/java/org/autojs/autojs/service/NotificationService.kt @@ -3,13 +3,13 @@ package org.autojs.autojs.service import android.content.Context import android.content.Intent import android.provider.Settings -import org.autojs.autojs.core.notification.NotificationListenerService.Companion.instance +import org.autojs.autojs.core.notification.NotificationListenerService import org.autojs.autojs.ui.main.drawer.ServiceItemHelper class NotificationService(override val context: Context) : ServiceItemHelper { override val isRunning: Boolean - get() = instance != null + get() = NotificationListenerService.instance != null override fun start() { config() diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.kt b/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.kt index 65c1ae93..3d5ef1eb 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.kt +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.kt @@ -193,7 +193,7 @@ open class EditActivity : BaseActivity(), DelegateHost, PermissionRequestProxyAc if (!mEditorView.isTextChanged) { return } - val text = mEditorView.editor!!.text + val text = mEditorView.editor.text if (text.length < 256 * 1024) { outState.putString("text", text) } else { @@ -228,7 +228,7 @@ open class EditActivity : BaseActivity(), DelegateHost, PermissionRequestProxyAc .observeOn(Schedulers.io()) .map { PFiles.read(it) } .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ mEditorView.editor!!.text = it }, Throwable::printStackTrace) + .subscribe({ mEditorView.editor.text = it }, Throwable::printStackTrace) } } 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 946b6abe..7380b385 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 @@ -197,7 +197,7 @@ public class EditorMenu { } private void startBuildApkActivity() { - Uri uri = mEditorView.getUri(); + Uri uri = mEditorView.uri; if (uri != null) { BuildActivity.launch(mContext, uri.getPath()); } @@ -217,12 +217,10 @@ 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) -> { - if (mEditorView.editor != null) { - String newKey = itemKeys.get(which); - if (!Objects.equals(newKey, itemKey)) { - Pref.putString(key, newKey); - mEditorView.editor.notifyPinchToZoomStrategyChanged(newKey); - } + String newKey = itemKeys.get(which); + if (!Objects.equals(newKey, itemKey)) { + Pref.putString(key, newKey); + mEditorView.editor.notifyPinchToZoomStrategyChanged(newKey); } return true; }) 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 6b990e14..c6727564 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 @@ -16,8 +16,8 @@ import android.util.AttributeSet import android.util.Log import android.util.SparseBooleanArray import android.view.View -import android.widget.FrameLayout import android.widget.ImageView +import android.widget.LinearLayout import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.FragmentActivity @@ -26,9 +26,6 @@ import com.google.android.material.snackbar.Snackbar import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers -import org.androidannotations.annotations.AfterViews -import org.androidannotations.annotations.EViewGroup -import org.androidannotations.annotations.ViewById import org.autojs.autojs.AutoJs import org.autojs.autojs.engine.JavaScriptEngine import org.autojs.autojs.engine.ScriptEngine @@ -77,6 +74,7 @@ import org.autojs.autojs.util.Observers import org.autojs.autojs.util.ViewUtils.showSnack import org.autojs.autojs.util.ViewUtils.showToast import org.autojs.autojs6.R +import org.autojs.autojs6.databinding.EditorViewBinding import java.io.File /** @@ -84,62 +82,49 @@ import java.io.File * Modified by SuperMonster003 as of May 1, 2023. * Transformed by SuperMonster003 on May 1, 2023. */ -@SuppressLint("NonConstantResourceId") -@EViewGroup(R.layout.editor_view) -// FIXME by SuperMonster003 on May 13, 2023. -// ! Failed many times to migrate this view with annotation to view binding. -// ! And this is the last view with annotation in the whole project so far. -// FIXME by SuperMonster003 on Oct 18, 2023. -// ! Gradle property "android.nonFinalResIds" -// ! can't be enabled (by default since Android Gradle Plugin version 8.0), -// ! as final resource ID used in current class. -open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, ToolbarFragment.OnMenuItemClickListener { +@SuppressLint("CheckResult") +class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFragment.OnMenuItemClickListener { + + private var binding: EditorViewBinding = EditorViewBinding.bind(View.inflate(context, R.layout.editor_view, this)) @JvmField - @ViewById(R.id.editor) - var editor: CodeEditor? = null + val editor: CodeEditor = binding.editor @JvmField - @ViewById(R.id.debug_bar) - var debugBar: DebugBar? = null + val debugBar: DebugBar = binding.debugBar @JvmField - @ViewById(R.id.code_completion_bar) - var mCodeCompletionBar: CodeCompletionBar? = null - - @JvmField - @ViewById(R.id.input_method_enhance_bar) - var mInputMethodEnhanceBar: View? = null - - @JvmField - @ViewById(R.id.symbol_bar) - var mSymbolBar: CodeCompletionBar? = null - - @JvmField - @ViewById(R.id.functions) - var mShowFunctionsButton: ImageView? = null - - @JvmField - @ViewById(R.id.functions_keyboard) - var mFunctionsKeyboard: FunctionsKeyboardView? = null - - @JvmField - @ViewById(R.id.docs) - var mDocsWebView: EWebView? = null - - @JvmField - @ViewById(R.id.drawer_layout) - var mDrawerLayout: DrawerLayout? = null - var name: String? = null - private set + @JvmField var uri: Uri? = null - private set var scriptExecutionId = 0 private set + val activity: FragmentActivity + get() { + var context = context + while (context !is Activity && context is ContextWrapper) { + context = context.baseContext + } + return context as FragmentActivity + } + + val isTextChanged: Boolean + get() = editor.isTextChanged + + private val scriptExecution: ScriptExecution? + get() = AutoJs.instance.scriptEngineService.getScriptExecution(scriptExecutionId) + + private val mCodeCompletionBar: CodeCompletionBar = binding.codeCompletionBar + private val mInputMethodEnhanceBar: View = binding.inputMethodEnhanceBar + private val mSymbolBar: CodeCompletionBar = binding.symbolBar + private val mShowFunctionsButton: ImageView = binding.functions + private val mFunctionsKeyboard: FunctionsKeyboardView = binding.functionsKeyboard + private val mDocsWebView: EWebView = binding.docs + private val mDrawerLayout: DrawerLayout = binding.drawerLayout + private var mReadOnly = false private var mAutoCompletion: AutoCompletion? = null private var mEditorTheme: Theme? = null @@ -156,7 +141,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar val line = intent.getIntExtra(EXTRA_EXCEPTION_LINE_NUMBER, -1) val col = intent.getIntExtra(EXTRA_EXCEPTION_COLUMN_NUMBER, 0) if (line >= 1) { - editor!!.jumpTo(line - 1, col) + editor.jumpTo(line - 1, col) } msg?.let { showErrorMessage(it) } } @@ -169,9 +154,26 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar private var mTmpSavedFileForRunning: File? = null constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + init { + setUpEditor() + setUpInputMethodEnhancedBar() + setUpFunctionsKeyboard() + setMenuItemStatus(R.id.save, false) + mDocsWebView.apply { + webView.settings.displayZoomControls = true + webView.loadUrl(getUrl("index.html")) + } + Themes.getCurrent(context) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { theme: Theme? -> setTheme(theme) } + initNormalToolbar() + } + @SuppressLint("UnspecifiedRegisterReceiverFlag") override fun onAttachedToWindow() { super.onAttachedToWindow() @@ -203,14 +205,14 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar findViewById(R.id.run).visibility = GONE } if (mReadOnly) { - editor!!.setReadOnly(true) + editor.setReadOnly(true) } } } fun setRestoredText(text: String) { mRestoredText = text - editor!!.text = text + editor.text = text } private fun handleText(intent: Intent): Observable { @@ -241,11 +243,11 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar private fun setInitialText(text: String) { if (mRestoredText != null) { - editor!!.text = mRestoredText!! + editor.text = mRestoredText!! mRestoredText = null return } - editor!!.setInitialText(text) + editor.setInitialText(text) } private fun setMenuItemStatus(id: Int, enabled: Boolean) { @@ -263,23 +265,6 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar return mMenuItemStatus[id, defValue] } - @SuppressLint("CheckResult") - @AfterViews - fun init() { - setUpEditor() - setUpInputMethodEnhancedBar() - setUpFunctionsKeyboard() - setMenuItemStatus(R.id.save, false) - mDocsWebView?.apply { - webView.settings.displayZoomControls = true - webView.loadUrl(getUrl("index.html")) - } - Themes.getCurrent(context) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { theme: Theme? -> setTheme(theme) } - initNormalToolbar() - } - private fun initNormalToolbar() { mNormalToolbar.apply { setOnMenuItemClickListener(this@EditorView) @@ -298,7 +283,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar .setFunctionsView(mFunctionsKeyboard) .setFunctionsTrigger(mShowFunctionsButton) - .setEditView(editor!!.codeEditText) + .setEditView(editor.codeEditText) .build() // @ArchivedTodo by 抠脚本人 on Jul 10, 2023. @@ -313,21 +298,21 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar // ! 此处的点击事件回调注册是为了使功能键盘智能提示的属性可以实现其接口对应的功能: // ! 点击: 自动补全, 并根据情况添加括号或句点符号等. // ! 长按: 以浮动窗口形式展示 [方法/属性/模块] 对应的文档内容 (如果存在的话). - mFunctionsKeyboard!!.setClickCallback(this) + mFunctionsKeyboard.setClickCallback(this) - mShowFunctionsButton!!.setOnLongClickListener { - true.also { editor!!.beautifyCode() } + mShowFunctionsButton.setOnLongClickListener { + true.also { editor.beautifyCode() } } } private fun setUpInputMethodEnhancedBar() { - mSymbolBar!!.let { bar -> + mSymbolBar.let { bar -> bar.setOnHintClickListener(this) bar.codeCompletions = Symbols.getSymbols() } - mCodeCompletionBar!!.let { bar -> + mCodeCompletionBar.let { bar -> bar.setOnHintClickListener(this) - AutoCompletion(context, editor!!.codeEditText).apply { + AutoCompletion(context, editor.codeEditText).apply { setAutoCompleteCallback { bar.codeCompletions = it } mAutoCompletion = this } @@ -335,14 +320,14 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar } private fun setUpEditor() { - editor!!.let { editor -> + editor.let { editor -> editor.codeEditText.let { editText -> editText.addTextChangedListener(SimpleTextWatcher { _ -> setMenuItemStatus(R.id.save, editor.isTextChanged) setMenuItemStatus(R.id.undo, editor.canUndo()) setMenuItemStatus(R.id.redo, editor.canRedo()) }) - editText.textSize = getEditorTextSize(pxToSp(context, editText.textSize).toInt()).toFloat() + editText.textSize = getEditorTextSize(pxToSp(editText.textSize).toInt()).toFloat() } editor.addCursorChangeCallback(object : CodeEditor.CursorChangeCallback { override fun onCursorChange(line: String, cursor: Int) { @@ -360,22 +345,22 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar private fun setTheme(theme: Theme?) { theme?.let { mEditorTheme = it - editor!!.setTheme(it) - mInputMethodEnhanceBar!!.setBackgroundColor(it.imeBarBackgroundColor) + editor.setTheme(it) + mInputMethodEnhanceBar.setBackgroundColor(it.imeBarBackgroundColor) val textColor = it.imeBarForegroundColor - mCodeCompletionBar!!.setTextColor(textColor) - mSymbolBar!!.setTextColor(textColor) - mShowFunctionsButton!!.setColorFilter(textColor) + mCodeCompletionBar.setTextColor(textColor) + mSymbolBar.setTextColor(textColor) + mShowFunctionsButton.setColorFilter(textColor) invalidate() } } fun onBackPressed(): Boolean { - if (mDrawerLayout!!.isDrawerOpen(GravityCompat.START)) { - if (mDocsWebView!!.webView.canGoBack()) { - mDocsWebView!!.webView.goBack() + if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { + if (mDocsWebView.webView.canGoBack()) { + mDocsWebView.webView.goBack() } else { - mDrawerLayout!!.closeDrawer(GravityCompat.START) + mDrawerLayout.closeDrawer(GravityCompat.START) } return true } @@ -423,20 +408,20 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar return run(true, file, uri!!.path) } - private fun undo() = editor!!.undo() + private fun undo() = editor.undo() - private fun redo() = editor!!.redo() + private fun redo() = editor.redo() fun save(): Observable { val path = uri!!.path val backPath = "$path.save" move(path!!, backPath) - return Observable.just(editor!!.text) + return Observable.just(editor.text) .observeOn(Schedulers.io()) .doOnNext { s: String? -> write(context.contentResolver.openOutputStream(uri!!)!!, s!!) } .observeOn(AndroidSchedulers.mainThread()) .doOnNext { - editor!!.markTextAsSaved() + editor.markTextAsSaved() setMenuItemStatus(R.id.save, false) } .doOnNext { @@ -451,13 +436,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar } private fun doWithCurrentEngine(callback: Callback>) { - val execution = AutoJs.instance.scriptEngineService.getScriptExecution(scriptExecutionId) - if (execution != null) { - val engine = execution.engine - if (engine != null) { - callback.call(engine) - } - } + scriptExecution?.engine?.let { callback.call(it) } } @SuppressLint("CheckResult") @@ -470,9 +449,9 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar } } - private fun findNext() = editor!!.findNext() + private fun findNext() = editor.findNext() - private fun findPrev() = editor!!.findPrev() + private fun findPrev() = editor.findPrev() private fun cancelSearch() = showNormalToolbar() @@ -482,22 +461,10 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar .commitAllowingStateLoss() } - val activity: FragmentActivity - get() { - var context = context - while (context !is Activity && context is ContextWrapper) { - context = context.baseContext - } - return context as FragmentActivity - } - fun replace() { - editor!!.replaceSelection() + editor.replaceSelection() } - val isTextChanged: Boolean - get() = editor!!.isTextChanged - fun showConsole() { doWithCurrentEngine { engine: ScriptEngine<*> -> (engine as JavaScriptEngine).runtime.console.show() } } @@ -509,32 +476,32 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar } fun beautifyCode() { - editor!!.beautifyCode() + editor.beautifyCode() } @SuppressLint("CheckResult") fun selectEditorTheme() { - editor!!.setProgress(true) + editor.setProgress(true) Themes.getAllThemes(context) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { themes: List -> - editor!!.setProgress(false) + editor.setProgress(false) selectEditorTheme(themes) } } fun selectTextSize() { TextSizeSettingDialogBuilder(context) - .initialValue(pxToSp(context, editor!!.codeEditText.textSize).toInt()) + .initialValue(pxToSp(editor.codeEditText.textSize).toInt()) .callback { value: Int -> setTextSize(value) } .show() } fun setTextSize(value: Int) { setEditorTextSize(value) - editor!!.codeEditText.textSize = value.toFloat() - editor!!.lastTextSize = value + editor.codeEditText.textSize = value.toFloat() + editor.lastTextSize = value } private fun selectEditorTheme(themes: List) { @@ -557,7 +524,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar @Throws(CheckedPatternSyntaxException::class) fun find(keywords: String, usingRegex: Boolean) { - editor!!.find(keywords, usingRegex) + editor.find(keywords, usingRegex) showSearchToolbar(false) } @@ -572,21 +539,21 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar @Throws(CheckedPatternSyntaxException::class) fun replace(keywords: String, replacement: String, usingRegex: Boolean) { - editor!!.replace(keywords, replacement, usingRegex) + editor.replace(keywords, replacement, usingRegex) showSearchToolbar(true) } @Throws(CheckedPatternSyntaxException::class) fun replaceAll(keywords: String, replacement: String, usingRegex: Boolean) { - editor!!.replaceAll(keywords, replacement, usingRegex) + editor.replaceAll(keywords, replacement, usingRegex) } fun debug() { activity.supportFragmentManager.beginTransaction() .replace(R.id.toolbar_menu, DebugToolbarFragment()) .commit() - debugBar!!.visibility = VISIBLE - mInputMethodEnhanceBar!!.visibility = GONE + debugBar.visibility = VISIBLE + mInputMethodEnhanceBar.visibility = GONE mDebugging = true } @@ -597,9 +564,9 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar fragment.detachDebugger() } showNormalToolbar() - editor!!.setDebuggingLine(-1) - debugBar!!.visibility = GONE - mInputMethodEnhanceBar!!.visibility = VISIBLE + editor.setDebuggingLine(-1) + debugBar.visibility = GONE + mInputMethodEnhanceBar.visibility = VISIBLE mDebugging = false } @@ -627,7 +594,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar // editor!!.commentLine() // } else editor!!.insert(completion.insertText) - editor!!.insert(completion.insertText) + editor.insert(completion.insertText) } override fun onHintLongClick(completions: CodeCompletions, pos: Int) { @@ -653,7 +620,7 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar completions[pos].let { when { // @Inspired by 抠脚本人 (https://github.com/little-alei) on Jul 13, 2023. - it.insertText == "/" -> editor!!.commentHelper.handle() + it.insertText == "/" -> editor.commentHelper.handle() it.url != null -> showManual(it.url, it.hint) } } @@ -665,8 +632,8 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar .title(title) .url(absUrl) .pinToLeft { - mDocsWebView!!.webView.loadUrl(absUrl) - mDrawerLayout!!.openDrawer(GravityCompat.START) + mDocsWebView.webView.loadUrl(absUrl) + mDrawerLayout.openDrawer(GravityCompat.START) } .show() } @@ -681,12 +648,12 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar p = "$p()" } if (property.isGlobal) { - editor!!.insert(p) + editor.insert(p) } else { - editor!!.insert(m.name + "." + p) + editor.insert(m.name + "." + p) } if (!property.isVariable) { - editor!!.moveCursor(-1) + editor.moveCursor(-1) } mFunctionsKeyboardHelper!!.hideFunctionsLayout(true) } @@ -699,15 +666,9 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar } } - val scriptExecution: ScriptExecution? - get() = AutoJs.instance.scriptEngineService.getScriptExecution(scriptExecutionId) - - override fun onSaveInstanceState(): Parcelable? { - val bundle = Bundle() - val superData = super.onSaveInstanceState() - bundle.putParcelable("super_data", superData) - bundle.putInt("script_execution_id", scriptExecutionId) - return bundle + override fun onSaveInstanceState() = Bundle().apply { + putParcelable("super_data", super.onSaveInstanceState()) + putInt("script_execution_id", scriptExecutionId) } override fun onRestoreInstanceState(state: Parcelable) { @@ -721,17 +682,17 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar } fun destroy() { - editor!!.destroy() + editor.destroy() mAutoCompletion?.shutdown() } @SuppressLint("CheckResult") private fun saveToTmpFile(): Observable { return Observable.fromCallable { - val tmp = TmpScriptFiles.create(context) - write(tmp, editor!!.text) - mTmpSavedFileForRunning = tmp - tmp + TmpScriptFiles.create(context).also { + write(it, editor.text) + mTmpSavedFileForRunning = it + } }.observeOn(Schedulers.io()) } @@ -748,4 +709,5 @@ open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, Toolbar const val EXTRA_SAVE_ENABLED = "saveEnabled" const val EXTRA_RUN_ENABLED = "runEnabled" } + } \ No newline at end of file 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 index 21507dfd..3994c5a8 100644 --- 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 @@ -59,7 +59,7 @@ class CodeEditor : HVScrollView { val codeEditText: CodeEditText = binding.codeEditText.also { it.addTextChangedListener(AutoIndent(it)) - lastTextSize = getEditorTextSize(pxToSp(it.context, it.textSize).toInt()) + lastTextSize = getEditorTextSize(pxToSp(it.textSize).toInt()) } val lineCount diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/DebugToolbarFragment.java b/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/DebugToolbarFragment.java deleted file mode 100644 index 191a2635..00000000 --- a/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/DebugToolbarFragment.java +++ /dev/null @@ -1,301 +0,0 @@ -package org.autojs.autojs.ui.edit.toolbar; - -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; - -import org.autojs.autojs.execution.ScriptExecution; -import org.autojs.autojs.pio.PFiles; -import org.autojs.autojs.rhino.debug.DebugCallback; -import org.autojs.autojs.rhino.debug.Debugger; -import org.autojs.autojs.rhino.debug.Dim; -import org.autojs.autojs.runtime.exception.ScriptInterruptedException; -import org.autojs.autojs.ui.edit.EditorView; -import org.autojs.autojs.ui.edit.debug.CodeEvaluator; -import org.autojs.autojs.ui.edit.debug.DebugBar; -import org.autojs.autojs.ui.edit.debug.DebuggerSingleton; -import org.autojs.autojs.ui.edit.debug.WatchingVariable; -import org.autojs.autojs.ui.edit.editor.CodeEditor; -import org.autojs.autojs.util.ViewUtils; -import org.autojs.autojs6.R; -import org.autojs.autojs6.databinding.FragmentDebugToolbarBinding; - -import java.lang.ref.WeakReference; -import java.util.Arrays; -import java.util.List; - -public class DebugToolbarFragment extends ToolbarFragment implements DebugCallback, CodeEditor.CursorChangeCallback, CodeEvaluator { - - private static final String LOG_TAG = "DebugToolbarFragment"; - private EditorView mEditorView; - private boolean mCursorChangeFromUser = true; - private Debugger mDebugger; - private Handler mHandler; - private String mCurrentEditorSourceUrl; - private String mInitialEditorSourceUrl; - private String mInitialEditorSource; - - private final RecyclerView.AdapterDataObserver mVariableChangeObserver = new RecyclerView.AdapterDataObserver() { - @Override - public void onItemRangeInserted(int positionStart, int itemCount) { - updateWatchingVariables(positionStart, positionStart + itemCount); - } - }; - private final CodeEditor.BreakpointChangeListener mBreakpointChangeListener = new CodeEditor.BreakpointChangeListener() { - @Override - public void onBreakpointChange(int line, boolean enabled) { - if (mDebugger != null) { - mDebugger.breakpoint(line + 1, enabled); - } - } - - @Override - public void onAllBreakpointRemoved(int count) { - mDebugger.clearAllBreakpoints(); - } - }; - private FragmentDebugToolbarBinding binding; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mHandler = new Handler(); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - binding = FragmentDebugToolbarBinding.inflate(inflater, container, false); - return binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - mEditorView = findEditorView(view); - mDebugger = DebuggerSingleton.get(); - mDebugger.setWeakDebugCallback(new WeakReference<>(this)); - setInterrupted(false); - mCurrentEditorSourceUrl = mInitialEditorSourceUrl = mEditorView.getUri().toString(); - mInitialEditorSource = mEditorView.editor.getText(); - setupEditor(); - ScriptExecution execution = mEditorView.run(false); - if (execution != null) { - mDebugger.attach(execution); - } else { - mEditorView.exitDebugging(); - } - - Log.d(LOG_TAG, "onViewCreated"); - - // @Caution by SuperMonster003 on May 13, 2023. - // ! Do not place these listeners into onCreateView method. - - binding.stepOver.setOnClickListener(v -> stepOver()); - binding.stepInto.setOnClickListener(v -> stepInto()); - binding.stepOut.setOnClickListener(v -> stepOut()); - binding.stopScript.setOnClickListener(v -> stopScript()); - binding.resumeScript.setOnClickListener(v -> resumeScript()); - } - - private void setupEditor() { - CodeEditor editor = mEditorView.editor; - editor.setRedoUndoEnabled(false); - editor.addCursorChangeCallback(this); - editor.setBreakpointChangeListener(mBreakpointChangeListener); - DebugBar debugBar = mEditorView.debugBar; - debugBar.registerVariableChangeObserver(mVariableChangeObserver); - debugBar.setCodeEvaluator(this); - } - - private void setInterrupted(boolean interrupted) { - setMenuItemStatus(R.id.step_into, interrupted); - setMenuItemStatus(R.id.step_over, interrupted); - setMenuItemStatus(R.id.step_out, interrupted); - setMenuItemStatus(R.id.resume_script, interrupted); - if (!interrupted && mEditorView != null) { - mEditorView.editor.setDebuggingLine(-1); - } - } - - public void detachDebugger() { - if (!mDebugger.isAttached()) { - return; - } - Log.d(LOG_TAG, "detachDebugger"); - mDebugger.detach(); - if (mEditorView == null) { - return; - } - CodeEditor editor = mEditorView.editor; - if (!TextUtils.equals(mInitialEditorSourceUrl, mCurrentEditorSourceUrl)) { - editor.setText(mInitialEditorSource); - } - editor.setRedoUndoEnabled(true); - DebugBar debugBar = mEditorView.debugBar; - debugBar.setTitle(null); - debugBar.setCodeEvaluator(null); - } - - void stepOver() { - setInterrupted(false); - mDebugger.stepOver(); - } - - void stepInto() { - setInterrupted(false); - mDebugger.stepInto(); - } - - void stepOut() { - setInterrupted(false); - mDebugger.stepOut(); - } - - void stopScript() { - mEditorView.forceStop(); - } - - void resumeScript() { - setInterrupted(false); - mDebugger.resume(); - } - - @Override - public void updateSourceText(Dim.SourceInfo sourceInfo) { - Log.d(LOG_TAG, "updateSourceText: url = " + sourceInfo.url()); - sourceInfo.removeAllBreakpoints(); - for (CodeEditor.Breakpoint breakpoint : mEditorView.editor.getBreakpoints().values()) { - int line = breakpoint.line + 1; - if (sourceInfo.breakableLine(line)) { - sourceInfo.breakpoint(line, breakpoint.enabled); - Log.d(LOG_TAG, "not breakable: " + line); - } - } - } - - @Override - public void enterInterrupt(Dim.StackFrame stackFrame, String threadName, String message) { - showDebuggingLineOnEditor(stackFrame, message); - mHandler.post(this::updateWatchingVariables); - } - - private void updateWatchingVariables() { - updateWatchingVariables(0, mEditorView.debugBar.getWatchingVariables().size()); - } - - private void updateWatchingVariables(int start, int end) { - if (!mDebugger.isAttached()) { - return; - } - DebugBar debugBar = mEditorView.debugBar; - List variables = debugBar.getWatchingVariables(); - for (int i = start; i < end; i++) { - WatchingVariable variable = variables.get(i); - String value = eval(variable.getName()); - variable.setValue(value); - } - debugBar.refresh(start, end - start); - } - - public String eval(String expr) { - return mDebugger.eval(expr); - } - - private void showDebuggingLineOnEditor(Dim.StackFrame stackFrame, String message) { - //如果调试进入到其他脚本(例如模块脚本),则改变当前编辑器的文本为自动调试的脚本的代码 - String source; - //标记是否需要更改编辑器文本 - boolean shouldChangeText = !stackFrame.getUrl().equals(mCurrentEditorSourceUrl); - if (shouldChangeText) { - source = stackFrame.sourceInfo().source(); - } else { - source = null; - } - mCurrentEditorSourceUrl = stackFrame.getUrl(); - final int line = stackFrame.getLineNumber() - 1; - mHandler.post(() -> { - if (mEditorView == null) { - return; - } - if (shouldChangeText) { - mEditorView.editor.setText(source); - } - mCursorChangeFromUser = false; - mEditorView.editor.setDebuggingLine(line); - mEditorView.editor.jumpTo(line, 0); - mEditorView.debugBar.setTitle(PFiles.getName(mCurrentEditorSourceUrl)); - setInterrupted(true); - if (message != null && !message.equals(ScriptInterruptedException.class.getName())) { - ViewUtils.showToast(mEditorView.getContext(), message, true); - } - }); - } - - - @Override - public void onCursorChange(String line, int ch) { - if (ch == 0 && !mCursorChangeFromUser) { - mCursorChangeFromUser = true; - return; - } - mCursorChangeFromUser = true; - if (!mDebugger.isAttached()) { - return; - } - String variable = findVariableOnCursor(line, ch); - Log.d(LOG_TAG, "onCursorChange: variable = " + variable + ", ch = " + ch + ", line = " + line); - String value = eval(variable); - mEditorView.debugBar.updateCurrentVariable(variable, value); - } - - private String findVariableOnCursor(String line, int ch) { - int end; - for (end = ch; end < line.length(); end++) { - if (!isIdentifierChar(line.charAt(end))) { - break; - } - } - int start; - for (start = Math.min(ch - 1, line.length() - 1); start >= 0; start--) { - if (!isIdentifierChar(line.charAt(start))) { - break; - } - } - start++; - if (start < end && start < line.length() && start >= 0) { - return line.substring(start, end); - } - return null; - } - - private boolean isIdentifierChar(char c) { - return Character.isDigit(c) || Character.isLetter(c) || c == '.' || c == '_'; - } - - @Override - public List getMenuItemIds() { - return Arrays.asList(R.id.step_over, R.id.step_into, R.id.step_out, R.id.resume_script, R.id.stop_script); - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (mEditorView == null) { - return; - } - CodeEditor editor = mEditorView.editor; - editor.removeCursorChangeCallback(this); - editor.setBreakpointChangeListener(null); - DebugBar debugBar = mEditorView.debugBar; - debugBar.unregisterVariableChangeObserver(mVariableChangeObserver); - } -} diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/DebugToolbarFragment.kt b/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/DebugToolbarFragment.kt new file mode 100644 index 00000000..9ded9854 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/ui/edit/toolbar/DebugToolbarFragment.kt @@ -0,0 +1,263 @@ +package org.autojs.autojs.ui.edit.toolbar + +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.text.TextUtils +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver +import org.autojs.autojs.pio.PFiles.getName +import org.autojs.autojs.rhino.debug.DebugCallback +import org.autojs.autojs.rhino.debug.Debugger +import org.autojs.autojs.rhino.debug.Dim +import org.autojs.autojs.runtime.exception.ScriptInterruptedException +import org.autojs.autojs.ui.edit.EditorView +import org.autojs.autojs.ui.edit.debug.CodeEvaluator +import org.autojs.autojs.ui.edit.debug.DebuggerSingleton +import org.autojs.autojs.ui.edit.editor.CodeEditor.BreakpointChangeListener +import org.autojs.autojs.ui.edit.editor.CodeEditor.CursorChangeCallback +import org.autojs.autojs.util.ViewUtils.showToast +import org.autojs.autojs6.R +import org.autojs.autojs6.databinding.FragmentDebugToolbarBinding +import java.lang.ref.WeakReference + +/** + * Transformed by SuperMonster003 on Oct 27, 2023. + */ +class DebugToolbarFragment : ToolbarFragment(), DebugCallback, CursorChangeCallback, CodeEvaluator { + + private lateinit var binding: FragmentDebugToolbarBinding + + private lateinit var mEditorView: EditorView + private lateinit var mDebugger: Debugger + private lateinit var mHandler: Handler + private lateinit var mCurrentEditorSourceUrl: String + private lateinit var mInitialEditorSourceUrl: String + private lateinit var mInitialEditorSource: String + + private var mCursorChangeFromUser = true + + private val mVariableChangeObserver: AdapterDataObserver = object : AdapterDataObserver() { + override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { + updateWatchingVariables(positionStart, positionStart + itemCount) + } + } + + private val mBreakpointChangeListener: BreakpointChangeListener = object : BreakpointChangeListener { + override fun onBreakpointChange(line: Int, enabled: Boolean) { + mDebugger.breakpoint(line + 1, enabled) + } + + override fun onAllBreakpointRemoved(count: Int) { + mDebugger.clearAllBreakpoints() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mHandler = Handler(Looper.getMainLooper()) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return FragmentDebugToolbarBinding.inflate(inflater, container, false) + .also { binding = it }.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mEditorView = findEditorView(view) + mDebugger = DebuggerSingleton.get() + mDebugger.setWeakDebugCallback(WeakReference(this)) + setInterrupted(false) + mInitialEditorSourceUrl = mEditorView.uri.toString() + mCurrentEditorSourceUrl = mInitialEditorSourceUrl + mInitialEditorSource = mEditorView.editor.text + setupEditor() + val execution = mEditorView.run(false) + if (execution != null) { + mDebugger.attach(execution) + } else { + mEditorView.exitDebugging() + } + Log.d(LOG_TAG, "onViewCreated") + + // @Caution by SuperMonster003 on May 13, 2023. + // ! Do not place these listeners into onCreateView method. + binding.stepOver.setOnClickListener { stepOver() } + binding.stepInto.setOnClickListener { stepInto() } + binding.stepOut.setOnClickListener { stepOut() } + binding.stopScript.setOnClickListener { stopScript() } + binding.resumeScript.setOnClickListener { resumeScript() } + } + + private fun setupEditor() { + mEditorView.editor.apply { + setRedoUndoEnabled(false) + addCursorChangeCallback(this@DebugToolbarFragment) + setBreakpointChangeListener(mBreakpointChangeListener) + } + mEditorView.debugBar.apply { + registerVariableChangeObserver(mVariableChangeObserver) + setCodeEvaluator(this@DebugToolbarFragment) + } + } + + private fun setInterrupted(interrupted: Boolean) { + setMenuItemStatus(R.id.step_into, interrupted) + setMenuItemStatus(R.id.step_over, interrupted) + setMenuItemStatus(R.id.step_out, interrupted) + setMenuItemStatus(R.id.resume_script, interrupted) + if (!interrupted) { + mEditorView.editor.setDebuggingLine(-1) + } + } + + fun detachDebugger() { + if (!mDebugger.isAttached) { + return + } + Log.d(LOG_TAG, "detachDebugger") + mDebugger.detach() + mEditorView.editor.also { + if (!TextUtils.equals(mInitialEditorSourceUrl, mCurrentEditorSourceUrl)) { + it.text = mInitialEditorSource + } + it.setRedoUndoEnabled(true) + } + mEditorView.debugBar.apply { + setTitle(null) + setCodeEvaluator(null) + } + } + + private fun stepOver() = setInterrupted(false).also { mDebugger.stepOver() } + + private fun stepInto() = setInterrupted(false).also { mDebugger.stepInto() } + + private fun stepOut() = setInterrupted(false).also { mDebugger.stepOut() } + + private fun stopScript() = mEditorView.forceStop() + + private fun resumeScript() = setInterrupted(false).also { mDebugger.resume() } + + override fun updateSourceText(sourceInfo: Dim.SourceInfo) { + Log.d(LOG_TAG, "updateSourceText: url = " + sourceInfo.url()) + sourceInfo.removeAllBreakpoints() + for (breakpoint in mEditorView.editor.breakpoints.values) { + val line = breakpoint.line + 1 + if (sourceInfo.breakableLine(line)) { + sourceInfo.breakpoint(line, breakpoint.enabled) + Log.d(LOG_TAG, "not breakable: $line") + } + } + } + + override fun enterInterrupt(stackFrame: Dim.StackFrame, threadName: String, message: String) { + showDebuggingLineOnEditor(stackFrame, message) + mHandler.post { updateWatchingVariables() } + } + + private fun updateWatchingVariables(start: Int = 0, end: Int = mEditorView.debugBar.watchingVariables.size) { + if (!mDebugger.isAttached) { + return + } + val debugBar = mEditorView.debugBar + val variables = debugBar.watchingVariables + for (i in start until end) { + val variable = variables[i] + variable.value = eval(variable.name) + } + debugBar.refresh(start, end - start) + } + + override fun eval(expr: String): String = mDebugger.eval(expr) + + private fun showDebuggingLineOnEditor(stackFrame: Dim.StackFrame, message: String?) { + // 标记是否需要更改编辑器文本 + val shouldChangeText = stackFrame.url != mCurrentEditorSourceUrl + + // 如果调试进入到其他脚本 (例如模块脚本), 则改变当前编辑器的文本为自动调试的脚本的代码 + val source: String? = if (shouldChangeText) stackFrame.sourceInfo().source() else null + + mCurrentEditorSourceUrl = stackFrame.url + + val line = stackFrame.lineNumber - 1 + + mHandler.post { + source?.let { mEditorView.editor.text = it } + mCursorChangeFromUser = false + mEditorView.editor.setDebuggingLine(line) + mEditorView.editor.jumpTo(line, 0) + mEditorView.debugBar.setTitle(getName(mCurrentEditorSourceUrl)) + setInterrupted(true) + if (message != ScriptInterruptedException::class.java.name) { + showToast(mEditorView.context, message, true) + } + } + } + + override fun onCursorChange(line: String, cursor: Int) { + if (cursor == 0 && !mCursorChangeFromUser) { + mCursorChangeFromUser = true + return + } + mCursorChangeFromUser = true + if (!mDebugger.isAttached) { + return + } + val variable = findVariableOnCursor(line, cursor) + Log.d(LOG_TAG, "onCursorChange: variable = $variable, ch = $cursor, line = $line") + variable?.let { + eval(it) + mEditorView.debugBar.updateCurrentVariable(variable, it) + } + } + + private fun findVariableOnCursor(line: String, ch: Int): String? { + var end = ch + while (end < line.length) { + if (!isIdentifierChar(line[end])) { + break + } + end++ + } + var start = (ch - 1).coerceAtMost(line.length - 1) + while (start >= 0) { + if (!isIdentifierChar(line[start])) { + break + } + start-- + } + start++ + return if (start < end && start < line.length && start >= 0) { + line.substring(start, end) + } else null + } + + private fun isIdentifierChar(c: Char): Boolean { + return Character.isDigit(c) || Character.isLetter(c) || c == '.' || c == '_' + } + + override fun getMenuItemIds(): List { + return listOf(R.id.step_over, R.id.step_into, R.id.step_out, R.id.resume_script, R.id.stop_script) + } + + override fun onDestroy() { + super.onDestroy() + mEditorView.editor.apply { + removeCursorChangeCallback(this@DebugToolbarFragment) + setBreakpointChangeListener(null) + } + mEditorView.debugBar.apply { + unregisterVariableChangeObserver(mVariableChangeObserver) + } + } + + companion object { + private const val LOG_TAG = "DebugToolbarFragment" + } + +} diff --git a/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.kt b/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.kt index e48c44b2..f9e83758 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.kt +++ b/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.kt @@ -123,7 +123,7 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity { override fun onStart() { super.onStart() - WrappedShizuku.checkShizukuPermission() + WrappedShizuku.bindUserServiceIfNeeded() } private fun recreateIfNeeded() { diff --git a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.kt b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.kt index d2ed1365..f1a19de8 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.kt +++ b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.kt @@ -2,7 +2,6 @@ package org.autojs.autojs.ui.main.drawer import android.content.Context import android.content.Intent -import android.content.IntentFilter import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -18,6 +17,7 @@ import org.autojs.autojs.permission.DisplayOverOtherAppsPermission import org.autojs.autojs.permission.IgnoreBatteryOptimizationsPermission import org.autojs.autojs.permission.MediaProjectionPermission import org.autojs.autojs.permission.PostNotificationPermission +import org.autojs.autojs.permission.ShizukuPermission import org.autojs.autojs.permission.UsageStatsPermission import org.autojs.autojs.permission.WriteSecureSettingsPermission import org.autojs.autojs.permission.WriteSystemSettingsPermission @@ -55,7 +55,6 @@ open class DrawerFragment : Fragment() { private lateinit var mDrawerMenu: RecyclerView private lateinit var mContext: Context private lateinit var mActivity: MainActivity - // private lateinit var mReceiver: BatteryChangedReceiver private lateinit var mAccessibilityServiceItem: DrawerMenuToggleableItem private lateinit var mForegroundServiceItem: DrawerMenuToggleableItem @@ -70,29 +69,21 @@ open class DrawerFragment : Fragment() { private lateinit var mWriteSystemSettingsItem: DrawerMenuToggleableItem private lateinit var mWriteSecuritySettingsItem: DrawerMenuToggleableItem private lateinit var mProjectMediaAccessItem: DrawerMenuToggleableItem + private lateinit var mShizukuAccessItem: DrawerMenuToggleableItem private lateinit var mNightModeItem: DrawerMenuToggleableItem private lateinit var mAutoNightModeItem: DrawerMenuToggleableItem private lateinit var mKeepScreenOnWhenInForegroundItem: DrawerMenuToggleableItem private lateinit var mThemeColorItem: DrawerMenuShortcutItem private lateinit var mAboutAppAndDevItem: DrawerMenuShortcutItem - private val actionBatteryChangedIntentFilter = IntentFilter().apply { - addAction("android.intent.action.ACTION_BATTERY_CHANGED") - addAction("android.intent.action.ACTION_POWER_CONNECTED") - addAction("android.intent.action.ACTION_POWER_DISCONNECTED") - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) EventBus.getDefault().register(this) - // mReceiver = BatteryChangedReceiver() mContext = requireContext() - mActivity = (requireActivity() as MainActivity).apply { - // registerReceiver(mReceiver, actionBatteryChangedIntentFilter) - } + mActivity = (requireActivity() as MainActivity) mAccessibilityServiceItem = DrawerMenuToggleableItem( AccessibilityService(mContext), @@ -224,6 +215,13 @@ open class DrawerFragment : Fragment() { R.string.text_project_media_access_description, ) + mShizukuAccessItem = DrawerMenuToggleableItem( + ShizukuPermission(mContext), + R.drawable.ic_app_shizuku_representative, + R.string.text_shizuku_access, + R.string.text_shizuku_access_description, + ) + mAutoNightModeItem = DrawerMenuToggleableItem( object : DrawerMenuItemCustomHelper(mContext) { override fun toggle() { @@ -397,6 +395,7 @@ open class DrawerFragment : Fragment() { mWriteSystemSettingsItem, mWriteSecuritySettingsItem, mProjectMediaAccessItem, + mShizukuAccessItem, DrawerMenuGroup(R.string.text_appearance), mAutoNightModeItem, mNightModeItem, @@ -432,6 +431,7 @@ open class DrawerFragment : Fragment() { mWriteSystemSettingsItem, mWriteSecuritySettingsItem, mProjectMediaAccessItem, + mShizukuAccessItem, mKeepScreenOnWhenInForegroundItem, ).forEach { it.sync() } @@ -446,8 +446,6 @@ open class DrawerFragment : Fragment() { companion object { - private val TAG = DrawerFragment::class.java.simpleName - lateinit var drawerMenuAdapter: DrawerMenuAdapter private set @@ -458,27 +456,4 @@ open class DrawerFragment : Fragment() { } - // class BatteryChangedReceiver : BroadcastReceiver() { - // - // override fun onReceive(context: Context, intent: Intent) { - // val action = intent.action ?: return - // when { - // action.matches(Regex("android.intent.action.ACTION_(POWER_CONNECTED|BATTERY_CHANGED)", RegexOption.IGNORE_CASE)) -> { - // Log.d(TAG, "Received broadcast: $action") - // - // // FIXME by SuperMonster003 on Jun 7, 2023. - // // ! Code below doesn't work. - // - // // if (DeviceUtils.isPowerSourceUSB(context)) { - // // Log.d(TAG, "Current power source is USB") - // // drawerMenuAdapter.drawerMenuItems.find { it.title == R.string.text_server_mode } - // // ?.takeUnless { it.isChecked } - // // ?.let { it.subtitle = "true" } - // // } - // } - // } - // } - // - // } - } \ No newline at end of file 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 799d104d..93e18a9b 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 @@ -77,7 +77,6 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa private MaterialDialog mProgressDialog; private String mSource; private boolean mIsDefaultIcon = true; - private boolean mIsApkTemplateInAssets = false; @SuppressLint("SetTextI18n") @Override @@ -102,9 +101,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa binding.fab.setOnClickListener(v -> buildApk()); binding.selectSource.setOnClickListener(v -> selectSourceFilePath()); binding.selectOutput.setOnClickListener(v -> selectOutputDirPath()); - binding.selectTemplate.setOnClickListener( - v -> selectTemplateFilePath() - ); + binding.selectTemplate.setOnClickListener(v -> selectTemplateFilePath()); mIcon.setOnClickListener(v -> selectIcon()); setToolbarAsBack(R.string.text_build_apk); @@ -112,9 +109,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa if (mSource != null) { setupWithSourceFile(new ScriptFile(mSource)); } - mIsApkTemplateInAssets = checkApkTemplateInAssets(); - if (mIsApkTemplateInAssets) { - mTemplatePath.setText("file:///android_asset/template.apk"); + if (checkApkTemplateInAssets()) { + mTemplatePath.setText(R.string.path_android_asset_template); } } @@ -319,7 +315,11 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa } private ApkBuilder callApkBuilder(File tmpDir, File outApk, ApkBuilder.AppConfig appConfig) throws Exception { - InputStream templateApk = mTemplatePath.getText() != null ? (mTemplatePath.getText().toString().equals("file:///android_asset/template.apk") ? getAssets().open("template.apk") : new java.io.FileInputStream(mTemplatePath.getText().toString())) : ApkBuilderPluginHelper.openTemplateApk(BuildActivity.this); + InputStream templateApk = mTemplatePath.getText() != null + ? mTemplatePath.getText().toString().equals(getString(R.string.path_android_asset_template)) + ? getAssets().open(TEMPLATE_APK_PATH) + : new java.io.FileInputStream(mTemplatePath.getText().toString()) + : ApkBuilderPluginHelper.openTemplateApk(BuildActivity.this); return new ApkBuilder(templateApk, outApk, tmpDir.getPath()) .setProgressCallback(BuildActivity.this) .prepare() diff --git a/app/src/main/java/org/autojs/autojs/util/DisplayUtils.kt b/app/src/main/java/org/autojs/autojs/util/DisplayUtils.kt index 8ac952c2..b649777b 100644 --- a/app/src/main/java/org/autojs/autojs/util/DisplayUtils.kt +++ b/app/src/main/java/org/autojs/autojs/util/DisplayUtils.kt @@ -1,36 +1,56 @@ package org.autojs.autojs.util -import android.content.Context +import android.os.Build +import android.util.DisplayMetrics import android.util.DisplayMetrics.DENSITY_DEFAULT +import android.util.TypedValue +import android.util.TypedValue.COMPLEX_UNIT_DIP +import android.util.TypedValue.COMPLEX_UNIT_SP +import org.autojs.autojs.app.GlobalAppContext import org.autojs.autojs.runtime.api.ScreenMetrics import kotlin.math.ceil import kotlin.math.floor import kotlin.math.round import kotlin.math.roundToInt +@Suppress("unused") object DisplayUtils { + + @JvmStatic + val displayMetrics: DisplayMetrics + get() = ScreenMetrics.resources?.displayMetrics ?: GlobalAppContext.get().resources.displayMetrics + /** * This method converts dp unit to equivalent pixels, depending on device density. * - * @param ctx Context to get resources and device specific display metrics * @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels * @return A float value to represent px equivalent to dp depending on device density */ @JvmStatic - fun dpToPx(ctx: Context, dp: Float) = dp * (ctx.resources.displayMetrics.densityDpi.toFloat() / DENSITY_DEFAULT) + fun dpToPx(dp: Float) = TypedValue.applyDimension(COMPLEX_UNIT_DIP, dp, displayMetrics) /** * This method converts device specific pixels to density independent pixels. * - * @param ctx Context to get resources and device specific display metrics * @param px A value in px (pixels) unit. Which we need to convert into db * @return A float value to represent dp equivalent to px value */ @JvmStatic - fun pxToDp(ctx: Context, px: Float) = px / (ctx.resources.displayMetrics.densityDpi.toFloat() / DENSITY_DEFAULT) + fun pxToDp(px: Float): Float { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + return TypedValue.deriveDimension(COMPLEX_UNIT_DIP, px, displayMetrics) + } + return px / (displayMetrics.densityDpi.toFloat() / DENSITY_DEFAULT) + } @JvmStatic - fun pxToSp(ctx: Context, px: Float) = px / ctx.resources.displayMetrics.scaledDensity + fun pxToSp(px: Float): Float { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + return TypedValue.deriveDimension(COMPLEX_UNIT_SP, px, displayMetrics) + } + @Suppress("DEPRECATION") + return px / displayMetrics.scaledDensity + } @JvmOverloads @JvmStatic diff --git a/app/src/main/res/drawable/ic_app_shizuku_representative.png b/app/src/main/res/drawable/ic_app_shizuku_representative.png new file mode 100644 index 00000000..6b20b034 Binary files /dev/null and b/app/src/main/res/drawable/ic_app_shizuku_representative.png differ diff --git a/app/src/main/res/layout/activity_edit.xml b/app/src/main/res/layout/activity_edit.xml index 173b803a..9260cca7 100644 --- a/app/src/main/res/layout/activity_edit.xml +++ b/app/src/main/res/layout/activity_edit.xml @@ -1,5 +1,5 @@ - diff --git a/app/src/main/res/layout/activity_log.xml b/app/src/main/res/layout/activity_log.xml index b9af4322..0ffb4999 100644 --- a/app/src/main/res/layout/activity_log.xml +++ b/app/src/main/res/layout/activity_log.xml @@ -24,7 +24,8 @@ + android:paddingStart="4dp" + android:paddingEnd="2dp"> - + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true" + android:theme="@style/Theme.AppCompat.Light.NoActionBar" + android:orientation="vertical"> - - + android:theme="@style/ThemeOverlay.AppCompat.ActionBar" + android:fitsSystemWindows="true"> + + - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_project_config.xml b/app/src/main/res/layout/activity_project_config.xml index aa5758fa..cf9a5f3a 100644 --- a/app/src/main/res/layout/activity_project_config.xml +++ b/app/src/main/res/layout/activity_project_config.xml @@ -5,11 +5,11 @@ android:layout_height="match_parent" android:orientation="vertical"> - + android:fitsSystemWindows="true">` - + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true" + android:theme="@style/Theme.AppCompat.Light.NoActionBar" + android:orientation="vertical"> - - + android:theme="@style/ThemeOverlay.AppCompat.ActionBar" + android:fitsSystemWindows="true"> + + + android:id="@+id/fragment_setting" + android:layout_width="match_parent" + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_tasker_script_edit.xml b/app/src/main/res/layout/activity_tasker_script_edit.xml index 7f5b5291..1de25708 100644 --- a/app/src/main/res/layout/activity_tasker_script_edit.xml +++ b/app/src/main/res/layout/activity_tasker_script_edit.xml @@ -1,5 +1,5 @@ -أضف اختصارات إلى المشغل تثبيت الاختصارات على المشغل غير مدعوم قالب مسار APK + يتيح Shizuku استخدام واجهة برمجة تطبيقات النظام مع امتيازات ADB + وصول Shizuku + منح الوصول إلى AutoJs6 في تطبيق Shizuku + قم بإلغاء الوصول إلى AutoJs6 في تطبيق Shizuku + قد يلزم تشغيل خدمة Shizuku أولاً + لم يتم تثبيت Shizuku + نسخة Shizuku غير مدعومة + فشل في إلغاء وصول Shizuku + فشل منح Shizuku حق الوصول + غير قادر على استخدام خدمة Shizuku المجموع: عنصر واحد diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index d77dc7cb..87127545 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -696,7 +696,17 @@ Shortcuts can perform specific actions in AutoJs6. Users can display these shortcuts in a supported launcher and quickly start some tasks or launch an activity, such as reading AutoJs6 application documentation, launching AutoJs6 settings page, and so on. Add shortcuts to launcher Pinning shortcuts to launcher is not supported - template apk path + Template APK path + Shizuku makes it possible to uses system API with ADB privileges + Shizuku access + Grant AutoJs6 access in Shizuku app + Revoke AutoJs6 access in Shizuku app + Shizuku service may need to be run first + Shizuku is not installed + Shizuku version is not supported + Failed to revoke Shizuku access + Failed to grant Shizuku access + Unable to use Shizuku service Total: %d item diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 51a21bcd..8cb2167b 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -697,8 +697,18 @@ Atajos del lanzador Los accesos directos pueden realizar acciones específicas en AutoJs6. Los usuarios pueden mostrar estos accesos directos en un lanzador compatible e iniciar rápidamente algunas tareas o iniciar una actividad, como leer la documentación de la aplicación AutoJs6, iniciar la página de configuración de AutoJs6, etc. Añadir accesos directos al lanzador - No es posible anclar accesos directos al lanzador. + No es posible anclar accesos directos al lanzador Ruta de acceso de APK de plantilla + Shizuku te permite obtener privilegios ADB y acceso a las APIs del sistema + Permisos de Shizuku + Conceder privilegios AutoJs6 en una aplicación Shizuku + Revocar privilegios de AutoJs6 en una aplicación Shizuku + Puede que necesite ejecutar primero el servicio Shizuku + Shizuku no está instalado + No se admite la versión de Shizuku + Fallo en la revocación de permisos Shizuku + Fallo en la concesión de permisos Shizuku + No se puede utilizar el servicio Shizuku 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 753da767..7ec6f03f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -698,7 +698,17 @@ Les raccourcis permettent d\'effectuer des actions spécifiques dans AutoJs6. Les utilisateurs peuvent afficher ces raccourcis dans un lanceur pris en charge et démarrer rapidement certaines tâches ou lancer une activité, comme la lecture de la documentation de l\'application AutoJs6, le lancement de la page de configuration d\'AutoJs6, etc. Ajouter des raccourcis au lanceur L\'épinglage de raccourcis dans le lanceur n\'est pas pris en charge - Chemin d’accès APK du modèle + Chemin d\'accès APK du modèle + Shizuku vous permet d\'obtenir des privilèges ADB et d\'accéder aux API du système + Permissions de Shizuku + Accorder des privilèges AutoJs6 dans une application Shizuku + Révoquer les privilèges AutoJs6 dans une application Shizuku + Il se peut que vous deviez d\'abord exécuter le service Shizuku + Shizuku n\'est pas installé + La version de Shizuku n\'est pas prise en charge + La révocation des droits de Shizuku a échoué + Échec de l\'octroi de la permission de Shizuku + Impossible d\'utiliser le service Shizuku Total: %d article diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 79ba537f..8e83f339 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -698,7 +698,17 @@ ショートカットは、AutoJs6 で特定のアクションを実行することができます。ユーザーは、サポートされているランチャーにこれらのショートカットを表示し、AutoJs6アプリケーションのドキュメントを読んだり、AutoJs6の設定ページを起動したりするなど、いくつかのタスクを素早く開始したり、アクティビティを起動したりすることができます ランチャーへのショートカットの追加 ショートカットをランチャーにピン留めすることはサポートされていません - テンプレートAPKパス + テンプレート APK パス + Shizuku を使用すると、ADB 権限を取得し、システム API にアクセスできるようになります + Shizuku のパーミッション + Shizuku アプリケーションで AutoJs6 権限を付与する + Shizuku アプリケーションで AutoJs6 権限を取り消す + 最初に Shizuku サービスを実行する必要があるかもしれない + Shizuku がインストールされていない + Shizuku のバージョンがサポートされていない + Shizuku 権限の取り消しに失敗しました + Shizuku の権限付与に失敗しました + Shizuku サービスが使用できない 合計: %d 件 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 6f2c24eb..2c41073f 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -700,6 +700,16 @@ 런처에 바로 가기 추가 런처에 바로가기를 고정하는 것은 지원되지 않습니다 템플릿 APK 경로 + Shizuku 를 사용하면 ADB 권한과 시스템 API 에 대한 액세스 권한을 얻을 수 있습니다 + Shizuku 권한 + Shizuku 애플리케이션에서 AutoJs6 권한 부여하기 + Shizuku 애플리케이션에서 AutoJs6 권한 취소하기 + 먼저 Shizuku 서비스를 실행해야 할 수 있습니다 + Shizuku 가 설치되지 않았습니다 + 지원되지 않는 Shizuku 버전 + Shizuku 권한 해지에 실패했습니다 + Shizuku 권한을 부여하지 못했습니다 + Shizuku 서비스를 사용할 수 없음 총: %d 개 항목 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 8512fb2e..0d66c082 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -698,7 +698,17 @@ Ярлыки позволяют выполнять определенные действия в AutoJs6. Пользователи могут отображать эти ярлыки в поддерживаемой программе запуска и быстро запускать некоторые задачи или действия, например, читать документацию по приложению AutoJs6, запускать страницу настроек AutoJs6 и т.д. Добавление ярлыков в пусковую установку Закрепление ярлыков в пусковой установке не поддерживается - Шаблон APK-путь + Шаблон APK путь + Shizuku позволяет получить привилегии ADB и доступ к системным API. + Разрешения Shizuku + Предоставление привилегий AutoJs6 в приложении Shizuku + Отмена привилегий AutoJs6 в приложении Shizuku + Возможно, сначала потребуется запустить службу Shizuku + Shizuku не установлен + Версия Shizuku не поддерживается + Не удалось отозвать права на Shizuku + Не удалось предоставить права на использование Shizuku + Невозможно использовать службу Shizuku Всего: %d элемент diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index c039e423..b2c1ba21 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -698,7 +698,17 @@ 快捷方式可以在 AutoJs6 中執行特定操作, 用户可以在啓動器中顯示這些快捷方式, 並快速開始某些任務或啓動某個活動, 如閲讀 AutoJs6 應用文檔, 啓動 AutoJs6 設置頁面等. 添加快捷方式到啓動器 將快捷方式固定到啓動器不被支持 - 模板APK路徑 + 模板 APK 路徑 + 通過 Shizuku 可以獲得 ADB 特權並使用系統 API + Shizuku 權限 + 在 Shizuku 應用中授予 AutoJs6 權限 + 在 Shizuku 應用中撤銷 AutoJs6 權限 + 可能需要先運行 Shizuku 服務 + Shizuku 未安裝 + Shizuku 版本不支持 + Shizuku 權限撤銷失敗 + Shizuku 權限授予失敗 + 無法使用 Shizuku 服務 共計: %d 項 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 483c455e..ad565b08 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -698,7 +698,17 @@ 快捷方式可以在 AutoJs6 中執行特定操作, 使用者可以在啟動器中顯示這些快捷方式, 並快速開始某些任務或啟動某個活動, 如閲讀 AutoJs6 應用文件, 啟動 AutoJs6 設定頁面等. 新增快捷方式到啟動器 將快捷方式固定到啟動器不被支援 - 模板APK路徑 + 模板 APK 路徑 + 透過 Shizuku 可以獲得 ADB 特權並使用系統 API + Shizuku 許可權 + 在 Shizuku 應用中授予 AutoJs6 許可權 + 在 Shizuku 應用中撤銷 AutoJs6 許可權 + 可能需要先執行 Shizuku 服務 + Shizuku 未安裝 + Shizuku 版本不支援 + Shizuku 許可權撤銷失敗 + Shizuku 許可權授予失敗 + 無法使用 Shizuku 服務 共計: %d 項 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index be3a3092..dfaa37fe 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -694,7 +694,17 @@ 快捷方式可以在 AutoJs6 中执行特定操作, 用户可以在启动器中显示这些快捷方式, 并快速开始某些任务或启动某个活动, 如阅读 AutoJs6 应用文档, 启动 AutoJs6 设置页面等. 添加快捷方式到启动器 将快捷方式固定到启动器不被支持 - 模板APK路径 + 模板 APK 路径 + 通过 Shizuku 可以获得 ADB 特权并使用系统 API + Shizuku 权限 + 在 Shizuku 应用中授予 AutoJs6 权限 + 在 Shizuku 应用中撤销 AutoJs6 权限 + 可能需要先运行 Shizuku 服务 + Shizuku 未安装 + Shizuku 版本不支持 + Shizuku 权限撤销失败 + Shizuku 权限授予失败 + 无法使用 Shizuku 服务 共计: %d 项 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8f535570..5821eea0 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -14,8 +14,8 @@ #111214 @color/md_teal_800 - #CC000000 #DFC0C0C0 + #CC000000 #FF43A047 #FF1976D2 #FFC62828 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e9c54852..d3f9ec97 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -907,7 +907,18 @@ Shortcuts can perform specific actions in AutoJs6. Users can display these shortcuts in a supported launcher and quickly start some tasks or launch an activity, such as reading AutoJs6 application documentation, launching AutoJs6 settings page, and so on. Add shortcuts to launcher Pinning shortcuts to launcher is not supported - template apk path + Template APK path + file:///android_asset/template.apk + Shizuku makes it possible to uses system API with ADB privileges + Shizuku access + Grant AutoJs6 access in Shizuku app + Revoke AutoJs6 access in Shizuku app + Shizuku service may need to be run first + Shizuku is not installed + Shizuku version is not supported + Failed to revoke Shizuku access + Failed to grant Shizuku access + Unable to use Shizuku service Total: %d item diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3797c119..2b4a5669 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -28,6 +28,10 @@ @style/PopupMenuTheme + + + + diff --git a/gradle.properties b/gradle.properties index b808343c..3724ba6c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,7 +34,7 @@ android.enableJetifier=true # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true -android.nonFinalResIds=false +android.nonFinalResIds=true # -- archived fields -- # Added since Gradle Build Tool version 8.0.0-alpha09 # Removed since Gradle Build Tool version 8.2.0-alpha15 diff --git a/libs/paddleocr/build.gradle b/libs/paddleocr/build.gradle index d82c1fa4..38f35802 100644 --- a/libs/paddleocr/build.gradle +++ b/libs/paddleocr/build.gradle @@ -68,6 +68,8 @@ android { dependencies { implementation fileTree(include: ["*.jar"], dir: "libs") implementation project(path: ":libs:org.opencv-${versionOpencv}") + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.preference:preference-ktx:1.2.1' } ext { diff --git a/libs/paddleocr/src/main/java/com/baidu/paddle/lite/ocr/Predictor.java b/libs/paddleocr/src/main/java/com/baidu/paddle/lite/ocr/Predictor.java index 1e49bc4b..e6aa8da3 100644 --- a/libs/paddleocr/src/main/java/com/baidu/paddle/lite/ocr/Predictor.java +++ b/libs/paddleocr/src/main/java/com/baidu/paddle/lite/ocr/Predictor.java @@ -3,13 +3,17 @@ package com.baidu.paddle.lite.ocr; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.preference.PreferenceManager; +import android.os.Build; import android.util.Base64; import android.util.Log; +import androidx.preference.PreferenceManager; + import java.io.File; import java.io.FileInputStream; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -20,8 +24,10 @@ import java.util.List; /** * @author PaddleOCR - * Modified by TonyJiangWJ * @since 2023-08-06 + * @noinspection unused + * Modified by TonyJiangWJ as of Aug 7, 2023. + * Modified by SuperMonster003 as of Oct 27, 2023. */ public class Predictor { private static final String TAG = Predictor.class.getSimpleName(); @@ -50,6 +56,7 @@ public class Predictor { public boolean runDet = false; public boolean runRec = true; + /** @noinspection SpellCheckingInspection*/ private static final String CHECK_IMG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAFQAAAA5CAYAAACoAQxFAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAqMSURBVHic7ZtrUJTVH8c/uzyIxHpLLgUiKOM4kZYUqVM4I5lW5IsUm14x2kz2JotxhkVDMbSmbBvUmhhzAsRmEF5oeKlmGk0MzNKGmClDZfICm+AukagtsBf2+b9g9vQ87oV9lvX2n/28Ovucy3P2y9lzfpeDTpZlmQhhQ3+3J/D/RkTQMBMRNMxEBA0zEUHDTETQMBMRNMxEBA0zEUHDTETQMBMRNMxEBA0zEUHDjHS3J3CvcfPmTZxOJwDjx49HkrRJNOIKdbvdWCyW0GYXAo2NjRQUFFBQUMDZs2dHbH/x4kX++uuvsL3/448/ZuXKlaxcuZJLly6J54ODg0H1Dyi/2Wzm008/paurC5PJREpKiqr+xIkTDA0NaZpwdnY2cXFxfuvtdjt9fX0AuFwuv+2GhoZoaGigtraWxMRETCYTEyZM0DQXX+j1/60xt9uNy+Vi9+7dnD59mnfffZcpU6YE7B9Q0L1793Lu3DkAysrKMJlMTJo0SdRv374dh8OhacKff/55QEGD5dq1azQ0NOByuejq6uLDDz9ky5YtjBkzZlTjKgWVZZmmpiYOHToEgNFopLS0lMzMTP/9f/nlF7+Vb7/9NhkZGQBcvXqVLVu20N/fP6oJe3A6ndTW1lJbW8v58+c194+Pj8doNKLT6QD4448/qKioYLQJiKioKFGWZZlnn32WVatWAfDvv/+yYcMGTp486be/NG3aNL+VsbGxbNy4kaKiInp7e/nzzz8xmUxs3LhRtVnPnTuXF154we84LS0tfPPNN6pnTqeT+vp6ACZNmsTMmTMDflFfzJkzh4KCAr788ksAjh07Rnp6OsuWLdM8lgfPHwiGf/IA+fn5xMbGsnPnTlwuF1u3bmXHjh1Mnz7dq78UHx8f8AXx8fGUlpaybt067HY7LS0tfPbZZxQWFoo2SUlJPPXUU37H6O3t1fq9gmbFihW0t7fz888/A7B7927S0tJ44oknQhpPuUKV50NeXh6yLLNnzx6MRqNPMSFIOzQjI4OioiIAJkyYwLx581R/ybuJTqejsLCQ5ORkYPhnevTo0ZDHi46OFmXPCvXw0ksvUVVVFXDxBG1kzZ8/n/Xr15OZmak6mG7lxIkTDAwMIEkSubm5wQ4/KgwGA++88w4bNmygoKCA559/HoCTJ09y8OBBTWO1tbWJ8kgH0Pz58722F8loNPL6668HtYc988wzI7apqanBYrEQGxt7xwQFSE9Pp7q6mpiYGPGsr69PJVAoBOqfnp7u9Uw6d+5c0EbrvY5STABJkoiNjdU0xsDAgOpzoP6+TDQJ4OGHH/bZwWKxcObMGRYtWqRpUvcKS5YsYcmSJZr61NTUsH//fgCKi4tZsGCBpv5SdHQ0vk76o0ePsmvXLgYHB3E4HLz44ouaBr6dXLt2zeevKioqisTExFGNrTyU7Ha75v5SRkaGyjvwEBMTIya9c+dODAaD5r/W7aKyspKmpiav58nJyezatWtUYyu3jVAE1S9cuNBnxYIFC4SHIMsy5eXltLS0hDDF+wvlvqjVrQbQL1682G9lfn4+eXl5wLCR+8EHHwjf/m7yyCOPkJubS25uLk8//XRYxx47dqwoh+JmSyMFE9544w0sFgstLS04HA4qKysxmUw+t4k7xdKlS0XZarX69a2dTifff/99UGMaDAZycnJUp7rNZtM8txEN+6ioKIxGI+vWrePRRx/ltddeu6tiasFut1NRURFU29TU1DsjKEBcXBzl5eVedp4vPP6v0ie+n1CGFm/cuKG5f9CuZzBiAiJ9oDQ/7hZjx46lrKxM9Uz5+c033yQhIQH4z4A3GAyiPiRBbTZbWAK+Hjwn470gqCRJPPnkk6pnycnJdHV1AcPZg1tt8HHjxonyP//8o/md+pqamqAa/vTTT3R0dARs43a7he2q1eW7Vxg/fryIpPX29mpO8UjZ2dlBNfziiy/o6ekhLS2N7du3+1yBN27cEBHzyZMna5rIvYJerychIQGr1Yosy1y/fp0HH3ww+P7z5s0bsVF3dzc9PT3A8F7q7+fsSa7B/SsowEMPPSTKVqtVU9+g7B9lCCtQJLyzs1OUR8oO3m7cbndIZg+og0VaBQ3qlP/9999Fefbs2aK8Z88e4L8DSJlsO3XqFMuXLwdg0aJF5OTkAPDAAw9ommCoVFZWkpmZKd6rBWW63Gw2e9VbLBYSEhJ82uMjrlBZlmltbQWGT01lINpgMGAwGIiJicHlcvHDDz+Iura2Nqqrq5FlmejoaNH2TjgFdXV1HD58mIsXL4bUf+rUqaKsvOzgobq6mtWrV/PVV1951enXrFmjWoG30tXVJcyH2bNn+7VHm5ubuX79uupZQ0MDFRUVXrkZGLYC6urqqKur47nnnvP7fq18/fXX7N27FyBkQZWR+La2Nq/UtMViwWq1+ox46Ts6OryEUPLbb7+JclZWls82fX19VFdXi8+rVq0S9tx3331HeXm51y0QnU4nVu1oLyd4sFqtInwnSZII7ASL51CdPHmyiKvevHlTdTbIsizsWE9iUIkE+Awwe/j1119FWbl/enC5XGzbtk1MZu7cueTn55OVlUVJSQk2m42mpibsdjvFxcVhE8+DxzPzzAWGxSwpKfGZnVTalQcPHsThcNDZ2cnly5ex2Wzilkh2djbffvstMHyvIC0tDRgObnvSJL4yHQFTIA6HQwhqMBi8ctEul4tPPvlE7LFxcXGsXr0agOnTp/Pee+9RWlqKzWbj1KlTvP/++5SUlKhCZIEECiZVfetFsaioKCHm+fPn6e7u5sqVK1y5cgWz2ay6+HbgwAFV31svb3gEPXbsGMuWLUOn06lWq68knX7ixIl+L1mdPXtWuJJZWVmqA6W/v5+tW7dy/Phx8eWLi4tVNtyMGTPYvHmz8JpaW1vZvHlzQHPmwoULohyM+3rkyBFR1ul0GI1GsTIrKiooLy+nvr6e5uZmLl++HHAspe38+OOPM3HiRAA6Ojr48ccfAfUv1mfWM5BZ4Vl5MHztxYPZbOajjz5SuaJr1qzxaaPOnDmTsrIyNm3ahN1u58yZM7S2tpKTk8Phw4cZGBggOjqaoaEhLl26RHNzs+jrCVz4w263q+ZQWFioSnU/9thjPk/ppKQkpk2bxtSpU0lNTWXKlCmkpKSo3GVJkli+fLk4G7Zt20ZjYyOeu2Djxo3zuo0IIL388st+J3z69GlRnjVrFjAcMFi7dq3It+h0OtauXRswB5+ZmcmmTZsoKyvj1VdfFbZhe3u7WOG3MmvWrBFdvpiYGEpKSigqKuKVV17xys5mZWXR09MjhEtJSSElJSXglqMkLy+PI0eOYDabcTqdKj0WLlzo0wTU+ft/+cHBQdavX8+FCxdITEykqqpK1NXX11NbW0tcXBxGo9ErouMPs9lMamqq+Lxv3z7hHCiZM2cOb731VtAZzPb2dmbMmHFbrgd1d3dTWlqq2nvj4+PZsWOHz63Sr6DKAa9evaoymdxuN1VVVSxdutTvgRYMf//9N52dnej1eiRJYsyYMSQlJYXl4mw46e/vp7GxEbPZTGJiIosXL1aF+ZSMKGgEbdwfyaH7iIigYSYiaJiJCBpmIoKGmYigYeZ/jd/+RcTqcugAAAAASUVORK5CYII="; private static final Bitmap checkingBitmap = BitmapFactory.decodeByteArray(Base64.decode(CHECK_IMG_BASE64, Base64.DEFAULT), 0, Base64.decode(CHECK_IMG_BASE64, Base64.DEFAULT).length); /** @@ -120,8 +127,6 @@ public class Predictor { /** * 初始化模型后通过识别预设图片校验是否初始化成功 * 曲线救国 深层的失败原因需要后续排查 - * - * @return */ private boolean checkModelLoadedSuccess() { if (!checkModelLoaded) { @@ -164,7 +169,6 @@ public class Predictor { return true; } - /** @noinspection deprecation*/ protected boolean loadModel(Context appCtx, String modelPath, int cpuThreadNum, String cpuPowerMode) { // Release model if exists releaseModel(); @@ -237,9 +241,14 @@ public class Predictor { wordLabels.add("black"); // Load word labels from file try { - InputStream labelInputStream = null; + InputStream labelInputStream; if (labelPath.startsWith("/")) { - labelInputStream = new FileInputStream(labelPath); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + labelInputStream = Files.newInputStream(Paths.get(labelPath)); + } else { + // noinspection IOStreamConstructor + labelInputStream = new FileInputStream(labelPath); + } } else { labelInputStream = appCtx.getAssets().open(labelPath); } diff --git a/settings.gradle.kts b/settings.gradle.kts index 4c7f2ddc..a60989d9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,8 +1,5 @@ @file:Suppress("SpellCheckingInspection") -import org.gradle.kotlin.dsl.support.uppercaseFirstChar - - enableFeaturePreview("STABLE_CONFIGURATION_CACHE") include( @@ -28,17 +25,7 @@ include( pluginManagement { - val platformIdentifierForAS = "AndroidStudio" - val platformIdentifierForIdea = "IntelliJIdea" - - val unknownIdentifier = "unknown" - val unknownIdentifier1Up = unknownIdentifier[0].uppercase() + unknownIdentifier.substring(1) - val previewIdentifier = "preview" - val previewIdentifier1Up = previewIdentifier[0].uppercase() + previewIdentifier.substring(1) - val predictedIdentifier = "predicted" - val predictedSuffix = " [$predictedIdentifier]" val fallbackIdentifier = "fallback" - val fallbackSuffix = " [$fallbackIdentifier]" // @Hint by SuperMonster003 on May 3, 2023. // ! To download archives of Android Studio, @@ -66,7 +53,7 @@ pluginManagement { "2022.1" to "E", /* May 3, 2023. */ ), "android" to mapOf( - "Preview2023.2" to "8.3.0-alpha10", /* Oct 24, 2023. */ + "Preview2023.2" to "8.3.0-alpha11", /* Oct 27, 2023. */ "2023.1" to "8.2.0-rc01", /* Oct 18, 2023. */ "Preview2023.1" to "8.2.0-beta06", /* Oct 11, 2023. */ "2022.3" to "8.1.2", /* Sep 29, 2023. */ @@ -90,13 +77,15 @@ pluginManagement { ), "idea" to mapOf( "android" to mapOf( + "Eap2023.3" to "8.1.2", /* Oct 25, 2023. */ "2023.2" to "8.1.2", /* Oct 18, 2023. */ "2023.1" to "7.4.2", /* May 26, 2023. */ "2022.3" to "7.4.0-beta02", /* Mar 25, 2023. */ fallbackIdentifier to "7.4.0", /* May 3, 2023. */ ), "kotlin" to mapOf( - "2023.2" to "1.9.20-RC", /* Oct 18, 2023. */ + "Eap2023.3" to "1.9.20-RC2", /* Oct 25, 2023. */ + "2023.2" to "1.9.20-RC2", /* Oct 18, 2023. */ "2023.1" to "1.8.21", /* Apr 25, 2023. */ "2022.3" to "1.8.21", /* Apr 25, 2023. */ fallbackIdentifier to "1.8.21", /* May 3, 2023. */ @@ -105,7 +94,7 @@ pluginManagement { ) val kspPluginVersionMap = mapOf( - // "1.9.20-RC2" to "1.0.13", /* Oct 25, 2023. */ + "1.9.20-RC2" to "1.0.13", /* Oct 26, 2023. */ "1.9.20-RC" to "1.0.13", /* Oct 12, 2023. */ "1.9.20-Beta2" to "1.0.13", /* Sep 29, 2023. */ "1.9.20-Beta" to "1.0.13", /* Sep 12, 2023. */ @@ -119,40 +108,60 @@ pluginManagement { fallbackIdentifier to "1.8.0-1.0.9", /* Aug 16, 2023. */ ) - val platformNicknameForAS = mapOf( - "Z" to "Zebra", /* Predicted on Oct 18, 2023. */ - "Y" to "Yeti", /* Predicted on Oct 18, 2023. */ - "X" to "Xiphias", /* Predicted on Oct 18, 2023. */ - "W" to "Walrus", /* Predicted on Oct 18, 2023. */ - "V" to "Vicuna", /* Predicted on Oct 18, 2023. */ - "U" to "Unicorn", /* Predicted on Oct 18, 2023. */ - "T" to "Tiger", /* Predicted on Oct 18, 2023. */ - "S" to "Snail", /* Predicted on Oct 18, 2023. */ - "R" to "Rhino", /* Predicted on Oct 18, 2023. */ - "Q" to "Quail", /* Predicted on Oct 18, 2023. */ - "P" to "Penguin", /* Predicted on Oct 18, 2023. */ - "O" to "Ostrich", /* Predicted on Oct 18, 2023. */ - "N" to "Newt", /* Predicted on Oct 18, 2023. */ - "M" to "Monkey", /* Predicted on Oct 18, 2023. */ - "L" to "Lion", /* Predicted on Oct 18, 2023. */ - "K" to "Koala", /* Predicted on Oct 18, 2023. */ - "J" to "Jaguar", /* Predicted on Oct 18, 2023. */ - "I" to "Iguana", /* Aug 25, 2023. */ - "H" to "Hedgehog", /* Apr 25, 2023. */ - "G" to "Giraffe", /* Jan 17, 2023. */ - "F" to "Flamingo", /* Sep 20, 2022. */ - "E" to "Electric Eel", /* May 11, 2022. */ - "D" to "Dolphin", /* Jan 31, 2022. */ - "C" to "Chipmunk", /* Oct 13, 2021. */ - "B" to "Bumblebee", /* May 18, 2021. */ - "A" to "Arctic Fox", /* Jan 26, 2021. */ + val platformCodenameForAS = mapOf( + "I" to "Iguana", /* Born on Aug 25, 2023. */ + "H" to "Hedgehog", /* Born on Apr 25, 2023. */ + "G" to "Giraffe", /* Born on Jan 17, 2023. */ + "F" to "Flamingo", /* Born on Sep 20, 2022. */ + "E" to "Electric Eel", /* Born on May 11, 2022. */ + "D" to "Dolphin", /* Born on Jan 31, 2022. */ + "C" to "Chipmunk", /* Born on Oct 13, 2021. */ + "B" to "Bumblebee", /* Born on May 18, 2021. */ + "A" to "Arctic Fox", /* Born on Jan 26, 2021. */ + + /* Codenames below were predicted on Oct 18, 2023. */ + + "J" to "Jaguar", + "K" to "Koala", + "L" to "Lion", + "M" to "Monkey", + "N" to "Newt", + "O" to "Ostrich", + "P" to "Penguin", + "Q" to "Quail", + "R" to "Rhino", + "S" to "Snail", + "T" to "Tiger", + "U" to "Unicorn", + "V" to "Vicuna", + "W" to "Walrus", + "X" to "Xiphias", + "Y" to "Yeti", + "Z" to "Zebra", ) - /* Null safety. */ - val platform = System.getProperty("idea.paths.selector") ?: platformIdentifierForAS + val platformIdentifierForAS = "AndroidStudio" + val vendorNameForAS = "Google" + val platformIdentifierForIdea = "IntelliJIdea" + val vendorNameForIdea = "Jetbrains" - val isPlatformAS = platform.startsWith(platformIdentifierForAS) - val isPlatformIdea = platform.startsWith(platformIdentifierForIdea) + fun uppercaseFirstChar(s: String) = s[0].uppercase() + s.substring(1) + + val unknownIdentifier = "unknown" + val unknownIdentifier1Up = uppercaseFirstChar(unknownIdentifier) + val predictedIdentifier = "predicted" + val unfocusedIdentifier = "unfocused" + val unfocusedSuffix = " [$unfocusedIdentifier]" + val predictedSuffix = " [$predictedIdentifier]" + val fallbackSuffix = " [$fallbackIdentifier]" + + /* Nullable. */ + val platform = System.getProperty("idea.paths.selector") ?: System.getProperty("idea.platform.prefix") + + val isPlatformAS = platform?.startsWith(platformIdentifierForAS) == true + || System.getProperty("idea.vendor.name").equals(vendorNameForAS, true) + val isPlatformIdea = platform?.startsWith(platformIdentifierForIdea) == true + || System.getProperty("idea.vendor.name").equals(vendorNameForIdea, true) val platformType = when { isPlatformAS -> "as" @@ -160,12 +169,29 @@ pluginManagement { else -> throw Exception("$unknownIdentifier1Up platform: $platform") } - val platformVersion = when { - isPlatformAS -> platform.substring(platformIdentifierForAS.length) - isPlatformIdea -> platform.substring(platformIdentifierForIdea.length) - else -> "$unknownIdentifier1Up platform version" /* Should never happen. */ + val previewIdentifier = when { + isPlatformAS -> "preview" + isPlatformIdea -> "eap" + else -> "" } + val previewIdentifier1Up = uppercaseFirstChar(previewIdentifier) + + val platformVersion = (System.getProperty("idea.version") ?: when { + isPlatformAS -> platform?.substring(platformIdentifierForAS.length) + isPlatformIdea -> platform?.substring(platformIdentifierForIdea.length) + else -> null + } ?: throw Exception("$unknownIdentifier1Up platform version")) + .let { rawVersion -> + platform?.let { rawVersion } + ?: when (rawVersion.contains(previewIdentifier, true)) { + true -> rawVersion + else -> "$previewIdentifier1Up$rawVersion" + } + } + + val platformVersionUnfocused = platformVersion.replace(Regex("(.+)(\\.\\d+)(\\.\\d+$)"), "$1$2") + val abbrMap = gradlePluginVersionMap[platformType]!!["abbr"] ?: mapOf() val androidMap = gradlePluginVersionMap[platformType]!!["android"]!! val kotlinMap = gradlePluginVersionMap[platformType]!!["kotlin"]!! @@ -193,11 +219,11 @@ pluginManagement { when { isPlatformAS -> { var isPredicted = false - val platformNickAbbr = abbrMap[platformVersion] ?: abbrMap["$previewIdentifier1Up$platformVersion"]?.also { + val platformCodenameAbbr = abbrMap[platformVersion] ?: abbrMap["$previewIdentifier1Up$platformVersion"]?.also { isPredicted = true } - val platformNick = platformNickAbbr?.let { abbr -> - platformNicknameForAS[abbr]?.let { + val platformCodeName = platformCodenameAbbr?.let { abbr -> + platformCodenameForAS[abbr]?.let { when (isPredicted) { true -> " [ $it ($predictedIdentifier) ]" else -> " $it" @@ -209,33 +235,49 @@ pluginManagement { val previewSuffix = if (isPreview) " ($previewIdentifier)" else "" val niceVersion = if (isPreview) platformVersion.substring(previewIdentifier1Up.length) else platformVersion - "Android Studio$platformNick$previewSuffix | $niceVersion" + "Android Studio$platformCodeName$previewSuffix${if (niceVersion.isNotEmpty()) " | $niceVersion" else ""}" + } + isPlatformIdea -> { + val isEap = platformVersion.contains(previewIdentifier1Up, true) + val isPredicted = platform == null + val suffix = when (isEap) { + true -> when (isPredicted) { + true -> " [ ${previewIdentifier.uppercase()} ($predictedIdentifier) ]" + else -> " ${previewIdentifier.uppercase()}" + } + else -> when (isPredicted) { + true -> " ($predictedIdentifier)" + else -> "" + } + } + val niceVersion = if (isEap) platformVersion.substring(previewIdentifier1Up.length) else platformVersion + + "IntelliJ IDEA$suffix${if (niceVersion.isNotEmpty()) " | $niceVersion" else ""}" } - isPlatformIdea -> "IntelliJ IDEA $platformVersion" else -> unknownIdentifier1Up }.let { infoList += "Platform: $it" } } dependencies /* Android/Kotlin Gradle Plugin. */ { arrayOf( - arrayOf( - /* classpath = */ "com.android.tools.build:gradle", - /* version = */ androidMap[platformVersion], - /* versionPredicted = */ androidMap["$previewIdentifier1Up$platformVersion"], - /* versionFallback = */ androidMap[fallbackIdentifier], - ), - arrayOf( - /* classpath = */ "org.jetbrains.kotlin:kotlin-gradle-plugin", - /* version = */ kotlinMap[platformVersion], - /* versionPredicted = */ kotlinMap["$previewIdentifier1Up$platformVersion"], - /* versionFallback = */ kotlinMap[fallbackIdentifier], - ), + arrayOf("com.android.tools.build:gradle", androidMap), + arrayOf("org.jetbrains.kotlin:kotlin-gradle-plugin", kotlinMap), ).forEach { - val (classpath, version, versionPredicted, versionFallback) = it + val (classpath, map) = it + + map as? Map<*, *> ?: throw Exception("Invalid map for classpath") + + val version = map[platformVersion] as? String + val versionUnfocused = map[platformVersionUnfocused] as? String + val versionPredicted = (map["$previewIdentifier1Up$platformVersion"] + ?: map["$previewIdentifier1Up$platformVersionUnfocused"]) as? String + val versionFallback = map[fallbackIdentifier] as? String var suffix = "" val classpathNotation = "$classpath:" + ( - version ?: versionPredicted?.also { + version ?: versionUnfocused?.also { + suffix += unfocusedSuffix + } ?: versionPredicted?.also { suffix += predictedSuffix } ?: versionFallback?.also { suffix += fallbackSuffix @@ -249,25 +291,27 @@ pluginManagement { } plugins { - arrayOf( - arrayOf( - /* id = */ "com.google.devtools.ksp", - /* kspVersionKey = */ kotlinMap[platformVersion], - /* kspVersionPredictedKey = */ kotlinMap["$previewIdentifier1Up$platformVersion"], - /* kspVersionFallbackKey = */ kotlinMap[fallbackIdentifier], - /* isApply = */ false, - ), - ).forEach { data -> - val id = data[0] as String - val kspVersionKey = data[1] as String? - val kspVersionPredictedKey = data[2] as String? - val kspVersionFallbackKey = data[3] as String - val isApply = data[4] as Boolean + + arrayOf("com.google.devtools.ksp", kotlinMap, false).let { data -> + var (id, map, isApply) = data + + id as? String ?: throw Exception("Invalid id for plugin") + map as? Map<*, *> ?: throw Exception("Invalid map for plugin") + isApply = isApply as? Boolean ?: false + + val kspVersionKey = map[platformVersion] as String? + val kspVersionUnfocusedKey = map[platformVersionUnfocused] as String? + val kspVersionPredictedKey = (map["$previewIdentifier1Up$platformVersion"] + ?: map["$previewIdentifier1Up$platformVersionUnfocused"]) as String? + val kspVersionFallbackKey = map[fallbackIdentifier] as String var suffix = "" val pluginVersion = kspPluginVersionMap[kspVersionKey]?.let { "$kspVersionKey-$it" + } ?: kspPluginVersionMap[kspVersionUnfocusedKey]?.let { + suffix += unfocusedSuffix + "$kspVersionUnfocusedKey-$it" } ?: kspPluginVersionMap[kspVersionPredictedKey]?.let { suffix += predictedSuffix "$kspVersionPredictedKey-$it" @@ -277,7 +321,7 @@ pluginManagement { } ?: throw Exception("$unknownIdentifier1Up version for plugin $id") infoList += "Plugin: \"$id:$pluginVersion\"$suffix" - id(id) version pluginVersion apply isApply + id(id) version pluginVersion apply isApply as Boolean } } diff --git a/version.properties b/version.properties index 1380ff39..6cf4aecb 100644 --- a/version.properties +++ b/version.properties @@ -1,8 +1,9 @@ -#Wed Oct 25 13:19:15 CST 2023 -BUILD_TIME=1698211155142 +#Mon Oct 30 17:13:29 CST 2023 +BUILD_TIME=1698657209970 CMAKE_VERSION=3.10.2 COMPILE_SDK_VERSION=34 JAVA_VERSION=22 +JAVA_VERSION_MIN_RADICAL=21 JAVA_VERSION_MIN_SUGGESTED=19 JAVA_VERSION_MIN_SUPPORTED=17 MIN_SDK_VERSION=24 @@ -10,6 +11,6 @@ NDK_VERSION=21.1.6352462 OPENCV_VERSION=4.8.0 TARGET_SDK_VERSION=34 TARGET_SDK_VERSION_INRT=29 -VERSION_BUILD=2133 -VERSION_NAME=6.4.0 Alpha3 +VERSION_BUILD=2149 +VERSION_NAME=6.4.0 VSCODE_EXT_REQUIRED_VERSION=1.0.7