6.4.0 Pre - 新增 barcode/qrcode/shizuku 等模块; 适配高版本系统语言设置; 优化打包功能; 支持应用快捷方式; 修复屏幕旋转相关问题

This commit is contained in:
SuperMonster003 committed 2023-11-12 23:27:46 +08:00
1 parent 5ee2f62d89
commit 4b64922a0b
194 files changed
+2719 -1493

No files matched your search

+13 -5
View File
@@ -131,26 +131,33 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 Dec 1, 2021 进行二次开发
[comment]: <> "Version history only shows last 3 versions" [comment]: <> "Version history only shows last 3 versions"
# v6.3.4 # v6.4.0
###### 2023/10/03 ###### 2023/10/25
* `新增` ocr 模块支持 Paddle Lite 引擎 (by [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`pr #120`](http://pr.autojs6.com/120)_ * `新增` ocr 模块支持 Paddle Lite 引擎 (by [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`pr #120`](http://pr.autojs6.com/120)_
* `新增` WebSocket 模块 (参阅 项目文档 > [颜色类](https://docs.autojs6.com/#/webSocketType)) * `新增` 打包功能支持内置插件与外部插件两种打包方式 (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))
* `新增` device.rotation / device.orientation 等方法 * `新增` device.rotation / device.orientation 等方法
* `新增` 内部 Java 类支持 class 静态属性访问 * `新增` 内部 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 插件保存文件到本地时文件扩展名信息丢失的问题 * `修复` VSCode 插件保存文件到本地时文件扩展名信息丢失的问题
* `修复` 使用协程特性运行项目产生未捕获异常致使应用崩溃的问题 * `修复` 使用协程特性运行项目产生未捕获异常致使应用崩溃的问题
* `修复` 重启或退出应用时浮动按钮无法记录其位置状态信息的问题 * `修复` 重启或退出应用时浮动按钮无法记录其位置状态信息的问题
* `修复` 设备屏幕方向改变时无法获取更新后的设备配置信息的问题 * `修复` 设备屏幕方向改变时无法获取更新后的设备配置信息的问题 _[`issue #153`](http://issues.autojs6.com/153)_
* `修复` 屏幕旋转至横向时 Toolbar 标题字体过小的问题 * `修复` 屏幕旋转至横向时 Toolbar 标题字体过小的问题
* `修复` 屏幕旋转至横向时应用主页的页签排版过于拥挤的问题 * `修复` 屏幕旋转至横向时应用主页的页签排版过于拥挤的问题
* `修复` 屏幕旋转至横向时浮动按钮可能溢出屏幕的问题 _[`issue #90`](http://issues.autojs6.com/90)_ * `修复` 屏幕旋转至横向时浮动按钮可能溢出屏幕的问题 _[`issue #90`](http://issues.autojs6.com/90)_
* `修复` 屏幕多次旋转时无法恢复浮动按钮的坐标及屏幕侧边方向的问题 * `修复` 屏幕多次旋转时无法恢复浮动按钮的坐标及屏幕侧边方向的问题
* `修复` 部分设备消息浮动框可能出现遗漏显示或重复显示的问题
* `修复` 消息浮动框在多个脚本同时运行时可能存在被遮蔽的问题 _[`issue #67`](http://issues.autojs6.com/67)_ * `修复` 消息浮动框在多个脚本同时运行时可能存在被遮蔽的问题 _[`issue #67`](http://issues.autojs6.com/67)_
* `修复` 使用广播分析布局时点击布局无法弹出菜单且导致应用崩溃的问题 * `修复` 使用广播分析布局时点击布局无法弹出菜单且导致应用崩溃的问题
* `修复` 第二次及以后创建的 WebSocket 实例均无法正常触发监听器的问题 * `修复` 第二次及以后创建的 WebSocket 实例均无法正常触发监听器的问题
* `修复` 重新合并部分 PR (by [aiselp](https://github.com/aiselp)) 以解决部分脚本无法正常结束运行的问题 _[`pr #75`](http://pr.autojs6.com/75)_ _[`pr #78`](http://pr.autojs6.com/78)_
* `修复` 撤销 importPackage 的全局重定向方法以避免某些作用域下的包导入异常 _[`issue #88`](http://issues.autojs6.com/88)_ * `修复` 撤销 importPackage 的全局重定向方法以避免某些作用域下的包导入异常 _[`issue #88`](http://issues.autojs6.com/88)_
* `修复` 日志活动页面使用复制或导出功能时可能导致应用崩溃的问题 * `修复` 日志活动页面使用复制或导出功能时可能导致应用崩溃的问题
* `优化` 日志活动页面导出功能重命名为发送功能并重新实现符合实际意义的导出功能 * `优化` 日志活动页面导出功能重命名为发送功能并重新实现符合实际意义的导出功能
@@ -158,6 +165,7 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 Dec 1, 2021 进行二次开发
* `优化` ocr 模块同时兼容 Google MLKit 及 Paddle Lite 引擎 (参阅 项目文档 > [光学字符识别](https://docs.autojs6.com/#/ocr?id=p-mode)) * `优化` ocr 模块同时兼容 Google MLKit 及 Paddle Lite 引擎 (参阅 项目文档 > [光学字符识别](https://docs.autojs6.com/#/ocr?id=p-mode))
* `优化` 提升无障碍服务自动启动的成功概率 * `优化` 提升无障碍服务自动启动的成功概率
* `优化` 构建工具自适应 Java 发行版本以尽量避免 "无效的发行版本" 问题 * `优化` 构建工具自适应 Java 发行版本以尽量避免 "无效的发行版本" 问题
* `优化` 构建工具优化 IDE 及相关插件的版本退级逻辑并增加版本预测能力
* `优化` 部分 Kotlin 注解处理由 kapt 迁移至 KSP * `优化` 部分 Kotlin 注解处理由 kapt 迁移至 KSP
# v6.3.3 # v6.3.3
+162 -81
View File
@@ -2,17 +2,30 @@
import com.android.build.gradle.internal.api.ApplicationVariantImpl import com.android.build.gradle.internal.api.ApplicationVariantImpl
import com.android.build.gradle.internal.api.BaseVariantOutputImpl import com.android.build.gradle.internal.api.BaseVariantOutputImpl
import org.gradle.kotlin.dsl.support.uppercaseFirstChar
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.Date
import java.util.Locale
import java.util.Properties
import java.util.TimeZone
import java.util.zip.CRC32 import java.util.zip.CRC32
import kotlin.text.RegexOption.IGNORE_CASE
val applicationId = "org.autojs.autojs6" val applicationId = "org.autojs.autojs6"
val sign = Sign("$rootDir/sign.properties") val sign = Sign("$rootDir/sign.properties")
val versions = Versions("$rootDir/version.properties") val versions = Versions("$rootDir/version.properties")
val dimention = "channel"
val flavorNameApp = "app"
val flavorNameInrt = "inrt"
val buildTypeDebug = "debug"
val buildTypeRelease = "release"
val buildActionAssemble = "assemble"
val templateName = "template"
plugins { plugins {
id("com.android.application") id("com.android.application")
id("com.google.devtools.ksp") id("com.google.devtools.ksp")
@@ -27,7 +40,7 @@ dependencies /* Unclassified */ {
// Android supports // Android supports
implementation("androidx.cardview:cardview:1.0.0") implementation("androidx.cardview:cardview:1.0.0")
implementation("androidx.multidex:multidex:2.0.1") implementation("androidx.multidex:multidex:2.0.1")
implementation("com.google.android.material:material:1.9.0") implementation("com.google.android.material:material:1.10.0")
// SwipeRefreshLayout // SwipeRefreshLayout
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
@@ -103,11 +116,8 @@ dependencies /* Unclassified */ {
// Dex // Dex
implementation(files("$rootDir/libs/com.android.dx-1.7.0.jar")) implementation(files("$rootDir/libs/com.android.dx-1.7.0.jar"))
// LocaleHelper
implementation(project(":libs:com.zeugmasolutions.localehelper-1.5.1"))
// OpenCV // OpenCV
implementation(project(":libs:org.opencv-4.5.5")) implementation(project(":libs:org.opencv-4.8.0"))
// PaddleOCR // PaddleOCR
implementation(project(":libs:paddleocr")) implementation(project(":libs:paddleocr"))
@@ -132,9 +142,13 @@ dependencies /* Unclassified */ {
implementation(files("$rootDir/libs/javamail-android/mail.jar")) implementation(files("$rootDir/libs/javamail-android/mail.jar"))
// Shizuku // Shizuku
implementation("dev.rikka.shizuku:api:13.1.5") implementation(project(":libs:dev.rikka.shizuku-shared-13.1.5"))
implementation("dev.rikka.shizuku:provider:13.1.5") implementation(project(":libs:dev.rikka.shizuku-aidl-13.1.5"))
implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3@aar") implementation(project(":libs:dev.rikka.shizuku-api-13.1.5"))
implementation(project(":libs:dev.rikka.shizuku-provider-13.1.5"))
// ARSCLib
implementation("io.github.reandroid:ARSCLib:1.2.4")
} }
dependencies /* Test */ { dependencies /* Test */ {
@@ -159,12 +173,22 @@ dependencies /* Annotations */ {
} }
dependencies /* AppCompat */ { dependencies /* AppCompat */ {
implementation("androidx.appcompat:appcompat") { // @Obsoleted by SuperMonster003 on Oct 5, 2023.
version { // ! Comment with related code will be removed since Jan 5, 2024.
strictly("1.4.2") //
because("version 1.5.0 duplicates some classes") // @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.
} // implementation("androidx.appcompat:appcompat") {
// version {
// strictly("1.4.2")
// because("version 1.5.0 duplicates some classes")
// }
// }
//
// @Hint by SuperMonster003 on Oct 5, 2023.
// ! To check the releases for Appcompat library,
// ! visit https://developer.android.com/jetpack/androidx/releases/appcompat
implementation("androidx.appcompat:appcompat:1.6.1")
// AppCompat for legacy views (such as JsTextViewLegacy) // AppCompat for legacy views (such as JsTextViewLegacy)
implementation(project(":libs:androidx.appcompat-1.0.2")) { implementation(project(":libs:androidx.appcompat-1.0.2")) {
@@ -213,18 +237,13 @@ dependencies /* View */ {
dependencies /* GitHub API */ { dependencies /* GitHub API */ {
implementation(files("$rootDir/libs/github-api-1.306.jar")) implementation(files("$rootDir/libs/github-api-1.306.jar"))
implementation("commons-io:commons-io") { implementation("commons-io:commons-io:2.14.0")
version {
strictly("2.8.0")
because("Exception on newer versions: 'NoClassDefFoundError: org.apache.commons.io.IOUtils'")
}
}
implementation("com.fasterxml.jackson.core:jackson-databind") { implementation("com.fasterxml.jackson.core:jackson-databind") {
because("compatibility for Android API Level < 26 (Android 8.0) [O]") because("compatibility for Android API Level < 26 (Android 8.0) [O]")
version { version /* as of Oct 12, 2023 */ {
strictly("2.13.3") strictly("2.14.3")
because("Exception on 2.14.x: 'No virtual method getParameterCount()I in class Ljava/lang/reflect/Method'") because("Exception on 2.15.x: 'IllegalArgumentException: Unsupported class file major version 63'")
} }
} }
@@ -234,7 +253,11 @@ dependencies /* GitHub API */ {
} }
dependencies /* MLKit */ { dependencies /* MLKit */ {
// OCR
implementation("com.google.mlkit:text-recognition-chinese:16.0.0") implementation("com.google.mlkit:text-recognition-chinese:16.0.0")
// Barcode
implementation("com.google.mlkit:barcode-scanning:17.2.0")
} }
dependencies /* Auto.js Extensions */ { dependencies /* Auto.js Extensions */ {
@@ -257,7 +280,7 @@ dependencies /* Auto.js Extensions */ {
// Extracted from com.github.hyb1996:MutableTheme:1.0.0 // Extracted from com.github.hyb1996:MutableTheme:1.0.0
// @Legacy com.jrummyapps:colorpicker:2.1.7 // @Legacy com.jrummyapps:colorpicker:2.1.7
implementation("com.jaredrummler:colorpicker:1.1.0") implementation("com.jaredrummler:colorpicker:1.1.0")
implementation("androidx.recyclerview:recyclerview:1.3.1") implementation("androidx.recyclerview:recyclerview:1.3.2")
implementation("com.github.ozodrukh:CircularReveal:2.0.1") implementation("com.github.ozodrukh:CircularReveal:2.0.1")
} }
@@ -280,6 +303,7 @@ dependencies /* Reserved for auto append by IDE */ {
} }
android { android {
namespace = applicationId namespace = applicationId
compileSdk = versions.sdkVersionCompile compileSdk = versions.sdkVersionCompile
@@ -288,45 +312,95 @@ android {
minSdk = versions.sdkVersionMin minSdk = versions.sdkVersionMin
targetSdk = versions.sdkVersionTarget targetSdk = versions.sdkVersionTarget
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("boolean", "isInrt", "false") multiDexEnabled = true
buildConfigField("boolean", "is${flavorNameInrt.uppercaseFirstChar()}", "false")
} }
flavorDimensions.add("channel") flavorDimensions.add(dimention)
productFlavors { productFlavors {
create("app") {
dimension = "channel" create(flavorNameApp) {
dimension = dimention
versionCode = versions.appVersionCode versionCode = versions.appVersionCode
versionName = versions.appVersionName versionName = versions.appVersionName
buildConfigField("String", "CHANNEL", "\"app\"") buildConfigField("String", "CHANNEL", "\"$flavorNameApp\"")
buildConfigField("boolean", "isInrt", "false") buildConfigField("boolean", "is${flavorNameInrt.uppercaseFirstChar()}", "false")
manifestPlaceholders.putAll( manifestPlaceholders.putAll(
mapOf( mapOf(
"CHANNEL" to "app", "CHANNEL" to flavorNameApp,
"appName" to "@string/app_name", "appName" to "@string/app_name",
"intentCategory" to "android.intent.category.LAUNCHER", "intentCategory" to "android.intent.category.LAUNCHER",
"intentCategoryInrt" to "android.intent.category.DEFAULT", "intentCategoryInrt" to "android.intent.category.DEFAULT",
"authorities" to "org.autojs.autojs6.fileprovider" "authorities" to "org.autojs.autojs6.fileprovider",
"icon" to "@drawable/autojs6_material",
) )
) )
} }
create("inrt") { create(flavorNameInrt) {
dimension = "channel" dimension = dimention
applicationIdSuffix = ".inrt" applicationIdSuffix = ".$flavorNameInrt"
minSdk = versions.sdkVersionMin
targetSdk = versions.sdkVersionTargetInrt
compileSdk = versions.sdkVersionCompile
versionCode = versions.appVersionCode versionCode = versions.appVersionCode
versionName = versions.appVersionName versionName = versions.appVersionName
buildConfigField("String", "CHANNEL", "\"inrt\"") buildConfigField("String", "CHANNEL", "\"$flavorNameInrt\"")
buildConfigField("boolean", "isInrt", "true") buildConfigField("boolean", "is${flavorNameInrt.uppercaseFirstChar()}", "true")
manifestPlaceholders.putAll( manifestPlaceholders.putAll(
mapOf( mapOf(
"CHANNEL" to "inrt", "CHANNEL" to flavorNameInrt,
"appName" to "AutoJs6.inrt", "appName" to "AutoJs6.$flavorNameInrt",
"intentCategory" to "android.intent.category.DEFAULT", "intentCategory" to "android.intent.category.DEFAULT",
"intentCategoryInrt" to "android.intent.category.LAUNCHER", "intentCategoryInrt" to "android.intent.category.LAUNCHER",
"authorities" to "org.autojs.autojs6.inrt.fileprovider" "authorities" to "org.autojs.autojs6.$flavorNameInrt.fileprovider",
"icon" to "@mipmap/ic_launcher",
) )
) )
gradle.taskGraph.whenReady(object : Action<TaskExecutionGraph> {
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
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 ""}")
}
}
}
})
} }
androidResources {
if (gradle.startParameter.taskNames.any { it.contains(Regex("^(:?$flavorNameApp:)?$buildActionAssemble")) }) {
ignoreAssetsPatterns.add(".idea")
}
if (gradle.startParameter.taskNames.any { it.contains(Regex("^(:?$flavorNameApp:)?$buildActionAssemble$flavorNameInrt", IGNORE_CASE)) }) {
// @Hint by SuperMonster003 on Oct 16, 2023.
// ! Nothing needs to be added into assets for flavor "inrt",
// ! as assets will be copied from flavor "app"
// ! while building an apk on org.autojs.autojs.ui.project.BuildActivity.
ignoreAssetsPatterns.add("!*") /* Ignore everything. */
}
}
} }
compileOptions { compileOptions {
@@ -337,8 +411,8 @@ android {
// Utils.Version(ANDROID_GRADLE_PLUGIN_VERSION) < Utils.Version("8.0") // Utils.Version(ANDROID_GRADLE_PLUGIN_VERSION) < Utils.Version("8.0")
@Suppress("DEPRECATION") // @Legacy packagingOptions { ... }
packagingOptions { packaging {
arrayOf( arrayOf(
"META-INF/DEPENDENCIES", "META-INF/DEPENDENCIES",
"META-INF/LICENSE", "META-INF/LICENSE",
@@ -350,7 +424,12 @@ android {
"META-INF/notice.*", "META-INF/notice.*",
"META-INF/ASL2.0", "META-INF/ASL2.0",
"META-INF/*.kotlin_module", "META-INF/*.kotlin_module",
"lib/x86/libc++_shared.so",
"lib/x86_64/libc++_shared.so",
"lib/armeabi-v7a/libc++_shared.so",
"lib/arm64-v8a/libc++_shared.so",
).let { resources.pickFirsts.addAll(it) } ).let { resources.pickFirsts.addAll(it) }
jniLibs { jniLibs {
useLegacyPackaging = true useLegacyPackaging = true
} }
@@ -366,8 +445,8 @@ android {
} }
signingConfigs { signingConfigs {
takeIf { sign.isValid }?.let { if (sign.isValid) {
create("release") { create(buildTypeRelease) {
storeFile = sign.properties["storeFile"]?.let { file(it as String) } storeFile = sign.properties["storeFile"]?.let { file(it as String) }
keyPassword = sign.properties["keyPassword"] as String keyPassword = sign.properties["keyPassword"] as String
keyAlias = sign.properties["keyAlias"] as String keyAlias = sign.properties["keyAlias"] as String
@@ -382,23 +461,23 @@ android {
"proguard-rules.pro", "proguard-rules.pro",
) )
val niceSigningConfig = takeIf { sign.isValid }?.let { val niceSigningConfig = takeIf { sign.isValid }?.let {
signingConfigs.getByName("release") signingConfigs.getByName(buildTypeRelease)
} }
getByName("release") { getByName(buildTypeRelease) {
isMinifyEnabled = false isMinifyEnabled = false
proguardFiles(*proguardFiles) proguardFiles(*proguardFiles)
niceSigningConfig?.let { signingConfig = it } niceSigningConfig?.let { signingConfig = it }
} }
getByName("debug") { getByName(buildTypeDebug) {
isMinifyEnabled = getByName("release").isMinifyEnabled isMinifyEnabled = getByName(buildTypeRelease).isMinifyEnabled
proguardFiles(*proguardFiles) proguardFiles(*proguardFiles)
niceSigningConfig?.let { signingConfig = it } niceSigningConfig?.let { signingConfig = it }
} }
} }
buildFeatures { buildFeatures {
aidl = true
viewBinding = true viewBinding = true
// @Hint by SuperMonster003 on Aug 14, 2023. // @Hint by SuperMonster003 on Aug 14, 2023.
// ! Substitution of "android.defaults.buildfeatures.buildconfig=true" // ! Substitution of "android.defaults.buildfeatures.buildconfig=true"
buildConfig = true buildConfig = true
@@ -459,6 +538,7 @@ android {
isUniversalApk = true isUniversalApk = true
} }
} }
} }
tasks { tasks {
@@ -477,24 +557,30 @@ tasks {
} }
register<Copy>("appendDigestToReleasedFiles") { register<Copy>("appendDigestToReleasedFiles") {
val src = "release" listOf(flavorNameApp, flavorNameInrt).forEach { flavorName ->
val dst = "${src}s" val src = "$flavorName/$buildTypeRelease"
val ext = Utils.FILE_EXTENSION_APK val dst = "${src}s"
val ext = Utils.FILE_EXTENSION_APK
from(src); into(dst); include("*.$ext") if (!file(src).isDirectory) {
return@forEach
rename { name ->
Utils.digestCRC32(file("${src}/$name")).let { digest ->
name.replace("^(.+?)(\\.$ext)\$".toRegex(), "\$1-$digest\$2")
} }
}
doLast { println("Destination: ${file(dst)}") } from(src); into(dst); include("*.$ext")
rename { name ->
Utils.digestCRC32(file("${src}/$name")).let { digest ->
name.replace("^(.+?)(\\.$ext)\$".toRegex(), "\$1-$digest\$2")
}
}
doLast { println("Destination: ${file(dst)}") }
}
} }
} }
extra { extra {
versions.handleIfNeeded(project) versions.handleIfNeeded(project, flavorNameApp, listOf(buildTypeDebug, buildTypeRelease))
} }
class Sign(filePath: String) { class Sign(filePath: String) {
@@ -523,6 +609,7 @@ class Versions(filePath: String) {
val sdkVersionMin = properties["MIN_SDK_VERSION"].let { it as String }.toInt() val sdkVersionMin = properties["MIN_SDK_VERSION"].let { it as String }.toInt()
val sdkVersionTarget = properties["TARGET_SDK_VERSION"].let { it as String }.toInt() val sdkVersionTarget = properties["TARGET_SDK_VERSION"].let { it as String }.toInt()
val sdkVersionTargetInrt = properties["TARGET_SDK_VERSION_INRT"].let { it as String }.toInt()
val sdkVersionCompile = properties["COMPILE_SDK_VERSION"].let { it as String }.toInt() val sdkVersionCompile = properties["COMPILE_SDK_VERSION"].let { it as String }.toInt()
val appVersionName = properties["VERSION_NAME"] as String val appVersionName = properties["VERSION_NAME"] as String
val appVersionCode = properties["VERSION_BUILD"].let { it as String }.toInt() val appVersionCode = properties["VERSION_BUILD"].let { it as String }.toInt()
@@ -559,7 +646,6 @@ class Versions(filePath: String) {
var versionInt = javaVersionRaw.toInt() var versionInt = javaVersionRaw.toInt()
while (versionInt > javaVersionMinSupported) { while (versionInt > javaVersionMinSupported) {
@Suppress("EnumValuesSoftDeprecate")
if (JvmTarget.values().any { it.name.contains(Regex("_$versionInt$")) }) { if (JvmTarget.values().any { it.name.contains(Regex("_$versionInt$")) }) {
break break
} }
@@ -590,15 +676,14 @@ class Versions(filePath: String) {
private var isBuildNumberAutoIncremented = false private var isBuildNumberAutoIncremented = false
private val minBuildTimeGap = Utils.hours2Millis(0.75) private val minBuildTimeGap = Utils.hours2Millis(0.75)
private val assembleTargets = mapOf("app" to listOf("debug", "release"))
private val isBuildGapEnough private val isBuildGapEnough
get() = properties["BUILD_TIME"]?.let { get() = properties["BUILD_TIME"]?.let {
Date().time - (it as String).toLong() > minBuildTimeGap Date().time - (it as String).toLong() > minBuildTimeGap
} ?: false } ?: false
private fun appendToTask(project: Project, buildType: String) { private fun appendToTask(project: Project, flavorName: String, buildType: String) {
project.tasks.getByName(Utils.getAssembleTaskName(buildType)).doLast { project.tasks.getByName(Utils.getAssembleTaskName(flavorName, buildType)).doLast {
updateProperties() updateProperties()
println() println()
showInfo() showInfo()
@@ -618,7 +703,7 @@ class Versions(filePath: String) {
val title = "Version information for AutoJs6 app library" val title = "Version information for AutoJs6 app library"
val infoVerName = "Version name: $appVersionName" val infoVerName = "Version name: $appVersionName"
val infoVerCode = "Version code: $appVersionCode${" [auto-incremented]".takeIf { isBuildNumberAutoIncremented } ?: ""}" val infoVerCode = "Version code: ${if (isBuildNumberAutoIncremented) "${appVersionCode + 1} [auto-incremented]" else appVersionCode}"
val infoVerSdk = "SDK versions: min [$sdkVersionMin] / target [$sdkVersionTarget] / compile [$sdkVersionCompile]" val infoVerSdk = "SDK versions: min [$sdkVersionMin] / target [$sdkVersionTarget] / compile [$sdkVersionCompile]"
val infoVerJava = "Java version: $javaVersion$javaVersionInfoSuffix" val infoVerJava = "Java version: $javaVersion$javaVersionInfoSuffix"
@@ -637,21 +722,17 @@ class Versions(filePath: String) {
).forEach { println(it) } ).forEach { println(it) }
} }
fun handleIfNeeded(project: Project) = assembleTargets.forEach { fun handleIfNeeded(project: Project, flavorName: String, targetBuildType: List<String>) {
val targetName = it.key project.gradle.taskGraph.whenReady(object : Action<TaskExecutionGraph> {
val targetBuildType = it.value override fun execute(taskGraph: TaskExecutionGraph) {
if (targetName == project.name) { for (buildType in targetBuildType) {
project.gradle.taskGraph.whenReady(object : Action<TaskExecutionGraph> { if (taskGraph.hasTask(Utils.getAssembleFullTaskName(project.name, flavorName, buildType))) {
override fun execute(taskGraph: TaskExecutionGraph) { return appendToTask(project, flavorName, buildType)
for (buildType in targetBuildType) {
if (taskGraph.hasTask(Utils.getAssembleFullTaskName(targetName, buildType))) {
return appendToTask(project, buildType)
}
} }
return showInfo()
} }
}) return showInfo()
} }
})
} }
} }
@@ -676,9 +757,9 @@ object Utils {
return "$autojs-v$version-$architecture.$extension".lowercase(Locale.getDefault()) return "$autojs-v$version-$architecture.$extension".lowercase(Locale.getDefault())
} }
fun getAssembleTaskName(buildType: String) = "assemble${capitalize(buildType)}" fun getAssembleTaskName(flavorName: String, buildType: String) = "assemble${capitalize(flavorName)}${capitalize(buildType)}"
fun getAssembleFullTaskName(name: String, buildType: String) = ":$name:${getAssembleTaskName(buildType)}" fun getAssembleFullTaskName(projectName: String, flavorName: String, buildType: String) = ":$projectName:${getAssembleTaskName(flavorName, buildType)}"
fun digestCRC32(file: File): String { fun digestCRC32(file: File): String {
val fis = FileInputStream(file) val fis = FileInputStream(file)
+48 -17
View File
@@ -74,7 +74,9 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"/> <uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"
tools:ignore="SystemPermissionTypo"/>
<uses-permission <uses-permission
android:name="android.permission.DUMP" android:name="android.permission.DUMP"
@@ -113,19 +115,22 @@
<uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.READ_SMS"/>
<!-- Property "largeHeap" is not recommended to set to true. -->
<!-- https://stackoverflow.com/questions/27396892/what-are-advantages-of-setting-largeheap-to-true -->
<application <application
android:name="org.autojs.autojs.App" android:name="org.autojs.autojs.App"
android:allowBackup="false" android:allowBackup="false"
android:extractNativeLibs="true"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:icon="@drawable/autojs6_material" android:icon="${icon}"
android:label="${appName}" android:label="${appName}"
android:largeHeap="true" android:largeHeap="false"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:localeConfig="@xml/locales_config"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:ignore="DataExtractionRules"> tools:ignore="DataExtractionRules"
tools:targetApi="tiramisu">
<meta-data <meta-data
android:name="CHANNEL" android:name="CHANNEL"
@@ -141,8 +146,14 @@
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.MAIN"/>
<category android:name="${intentCategory}"/>
<category android:name="${intentCategory}"/> <category android:name="${intentCategory}"/>
</intent-filter> </intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/app_shortcuts"/>
</activity> </activity>
<activity <activity
@@ -165,6 +176,16 @@
</intent-filter> </intent-filter>
</activity> </activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${authorities}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
<activity <activity
android:name="org.autojs.autojs.ui.edit.EditActivity" android:name="org.autojs.autojs.ui.edit.EditActivity"
android:configChanges="orientation|keyboardHidden|screenSize|locale" android:configChanges="orientation|keyboardHidden|screenSize|locale"
@@ -179,6 +200,7 @@
<activity <activity
android:name="org.autojs.autojs.ui.settings.PreferencesActivity" android:name="org.autojs.autojs.ui.settings.PreferencesActivity"
android:exported="true"
android:theme="@style/AppTheme.Settings"/> android:theme="@style/AppTheme.Settings"/>
<activity <activity
@@ -197,13 +219,14 @@
<activity android:name="org.autojs.autojs.ui.project.ProjectConfigActivity"/> <activity android:name="org.autojs.autojs.ui.project.ProjectConfigActivity"/>
<activity android:name="org.autojs.autojs.ui.log.LogActivity"/> <activity
android:name="org.autojs.autojs.ui.log.LogActivity"
android:exported="true"/>
<activity <activity
android:name="org.autojs.autojs.ui.doc.DocumentationActivity" android:name="org.autojs.autojs.ui.doc.DocumentationActivity"
android:exported="true" android:exported="true"
android:icon="@drawable/ic_docs_app" android:label="@string/text_app_shortcut_docs_long_label"
android:label="@string/docs_app_name"
android:taskAffinity="org.autojs.autojs.ui.doc.DocumentationActivity" android:taskAffinity="org.autojs.autojs.ui.doc.DocumentationActivity"
android:theme="@style/AppTheme.FullScreen"> android:theme="@style/AppTheme.FullScreen">
@@ -562,14 +585,25 @@
android:multiprocess="false" android:multiprocess="false"
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL"/> android:permission="android.permission.INTERACT_ACROSS_USERS_FULL"/>
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
<activity <activity
android:name="org.autojs.autojs.inrt.SplashActivity" android:name="org.autojs.autojs.inrt.SplashActivity"
android:exported="true" android:exported="true"
android:theme="@style/AppTheme.Splash"> android:theme="@style/AppTheme.Splash">
<intent-filter tools:ignore="ExtraText"> <intent-filter tools:ignore="ExtraText">
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="${intentCategoryInrt}"/> <category android:name="${intentCategoryInrt}"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="org.autojs.autojs.inrt.LogActivity"/> <activity android:name="org.autojs.autojs.inrt.LogActivity"/>
@@ -577,15 +611,12 @@
<activity android:name="org.autojs.autojs.inrt.SettingsActivity"/> <activity android:name="org.autojs.autojs.inrt.SettingsActivity"/>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="rikka.shizuku.ShizukuProvider"
android:authorities="${authorities}" android:authorities="org.autojs.autojs6.shizuku"
android:exported="false" android:enabled="true"
android:grantUriPermissions="true"> android:exported="true"
<meta-data android:multiprocess="false"
android:name="android.support.FILE_PROVIDER_PATHS" android:permission="android.permission.INTERACT_ACROSS_USERS_FULL"/>
android:resource="@xml/provider_paths"/>
</provider>
</application> </application>
</manifest> </manifest>
@@ -0,0 +1,11 @@
package org.autojs.autojs.core.shizuku;
interface IUserService {
void destroy() = 16777114; // Destroy method defined by Shizuku server
void exit() = 1; // Exit method defined by user
String execCommand(String command) = 2;
}
+161 -145
View File
@@ -4,26 +4,33 @@
****** ******
# v6.3.4 # v6.4.0
###### 2023/10/03 ###### 2023/10/25
* `新增` ocr 模块支持 Paddle Lite 引擎 (by [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`pr #120`](http://pr.autojs6.com/120)_ * `新增` ocr 模块支持 Paddle Lite 引擎 (by [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`pr #120`](http://pr.autojs6.com/120)_
* `新增` WebSocket 模块 (参阅 项目文档 > [颜色类](https://docs.autojs6.com/#/webSocketType)) * `新增` 打包功能支持内置插件与外部插件两种打包方式 (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))
* `新增` device.rotation / device.orientation 等方法 * `新增` device.rotation / device.orientation 等方法
* `新增` 内部 Java 类支持 class 静态属性访问 * `新增` 内部 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 插件保存文件到本地时文件扩展名信息丢失的问题 * `修复` VSCode 插件保存文件到本地时文件扩展名信息丢失的问题
* `修复` 使用协程特性运行项目产生未捕获异常致使应用崩溃的问题 * `修复` 使用协程特性运行项目产生未捕获异常致使应用崩溃的问题
* `修复` 重启或退出应用时浮动按钮无法记录其位置状态信息的问题 * `修复` 重启或退出应用时浮动按钮无法记录其位置状态信息的问题
* `修复` 设备屏幕方向改变时无法获取更新后的设备配置信息的问题 * `修复` 设备屏幕方向改变时无法获取更新后的设备配置信息的问题 _[`issue #153`](http://issues.autojs6.com/153)_
* `修复` 屏幕旋转至横向时 Toolbar 标题字体过小的问题 * `修复` 屏幕旋转至横向时 Toolbar 标题字体过小的问题
* `修复` 屏幕旋转至横向时应用主页的页签排版过于拥挤的问题 * `修复` 屏幕旋转至横向时应用主页的页签排版过于拥挤的问题
* `修复` 屏幕旋转至横向时浮动按钮可能溢出屏幕的问题 _[`issue #90`](http://issues.autojs6.com/90)_ * `修复` 屏幕旋转至横向时浮动按钮可能溢出屏幕的问题 _[`issue #90`](http://issues.autojs6.com/90)_
* `修复` 屏幕多次旋转时无法恢复浮动按钮的坐标及屏幕侧边方向的问题 * `修复` 屏幕多次旋转时无法恢复浮动按钮的坐标及屏幕侧边方向的问题
* `修复` 部分设备消息浮动框可能出现遗漏显示或重复显示的问题
* `修复` 消息浮动框在多个脚本同时运行时可能存在被遮蔽的问题 _[`issue #67`](http://issues.autojs6.com/67)_ * `修复` 消息浮动框在多个脚本同时运行时可能存在被遮蔽的问题 _[`issue #67`](http://issues.autojs6.com/67)_
* `修复` 使用广播分析布局时点击布局无法弹出菜单且导致应用崩溃的问题 * `修复` 使用广播分析布局时点击布局无法弹出菜单且导致应用崩溃的问题
* `修复` 第二次及以后创建的 WebSocket 实例均无法正常触发监听器的问题 * `修复` 第二次及以后创建的 WebSocket 实例均无法正常触发监听器的问题
* `修复` 重新合并部分 PR (by [aiselp](https://github.com/aiselp)) 以解决部分脚本无法正常结束运行的问题 _[`pr #75`](http://pr.autojs6.com/75)_ _[`pr #78`](http://pr.autojs6.com/78)_
* `修复` 撤销 importPackage 的全局重定向方法以避免某些作用域下的包导入异常 _[`issue #88`](http://issues.autojs6.com/88)_ * `修复` 撤销 importPackage 的全局重定向方法以避免某些作用域下的包导入异常 _[`issue #88`](http://issues.autojs6.com/88)_
* `修复` 日志活动页面使用复制或导出功能时可能导致应用崩溃的问题 * `修复` 日志活动页面使用复制或导出功能时可能导致应用崩溃的问题
* `优化` 日志活动页面导出功能重命名为发送功能并重新实现符合实际意义的导出功能 * `优化` 日志活动页面导出功能重命名为发送功能并重新实现符合实际意义的导出功能
@@ -31,17 +38,26 @@
* `优化` ocr 模块同时兼容 Google MLKit 及 Paddle Lite 引擎 (参阅 项目文档 > [光学字符识别](https://docs.autojs6.com/#/ocr?id=p-mode)) * `优化` ocr 模块同时兼容 Google MLKit 及 Paddle Lite 引擎 (参阅 项目文档 > [光学字符识别](https://docs.autojs6.com/#/ocr?id=p-mode))
* `优化` 提升无障碍服务自动启动的成功概率 * `优化` 提升无障碍服务自动启动的成功概率
* `优化` 构建工具自适应 Java 发行版本以尽量避免 "无效的发行版本" 问题 * `优化` 构建工具自适应 Java 发行版本以尽量避免 "无效的发行版本" 问题
* `优化` 构建工具优化 IDE 及相关插件的版本退级逻辑并增加版本预测能力
* `优化` 部分 Kotlin 注解处理由 kapt 迁移至 KSP * `优化` 部分 Kotlin 注解处理由 kapt 迁移至 KSP
* `优化` 附加 Rikka Shizuku 版本 13.1.5 * `依赖` 附加 Rikka Shizuku 版本 13.1.5
* `优化` 升级 Gradle Compile 版本 33 -> 34 * `依赖` 附加 MLKit Barcode Scanning 版本 17.2.0
* `优化` 升级 Gradle 版本 8.3-rc-1 -> 8.4-rc-3 * `依赖` 升级 OpenCV 版本 4.5.5 -> 4.8.0 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ))
* `优化` 升级 Apache Commons Lang3 版本 3.12.0 -> 3.13.0 * `依赖` 升级 Gradle Compile 版本 33 -> 34
* `优化` 升级 Glide 版本 4.15.1 -> 4.16.0 * `依赖` 升级 Gradle 版本 8.3-rc-1 -> 8.4-rc-3
* `优化` 升级 Android Analytics 版本 14.3.0 -> 14.4.0 * `依赖` 升级 Apache Commons Lang3 版本 3.12.0 -> 3.13.0
* `优化` 升级 Androidx WebKit 版本 1.7.0 -> 1.8.0 * `依赖` 升级 Glide 版本 4.15.1 -> 4.16.0
* `优化` 升级 Androidx Preference 版本 1.2.0 -> 1.2.1 * `依赖` 升级 Android Analytics 版本 14.3.0 -> 14.4.0
* `优化` 升级 Androidx Annotation 版本 1.6.0 -> 1.7.0 * `依赖` 升级 Androidx AppCompat 版本 1.4.2 -> 1.6.0-alpha05
* `优化` 升级 Androidx Recyclerview 版本 1.3.0 -> 1.3.1 * `依赖` 升级 Androidx WebKit 版本 1.7.0 -> 1.8.0
* `依赖` 升级 Androidx Preference 版本 1.2.0 -> 1.2.1
* `依赖` 升级 Androidx Annotation 版本 1.6.0 -> 1.7.0
* `依赖` 升级 Androidx Recyclerview 版本 1.3.0 -> 1.3.2
* `依赖` 升级 Android Material 版本 1.9.0 -> 1.10.0
* `依赖` 升级 Androidx AppCompat 版本 1.4.2 -> 1.6.1
* `依赖` 升级 Commons IO 版本 2.8.0 -> 2.14.0
* `依赖` 升级 Jackson DataBind 版本 2.13.3 -> 2.14.3
* `依赖` 移除 Zeugma Solutions LocaleHelper 版本 1.5.1
# v6.3.3 # v6.3.3
@@ -63,7 +79,7 @@
* `优化` 在 _[`pr #98`](http://pr.autojs6.com/98)_ 基础上添加代码注释菜单项 * `优化` 在 _[`pr #98`](http://pr.autojs6.com/98)_ 基础上添加代码注释菜单项
* `优化` 适配 VSCode 插件 1.0.6 * `优化` 适配 VSCode 插件 1.0.6
* `优化` UiObject#parent 方法增加级数参数支持 (参阅 项目文档 > [控件节点](https://docs.autojs6.com/#/uiObjectType)) * `优化` UiObject#parent 方法增加级数参数支持 (参阅 项目文档 > [控件节点](https://docs.autojs6.com/#/uiObjectType))
* `优化` 升级 Gradle 版本 8.2 -> 8.3-rc-1 * `依赖` 升级 Gradle 版本 8.2 -> 8.3-rc-1
# v6.3.2 # v6.3.2
@@ -97,9 +113,9 @@
* `优化` 增加短信数据读取权限 (android.permission.READ_SMS) (默认关闭) * `优化` 增加短信数据读取权限 (android.permission.READ_SMS) (默认关闭)
* `优化` findMultiColors 方法内部实现 (by [LYS86](https://github.com/LYS86)) _[`pr #72`](http://pr.autojs6.com/72)_ * `优化` findMultiColors 方法内部实现 (by [LYS86](https://github.com/LYS86)) _[`pr #72`](http://pr.autojs6.com/72)_
* `优化` runtime.loadDex/loadJar/load 支持按目录级别加载或同时加载多个文件 * `优化` runtime.loadDex/loadJar/load 支持按目录级别加载或同时加载多个文件
* `优化` 升级 Leakcanary 版本 2.11 -> 2.12 * `依赖` 升级 Leakcanary 版本 2.11 -> 2.12
* `优化` 升级 Android Analytics 版本 14.2.0 -> 14.3.0 * `依赖` 升级 Android Analytics 版本 14.2.0 -> 14.3.0
* `优化` 升级 Gradle 版本 8.2-milestone-1 -> 8.2 * `依赖` 升级 Gradle 版本 8.2-milestone-1 -> 8.2
# v6.3.1 # v6.3.1
@@ -131,11 +147,11 @@
* `优化` 视图 (View) 绑定方式由 ButterKnife 迁移至 View Binding _[`issue #48`](http://issues.autojs6.com/48)_ * `优化` 视图 (View) 绑定方式由 ButterKnife 迁移至 View Binding _[`issue #48`](http://issues.autojs6.com/48)_
* `优化` 服务端模式非正常关闭时将于 AutoJs6 启动时自动开启 _[`issue #64`](http://issues.autojs6.com/64)_ * `优化` 服务端模式非正常关闭时将于 AutoJs6 启动时自动开启 _[`issue #64`](http://issues.autojs6.com/64)_
* `优化` 客户端模式非正常关闭时将于 AutoJs6 启动时按最近一次的历史地址自动连接 * `优化` 客户端模式非正常关闭时将于 AutoJs6 启动时按最近一次的历史地址自动连接
* `优化` 升级 Leakcanary 版本 2.10 -> 2.11 * `依赖` 升级 Leakcanary 版本 2.10 -> 2.11
* `优化` 升级 Android Material 版本 1.8.0 -> 1.9.0 * `依赖` 升级 Android Material 版本 1.8.0 -> 1.9.0
* `优化` 升级 Androidx WebKit 版本 1.6.1 -> 1.7.0 * `依赖` 升级 Androidx WebKit 版本 1.6.1 -> 1.7.0
* `优化` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.9 -> 5.0.0-alpha.11 * `依赖` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.9 -> 5.0.0-alpha.11
* `优化` 升级 MLKit Text Recognition Chinese 版本 16.0.0-beta6 -> 16.0.0 * `依赖` 升级 MLKit Text Recognition Chinese 版本 16.0.0-beta6 -> 16.0.0
# v6.3.0 # v6.3.0
@@ -184,23 +200,23 @@
* `优化` http 模块相关方法支持超时参数 (timeout) * `优化` http 模块相关方法支持超时参数 (timeout)
* `优化` Gradle 构建脚本支持 JDK 版本主动降级 (fallback) * `优化` Gradle 构建脚本支持 JDK 版本主动降级 (fallback)
* `优化` Gradle 构建脚本支持根据平台类型及版本自动选择合适的构建工具版本 (程度有限) * `优化` Gradle 构建脚本支持根据平台类型及版本自动选择合适的构建工具版本 (程度有限)
* `优化` 本地化 Auto.js APK Builder 版本 1.0.3 * `依赖` 本地化 Auto.js APK Builder 版本 1.0.3
* `优化` 本地化 MultiLevelListView 版本 1.1 * `依赖` 本地化 MultiLevelListView 版本 1.1
* `优化` 本地化 Settings Compat 版本 1.1.5 * `依赖` 本地化 Settings Compat 版本 1.1.5
* `优化` 本地化 Enhanced Floaty 版本 0.31 * `依赖` 本地化 Enhanced Floaty 版本 0.31
* `优化` 附加 MLKit Text Recognition Chinese 版本 16.0.0-beta6 * `依赖` 附加 MLKit Text Recognition Chinese 版本 16.0.0-beta6
* `优化` 升级 Gradle 版本 8.0-rc-1 -> 8.2-milestone-1 * `依赖` 升级 Gradle 版本 8.0-rc-1 -> 8.2-milestone-1
* `优化` 升级 Android Material 版本 1.7.0 -> 1.8.0 * `依赖` 升级 Android Material 版本 1.7.0 -> 1.8.0
* `优化` 升级 Glide 版本 4.14.2 -> 4.15.1 * `依赖` 升级 Glide 版本 4.14.2 -> 4.15.1
* `优化` 升级 Joda Time 版本 2.12.2 -> 2.12.5 * `依赖` 升级 Joda Time 版本 2.12.2 -> 2.12.5
* `优化` 升级 Android Analytics 版本 14.0.0 -> 14.2.0 * `依赖` 升级 Android Analytics 版本 14.0.0 -> 14.2.0
* `优化` 升级 Androidx WebKit 版本 1.5.0 -> 1.6.1 * `依赖` 升级 Androidx WebKit 版本 1.5.0 -> 1.6.1
* `优化` 升级 Androidx Recyclerview 版本 1.2.1 -> 1.3.0 * `依赖` 升级 Androidx Recyclerview 版本 1.2.1 -> 1.3.0
* `优化` 升级 Zip4j 版本 2.11.2 -> 2.11.5 * `依赖` 升级 Zip4j 版本 2.11.2 -> 2.11.5
* `优化` 升级 Junit 版本 5.9.2 -> 5.9.3 * `依赖` 升级 Junit 版本 5.9.2 -> 5.9.3
* `优化` 升级 Androidx Annotation 版本 1.5.0 -> 1.6.0 * `依赖` 升级 Androidx Annotation 版本 1.5.0 -> 1.6.0
* `优化` 升级 Jackson DataBind 版本 2.14.1 -> 2.14.2 * `依赖` 升级 Jackson DataBind 版本 2.14.1 -> 2.14.2
* `优化` 升级 Desugar JDK Libs 版本 2.0.0 -> 2.0.3 * `依赖` 升级 Desugar JDK Libs 版本 2.0.0 -> 2.0.3
# v6.2.0 # v6.2.0
@@ -300,32 +316,32 @@
* `优化` 提升 console.show 显示的日志浮动窗口文字亮度以增强内容辨识度 * `优化` 提升 console.show 显示的日志浮动窗口文字亮度以增强内容辨识度
* `优化` ImageWrapper#saveTo 支持相对路径保存图像文件 * `优化` ImageWrapper#saveTo 支持相对路径保存图像文件
* `优化` 重新设计 colors 全局对象并增加 HSV / HSL 等色彩模式支持 (参阅 项目文档 > [颜色](https://docs.autojs6.com/#/color)) * `优化` 重新设计 colors 全局对象并增加 HSV / HSL 等色彩模式支持 (参阅 项目文档 > [颜色](https://docs.autojs6.com/#/color))
* `优化` 升级 Gradle Compile 版本 32 -> 33 * `依赖` 升级 Gradle Compile 版本 32 -> 33
* `优化` 本地化 Android Job 版本 1.4.3 * `依赖` 本地化 Android Job 版本 1.4.3
* `优化` 本地化 Android Plugin Client SDK For Locale 版本 9.0.0 * `依赖` 本地化 Android Plugin Client SDK For Locale 版本 9.0.0
* `优化` 本地化 GitHub API 版本 1.306 * `依赖` 本地化 GitHub API 版本 1.306
* `优化` 附加 JCIP Annotations 版本 1.0 * `依赖` 附加 JCIP Annotations 版本 1.0
* `优化` 附加 Androidx WebKit 版本 1.5.0 * `依赖` 附加 Androidx WebKit 版本 1.5.0
* `优化` 附加 Commons IO 版本 2.8.0 * `依赖` 附加 Commons IO 版本 2.8.0
* `优化` 附加 Desugar JDK Libs 版本 2.0.0 * `依赖` 附加 Desugar JDK Libs 版本 2.0.0
* `优化` 附加 Jackson DataBind 版本 2.13.3 * `依赖` 附加 Jackson DataBind 版本 2.13.3
* `优化` 附加 Jaredrummler Android Device Names 版本 2.1.0 * `依赖` 附加 Jaredrummler Android Device Names 版本 2.1.0
* `优化` 附加 Jaredrummler Animated SVG View 版本 1.0.6 * `依赖` 附加 Jaredrummler Animated SVG View 版本 1.0.6
* `优化` 替换 Jrummyapps ColorPicker 版本 2.1.7 为 Jaredrummler ColorPicker 版本 1.1.0 * `依赖` 替换 Jrummyapps ColorPicker 版本 2.1.7 为 Jaredrummler ColorPicker 版本 1.1.0
* `优化` 升级 Gradle 版本 7.5-rc-1 -> 8.0-rc-1 * `依赖` 升级 Gradle 版本 7.5-rc-1 -> 8.0-rc-1
* `优化` 升级 Gradle 构建工具版本 7.4.0-alpha02 -> 8.0.0-alpha09 * `依赖` 升级 Gradle 构建工具版本 7.4.0-alpha02 -> 8.0.0-alpha09
* `优化` 升级 Kotlin Gradle 插件版本 1.6.10 -> 1.8.0-RC2 * `依赖` 升级 Kotlin Gradle 插件版本 1.6.10 -> 1.8.0-RC2
* `优化` 升级 Android Material 版本 1.6.0 -> 1.7.0 * `依赖` 升级 Android Material 版本 1.6.0 -> 1.7.0
* `优化` 升级 Androidx Annotation 版本 1.3.0 -> 1.5.0 * `依赖` 升级 Androidx Annotation 版本 1.3.0 -> 1.5.0
* `优化` 升级 Androidx AppCompat 版本 1.4.1 -> 1.4.2 * `依赖` 升级 Androidx AppCompat 版本 1.4.1 -> 1.4.2
* `优化` 升级 Android Analytics 版本 13.3.0 -> 14.0.0 * `依赖` 升级 Android Analytics 版本 13.3.0 -> 14.0.0
* `优化` 升级 Gson 版本 2.9.0 -> 2.10 * `依赖` 升级 Gson 版本 2.9.0 -> 2.10
* `优化` 升级 Joda Time 版本 2.10.14 -> 2.12.1 * `依赖` 升级 Joda Time 版本 2.10.14 -> 2.12.1
* `优化` 升级 Kotlinx Coroutines 版本 1.6.1-native-mt -> 1.6.1 * `依赖` 升级 Kotlinx Coroutines 版本 1.6.1-native-mt -> 1.6.1
* `优化` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.7 -> 5.0.0-alpha.9 * `依赖` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.7 -> 5.0.0-alpha.9
* `优化` 升级 Zip4j 版本 2.10.0 -> 2.11.2 * `依赖` 升级 Zip4j 版本 2.10.0 -> 2.11.2
* `优化` 升级 Glide 版本 4.13.2 -> 4.14.2 * `依赖` 升级 Glide 版本 4.13.2 -> 4.14.2
* `优化` 升级 Junit 版本 5.9.0 -> 5.9.1 * `依赖` 升级 Junit 版本 5.9.0 -> 5.9.1
# v6.1.1 # v6.1.1
@@ -338,8 +354,8 @@
* `修复` 编辑器页面选项菜单中 "粘贴" 功能缺失的问题 * `修复` 编辑器页面选项菜单中 "粘贴" 功能缺失的问题
* `优化` 部分异常消息字符串资源化 (en / zh) * `优化` 部分异常消息字符串资源化 (en / zh)
* `优化` 调整内容未保存对话框的按钮布局并增加颜色区分 * `优化` 调整内容未保存对话框的按钮布局并增加颜色区分
* `优化` 附加 github-api 版本 1.306 * `依赖` 附加 github-api 版本 1.306
* `优化` 替换 retrofit2-rxjava2-adapter 版本 1.0.0 为 adapter-rxjava2 版本 2.9.0 * `依赖` 替换 retrofit2-rxjava2-adapter 版本 1.0.0 为 adapter-rxjava2 版本 2.9.0
# v6.1.0 # v6.1.0
@@ -385,22 +401,22 @@
* `优化` 外部存储读写权限适配高版本安卓系统 (API >= 30) * `优化` 外部存储读写权限适配高版本安卓系统 (API >= 30)
* `优化` 控制台字体采用 Material Color 增强普通及夜间主题下的字体可读性 * `优化` 控制台字体采用 Material Color 增强普通及夜间主题下的字体可读性
* `优化` 保存 ImageWrapper 所有实例弱引用并在脚本结束时自动回收 (实验性) * `优化` 保存 ImageWrapper 所有实例弱引用并在脚本结束时自动回收 (实验性)
* `优化` 附加 CircleImageView 版本 3.1.0 * `依赖` 附加 CircleImageView 版本 3.1.0
* `优化` 升级 Android Analytics 版本 13.1.0 -> 13.3.0 * `依赖` 升级 Android Analytics 版本 13.1.0 -> 13.3.0
* `优化` 升级 Gradle 构建工具版本 7.3.0-alpha06 -> 7.4.0-alpha02 * `依赖` 升级 Gradle 构建工具版本 7.3.0-alpha06 -> 7.4.0-alpha02
* `优化` 升级 Android Job 版本 1.4.2 -> 1.4.3 * `依赖` 升级 Android Job 版本 1.4.2 -> 1.4.3
* `优化` 升级 Android Material 版本 1.5.0 -> 1.6.0 * `依赖` 升级 Android Material 版本 1.5.0 -> 1.6.0
* `优化` 升级 CrashReport 版本 2.6.6 -> 4.0.4 * `依赖` 升级 CrashReport 版本 2.6.6 -> 4.0.4
* `优化` 升级 Glide 版本 4.13.1 -> 4.13.2 * `依赖` 升级 Glide 版本 4.13.1 -> 4.13.2
* `优化` 升级 Joda Time 版本 2.10.13 -> 2.10.14 * `依赖` 升级 Joda Time 版本 2.10.13 -> 2.10.14
* `优化` 升级 Kotlin Gradle 插件版本 1.6.10 -> 1.6.21 * `依赖` 升级 Kotlin Gradle 插件版本 1.6.10 -> 1.6.21
* `优化` 升级 Kotlinx Coroutines 版本 1.6.0 -> 1.6.1-native-mt * `依赖` 升级 Kotlinx Coroutines 版本 1.6.0 -> 1.6.1-native-mt
* `优化` 升级 Leakcanary 版本 2.8.1 -> 2.9.1 * `依赖` 升级 Leakcanary 版本 2.8.1 -> 2.9.1
* `优化` 升级 Okhttp3 版本 5.0.0-alpha.6 -> 5.0.0-alpha.7 * `依赖` 升级 Okhttp3 版本 5.0.0-alpha.6 -> 5.0.0-alpha.7
* `优化` 升级 Rhino 引擎版本 1.7.14 -> 1.7.15-snapshot * `依赖` 升级 Rhino 引擎版本 1.7.14 -> 1.7.15-snapshot
* `优化` 升级 Zip4j 版本 2.9.1 -> 2.10.0 * `依赖` 升级 Zip4j 版本 2.9.1 -> 2.10.0
* `优化` 移除 Groovy JSON 版本 3.0.8 * `依赖` 移除 Groovy JSON 版本 3.0.8
* `优化` 移除 Kotlin Stdlib JDK7 版本 1.6.21 * `依赖` 移除 Kotlin Stdlib JDK7 版本 1.6.21
# v6.0.3 # v6.0.3
@@ -423,17 +439,17 @@
* `优化` 浮动按钮初始化时同时应用透明度设置 (无需点击后再应用透明度) * `优化` 浮动按钮初始化时同时应用透明度设置 (无需点击后再应用透明度)
* `优化` 重置文件内容时增加是否为示例代码文件的检测并增加结果提示 * `优化` 重置文件内容时增加是否为示例代码文件的检测并增加结果提示
* `优化` 转移打包插件下载地址 GitHub -> JsDelivr * `优化` 转移打包插件下载地址 GitHub -> JsDelivr
* `优化` 附加 Zeugma Solutions LocaleHelper 版本 1.5.1 * `依赖` 附加 Zeugma Solutions LocaleHelper 版本 1.5.1
* `优化` 降级 Android Material 版本 1.6.0-alpha02 -> 1.5.0 * `依赖` 降级 Android Material 版本 1.6.0-alpha02 -> 1.5.0
* `优化` 升级 Kotlinx Coroutines 版本 1.6.0-native-mt -> 1.6.0 * `依赖` 升级 Kotlinx Coroutines 版本 1.6.0-native-mt -> 1.6.0
* `优化` 升级 OpenCV 版本 3.4.3 -> 4.5.4 -> 4.5.5 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ)) * `依赖` 升级 OpenCV 版本 3.4.3 -> 4.5.4 -> 4.5.5 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ))
* `优化` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.4 -> 5.0.0-alpha.6 * `依赖` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.4 -> 5.0.0-alpha.6
* `优化` 升级 Gradle 构建工具版本 7.2.0-beta01 -> 7.3.0-alpha06 * `依赖` 升级 Gradle 构建工具版本 7.2.0-beta01 -> 7.3.0-alpha06
* `优化` 升级 Auto.js-ApkBuilder 版本 1.0.1 -> 1.0.3 * `依赖` 升级 Auto.js-ApkBuilder 版本 1.0.1 -> 1.0.3
* `优化` 升级 Glide Compiler 版本 4.12.0 -> 4.13.1 * `依赖` 升级 Glide Compiler 版本 4.12.0 -> 4.13.1
* `优化` 升级 Gradle 发行版本 7.4-rc-2 -> 7.4.1 * `依赖` 升级 Gradle 发行版本 7.4-rc-2 -> 7.4.1
* `优化` 升级 Gradle Compile 版本 31 -> 32 * `依赖` 升级 Gradle Compile 版本 31 -> 32
* `优化` 升级 Gson 版本 2.8.9 -> 2.9.0 * `依赖` 升级 Gson 版本 2.8.9 -> 2.9.0
# v6.0.2 # v6.0.2
@@ -446,14 +462,14 @@
* `修复` 客户端模式及服务端模式开关状态不能正常刷新的问题 * `修复` 客户端模式及服务端模式开关状态不能正常刷新的问题
* `修复` Android 7.x 解析 UI 模式 text 元素异常 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`issue #4`](http://issues.autojs6.com/4)_ _[`issue #9`](http://issues.autojs6.com/9)_ * `修复` Android 7.x 解析 UI 模式 text 元素异常 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ)) _[`issue #4`](http://issues.autojs6.com/4)_ _[`issue #9`](http://issues.autojs6.com/9)_
* `优化` 忽略 sleep() 的 ScriptInterruptedException 异常 * `优化` 忽略 sleep() 的 ScriptInterruptedException 异常
* `优化` 附加 Androidx AppCompat (Legacy) 版本 1.0.2 * `依赖` 附加 Androidx AppCompat (Legacy) 版本 1.0.2
* `优化` 升级 Androidx AppCompat 版本 1.4.0 -> 1.4.1 * `依赖` 升级 Androidx AppCompat 版本 1.4.0 -> 1.4.1
* `优化` 升级 Androidx Preference 版本 1.1.1 -> 1.2.0 * `依赖` 升级 Androidx Preference 版本 1.1.1 -> 1.2.0
* `优化` 升级 Rhino 引擎版本 1.7.14-snapshot -> 1.7.14 * `依赖` 升级 Rhino 引擎版本 1.7.14-snapshot -> 1.7.14
* `优化` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.3 -> 5.0.0-alpha.4 * `依赖` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.3 -> 5.0.0-alpha.4
* `优化` 升级 Android Material 版本 1.6.0-alpha01 -> 1.6.0-alpha02 * `依赖` 升级 Android Material 版本 1.6.0-alpha01 -> 1.6.0-alpha02
* `优化` 升级 Gradle 构建工具版本 7.2.0-alpha06 -> 7.2.0-beta01 * `依赖` 升级 Gradle 构建工具版本 7.2.0-alpha06 -> 7.2.0-beta01
* `优化` 升级 Gradle 发行版本 7.3.3 -> 7.4-rc-2 * `依赖` 升级 Gradle 发行版本 7.3.3 -> 7.4-rc-2
# v6.0.1 # v6.0.1
@@ -467,11 +483,11 @@
* `优化` 扩展 global.sleep 支持 随机范围/负数兼容 * `优化` 扩展 global.sleep 支持 随机范围/负数兼容
* `优化` 扩展 global.toast 支持 时长控制/强制覆盖控制/dismiss * `优化` 扩展 global.toast 支持 时长控制/强制覆盖控制/dismiss
* `优化` 包名对象全局化 (okhttp3/androidx/de) * `优化` 包名对象全局化 (okhttp3/androidx/de)
* `优化` 升级 Android Material 版本 1.5.0-beta01 -> 1.6.0-alpha01 * `依赖` 升级 Android Material 版本 1.5.0-beta01 -> 1.6.0-alpha01
* `优化` 升级 Gradle 构建工具版本 7.2.0-alpha04 -> 7.2.0-alpha06 * `依赖` 升级 Gradle 构建工具版本 7.2.0-alpha04 -> 7.2.0-alpha06
* `优化` 升级 Kotlinx Coroutines 版本 1.5.2-native-mt -> 1.6.0-native-mt * `依赖` 升级 Kotlinx Coroutines 版本 1.5.2-native-mt -> 1.6.0-native-mt
* `优化` 升级 Kotlin Gradle 插件版本 1.6.0 -> 1.6.10 * `依赖` 升级 Kotlin Gradle 插件版本 1.6.0 -> 1.6.10
* `优化` 升级 Gradle 发行版本 7.3 -> 7.3.3 * `依赖` 升级 Gradle 发行版本 7.3 -> 7.3.3
# v6.0.0 # v6.0.0
@@ -499,39 +515,39 @@
* `优化` ResourceMonitor 使用 ReentrantLock 增强安全性 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ)) * `优化` ResourceMonitor 使用 ReentrantLock 增强安全性 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ))
* `优化` 使用 Maven Central 等仓库替换 JCenter 仓库 * `优化` 使用 Maven Central 等仓库替换 JCenter 仓库
* `优化` 抽离并移除重复的本地库文件 * `优化` 抽离并移除重复的本地库文件
* `优化` 本地化 CrashReport 版本 2.6.6 * `依赖` 本地化 CrashReport 版本 2.6.6
* `优化` 本地化 MutableTheme 版本 1.0.0 * `依赖` 本地化 MutableTheme 版本 1.0.0
* `优化` 附加 Androidx Preference 版本 1.1.1 * `依赖` 附加 Androidx Preference 版本 1.1.1
* `优化` 附加 SwipeRefreshLayout 版本 1.1.0 * `依赖` 附加 SwipeRefreshLayout 版本 1.1.0
* `优化` 升级 Android Analytics 版本 7.0.0 -> 13.1.0 * `依赖` 升级 Android Analytics 版本 7.0.0 -> 13.1.0
* `优化` 升级 Android Annotations 版本 4.5.2 -> 4.8.0 * `依赖` 升级 Android Annotations 版本 4.5.2 -> 4.8.0
* `优化` 升级 Gradle 构建工具版本 3.2.1 -> 4.1.0 -> 7.0.3 -> 7.2.0-alpha04 * `依赖` 升级 Gradle 构建工具版本 3.2.1 -> 4.1.0 -> 7.0.3 -> 7.2.0-alpha04
* `优化` 升级 Android Job 版本 1.2.6 -> 1.4.2 * `依赖` 升级 Android Job 版本 1.2.6 -> 1.4.2
* `优化` 升级 Android Material 版本 1.1.0-alpha01 -> 1.5.0-beta01 * `依赖` 升级 Android Material 版本 1.1.0-alpha01 -> 1.5.0-beta01
* `优化` 升级 Androidx MultiDex 版本 2.0.0 -> 2.0.1 * `依赖` 升级 Androidx MultiDex 版本 2.0.0 -> 2.0.1
* `优化` 升级 Apache Commons Lang3 版本 3.6 -> 3.12.0 * `依赖` 升级 Apache Commons Lang3 版本 3.6 -> 3.12.0
* `优化` 升级 Appcompat 版本 1.0.2 -> 1.4.0 * `依赖` 升级 Appcompat 版本 1.0.2 -> 1.4.0
* `优化` 升级 ButterKnife Gradle 插件版本 9.0.0-rc2 -> 10.2.1 -> 10.2.3 * `依赖` 升级 ButterKnife Gradle 插件版本 9.0.0-rc2 -> 10.2.1 -> 10.2.3
* `优化` 升级 ColorPicker 版本 2.1.5 -> 2.1.7 * `依赖` 升级 ColorPicker 版本 2.1.5 -> 2.1.7
* `优化` 升级 Espresso Core 版本 3.1.1-alpha01 -> 3.5.0-alpha03 * `依赖` 升级 Espresso Core 版本 3.1.1-alpha01 -> 3.5.0-alpha03
* `优化` 升级 Eventbus 版本 3.0.0 -> 3.2.0 * `依赖` 升级 Eventbus 版本 3.0.0 -> 3.2.0
* `优化` 升级 Glide Compiler 版本 4.8.0 -> 4.12.0 -> 4.12.0 * `依赖` 升级 Glide Compiler 版本 4.8.0 -> 4.12.0 -> 4.12.0
* `优化` 升级 Gradle Build Tool 版本 29.0.2 -> 30.0.2 * `依赖` 升级 Gradle Build Tool 版本 29.0.2 -> 30.0.2
* `优化` 升级 Gradle Compile 版本 28 -> 30 -> 31 * `依赖` 升级 Gradle Compile 版本 28 -> 30 -> 31
* `优化` 升级 Gradle 发行版本 4.10.2 -> 6.5 -> 7.0.2 -> 7.3 * `依赖` 升级 Gradle 发行版本 4.10.2 -> 6.5 -> 7.0.2 -> 7.3
* `优化` 升级 Groovy-Json 插件版本 3.0.7 -> 3.0.8 * `依赖` 升级 Groovy-Json 插件版本 3.0.7 -> 3.0.8
* `优化` 升级 Gson 版本 2.8.2 -> 2.8.9 * `依赖` 升级 Gson 版本 2.8.2 -> 2.8.9
* `优化` 升级 JavaVersion 版本 1.8 -> 11 -> 16 * `依赖` 升级 JavaVersion 版本 1.8 -> 11 -> 16
* `优化` 升级 Joda Time 版本 2.9.9 -> 2.10.13 * `依赖` 升级 Joda Time 版本 2.9.9 -> 2.10.13
* `优化` 升级 Junit 版本 4.12 -> 4.13.2 * `依赖` 升级 Junit 版本 4.12 -> 4.13.2
* `优化` 升级 Kotlin Gradle 插件版本 1.3.10 -> 1.4.10 -> 1.6.0 * `依赖` 升级 Kotlin Gradle 插件版本 1.3.10 -> 1.4.10 -> 1.6.0
* `优化` 升级 Kotlinx Coroutines 版本 1.0.1 -> 1.5.2-native-mt * `依赖` 升级 Kotlinx Coroutines 版本 1.0.1 -> 1.5.2-native-mt
* `优化` 升级 Leakcanary 版本 1.6.1 -> 2.7 * `依赖` 升级 Leakcanary 版本 1.6.1 -> 2.7
* `优化` 升级 LicensesDialog 版本 1.8.1 -> 2.2.0 * `依赖` 升级 LicensesDialog 版本 1.8.1 -> 2.2.0
* `优化` 升级 Material Dialogs 版本 0.9.2.3 -> 0.9.6.0 * `依赖` 升级 Material Dialogs 版本 0.9.2.3 -> 0.9.6.0
* `优化` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.2 -> 5.0.0-alpha.3 * `依赖` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.2 -> 5.0.0-alpha.3
* `优化` 升级 Reactivex RxJava2 RxAndroid 版本 2.0.1 -> 2.1.1 * `依赖` 升级 Reactivex RxJava2 RxAndroid 版本 2.0.1 -> 2.1.1
* `优化` 升级 Reactivex RxJava2 版本 2.1.2 -> 2.2.21 * `依赖` 升级 Reactivex RxJava2 版本 2.1.2 -> 2.2.21
* `优化` 升级 Retrofit2 Converter Gson 版本 2.3.0 -> 2.9.0 * `依赖` 升级 Retrofit2 Converter Gson 版本 2.3.0 -> 2.9.0
* `优化` 升级 Retrofit2 Retrofit 版本 2.3.0 -> 2.9.0 * `依赖` 升级 Retrofit2 Retrofit 版本 2.3.0 -> 2.9.0
* `优化` 升级 Zip4j 版本 1.3.2 -> 2.9.1 * `依赖` 升级 Zip4j 版本 1.3.2 -> 2.9.1
+11 -4
View File
@@ -47,12 +47,16 @@ let ColorUtils = org.autojs.autojs.util.ColorUtils;
let TextUtils = org.autojs.autojs.util.TextUtils; let TextUtils = org.autojs.autojs.util.TextUtils;
let ProxyObject = org.autojs.autojs.rhino.ProxyObject; let ProxyObject = org.autojs.autojs.rhino.ProxyObject;
let ProxyJavaObject = org.autojs.autojs.rhino.ProxyJavaObject; let ProxyJavaObject = org.autojs.autojs.rhino.ProxyJavaObject;
let JavaScriptSource = org.autojs.autojs.script.JavaScriptSource;
let NotificationManager = android.app.NotificationManager; let NotificationManager = android.app.NotificationManager;
let NotificationCompat = Packages.androidx.core.app.NotificationCompat; let NotificationCompat = Packages.androidx.core.app.NotificationCompat;
let SecurityException = java.lang.SecurityException; let SecurityException = java.lang.SecurityException;
let FileProvider = androidx.core.content.FileProvider;
let AppUtils = org.autojs.autojs.runtime.api.AppUtils;
let Locale = java.util.Locale; let Locale = java.util.Locale;
let URI = java.net.URI; let URI = java.net.URI;
let File = java.io.File; let File = java.io.File;
let Uri = android.net.Uri;
/* Global View classes. */ /* Global View classes. */
@@ -492,13 +496,16 @@ Object.assign(this, {
/* ! Numberx < Mathx */ /* ! Numberx < Mathx */
[ 'plugins', 'Arrayx', 'Numberx', 'Mathx' ], [ 'plugins', 'Arrayx', 'Numberx', 'Mathx' ],
/* ! ocr < images */ /* ! images < ocr */
/* ! ocr < files */ /* ! images < barcode */
[ 'ocr' ], /* ! images < qrcode */
/* ! barcode < qrcode */
/* ! files < ocr */
[ 'ocr', 'barcode', 'qrcode' ],
/* Safe to put last regardless of the order, no guarantee ;). */ /* Safe to put last regardless of the order, no guarantee ;). */
[ 'floaty', 'storages', 'device', 'recorder', 'toast' ], [ 'floaty', 'storages', 'device', 'recorder', 'toast' ],
[ 'media', 'sensors', 'events', 'base64', 'notice' ], [ 'media', 'sensors', 'events', 'base64', 'notice', 'shizuku' ],
/* Last but not the least */ /* Last but not the least */
[ 'globals' ], [ 'globals' ],
-4
View File
@@ -10,11 +10,7 @@ let { autojs, shell, files, util } = global;
* @return {Internal.App} * @return {Internal.App}
*/ */
module.exports = function (scriptRuntime, scope) { module.exports = function (scriptRuntime, scope) {
const File = java.io.File;
const Uri = android.net.Uri;
const JavaInteger = java.lang.Integer; const JavaInteger = java.lang.Integer;
const FileProvider = androidx.core.content.FileProvider;
const AppUtils = org.autojs.autojs.runtime.api.AppUtils;
/** /**
* @type {org.autojs.autojs.runtime.api.AppUtils} * @type {org.autojs.autojs.runtime.api.AppUtils}
+146
View File
@@ -0,0 +1,146 @@
// noinspection JSUnusedLocalSymbols,UnnecessaryLocalVariableJS
/* Overwritten protection. */
let { images } = global;
/**
* @param {ScriptRuntime} scriptRuntime
* @param {org.mozilla.javascript.Scriptable | global} scope
* @return {Internal.Barcode}
*/
module.exports = function (scriptRuntime, scope) {
let _ = {
BarcodeCtor: (/* @IIFE */ () => {
const instanceFunc = function __(img, options) {
if (typeof arguments[0] === 'string') {
let image = images.read(/* path = */ arguments[0]);
if (image === null) {
throw TypeError(`Invalid image of path "${arguments[0]}" for barcode(img, options?)`);
}
let args = [ image.oneShot() ].concat(Array.from(arguments).slice(1));
return BarcodeCtor.prototype.recognizeText.apply(BarcodeCtor.prototype, args);
}
return BarcodeCtor.prototype.recognizeText.apply(BarcodeCtor.prototype, arguments);
};
/**
* @implements Internal.Barcode
*/
const BarcodeCtor = function () {
let o = Object.assign(instanceFunc, BarcodeCtor.prototype);
Object.defineProperties(instanceFunc, Object.getOwnPropertyDescriptors(BarcodeCtor.prototype));
return o;
};
BarcodeCtor.prototype = {
constructor: BarcodeCtor,
recognizeTexts() {
let results = this.detectAll.apply(this, arguments);
return results.map(result => result.getRawValue()).filter(o => o !== null);
},
recognizeText() {
let results = this.recognizeTexts.apply(this, arguments);
if (_.shouldTakenAsAll(arguments)) {
return results;
}
return results.length > 0 ? results[0] : null;
},
/**
* @returns {Internal.Barcode.Result[]}
*/
detectAll() {
if (typeof arguments[0] === 'string') {
// @Signature
// detectAll(imgPath: string): Barcode.Result[];
// detectAll(imgPath: string, options: DetectOptionsWithoutIsAll): Barcode.Result[];
let img = images.read(/* path = */ arguments[0]);
if (img === null) {
throw TypeError(`Invalid image of path "${arguments[0]}" for barcode.detectAll(img, options?)`);
}
// @Overload
// detectAll(img: ImageWrapper): Barcode.Result[];
// detectAll(img: ImageWrapper, options: DetectOptionsWithoutIsAll): Barcode.Result[];
return this.detectAll(img.oneShot(), arguments[1]);
}
if (!(arguments[0] instanceof ImageWrapper)) {
// @Signature detectAll(options?: DetectOptionsWithoutIsAll): Barcode.Result[];
// @Overload detectAll(img: ImageWrapper, options?: DetectOptionsWithoutIsAll): Barcode.Result[];
return this.detectAll.apply(this, [ images.captureScreen() ].concat(Array.from(arguments)));
}
// @Signature detectAll(img: ImageWrapper, options?: DetectOptionsWithoutIsAll): Barcode.Result[];
let img = arguments[0];
/** @type {Internal.Barcode.DetectOptionsWithoutIsAll} */
let opt = /* options */ arguments[1] || {};
let formats = (() => {
if (isNullish(opt.format)) {
return [];
}
let results = Array.isArray(opt.format) ? opt.format : [ opt.format ];
let transformer = {
FORMAT_QRCODE: 'FORMAT_QR_CODE',
};
return results.map((result) => {
if (typeof result === 'number') {
return result;
}
if (typeof result === 'string') {
let tmp = result.toUpperCase().replace(/\W+/g, '_');
let prefix = 'FORMAT_';
if (!result.startsWith(prefix)) {
tmp = prefix + tmp;
}
if (tmp in transformer) {
tmp = transformer[tmp];
}
return com.google.mlkit.vision.barcode.common.Barcode[tmp];
}
throw TypeError(`Unknown format (value: ${result}, species: ${species(result)}) for barcode`);
});
})();
let enableAllPotentialBarcodes = Boolean(opt.enableAllPotentialBarcodes);
// noinspection JSValidateTypes
return scriptRuntime.barcode.detect(img, formats, enableAllPotentialBarcodes);
},
detect() {
let results = this.detectAll.apply(this, arguments);
if (_.shouldTakenAsAll(arguments)) {
return results;
}
return results.length > 0 ? results[0] : null;
},
};
return BarcodeCtor;
})(),
/**
* @param {IArguments} args
* @returns {boolean}
*/
shouldTakenAsAll(args) {
if (args.length === 0) {
return false;
}
/**
* @type {Internal.Barcode.DetectOptions | boolean | OmniRegion}
*/
let arg = args[args.length - 1];
return arg === true || isObject(arg) && Boolean(arg.isAll);
},
};
/**
* @type {Internal.Barcode}
*/
const barcode = new _.BarcodeCtor();
return barcode;
};
+1 -1
View File
@@ -638,7 +638,7 @@ module.exports = function (scriptRuntime, scope) {
if (alphaMatters === true) { if (alphaMatters === true) {
return this.toInt(colorA) === this.toInt(colorB); return this.toInt(colorA) === this.toInt(colorB);
} }
throw TypeError(`Invalid argument isStrict (value: ${alphaMatters}, type: ${species(alphaMatters)}) for colors.isEqual`); throw TypeError(`Invalid argument isStrict (value: ${alphaMatters}, species: ${species(alphaMatters)}) for colors.isEqual`);
}, },
/** /**
* Get a ColorStateList containing a single color or more. * Get a ColorStateList containing a single color or more.
+1 -1
View File
@@ -71,7 +71,7 @@ module.exports = function (scriptRuntime, scope) {
setEngineExecArgv() { setEngineExecArgv() {
engines.myEngine().setExecArgv(/* @IIFE */ ((e) => { engines.myEngine().setExecArgv(/* @IIFE */ ((e) => {
let execArgv = {}; let execArgv = {};
let iterator = e.getTag(ExecutionConfig.CREATOR.getTag()).arguments.entrySet().iterator(); let iterator = e.getTag(ExecutionConfig.tag).arguments.entrySet().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
let entry = iterator.next(); let entry = iterator.next();
execArgv[entry.getKey()] = entry.getValue(); execArgv[entry.getKey()] = entry.getValue();
@@ -49,6 +49,9 @@ module.exports = function (scriptRuntime, scope) {
Images.prototype = { Images.prototype = {
constructor: Images, constructor: Images,
captureScreen(path) { captureScreen(path) {
if (rtImages.getScreenCapturer() === null) {
this.requestScreenCapture();
}
return path === undefined return path === undefined
? rtImages.captureScreen() ? rtImages.captureScreen()
: rtImages.captureScreen(path); : rtImages.captureScreen(path);
+33 -7
View File
@@ -40,7 +40,7 @@ module.exports = function (scriptRuntime, scope) {
enumerable: true, /* Emphasis. */ enumerable: true, /* Emphasis. */
}; };
const instanceFunc = function (img, options) { const instanceFunc = function __(img, options) {
if (typeof arguments[0] === 'string') { if (typeof arguments[0] === 'string') {
let img = images.read(/* path = */ arguments[0]); let img = images.read(/* path = */ arguments[0]);
if (img === null) { if (img === null) {
@@ -87,7 +87,7 @@ module.exports = function (scriptRuntime, scope) {
// @Signature // @Signature
// recognizeText(imgPath: string, options?: DetectOptionsMLKit | DetectOptionsPaddle): string[]; // recognizeText(imgPath: string, options?: DetectOptionsMLKit | DetectOptionsPaddle): string[];
// recognizeText(imgPath: string, region: Images.Options.Region): string[]; // recognizeText(imgPath: string, region: OmniRegion): string[];
let img = images.read(/* path = */ arguments[0]); let img = images.read(/* path = */ arguments[0]);
if (img === null) { if (img === null) {
@@ -95,12 +95,25 @@ module.exports = function (scriptRuntime, scope) {
} }
// @Overload // @Overload
// recognizeText(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): string[]; // recognizeText(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): string[];
// recognizeText(img: ImageWrapper, region: Images.Options.Region): string[]; // recognizeText(img: ImageWrapper, region: OmniRegion): string[];
return this.recognizeText(img.oneShot(), arguments[1]); return this.recognizeText(img.oneShot(), arguments[1]);
} }
if (!(arguments[0] instanceof ImageWrapper)) {
// @Signature
// recognizeText(options?: DetectOptionsMLKit | DetectOptionsPaddle): string[];
// recognizeText(region: OmniRegion): string[];
// @Overload
// recognizeText(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): string[];
// recognizeText(img: ImageWrapper, region: OmniRegion): string[];
return this.recognizeText.apply(this, [ images.captureScreen() ].concat(Array.from(arguments)));
}
if (_.shouldTakenAsRegion(arguments[1])) { if (_.shouldTakenAsRegion(arguments[1])) {
// @Signature recognizeText(img: ImageWrapper, region: Images.Options.Region): string[]; // @Signature recognizeText(img: ImageWrapper, region: OmniRegion): string[];
// @Overload recognizeText(img: ImageWrapper, options: DetectOptionsMLKit | DetectOptionsPaddle): string[]; // @Overload recognizeText(img: ImageWrapper, options: DetectOptionsMLKit | DetectOptionsPaddle): string[];
return this.recognizeText(img, { region: arguments[1] }); return this.recognizeText(img, { region: arguments[1] });
@@ -144,7 +157,7 @@ module.exports = function (scriptRuntime, scope) {
// @Signature // @Signature
// detect(imgPath: string, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; // detect(imgPath: string, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[];
// detect(imgPath: string, region: Images.Options.Region): org.autojs.autojs.runtime.api.OcrResult[]; // detect(imgPath: string, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[];
let img = images.read(/* path = */ arguments[0]); let img = images.read(/* path = */ arguments[0]);
if (img === null) { if (img === null) {
@@ -152,12 +165,25 @@ module.exports = function (scriptRuntime, scope) {
} }
// @Overload // @Overload
// detect(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; // detect(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[];
// detect(img: ImageWrapper, region: Images.Options.Region): org.autojs.autojs.runtime.api.OcrResult[]; // detect(img: ImageWrapper, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[];
return this.detect(img.oneShot(), arguments[1]); return this.detect(img.oneShot(), arguments[1]);
} }
if (!(arguments[0] instanceof ImageWrapper)) {
// @Signature
// detect(options?: DetectOptionsMLKit | DetectOptionsPaddle): string[];
// detect(region: OmniRegion): string[];
// @Overload
// detect(img: ImageWrapper, options?: DetectOptionsMLKit | DetectOptionsPaddle): string[];
// detect(img: ImageWrapper, region: OmniRegion): string[];
return this.detect.apply(this, [ images.captureScreen() ].concat(Array.from(arguments)));
}
if (_.shouldTakenAsRegion(arguments[1])) { if (_.shouldTakenAsRegion(arguments[1])) {
// @Signature detect(img: ImageWrapper, region: Images.Options.Region): org.autojs.autojs.runtime.api.OcrResult[]; // @Signature detect(img: ImageWrapper, region: OmniRegion): org.autojs.autojs.runtime.api.OcrResult[];
// @Overload detect(img: ImageWrapper, options: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[]; // @Overload detect(img: ImageWrapper, options: DetectOptionsMLKit | DetectOptionsPaddle): org.autojs.autojs.runtime.api.OcrResult[];
return this.detect(img, { region: arguments[1] }); return this.detect(img, { region: arguments[1] });
+121
View File
@@ -0,0 +1,121 @@
// noinspection JSUnusedLocalSymbols,UnnecessaryLocalVariableJS
/* Overwritten protection. */
let { images, qrcode } = global;
/**
* @param {ScriptRuntime} scriptRuntime
* @param {org.mozilla.javascript.Scriptable | global} scope
* @return {Internal.QrCode}
*/
module.exports = function (scriptRuntime, scope) {
let _ = {
QrCodeCtor: (/* @IIFE */ () => {
const instanceFunc = function __(img, options) {
if (typeof arguments[0] === 'string') {
let image = images.read(/* path = */ arguments[0]);
if (image === null) {
throw TypeError(`Invalid image of path "${arguments[0]}" for qrcode(img, options?)`);
}
let args = [ image.oneShot() ].concat(Array.from(arguments).slice(1));
return QrCodeCtor.prototype.recognizeText.apply(QrCodeCtor.prototype, args);
}
return QrCodeCtor.prototype.recognizeText.apply(QrCodeCtor.prototype, arguments);
};
/**
* @implements Internal.QrCode
*/
const QrCodeCtor = function () {
let o = Object.assign(instanceFunc, QrCodeCtor.prototype);
Object.defineProperties(instanceFunc, Object.getOwnPropertyDescriptors(QrCodeCtor.prototype));
return o;
};
QrCodeCtor.prototype = {
constructor: QrCodeCtor,
recognizeTexts() {
let results = this.detectAll.apply(this, arguments);
return results.map(result => result.getRawValue()).filter(o => o !== null);
},
recognizeText() {
let results = this.recognizeTexts.apply(this, arguments);
if (_.shouldTakenAsAll(arguments)) {
return results;
}
return results.length > 0 ? results[0] : null;
},
/**
* @returns {Internal.Barcode.Result[]}
*/
detectAll() {
if (typeof arguments[0] === 'string') {
// @Signature
// detectAll(imgPath: string): QrCode.Result[];
// detectAll(imgPath: string, options: DetectOptionsWithoutIsAll): QrCode.Result[];
let img = images.read(/* path = */ arguments[0]);
if (img === null) {
throw TypeError(`Invalid image of path "${arguments[0]}" for qrcode.detectAll(img, options?)`);
}
// @Overload
// detectAll(img: ImageWrapper): QrCode.Result[];
// detectAll(img: ImageWrapper, options: DetectOptionsWithoutIsAll): QrCode.Result[];
return this.detectAll(img.oneShot(), arguments[1]);
}
if (!(arguments[0] instanceof ImageWrapper)) {
// @Signature detectAll(options?: DetectOptionsWithoutIsAll): QrCode.Result[];
// @Overload detectAll(img: ImageWrapper, options?: DetectOptionsWithoutIsAll): QrCode.Result[];
return this.detectAll.apply(this, [ images.captureScreen() ].concat(Array.from(arguments)));
}
// @Signature detectAll(img: ImageWrapper, options?: DetectOptionsWithoutIsAll): QrCode.Result[];
let img = arguments[0];
/** @type {Internal.QrCode.DetectOptionsWithoutIsAll} */
let opt = /* options */ arguments[1] || {};
let enableAllPotentialBarcodes = Boolean(opt.enableAllPotentialBarcodes);
let formats = [ com.google.mlkit.vision.barcode.common.Barcode.FORMAT_QR_CODE ];
// noinspection JSValidateTypes
return scriptRuntime.barcode.detect(img, formats, enableAllPotentialBarcodes);
},
detect() {
let results = this.detectAll.apply(this, arguments);
if (_.shouldTakenAsAll(arguments)) {
return results;
}
return results.length > 0 ? results[0] : null;
},
};
return QrCodeCtor;
})(),
/**
* @param {IArguments} args
* @returns {boolean}
*/
shouldTakenAsAll(args) {
if (args.length === 0) {
return false;
}
/**
* @type {Internal.QrCode.DetectOptions | boolean | OmniRegion}
*/
let arg = args[args.length - 1];
return arg === true || isObject(arg) && Boolean(arg.isAll);
},
};
/**
* @type {Internal.QrCode}
*/
const qrcode = new _.QrCodeCtor();
return qrcode;
};
@@ -0,0 +1,45 @@
// noinspection UnnecessaryLocalVariableJS,JSUnusedLocalSymbols
/**
* @param {ScriptRuntime} scriptRuntime
* @param {org.mozilla.javascript.Scriptable | global} scope
* @return {Internal.Shizuku}
*/
module.exports = function (scriptRuntime, scope) {
const rtShizuku = scriptRuntime.shizuku;
let _ = {
ShizukuCtor: (/* @IIFE */ () => {
/**
* @implements Internal.Shizuku
*/
const ShizukuCtor = function () {
/** @global */
const shizuku = function (cmd) {
return rtShizuku.execCommand(cmd);
};
return Object.assign(shizuku, ShizukuCtor.prototype);
};
ShizukuCtor.prototype = {
constructor: ShizukuCtor,
ensureService() {
rtShizuku.ensureService();
},
execCommand() {
return rtShizuku.execCommand.apply(rtShizuku, arguments);
},
};
return ShizukuCtor;
})(),
};
/**
* @type {Internal.Shizuku}
*/
const shizuku = new _.ShizukuCtor();
return shizuku;
};
+63 -36
View File
@@ -1,86 +1,113 @@
// noinspection UnnecessaryLocalVariableJS,JSUnusedLocalSymbols // noinspection JSUnusedLocalSymbols,UnnecessaryLocalVariableJS
/** /**
* @param {ScriptRuntime} scriptRuntime * @param {org.autojs.autojs.runtime.ScriptRuntime} scriptRuntime
* @param {org.mozilla.javascript.Scriptable | global} scope * @param {org.mozilla.javascript.Scriptable | global} scope
* @return {Internal.Toast} * @return {Internal.Toast}
*/ */
module.exports = function (scriptRuntime, scope) { module.exports = function (scriptRuntime, scope) {
let rtToast = scriptRuntime.toast;
const ScriptToast = org.autojs.autojs.runtime.api.ScriptToast;
let _ = { let _ = {
uiHandler: runtime.getUiHandler(),
ToastCtor: (/* @IIFE */ () => { ToastCtor: (/* @IIFE */ () => {
/** /**
* @implements Internal.Toast * @implements Internal.Toast
*/ */
const Ctor = function () { const ToastCtor = function () {
// @Caution by SuperMonster003 on Oct 11, 2022.
// ! android.widget.Toast.makeText() doesn't work well on Android API Level 28 (Android 9) [P].
// ! There hasn't been a solution for this so far.
// ! Tested devices:
// ! 1. SONY XPERIA XZ1 Compact (G8441)
// ! 2. Android Studio AVD (Android 9.0 x86)
/** @global */ /** @global */
const instance = function (msg, isLong, isForcible) { const toast = function (msg, isLong, isForcible) {
let $ = { let $ = {
badge: { badge: {
long: /^l(ong)?$/i, long: /^l(ong)?$/i,
short: /^s(hort)?$/i, short: /^s(hort)?$/i,
forcible: /^f(orcible)?$/i, forcible: /^f(orcible)?$/i,
}, },
toast() {
this.init(arguments);
this.show();
},
init() { init() {
this.message = isNullish(msg) ? '' : String(msg); this.message = isNullish(msg) ? '' : String(msg);
this.isForcible = this.parseIsForcible(isForcible);
this.isLong = this.parseIsLong(isLong); this.isLong = this.parseIsLong(isLong);
this.isForcible = this.parseIsForcible(isForcible);
}, },
parseIsLong(isLong) { parseIsLong(o) {
if (typeof isLong === 'boolean') { let def = typeof this.isLong === 'boolean' ? this.isLong : false;
return isLong; if (typeof o === 'boolean') {
return o;
} }
if (typeof isLong === 'number') { if (typeof o === 'number') {
return Boolean(isLong); return Boolean(o);
} }
if (typeof isLong === 'string') { if (typeof o === 'string') {
if (this.badge.long.test(isLong)) { if (this.badge.long.test(o)) {
return true; return true;
} }
if (this.badge.short.test(isLong)) { if (this.badge.short.test(o)) {
return false; return false;
} }
if (this.badge.forcible.test(isLong)) { if (this.badge.forcible.test(o)) {
this.isForcible = true; this.isForcible = true;
return false; return def;
} }
throw Error(`Invalid param: {name: isLong, value: ${isLong}, type: ${species(isLong)}.`); throw Error(`Invalid param: {name: isLong, value: ${o}, type: ${species(o)}.`);
} }
return false; return def;
}, },
parseIsForcible(isForcible) { parseIsForcible(o) {
if (typeof isForcible === 'boolean') { let def = typeof this.isForcible === 'boolean' ? this.isForcible : false;
return isForcible; if (typeof o === 'boolean') {
return o;
} }
if (typeof isForcible === 'number') { if (typeof o === 'number') {
return Boolean(isForcible); return Boolean(o);
} }
if (typeof isForcible === 'string') { if (typeof o === 'string') {
if (this.badge.forcible.test(isForcible)) { if (this.badge.forcible.test(o)) {
return true; return true;
} }
throw Error(`Invalid param: {name: isForcible, value: ${isForcible}, type: ${species(isForcible)}.`); if (this.badge.long.test(o)) {
this.isLong = true;
return def;
}
if (this.badge.short.test(o)) {
this.isLong = false;
return def;
}
throw Error(`Invalid param: {name: isForcible, value: ${o}, type: ${species(o)}.`);
} }
return false; return def;
}, },
makeToast() { show() {
rtToast.makeToast(this.message, this.isLong, this.isForcible); if ($.isForcible) {
ToastCtor.prototype.dismissAll();
}
scriptRuntime.uiHandler.toast($.message, $.isLong);
}, },
}; };
$.init(); $.toast();
$.makeToast();
}; };
return Object.assign(instance, Ctor.prototype); return Object.assign(toast, ToastCtor.prototype);
}; };
Ctor.prototype = { ToastCtor.prototype = {
constructor: Ctor, constructor: ToastCtor,
dismissAll: () => rtToast.dismissAll(), dismissAll() {
runtime.uiHandler.dismissAllToasts();
},
}; };
return Ctor; return ToastCtor;
})(), })(),
}; };
+3 -3
View File
@@ -317,7 +317,7 @@ module.exports = function (scriptRuntime, scope) {
})(); })();
}, },
afterApplyAttribute(context, inflater, view, ns, attrName, value, parent) { afterApplyAttribute(context, inflater, view, ns, attrName, value, parent) {
// Empty method body /* Empty body. */
}, },
afterApplyAttributes(context, view, inflater, attrs, parent) { afterApplyAttributes(context, view, inflater, attrs, parent) {
context.remove('widget'); context.remove('widget');
@@ -326,13 +326,13 @@ module.exports = function (scriptRuntime, scope) {
return false; return false;
}, },
afterInflateChildren(context, inflater, node, parent) { afterInflateChildren(context, inflater, node, parent) {
// Empty method body /* Empty body. */
}, },
beforeApplyPendingAttributesOfChildren(context, inflater, view) { beforeApplyPendingAttributesOfChildren(context, inflater, view) {
return false; return false;
}, },
afterApplyPendingAttributesOfChildren(context, inflater, view) { afterApplyPendingAttributesOfChildren(context, inflater, view) {
// Empty method body /* Empty body. */
}, },
afterInflateView(context, view, node, parent, attachToParent) { afterInflateView(context, view, node, parent, attachToParent) {
let { widget } = view; let { widget } = view;
+6 -2
View File
@@ -15,12 +15,16 @@ module.exports = function (scriptRuntime, scope) {
* @implements Internal.OcrMLKit * @implements Internal.OcrMLKit
*/ */
const OcrMLKitCtor = function () { const OcrMLKitCtor = function () {
return Object.assign(function () { let instanceFunc = function __() {
let argArray = Array.from(arguments); let argArray = Array.from(arguments);
if (arguments.length === 0 || !(arguments[0] instanceof ImageWrapper)) {
return __.apply(this, [ images.captureScreen() ].concat(argArray));
}
/** @type {Internal.Ocr.ModeName} */ /** @type {Internal.Ocr.ModeName} */
argArray[ocr['_forcibleModeArgIndex']] = ocr['_modeName']['mlkit']; argArray[ocr['_forcibleModeArgIndex']] = ocr['_modeName']['mlkit'];
return ocr.recognizeText.apply(ocr, argArray); return ocr.recognizeText.apply(ocr, argArray);
}, OcrMLKitCtor.prototype); };
return Object.assign(instanceFunc, OcrMLKitCtor.prototype);
}; };
OcrMLKitCtor.prototype = { OcrMLKitCtor.prototype = {
+6 -2
View File
@@ -15,12 +15,16 @@ module.exports = function (scriptRuntime, scope) {
* @implements Internal.OcrPaddle * @implements Internal.OcrPaddle
*/ */
const OcrPaddleCtor = function () { const OcrPaddleCtor = function () {
return Object.assign(function () { const instanceFunc = function __() {
let argArray = Array.from(arguments); let argArray = Array.from(arguments);
if (arguments.length === 0 || !(arguments[0] instanceof ImageWrapper)) {
return __.apply(this, [ images.captureScreen() ].concat(argArray));
}
/** @type {Internal.Ocr.ModeName} */ /** @type {Internal.Ocr.ModeName} */
argArray[ocr['_forcibleModeArgIndex']] = ocr['_modeName']['paddle']; argArray[ocr['_forcibleModeArgIndex']] = ocr['_modeName']['paddle'];
return ocr.recognizeText.apply(ocr, argArray); return ocr.recognizeText.apply(ocr, argArray);
}, OcrPaddleCtor.prototype); };
return Object.assign(instanceFunc, OcrPaddleCtor.prototype);
}; };
OcrPaddleCtor.prototype = { OcrPaddleCtor.prototype = {
@@ -147,11 +147,11 @@ assert(util.getClass(java.lang.String) === java.lang.String, 'util.getClass()');
assert(util.getClassName(java.lang.String) === 'java.lang.String', 'util.getClassName()'); assert(util.getClassName(java.lang.String) === 'java.lang.String', 'util.getClassName()');
assert(isFunc(new (function () { assert(isFunc(new (function () {
let C = function () { let C = function () {
// Empty constructor /* Empty body. */
}; };
util.extend(C, (function () { util.extend(C, (function () {
let P = function () { let P = function () {
// Empty constructor /* Empty body. */
}; };
P.prototype.getName = () => context.packageName; P.prototype.getName = () => context.packageName;
return P; return P;
@@ -20,7 +20,7 @@ import org.autojs.autojs.engine.RootAutomatorEngine
import org.autojs.autojs.engine.ScriptEngineManager import org.autojs.autojs.engine.ScriptEngineManager
import org.autojs.autojs.engine.ScriptEngineService import org.autojs.autojs.engine.ScriptEngineService
import org.autojs.autojs.engine.ScriptEngineServiceBuilder import org.autojs.autojs.engine.ScriptEngineServiceBuilder
import org.autojs.autojs.inrt.autojs.XJavaScriptEngine import org.autojs.autojs.inrt.autojs.LoopBasedJavaScriptEngineWithDecryption
import org.autojs.autojs.pref.Pref.registerOnSharedPreferenceChangeListener import org.autojs.autojs.pref.Pref.registerOnSharedPreferenceChangeListener
import org.autojs.autojs.rhino.InterruptibleAndroidContextFactory import org.autojs.autojs.rhino.InterruptibleAndroidContextFactory
import org.autojs.autojs.runtime.ScriptRuntime import org.autojs.autojs.runtime.ScriptRuntime
@@ -35,6 +35,7 @@ import org.autojs.autojs.util.ResourceMonitor
import org.autojs.autojs.util.ResourceMonitor.UnclosedResourceException import org.autojs.autojs.util.ResourceMonitor.UnclosedResourceException
import org.autojs.autojs.util.StringUtils import org.autojs.autojs.util.StringUtils
import org.autojs.autojs.util.ViewUtils import org.autojs.autojs.util.ViewUtils
import org.autojs.autojs6.BuildConfig
import org.autojs.autojs6.R import org.autojs.autojs6.R
import org.mozilla.javascript.ContextFactory import org.mozilla.javascript.ContextFactory
import org.mozilla.javascript.WrappedException import org.mozilla.javascript.WrappedException
@@ -51,6 +52,12 @@ abstract class AbstractAutoJs protected constructor(protected val application: A
lateinit var runtime: ScriptRuntime lateinit var runtime: ScriptRuntime
private set private set
private val scriptEngine
get() = when (BuildConfig.isInrt) {
true -> LoopBasedJavaScriptEngineWithDecryption(context)
else -> LoopBasedJavaScriptEngine(context)
}
val context: Context = application.applicationContext val context: Context = application.applicationContext
val appUtils by lazy { createAppUtils(context) } val appUtils by lazy { createAppUtils(context) }
val globalConsole by lazy { createGlobalConsole() } val globalConsole by lazy { createGlobalConsole() }
@@ -58,19 +65,11 @@ abstract class AbstractAutoJs protected constructor(protected val application: A
val layoutInspector = LayoutInspector(context) val layoutInspector = LayoutInspector(context)
val uiHandler = UiHandler(context) val uiHandler = UiHandler(context)
val infoProvider = ActivityInfoProvider(context) val infoProvider = ActivityInfoProvider(context)
val scriptEngineManager = ScriptEngineManager(context)
val scriptEngineService: ScriptEngineService = run { val scriptEngineService: ScriptEngineService = run {
if (org.autojs.autojs6.BuildConfig.isInrt) { val scriptEngineManager = ScriptEngineManager(context)
scriptEngineManager.registerEngine(JavaScriptSource.ENGINE) { scriptEngineManager.registerEngine(JavaScriptSource.ENGINE) {
XJavaScriptEngine(context).also { engine -> scriptEngine.also { engine ->
engine.runtime = createRuntime().also { runtime = it } engine.runtime = createRuntime().also { runtime = it }
}
}
} else {
scriptEngineManager.registerEngine(JavaScriptSource.ENGINE) {
LoopBasedJavaScriptEngine(context).also { engine ->
engine.runtime = createRuntime().also { runtime = it }
}
} }
} }
initContextFactory() initContextFactory()
+2 -23
View File
@@ -20,8 +20,6 @@ import com.bumptech.glide.request.transition.Transition
import com.flurry.android.FlurryAgent import com.flurry.android.FlurryAgent
import com.tencent.bugly.Bugly import com.tencent.bugly.Bugly
import com.tencent.bugly.crashreport.CrashReport import com.tencent.bugly.crashreport.CrashReport
import com.zeugmasolutions.localehelper.LocaleHelper
import com.zeugmasolutions.localehelper.LocaleHelperApplicationDelegate
import org.autojs.autojs.app.GlobalAppContext import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.core.accessibility.AccessibilityTool import org.autojs.autojs.core.accessibility.AccessibilityTool
import org.autojs.autojs.core.ui.inflater.ImageLoader import org.autojs.autojs.core.ui.inflater.ImageLoader
@@ -38,11 +36,9 @@ import org.autojs.autojs.ui.floating.FloatyWindowManger
import org.autojs.autojs.util.ViewUtils import org.autojs.autojs.util.ViewUtils
import org.autojs.autojs6.BuildConfig import org.autojs.autojs6.BuildConfig
import org.autojs.autojs6.R import org.autojs.autojs6.R
import org.lsposed.hiddenapibypass.HiddenApiBypass
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.lang.reflect.Method import java.lang.reflect.Method
/** /**
* Created by Stardust on 2017/1/27. * Created by Stardust on 2017/1/27.
* Modified by SuperMonster003 as of Aug 23, 2022. * Modified by SuperMonster003 as of Aug 23, 2022.
@@ -55,8 +51,6 @@ class App : MultiDexApplication() {
lateinit var devPluginService: DevPluginService lateinit var devPluginService: DevPluginService
private set private set
private val localeAppDelegate = LocaleHelperApplicationDelegate()
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@@ -79,21 +73,8 @@ class App : MultiDexApplication() {
} }
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {
// @Caution by SuperMonster003 on Aug 2, 2023.
// ! This will cause AutoJs6 not being aware of
// ! configuration changes (like orientation and so forth).
// super.attachBaseContext(localeAppDelegate.attachBaseContext(base))
super.attachBaseContext(base) super.attachBaseContext(base)
Log.d("Shizuku", "${App::class.java.simpleName} attachBaseContext | Process=${getProcessNameCompat()}")
// @Dubious by SuperMonster003 on Aug 2, 2023.
// ! Locale helper may be not work as expected ?
localeAppDelegate.attachBaseContext(base)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
HiddenApiBypass.addHiddenApiExemptions("L");
}
Log.d("Shizuku", "${App::class.java.getSimpleName()} attachBaseContext | Process=${getProcessNameCompat()}")
} }
private fun setUpStaticsTool() { private fun setUpStaticsTool() {
@@ -129,6 +110,7 @@ class App : MultiDexApplication() {
else -> ViewUtils.MODE.DAY else -> ViewUtils.MODE.DAY
} }
} }
else -> ViewUtils.MODE.NULL else -> ViewUtils.MODE.NULL
} }
) )
@@ -234,14 +216,11 @@ class App : MultiDexApplication() {
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
localeAppDelegate.onConfigurationChanged(this)
ViewUtils.onConfigurationChanged(newConfig) ViewUtils.onConfigurationChanged(newConfig)
FloatyWindowManger.getCircularMenu()?.savePosition(newConfig) FloatyWindowManger.getCircularMenu()?.savePosition(newConfig)
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
} }
override fun getApplicationContext() = LocaleHelper.onAttach(super.getApplicationContext())
companion object { companion object {
private const val BUGLY_APP_ID = "19b3607b53" private const val BUGLY_APP_ID = "19b3607b53"
@@ -1,5 +1,6 @@
package org.autojs.autojs package org.autojs.autojs
import android.annotation.SuppressLint
import android.app.Application import android.app.Application
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
@@ -30,8 +31,10 @@ import org.autojs.autojs.ui.main.MainActivity
import org.autojs.autojs.ui.project.BuildActivity import org.autojs.autojs.ui.project.BuildActivity
import org.autojs.autojs.ui.settings.AboutActivity import org.autojs.autojs.ui.settings.AboutActivity
import org.autojs.autojs.ui.settings.PreferencesActivity import org.autojs.autojs.ui.settings.PreferencesActivity
import org.autojs.autojs6.BuildConfig
import org.autojs.autojs6.R import org.autojs.autojs6.R
import java.util.concurrent.Executors import java.util.concurrent.Executors
import org.autojs.autojs.inrt.autojs.AutoJs as AutoJsInrt
/** /**
* Created by Stardust on 2017/4/2. * Created by Stardust on 2017/4/2.
@@ -179,6 +182,7 @@ open class AutoJs(private val appContext: Application) : AbstractAutoJs(appConte
companion object { companion object {
private var isInitialized = false private var isInitialized = false
@SuppressLint("StaticFieldLeak")
@JvmStatic @JvmStatic
lateinit var instance: AutoJs lateinit var instance: AutoJs
private set private set
@@ -187,7 +191,10 @@ open class AutoJs(private val appContext: Application) : AbstractAutoJs(appConte
@JvmStatic @JvmStatic
fun initInstance(application: Application) { fun initInstance(application: Application) {
if (!isInitialized) { if (!isInitialized) {
instance = AutoJs(application) instance = when (BuildConfig.isInrt) {
true -> AutoJsInrt(application)
else -> AutoJs(application)
}
isInitialized = true isInitialized = true
} }
} }
@@ -1,7 +1,10 @@
package org.autojs.autojs.apkbuilder package org.autojs.autojs.apkbuilder
import android.content.res.AssetManager
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.util.Log
import com.reandroid.arsc.chunk.TableBlock
import org.autojs.autojs.apkbuilder.util.StreamUtils import org.autojs.autojs.apkbuilder.util.StreamUtils
import org.autojs.autojs.app.GlobalAppContext import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.engine.encryption.AdvancedEncryptionStandard import org.autojs.autojs.engine.encryption.AdvancedEncryptionStandard
@@ -30,9 +33,14 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
private var mInitVector: String? = null private var mInitVector: String? = null
private var mKey: String? = null private var mKey: String? = null
private val mAssetManager: AssetManager by lazy { GlobalAppContext.get().assets }
private val mManifestFile private val mManifestFile
get() = File(mWorkspacePath, "AndroidManifest.xml") get() = File(mWorkspacePath, "AndroidManifest.xml")
private val mResourcesArscFile
get() = File(mWorkspacePath, "resources.arsc")
private lateinit var mAppConfig: AppConfig private lateinit var mAppConfig: AppConfig
init { init {
@@ -43,9 +51,10 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
@Throws(IOException::class) @Throws(IOException::class)
fun prepare() = also { fun prepare() = also {
mProgressCallback?.run { GlobalAppContext.post { onPrepare(this@ApkBuilder) } } mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onPrepare(this) } }
File(mWorkspacePath).mkdirs() File(mWorkspacePath).mkdirs()
mApkPackager.unzip() mApkPackager.unzip()
copyAssetsRecursively("", File(mWorkspacePath, "assets"))
} }
@Throws(IOException::class) @Throws(IOException::class)
@@ -147,13 +156,24 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
@Throws(IOException::class) @Throws(IOException::class)
fun build() = also { fun build() = also {
mProgressCallback?.run { GlobalAppContext.post { onBuild(this@ApkBuilder) } } mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onBuild(this) } }
mAppConfig.icon?.run { mAppConfig.icon?.let { callable ->
try { try {
call()?.compress( val tableBlock = TableBlock.load(mResourcesArscFile)
Bitmap.CompressFormat.PNG, 100, val packageName = "${GlobalAppContext.get().packageName}.inrt"
FileOutputStream(File(mWorkspacePath, "res/mipmap/ic_launcher.png")) val packageBlock = tableBlock.getOrCreatePackage(0x7f, packageName).also {
) tableBlock.currentPackage = it
}
val appIcon = packageBlock.getOrCreate("", ICON_RES_DIR, ICON_NAME)
val appIconPath = appIcon.resValue.decodeValue()
Log.d(TAG, "Icon path: $appIconPath")
val file = File(mWorkspacePath, appIconPath).also {
if (!it.exists()) {
File(it.parent!!).mkdirs()
it.createNewFile()
}
}
callable.call()?.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream(file))
} catch (e: Exception) { } catch (e: Exception) {
throw RuntimeException(e) throw RuntimeException(e)
} }
@@ -162,16 +182,36 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
mArscPackageName?.let { buildArsc() } mArscPackageName?.let { buildArsc() }
} }
private fun copyAssetsRecursively(assetPath: String, targetFile: File) {
if (targetFile.isFile && targetFile.exists()) return
val list = mAssetManager.list(assetPath) ?: return
if (list.isEmpty()) /* asset is file */ {
mAssetManager.open(assetPath).use { input ->
FileOutputStream(targetFile.absolutePath).use { output ->
input.copyTo(output)
output.flush()
}
}
} else /* asset is folder */ {
targetFile.delete()
targetFile.mkdir()
list.forEach {
val sourcePath = if (assetPath.isEmpty()) it else "$assetPath/$it"
copyAssetsRecursively(sourcePath, File(targetFile, it))
}
}
}
@Throws(Exception::class) @Throws(Exception::class)
fun sign() = also { fun sign() = also {
mProgressCallback?.run { GlobalAppContext.post { onSign(this@ApkBuilder) } } mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onSign(this) } }
val fos = FileOutputStream(mOutApkFile) val fos = FileOutputStream(mOutApkFile)
TinySign.sign(File(mWorkspacePath), fos) TinySign.sign(File(mWorkspacePath), fos)
fos.close() fos.close()
} }
fun cleanWorkspace() = also { fun cleanWorkspace() = also {
mProgressCallback?.run { GlobalAppContext.post { onClean(this@ApkBuilder) } } mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onClean(this) } }
delete(File(mWorkspacePath)) delete(File(mWorkspacePath))
} }
@@ -258,4 +298,13 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
} }
} }
companion object {
private val TAG = ApkBuilder::class.java.simpleName
const val ICON_NAME = "ic_launcher"
const val ICON_RES_DIR = "mipmap"
}
} }
@@ -19,11 +19,12 @@ import java.io.InputStream;
public class ApkBuilderPluginHelper { public class ApkBuilderPluginHelper {
private static final String PLUGIN_PACKAGE_NAME = "org.autojs.apkbuilderplugin"; private static final String PLUGIN_PACKAGE_NAME = "org.autojs.apkbuilderplugin";
private static final String TEMPLATE_APK_PATH = "template.apk";
private static final boolean DEBUG_APK_PLUGIN = false; private static final boolean DEBUG_APK_PLUGIN = false;
public static final String TEMPLATE_APK_PATH = "template.apk";
public static boolean isPluginAvailable(Context context) { public static boolean isPluginAvailable(Context context) {
// return DeveloperUtils.checkSignature(context, PLUGIN_PACKAGE_NAME); // return DeveloperUtils.checkSignature(context, PLUGIN_PACKAGE_NAME);
try { try {
context.getPackageManager().getPackageInfo(PLUGIN_PACKAGE_NAME, 0); context.getPackageManager().getPackageInfo(PLUGIN_PACKAGE_NAME, 0);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
@@ -55,5 +56,3 @@ public class ApkBuilderPluginHelper {
} }
} }
} }
@@ -22,7 +22,6 @@ public class ManifestEditor {
private String mPackageName; private String mPackageName;
private byte[] mManifestData; private byte[] mManifestData;
public ManifestEditor(InputStream manifestInputStream) { public ManifestEditor(InputStream manifestInputStream) {
mManifestInputStream = manifestInputStream; mManifestInputStream = manifestInputStream;
} }
@@ -90,7 +89,6 @@ public class ManifestEditor {
} }
} }
private class MutableAxmlWriter extends AxmlWriter { private class MutableAxmlWriter extends AxmlWriter {
private class MutableNodeImpl extends AxmlWriter.NodeImpl { private class MutableNodeImpl extends AxmlWriter.NodeImpl {
@@ -9,7 +9,7 @@ import android.provider.Settings.Secure
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import org.autojs.autojs.pref.Pref import org.autojs.autojs.pref.Pref
import org.autojs.autojs.runtime.api.ProcessShell.execCommand import org.autojs.autojs.runtime.api.ProcessShell
import org.autojs.autojs.util.RootUtils import org.autojs.autojs.util.RootUtils
import org.autojs.autojs.util.SettingsUtils import org.autojs.autojs.util.SettingsUtils
import org.autojs.autojs.util.ViewUtils import org.autojs.autojs.util.ViewUtils
@@ -102,10 +102,10 @@ class AccessibilityTool(val context: Context) {
val services = getEnabledWithRoot(true) val services = getEnabledWithRoot(true)
val cmdServices = "settings put secure enabled_accessibility_services $services" val cmdServices = "settings put secure enabled_accessibility_services $services"
val resultServices = execCommand(cmdServices, true) val resultServices = ProcessShell.execCommand(cmdServices, true)
val cmdState = "settings put secure accessibility_enabled 1" val cmdState = "settings put secure accessibility_enabled 1"
val resultState = execCommand(cmdState, true) val resultState = ProcessShell.execCommand(cmdState, true)
TextUtils.isEmpty(resultServices.error) && TextUtils.isEmpty(resultState.error) TextUtils.isEmpty(resultServices.error) && TextUtils.isEmpty(resultState.error)
} catch (e: Exception) { } catch (e: Exception) {
@@ -136,10 +136,10 @@ class AccessibilityTool(val context: Context) {
val services = getEnabledWithRoot(false) val services = getEnabledWithRoot(false)
val cmdServices = "settings put secure enabled_accessibility_services $services" val cmdServices = "settings put secure enabled_accessibility_services $services"
val resultServices = execCommand(cmdServices, true) val resultServices = ProcessShell.execCommand(cmdServices, true)
val cmdState = "settings put secure accessibility_enabled 0" val cmdState = "settings put secure accessibility_enabled 0"
val resultState = execCommand(cmdState, true) val resultState = ProcessShell.execCommand(cmdState, true)
TextUtils.isEmpty(resultServices.error) && TextUtils.isEmpty(resultState.error) TextUtils.isEmpty(resultServices.error) && TextUtils.isEmpty(resultState.error)
} catch (e: Exception) { } catch (e: Exception) {
@@ -159,7 +159,7 @@ class AccessibilityTool(val context: Context) {
false -> detachAutoJs(getEnabledWithRoot()) false -> detachAutoJs(getEnabledWithRoot())
else -> { else -> {
val cmd = "settings get secure enabled_accessibility_services" val cmd = "settings get secure enabled_accessibility_services"
execCommand(cmd, true).result.takeUnless { it == "null" }?.replace("\n", "") ?: "" ProcessShell.execCommand(cmd, true).result.takeUnless { it == "null" }?.replace("\n", "") ?: ""
} }
} }
@@ -34,18 +34,19 @@ import android.accessibilityservice.AccessibilityService as AndroidAccessibility
*/ */
class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge, private val scriptRuntime: ScriptRuntime) { class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge, private val scriptRuntime: ScriptRuntime) {
private val globalActionAutomatorRaw by lazy { private val globalActionAutomatorRaw
GlobalActionAutomator(Handler(scriptRuntime.loopers.servantLooper)) { get() = GlobalActionAutomator(Handler(scriptRuntime.loopers.servantLooper)) {
ensureService() ensureService()
accessibilityBridge.service!! accessibilityBridge.service!!
} }
private val globalActionAutomator by lazy {
globalActionAutomatorRaw
} }
private val globalActionAutomator private val globalActionAutomatorForGesture by lazy {
get() = globalActionAutomatorRaw globalActionAutomatorRaw.apply { setScreenMetrics(mScreenMetrics) }
}
private val globalActionAutomatorForGesture
get() = globalActionAutomatorRaw.apply { setScreenMetrics(mScreenMetrics) }
private val isRunningPackageSelf private val isRunningPackageSelf
get() = DeveloperUtils.isSelfPackage(accessibilityBridge.infoProvider.latestPackage) get() = DeveloperUtils.isSelfPackage(accessibilityBridge.infoProvider.latestPackage)
@@ -332,6 +332,7 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
// SecurityException: https://github.com/hyb1996-guest/AutoJsIssueReport/issues/4781 // SecurityException: https://github.com/hyb1996-guest/AutoJsIssueReport/issues/4781
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
ScriptRuntime.popException(e.message)
uiHandler.toast(R.string.error_no_display_over_other_apps_permission) uiHandler.toast(R.string.error_no_display_over_other_apps_permission)
} }
} }
@@ -25,11 +25,13 @@ class Loopers(val runtime: ScriptRuntime) {
@Volatile @Volatile
private var mServantLooper: Looper? = null private var mServantLooper: Looper? = null
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
private var mMainLooperQuitHandler: LooperQuitHandler? = null private var mMainLooperQuitHandler: LooperQuitHandler? = null
private var waitWhenIdle: Boolean
private val allTasks = ConcurrentLinkedQueue<AsyncTask>()
private var mWaitWhenIdle: Boolean
private val allTasks = ConcurrentLinkedQueue<AsyncTask>()
private val lock = ReentrantLock() private val lock = ReentrantLock()
private val condition = lock.newCondition() private val condition = lock.newCondition()
@@ -55,7 +57,7 @@ class Loopers(val runtime: ScriptRuntime) {
prepare() prepare()
myLooper = Looper.myLooper()!! myLooper = Looper.myLooper()!!
timer = Timer(runtime, myLooper) timer = Timer(runtime, myLooper)
waitWhenIdle = myLooper == Looper.getMainLooper() mWaitWhenIdle = myLooper == Looper.getMainLooper()
} }
@Deprecated("Deprecated in Java", ReplaceWith("AsyncTask")) @Deprecated("Deprecated in Java", ReplaceWith("AsyncTask"))
@@ -111,10 +113,9 @@ class Loopers(val runtime: ScriptRuntime) {
return when { return when {
Thread.currentThread().isInterrupted -> true Thread.currentThread().isInterrupted -> true
timer.hasPendingCallbacks() -> false timer.hasPendingCallbacks() -> false
// 检查是否有运行中的线程 checkTask() /* 检查是否有运行中的线程. */ -> false
checkTask() -> false (Context.getCurrentContext() as? AutoJsContext)?.hasPendingContinuation() == true -> false
waitWhenIdle -> false mWaitWhenIdle -> false
(Context.getCurrentContext() as AutoJsContext).hasPendingContinuation() -> false
else -> true else -> true
} }
} }
@@ -133,7 +134,7 @@ class Loopers(val runtime: ScriptRuntime) {
@Deprecated("Deprecated in Java", ReplaceWith("AsyncTask")) @Deprecated("Deprecated in Java", ReplaceWith("AsyncTask"))
fun waitWhenIdle(b: Boolean) { fun waitWhenIdle(b: Boolean) {
waitWhenIdle = b mWaitWhenIdle = b
} }
fun recycle() { fun recycle() {
@@ -160,7 +161,7 @@ class Loopers(val runtime: ScriptRuntime) {
Looper.myQueue().addIdleHandler(MessageQueue.IdleHandler { Looper.myQueue().addIdleHandler(MessageQueue.IdleHandler {
if (this == runtime.loopers) { if (this == runtime.loopers) {
Log.d(LOG_TAG, "main looper queueIdle") Log.d(LOG_TAG, "main looper queueIdle")
if (shouldQuitLooper() && mMainLooperQuitHandler?.shouldQuit() == true) { if (shouldQuitLooper() && mMainLooperQuitHandler?.shouldQuit() != false) {
Log.d(LOG_TAG, "main looper quit") Log.d(LOG_TAG, "main looper quit")
Looper.myLooper()?.quitSafely() Looper.myLooper()?.quitSafely()
} }
@@ -0,0 +1,61 @@
package org.autojs.autojs.core.shizuku;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.Keep;
import org.autojs.autojs.runtime.api.AbstractShell;
import org.autojs.autojs.runtime.api.ProcessShell;
public class UserService extends IUserService.Stub {
/**
* Constructor is required.
*/
public UserService() {
Log.i("UserService", "constructor");
}
/**
* Constructor with Context. This is only available from Shizuku API v13.
* <p>
* This method need to be annotated with {@link Keep} to prevent ProGuard from removing it.
*
* @param context Context created with createPackageContextAsUser
* @see <a href="https://github.com/RikkaApps/Shizuku-API/blob/672f5efd4b33c2441dbf609772627e63417587ac/server-shared/src/main/java/rikka/shizuku/server/UserService.java#L66">code used to create the instance of this class</a>
*/
@Keep
public UserService(Context context) {
Log.i("UserService", "constructor with Context: context=" + context.toString());
}
/**
* Reserved destroy method
*/
@Override
public void destroy() {
Log.i("UserService", "destroy");
}
@Override
public void exit() {
destroy();
}
@Override
public String execCommand(String command) throws RemoteException {
try {
return ProcessShell
.execCommand(command.split("\n"), ProcessShell.getShellProcess())
.toJson();
} catch (Exception e) {
AbstractShell.Result result = new AbstractShell.Result();
result.code = 1;
result.error = e.getMessage();
return result.toJson();
}
}
}
@@ -5,6 +5,7 @@ import android.util.Log
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import org.autojs.autojs.core.inputevent.InputDevices import org.autojs.autojs.core.inputevent.InputDevices
import org.autojs.autojs.pio.PFiles import org.autojs.autojs.pio.PFiles
import org.autojs.autojs.runtime.api.AbstractShell
import org.autojs.autojs.runtime.api.ProcessShell import org.autojs.autojs.runtime.api.ProcessShell
import org.autojs.autojs.runtime.exception.ScriptException import org.autojs.autojs.runtime.exception.ScriptException
import org.autojs.autojs.runtime.exception.ScriptInterruptedException import org.autojs.autojs.runtime.exception.ScriptInterruptedException
@@ -50,7 +51,7 @@ class RootAutomatorEngine @JvmOverloads constructor(private val mContext: Contex
"exit", "exit",
) )
try { try {
Runtime.getRuntime().exec("su").also { Runtime.getRuntime().exec(AbstractShell.COMMAND_SU).also {
mProcess = it mProcess = it
executeCommands(it, commands) executeCommands(it, commands)
mPid = readPid(it) mPid = readPid(it)
@@ -86,8 +86,10 @@ data class ExecutionConfig(
companion object CREATOR : Parcelable.Creator<ExecutionConfig> { companion object CREATOR : Parcelable.Creator<ExecutionConfig> {
const val tag = "execution.config"
@JvmStatic @JvmStatic
val tag = "execution.config" fun getTag() = tag
@JvmStatic @JvmStatic
val default: ExecutionConfig val default: ExecutionConfig
@@ -30,7 +30,7 @@ public class RunnableScriptExecution extends ScriptExecution.AbstractScriptExecu
public Object execute() { public Object execute() {
mScriptEngine = mScriptEngineManager.createEngineOfSourceOrThrow(getSource(), getId()); mScriptEngine = mScriptEngineManager.createEngineOfSourceOrThrow(getSource(), getId());
mScriptEngine.setTag(ExecutionConfig.getTag(), getConfig()); mScriptEngine.setTag(ExecutionConfig.tag, getConfig());
return execute(mScriptEngine); return execute(mScriptEngine);
} }
@@ -144,6 +144,7 @@ public class ScriptExecuteActivity extends AppCompatActivity {
mScriptEngine.destroy(); mScriptEngine.destroy();
} }
mScriptExecution = null; mScriptExecution = null;
mRuntime.loopers.waitWhenIdle(false);
} }
@Override @Override
@@ -243,7 +244,7 @@ public class ScriptExecuteActivity extends AppCompatActivity {
mScriptEngine.forceStop(); mScriptEngine.forceStop();
} }
mScriptEngine = mScriptEngineManager.createEngineOfSourceOrThrow(getSource(), getId()); mScriptEngine = mScriptEngineManager.createEngineOfSourceOrThrow(getSource(), getId());
mScriptEngine.setTag(ExecutionConfig.getTag(), getConfig()); mScriptEngine.setTag(ExecutionConfig.tag, getConfig());
return mScriptEngine; return mScriptEngine;
} }
@@ -3,7 +3,7 @@ package org.autojs.autojs.external.foreground
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import org.autojs.autojs.tool.ForegroundServiceCreator import org.autojs.autojs.tool.ForegroundServiceCreator
import org.autojs.autojs.ui.main.MainActivity.Companion.getIntent import org.autojs.autojs.ui.main.MainActivity
import org.autojs.autojs.util.ForegroundServiceUtils import org.autojs.autojs.util.ForegroundServiceUtils
import org.autojs.autojs6.R import org.autojs.autojs6.R
@@ -23,7 +23,7 @@ class MainActivityForegroundService : Service() {
ForegroundServiceUtils.startForeground( ForegroundServiceUtils.startForeground(
ForegroundServiceCreator.Builder(this) ForegroundServiceCreator.Builder(this)
.setClassName(sClassName) .setClassName(sClassName)
.setIntent(getIntent(this)) .setIntent(MainActivity.getIntent(this))
.setNotificationId(NOTIFICATION_ID) .setNotificationId(NOTIFICATION_ID)
.setServiceName(R.string.foreground_notification_channel_name) .setServiceName(R.string.foreground_notification_channel_name)
.setServiceDescription(R.string.foreground_notification_channel_name) .setServiceDescription(R.string.foreground_notification_channel_name)
@@ -18,7 +18,6 @@ import java.util.List;
@RequiresApi(api = Build.VERSION_CODES.N_MR1) @RequiresApi(api = Build.VERSION_CODES.N_MR1)
public class ShortcutManager { public class ShortcutManager {
private static ShortcutManager sInstance; private static ShortcutManager sInstance;
private final Context mContext; private final Context mContext;
private final android.content.pm.ShortcutManager mShortcutManager; private final android.content.pm.ShortcutManager mShortcutManager;
@@ -15,6 +15,7 @@ import org.autojs.autojs.model.explorer.ExplorerFileProvider;
import org.autojs.autojs.model.script.Scripts; import org.autojs.autojs.model.script.Scripts;
import org.autojs.autojs.ui.BaseActivity; import org.autojs.autojs.ui.BaseActivity;
import org.autojs.autojs.ui.explorer.ExplorerView; import org.autojs.autojs.ui.explorer.ExplorerView;
import org.autojs.autojs.util.WorkingDirectoryUtils;
import org.autojs.autojs6.R; import org.autojs.autojs6.R;
import org.autojs.autojs6.databinding.ActivityScriptWidgetSettingsBinding; import org.autojs.autojs6.databinding.ActivityScriptWidgetSettingsBinding;
@@ -42,9 +43,10 @@ public class ScriptWidgetSettingsActivity extends BaseActivity {
private void initScriptListRecyclerView() { private void initScriptListRecyclerView() {
mExplorer = new Explorer(new ExplorerFileProvider(Scripts.getFILE_FILTER()), 0); mExplorer = new Explorer(new ExplorerFileProvider(Scripts.FILE_FILTER), 0);
ExplorerView explorerView = binding.scriptList; ExplorerView explorerView = binding.scriptList;
explorerView.setExplorer(mExplorer, ExplorerDirPage.createRoot(Environment.getExternalStorageDirectory())); String workingDirPath = WorkingDirectoryUtils.getPath();
explorerView.setExplorer(mExplorer, ExplorerDirPage.createRoot(workingDirPath));
explorerView.setOnItemClickListener((view, file) -> { explorerView.setOnItemClickListener((view, file) -> {
mSelectedScriptFilePath = file.getPath(); mSelectedScriptFilePath = file.getPath();
finish(); finish();
@@ -1,86 +0,0 @@
package org.autojs.autojs.inrt
import android.app.Application
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.net.Uri
import android.view.View
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
import org.autojs.autojs.inrt.App
import org.autojs.autojs.AutoJs
import org.autojs.autojs.inrt.autojs.GlobalKeyObserver
import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.core.ui.inflater.ImageLoader
import org.autojs.autojs.core.ui.inflater.util.Drawables
import java.lang.ref.WeakReference
/**
* Created by Stardust on 2017/7/1.
*/
class App : Application() {
companion object {
private const val BUGLY_APP_ID = "19b3607b53"
private lateinit var instance: WeakReference<App>
val app: App
get() = instance.get()!!
}
override fun onCreate() {
super.onCreate()
GlobalAppContext.set(this)
instance = WeakReference(this)
AutoJs.initInstance(this)
GlobalKeyObserver.init()
Drawables.defaultImageLoader=object : ImageLoader {
override fun loadInto(imageView: ImageView, uri: Uri) {
Glide.with(this@App)
.load(uri)
.into(imageView)
}
override fun loadIntoBackground(view: View, uri: Uri) {
Glide.with(this@App)
.load(uri)
.into(object : SimpleTarget<Drawable>() {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
view.background = resource
}
})
}
override fun load(view: View, uri: Uri): Drawable {
throw UnsupportedOperationException()
}
override fun load(view: View, uri: Uri, drawableCallback: ImageLoader.DrawableCallback) {
Glide.with(this@App)
.load(uri)
.into(object : SimpleTarget<Drawable>() {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
drawableCallback.onLoaded(resource)
}
})
}
override fun load(view: View, uri: Uri, bitmapCallback: ImageLoader.BitmapCallback) {
Glide.with(this@App)
.asBitmap()
.load(uri)
.into(object : SimpleTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
bitmapCallback.onLoaded(resource)
}
})
}
}
}
}
@@ -8,7 +8,7 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import org.autojs.autojs.AutoJs import org.autojs.autojs.inrt.autojs.AutoJs
import org.autojs.autojs.inrt.launch.GlobalProjectLauncher import org.autojs.autojs.inrt.launch.GlobalProjectLauncher
import org.autojs.autojs.core.console.ConsoleImpl import org.autojs.autojs.core.console.ConsoleImpl
import org.autojs.autojs.core.console.ConsoleView import org.autojs.autojs.core.console.ConsoleView
@@ -12,7 +12,7 @@ import android.widget.Toast
import androidx.annotation.NonNull import androidx.annotation.NonNull
import androidx.annotation.Nullable import androidx.annotation.Nullable
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import org.autojs.autojs.AutoJs import org.autojs.autojs.inrt.autojs.AutoJs
import org.autojs.autojs.inrt.launch.GlobalProjectLauncher import org.autojs.autojs.inrt.launch.GlobalProjectLauncher
import org.autojs.autojs6.R import org.autojs.autojs6.R
@@ -1,98 +1,28 @@
package org.autojs.autojs.inrt.autojs package org.autojs.autojs.inrt.autojs
import android.annotation.SuppressLint
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.inrt.LogActivity import org.autojs.autojs.inrt.LogActivity
import org.autojs.autojs.inrt.SettingsActivity import org.autojs.autojs.inrt.SettingsActivity
import org.autojs.autojs.core.accessibility.AccessibilityTool
import org.autojs.autojs.core.accessibility.AccessibilityService
import org.autojs.autojs.runtime.ScriptRuntime
import org.autojs.autojs.runtime.api.AppUtils import org.autojs.autojs.runtime.api.AppUtils
import org.autojs.autojs.runtime.exception.ScriptException import org.autojs.autojs.AutoJs as AutoJsApp
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
import org.autojs.autojs.script.JavaScriptSource
import org.autojs.autojs6.R
/** /**
* Created by Stardust on 2017/4/2. * Created by Stardust on 2017/4/2.
*/ */
class AutoJs private constructor(application: Application) : org.autojs.autojs.AutoJs(application) { class AutoJs(application: Application) : AutoJsApp(application) {
init { override fun createAppUtils(context: Context) = AppUtils(context, context.packageName + ".fileprovider")
scriptEngineService.registerGlobalScriptExecutionListener(ScriptExecutionGlobalListener())
scriptEngineManager.registerEngine(JavaScriptSource.ENGINE) {
val engine = XJavaScriptEngine(application)
engine.runtime = createRuntime()
engine
}
}
override fun createAppUtils(context: Context): AppUtils { override fun createRuntime() = super.createRuntime().apply {
return AppUtils(context, context.packageName + ".fileprovider") putProperty("class.settings", SettingsActivity::class.java)
} putProperty("class.console", LogActivity::class.java)
override fun ensureAccessibilityServiceEnabled() {
if (AccessibilityService.instance != null) {
return
}
var errorMessage: String? = null
if (isAccessibilityServiceEnabled()) {
errorMessage = GlobalAppContext.get().getString(R.string.text_a11y_service_enabled_but_not_running)
} else if (!getAccessibilityServiceTool().service.enableIfNeededAndWaitFor(2000)) {
errorMessage = GlobalAppContext.get().getString(R.string.error_no_accessibility_permission)
}
if (errorMessage != null) {
getAccessibilityServiceTool().launchSettings()
throw ScriptException(errorMessage)
}
}
override fun waitForAccessibilityServiceEnabled(timeout: Long) {
if (AccessibilityService.instance != null) {
return
}
var errorMessage: String? = null
if (isAccessibilityServiceEnabled()) {
errorMessage = GlobalAppContext.get().getString(R.string.text_a11y_service_enabled_but_not_running)
} else if (!getAccessibilityServiceTool().service.enableIfNeededAndWaitFor(2000)) {
errorMessage = GlobalAppContext.get().getString(R.string.error_no_accessibility_permission)
}
if (errorMessage != null) {
getAccessibilityServiceTool().launchSettings()
if (!AccessibilityService.waitForEnabled(timeout)) {
throw ScriptInterruptedException()
}
}
}
override fun createRuntime(): ScriptRuntime {
val runtime = super.createRuntime()
runtime.putProperty("class.settings", SettingsActivity::class.java)
runtime.putProperty("class.console", LogActivity::class.java)
return runtime
}
private fun getAccessibilityServiceTool(): AccessibilityTool {
return AccessibilityTool(application)
}
private fun isAccessibilityServiceEnabled(): Boolean {
return getAccessibilityServiceTool().service.isEnabled()
} }
companion object { companion object {
@SuppressLint("StaticFieldLeak") val instance
@JvmStatic get() = AutoJsApp.instance
lateinit var instance: AutoJs
private set
@Synchronized
fun initInstance(application: Application) {
instance = AutoJs(application)
}
} }
} }
@@ -1,93 +0,0 @@
package org.autojs.autojs.inrt.autojs
import android.util.Log
import android.view.KeyEvent
import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.inrt.Pref
import org.autojs.autojs.core.accessibility.AccessibilityService
import org.autojs.autojs.core.accessibility.OnKeyListener
import org.autojs.autojs.core.inputevent.InputEventObserver
import org.autojs.autojs.core.inputevent.ShellKeyObserver
/**
* Created by Stardust on 2017/8/14.
*/
class GlobalKeyObserver internal constructor() : OnKeyListener, ShellKeyObserver.KeyListener {
private var mVolumeDownFromShell: Boolean = false
private var mVolumeDownFromAccessibility: Boolean = false
private var mVolumeUpFromShell: Boolean = false
private var mVolumeUpFromAccessibility: Boolean = false
init {
AccessibilityService.stickOnKeyObserver
.addListener(this)
val observer = ShellKeyObserver()
observer.setKeyListener(this)
InputEventObserver.getGlobal(GlobalAppContext.get()).addListener(observer)
}
fun onVolumeUp() {
Log.d(LOG_TAG, "onVolumeUp at " + System.currentTimeMillis())
if (Pref.shouldStopAllScriptsWhenVolumeUp()) {
AutoJs.instance.scriptEngineService.stopAllAndToast()
}
}
override fun onKeyEvent(keyCode: Int, event: KeyEvent) {
if (event.action != KeyEvent.ACTION_UP)
return
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if (mVolumeDownFromShell) {
mVolumeDownFromShell = false
return
}
mVolumeUpFromAccessibility = true
onVolumeDown()
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
if (mVolumeUpFromShell) {
mVolumeUpFromShell = false
return
}
mVolumeUpFromAccessibility = true
onVolumeUp()
}
}
fun onVolumeDown() {
}
override fun onKeyDown(keyName: String) {
}
override fun onKeyUp(keyName: String) {
if ("KEY_VOLUMEUP" == keyName) {
if (mVolumeUpFromAccessibility) {
mVolumeUpFromAccessibility = false
return
}
mVolumeUpFromShell = true
onVolumeUp()
} else if ("KEY_VOLUMEDOWN" == keyName) {
if (mVolumeDownFromAccessibility) {
mVolumeDownFromAccessibility = false
return
}
mVolumeDownFromShell = true
onVolumeDown()
}
}
companion object {
private const val LOG_TAG = "GlobalKeyObserver"
private val sSingleton = GlobalKeyObserver()
fun init() {
//do nothing
}
}
}
@@ -0,0 +1,33 @@
package org.autojs.autojs.inrt.autojs
import android.content.Context
import org.autojs.autojs.engine.LoopBasedJavaScriptEngine
import org.autojs.autojs.engine.encryption.ScriptEncryption
import org.autojs.autojs.pio.PFiles
import org.autojs.autojs.script.EncryptedScriptFileHeader
import org.autojs.autojs.script.EncryptedScriptFileHeader.BLOCK_SIZE
import org.autojs.autojs.script.JavaScriptFileSource
import org.autojs.autojs.script.ScriptSource
import org.autojs.autojs.script.StringScriptSource
class LoopBasedJavaScriptEngineWithDecryption(context: Context) : LoopBasedJavaScriptEngine(context) {
override fun execute(source: ScriptSource, callback: ExecuteCallback?) {
if (source is JavaScriptFileSource) {
try {
val file = source.file
val bytes = PFiles.readBytes(file.path)
if (EncryptedScriptFileHeader.isValidFile(bytes)) {
super.execute(StringScriptSource(file.name, String(ScriptEncryption.decrypt(bytes, BLOCK_SIZE))), callback)
} else {
super.execute(source, callback)
}
} catch (e: Throwable) {
e.printStackTrace()
}
} else {
super.execute(source, callback)
}
}
}
@@ -1,36 +0,0 @@
package org.autojs.autojs.inrt.autojs
import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.execution.ScriptExecution
import org.autojs.autojs.execution.ScriptExecutionListener
import org.autojs.autojs6.R
/**
* Created by Stardust on 2017/5/3.
*/
class ScriptExecutionGlobalListener : ScriptExecutionListener {
override fun onStart(execution: ScriptExecution) {
execution.engine.setTag(ENGINE_TAG_START_TIME, System.currentTimeMillis())
}
override fun onSuccess(execution: ScriptExecution, result: Any?) {
onFinish(execution)
}
private fun onFinish(execution: ScriptExecution) {
val millis = execution.engine.getTag(ENGINE_TAG_START_TIME) as Long? ?: return
val seconds = (System.currentTimeMillis() - millis) / 1000.0
AutoJs.instance.scriptEngineService.globalConsole
.verbose(GlobalAppContext.get().getString(R.string.text_execution_finished), execution.source.toString(), seconds)
}
override fun onException(execution: ScriptExecution, e: Throwable) {
onFinish(execution)
}
companion object {
private const val ENGINE_TAG_START_TIME = "start_time"
}
}
@@ -1,44 +0,0 @@
package org.autojs.autojs.inrt.autojs
import android.content.Context
import org.autojs.autojs.engine.LoopBasedJavaScriptEngine
import org.autojs.autojs.engine.encryption.ScriptEncryption
import org.autojs.autojs.pio.PFiles
import org.autojs.autojs.script.EncryptedScriptFileHeader
import org.autojs.autojs.script.JavaScriptFileSource
import org.autojs.autojs.script.ScriptSource
import org.autojs.autojs.script.StringScriptSource
import java.io.File
import java.security.GeneralSecurityException
class XJavaScriptEngine(context: Context) : LoopBasedJavaScriptEngine(context) {
override fun execute(source: ScriptSource, callback: ExecuteCallback?) {
if (source is JavaScriptFileSource) {
try {
if (execute(source.file)) {
return
}
} catch (e: Throwable) {
e.printStackTrace()
return
}
}
super.execute(source, callback)
}
private fun execute(file: File): Boolean {
val bytes = PFiles.readBytes(file.path)
if (!EncryptedScriptFileHeader.isValidFile(bytes)) {
return false
}
try {
super.execute(StringScriptSource(file.name, String(ScriptEncryption.decrypt(bytes, EncryptedScriptFileHeader.BLOCK_SIZE))))
} catch (e: GeneralSecurityException) {
e.printStackTrace()
}
return true
}
}
@@ -8,7 +8,7 @@ import android.os.Looper
import android.text.TextUtils import android.text.TextUtils
import org.autojs.autojs.inrt.LogActivity import org.autojs.autojs.inrt.LogActivity
import org.autojs.autojs.inrt.Pref import org.autojs.autojs.inrt.Pref
import org.autojs.autojs.AutoJs import org.autojs.autojs.inrt.autojs.AutoJs
import org.autojs.autojs.engine.encryption.ScriptEncryption import org.autojs.autojs.engine.encryption.ScriptEncryption
import org.autojs.autojs.execution.ExecutionConfig import org.autojs.autojs.execution.ExecutionConfig
import org.autojs.autojs.execution.ScriptExecution import org.autojs.autojs.execution.ScriptExecution
@@ -36,7 +36,7 @@ object Scripts {
const val EXTRA_EXCEPTION_LINE_NUMBER = "lineNumber" const val EXTRA_EXCEPTION_LINE_NUMBER = "lineNumber"
const val EXTRA_EXCEPTION_COLUMN_NUMBER = "columnNumber" const val EXTRA_EXCEPTION_COLUMN_NUMBER = "columnNumber"
@JvmStatic @JvmField
val FILE_FILTER = FileFilter { file -> val FILE_FILTER = FileFilter { file ->
file.isDirectory || file.isDirectory ||
file.name.endsWith(FileUtils.TYPE.JAVASCRIPT.extension) || file.name.endsWith(FileUtils.TYPE.JAVASCRIPT.extension) ||
@@ -34,7 +34,8 @@ class DisplayOverOtherAppsPermission(override val context: Context) : Permission
fun config() { fun config() {
try { try {
SettingsCompat.manageDrawOverlays(context) SettingsCompat.manageDrawOverlays(context)
} catch (ex: Exception) { } catch (e: Exception) {
e.printStackTrace()
FloatingWindowPermissionUtil.goToAppDetailSettings(context, context.packageName) FloatingWindowPermissionUtil.goToAppDetailSettings(context, context.packageName)
} }
} }
@@ -75,7 +75,7 @@ class ManageAllFilesPermission(override val context: Context) : PermissionItemHe
.onNegative { dialog, _ -> dialog.dismiss() } .onNegative { dialog, _ -> dialog.dismiss() }
.onPositive { dialog, _ -> .onPositive { dialog, _ ->
request() request()
MainActivity.setShouldRecreateMainActivity(true) MainActivity.shouldRecreateMainActivity = true
dialog.dismiss() dialog.dismiss()
} }
.cancelable(false) .cancelable(false)
@@ -9,10 +9,9 @@ import java.util.Locale
enum class Language(val locale: Locale, @KeyRes private val keyRes: Int, private val entryNameRes: Int) { enum class Language(val locale: Locale, @KeyRes private val keyRes: Int, private val entryNameRes: Int) {
AUTO(LocaleUtils.getSystemLocale(), R.string.key_app_language_auto, R.string.entry_app_language_auto), ZH_HANS(Locale.forLanguageTag("zh-Hans"), R.string.key_app_language_zh_hans, R.string.entry_app_language_zh_hans),
ZH(Locale.forLanguageTag("zh"), R.string.key_app_language_zh, R.string.entry_app_language_zh), ZH_HANT_HK(Locale.forLanguageTag("zh-Hant-HK"), R.string.key_app_language_zh_hant_hk, R.string.entry_app_language_zh_hant_hk),
ZH_HK(Locale.forLanguageTag("zh-hk"), R.string.key_app_language_zh_hk, R.string.entry_app_language_zh_hk), ZH_HANT_TW(Locale.forLanguageTag("zh-Hant-TW"), R.string.key_app_language_zh_hant_tw, R.string.entry_app_language_zh_hant_tw),
ZH_TW(Locale.forLanguageTag("zh-tw"), R.string.key_app_language_zh_tw, R.string.entry_app_language_zh_tw),
EN(Locale.forLanguageTag("en"), R.string.key_app_language_en, R.string.entry_app_language_en), EN(Locale.forLanguageTag("en"), R.string.key_app_language_en, R.string.entry_app_language_en),
FR(Locale.forLanguageTag("fr"), R.string.key_app_language_fr, R.string.entry_app_language_fr), FR(Locale.forLanguageTag("fr"), R.string.key_app_language_fr, R.string.entry_app_language_fr),
ES(Locale.forLanguageTag("es"), R.string.key_app_language_es, R.string.entry_app_language_es), ES(Locale.forLanguageTag("es"), R.string.key_app_language_es, R.string.entry_app_language_es),
@@ -20,6 +19,7 @@ enum class Language(val locale: Locale, @KeyRes private val keyRes: Int, private
KO(Locale.forLanguageTag("ko"), R.string.key_app_language_ko, R.string.entry_app_language_ko), KO(Locale.forLanguageTag("ko"), R.string.key_app_language_ko, R.string.entry_app_language_ko),
RU(Locale.forLanguageTag("ru"), R.string.key_app_language_ru, R.string.entry_app_language_ru), RU(Locale.forLanguageTag("ru"), R.string.key_app_language_ru, R.string.entry_app_language_ru),
AR(Locale.forLanguageTag("ar"), R.string.key_app_language_ar, R.string.entry_app_language_ar), AR(Locale.forLanguageTag("ar"), R.string.key_app_language_ar, R.string.entry_app_language_ar),
AUTO(LocaleUtils.getSystemLocale(), R.string.key_app_language_auto, R.string.entry_app_language_auto),
; ;
fun getEntryName(context: Context) = context.getString(entryNameRes) fun getEntryName(context: Context) = context.getString(entryNameRes)
@@ -31,9 +31,39 @@ enum class Language(val locale: Locale, @KeyRes private val keyRes: Int, private
companion object { companion object {
@JvmStatic @JvmStatic
fun getPrefLanguage() = Language.values().find { fun getPrefLanguage() = getPrefLanguageOrNull() ?: AUTO
@JvmStatic
fun getPrefLanguageOrNull() = Language.values().find {
it.getKey() == Pref.getString(R.string.key_app_language, null) it.getKey() == Pref.getString(R.string.key_app_language, null)
} ?: AUTO }
fun setPrefLanguage(locale: Locale) {
val candidates = Language.values().filter { it != AUTO }
val splitLocale = locale.toLanguageTag().split("-")
var maxLettersLen = 3
do {
candidates.find {
if (maxLettersLen > splitLocale.size) {
return@find false
}
val splitCandidate = it.locale.toLanguageTag().split("-")
if (maxLettersLen > splitCandidate.size) {
return@find false
}
return@find splitCandidate.subList(0, maxLettersLen).joinToString("-")
.startsWith(splitLocale.subList(0, maxLettersLen).joinToString("-"), true)
}?.let {
Pref.putString(R.string.key_app_language, it.getKey()).also { return }
}
} while (--maxLettersLen > 0)
Pref.putString(R.string.key_app_language, null)
}
fun setPrefLanguageAuto() {
Pref.putString(R.string.key_app_language, AUTO.getKey())
}
} }
@@ -95,13 +95,6 @@ object Pref {
resources.getBoolean(R.bool.pref_auto_check_for_updates), resources.getBoolean(R.bool.pref_auto_check_for_updates),
) )
@JvmStatic
val isDocumentationLauncherIconShouldShow: Boolean
get() {
val shouldShow = key(R.string.key_documentation_launcher_icon_show)
return getString(R.string.key_documentation_launcher_icon, shouldShow) == shouldShow
}
private val lastUpdatesCheckedTimestamp: Long private val lastUpdatesCheckedTimestamp: Long
get() = getTimestamp(R.string.key_last_updates_checked) get() = getTimestamp(R.string.key_last_updates_checked)
@@ -24,7 +24,7 @@ public class ProjectConfig {
public static final String CONFIG_FILE_NAME = "project.json"; public static final String CONFIG_FILE_NAME = "project.json";
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); private static final Gson sGson = new GsonBuilder().setPrettyPrinting().create();
@SerializedName("name") @SerializedName("name")
private String mName; private String mName;
@@ -64,7 +64,7 @@ public class ProjectConfig {
if (json == null) { if (json == null) {
return null; return null;
} }
ProjectConfig config = GSON.fromJson(json, ProjectConfig.class); ProjectConfig config = sGson.fromJson(json, ProjectConfig.class);
if (!isValid(config)) { if (!isValid(config)) {
return null; return null;
} }
@@ -209,7 +209,7 @@ public class ProjectConfig {
} }
public String toJson() { public String toJson() {
return GSON.toJson(this); return sGson.toJson(this);
} }
public String getIcon() { public String getIcon() {
@@ -1161,7 +1161,7 @@ public class TokenStream {
} }
// is it a string? // is it a string?
if (c == '"' || c == '\'') { if (c == '"' || c == '\'' || c == '`') {
// We attempt to accumulate a string the fast way, by // We attempt to accumulate a string the fast way, by
// building it directly out of the reader. But if there // building it directly out of the reader. But if there
// are any escaped characters in the string, we revert to // are any escaped characters in the string, we revert to
@@ -1646,7 +1646,7 @@ public class TokenStream {
this.string = getStringFromBuffer(); this.string = getStringFromBuffer();
return Token.XML; return Token.XML;
} }
case '\'', '"' -> { case '\'', '"', '`' -> {
addToString(c); addToString(c);
if (!readQuotedString(c)) if (!readQuotedString(c))
return Token.ERROR; return Token.ERROR;
@@ -7,7 +7,6 @@ import android.os.Build;
import android.os.Looper; import android.os.Looper;
import android.util.Log; import android.util.Log;
import org.autojs.autojs.core.permission.Permissions;
import org.autojs.autojs.AutoJs; import org.autojs.autojs.AutoJs;
import org.autojs.autojs.annotation.ScriptInterface; import org.autojs.autojs.annotation.ScriptInterface;
import org.autojs.autojs.annotation.ScriptVariable; import org.autojs.autojs.annotation.ScriptVariable;
@@ -22,6 +21,7 @@ import org.autojs.autojs.core.image.Colors;
import org.autojs.autojs.core.image.ImageWrapper; import org.autojs.autojs.core.image.ImageWrapper;
import org.autojs.autojs.core.image.capture.ScreenCaptureRequester; import org.autojs.autojs.core.image.capture.ScreenCaptureRequester;
import org.autojs.autojs.core.looper.Loopers; import org.autojs.autojs.core.looper.Loopers;
import org.autojs.autojs.core.permission.Permissions;
import org.autojs.autojs.core.web.WebSocket; import org.autojs.autojs.core.web.WebSocket;
import org.autojs.autojs.engine.ScriptEngineService; import org.autojs.autojs.engine.ScriptEngineService;
import org.autojs.autojs.lang.ThreadCompat; import org.autojs.autojs.lang.ThreadCompat;
@@ -32,6 +32,7 @@ import org.autojs.autojs.rhino.TopLevelScope;
import org.autojs.autojs.rhino.continuation.Continuation; import org.autojs.autojs.rhino.continuation.Continuation;
import org.autojs.autojs.runtime.api.AbstractShell; import org.autojs.autojs.runtime.api.AbstractShell;
import org.autojs.autojs.runtime.api.AppUtils; import org.autojs.autojs.runtime.api.AppUtils;
import org.autojs.autojs.runtime.api.Barcode;
import org.autojs.autojs.runtime.api.Device; import org.autojs.autojs.runtime.api.Device;
import org.autojs.autojs.runtime.api.Dialogs; import org.autojs.autojs.runtime.api.Dialogs;
import org.autojs.autojs.runtime.api.Engines; import org.autojs.autojs.runtime.api.Engines;
@@ -50,6 +51,7 @@ import org.autojs.autojs.runtime.api.Sensors;
import org.autojs.autojs.runtime.api.Threads; import org.autojs.autojs.runtime.api.Threads;
import org.autojs.autojs.runtime.api.Timers; import org.autojs.autojs.runtime.api.Timers;
import org.autojs.autojs.runtime.api.UI; import org.autojs.autojs.runtime.api.UI;
import org.autojs.autojs.runtime.api.WrappedShizuku;
import org.autojs.autojs.runtime.exception.ScriptEnvironmentException; import org.autojs.autojs.runtime.exception.ScriptEnvironmentException;
import org.autojs.autojs.runtime.exception.ScriptException; import org.autojs.autojs.runtime.exception.ScriptException;
import org.autojs.autojs.runtime.exception.ScriptInterruptedException; import org.autojs.autojs.runtime.exception.ScriptInterruptedException;
@@ -85,6 +87,8 @@ public class ScriptRuntime {
private static final String TAG = "ScriptRuntime"; private static final String TAG = "ScriptRuntime";
public boolean isExiting;
public static void popException(String message) { public static void popException(String message) {
try { try {
throw new Exception(message); throw new Exception(message);
@@ -244,10 +248,13 @@ public class ScriptRuntime {
private final Images images; private final Images images;
@ScriptVariable @ScriptVariable
public final ScriptToast toast; public final OcrMLKit ocrMLKit;
@ScriptVariable @ScriptVariable
public final OcrMLKit ocrMLKit; public final Barcode barcode;
@ScriptVariable
public final WrappedShizuku shizuku;
@ScriptVariable @ScriptVariable
public final OcrPaddle ocrPaddle; public final OcrPaddle ocrPaddle;
@@ -288,7 +295,8 @@ public class ScriptRuntime {
ocrMLKit = new OcrMLKit(); ocrMLKit = new OcrMLKit();
ocrPaddle = new OcrPaddle(); ocrPaddle = new OcrPaddle();
toast = new ScriptToast(context, this); barcode = new Barcode();
shizuku = WrappedShizuku.INSTANCE;
} }
public void init() { public void init() {
@@ -531,6 +539,7 @@ public class ScriptRuntime {
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
public void onExit() { public void onExit() {
Log.d(TAG, "on exit"); Log.d(TAG, "on exit");
this.isExiting = true;
ignoresException(() -> { ignoresException(() -> {
if (console.getConfigurator().isExitOnClose()) { if (console.getConfigurator().isExitOnClose()) {
@@ -555,6 +564,7 @@ public class ScriptRuntime {
ignoresException(floaty::closeAll); ignoresException(floaty::closeAll);
ignoresException(() -> events.emit("exit"), "exception on exit: %s"); ignoresException(() -> events.emit("exit"), "exception on exit: %s");
ignoresException(() -> ScriptToast.clear(this));
ignoresException(threads::shutDownAll); ignoresException(threads::shutDownAll);
ignoresException(events::recycle); ignoresException(events::recycle);
@@ -5,6 +5,10 @@ import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import org.autojs.autojs.engine.RootAutomatorEngine; import org.autojs.autojs.engine.RootAutomatorEngine;
/** /**
@@ -13,26 +17,42 @@ import org.autojs.autojs.engine.RootAutomatorEngine;
public abstract class AbstractShell { public abstract class AbstractShell {
public static class Result { public static class Result {
private static final Gson sGson = new GsonBuilder().setPrettyPrinting().create();
@SerializedName("code")
public int code = -1; public int code = -1;
public String error;
public String result; @SerializedName("result")
public String result = "";
@SerializedName("error")
public String error = "";
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {
return "ShellResult{" + return "ShellResult{" +
"code=" + code + "code=" + code +
", error='" + error + '\'' + ", error=\"" + error + '"' +
", result='" + result + '\'' + ", result=\"" + result + '"' +
'}'; '}';
} }
public String toJson() {
return sGson.toJson(this);
}
public static Result fromJson(String json) {
return sGson.fromJson(json, Result.class);
}
} }
protected static final String COMMAND_SU = "su"; public static final String COMMAND_SU = "su";
protected static final String COMMAND_SH = "sh"; public static final String COMMAND_SH = "sh";
protected static final String COMMAND_EXIT = "exit\n"; public static final String COMMAND_LINE_END = "\n";
protected static final String COMMAND_LINE_END = "\n"; public static final String COMMAND_EXIT = "exit" + COMMAND_LINE_END;
private int mTouchDevice = -1; private int mTouchDevice = -1;
private ScreenMetrics mScreenMetrics; private ScreenMetrics mScreenMetrics;
@@ -91,7 +111,6 @@ public abstract class AbstractShell {
mScreenMetrics = screenMetrics; mScreenMetrics = screenMetrics;
} }
public void Touch(int x, int y) { public void Touch(int x, int y) {
TouchX(x); TouchX(x);
TouchY(y); TouchY(y);
@@ -184,11 +184,9 @@ class AppUtils {
companion object { companion object {
@JvmStatic const val activityShortFormPrefix = "class."
val activityShortFormPrefix = "class."
@JvmStatic const val broadcastShortFormPrefix = "broadcast."
val broadcastShortFormPrefix = "broadcast."
enum class ActivityShortForm(val shortName: String) { enum class ActivityShortForm(val shortName: String) {
@@ -237,23 +235,12 @@ class AppUtils {
@JvmStatic @JvmStatic
fun isActivityShortForm(s: String) = ActivityShortForm.values().any { it.shortName.contentEquals(s) } fun isActivityShortForm(s: String) = ActivityShortForm.values().any { it.shortName.contentEquals(s) }
@JvmStatic
fun showLauncherIcon(context: Context, cls: Class<*>) {
context.packageManager.setComponentEnabledSetting(ComponentName(context, cls), COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP)
}
@JvmStatic
fun hideLauncherIcon(context: Context, cls: Class<*>) {
context.packageManager.setComponentEnabledSetting(ComponentName(context, cls), COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP)
}
fun getInstalledApplications(context: Context): List<ApplicationInfo> = context.packageManager.let { fun getInstalledApplications(context: Context): List<ApplicationInfo> = context.packageManager.let {
when { when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> { Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
it.getInstalledApplications(ApplicationInfoFlags.of(GET_META_DATA.toLong())) it.getInstalledApplications(ApplicationInfoFlags.of(GET_META_DATA.toLong()))
} }
else -> { else -> {
@Suppress("DEPRECATION")
it.getInstalledApplications(GET_META_DATA) it.getInstalledApplications(GET_META_DATA)
} }
} }
@@ -0,0 +1,72 @@
package org.autojs.autojs.runtime.api
import android.util.Log
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.common.InputImage
import org.autojs.autojs.core.image.ImageWrapper
import com.google.mlkit.vision.barcode.common.Barcode as MLKitBarcode
/**
* Created by SuperMonster003 on Oct 10, 2023.
*/
class Barcode {
@JvmOverloads
fun detect(image: ImageWrapper?, formats: IntArray = intArrayOf(), enableAllPotentialBarcodes: Boolean = false): Array<WrappedBarcode> {
image?.takeUnless { image.isRecycled } ?: return emptyArray()
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(MLKitBarcode.FORMAT_ALL_FORMATS, *formats)
.apply { if (enableAllPotentialBarcodes) enableAllPotentialBarcodes() }
.build()
val scanner = BarcodeScanning.getClient(options)
val inputImage = InputImage.fromBitmap(image.bitmap, 0)
var results: Array<WrappedBarcode>? = null
val resultScan = scanner.process(inputImage)
.addOnSuccessListener { barcodes: MutableList<MLKitBarcode> ->
results = barcodes.map { WrappedBarcode(it) }.toTypedArray()
lockNotify()
}
.addOnFailureListener { e ->
e.printStackTrace()
lockNotify()
}
.addOnCanceledListener { lockNotify() }
.addOnCompleteListener { lockNotify() }
while (!resultScan.isComplete) {
synchronized(lock) {
try {
lock.wait(50)
} catch (_: InterruptedException) {
// Ignored.
}
}
}
image.shoot()
if (!resultScan.isSuccessful) {
Log.w(TAG, "Barcode scanning is not successful")
}
return results ?: emptyArray()
}
private fun lockNotify() = synchronized(lock) { lock.notify() }
companion object {
private val lock = Object()
private val TAG: String = Companion::class.java.simpleName
const val TYPE_NAME_UNKNOWN = "TYPE_NAME_UNKNOWN"
const val FORMAT_NAME_UNKNOWN = "FORMAT_NAME_UNKNOWN"
}
}
@@ -0,0 +1,15 @@
package org.autojs.autojs.runtime.api
import com.google.mlkit.vision.barcode.common.internal.BarcodeSource
interface IWrappedBarcode : BarcodeSource {
fun getFormatName(): String
fun getValueTypeName(): String
fun getType(): Int
fun getTypeName(): String
}
@@ -18,6 +18,7 @@ import android.view.Gravity;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.autojs.autojs.annotation.ScriptInterface;
import org.autojs.autojs.annotation.ScriptVariable; import org.autojs.autojs.annotation.ScriptVariable;
import org.autojs.autojs.concurrent.VolatileDispose; import org.autojs.autojs.concurrent.VolatileDispose;
import org.autojs.autojs.core.image.ColorFinder; import org.autojs.autojs.core.image.ColorFinder;
@@ -371,6 +372,7 @@ public class Images {
return new Mat(mat, roi); return new Mat(mat, roi);
} }
@ScriptInterface
public void initOpenCvIfNeeded() { public void initOpenCvIfNeeded() {
if (mOpenCvInitialized || OpenCVHelper.isInitialized()) { if (mOpenCvInitialized || OpenCVHelper.isInitialized()) {
return; return;
@@ -388,7 +390,6 @@ public class Images {
}); });
result.blockedGet(); result.blockedGet();
} }
} }
private void initSuccessfully() { private void initSuccessfully() {
@@ -396,4 +397,8 @@ public class Images {
Log.i(TAG, "opencv: initialized"); Log.i(TAG, "opencv: initialized");
} }
public ScreenCapturer getScreenCapturer() {
return mScreenCapturer;
}
} }
@@ -69,6 +69,7 @@ class OcrMLKit {
} }
fun recognizeText(image: ImageWrapper?): Array<String> { fun recognizeText(image: ImageWrapper?): Array<String> {
image?.takeUnless { image.isRecycled } ?: return emptyArray()
initIfNeeded() initIfNeeded()
val words = detect(image).sorted() val words = detect(image).sorted()
val output = mutableListOf<String>() val output = mutableListOf<String>()
@@ -55,8 +55,8 @@ public class ProcessShell extends AbstractShell {
public void exec(String command) { public void exec(String command) {
try { try {
mCommandOutputStream.writeBytes(command); mCommandOutputStream.writeBytes(command);
if (!command.endsWith(AbstractShell.COMMAND_LINE_END)) { if (!command.endsWith(COMMAND_LINE_END)) {
mCommandOutputStream.writeBytes(AbstractShell.COMMAND_LINE_END); mCommandOutputStream.writeBytes(COMMAND_LINE_END);
} }
mCommandOutputStream.flush(); mCommandOutputStream.flush();
} catch (IOException e) { } catch (IOException e) {
@@ -92,7 +92,7 @@ public class ProcessShell extends AbstractShell {
@Override @Override
public void exitAndWaitFor() { public void exitAndWaitFor() {
exec(AbstractShell.COMMAND_EXIT); exec(COMMAND_EXIT);
waitFor(); waitFor();
exit(); exit();
} }
@@ -118,7 +118,7 @@ public class ProcessShell extends AbstractShell {
try { try {
String line; String line;
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
sb.append(line).append("\n"); sb.append(line).append(COMMAND_LINE_END);
} }
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
@@ -151,7 +151,7 @@ public class ProcessShell extends AbstractShell {
} }
public static Result exec(String command, boolean isRoot) { public static Result exec(String command, boolean isRoot) {
String[] commands = command.split("\n"); String[] commands = command.split(COMMAND_LINE_END);
return exec(commands, isRoot); return exec(commands, isRoot);
} }
@@ -162,7 +162,7 @@ public class ProcessShell extends AbstractShell {
for (String command : commands) { for (String command : commands) {
shell.exec(command); shell.exec(command);
} }
shell.exec(AbstractShell.COMMAND_EXIT); shell.exec(COMMAND_EXIT);
Result result = new Result(); Result result = new Result();
result.code = shell.waitFor(); result.code = shell.waitFor();
shell.readAll(); shell.readAll();
@@ -178,22 +178,35 @@ public class ProcessShell extends AbstractShell {
} }
public static Result execCommand(String[] commands, boolean isRoot) { public static Result execCommand(String[] commands, boolean isRoot) {
try {
Process process = isRoot ? getRootProcess() : getShellProcess();
return execCommand(commands, process);
} catch (Exception e) {
Result result = new Result();
String message = e.getMessage();
result.code = 1;
result.result = "";
result.error = message != null ? message : "";
return result;
}
}
public static Result execCommand(String[] commands, Process process) {
Result commandResult = new Result(); Result commandResult = new Result();
if (commands == null || commands.length == 0) if (commands == null || commands.length == 0) {
throw new IllegalArgumentException(str(R.string.error_empty_shell_command)); throw new IllegalArgumentException(str(R.string.error_empty_shell_command));
Process process = null; }
DataOutputStream os = null; DataOutputStream os = null;
try { try {
process = Runtime.getRuntime().exec(isRoot ? AbstractShell.COMMAND_SU : AbstractShell.COMMAND_SH);
os = new DataOutputStream(process.getOutputStream()); os = new DataOutputStream(process.getOutputStream());
for (String command : commands) { for (String command : commands) {
if (command != null) { if (command != null) {
os.write(command.getBytes()); os.write(command.getBytes());
os.writeBytes(AbstractShell.COMMAND_LINE_END); os.writeBytes(COMMAND_LINE_END);
os.flush(); os.flush();
} }
} }
os.writeBytes(AbstractShell.COMMAND_EXIT); os.writeBytes(COMMAND_EXIT);
os.flush(); os.flush();
Log.d(TAG, "pid = " + ProcessUtils.getProcessPid(process)); Log.d(TAG, "pid = " + ProcessUtils.getProcessPid(process));
commandResult.code = process.waitFor(); commandResult.code = process.waitFor();
@@ -232,18 +245,26 @@ public class ProcessShell extends AbstractShell {
return commandResult; return commandResult;
} }
public static Process getShellProcess() throws IOException {
return Runtime.getRuntime().exec(COMMAND_SH);
}
public static Process getRootProcess() throws IOException {
return Runtime.getRuntime().exec(COMMAND_SU);
}
private static String readAll(InputStream inputStream) throws IOException { private static String readAll(InputStream inputStream) throws IOException {
String line; String line;
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
builder.append(line).append('\n'); builder.append(line).append(COMMAND_LINE_END);
} }
return builder.toString(); return builder.toString();
} }
public static Result execCommand(String command, boolean withRoot) { public static Result execCommand(String command, boolean withRoot) {
String[] commands = command.split("\n"); String[] commands = command.split(COMMAND_LINE_END);
return execCommand(commands, withRoot); return execCommand(commands, withRoot);
} }
@@ -1,96 +0,0 @@
package org.autojs.autojs.runtime.api;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
import org.autojs.autojs.runtime.ScriptRuntime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by SuperMonster003 on Aug 3, 2023.
*/
public class ScriptToast {
private static final String TAG = ScriptToast.class.getSimpleName();
private final Context mContext;
private final ScriptRuntime mScriptRuntime;
private final Handler mUiHandler;
private static final ConcurrentHashMap<Toast, ScriptRuntime> pool = new ConcurrentHashMap<>();
public ScriptToast(Context context, ScriptRuntime runtime) {
mContext = context;
mScriptRuntime = runtime;
mUiHandler = mScriptRuntime.uiHandler;
}
// @Caution by SuperMonster003 on Oct 11, 2022.
// ! android.widget.ScriptToast.makeText() doesn't work well on Android API Level 28 (Android 9) [P].
// ! There hasn't been a solution for this so far.
// ! Tested devices:
// ! 1. SONY XPERIA XZ1 Compact (G8441)
// ! 2. Android Studio AVD (Android 9.0 x86)
public void makeToast(String msg, boolean isLong, boolean isForcible) {
synchronized (ScriptToast.class) {
if (isForcible) {
dismissAll();
}
Toast toast = Toast.makeText(mContext, msg, isLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
Log.d(TAG + " new toast", toast.toString());
toast.show();
Log.d(TAG + " before put", pool.toString());
pool.put(toast, mScriptRuntime);
Log.d(TAG + " after put", pool.toString());
addCallback(toast);
}
}
private void addCallback(Toast toast) {
// FIXME by SuperMonster003 on Aug 3, 2023.
// ! A more graceful way is needed.
// @Hint by SuperMonster003 on Aug 3, 2023.
// ! It is proved that Toast.Callback for Android API Level 30+
// ! was not a good replacement.
mUiHandler.postDelayed(() -> dismiss(toast), getAccumulatedDuration());
}
private long getAccumulatedDuration() {
var sum = 200L;
for (Map.Entry<Toast, ScriptRuntime> entry : pool.entrySet()) {
Toast toast = entry.getKey();
sum += toast.getDuration() == Toast.LENGTH_SHORT ? 2_000L : 3_500L;
}
return sum;
}
private void dismiss(Toast aim) {
Log.d(TAG + " before dismiss", pool.toString());
for (Map.Entry<Toast, ScriptRuntime> entry : pool.entrySet()) {
Toast toast = entry.getKey();
ScriptRuntime scriptRuntime = entry.getValue();
if (scriptRuntime == mScriptRuntime && toast == aim) {
toast.cancel();
pool.remove(toast);
}
}
Log.d(TAG + " after dismiss", pool.toString());
}
public void dismissAll() {
Log.d(TAG + " before disAll", pool.toString());
for (Map.Entry<Toast, ScriptRuntime> entry : pool.entrySet()) {
Toast toast = entry.getKey();
ScriptRuntime scriptRuntime = entry.getValue();
if (scriptRuntime == mScriptRuntime) {
toast.cancel();
pool.remove(toast);
}
}
Log.d(TAG + " after disAll", pool.toString());
}
}
@@ -0,0 +1,43 @@
package org.autojs.autojs.runtime.api
import android.widget.Toast
import org.autojs.autojs.runtime.ScriptRuntime
import java.util.concurrent.ConcurrentHashMap
/**
* Created by SuperMonster003 on Aug 3, 2023.
* Modified by SuperMonster003 as of Oct 24, 2023.
* Transformed by SuperMonster003 on Oct 24, 2023.
*/
object ScriptToast {
@JvmField
val pool = ConcurrentHashMap<Toast, ScriptRuntime>()
@JvmStatic
fun add(t: Any?, scriptRuntime: ScriptRuntime) {
if (t is Toast) {
pool[t] = scriptRuntime
}
}
@JvmStatic
fun dismissAll(scriptRuntime: ScriptRuntime) {
for (entry in pool.entries) {
if (entry.value === scriptRuntime) {
entry.key.cancel()
pool.remove(entry.key, scriptRuntime)
}
}
}
@JvmStatic
fun clear(scriptRuntime: ScriptRuntime) {
for (entry in pool.entries) {
if (entry.value === scriptRuntime) {
pool.remove(entry.key, scriptRuntime)
}
}
}
}
@@ -0,0 +1,78 @@
package org.autojs.autojs.runtime.api
import android.graphics.Point
import android.graphics.Rect
import org.autojs.autojs.runtime.api.Barcode.Companion.FORMAT_NAME_UNKNOWN
import org.autojs.autojs.runtime.api.Barcode.Companion.TYPE_NAME_UNKNOWN
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import com.google.mlkit.vision.barcode.common.Barcode as MLKitBarcode
class WrappedBarcode(val barcode: MLKitBarcode) : IWrappedBarcode {
override fun getFormat(): Int = barcode.format
override fun getFormatName(): String {
val declaredFields: Array<Field> = MLKitBarcode::class.java.declaredFields
for (field in declaredFields) {
if (Modifier.isStatic(field.modifiers)) {
if (field.type == Int::class.java && field.name.startsWith("FORMAT_")) {
if (field.getInt(barcode) == barcode.format) {
return field.name
}
}
}
}
return FORMAT_NAME_UNKNOWN
}
override fun getValueType(): Int = barcode.valueType
override fun getType(): Int = valueType
override fun getValueTypeName(): String = getTypeName()
override fun getTypeName(): String {
val declaredFields: Array<Field> = MLKitBarcode::class.java.declaredFields
for (field in declaredFields) {
if (Modifier.isStatic(field.modifiers)) {
if (field.type == Int::class.java && field.name.startsWith("TYPE_")) {
if (field.getInt(barcode) == barcode.valueType) {
return field.name
}
}
}
}
return TYPE_NAME_UNKNOWN
}
override fun getBoundingBox(): Rect? = barcode.boundingBox
override fun getCalendarEvent(): MLKitBarcode.CalendarEvent? = barcode.calendarEvent
override fun getContactInfo(): MLKitBarcode.ContactInfo? = barcode.contactInfo
override fun getDriverLicense(): MLKitBarcode.DriverLicense? = barcode.driverLicense
override fun getEmail(): MLKitBarcode.Email? = barcode.email
override fun getGeoPoint(): MLKitBarcode.GeoPoint? = barcode.geoPoint
override fun getPhone(): MLKitBarcode.Phone? = barcode.phone
override fun getSms(): MLKitBarcode.Sms? = barcode.sms
override fun getUrl(): MLKitBarcode.UrlBookmark? = barcode.url
override fun getWifi(): MLKitBarcode.WiFi? = barcode.wifi
override fun getDisplayValue(): String? = barcode.displayValue
override fun getRawValue(): String? = barcode.rawValue
override fun getRawBytes(): ByteArray? = barcode.rawBytes
override fun getCornerPoints(): Array<Point>? = barcode.cornerPoints
}
@@ -0,0 +1,146 @@
package org.autojs.autojs.runtime.api
import android.content.ComponentName
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.core.shizuku.IUserService
import org.autojs.autojs.core.shizuku.UserService
import org.autojs.autojs6.BuildConfig
import rikka.shizuku.Shizuku
import org.autojs.autojs.runtime.api.AbstractShell.Result as ShellResult
/**
* Created by SuperMonster003 on Oct 19, 2023.
*/
object WrappedShizuku {
@JvmField
var service: IUserService? = null
private val TAG: String = WrappedShizuku::class.java.simpleName
private val mRequestCode = "shizuku-request-code".hashCode()
private val mUserServiceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, binder: IBinder?) {
Log.d(TAG, "onServiceConnected: ${componentName.className}")
if (binder?.pingBinder() == true) {
service = IUserService.Stub.asInterface(binder)
} else {
Log.w(TAG, "invalid binder for $componentName received")
}
}
override fun onServiceDisconnected(componentName: ComponentName) {
// ... ...
}
}
private val mUserServiceArgs = Shizuku.UserServiceArgs(ComponentName(BuildConfig.APPLICATION_ID, UserService::class.java.name))
.daemon(false)
.processNameSuffix("service-for-${BuildConfig.APPLICATION_ID.split(".").lastOrNull() ?: "autojs"}")
.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 mBinderDeadListener = {
// binding.text1.setText("Binder dead")
}
private val mRequestPermissionResultListener: (requestCode: Int, grantResult: Int) -> Unit = { requestCode: Int, grantResult: Int ->
if (grantResult == PackageManager.PERMISSION_GRANTED && requestCode == mRequestCode) {
bindUserService()
}
}
internal fun onCreate() {
Log.d(TAG, "${App::class.java.simpleName} onCreate | Process=${App.getProcessNameCompat()}")
Shizuku.addBinderReceivedListenerSticky(mBinderReceivedListener)
Shizuku.addBinderDeadListener(mBinderDeadListener)
Shizuku.addRequestPermissionResultListener(mRequestPermissionResultListener)
}
private fun bindUserService() {
val res = StringBuilder()
try {
if (Shizuku.getVersion() < 10) {
res.append("requires Shizuku API 10")
} else {
Shizuku.bindUserService(mUserServiceArgs, mUserServiceConnection)
}
} catch (tr: Throwable) {
tr.printStackTrace()
res.append(tr)
}
// 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 {
Shizuku.unbindUserService(mUserServiceArgs, mUserServiceConnection, true)
}
} catch (tr: Throwable) {
tr.printStackTrace()
res.append(tr)
}
// 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) }
}
} catch (e: Throwable) {
false.also { e.printStackTrace() }
}
@ScriptInterface
fun execCommand(cmd: String): ShellResult {
ensureService()
return try {
ShellResult.fromJson(service!!.execCommand(cmd))
} catch (e: Exception) {
ShellResult().apply {
code = 1
error = e.message ?: ""
result = ""
}.also { e.printStackTrace() }
}
}
@ScriptInterface
fun execCommand(cmdList: Array<String>): ShellResult {
return execCommand(cmdList.joinToString("\n"))
}
@ScriptInterface
fun ensureService() {
service ?: throw IllegalStateException("Shizuku service is not available")
}
internal fun onDestroy() {
unbindUserService()
Shizuku.removeBinderReceivedListener(mBinderReceivedListener)
Shizuku.removeBinderDeadListener(mBinderDeadListener)
Shizuku.removeRequestPermissionResultListener(mRequestPermissionResultListener)
}
}
@@ -5,6 +5,7 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.autojs.autojs.pref.Language;
import org.autojs.autojs.rhino.TokenStream; import org.autojs.autojs.rhino.TokenStream;
import org.autojs.autojs.tool.MapBuilder; import org.autojs.autojs.tool.MapBuilder;
import org.mozilla.javascript.Token; import org.mozilla.javascript.Token;
@@ -22,19 +23,23 @@ public abstract class JavaScriptSource extends ScriptSource {
public static final String EXECUTION_MODE_UI_PREFIX = "\"ui\";"; public static final String EXECUTION_MODE_UI_PREFIX = "\"ui\";";
public static final int EXECUTION_MODE_NORMAL = 0; public static final int EXECUTION_MODE_RAW = -0x1;
public static final int EXECUTION_MODE_UI = 0x00000001; public static final int EXECUTION_MODE_NORMAL = 0x0;
public static final int EXECUTION_MODE_AUTO = 0x00000002; public static final int EXECUTION_MODE_UI = 0x1;
public static final int EXECUTION_MODE_AUTO = 0x2;
public static final int EXECUTION_MODE_MODULE_CHEERIO = 0x10;
private static final String LOG_TAG = "JavaScriptSource"; private static final String LOG_TAG = "JavaScriptSource";
private static final Map<String, Integer> EXECUTION_MODES = new MapBuilder<String, Integer>() private static final Map<String, Integer> EXECUTION_MODES = new MapBuilder<String, Integer>()
.put("ui", EXECUTION_MODE_UI) .put("ui", EXECUTION_MODE_UI)
.put("auto", EXECUTION_MODE_AUTO) .put("auto", EXECUTION_MODE_AUTO)
.put("cheerio", EXECUTION_MODE_MODULE_CHEERIO)
.build(); .build();
private static final int PARSING_MAX_TOKEN = 300; private static final int PARSING_MAX_TOKEN = 300;
private int mExecutionMode = -1; private int mExecutionMode = EXECUTION_MODE_RAW;
public JavaScriptSource(String name) { public JavaScriptSource(String name) {
super(name); super(name);
@@ -62,7 +67,7 @@ public abstract class JavaScriptSource extends ScriptSource {
} }
public int getExecutionMode() { public int getExecutionMode() {
if (mExecutionMode == -1) { if (mExecutionMode == EXECUTION_MODE_RAW) {
mExecutionMode = parseExecutionMode(); mExecutionMode = parseExecutionMode();
} }
return mExecutionMode; return mExecutionMode;
@@ -79,28 +84,27 @@ public abstract class JavaScriptSource extends ScriptSource {
if (token == Token.EOL || token == Token.COMMENT) { if (token == Token.EOL || token == Token.COMMENT) {
continue; continue;
} }
if (token == Token.STRING && ts.getTokenLength() > 2) { if (token != Token.STRING || ts.getTokenLength() < 3) {
String tokenString = script.substring(ts.getTokenBeg() + 1, ts.getTokenEnd() - 1); break;
if (ts.getToken() != Token.SEMI) {
break;
}
Log.d(LOG_TAG, "string = " + tokenString);
return parseExecutionMode(tokenString.split(" "));
} }
break; String tokenString = script.substring(ts.getTokenBeg() + 1, ts.getTokenEnd() - 1);
if (ts.getToken() != Token.SEMI) {
break;
}
Log.d(LOG_TAG, "string = " + tokenString);
return parseExecutionMode(tokenString.split("\\s*[,;|]\\s*|\\s+"));
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return EXECUTION_MODE_NORMAL;
} }
return EXECUTION_MODE_NORMAL; return EXECUTION_MODE_NORMAL;
} }
private int parseExecutionMode(String[] modeStrings) { private int parseExecutionMode(String[] modeStrings) {
int mode = 0; int mode = EXECUTION_MODE_NORMAL;
for (String modeString : modeStrings) { for (String modeString : modeStrings) {
Integer i = EXECUTION_MODES.get(modeString); String niceModeString = modeString.toLowerCase(Language.getPrefLanguage().getLocale());
Integer i = EXECUTION_MODES.get(niceModeString);
if (i != null) { if (i != null) {
mode |= i; mode |= i;
} }
@@ -14,7 +14,7 @@ import java.util.Vector
*/ */
object ThemeColorManager { object ThemeColorManager {
@JvmStatic @JvmField
var defaultThemeColor = ThemeColor(ColorUtils.fromResources(R.color.theme_color_default)) var defaultThemeColor = ThemeColor(ColorUtils.fromResources(R.color.theme_color_default))
@JvmStatic @JvmStatic
@@ -1,33 +0,0 @@
package org.autojs.autojs.tool;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import org.autojs.autojs.util.ViewUtils;
/**
* Created by Stardust on 2017/5/2.
*/
public class UiHandler extends Handler {
private final Context mContext;
public UiHandler(Context context) {
super(Looper.getMainLooper());
mContext = context;
}
public Context getContext() {
return mContext;
}
public void toast(final String message) {
post(() -> ViewUtils.showToast(mContext, message));
}
public void toast(final int resId) {
post(() -> ViewUtils.showToast(mContext, resId));
}
}
@@ -0,0 +1,46 @@
package org.autojs.autojs.tool
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import android.widget.Toast.LENGTH_SHORT
import org.autojs.autojs.AutoJs
import org.autojs.autojs.annotation.ScriptInterface
import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.runtime.api.ScriptToast
/**
* Created by Stardust on 2017/5/2.
* Transformed by SuperMonster003 on Oct 25, 2023.
*/
class UiHandler(val context: Context) : Handler(Looper.getMainLooper()) {
@ScriptInterface
@JvmOverloads
fun toast(message: String?, isLong: Boolean = false) {
post {
message?.let {
val rawToast = Toast.makeText(context, it, if (isLong) LENGTH_LONG else LENGTH_SHORT)
when (Looper.getMainLooper() == Looper.myLooper()) {
true -> addAndShow(rawToast)
else -> GlobalAppContext.post { addAndShow(rawToast) }
}
}
}
}
@ScriptInterface
@JvmOverloads
fun toast(resId: Int, isLong: Boolean = false) = toast(context.getString(resId), isLong)
@ScriptInterface
fun dismissAllToasts() = ScriptToast.dismissAll(AutoJs.instance.runtime)
private fun addAndShow(rawToast: Toast) {
rawToast.show()
ScriptToast.add(rawToast, AutoJs.instance.runtime)
}
}
@@ -3,15 +3,13 @@
package org.autojs.autojs.ui package org.autojs.autojs.ui
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN import android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import org.autojs.autojs.pref.Language
import com.zeugmasolutions.localehelper.LocaleHelper
import com.zeugmasolutions.localehelper.LocaleHelperActivityDelegate
import com.zeugmasolutions.localehelper.LocaleHelperActivityDelegateImpl
import org.autojs.autojs.theme.ThemeColorManager import org.autojs.autojs.theme.ThemeColorManager
import org.autojs.autojs.util.LocaleUtils
import org.autojs.autojs.util.ViewUtils import org.autojs.autojs.util.ViewUtils
/** /**
@@ -20,8 +18,6 @@ import org.autojs.autojs.util.ViewUtils
*/ */
abstract class BaseActivity : AppCompatActivity() { abstract class BaseActivity : AppCompatActivity() {
private val localeDelegate: LocaleHelperActivityDelegate = LocaleHelperActivityDelegateImpl()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -38,15 +34,16 @@ abstract class BaseActivity : AppCompatActivity() {
} }
ViewUtils.makeBarsAdaptToNightMode(this) ViewUtils.makeBarsAdaptToNightMode(this)
localeDelegate.onCreate(this)
setLocaleForAutoLanguageIfNeeded(this)
} }
override fun getDelegate(): AppCompatDelegate { private fun setLocaleForAutoLanguageIfNeeded(context: Context) {
return localeDelegate.getAppCompatDelegate(super.getDelegate()) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
} if (Language.getPrefLanguage().isAuto()) {
LocaleUtils.setFollowSystem(context)
override fun attachBaseContext(newBase: Context) { }
super.attachBaseContext(localeDelegate.attachBaseContext(newBase)) }
} }
override fun onStart() { override fun onStart() {
@@ -58,21 +55,9 @@ abstract class BaseActivity : AppCompatActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
localeDelegate.onResumed(this) LocaleUtils.syncPrefWithCurrentStateIfNeeded(this)
} }
override fun onPause() {
super.onPause()
localeDelegate.onPaused()
}
override fun createConfigurationContext(overrideConfiguration: Configuration): Context {
val context = super.createConfigurationContext(overrideConfiguration)
return LocaleHelper.onAttach(context)
}
override fun getApplicationContext() = localeDelegate.getApplicationContext(super.getApplicationContext())
fun setToolbarAsBack(titleRes: Int) = ViewUtils.setToolbarAsBack(this, titleRes) fun setToolbarAsBack(titleRes: Int) = ViewUtils.setToolbarAsBack(this, titleRes)
fun setToolbarAsBack(title: String?) = ViewUtils.setToolbarAsBack(this, title) fun setToolbarAsBack(title: String?) = ViewUtils.setToolbarAsBack(this, title)
@@ -183,6 +183,8 @@ public class EditorMenu {
dialog.dismiss(); dialog.dismiss();
}) })
.onNegative((ignored, which) -> { .onNegative((ignored, which) -> {
// TODO by SuperMonster003 on Oct 20, 2023.
// ! Check by execution mode in class org.autojs.autojs.script.JavaScriptSource .
if (mEditor.getText().startsWith(JavaScriptSource.EXECUTION_MODE_UI_PREFIX)) { if (mEditor.getText().startsWith(JavaScriptSource.EXECUTION_MODE_UI_PREFIX)) {
mEditor.insert(1, item.getImportText() + ";\n"); mEditor.insert(1, item.getImportText() + ";\n");
} else { } else {
@@ -89,6 +89,10 @@ import java.io.File
// FIXME by SuperMonster003 on May 13, 2023. // FIXME by SuperMonster003 on May 13, 2023.
// ! Failed many times to migrate this view with annotation to view binding. // ! 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. // ! 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 { open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, ToolbarFragment.OnMenuItemClickListener {
@JvmField @JvmField
@@ -85,7 +85,7 @@ public class FileChooserDialogBuilder extends MaterialDialog.Builder {
} }
public FileChooserDialogBuilder justScriptFile() { public FileChooserDialogBuilder justScriptFile() {
mFileFilter = Scripts.getFILE_FILTER(); mFileFilter = Scripts.FILE_FILTER;
return this; return this;
} }
@@ -12,14 +12,12 @@ import android.view.Gravity
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.WindowManager
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.viewpager.widget.ViewPager.SimpleOnPageChangeListener import androidx.viewpager.widget.ViewPager.SimpleOnPageChangeListener
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import org.autojs.autojs.App
import org.autojs.autojs.AutoJs import org.autojs.autojs.AutoJs
import org.autojs.autojs.app.FragmentPagerAdapterBuilder import org.autojs.autojs.app.FragmentPagerAdapterBuilder
import org.autojs.autojs.app.FragmentPagerAdapterBuilder.StoredFragmentPagerAdapter import org.autojs.autojs.app.FragmentPagerAdapterBuilder.StoredFragmentPagerAdapter
@@ -36,11 +34,9 @@ import org.autojs.autojs.permission.DisplayOverOtherAppsPermission
import org.autojs.autojs.permission.ManageAllFilesPermission import org.autojs.autojs.permission.ManageAllFilesPermission
import org.autojs.autojs.permission.PostNotificationPermission import org.autojs.autojs.permission.PostNotificationPermission
import org.autojs.autojs.pref.Pref import org.autojs.autojs.pref.Pref
import org.autojs.autojs.runtime.api.AppUtils.Companion.hideLauncherIcon import org.autojs.autojs.runtime.api.WrappedShizuku
import org.autojs.autojs.runtime.api.AppUtils.Companion.showLauncherIcon
import org.autojs.autojs.theme.ThemeColorManager.addViewBackground import org.autojs.autojs.theme.ThemeColorManager.addViewBackground
import org.autojs.autojs.ui.BaseActivity import org.autojs.autojs.ui.BaseActivity
import org.autojs.autojs.ui.doc.DocumentationActivity
import org.autojs.autojs.ui.doc.DocumentationFragment import org.autojs.autojs.ui.doc.DocumentationFragment
import org.autojs.autojs.ui.enhancedfloaty.FloatyService import org.autojs.autojs.ui.enhancedfloaty.FloatyService
import org.autojs.autojs.ui.explorer.ExplorerView import org.autojs.autojs.ui.explorer.ExplorerView
@@ -55,14 +51,12 @@ import org.autojs.autojs.ui.settings.PreferencesActivity
import org.autojs.autojs.ui.widget.DrawerAutoClose import org.autojs.autojs.ui.widget.DrawerAutoClose
import org.autojs.autojs.ui.widget.SearchViewItem import org.autojs.autojs.ui.widget.SearchViewItem
import org.autojs.autojs.util.ForegroundServiceUtils import org.autojs.autojs.util.ForegroundServiceUtils
import org.autojs.autojs.util.StringUtils import org.autojs.autojs.util.UpdateUtils
import org.autojs.autojs.util.UpdateUtils.autoCheckForUpdatesIfNeededWithSnackbar
import org.autojs.autojs.util.ViewUtils import org.autojs.autojs.util.ViewUtils
import org.autojs.autojs.util.WorkingDirectoryUtils import org.autojs.autojs.util.WorkingDirectoryUtils
import org.autojs.autojs6.R import org.autojs.autojs6.R
import org.autojs.autojs6.databinding.ActivityMainBinding import org.autojs.autojs6.databinding.ActivityMainBinding
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import rikka.shizuku.Shizuku
/** /**
* Modified by SuperMonster003 as of Dec 1, 2021. * Modified by SuperMonster003 as of Dec 1, 2021.
@@ -93,45 +87,6 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
} }
} }
private val shizukuBinderReceivedListener = {
// if (Shizuku.isPreV11()) {
// binding.text1.setText("Shizuku pre-v11 is not supported")
// } else {
// binding.text1.setText("Binder received")
// }
}
private val shizukuBinderDeadListener = {
// binding.text1.setText("Binder dead")
}
private val shizukuRequestPermissionResultListener: (requestCode: Int, grantResult: Int) -> Unit = { /* requestCode: */ _: Int, /* grantResult */ _: Int ->
// if (grantResult == PackageManager.PERMISSION_GRANTED) {
// switch (requestCode) {
// case REQUEST_CODE_BUTTON1: {
// getUsers();
// break;
// }
// case REQUEST_CODE_BUTTON4: {
// getSystemProperty();
// break;
// }
// case REQUEST_CODE_BUTTON7: {
// bindUserService();
// break;
// }
// case REQUEST_CODE_BUTTON8: {
// unbindUserService();
// break;
// }
// case REQUEST_CODE_BUTTON9: {
// peekUserService();
// break;
// }
// }
// } else {
// binding.text1.setText("User denied permission");
// }
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -150,34 +105,30 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
ViewUtils.appendSystemUiVisibility(this, View.SYSTEM_UI_FLAG_LAYOUT_STABLE) ViewUtils.appendSystemUiVisibility(this, View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
ViewUtils.registerOnSharedPreferenceChangeListener(this)
Pref.registerOnSharedPreferenceChangeListener { _, key -> WrappedShizuku.onCreate()
if (key == StringUtils.key(R.string.key_keep_screen_on_when_in_foreground)) {
configKeepScreenOnWhenInForeground()
}
}
ExplorerView.clearViewStates() ExplorerView.clearViewStates()
setUpToolbar() setUpToolbar()
setUpTabViewPager() setUpTabViewPager()
registerBackPressHandlers() registerBackPressHandlers()
addViewBackground(binding.appBar) addViewBackground(binding.appBar)
Log.d("Shizuku", "${App::class.java.simpleName} onCreate | Process=${App.getProcessNameCompat()}")
Shizuku.addBinderReceivedListenerSticky(this.shizukuBinderReceivedListener)
Shizuku.addBinderDeadListener(shizukuBinderDeadListener)
Shizuku.addRequestPermissionResultListener(shizukuRequestPermissionResultListener)
} }
override fun onPostResume() { override fun onPostResume() {
recreateIfNeeded() recreateIfNeeded()
autoCheckForUpdatesIfNeededWithSnackbar(this, android.R.id.content) UpdateUtils.autoCheckForUpdatesIfNeededWithSnackbar(this)
super.onPostResume() super.onPostResume()
} }
override fun onStart() {
super.onStart()
WrappedShizuku.checkShizukuPermission()
}
private fun recreateIfNeeded() { private fun recreateIfNeeded() {
if (sShouldRecreateMainActivity) { if (shouldRecreateMainActivity) {
setShouldRecreateMainActivity(false) shouldRecreateMainActivity = false
recreate() recreate()
Explorers.workspace().refreshAll() Explorers.workspace().refreshAll()
} }
@@ -290,7 +241,7 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
override fun getOnActivityResultDelegateMediator() = mActivityResultMediator override fun getOnActivityResultDelegateMediator() = mActivityResultMediator
@Suppress("OVERRIDE_DEPRECATION") @Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
override fun onBackPressed() { override fun onBackPressed() {
val fragment = mPagerAdapter.getStoredFragment(mViewPager.currentItem) val fragment = mPagerAdapter.getStoredFragment(mViewPager.currentItem)
if (fragment is BackPressedHandler) { if (fragment is BackPressedHandler) {
@@ -384,52 +335,22 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
WrappedShizuku.onDestroy()
Shizuku.removeBinderReceivedListener(this.shizukuBinderReceivedListener)
Shizuku.removeBinderDeadListener(shizukuBinderDeadListener)
Shizuku.removeRequestPermissionResultListener(shizukuRequestPermissionResultListener)
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
configKeepScreenOnWhenInForeground() ViewUtils.configKeepScreenOnWhenInForeground(this)
configDocumentationLauncherIcon()
}
private fun configKeepScreenOnWhenInForeground() {
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON.let { flags ->
if (ViewUtils.isKeepScreenOnWhenInForegroundEnabled) {
window.addFlags(flags)
} else {
window.clearFlags(flags)
}
}
}
private fun configDocumentationLauncherIcon() {
DocumentationActivity::class.java.let { clazz ->
if (Pref.isDocumentationLauncherIconShouldShow) {
showLauncherIcon(this, clazz)
} else {
hideLauncherIcon(this, clazz)
}
}
} }
companion object { companion object {
private val TAG = MainActivity::class.java.simpleName private val TAG = MainActivity::class.java.simpleName
private var sShouldRecreateMainActivity = false var shouldRecreateMainActivity = false
fun setShouldRecreateMainActivity(b: Boolean) {
sShouldRecreateMainActivity = b
}
@JvmStatic @JvmStatic
fun launch(context: Context) { fun launch(context: Context) = context.startActivity(getIntent(context).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
context.startActivity(getIntent(context).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
@JvmStatic @JvmStatic
fun getIntent(context: Context?) = Intent(context, MainActivity::class.java) fun getIntent(context: Context?) = Intent(context, MainActivity::class.java)
@@ -1,5 +1,7 @@
package org.autojs.autojs.ui.project; package org.autojs.autojs.ui.project;
import static org.autojs.autojs.apkbuilder.ApkBuilderPluginHelper.TEMPLATE_APK_PATH;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -11,6 +13,7 @@ import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView; import androidx.cardview.widget.CardView;
@@ -21,6 +24,7 @@ import org.autojs.autojs.apkbuilder.ApkBuilder;
import org.autojs.autojs.apkbuilder.ApkBuilderPluginHelper; import org.autojs.autojs.apkbuilder.ApkBuilderPluginHelper;
import org.autojs.autojs.external.fileprovider.AppFileProvider; import org.autojs.autojs.external.fileprovider.AppFileProvider;
import org.autojs.autojs.model.script.ScriptFile; import org.autojs.autojs.model.script.ScriptFile;
import org.autojs.autojs.pref.Language;
import org.autojs.autojs.project.ProjectConfig; import org.autojs.autojs.project.ProjectConfig;
import org.autojs.autojs.ui.BaseActivity; import org.autojs.autojs.ui.BaseActivity;
import org.autojs.autojs.ui.filechooser.FileChooserDialogBuilder; import org.autojs.autojs.ui.filechooser.FileChooserDialogBuilder;
@@ -107,11 +111,15 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
private boolean checkApkTemplateInAssets() { private boolean checkApkTemplateInAssets() {
try { try {
if (Arrays.asList(getAssets().list("")).contains("template.apk")) { String[] files = getAssets().list("");
if (files != null && Arrays.asList(files).contains(TEMPLATE_APK_PATH)) {
return true; return true;
} }
} catch (IOException e) { } catch (IOException e) {
Log.e(LOG_TAG, e.getMessage()); String msg = e.getMessage();
if (msg != null) {
Log.e(LOG_TAG, msg);
}
} }
return false; return false;
} }
@@ -149,10 +157,19 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
} }
mOutputPath.setText(dir); mOutputPath.setText(dir);
mAppName.setText(file.getSimplifiedName()); mAppName.setText(file.getSimplifiedName());
mPackageName.setText(getString(R.string.format_default_package_name, System.currentTimeMillis())); mPackageName.setText(getString(R.string.format_default_package_name, getPackageNameSuffix(file)));
setSource(file); setSource(file);
} }
private static String getPackageNameSuffix(ScriptFile file) {
String name = file.getSimplifiedName()
.replaceAll("[^\\w$]+", "_");
if (name.matches("^\\d.*")) {
name = "app_" + name;
}
return name.toLowerCase(Language.getPrefLanguage().getLocale());
}
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
@@ -277,7 +294,9 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
} }
private ApkBuilder callApkBuilder(File tmpDir, File outApk, ApkBuilder.AppConfig appConfig) throws Exception { private ApkBuilder callApkBuilder(File tmpDir, File outApk, ApkBuilder.AppConfig appConfig) throws Exception {
InputStream templateApk = mIsApkTemplateInAssets ? getAssets().open("template.apk") : ApkBuilderPluginHelper.openTemplateApk(BuildActivity.this); InputStream templateApk = mIsApkTemplateInAssets
? getAssets().open(TEMPLATE_APK_PATH)
: ApkBuilderPluginHelper.openTemplateApk(BuildActivity.this);
return new ApkBuilder(templateApk, outApk, tmpDir.getPath()) return new ApkBuilder(templateApk, outApk, tmpDir.getPath())
.setProgressCallback(BuildActivity.this) .setProgressCallback(BuildActivity.this)
.prepare() .prepare()
@@ -319,24 +338,23 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
} }
@Override @Override
public void onPrepare(ApkBuilder builder) { public void onPrepare(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_prepare); mProgressDialog.setContent(R.string.apk_builder_prepare);
} }
@Override @Override
public void onBuild(ApkBuilder builder) { public void onBuild(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_build); mProgressDialog.setContent(R.string.apk_builder_build);
} }
@Override @Override
public void onSign(ApkBuilder builder) { public void onSign(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_package); mProgressDialog.setContent(R.string.apk_builder_package);
} }
@Override @Override
public void onClean(ApkBuilder builder) { public void onClean(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_clean); mProgressDialog.setContent(R.string.apk_builder_clean);
} }
@@ -1,36 +0,0 @@
package org.autojs.autojs.ui.settings
import android.content.Context
import android.util.AttributeSet
import com.afollestad.materialdialogs.MaterialDialog
import org.autojs.autojs.runtime.api.AppUtils
import org.autojs.autojs.theme.preference.MaterialListPreference
import org.autojs.autojs.ui.doc.DocumentationActivity
import org.autojs.autojs6.R
class DocumentationLauncherIconPreference : MaterialListPreference {
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context) : super(context)
override fun onChangeConfirmed(dialog: MaterialDialog) {
super.onChangeConfirmed(dialog)
when (dialog.items?.get(dialog.selectedIndex)?.toString()) {
prefContext.getString(R.string.entry_documentation_launcher_icon_show) -> {
AppUtils.showLauncherIcon(prefContext, DocumentationActivity::class.java)
}
prefContext.getString(R.string.entry_documentation_launcher_icon_hide) -> {
AppUtils.hideLauncherIcon(prefContext, DocumentationActivity::class.java)
}
else -> Unit
}
}
}
@@ -0,0 +1,90 @@
package org.autojs.autojs.ui.settings
import android.content.Context
import android.util.AttributeSet
import android.widget.LinearLayout
import androidx.core.content.pm.ShortcutManagerCompat
import com.afollestad.materialdialogs.MaterialDialog
import org.autojs.autojs.theme.preference.MaterialPreference
import org.autojs.autojs.util.ShortcutUtils
import org.autojs.autojs6.R
/**
* Created by SuperMonster003 on Sep 25, 2022.
*/
class LauncherShortcutsPreference : MaterialPreference {
private val mDialog = MaterialDialog.Builder(prefContext)
.customView(R.layout.select_launcher_shortcut, true)
.build()
.also { dialog ->
val view = dialog.customView as LinearLayout
view.findViewById<LinearLayout?>(R.id.launcher_shortcut_settings).setOnClickListener {
dialog.dismiss()
ShortcutUtils.requestPinShortcut(
prefContext,
R.string.id_launcher_shortcut_settings,
"org.autojs.autojs.ui.settings.PreferencesActivity",
R.string.text_app_shortcut_settings_long_label,
R.string.text_app_shortcut_settings_short_label,
R.mipmap.ic_app_shortcut_settings_adaptive,
)
}
view.findViewById<LinearLayout?>(R.id.launcher_shortcut_docs).setOnClickListener {
dialog.dismiss()
ShortcutUtils.requestPinShortcut(
prefContext,
R.string.id_launcher_shortcut_docs,
"org.autojs.autojs.ui.doc.DocumentationActivity",
R.string.text_app_shortcut_docs_long_label,
R.string.text_app_shortcut_docs_short_label,
R.mipmap.ic_app_shortcut_docs_adaptive,
)
}
view.findViewById<LinearLayout?>(R.id.launcher_shortcut_log).setOnClickListener {
dialog.dismiss()
ShortcutUtils.requestPinShortcut(
prefContext,
R.string.id_launcher_shortcut_log,
"org.autojs.autojs.ui.log.LogActivity",
R.string.text_app_shortcut_log_long_label,
R.string.text_app_shortcut_log_short_label,
R.mipmap.ic_app_shortcut_log_adaptive,
)
}
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context) : super(context)
override fun onClick() {
if (ShortcutManagerCompat.isRequestPinShortcutSupported(prefContext)) {
showShortcutsSelectionDialog()
} else {
showPinShortcutNotSupportedDialog()
}
super.onClick()
}
private fun showShortcutsSelectionDialog() {
if (!mDialog.isShowing) mDialog.show()
}
private fun showPinShortcutNotSupportedDialog() {
MaterialDialog.Builder(prefContext)
.title(R.string.text_prompt)
.content(R.string.text_pin_shortcut_not_unsupported)
.positiveText(R.string.dialog_button_dismiss)
.build()
.show()
}
}
@@ -141,7 +141,7 @@ class WorkingDirectoryPreference : MaterialPreference {
transfer(mPrefFullPath, inputPath) transfer(mPrefFullPath, inputPath)
} }
WorkingDirectoryUtils.path = inputPath WorkingDirectoryUtils.path = inputPath
MainActivity.setShouldRecreateMainActivity(true) MainActivity.shouldRecreateMainActivity = true
} }
dialog.dismiss() dialog.dismiss()
onFinish() onFinish()
@@ -106,7 +106,7 @@ public class ShortcutCreateActivity extends AppCompatActivity {
private void createShortcutByShortcutManager() { private void createShortcutByShortcutManager() {
Icon icon; Icon icon;
if (mIsDefaultIcon) { if (mIsDefaultIcon) {
icon = Icon.createWithResource(this, R.drawable.ic_file_type_js); icon = Icon.createWithResource(this, R.drawable.ic_file_type_js_dark_green);
} else { } else {
Bitmap bitmap = BitmapUtils.drawableToBitmap(mIcon.getDrawable()); Bitmap bitmap = BitmapUtils.drawableToBitmap(mIcon.getDrawable());
icon = Icon.createWithBitmap(bitmap); icon = Icon.createWithBitmap(bitmap);
@@ -125,7 +125,6 @@ public class ShortcutCreateActivity extends AppCompatActivity {
} }
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
@SuppressLint({"CheckResult", "MissingSuperCall"}) @SuppressLint({"CheckResult", "MissingSuperCall"})
@Override @Override
@@ -23,15 +23,16 @@ import android.webkit.WebView;
import androidx.core.view.NestedScrollingChild; import androidx.core.view.NestedScrollingChild;
import androidx.core.view.NestedScrollingChildHelper; import androidx.core.view.NestedScrollingChildHelper;
import androidx.core.view.ViewCompat;
import org.autojs.autojs.ui.pager.ViewPager; import org.autojs.autojs.ui.pager.ViewPager;
public class NestedWebView extends WebView implements NestedScrollingChild { public class NestedWebView extends WebView implements NestedScrollingChild {
private int mLastY;
private final int[] mScrollOffset = new int[2]; // private int mLastY;
private final int[] mScrollConsumed = new int[2]; // private final int[] mScrollOffset = new int[2];
private int mNestedOffsetY; // private final int[] mScrollConsumed = new int[2];
// private int mNestedOffsetY;
private final NestedScrollingChildHelper mChildHelper; private final NestedScrollingChildHelper mChildHelper;
public NestedWebView(Context context) { public NestedWebView(Context context) {
@@ -89,7 +89,7 @@ enum class App(private val appNameResId: Int, val packageName: String, alias: St
} }
@ScriptInterface @ScriptInterface
fun getAppNameZh() = getAppName(Language.ZH) fun getAppNameZh() = getAppName(Language.ZH_HANS)
@ScriptInterface @ScriptInterface
fun getAppNameEn() = getAppName(Language.EN) fun getAppNameEn() = getAppName(Language.EN)
@@ -1,51 +1,61 @@
package org.autojs.autojs.util package org.autojs.autojs.util
import android.app.Activity import android.app.LocaleManager
import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import com.zeugmasolutions.localehelper.LocaleHelper import android.os.Build
import com.zeugmasolutions.localehelper.LocaleHelperActivityDelegate import android.os.LocaleList
import com.zeugmasolutions.localehelper.LocaleHelperActivityDelegateImpl import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import org.autojs.autojs.app.GlobalAppContext import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.pref.Language
import org.autojs.autojs6.R import org.autojs.autojs6.R
import java.util.Locale import java.util.Locale
object LocaleUtils { object LocaleUtils {
private val localeDelegate: LocaleHelperActivityDelegate = LocaleHelperActivityDelegateImpl()
private val localeHelper = LocaleHelper
private val globalAppContext = GlobalAppContext.get()
@JvmStatic @JvmStatic
fun setLocale(activity: Activity, locale: Locale?) { fun setLocale(context: Context, locale: Locale?) {
locale?.let { localeDelegate.setLocale(activity, it) } locale ?: throw IllegalArgumentException(context.getString(R.string.error_cannot_be_null, "Locale"))
?: throw IllegalArgumentException(globalAppContext.getString(R.string.error_cannot_be_null, "Locale")) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
} context.getSystemService(LocaleManager::class.java).applicationLocales = LocaleList(locale)
} else {
@JvmStatic AppCompatDelegate.setApplicationLocales(LocaleListCompat.create(locale))
fun setFollowSystem(activity: Activity) {
val systemLocale = localeHelper.systemLocale
val currentLocale = localeHelper.getLocale(activity)
localeDelegate.apply {
if (currentLocale !== systemLocale) {
setLocale(activity, systemLocale)
}
clearLocaleSelection(activity)
} }
} }
@JvmStatic @JvmStatic
fun getSystemLocale() = localeHelper.getLocale(globalAppContext) fun setFollowSystem(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.getSystemService(LocaleManager::class.java).applicationLocales = LocaleList.getEmptyLocaleList()
} else {
setLocale(context, getSystemLocale())
}
}
@JvmStatic
fun getSystemLocale(): Locale = Resources.getSystem().configuration.locales[0] ?: Locale.getDefault()
@JvmStatic @JvmStatic
fun getResources(desiredLocale: Locale?): Resources { fun getResources(desiredLocale: Locale?): Resources {
return globalAppContext.let { context -> return GlobalAppContext.get().let { context ->
context.resources.configuration.let { Configuration(it) }.run { context.resources.configuration.let { Configuration(it) }.run {
setLocale(desiredLocale ?: localeHelper.systemLocale) setLocale(desiredLocale ?: getSystemLocale())
context.createConfigurationContext(this).resources context.createConfigurationContext(this).resources
} }
} }
} }
fun syncPrefWithCurrentStateIfNeeded(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val appLocales = context.getSystemService(LocaleManager::class.java).applicationLocales
if (appLocales.isEmpty) {
Language.setPrefLanguageAuto()
} else {
Language.setPrefLanguage(appLocales[appLocales.size() - 1])
}
}
}
} }
@@ -40,7 +40,8 @@ public class SdkVersionUtils {
/* api: 30 */ "11", /* api: 30 */ "11",
/* api: 31 */ "12", /* api: 31 */ "12",
/* api: 32 */ "12.1", /* api: 32 */ "12.1",
/* api: 33 */ "13" /* api: 33 */ "13",
/* api: 34 */ "14"
}; };
public static String sdkIntToString(int i) { public static String sdkIntToString(int i) {
@@ -0,0 +1,52 @@
package org.autojs.autojs.util
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Icon
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
/**
* Created by SuperMonster003 on Oct 11, 2022.
*/
object ShortcutUtils {
@JvmStatic
fun requestPinShortcut(context: Context, id: Int, className: String, longLabelResId: Int, shortLabelResId: Int, iconResId: Int) {
requestPinShortcut(context, context.getString(id), className, context.getString(longLabelResId), context.getString(shortLabelResId), Icon.createWithResource(context, iconResId))
}
@JvmStatic
fun requestPinShortcut(context: Context, id: String, className: String, longLabel: CharSequence, shortLabel: CharSequence, icon: Icon) {
requestPinShortcut(context, id, Intent("android.intent.action.VIEW").setClassName(context, className), longLabel, shortLabel, icon)
}
@JvmStatic
fun requestPinShortcut(context: Context, id: String, intent: Intent, longLabel: CharSequence, shortLabel: CharSequence, icon: Icon) {
// The shortcut must be enabled.
// The "id" for ShortcutInfoCompat.Builder must contain stable, constant strings.
val pinShortcutInfo = ShortcutInfoCompat.Builder(context, id)
.setIntent(intent)
.setLongLabel(longLabel)
.setShortLabel(shortLabel)
.setIcon(IconCompat.createFromIcon(context, icon))
.setAlwaysBadged()
.build()
// Create the PendingIntent object only if your app needs to be notified
// that the user allowed the shortcut to be pinned. Note that, if the
// pinning operation fails, your app isn't notified. We assume here that the
// app has implemented a method called createShortcutResultIntent() that
// returns a broadcast intent.
val pinnedShortcutCallbackIntent = ShortcutManagerCompat.createShortcutResultIntent(context, pinShortcutInfo)
// Configure the intent so that your app's broadcast receiver gets
// the callback successfully.For details, see PendingIntent.getBroadcast().
val successCallback = PendingIntent.getBroadcast(context, 0, pinnedShortcutCallbackIntent, PendingIntent.FLAG_IMMUTABLE)
ShortcutManagerCompat.requestPinShortcut(context, pinShortcutInfo, successCallback.intentSender)
}
}
@@ -174,7 +174,8 @@ object UpdateUtils {
} }
@JvmStatic @JvmStatic
fun autoCheckForUpdatesIfNeededWithSnackbar(activity: AppCompatActivity, snackbarViewIdRes: Int) { @JvmOverloads
fun autoCheckForUpdatesIfNeededWithSnackbar(activity: AppCompatActivity, snackbarViewIdRes: Int = android.R.id.content) {
if (isAutoCheckForUpdatesEnabled) { if (isAutoCheckForUpdatesEnabled) {
val checker = IntervalChecker() val checker = IntervalChecker()
if (checker.isBeyondNoNewer && checker.isBeyondPostponed && checker.isBeyondAutoChecked) { if (checker.isBeyondNoNewer && checker.isBeyondPostponed && checker.isBeyondAutoChecked) {
@@ -16,6 +16,7 @@ import android.util.DisplayMetrics
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.view.Window import android.view.Window
import android.view.WindowManager
import android.widget.Toast import android.widget.Toast
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -290,6 +291,24 @@ object ViewUtils {
isKeepScreenOnWhenInForegroundEnabled = true isKeepScreenOnWhenInForegroundEnabled = true
} }
fun registerOnSharedPreferenceChangeListener(activity: Activity) {
Pref.registerOnSharedPreferenceChangeListener { _, key ->
if (key == key(R.string.key_keep_screen_on_when_in_foreground)) {
configKeepScreenOnWhenInForeground(activity)
}
}
}
fun configKeepScreenOnWhenInForeground(activity: Activity) {
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON.let { flags ->
if (isKeepScreenOnWhenInForegroundEnabled) {
activity.window.addFlags(flags)
} else {
activity.window.clearFlags(flags)
}
}
}
class AutoNightMode { class AutoNightMode {
companion object { companion object {
@@ -95,20 +95,18 @@ public class AxmlWriter extends AxmlVisitor {
for (NodeImpl first : firsts) { for (NodeImpl first : firsts) {
size += first.prepare(this); size += first.prepare(this);
} }
{ int a = 0;
int a = 0; for (Map.Entry<String, Ns> e : nses.entrySet()) {
for (Map.Entry<String, Ns> e : nses.entrySet()) { Ns ns = e.getValue();
Ns ns = e.getValue(); if (ns == null) {
if (ns == null) { ns = new Ns(null, new StringItem(e.getKey()), 0);
ns = new Ns(null, new StringItem(e.getKey()), 0); e.setValue(ns);
e.setValue(ns);
}
if (ns.prefix == null) {
ns.prefix = new StringItem(String.format(Language.getPrefLanguage().getLocale(), "axml_auto_%02d", a++));
}
ns.prefix = update(ns.prefix);
ns.uri = update(ns.uri);
} }
if (ns.prefix == null) {
ns.prefix = new StringItem(String.format(Language.getPrefLanguage().getLocale(), "axml_auto_%02d", a++));
}
ns.prefix = update(ns.prefix);
ns.uri = update(ns.uri);
} }
size += nses.size() * 24 * 2; size += nses.size() * 24 * 2;
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@@ -6,7 +6,7 @@
android:viewportWidth="128"> android:viewportWidth="128">
<path <path
android:fillColor="#46483E" android:fillColor="#DE46483E"
android:pathData="M64,14c-27.6,0-50,22.4-50,50s22.4,50,50,50s50-22.4,50-50S91.6,14,64,14z"/> android:pathData="M64,14c-27.6,0-50,22.4-50,50s22.4,50,50,50s50-22.4,50-50S91.6,14,64,14z"/>
<path <path
android:fillColor="#81CF08" android:fillColor="#81CF08"
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -25,7 +25,6 @@
<org.autojs.autojs.ui.explorer.ExplorerView <org.autojs.autojs.ui.explorer.ExplorerView
android:id="@+id/script_list" android:id="@+id/script_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent" />
android:background="#fafafa" />
</LinearLayout> </LinearLayout>
Loaded 100 of 194 files, more files were not shown because too many files have changed in this diff. Show more