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

This commit is contained in:
SuperMonster003
2023-10-25 14:15:44 +08:00
parent 5ee2f62d89
commit 4b64922a0b
194 changed files with 2723 additions and 1497 deletions

View File

@@ -131,26 +131,33 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 Dec 1, 2021 进行二次开发
[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)_
* `新增` 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 等方法
* `新增` 内部 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 插件保存文件到本地时文件扩展名信息丢失的问题
* `修复` 使用协程特性运行项目产生未捕获异常致使应用崩溃的问题
* `修复` 重启或退出应用时浮动按钮无法记录其位置状态信息的问题
* `修复` 设备屏幕方向改变时无法获取更新后的设备配置信息的问题
* `修复` 设备屏幕方向改变时无法获取更新后的设备配置信息的问题 _[`issue #153`](http://issues.autojs6.com/153)_
* `修复` 屏幕旋转至横向时 Toolbar 标题字体过小的问题
* `修复` 屏幕旋转至横向时应用主页的页签排版过于拥挤的问题
* `修复` 屏幕旋转至横向时浮动按钮可能溢出屏幕的问题 _[`issue #90`](http://issues.autojs6.com/90)_
* `修复` 屏幕多次旋转时无法恢复浮动按钮的坐标及屏幕侧边方向的问题
* `修复` 部分设备消息浮动框可能出现遗漏显示或重复显示的问题
* `修复` 消息浮动框在多个脚本同时运行时可能存在被遮蔽的问题 _[`issue #67`](http://issues.autojs6.com/67)_
* `修复` 使用广播分析布局时点击布局无法弹出菜单且导致应用崩溃的问题
* `修复` 第二次及以后创建的 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)_
* `修复` 日志活动页面使用复制或导出功能时可能导致应用崩溃的问题
* `优化` 日志活动页面导出功能重命名为发送功能并重新实现符合实际意义的导出功能
@@ -158,6 +165,7 @@ AutoJs6 在 Auto.js 最终项目的基础上, 于 Dec 1, 2021 进行二次开发
* `优化` ocr 模块同时兼容 Google MLKit 及 Paddle Lite 引擎 (参阅 项目文档 > [光学字符识别](https://docs.autojs6.com/#/ocr?id=p-mode))
* `优化` 提升无障碍服务自动启动的成功概率
* `优化` 构建工具自适应 Java 发行版本以尽量避免 "无效的发行版本" 问题
* `优化` 构建工具优化 IDE 及相关插件的版本退级逻辑并增加版本预测能力
* `优化` 部分 Kotlin 注解处理由 kapt 迁移至 KSP
# v6.3.3

View File

@@ -2,17 +2,30 @@
import com.android.build.gradle.internal.api.ApplicationVariantImpl
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
import org.gradle.kotlin.dsl.support.uppercaseFirstChar
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.io.FileInputStream
import java.io.FileNotFoundException
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 kotlin.text.RegexOption.IGNORE_CASE
val applicationId = "org.autojs.autojs6"
val sign = Sign("$rootDir/sign.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 {
id("com.android.application")
id("com.google.devtools.ksp")
@@ -27,7 +40,7 @@ dependencies /* Unclassified */ {
// Android supports
implementation("androidx.cardview:cardview:1.0.0")
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
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
@@ -103,11 +116,8 @@ dependencies /* Unclassified */ {
// Dex
implementation(files("$rootDir/libs/com.android.dx-1.7.0.jar"))
// LocaleHelper
implementation(project(":libs:com.zeugmasolutions.localehelper-1.5.1"))
// OpenCV
implementation(project(":libs:org.opencv-4.5.5"))
implementation(project(":libs:org.opencv-4.8.0"))
// PaddleOCR
implementation(project(":libs:paddleocr"))
@@ -132,9 +142,13 @@ dependencies /* Unclassified */ {
implementation(files("$rootDir/libs/javamail-android/mail.jar"))
// Shizuku
implementation("dev.rikka.shizuku:api:13.1.5")
implementation("dev.rikka.shizuku:provider:13.1.5")
implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3@aar")
implementation(project(":libs:dev.rikka.shizuku-shared-13.1.5"))
implementation(project(":libs:dev.rikka.shizuku-aidl-13.1.5"))
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 */ {
@@ -159,12 +173,22 @@ dependencies /* Annotations */ {
}
dependencies /* AppCompat */ {
implementation("androidx.appcompat:appcompat") {
version {
strictly("1.4.2")
because("version 1.5.0 duplicates some classes")
}
}
// @Obsoleted by SuperMonster003 on Oct 5, 2023.
// ! Comment with related code will be removed since Jan 5, 2024.
//
// @Hint by SuperMonster003 on Oct 5, 2023.
// ! It look like that bugs below in version 1.5.x has gone away since 1.6.x.
// 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)
implementation(project(":libs:androidx.appcompat-1.0.2")) {
@@ -213,18 +237,13 @@ dependencies /* View */ {
dependencies /* GitHub API */ {
implementation(files("$rootDir/libs/github-api-1.306.jar"))
implementation("commons-io:commons-io") {
version {
strictly("2.8.0")
because("Exception on newer versions: 'NoClassDefFoundError: org.apache.commons.io.IOUtils'")
}
}
implementation("commons-io:commons-io:2.14.0")
implementation("com.fasterxml.jackson.core:jackson-databind") {
because("compatibility for Android API Level < 26 (Android 8.0) [O]")
version {
strictly("2.13.3")
because("Exception on 2.14.x: 'No virtual method getParameterCount()I in class Ljava/lang/reflect/Method'")
version /* as of Oct 12, 2023 */ {
strictly("2.14.3")
because("Exception on 2.15.x: 'IllegalArgumentException: Unsupported class file major version 63'")
}
}
@@ -234,7 +253,11 @@ dependencies /* GitHub API */ {
}
dependencies /* MLKit */ {
// OCR
implementation("com.google.mlkit:text-recognition-chinese:16.0.0")
// Barcode
implementation("com.google.mlkit:barcode-scanning:17.2.0")
}
dependencies /* Auto.js Extensions */ {
@@ -257,7 +280,7 @@ dependencies /* Auto.js Extensions */ {
// Extracted from com.github.hyb1996:MutableTheme:1.0.0
// @Legacy com.jrummyapps:colorpicker:2.1.7
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")
}
@@ -280,6 +303,7 @@ dependencies /* Reserved for auto append by IDE */ {
}
android {
namespace = applicationId
compileSdk = versions.sdkVersionCompile
@@ -288,45 +312,95 @@ android {
minSdk = versions.sdkVersionMin
targetSdk = versions.sdkVersionTarget
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("boolean", "isInrt", "false")
multiDexEnabled = true
buildConfigField("boolean", "is${flavorNameInrt.uppercaseFirstChar()}", "false")
}
flavorDimensions.add("channel")
flavorDimensions.add(dimention)
productFlavors {
create("app") {
dimension = "channel"
create(flavorNameApp) {
dimension = dimention
versionCode = versions.appVersionCode
versionName = versions.appVersionName
buildConfigField("String", "CHANNEL", "\"app\"")
buildConfigField("boolean", "isInrt", "false")
buildConfigField("String", "CHANNEL", "\"$flavorNameApp\"")
buildConfigField("boolean", "is${flavorNameInrt.uppercaseFirstChar()}", "false")
manifestPlaceholders.putAll(
mapOf(
"CHANNEL" to "app",
"CHANNEL" to flavorNameApp,
"appName" to "@string/app_name",
"intentCategory" to "android.intent.category.LAUNCHER",
"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") {
dimension = "channel"
applicationIdSuffix = ".inrt"
create(flavorNameInrt) {
dimension = dimention
applicationIdSuffix = ".$flavorNameInrt"
minSdk = versions.sdkVersionMin
targetSdk = versions.sdkVersionTargetInrt
compileSdk = versions.sdkVersionCompile
versionCode = versions.appVersionCode
versionName = versions.appVersionName
buildConfigField("String", "CHANNEL", "\"inrt\"")
buildConfigField("boolean", "isInrt", "true")
buildConfigField("String", "CHANNEL", "\"$flavorNameInrt\"")
buildConfigField("boolean", "is${flavorNameInrt.uppercaseFirstChar()}", "true")
manifestPlaceholders.putAll(
mapOf(
"CHANNEL" to "inrt",
"appName" to "AutoJs6.inrt",
"CHANNEL" to flavorNameInrt,
"appName" to "AutoJs6.$flavorNameInrt",
"intentCategory" to "android.intent.category.DEFAULT",
"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 {
@@ -337,8 +411,8 @@ android {
// Utils.Version(ANDROID_GRADLE_PLUGIN_VERSION) < Utils.Version("8.0")
@Suppress("DEPRECATION")
packagingOptions {
// @Legacy packagingOptions { ... }
packaging {
arrayOf(
"META-INF/DEPENDENCIES",
"META-INF/LICENSE",
@@ -350,7 +424,12 @@ android {
"META-INF/notice.*",
"META-INF/ASL2.0",
"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) }
jniLibs {
useLegacyPackaging = true
}
@@ -366,8 +445,8 @@ android {
}
signingConfigs {
takeIf { sign.isValid }?.let {
create("release") {
if (sign.isValid) {
create(buildTypeRelease) {
storeFile = sign.properties["storeFile"]?.let { file(it as String) }
keyPassword = sign.properties["keyPassword"] as String
keyAlias = sign.properties["keyAlias"] as String
@@ -382,23 +461,23 @@ android {
"proguard-rules.pro",
)
val niceSigningConfig = takeIf { sign.isValid }?.let {
signingConfigs.getByName("release")
signingConfigs.getByName(buildTypeRelease)
}
getByName("release") {
getByName(buildTypeRelease) {
isMinifyEnabled = false
proguardFiles(*proguardFiles)
niceSigningConfig?.let { signingConfig = it }
}
getByName("debug") {
isMinifyEnabled = getByName("release").isMinifyEnabled
getByName(buildTypeDebug) {
isMinifyEnabled = getByName(buildTypeRelease).isMinifyEnabled
proguardFiles(*proguardFiles)
niceSigningConfig?.let { signingConfig = it }
}
}
buildFeatures {
aidl = true
viewBinding = true
// @Hint by SuperMonster003 on Aug 14, 2023.
// ! Substitution of "android.defaults.buildfeatures.buildconfig=true"
buildConfig = true
@@ -459,6 +538,7 @@ android {
isUniversalApk = true
}
}
}
tasks {
@@ -477,24 +557,30 @@ tasks {
}
register<Copy>("appendDigestToReleasedFiles") {
val src = "release"
val dst = "${src}s"
val ext = Utils.FILE_EXTENSION_APK
listOf(flavorNameApp, flavorNameInrt).forEach { flavorName ->
val src = "$flavorName/$buildTypeRelease"
val dst = "${src}s"
val ext = Utils.FILE_EXTENSION_APK
from(src); into(dst); include("*.$ext")
rename { name ->
Utils.digestCRC32(file("${src}/$name")).let { digest ->
name.replace("^(.+?)(\\.$ext)\$".toRegex(), "\$1-$digest\$2")
if (!file(src).isDirectory) {
return@forEach
}
}
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 {
versions.handleIfNeeded(project)
versions.handleIfNeeded(project, flavorNameApp, listOf(buildTypeDebug, buildTypeRelease))
}
class Sign(filePath: String) {
@@ -523,6 +609,7 @@ class Versions(filePath: String) {
val sdkVersionMin = properties["MIN_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 appVersionName = properties["VERSION_NAME"] as String
val appVersionCode = properties["VERSION_BUILD"].let { it as String }.toInt()
@@ -559,7 +646,6 @@ class Versions(filePath: String) {
var versionInt = javaVersionRaw.toInt()
while (versionInt > javaVersionMinSupported) {
@Suppress("EnumValuesSoftDeprecate")
if (JvmTarget.values().any { it.name.contains(Regex("_$versionInt$")) }) {
break
}
@@ -590,15 +676,14 @@ class Versions(filePath: String) {
private var isBuildNumberAutoIncremented = false
private val minBuildTimeGap = Utils.hours2Millis(0.75)
private val assembleTargets = mapOf("app" to listOf("debug", "release"))
private val isBuildGapEnough
get() = properties["BUILD_TIME"]?.let {
Date().time - (it as String).toLong() > minBuildTimeGap
} ?: false
private fun appendToTask(project: Project, buildType: String) {
project.tasks.getByName(Utils.getAssembleTaskName(buildType)).doLast {
private fun appendToTask(project: Project, flavorName: String, buildType: String) {
project.tasks.getByName(Utils.getAssembleTaskName(flavorName, buildType)).doLast {
updateProperties()
println()
showInfo()
@@ -618,7 +703,7 @@ class Versions(filePath: String) {
val title = "Version information for AutoJs6 app library"
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 infoVerJava = "Java version: $javaVersion$javaVersionInfoSuffix"
@@ -637,21 +722,17 @@ class Versions(filePath: String) {
).forEach { println(it) }
}
fun handleIfNeeded(project: Project) = assembleTargets.forEach {
val targetName = it.key
val targetBuildType = it.value
if (targetName == project.name) {
project.gradle.taskGraph.whenReady(object : Action<TaskExecutionGraph> {
override fun execute(taskGraph: TaskExecutionGraph) {
for (buildType in targetBuildType) {
if (taskGraph.hasTask(Utils.getAssembleFullTaskName(targetName, buildType))) {
return appendToTask(project, buildType)
}
fun handleIfNeeded(project: Project, flavorName: String, targetBuildType: List<String>) {
project.gradle.taskGraph.whenReady(object : Action<TaskExecutionGraph> {
override fun execute(taskGraph: TaskExecutionGraph) {
for (buildType in targetBuildType) {
if (taskGraph.hasTask(Utils.getAssembleFullTaskName(project.name, flavorName, buildType))) {
return appendToTask(project, flavorName, buildType)
}
return showInfo()
}
})
}
return showInfo()
}
})
}
}
@@ -676,9 +757,9 @@ object Utils {
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 {
val fis = FileInputStream(file)

View File

@@ -74,7 +74,9 @@
<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
android:name="android.permission.DUMP"
@@ -113,19 +115,22 @@
<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
android:name="org.autojs.autojs.App"
android:allowBackup="false"
android:extractNativeLibs="true"
android:hardwareAccelerated="true"
android:icon="@drawable/autojs6_material"
android:icon="${icon}"
android:label="${appName}"
android:largeHeap="true"
android:largeHeap="false"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:localeConfig="@xml/locales_config"
android:usesCleartextTraffic="true"
tools:ignore="DataExtractionRules">
tools:ignore="DataExtractionRules"
tools:targetApi="tiramisu">
<meta-data
android:name="CHANNEL"
@@ -141,8 +146,14 @@
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.MAIN"/>
<category android:name="${intentCategory}"/>
<category android:name="${intentCategory}"/>
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/app_shortcuts"/>
</activity>
<activity
@@ -165,6 +176,16 @@
</intent-filter>
</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
android:name="org.autojs.autojs.ui.edit.EditActivity"
android:configChanges="orientation|keyboardHidden|screenSize|locale"
@@ -179,6 +200,7 @@
<activity
android:name="org.autojs.autojs.ui.settings.PreferencesActivity"
android:exported="true"
android:theme="@style/AppTheme.Settings"/>
<activity
@@ -197,13 +219,14 @@
<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
android:name="org.autojs.autojs.ui.doc.DocumentationActivity"
android:exported="true"
android:icon="@drawable/ic_docs_app"
android:label="@string/docs_app_name"
android:label="@string/text_app_shortcut_docs_long_label"
android:taskAffinity="org.autojs.autojs.ui.doc.DocumentationActivity"
android:theme="@style/AppTheme.FullScreen">
@@ -562,14 +585,25 @@
android:multiprocess="false"
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
android:name="org.autojs.autojs.inrt.SplashActivity"
android:exported="true"
android:theme="@style/AppTheme.Splash">
<intent-filter tools:ignore="ExtraText">
<action android:name="android.intent.action.MAIN"/>
<category android:name="${intentCategoryInrt}"/>
</intent-filter>
</activity>
<activity android:name="org.autojs.autojs.inrt.LogActivity"/>
@@ -577,15 +611,12 @@
<activity android:name="org.autojs.autojs.inrt.SettingsActivity"/>
<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>
android:name="rikka.shizuku.ShizukuProvider"
android:authorities="org.autojs.autojs6.shizuku"
android:enabled="true"
android:exported="true"
android:multiprocess="false"
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL"/>
</application>
</manifest>

View File

@@ -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;
}

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)_
* `新增` 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 等方法
* `新增` 内部 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 插件保存文件到本地时文件扩展名信息丢失的问题
* `修复` 使用协程特性运行项目产生未捕获异常致使应用崩溃的问题
* `修复` 重启或退出应用时浮动按钮无法记录其位置状态信息的问题
* `修复` 设备屏幕方向改变时无法获取更新后的设备配置信息的问题
* `修复` 设备屏幕方向改变时无法获取更新后的设备配置信息的问题 _[`issue #153`](http://issues.autojs6.com/153)_
* `修复` 屏幕旋转至横向时 Toolbar 标题字体过小的问题
* `修复` 屏幕旋转至横向时应用主页的页签排版过于拥挤的问题
* `修复` 屏幕旋转至横向时浮动按钮可能溢出屏幕的问题 _[`issue #90`](http://issues.autojs6.com/90)_
* `修复` 屏幕多次旋转时无法恢复浮动按钮的坐标及屏幕侧边方向的问题
* `修复` 部分设备消息浮动框可能出现遗漏显示或重复显示的问题
* `修复` 消息浮动框在多个脚本同时运行时可能存在被遮蔽的问题 _[`issue #67`](http://issues.autojs6.com/67)_
* `修复` 使用广播分析布局时点击布局无法弹出菜单且导致应用崩溃的问题
* `修复` 第二次及以后创建的 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)_
* `修复` 日志活动页面使用复制或导出功能时可能导致应用崩溃的问题
* `优化` 日志活动页面导出功能重命名为发送功能并重新实现符合实际意义的导出功能
@@ -31,17 +38,26 @@
* `优化` ocr 模块同时兼容 Google MLKit 及 Paddle Lite 引擎 (参阅 项目文档 > [光学字符识别](https://docs.autojs6.com/#/ocr?id=p-mode))
* `优化` 提升无障碍服务自动启动的成功概率
* `优化` 构建工具自适应 Java 发行版本以尽量避免 "无效的发行版本" 问题
* `优化` 构建工具优化 IDE 及相关插件的版本退级逻辑并增加版本预测能力
* `优化` 部分 Kotlin 注解处理由 kapt 迁移至 KSP
* `优化` 附加 Rikka Shizuku 版本 13.1.5
* `优化` 升级 Gradle Compile 版本 33 -> 34
* `优化` 升级 Gradle 版本 8.3-rc-1 -> 8.4-rc-3
* `优化` 升级 Apache Commons Lang3 版本 3.12.0 -> 3.13.0
* `优化` 升级 Glide 版本 4.15.1 -> 4.16.0
* `优化` 升级 Android Analytics 版本 14.3.0 -> 14.4.0
* `优化` 升级 Androidx 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.1
* `依赖` 附加 Rikka Shizuku 版本 13.1.5
* `依赖` 附加 MLKit Barcode Scanning 版本 17.2.0
* `依赖` 升级 OpenCV 版本 4.5.5 -> 4.8.0 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ))
* `依赖` 升级 Gradle Compile 版本 33 -> 34
* `依赖` 升级 Gradle 版本 8.3-rc-1 -> 8.4-rc-3
* `依赖` 升级 Apache Commons Lang3 版本 3.12.0 -> 3.13.0
* `依赖` 升级 Glide 版本 4.15.1 -> 4.16.0
* `依赖` 升级 Android Analytics 版本 14.3.0 -> 14.4.0
* `依赖` 升级 Androidx AppCompat 版本 1.4.2 -> 1.6.0-alpha05
* `依赖` 升级 Androidx WebKit 版本 1.7.0 -> 1.8.0
* `依赖` 升级 Androidx Preference 版本 1.2.0 -> 1.2.1
* `依赖` 升级 Androidx Annotation 版本 1.6.0 -> 1.7.0
* `依赖` 升级 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
@@ -63,7 +79,7 @@
* `优化` 在 _[`pr #98`](http://pr.autojs6.com/98)_ 基础上添加代码注释菜单项
* `优化` 适配 VSCode 插件 1.0.6
* `优化` UiObject#parent 方法增加级数参数支持 (参阅 项目文档 > [控件节点](https://docs.autojs6.com/#/uiObjectType))
* `优化` 升级 Gradle 版本 8.2 -> 8.3-rc-1
* `依赖` 升级 Gradle 版本 8.2 -> 8.3-rc-1
# v6.3.2
@@ -97,9 +113,9 @@
* `优化` 增加短信数据读取权限 (android.permission.READ_SMS) (默认关闭)
* `优化` findMultiColors 方法内部实现 (by [LYS86](https://github.com/LYS86)) _[`pr #72`](http://pr.autojs6.com/72)_
* `优化` runtime.loadDex/loadJar/load 支持按目录级别加载或同时加载多个文件
* `优化` 升级 Leakcanary 版本 2.11 -> 2.12
* `优化` 升级 Android Analytics 版本 14.2.0 -> 14.3.0
* `优化` 升级 Gradle 版本 8.2-milestone-1 -> 8.2
* `依赖` 升级 Leakcanary 版本 2.11 -> 2.12
* `依赖` 升级 Android Analytics 版本 14.2.0 -> 14.3.0
* `依赖` 升级 Gradle 版本 8.2-milestone-1 -> 8.2
# v6.3.1
@@ -131,11 +147,11 @@
* `优化` 视图 (View) 绑定方式由 ButterKnife 迁移至 View Binding _[`issue #48`](http://issues.autojs6.com/48)_
* `优化` 服务端模式非正常关闭时将于 AutoJs6 启动时自动开启 _[`issue #64`](http://issues.autojs6.com/64)_
* `优化` 客户端模式非正常关闭时将于 AutoJs6 启动时按最近一次的历史地址自动连接
* `优化` 升级 Leakcanary 版本 2.10 -> 2.11
* `优化` 升级 Android Material 版本 1.8.0 -> 1.9.0
* `优化` 升级 Androidx WebKit 版本 1.6.1 -> 1.7.0
* `优化` 升级 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
* `依赖` 升级 Leakcanary 版本 2.10 -> 2.11
* `依赖` 升级 Android Material 版本 1.8.0 -> 1.9.0
* `依赖` 升级 Androidx WebKit 版本 1.6.1 -> 1.7.0
* `依赖` 升级 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
# v6.3.0
@@ -184,23 +200,23 @@
* `优化` http 模块相关方法支持超时参数 (timeout)
* `优化` Gradle 构建脚本支持 JDK 版本主动降级 (fallback)
* `优化` Gradle 构建脚本支持根据平台类型及版本自动选择合适的构建工具版本 (程度有限)
* `优化` 本地化 Auto.js APK Builder 版本 1.0.3
* `优化` 本地化 MultiLevelListView 版本 1.1
* `优化` 本地化 Settings Compat 版本 1.1.5
* `优化` 本地化 Enhanced Floaty 版本 0.31
* `优化` 附加 MLKit Text Recognition Chinese 版本 16.0.0-beta6
* `优化` 升级 Gradle 版本 8.0-rc-1 -> 8.2-milestone-1
* `优化` 升级 Android Material 版本 1.7.0 -> 1.8.0
* `优化` 升级 Glide 版本 4.14.2 -> 4.15.1
* `优化` 升级 Joda Time 版本 2.12.2 -> 2.12.5
* `优化` 升级 Android Analytics 版本 14.0.0 -> 14.2.0
* `优化` 升级 Androidx WebKit 版本 1.5.0 -> 1.6.1
* `优化` 升级 Androidx Recyclerview 版本 1.2.1 -> 1.3.0
* `优化` 升级 Zip4j 版本 2.11.2 -> 2.11.5
* `优化` 升级 Junit 版本 5.9.2 -> 5.9.3
* `优化` 升级 Androidx Annotation 版本 1.5.0 -> 1.6.0
* `优化` 升级 Jackson DataBind 版本 2.14.1 -> 2.14.2
* `优化` 升级 Desugar JDK Libs 版本 2.0.0 -> 2.0.3
* `依赖` 本地化 Auto.js APK Builder 版本 1.0.3
* `依赖` 本地化 MultiLevelListView 版本 1.1
* `依赖` 本地化 Settings Compat 版本 1.1.5
* `依赖` 本地化 Enhanced Floaty 版本 0.31
* `依赖` 附加 MLKit Text Recognition Chinese 版本 16.0.0-beta6
* `依赖` 升级 Gradle 版本 8.0-rc-1 -> 8.2-milestone-1
* `依赖` 升级 Android Material 版本 1.7.0 -> 1.8.0
* `依赖` 升级 Glide 版本 4.14.2 -> 4.15.1
* `依赖` 升级 Joda Time 版本 2.12.2 -> 2.12.5
* `依赖` 升级 Android Analytics 版本 14.0.0 -> 14.2.0
* `依赖` 升级 Androidx WebKit 版本 1.5.0 -> 1.6.1
* `依赖` 升级 Androidx Recyclerview 版本 1.2.1 -> 1.3.0
* `依赖` 升级 Zip4j 版本 2.11.2 -> 2.11.5
* `依赖` 升级 Junit 版本 5.9.2 -> 5.9.3
* `依赖` 升级 Androidx Annotation 版本 1.5.0 -> 1.6.0
* `依赖` 升级 Jackson DataBind 版本 2.14.1 -> 2.14.2
* `依赖` 升级 Desugar JDK Libs 版本 2.0.0 -> 2.0.3
# v6.2.0
@@ -300,32 +316,32 @@
* `优化` 提升 console.show 显示的日志浮动窗口文字亮度以增强内容辨识度
* `优化` ImageWrapper#saveTo 支持相对路径保存图像文件
* `优化` 重新设计 colors 全局对象并增加 HSV / HSL 等色彩模式支持 (参阅 项目文档 > [颜色](https://docs.autojs6.com/#/color))
* `优化` 升级 Gradle Compile 版本 32 -> 33
* `优化` 本地化 Android Job 版本 1.4.3
* `优化` 本地化 Android Plugin Client SDK For Locale 版本 9.0.0
* `优化` 本地化 GitHub API 版本 1.306
* `优化` 附加 JCIP Annotations 版本 1.0
* `优化` 附加 Androidx WebKit 版本 1.5.0
* `优化` 附加 Commons IO 版本 2.8.0
* `优化` 附加 Desugar JDK Libs 版本 2.0.0
* `优化` 附加 Jackson DataBind 版本 2.13.3
* `优化` 附加 Jaredrummler Android Device Names 版本 2.1.0
* `优化` 附加 Jaredrummler Animated SVG View 版本 1.0.6
* `优化` 替换 Jrummyapps ColorPicker 版本 2.1.7 为 Jaredrummler ColorPicker 版本 1.1.0
* `优化` 升级 Gradle 版本 7.5-rc-1 -> 8.0-rc-1
* `优化` 升级 Gradle 构建工具版本 7.4.0-alpha02 -> 8.0.0-alpha09
* `优化` 升级 Kotlin Gradle 插件版本 1.6.10 -> 1.8.0-RC2
* `优化` 升级 Android Material 版本 1.6.0 -> 1.7.0
* `优化` 升级 Androidx Annotation 版本 1.3.0 -> 1.5.0
* `优化` 升级 Androidx AppCompat 版本 1.4.1 -> 1.4.2
* `优化` 升级 Android Analytics 版本 13.3.0 -> 14.0.0
* `优化` 升级 Gson 版本 2.9.0 -> 2.10
* `优化` 升级 Joda Time 版本 2.10.14 -> 2.12.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
* `优化` 升级 Zip4j 版本 2.10.0 -> 2.11.2
* `优化` 升级 Glide 版本 4.13.2 -> 4.14.2
* `优化` 升级 Junit 版本 5.9.0 -> 5.9.1
* `依赖` 升级 Gradle Compile 版本 32 -> 33
* `依赖` 本地化 Android Job 版本 1.4.3
* `依赖` 本地化 Android Plugin Client SDK For Locale 版本 9.0.0
* `依赖` 本地化 GitHub API 版本 1.306
* `依赖` 附加 JCIP Annotations 版本 1.0
* `依赖` 附加 Androidx WebKit 版本 1.5.0
* `依赖` 附加 Commons IO 版本 2.8.0
* `依赖` 附加 Desugar JDK Libs 版本 2.0.0
* `依赖` 附加 Jackson DataBind 版本 2.13.3
* `依赖` 附加 Jaredrummler Android Device Names 版本 2.1.0
* `依赖` 附加 Jaredrummler Animated SVG View 版本 1.0.6
* `依赖` 替换 Jrummyapps ColorPicker 版本 2.1.7 为 Jaredrummler ColorPicker 版本 1.1.0
* `依赖` 升级 Gradle 版本 7.5-rc-1 -> 8.0-rc-1
* `依赖` 升级 Gradle 构建工具版本 7.4.0-alpha02 -> 8.0.0-alpha09
* `依赖` 升级 Kotlin Gradle 插件版本 1.6.10 -> 1.8.0-RC2
* `依赖` 升级 Android Material 版本 1.6.0 -> 1.7.0
* `依赖` 升级 Androidx Annotation 版本 1.3.0 -> 1.5.0
* `依赖` 升级 Androidx AppCompat 版本 1.4.1 -> 1.4.2
* `依赖` 升级 Android Analytics 版本 13.3.0 -> 14.0.0
* `依赖` 升级 Gson 版本 2.9.0 -> 2.10
* `依赖` 升级 Joda Time 版本 2.10.14 -> 2.12.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
* `依赖` 升级 Zip4j 版本 2.10.0 -> 2.11.2
* `依赖` 升级 Glide 版本 4.13.2 -> 4.14.2
* `依赖` 升级 Junit 版本 5.9.0 -> 5.9.1
# v6.1.1
@@ -338,8 +354,8 @@
* `修复` 编辑器页面选项菜单中 "粘贴" 功能缺失的问题
* `优化` 部分异常消息字符串资源化 (en / zh)
* `优化` 调整内容未保存对话框的按钮布局并增加颜色区分
* `优化` 附加 github-api 版本 1.306
* `优化` 替换 retrofit2-rxjava2-adapter 版本 1.0.0 为 adapter-rxjava2 版本 2.9.0
* `依赖` 附加 github-api 版本 1.306
* `依赖` 替换 retrofit2-rxjava2-adapter 版本 1.0.0 为 adapter-rxjava2 版本 2.9.0
# v6.1.0
@@ -385,22 +401,22 @@
* `优化` 外部存储读写权限适配高版本安卓系统 (API >= 30)
* `优化` 控制台字体采用 Material Color 增强普通及夜间主题下的字体可读性
* `优化` 保存 ImageWrapper 所有实例弱引用并在脚本结束时自动回收 (实验性)
* `优化` 附加 CircleImageView 版本 3.1.0
* `优化` 升级 Android Analytics 版本 13.1.0 -> 13.3.0
* `优化` 升级 Gradle 构建工具版本 7.3.0-alpha06 -> 7.4.0-alpha02
* `优化` 升级 Android Job 版本 1.4.2 -> 1.4.3
* `优化` 升级 Android Material 版本 1.5.0 -> 1.6.0
* `优化` 升级 CrashReport 版本 2.6.6 -> 4.0.4
* `优化` 升级 Glide 版本 4.13.1 -> 4.13.2
* `优化` 升级 Joda Time 版本 2.10.13 -> 2.10.14
* `优化` 升级 Kotlin Gradle 插件版本 1.6.10 -> 1.6.21
* `优化` 升级 Kotlinx Coroutines 版本 1.6.0 -> 1.6.1-native-mt
* `优化` 升级 Leakcanary 版本 2.8.1 -> 2.9.1
* `优化` 升级 Okhttp3 版本 5.0.0-alpha.6 -> 5.0.0-alpha.7
* `优化` 升级 Rhino 引擎版本 1.7.14 -> 1.7.15-snapshot
* `优化` 升级 Zip4j 版本 2.9.1 -> 2.10.0
* `优化` 移除 Groovy JSON 版本 3.0.8
* `优化` 移除 Kotlin Stdlib JDK7 版本 1.6.21
* `依赖` 附加 CircleImageView 版本 3.1.0
* `依赖` 升级 Android Analytics 版本 13.1.0 -> 13.3.0
* `依赖` 升级 Gradle 构建工具版本 7.3.0-alpha06 -> 7.4.0-alpha02
* `依赖` 升级 Android Job 版本 1.4.2 -> 1.4.3
* `依赖` 升级 Android Material 版本 1.5.0 -> 1.6.0
* `依赖` 升级 CrashReport 版本 2.6.6 -> 4.0.4
* `依赖` 升级 Glide 版本 4.13.1 -> 4.13.2
* `依赖` 升级 Joda Time 版本 2.10.13 -> 2.10.14
* `依赖` 升级 Kotlin Gradle 插件版本 1.6.10 -> 1.6.21
* `依赖` 升级 Kotlinx Coroutines 版本 1.6.0 -> 1.6.1-native-mt
* `依赖` 升级 Leakcanary 版本 2.8.1 -> 2.9.1
* `依赖` 升级 Okhttp3 版本 5.0.0-alpha.6 -> 5.0.0-alpha.7
* `依赖` 升级 Rhino 引擎版本 1.7.14 -> 1.7.15-snapshot
* `依赖` 升级 Zip4j 版本 2.9.1 -> 2.10.0
* `依赖` 移除 Groovy JSON 版本 3.0.8
* `依赖` 移除 Kotlin Stdlib JDK7 版本 1.6.21
# v6.0.3
@@ -423,17 +439,17 @@
* `优化` 浮动按钮初始化时同时应用透明度设置 (无需点击后再应用透明度)
* `优化` 重置文件内容时增加是否为示例代码文件的检测并增加结果提示
* `优化` 转移打包插件下载地址 GitHub -> JsDelivr
* `优化` 附加 Zeugma Solutions LocaleHelper 版本 1.5.1
* `优化` 降级 Android Material 版本 1.6.0-alpha02 -> 1.5.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))
* `优化` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.4 -> 5.0.0-alpha.6
* `优化` 升级 Gradle 构建工具版本 7.2.0-beta01 -> 7.3.0-alpha06
* `优化` 升级 Auto.js-ApkBuilder 版本 1.0.1 -> 1.0.3
* `优化` 升级 Glide Compiler 版本 4.12.0 -> 4.13.1
* `优化` 升级 Gradle 发行版本 7.4-rc-2 -> 7.4.1
* `优化` 升级 Gradle Compile 版本 31 -> 32
* `优化` 升级 Gson 版本 2.8.9 -> 2.9.0
* `依赖` 附加 Zeugma Solutions LocaleHelper 版本 1.5.1
* `依赖` 降级 Android Material 版本 1.6.0-alpha02 -> 1.5.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))
* `依赖` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.4 -> 5.0.0-alpha.6
* `依赖` 升级 Gradle 构建工具版本 7.2.0-beta01 -> 7.3.0-alpha06
* `依赖` 升级 Auto.js-ApkBuilder 版本 1.0.1 -> 1.0.3
* `依赖` 升级 Glide Compiler 版本 4.12.0 -> 4.13.1
* `依赖` 升级 Gradle 发行版本 7.4-rc-2 -> 7.4.1
* `依赖` 升级 Gradle Compile 版本 31 -> 32
* `依赖` 升级 Gson 版本 2.8.9 -> 2.9.0
# 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)_
* `优化` 忽略 sleep() 的 ScriptInterruptedException 异常
* `优化` 附加 Androidx AppCompat (Legacy) 版本 1.0.2
* `优化` 升级 Androidx AppCompat 版本 1.4.0 -> 1.4.1
* `优化` 升级 Androidx Preference 版本 1.1.1 -> 1.2.0
* `优化` 升级 Rhino 引擎版本 1.7.14-snapshot -> 1.7.14
* `优化` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.3 -> 5.0.0-alpha.4
* `优化` 升级 Android Material 版本 1.6.0-alpha01 -> 1.6.0-alpha02
* `优化` 升级 Gradle 构建工具版本 7.2.0-alpha06 -> 7.2.0-beta01
* `优化` 升级 Gradle 发行版本 7.3.3 -> 7.4-rc-2
* `依赖` 附加 Androidx AppCompat (Legacy) 版本 1.0.2
* `依赖` 升级 Androidx AppCompat 版本 1.4.0 -> 1.4.1
* `依赖` 升级 Androidx Preference 版本 1.1.1 -> 1.2.0
* `依赖` 升级 Rhino 引擎版本 1.7.14-snapshot -> 1.7.14
* `依赖` 升级 Okhttp3 版本 3.10.0 -> 5.0.0-alpha.3 -> 5.0.0-alpha.4
* `依赖` 升级 Android Material 版本 1.6.0-alpha01 -> 1.6.0-alpha02
* `依赖` 升级 Gradle 构建工具版本 7.2.0-alpha06 -> 7.2.0-beta01
* `依赖` 升级 Gradle 发行版本 7.3.3 -> 7.4-rc-2
# v6.0.1
@@ -467,11 +483,11 @@
* `优化` 扩展 global.sleep 支持 随机范围/负数兼容
* `优化` 扩展 global.toast 支持 时长控制/强制覆盖控制/dismiss
* `优化` 包名对象全局化 (okhttp3/androidx/de)
* `优化` 升级 Android Material 版本 1.5.0-beta01 -> 1.6.0-alpha01
* `优化` 升级 Gradle 构建工具版本 7.2.0-alpha04 -> 7.2.0-alpha06
* `优化` 升级 Kotlinx Coroutines 版本 1.5.2-native-mt -> 1.6.0-native-mt
* `优化` 升级 Kotlin Gradle 插件版本 1.6.0 -> 1.6.10
* `优化` 升级 Gradle 发行版本 7.3 -> 7.3.3
* `依赖` 升级 Android Material 版本 1.5.0-beta01 -> 1.6.0-alpha01
* `依赖` 升级 Gradle 构建工具版本 7.2.0-alpha04 -> 7.2.0-alpha06
* `依赖` 升级 Kotlinx Coroutines 版本 1.5.2-native-mt -> 1.6.0-native-mt
* `依赖` 升级 Kotlin Gradle 插件版本 1.6.0 -> 1.6.10
* `依赖` 升级 Gradle 发行版本 7.3 -> 7.3.3
# v6.0.0
@@ -499,39 +515,39 @@
* `优化` ResourceMonitor 使用 ReentrantLock 增强安全性 (Ref to [TonyJiangWJ](https://github.com/TonyJiangWJ))
* `优化` 使用 Maven Central 等仓库替换 JCenter 仓库
* `优化` 抽离并移除重复的本地库文件
* `优化` 本地化 CrashReport 版本 2.6.6
* `优化` 本地化 MutableTheme 版本 1.0.0
* `优化` 附加 Androidx Preference 版本 1.1.1
* `优化` 附加 SwipeRefreshLayout 版本 1.1.0
* `优化` 升级 Android Analytics 版本 7.0.0 -> 13.1.0
* `优化` 升级 Android Annotations 版本 4.5.2 -> 4.8.0
* `优化` 升级 Gradle 构建工具版本 3.2.1 -> 4.1.0 -> 7.0.3 -> 7.2.0-alpha04
* `优化` 升级 Android Job 版本 1.2.6 -> 1.4.2
* `优化` 升级 Android Material 版本 1.1.0-alpha01 -> 1.5.0-beta01
* `优化` 升级 Androidx MultiDex 版本 2.0.0 -> 2.0.1
* `优化` 升级 Apache Commons Lang3 版本 3.6 -> 3.12.0
* `优化` 升级 Appcompat 版本 1.0.2 -> 1.4.0
* `优化` 升级 ButterKnife Gradle 插件版本 9.0.0-rc2 -> 10.2.1 -> 10.2.3
* `优化` 升级 ColorPicker 版本 2.1.5 -> 2.1.7
* `优化` 升级 Espresso Core 版本 3.1.1-alpha01 -> 3.5.0-alpha03
* `优化` 升级 Eventbus 版本 3.0.0 -> 3.2.0
* `优化` 升级 Glide Compiler 版本 4.8.0 -> 4.12.0 -> 4.12.0
* `优化` 升级 Gradle Build Tool 版本 29.0.2 -> 30.0.2
* `优化` 升级 Gradle Compile 版本 28 -> 30 -> 31
* `优化` 升级 Gradle 发行版本 4.10.2 -> 6.5 -> 7.0.2 -> 7.3
* `优化` 升级 Groovy-Json 插件版本 3.0.7 -> 3.0.8
* `优化` 升级 Gson 版本 2.8.2 -> 2.8.9
* `优化` 升级 JavaVersion 版本 1.8 -> 11 -> 16
* `优化` 升级 Joda Time 版本 2.9.9 -> 2.10.13
* `优化` 升级 Junit 版本 4.12 -> 4.13.2
* `优化` 升级 Kotlin Gradle 插件版本 1.3.10 -> 1.4.10 -> 1.6.0
* `优化` 升级 Kotlinx Coroutines 版本 1.0.1 -> 1.5.2-native-mt
* `优化` 升级 Leakcanary 版本 1.6.1 -> 2.7
* `优化` 升级 LicensesDialog 版本 1.8.1 -> 2.2.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
* `优化` 升级 Reactivex RxJava2 RxAndroid 版本 2.0.1 -> 2.1.1
* `优化` 升级 Reactivex RxJava2 版本 2.1.2 -> 2.2.21
* `优化` 升级 Retrofit2 Converter Gson 版本 2.3.0 -> 2.9.0
* `优化` 升级 Retrofit2 Retrofit 版本 2.3.0 -> 2.9.0
* `优化` 升级 Zip4j 版本 1.3.2 -> 2.9.1
* `依赖` 本地化 CrashReport 版本 2.6.6
* `依赖` 本地化 MutableTheme 版本 1.0.0
* `依赖` 附加 Androidx Preference 版本 1.1.1
* `依赖` 附加 SwipeRefreshLayout 版本 1.1.0
* `依赖` 升级 Android Analytics 版本 7.0.0 -> 13.1.0
* `依赖` 升级 Android Annotations 版本 4.5.2 -> 4.8.0
* `依赖` 升级 Gradle 构建工具版本 3.2.1 -> 4.1.0 -> 7.0.3 -> 7.2.0-alpha04
* `依赖` 升级 Android Job 版本 1.2.6 -> 1.4.2
* `依赖` 升级 Android Material 版本 1.1.0-alpha01 -> 1.5.0-beta01
* `依赖` 升级 Androidx MultiDex 版本 2.0.0 -> 2.0.1
* `依赖` 升级 Apache Commons Lang3 版本 3.6 -> 3.12.0
* `依赖` 升级 Appcompat 版本 1.0.2 -> 1.4.0
* `依赖` 升级 ButterKnife Gradle 插件版本 9.0.0-rc2 -> 10.2.1 -> 10.2.3
* `依赖` 升级 ColorPicker 版本 2.1.5 -> 2.1.7
* `依赖` 升级 Espresso Core 版本 3.1.1-alpha01 -> 3.5.0-alpha03
* `依赖` 升级 Eventbus 版本 3.0.0 -> 3.2.0
* `依赖` 升级 Glide Compiler 版本 4.8.0 -> 4.12.0 -> 4.12.0
* `依赖` 升级 Gradle Build Tool 版本 29.0.2 -> 30.0.2
* `依赖` 升级 Gradle Compile 版本 28 -> 30 -> 31
* `依赖` 升级 Gradle 发行版本 4.10.2 -> 6.5 -> 7.0.2 -> 7.3
* `依赖` 升级 Groovy-Json 插件版本 3.0.7 -> 3.0.8
* `依赖` 升级 Gson 版本 2.8.2 -> 2.8.9
* `依赖` 升级 JavaVersion 版本 1.8 -> 11 -> 16
* `依赖` 升级 Joda Time 版本 2.9.9 -> 2.10.13
* `依赖` 升级 Junit 版本 4.12 -> 4.13.2
* `依赖` 升级 Kotlin Gradle 插件版本 1.3.10 -> 1.4.10 -> 1.6.0
* `依赖` 升级 Kotlinx Coroutines 版本 1.0.1 -> 1.5.2-native-mt
* `依赖` 升级 Leakcanary 版本 1.6.1 -> 2.7
* `依赖` 升级 LicensesDialog 版本 1.8.1 -> 2.2.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
* `依赖` 升级 Reactivex RxJava2 RxAndroid 版本 2.0.1 -> 2.1.1
* `依赖` 升级 Reactivex RxJava2 版本 2.1.2 -> 2.2.21
* `依赖` 升级 Retrofit2 Converter Gson 版本 2.3.0 -> 2.9.0
* `依赖` 升级 Retrofit2 Retrofit 版本 2.3.0 -> 2.9.0
* `依赖` 升级 Zip4j 版本 1.3.2 -> 2.9.1

View File

@@ -47,12 +47,16 @@ let ColorUtils = org.autojs.autojs.util.ColorUtils;
let TextUtils = org.autojs.autojs.util.TextUtils;
let ProxyObject = org.autojs.autojs.rhino.ProxyObject;
let ProxyJavaObject = org.autojs.autojs.rhino.ProxyJavaObject;
let JavaScriptSource = org.autojs.autojs.script.JavaScriptSource;
let NotificationManager = android.app.NotificationManager;
let NotificationCompat = Packages.androidx.core.app.NotificationCompat;
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 URI = java.net.URI;
let File = java.io.File;
let Uri = android.net.Uri;
/* Global View classes. */
@@ -492,13 +496,16 @@ Object.assign(this, {
/* ! Numberx < Mathx */
[ 'plugins', 'Arrayx', 'Numberx', 'Mathx' ],
/* ! ocr < images */
/* ! ocr < files */
[ 'ocr' ],
/* ! images < ocr */
/* ! images < barcode */
/* ! images < qrcode */
/* ! barcode < qrcode */
/* ! files < ocr */
[ 'ocr', 'barcode', 'qrcode' ],
/* Safe to put last regardless of the order, no guarantee ;). */
[ 'floaty', 'storages', 'device', 'recorder', 'toast' ],
[ 'media', 'sensors', 'events', 'base64', 'notice' ],
[ 'media', 'sensors', 'events', 'base64', 'notice', 'shizuku' ],
/* Last but not the least */
[ 'globals' ],

View File

@@ -10,11 +10,7 @@ let { autojs, shell, files, util } = global;
* @return {Internal.App}
*/
module.exports = function (scriptRuntime, scope) {
const File = java.io.File;
const Uri = android.net.Uri;
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}

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;
};

View File

@@ -638,7 +638,7 @@ module.exports = function (scriptRuntime, scope) {
if (alphaMatters === true) {
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.

View File

@@ -71,7 +71,7 @@ module.exports = function (scriptRuntime, scope) {
setEngineExecArgv() {
engines.myEngine().setExecArgv(/* @IIFE */ ((e) => {
let execArgv = {};
let iterator = e.getTag(ExecutionConfig.CREATOR.getTag()).arguments.entrySet().iterator();
let iterator = e.getTag(ExecutionConfig.tag).arguments.entrySet().iterator();
while (iterator.hasNext()) {
let entry = iterator.next();
execArgv[entry.getKey()] = entry.getValue();

View File

@@ -49,6 +49,9 @@ module.exports = function (scriptRuntime, scope) {
Images.prototype = {
constructor: Images,
captureScreen(path) {
if (rtImages.getScreenCapturer() === null) {
this.requestScreenCapture();
}
return path === undefined
? rtImages.captureScreen()
: rtImages.captureScreen(path);

View File

@@ -40,7 +40,7 @@ module.exports = function (scriptRuntime, scope) {
enumerable: true, /* Emphasis. */
};
const instanceFunc = function (img, options) {
const instanceFunc = function __(img, options) {
if (typeof arguments[0] === 'string') {
let img = images.read(/* path = */ arguments[0]);
if (img === null) {
@@ -87,7 +87,7 @@ module.exports = function (scriptRuntime, scope) {
// @Signature
// 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]);
if (img === null) {
@@ -95,12 +95,25 @@ module.exports = function (scriptRuntime, scope) {
}
// @Overload
// 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]);
}
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])) {
// @Signature recognizeText(img: ImageWrapper, region: Images.Options.Region): string[];
// @Signature recognizeText(img: ImageWrapper, region: OmniRegion): string[];
// @Overload recognizeText(img: ImageWrapper, options: DetectOptionsMLKit | DetectOptionsPaddle): string[];
return this.recognizeText(img, { region: arguments[1] });
@@ -144,7 +157,7 @@ module.exports = function (scriptRuntime, scope) {
// @Signature
// 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]);
if (img === null) {
@@ -152,12 +165,25 @@ module.exports = function (scriptRuntime, scope) {
}
// @Overload
// 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]);
}
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])) {
// @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[];
return this.detect(img, { region: arguments[1] });

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;
};

View File

@@ -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;
};

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
* @return {Internal.Toast}
*/
module.exports = function (scriptRuntime, scope) {
let rtToast = scriptRuntime.toast;
const ScriptToast = org.autojs.autojs.runtime.api.ScriptToast;
let _ = {
uiHandler: runtime.getUiHandler(),
ToastCtor: (/* @IIFE */ () => {
/**
* @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 */
const instance = function (msg, isLong, isForcible) {
const toast = function (msg, isLong, isForcible) {
let $ = {
badge: {
long: /^l(ong)?$/i,
short: /^s(hort)?$/i,
forcible: /^f(orcible)?$/i,
},
toast() {
this.init(arguments);
this.show();
},
init() {
this.message = isNullish(msg) ? '' : String(msg);
this.isForcible = this.parseIsForcible(isForcible);
this.isLong = this.parseIsLong(isLong);
this.isForcible = this.parseIsForcible(isForcible);
},
parseIsLong(isLong) {
if (typeof isLong === 'boolean') {
return isLong;
parseIsLong(o) {
let def = typeof this.isLong === 'boolean' ? this.isLong : false;
if (typeof o === 'boolean') {
return o;
}
if (typeof isLong === 'number') {
return Boolean(isLong);
if (typeof o === 'number') {
return Boolean(o);
}
if (typeof isLong === 'string') {
if (this.badge.long.test(isLong)) {
if (typeof o === 'string') {
if (this.badge.long.test(o)) {
return true;
}
if (this.badge.short.test(isLong)) {
if (this.badge.short.test(o)) {
return false;
}
if (this.badge.forcible.test(isLong)) {
if (this.badge.forcible.test(o)) {
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) {
if (typeof isForcible === 'boolean') {
return isForcible;
parseIsForcible(o) {
let def = typeof this.isForcible === 'boolean' ? this.isForcible : false;
if (typeof o === 'boolean') {
return o;
}
if (typeof isForcible === 'number') {
return Boolean(isForcible);
if (typeof o === 'number') {
return Boolean(o);
}
if (typeof isForcible === 'string') {
if (this.badge.forcible.test(isForcible)) {
if (typeof o === 'string') {
if (this.badge.forcible.test(o)) {
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() {
rtToast.makeToast(this.message, this.isLong, this.isForcible);
show() {
if ($.isForcible) {
ToastCtor.prototype.dismissAll();
}
scriptRuntime.uiHandler.toast($.message, $.isLong);
},
};
$.init();
$.makeToast();
$.toast();
};
return Object.assign(instance, Ctor.prototype);
return Object.assign(toast, ToastCtor.prototype);
};
Ctor.prototype = {
constructor: Ctor,
dismissAll: () => rtToast.dismissAll(),
ToastCtor.prototype = {
constructor: ToastCtor,
dismissAll() {
runtime.uiHandler.dismissAllToasts();
},
};
return Ctor;
return ToastCtor;
})(),
};

View File

@@ -317,7 +317,7 @@ module.exports = function (scriptRuntime, scope) {
})();
},
afterApplyAttribute(context, inflater, view, ns, attrName, value, parent) {
// Empty method body
/* Empty body. */
},
afterApplyAttributes(context, view, inflater, attrs, parent) {
context.remove('widget');
@@ -326,13 +326,13 @@ module.exports = function (scriptRuntime, scope) {
return false;
},
afterInflateChildren(context, inflater, node, parent) {
// Empty method body
/* Empty body. */
},
beforeApplyPendingAttributesOfChildren(context, inflater, view) {
return false;
},
afterApplyPendingAttributesOfChildren(context, inflater, view) {
// Empty method body
/* Empty body. */
},
afterInflateView(context, view, node, parent, attachToParent) {
let { widget } = view;

View File

@@ -15,12 +15,16 @@ module.exports = function (scriptRuntime, scope) {
* @implements Internal.OcrMLKit
*/
const OcrMLKitCtor = function () {
return Object.assign(function () {
let instanceFunc = function __() {
let argArray = Array.from(arguments);
if (arguments.length === 0 || !(arguments[0] instanceof ImageWrapper)) {
return __.apply(this, [ images.captureScreen() ].concat(argArray));
}
/** @type {Internal.Ocr.ModeName} */
argArray[ocr['_forcibleModeArgIndex']] = ocr['_modeName']['mlkit'];
return ocr.recognizeText.apply(ocr, argArray);
}, OcrMLKitCtor.prototype);
};
return Object.assign(instanceFunc, OcrMLKitCtor.prototype);
};
OcrMLKitCtor.prototype = {

View File

@@ -15,12 +15,16 @@ module.exports = function (scriptRuntime, scope) {
* @implements Internal.OcrPaddle
*/
const OcrPaddleCtor = function () {
return Object.assign(function () {
const instanceFunc = function __() {
let argArray = Array.from(arguments);
if (arguments.length === 0 || !(arguments[0] instanceof ImageWrapper)) {
return __.apply(this, [ images.captureScreen() ].concat(argArray));
}
/** @type {Internal.Ocr.ModeName} */
argArray[ocr['_forcibleModeArgIndex']] = ocr['_modeName']['paddle'];
return ocr.recognizeText.apply(ocr, argArray);
}, OcrPaddleCtor.prototype);
};
return Object.assign(instanceFunc, OcrPaddleCtor.prototype);
};
OcrPaddleCtor.prototype = {

View File

@@ -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(isFunc(new (function () {
let C = function () {
// Empty constructor
/* Empty body. */
};
util.extend(C, (function () {
let P = function () {
// Empty constructor
/* Empty body. */
};
P.prototype.getName = () => context.packageName;
return P;

View File

@@ -20,7 +20,7 @@ import org.autojs.autojs.engine.RootAutomatorEngine
import org.autojs.autojs.engine.ScriptEngineManager
import org.autojs.autojs.engine.ScriptEngineService
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.rhino.InterruptibleAndroidContextFactory
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.StringUtils
import org.autojs.autojs.util.ViewUtils
import org.autojs.autojs6.BuildConfig
import org.autojs.autojs6.R
import org.mozilla.javascript.ContextFactory
import org.mozilla.javascript.WrappedException
@@ -51,6 +52,12 @@ abstract class AbstractAutoJs protected constructor(protected val application: A
lateinit var runtime: ScriptRuntime
private set
private val scriptEngine
get() = when (BuildConfig.isInrt) {
true -> LoopBasedJavaScriptEngineWithDecryption(context)
else -> LoopBasedJavaScriptEngine(context)
}
val context: Context = application.applicationContext
val appUtils by lazy { createAppUtils(context) }
val globalConsole by lazy { createGlobalConsole() }
@@ -58,19 +65,11 @@ abstract class AbstractAutoJs protected constructor(protected val application: A
val layoutInspector = LayoutInspector(context)
val uiHandler = UiHandler(context)
val infoProvider = ActivityInfoProvider(context)
val scriptEngineManager = ScriptEngineManager(context)
val scriptEngineService: ScriptEngineService = run {
if (org.autojs.autojs6.BuildConfig.isInrt) {
scriptEngineManager.registerEngine(JavaScriptSource.ENGINE) {
XJavaScriptEngine(context).also { engine ->
engine.runtime = createRuntime().also { runtime = it }
}
}
} else {
scriptEngineManager.registerEngine(JavaScriptSource.ENGINE) {
LoopBasedJavaScriptEngine(context).also { engine ->
engine.runtime = createRuntime().also { runtime = it }
}
val scriptEngineManager = ScriptEngineManager(context)
scriptEngineManager.registerEngine(JavaScriptSource.ENGINE) {
scriptEngine.also { engine ->
engine.runtime = createRuntime().also { runtime = it }
}
}
initContextFactory()

View File

@@ -20,8 +20,6 @@ import com.bumptech.glide.request.transition.Transition
import com.flurry.android.FlurryAgent
import com.tencent.bugly.Bugly
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.core.accessibility.AccessibilityTool
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.autojs6.BuildConfig
import org.autojs.autojs6.R
import org.lsposed.hiddenapibypass.HiddenApiBypass
import java.lang.ref.WeakReference
import java.lang.reflect.Method
/**
* Created by Stardust on 2017/1/27.
* Modified by SuperMonster003 as of Aug 23, 2022.
@@ -55,8 +51,6 @@ class App : MultiDexApplication() {
lateinit var devPluginService: DevPluginService
private set
private val localeAppDelegate = LocaleHelperApplicationDelegate()
override fun onCreate() {
super.onCreate()
@@ -79,21 +73,8 @@ class App : MultiDexApplication() {
}
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)
// @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()}")
Log.d("Shizuku", "${App::class.java.simpleName} attachBaseContext | Process=${getProcessNameCompat()}")
}
private fun setUpStaticsTool() {
@@ -129,6 +110,7 @@ class App : MultiDexApplication() {
else -> ViewUtils.MODE.DAY
}
}
else -> ViewUtils.MODE.NULL
}
)
@@ -234,14 +216,11 @@ class App : MultiDexApplication() {
}
override fun onConfigurationChanged(newConfig: Configuration) {
localeAppDelegate.onConfigurationChanged(this)
ViewUtils.onConfigurationChanged(newConfig)
FloatyWindowManger.getCircularMenu()?.savePosition(newConfig)
super.onConfigurationChanged(newConfig)
}
override fun getApplicationContext() = LocaleHelper.onAttach(super.getApplicationContext())
companion object {
private const val BUGLY_APP_ID = "19b3607b53"

View File

@@ -1,5 +1,6 @@
package org.autojs.autojs
import android.annotation.SuppressLint
import android.app.Application
import android.content.BroadcastReceiver
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.settings.AboutActivity
import org.autojs.autojs.ui.settings.PreferencesActivity
import org.autojs.autojs6.BuildConfig
import org.autojs.autojs6.R
import java.util.concurrent.Executors
import org.autojs.autojs.inrt.autojs.AutoJs as AutoJsInrt
/**
* Created by Stardust on 2017/4/2.
@@ -179,6 +182,7 @@ open class AutoJs(private val appContext: Application) : AbstractAutoJs(appConte
companion object {
private var isInitialized = false
@SuppressLint("StaticFieldLeak")
@JvmStatic
lateinit var instance: AutoJs
private set
@@ -187,7 +191,10 @@ open class AutoJs(private val appContext: Application) : AbstractAutoJs(appConte
@JvmStatic
fun initInstance(application: Application) {
if (!isInitialized) {
instance = AutoJs(application)
instance = when (BuildConfig.isInrt) {
true -> AutoJsInrt(application)
else -> AutoJs(application)
}
isInitialized = true
}
}

View File

@@ -1,7 +1,10 @@
package org.autojs.autojs.apkbuilder
import android.content.res.AssetManager
import android.graphics.Bitmap
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.app.GlobalAppContext
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 mKey: String? = null
private val mAssetManager: AssetManager by lazy { GlobalAppContext.get().assets }
private val mManifestFile
get() = File(mWorkspacePath, "AndroidManifest.xml")
private val mResourcesArscFile
get() = File(mWorkspacePath, "resources.arsc")
private lateinit var mAppConfig: AppConfig
init {
@@ -43,9 +51,10 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
@Throws(IOException::class)
fun prepare() = also {
mProgressCallback?.run { GlobalAppContext.post { onPrepare(this@ApkBuilder) } }
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onPrepare(this) } }
File(mWorkspacePath).mkdirs()
mApkPackager.unzip()
copyAssetsRecursively("", File(mWorkspacePath, "assets"))
}
@Throws(IOException::class)
@@ -147,13 +156,24 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
@Throws(IOException::class)
fun build() = also {
mProgressCallback?.run { GlobalAppContext.post { onBuild(this@ApkBuilder) } }
mAppConfig.icon?.run {
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onBuild(this) } }
mAppConfig.icon?.let { callable ->
try {
call()?.compress(
Bitmap.CompressFormat.PNG, 100,
FileOutputStream(File(mWorkspacePath, "res/mipmap/ic_launcher.png"))
)
val tableBlock = TableBlock.load(mResourcesArscFile)
val packageName = "${GlobalAppContext.get().packageName}.inrt"
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) {
throw RuntimeException(e)
}
@@ -162,16 +182,36 @@ open class ApkBuilder(apkInputStream: InputStream?, private val mOutApkFile: Fil
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)
fun sign() = also {
mProgressCallback?.run { GlobalAppContext.post { onSign(this@ApkBuilder) } }
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onSign(this) } }
val fos = FileOutputStream(mOutApkFile)
TinySign.sign(File(mWorkspacePath), fos)
fos.close()
}
fun cleanWorkspace() = also {
mProgressCallback?.run { GlobalAppContext.post { onClean(this@ApkBuilder) } }
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onClean(this) } }
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"
}
}

View File

@@ -19,11 +19,12 @@ import java.io.InputStream;
public class ApkBuilderPluginHelper {
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;
public static final String TEMPLATE_APK_PATH = "template.apk";
public static boolean isPluginAvailable(Context context) {
// return DeveloperUtils.checkSignature(context, PLUGIN_PACKAGE_NAME);
// return DeveloperUtils.checkSignature(context, PLUGIN_PACKAGE_NAME);
try {
context.getPackageManager().getPackageInfo(PLUGIN_PACKAGE_NAME, 0);
} catch (PackageManager.NameNotFoundException e) {
@@ -55,5 +56,3 @@ public class ApkBuilderPluginHelper {
}
}
}

View File

@@ -22,7 +22,6 @@ public class ManifestEditor {
private String mPackageName;
private byte[] mManifestData;
public ManifestEditor(InputStream manifestInputStream) {
mManifestInputStream = manifestInputStream;
}
@@ -54,7 +53,7 @@ public class ManifestEditor {
mManifestData = writer.toByteArray();
return this;
}
private static byte[] readFully(InputStream is) throws IOException {
byte[] bytes = new byte[is.available()];
is.read(bytes);
@@ -90,7 +89,6 @@ public class ManifestEditor {
}
}
private class MutableAxmlWriter extends AxmlWriter {
private class MutableNodeImpl extends AxmlWriter.NodeImpl {

View File

@@ -9,7 +9,7 @@ import android.provider.Settings.Secure
import android.text.TextUtils
import android.util.Log
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.SettingsUtils
import org.autojs.autojs.util.ViewUtils
@@ -102,10 +102,10 @@ class AccessibilityTool(val context: Context) {
val services = getEnabledWithRoot(true)
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 resultState = execCommand(cmdState, true)
val resultState = ProcessShell.execCommand(cmdState, true)
TextUtils.isEmpty(resultServices.error) && TextUtils.isEmpty(resultState.error)
} catch (e: Exception) {
@@ -136,10 +136,10 @@ class AccessibilityTool(val context: Context) {
val services = getEnabledWithRoot(false)
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 resultState = execCommand(cmdState, true)
val resultState = ProcessShell.execCommand(cmdState, true)
TextUtils.isEmpty(resultServices.error) && TextUtils.isEmpty(resultState.error)
} catch (e: Exception) {
@@ -159,7 +159,7 @@ class AccessibilityTool(val context: Context) {
false -> detachAutoJs(getEnabledWithRoot())
else -> {
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", "") ?: ""
}
}

View File

@@ -34,18 +34,19 @@ import android.accessibilityservice.AccessibilityService as AndroidAccessibility
*/
class SimpleActionAutomator(private val accessibilityBridge: AccessibilityBridge, private val scriptRuntime: ScriptRuntime) {
private val globalActionAutomatorRaw by lazy {
GlobalActionAutomator(Handler(scriptRuntime.loopers.servantLooper)) {
private val globalActionAutomatorRaw
get() = GlobalActionAutomator(Handler(scriptRuntime.loopers.servantLooper)) {
ensureService()
accessibilityBridge.service!!
}
private val globalActionAutomator by lazy {
globalActionAutomatorRaw
}
private val globalActionAutomator
get() = globalActionAutomatorRaw
private val globalActionAutomatorForGesture
get() = globalActionAutomatorRaw.apply { setScreenMetrics(mScreenMetrics) }
private val globalActionAutomatorForGesture by lazy {
globalActionAutomatorRaw.apply { setScreenMetrics(mScreenMetrics) }
}
private val isRunningPackageSelf
get() = DeveloperUtils.isSelfPackage(accessibilityBridge.infoProvider.latestPackage)

View File

@@ -332,6 +332,7 @@ open class ConsoleImpl(val uiHandler: UiHandler) : AbstractConsole() {
// SecurityException: https://github.com/hyb1996-guest/AutoJsIssueReport/issues/4781
} catch (e: Exception) {
e.printStackTrace()
ScriptRuntime.popException(e.message)
uiHandler.toast(R.string.error_no_display_over_other_apps_permission)
}
}

View File

@@ -25,11 +25,13 @@ class Loopers(val runtime: ScriptRuntime) {
@Volatile
private var mServantLooper: Looper? = null
@Suppress("DEPRECATION")
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 condition = lock.newCondition()
@@ -55,7 +57,7 @@ class Loopers(val runtime: ScriptRuntime) {
prepare()
myLooper = Looper.myLooper()!!
timer = Timer(runtime, myLooper)
waitWhenIdle = myLooper == Looper.getMainLooper()
mWaitWhenIdle = myLooper == Looper.getMainLooper()
}
@Deprecated("Deprecated in Java", ReplaceWith("AsyncTask"))
@@ -111,10 +113,9 @@ class Loopers(val runtime: ScriptRuntime) {
return when {
Thread.currentThread().isInterrupted -> true
timer.hasPendingCallbacks() -> false
// 检查是否有运行中的线程
checkTask() -> false
waitWhenIdle -> false
(Context.getCurrentContext() as AutoJsContext).hasPendingContinuation() -> false
checkTask() /* 检查是否有运行中的线程. */ -> false
(Context.getCurrentContext() as? AutoJsContext)?.hasPendingContinuation() == true -> false
mWaitWhenIdle -> false
else -> true
}
}
@@ -133,7 +134,7 @@ class Loopers(val runtime: ScriptRuntime) {
@Deprecated("Deprecated in Java", ReplaceWith("AsyncTask"))
fun waitWhenIdle(b: Boolean) {
waitWhenIdle = b
mWaitWhenIdle = b
}
fun recycle() {
@@ -160,7 +161,7 @@ class Loopers(val runtime: ScriptRuntime) {
Looper.myQueue().addIdleHandler(MessageQueue.IdleHandler {
if (this == runtime.loopers) {
Log.d(LOG_TAG, "main looper queueIdle")
if (shouldQuitLooper() && mMainLooperQuitHandler?.shouldQuit() == true) {
if (shouldQuitLooper() && mMainLooperQuitHandler?.shouldQuit() != false) {
Log.d(LOG_TAG, "main looper quit")
Looper.myLooper()?.quitSafely()
}

View File

@@ -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();
}
}
}

View File

@@ -5,6 +5,7 @@ import android.util.Log
import androidx.preference.PreferenceManager
import org.autojs.autojs.core.inputevent.InputDevices
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.exception.ScriptException
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
@@ -50,7 +51,7 @@ class RootAutomatorEngine @JvmOverloads constructor(private val mContext: Contex
"exit",
)
try {
Runtime.getRuntime().exec("su").also {
Runtime.getRuntime().exec(AbstractShell.COMMAND_SU).also {
mProcess = it
executeCommands(it, commands)
mPid = readPid(it)

View File

@@ -86,8 +86,10 @@ data class ExecutionConfig(
companion object CREATOR : Parcelable.Creator<ExecutionConfig> {
const val tag = "execution.config"
@JvmStatic
val tag = "execution.config"
fun getTag() = tag
@JvmStatic
val default: ExecutionConfig

View File

@@ -30,7 +30,7 @@ public class RunnableScriptExecution extends ScriptExecution.AbstractScriptExecu
public Object execute() {
mScriptEngine = mScriptEngineManager.createEngineOfSourceOrThrow(getSource(), getId());
mScriptEngine.setTag(ExecutionConfig.getTag(), getConfig());
mScriptEngine.setTag(ExecutionConfig.tag, getConfig());
return execute(mScriptEngine);
}

View File

@@ -144,6 +144,7 @@ public class ScriptExecuteActivity extends AppCompatActivity {
mScriptEngine.destroy();
}
mScriptExecution = null;
mRuntime.loopers.waitWhenIdle(false);
}
@Override
@@ -243,7 +244,7 @@ public class ScriptExecuteActivity extends AppCompatActivity {
mScriptEngine.forceStop();
}
mScriptEngine = mScriptEngineManager.createEngineOfSourceOrThrow(getSource(), getId());
mScriptEngine.setTag(ExecutionConfig.getTag(), getConfig());
mScriptEngine.setTag(ExecutionConfig.tag, getConfig());
return mScriptEngine;
}

View File

@@ -3,7 +3,7 @@ package org.autojs.autojs.external.foreground
import android.app.Service
import android.content.Intent
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.autojs6.R
@@ -23,7 +23,7 @@ class MainActivityForegroundService : Service() {
ForegroundServiceUtils.startForeground(
ForegroundServiceCreator.Builder(this)
.setClassName(sClassName)
.setIntent(getIntent(this))
.setIntent(MainActivity.getIntent(this))
.setNotificationId(NOTIFICATION_ID)
.setServiceName(R.string.foreground_notification_channel_name)
.setServiceDescription(R.string.foreground_notification_channel_name)

View File

@@ -18,7 +18,6 @@ import java.util.List;
@RequiresApi(api = Build.VERSION_CODES.N_MR1)
public class ShortcutManager {
private static ShortcutManager sInstance;
private final Context mContext;
private final android.content.pm.ShortcutManager mShortcutManager;

View File

@@ -15,6 +15,7 @@ import org.autojs.autojs.model.explorer.ExplorerFileProvider;
import org.autojs.autojs.model.script.Scripts;
import org.autojs.autojs.ui.BaseActivity;
import org.autojs.autojs.ui.explorer.ExplorerView;
import org.autojs.autojs.util.WorkingDirectoryUtils;
import org.autojs.autojs6.R;
import org.autojs.autojs6.databinding.ActivityScriptWidgetSettingsBinding;
@@ -42,9 +43,10 @@ public class ScriptWidgetSettingsActivity extends BaseActivity {
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.setExplorer(mExplorer, ExplorerDirPage.createRoot(Environment.getExternalStorageDirectory()));
String workingDirPath = WorkingDirectoryUtils.getPath();
explorerView.setExplorer(mExplorer, ExplorerDirPage.createRoot(workingDirPath));
explorerView.setOnItemClickListener((view, file) -> {
mSelectedScriptFilePath = file.getPath();
finish();

View File

@@ -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)
}
})
}
}
}
}

View File

@@ -8,7 +8,7 @@ import android.view.Menu
import android.view.MenuItem
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.core.console.ConsoleImpl
import org.autojs.autojs.core.console.ConsoleView

View File

@@ -12,7 +12,7 @@ import android.widget.Toast
import androidx.annotation.NonNull
import androidx.annotation.Nullable
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.autojs6.R

View File

@@ -1,98 +1,28 @@
package org.autojs.autojs.inrt.autojs
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.inrt.LogActivity
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.exception.ScriptException
import org.autojs.autojs.runtime.exception.ScriptInterruptedException
import org.autojs.autojs.script.JavaScriptSource
import org.autojs.autojs6.R
import org.autojs.autojs.AutoJs as AutoJsApp
/**
* 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 {
scriptEngineService.registerGlobalScriptExecutionListener(ScriptExecutionGlobalListener())
scriptEngineManager.registerEngine(JavaScriptSource.ENGINE) {
val engine = XJavaScriptEngine(application)
engine.runtime = createRuntime()
engine
}
}
override fun createAppUtils(context: Context) = AppUtils(context, context.packageName + ".fileprovider")
override fun createAppUtils(context: Context): AppUtils {
return AppUtils(context, context.packageName + ".fileprovider")
}
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()
override fun createRuntime() = super.createRuntime().apply {
putProperty("class.settings", SettingsActivity::class.java)
putProperty("class.console", LogActivity::class.java)
}
companion object {
@SuppressLint("StaticFieldLeak")
@JvmStatic
lateinit var instance: AutoJs
private set
val instance
get() = AutoJsApp.instance
@Synchronized
fun initInstance(application: Application) {
instance = AutoJs(application)
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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"
}
}

View File

@@ -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
}
}

View File

@@ -8,7 +8,7 @@ import android.os.Looper
import android.text.TextUtils
import org.autojs.autojs.inrt.LogActivity
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.execution.ExecutionConfig
import org.autojs.autojs.execution.ScriptExecution

View File

@@ -36,7 +36,7 @@ object Scripts {
const val EXTRA_EXCEPTION_LINE_NUMBER = "lineNumber"
const val EXTRA_EXCEPTION_COLUMN_NUMBER = "columnNumber"
@JvmStatic
@JvmField
val FILE_FILTER = FileFilter { file ->
file.isDirectory ||
file.name.endsWith(FileUtils.TYPE.JAVASCRIPT.extension) ||

View File

@@ -34,7 +34,8 @@ class DisplayOverOtherAppsPermission(override val context: Context) : Permission
fun config() {
try {
SettingsCompat.manageDrawOverlays(context)
} catch (ex: Exception) {
} catch (e: Exception) {
e.printStackTrace()
FloatingWindowPermissionUtil.goToAppDetailSettings(context, context.packageName)
}
}

View File

@@ -75,7 +75,7 @@ class ManageAllFilesPermission(override val context: Context) : PermissionItemHe
.onNegative { dialog, _ -> dialog.dismiss() }
.onPositive { dialog, _ ->
request()
MainActivity.setShouldRecreateMainActivity(true)
MainActivity.shouldRecreateMainActivity = true
dialog.dismiss()
}
.cancelable(false)

View File

@@ -9,10 +9,9 @@ import java.util.Locale
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(Locale.forLanguageTag("zh"), R.string.key_app_language_zh, R.string.entry_app_language_zh),
ZH_HK(Locale.forLanguageTag("zh-hk"), R.string.key_app_language_zh_hk, R.string.entry_app_language_zh_hk),
ZH_TW(Locale.forLanguageTag("zh-tw"), R.string.key_app_language_zh_tw, R.string.entry_app_language_zh_tw),
ZH_HANS(Locale.forLanguageTag("zh-Hans"), R.string.key_app_language_zh_hans, R.string.entry_app_language_zh_hans),
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_HANT_TW(Locale.forLanguageTag("zh-Hant-TW"), R.string.key_app_language_zh_hant_tw, R.string.entry_app_language_zh_hant_tw),
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),
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),
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),
AUTO(LocaleUtils.getSystemLocale(), R.string.key_app_language_auto, R.string.entry_app_language_auto),
;
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 {
@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)
} ?: 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())
}
}

View File

@@ -95,13 +95,6 @@ object Pref {
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
get() = getTimestamp(R.string.key_last_updates_checked)

View File

@@ -24,7 +24,7 @@ public class ProjectConfig {
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")
private String mName;
@@ -64,7 +64,7 @@ public class ProjectConfig {
if (json == null) {
return null;
}
ProjectConfig config = GSON.fromJson(json, ProjectConfig.class);
ProjectConfig config = sGson.fromJson(json, ProjectConfig.class);
if (!isValid(config)) {
return null;
}
@@ -209,7 +209,7 @@ public class ProjectConfig {
}
public String toJson() {
return GSON.toJson(this);
return sGson.toJson(this);
}
public String getIcon() {

View File

@@ -1161,7 +1161,7 @@ public class TokenStream {
}
// is it a string?
if (c == '"' || c == '\'') {
if (c == '"' || c == '\'' || c == '`') {
// We attempt to accumulate a string the fast way, by
// building it directly out of the reader. But if there
// are any escaped characters in the string, we revert to
@@ -1646,7 +1646,7 @@ public class TokenStream {
this.string = getStringFromBuffer();
return Token.XML;
}
case '\'', '"' -> {
case '\'', '"', '`' -> {
addToString(c);
if (!readQuotedString(c))
return Token.ERROR;

View File

@@ -7,7 +7,6 @@ import android.os.Build;
import android.os.Looper;
import android.util.Log;
import org.autojs.autojs.core.permission.Permissions;
import org.autojs.autojs.AutoJs;
import org.autojs.autojs.annotation.ScriptInterface;
import org.autojs.autojs.annotation.ScriptVariable;
@@ -22,6 +21,7 @@ import org.autojs.autojs.core.image.Colors;
import org.autojs.autojs.core.image.ImageWrapper;
import org.autojs.autojs.core.image.capture.ScreenCaptureRequester;
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.engine.ScriptEngineService;
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.runtime.api.AbstractShell;
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.Dialogs;
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.Timers;
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.ScriptException;
import org.autojs.autojs.runtime.exception.ScriptInterruptedException;
@@ -85,6 +87,8 @@ public class ScriptRuntime {
private static final String TAG = "ScriptRuntime";
public boolean isExiting;
public static void popException(String message) {
try {
throw new Exception(message);
@@ -244,10 +248,13 @@ public class ScriptRuntime {
private final Images images;
@ScriptVariable
public final ScriptToast toast;
public final OcrMLKit ocrMLKit;
@ScriptVariable
public final OcrMLKit ocrMLKit;
public final Barcode barcode;
@ScriptVariable
public final WrappedShizuku shizuku;
@ScriptVariable
public final OcrPaddle ocrPaddle;
@@ -288,7 +295,8 @@ public class ScriptRuntime {
ocrMLKit = new OcrMLKit();
ocrPaddle = new OcrPaddle();
toast = new ScriptToast(context, this);
barcode = new Barcode();
shizuku = WrappedShizuku.INSTANCE;
}
public void init() {
@@ -531,6 +539,7 @@ public class ScriptRuntime {
@SuppressWarnings("ResultOfMethodCallIgnored")
public void onExit() {
Log.d(TAG, "on exit");
this.isExiting = true;
ignoresException(() -> {
if (console.getConfigurator().isExitOnClose()) {
@@ -555,6 +564,7 @@ public class ScriptRuntime {
ignoresException(floaty::closeAll);
ignoresException(() -> events.emit("exit"), "exception on exit: %s");
ignoresException(() -> ScriptToast.clear(this));
ignoresException(threads::shutDownAll);
ignoresException(events::recycle);

View File

@@ -5,6 +5,10 @@ import android.text.TextUtils;
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;
/**
@@ -13,26 +17,42 @@ import org.autojs.autojs.engine.RootAutomatorEngine;
public abstract class AbstractShell {
public static class Result {
private static final Gson sGson = new GsonBuilder().setPrettyPrinting().create();
@SerializedName("code")
public int code = -1;
public String error;
public String result;
@SerializedName("result")
public String result = "";
@SerializedName("error")
public String error = "";
@NonNull
@Override
public String toString() {
return "ShellResult{" +
"code=" + code +
", error='" + error + '\'' +
", result='" + result + '\'' +
", error=\"" + error + '"' +
", 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";
protected static final String COMMAND_SH = "sh";
protected static final String COMMAND_EXIT = "exit\n";
protected static final String COMMAND_LINE_END = "\n";
public static final String COMMAND_SU = "su";
public static final String COMMAND_SH = "sh";
public static final String COMMAND_LINE_END = "\n";
public static final String COMMAND_EXIT = "exit" + COMMAND_LINE_END;
private int mTouchDevice = -1;
private ScreenMetrics mScreenMetrics;
@@ -91,7 +111,6 @@ public abstract class AbstractShell {
mScreenMetrics = screenMetrics;
}
public void Touch(int x, int y) {
TouchX(x);
TouchY(y);

View File

@@ -184,11 +184,9 @@ class AppUtils {
companion object {
@JvmStatic
val activityShortFormPrefix = "class."
const val activityShortFormPrefix = "class."
@JvmStatic
val broadcastShortFormPrefix = "broadcast."
const val broadcastShortFormPrefix = "broadcast."
enum class ActivityShortForm(val shortName: String) {
@@ -237,23 +235,12 @@ class AppUtils {
@JvmStatic
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 {
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
it.getInstalledApplications(ApplicationInfoFlags.of(GET_META_DATA.toLong()))
}
else -> {
@Suppress("DEPRECATION")
it.getInstalledApplications(GET_META_DATA)
}
}

View File

@@ -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"
}
}

View File

@@ -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
}

View File

@@ -18,6 +18,7 @@ import android.view.Gravity;
import androidx.annotation.NonNull;
import org.autojs.autojs.annotation.ScriptInterface;
import org.autojs.autojs.annotation.ScriptVariable;
import org.autojs.autojs.concurrent.VolatileDispose;
import org.autojs.autojs.core.image.ColorFinder;
@@ -371,6 +372,7 @@ public class Images {
return new Mat(mat, roi);
}
@ScriptInterface
public void initOpenCvIfNeeded() {
if (mOpenCvInitialized || OpenCVHelper.isInitialized()) {
return;
@@ -388,7 +390,6 @@ public class Images {
});
result.blockedGet();
}
}
private void initSuccessfully() {
@@ -396,4 +397,8 @@ public class Images {
Log.i(TAG, "opencv: initialized");
}
public ScreenCapturer getScreenCapturer() {
return mScreenCapturer;
}
}

View File

@@ -69,6 +69,7 @@ class OcrMLKit {
}
fun recognizeText(image: ImageWrapper?): Array<String> {
image?.takeUnless { image.isRecycled } ?: return emptyArray()
initIfNeeded()
val words = detect(image).sorted()
val output = mutableListOf<String>()

View File

@@ -55,8 +55,8 @@ public class ProcessShell extends AbstractShell {
public void exec(String command) {
try {
mCommandOutputStream.writeBytes(command);
if (!command.endsWith(AbstractShell.COMMAND_LINE_END)) {
mCommandOutputStream.writeBytes(AbstractShell.COMMAND_LINE_END);
if (!command.endsWith(COMMAND_LINE_END)) {
mCommandOutputStream.writeBytes(COMMAND_LINE_END);
}
mCommandOutputStream.flush();
} catch (IOException e) {
@@ -92,7 +92,7 @@ public class ProcessShell extends AbstractShell {
@Override
public void exitAndWaitFor() {
exec(AbstractShell.COMMAND_EXIT);
exec(COMMAND_EXIT);
waitFor();
exit();
}
@@ -118,7 +118,7 @@ public class ProcessShell extends AbstractShell {
try {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
sb.append(line).append(COMMAND_LINE_END);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
@@ -151,7 +151,7 @@ public class ProcessShell extends AbstractShell {
}
public static Result exec(String command, boolean isRoot) {
String[] commands = command.split("\n");
String[] commands = command.split(COMMAND_LINE_END);
return exec(commands, isRoot);
}
@@ -162,7 +162,7 @@ public class ProcessShell extends AbstractShell {
for (String command : commands) {
shell.exec(command);
}
shell.exec(AbstractShell.COMMAND_EXIT);
shell.exec(COMMAND_EXIT);
Result result = new Result();
result.code = shell.waitFor();
shell.readAll();
@@ -178,22 +178,35 @@ public class ProcessShell extends AbstractShell {
}
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();
if (commands == null || commands.length == 0)
if (commands == null || commands.length == 0) {
throw new IllegalArgumentException(str(R.string.error_empty_shell_command));
Process process = null;
}
DataOutputStream os = null;
try {
process = Runtime.getRuntime().exec(isRoot ? AbstractShell.COMMAND_SU : AbstractShell.COMMAND_SH);
os = new DataOutputStream(process.getOutputStream());
for (String command : commands) {
if (command != null) {
os.write(command.getBytes());
os.writeBytes(AbstractShell.COMMAND_LINE_END);
os.writeBytes(COMMAND_LINE_END);
os.flush();
}
}
os.writeBytes(AbstractShell.COMMAND_EXIT);
os.writeBytes(COMMAND_EXIT);
os.flush();
Log.d(TAG, "pid = " + ProcessUtils.getProcessPid(process));
commandResult.code = process.waitFor();
@@ -232,18 +245,26 @@ public class ProcessShell extends AbstractShell {
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 {
String line;
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = reader.readLine()) != null) {
builder.append(line).append('\n');
builder.append(line).append(COMMAND_LINE_END);
}
return builder.toString();
}
public static Result execCommand(String command, boolean withRoot) {
String[] commands = command.split("\n");
String[] commands = command.split(COMMAND_LINE_END);
return execCommand(commands, withRoot);
}

View File

@@ -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());
}
}

View File

@@ -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)
}
}
}
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -5,6 +5,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.autojs.autojs.pref.Language;
import org.autojs.autojs.rhino.TokenStream;
import org.autojs.autojs.tool.MapBuilder;
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 int EXECUTION_MODE_NORMAL = 0;
public static final int EXECUTION_MODE_UI = 0x00000001;
public static final int EXECUTION_MODE_AUTO = 0x00000002;
public static final int EXECUTION_MODE_RAW = -0x1;
public static final int EXECUTION_MODE_NORMAL = 0x0;
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 Map<String, Integer> EXECUTION_MODES = new MapBuilder<String, Integer>()
.put("ui", EXECUTION_MODE_UI)
.put("auto", EXECUTION_MODE_AUTO)
.put("cheerio", EXECUTION_MODE_MODULE_CHEERIO)
.build();
private static final int PARSING_MAX_TOKEN = 300;
private int mExecutionMode = -1;
private int mExecutionMode = EXECUTION_MODE_RAW;
public JavaScriptSource(String name) {
super(name);
@@ -62,7 +67,7 @@ public abstract class JavaScriptSource extends ScriptSource {
}
public int getExecutionMode() {
if (mExecutionMode == -1) {
if (mExecutionMode == EXECUTION_MODE_RAW) {
mExecutionMode = parseExecutionMode();
}
return mExecutionMode;
@@ -79,28 +84,27 @@ public abstract class JavaScriptSource extends ScriptSource {
if (token == Token.EOL || token == Token.COMMENT) {
continue;
}
if (token == Token.STRING && ts.getTokenLength() > 2) {
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(" "));
if (token != Token.STRING || ts.getTokenLength() < 3) {
break;
}
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) {
e.printStackTrace();
return EXECUTION_MODE_NORMAL;
}
return EXECUTION_MODE_NORMAL;
}
private int parseExecutionMode(String[] modeStrings) {
int mode = 0;
int mode = EXECUTION_MODE_NORMAL;
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) {
mode |= i;
}

View File

@@ -14,7 +14,7 @@ import java.util.Vector
*/
object ThemeColorManager {
@JvmStatic
@JvmField
var defaultThemeColor = ThemeColor(ColorUtils.fromResources(R.color.theme_color_default))
@JvmStatic

View File

@@ -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));
}
}

View File

@@ -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)
}
}

View File

@@ -3,15 +3,13 @@
package org.autojs.autojs.ui
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import com.zeugmasolutions.localehelper.LocaleHelper
import com.zeugmasolutions.localehelper.LocaleHelperActivityDelegate
import com.zeugmasolutions.localehelper.LocaleHelperActivityDelegateImpl
import org.autojs.autojs.pref.Language
import org.autojs.autojs.theme.ThemeColorManager
import org.autojs.autojs.util.LocaleUtils
import org.autojs.autojs.util.ViewUtils
/**
@@ -20,8 +18,6 @@ import org.autojs.autojs.util.ViewUtils
*/
abstract class BaseActivity : AppCompatActivity() {
private val localeDelegate: LocaleHelperActivityDelegate = LocaleHelperActivityDelegateImpl()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -38,15 +34,16 @@ abstract class BaseActivity : AppCompatActivity() {
}
ViewUtils.makeBarsAdaptToNightMode(this)
localeDelegate.onCreate(this)
setLocaleForAutoLanguageIfNeeded(this)
}
override fun getDelegate(): AppCompatDelegate {
return localeDelegate.getAppCompatDelegate(super.getDelegate())
}
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(localeDelegate.attachBaseContext(newBase))
private fun setLocaleForAutoLanguageIfNeeded(context: Context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
if (Language.getPrefLanguage().isAuto()) {
LocaleUtils.setFollowSystem(context)
}
}
}
override fun onStart() {
@@ -58,21 +55,9 @@ abstract class BaseActivity : AppCompatActivity() {
override fun 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(title: String?) = ViewUtils.setToolbarAsBack(this, title)

View File

@@ -183,6 +183,8 @@ public class EditorMenu {
dialog.dismiss();
})
.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)) {
mEditor.insert(1, item.getImportText() + ";\n");
} else {

View File

@@ -89,6 +89,10 @@ import java.io.File
// FIXME by SuperMonster003 on May 13, 2023.
// ! Failed many times to migrate this view with annotation to view binding.
// ! And this is the last view with annotation in the whole project so far.
// FIXME by SuperMonster003 on Oct 18, 2023.
// ! Gradle property "android.nonFinalResIds"
// ! can't be enabled (by default since Android Gradle Plugin version 8.0),
// ! as final resource ID used in current class.
open class EditorView : FrameLayout, OnHintClickListener, ClickCallback, ToolbarFragment.OnMenuItemClickListener {
@JvmField

View File

@@ -85,7 +85,7 @@ public class FileChooserDialogBuilder extends MaterialDialog.Builder {
}
public FileChooserDialogBuilder justScriptFile() {
mFileFilter = Scripts.getFILE_FILTER();
mFileFilter = Scripts.FILE_FILTER;
return this;
}

View File

@@ -12,14 +12,12 @@ import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.WindowManager
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment
import androidx.viewpager.widget.ViewPager.SimpleOnPageChangeListener
import com.google.android.material.floatingactionbutton.FloatingActionButton
import org.autojs.autojs.App
import org.autojs.autojs.AutoJs
import org.autojs.autojs.app.FragmentPagerAdapterBuilder
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.PostNotificationPermission
import org.autojs.autojs.pref.Pref
import org.autojs.autojs.runtime.api.AppUtils.Companion.hideLauncherIcon
import org.autojs.autojs.runtime.api.AppUtils.Companion.showLauncherIcon
import org.autojs.autojs.runtime.api.WrappedShizuku
import org.autojs.autojs.theme.ThemeColorManager.addViewBackground
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.enhancedfloaty.FloatyService
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.SearchViewItem
import org.autojs.autojs.util.ForegroundServiceUtils
import org.autojs.autojs.util.StringUtils
import org.autojs.autojs.util.UpdateUtils.autoCheckForUpdatesIfNeededWithSnackbar
import org.autojs.autojs.util.UpdateUtils
import org.autojs.autojs.util.ViewUtils
import org.autojs.autojs.util.WorkingDirectoryUtils
import org.autojs.autojs6.R
import org.autojs.autojs6.databinding.ActivityMainBinding
import org.greenrobot.eventbus.EventBus
import rikka.shizuku.Shizuku
/**
* 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?) {
super.onCreate(savedInstanceState)
@@ -150,34 +105,30 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
@Suppress("DEPRECATION")
ViewUtils.appendSystemUiVisibility(this, View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
ViewUtils.registerOnSharedPreferenceChangeListener(this)
Pref.registerOnSharedPreferenceChangeListener { _, key ->
if (key == StringUtils.key(R.string.key_keep_screen_on_when_in_foreground)) {
configKeepScreenOnWhenInForeground()
}
}
WrappedShizuku.onCreate()
ExplorerView.clearViewStates()
setUpToolbar()
setUpTabViewPager()
registerBackPressHandlers()
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() {
recreateIfNeeded()
autoCheckForUpdatesIfNeededWithSnackbar(this, android.R.id.content)
UpdateUtils.autoCheckForUpdatesIfNeededWithSnackbar(this)
super.onPostResume()
}
override fun onStart() {
super.onStart()
WrappedShizuku.checkShizukuPermission()
}
private fun recreateIfNeeded() {
if (sShouldRecreateMainActivity) {
setShouldRecreateMainActivity(false)
if (shouldRecreateMainActivity) {
shouldRecreateMainActivity = false
recreate()
Explorers.workspace().refreshAll()
}
@@ -290,7 +241,7 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
override fun getOnActivityResultDelegateMediator() = mActivityResultMediator
@Suppress("OVERRIDE_DEPRECATION")
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
override fun onBackPressed() {
val fragment = mPagerAdapter.getStoredFragment(mViewPager.currentItem)
if (fragment is BackPressedHandler) {
@@ -384,52 +335,22 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
override fun onDestroy() {
super.onDestroy()
EventBus.getDefault().unregister(this)
Shizuku.removeBinderReceivedListener(this.shizukuBinderReceivedListener)
Shizuku.removeBinderDeadListener(shizukuBinderDeadListener)
Shizuku.removeRequestPermissionResultListener(shizukuRequestPermissionResultListener)
WrappedShizuku.onDestroy()
}
override fun onResume() {
super.onResume()
configKeepScreenOnWhenInForeground()
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)
}
}
ViewUtils.configKeepScreenOnWhenInForeground(this)
}
companion object {
private val TAG = MainActivity::class.java.simpleName
private var sShouldRecreateMainActivity = false
fun setShouldRecreateMainActivity(b: Boolean) {
sShouldRecreateMainActivity = b
}
var shouldRecreateMainActivity = false
@JvmStatic
fun launch(context: Context) {
context.startActivity(getIntent(context).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
fun launch(context: Context) = context.startActivity(getIntent(context).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
@JvmStatic
fun getIntent(context: Context?) = Intent(context, MainActivity::class.java)

View File

@@ -1,5 +1,7 @@
package org.autojs.autojs.ui.project;
import static org.autojs.autojs.apkbuilder.ApkBuilderPluginHelper.TEMPLATE_APK_PATH;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
@@ -11,6 +13,7 @@ import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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.external.fileprovider.AppFileProvider;
import org.autojs.autojs.model.script.ScriptFile;
import org.autojs.autojs.pref.Language;
import org.autojs.autojs.project.ProjectConfig;
import org.autojs.autojs.ui.BaseActivity;
import org.autojs.autojs.ui.filechooser.FileChooserDialogBuilder;
@@ -107,11 +111,15 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
private boolean checkApkTemplateInAssets() {
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;
}
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage());
String msg = e.getMessage();
if (msg != null) {
Log.e(LOG_TAG, msg);
}
}
return false;
}
@@ -149,10 +157,19 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
}
mOutputPath.setText(dir);
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);
}
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
protected void onNewIntent(Intent 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 {
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())
.setProgressCallback(BuildActivity.this)
.prepare()
@@ -319,24 +338,23 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
}
@Override
public void onPrepare(ApkBuilder builder) {
public void onPrepare(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_prepare);
}
@Override
public void onBuild(ApkBuilder builder) {
public void onBuild(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_build);
}
@Override
public void onSign(ApkBuilder builder) {
public void onSign(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_package);
}
@Override
public void onClean(ApkBuilder builder) {
public void onClean(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_clean);
}

View File

@@ -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
}
}
}

View File

@@ -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()
}
}

View File

@@ -141,7 +141,7 @@ class WorkingDirectoryPreference : MaterialPreference {
transfer(mPrefFullPath, inputPath)
}
WorkingDirectoryUtils.path = inputPath
MainActivity.setShouldRecreateMainActivity(true)
MainActivity.shouldRecreateMainActivity = true
}
dialog.dismiss()
onFinish()

View File

@@ -106,7 +106,7 @@ public class ShortcutCreateActivity extends AppCompatActivity {
private void createShortcutByShortcutManager() {
Icon icon;
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 {
Bitmap bitmap = BitmapUtils.drawableToBitmap(mIcon.getDrawable());
icon = Icon.createWithBitmap(bitmap);
@@ -125,7 +125,6 @@ public class ShortcutCreateActivity extends AppCompatActivity {
}
@SuppressWarnings("ResultOfMethodCallIgnored")
@SuppressLint({"CheckResult", "MissingSuperCall"})
@Override

View File

@@ -23,15 +23,16 @@ import android.webkit.WebView;
import androidx.core.view.NestedScrollingChild;
import androidx.core.view.NestedScrollingChildHelper;
import androidx.core.view.ViewCompat;
import org.autojs.autojs.ui.pager.ViewPager;
public class NestedWebView extends WebView implements NestedScrollingChild {
private int mLastY;
private final int[] mScrollOffset = new int[2];
private final int[] mScrollConsumed = new int[2];
private int mNestedOffsetY;
// private int mLastY;
// private final int[] mScrollOffset = new int[2];
// private final int[] mScrollConsumed = new int[2];
// private int mNestedOffsetY;
private final NestedScrollingChildHelper mChildHelper;
public NestedWebView(Context context) {

View File

@@ -89,7 +89,7 @@ enum class App(private val appNameResId: Int, val packageName: String, alias: St
}
@ScriptInterface
fun getAppNameZh() = getAppName(Language.ZH)
fun getAppNameZh() = getAppName(Language.ZH_HANS)
@ScriptInterface
fun getAppNameEn() = getAppName(Language.EN)

View File

@@ -1,51 +1,61 @@
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.Resources
import com.zeugmasolutions.localehelper.LocaleHelper
import com.zeugmasolutions.localehelper.LocaleHelperActivityDelegate
import com.zeugmasolutions.localehelper.LocaleHelperActivityDelegateImpl
import android.os.Build
import android.os.LocaleList
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.pref.Language
import org.autojs.autojs6.R
import java.util.Locale
object LocaleUtils {
private val localeDelegate: LocaleHelperActivityDelegate = LocaleHelperActivityDelegateImpl()
private val localeHelper = LocaleHelper
private val globalAppContext = GlobalAppContext.get()
@JvmStatic
fun setLocale(activity: Activity, locale: Locale?) {
locale?.let { localeDelegate.setLocale(activity, it) }
?: throw IllegalArgumentException(globalAppContext.getString(R.string.error_cannot_be_null, "Locale"))
}
@JvmStatic
fun setFollowSystem(activity: Activity) {
val systemLocale = localeHelper.systemLocale
val currentLocale = localeHelper.getLocale(activity)
localeDelegate.apply {
if (currentLocale !== systemLocale) {
setLocale(activity, systemLocale)
}
clearLocaleSelection(activity)
fun setLocale(context: Context, locale: Locale?) {
locale ?: throw IllegalArgumentException(context.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 {
AppCompatDelegate.setApplicationLocales(LocaleListCompat.create(locale))
}
}
@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
fun getResources(desiredLocale: Locale?): Resources {
return globalAppContext.let { context ->
return GlobalAppContext.get().let { context ->
context.resources.configuration.let { Configuration(it) }.run {
setLocale(desiredLocale ?: localeHelper.systemLocale)
setLocale(desiredLocale ?: getSystemLocale())
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])
}
}
}
}

View File

@@ -40,7 +40,8 @@ public class SdkVersionUtils {
/* api: 30 */ "11",
/* api: 31 */ "12",
/* api: 32 */ "12.1",
/* api: 33 */ "13"
/* api: 33 */ "13",
/* api: 34 */ "14"
};
public static String sdkIntToString(int i) {

View File

@@ -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)
}
}

View File

@@ -174,7 +174,8 @@ object UpdateUtils {
}
@JvmStatic
fun autoCheckForUpdatesIfNeededWithSnackbar(activity: AppCompatActivity, snackbarViewIdRes: Int) {
@JvmOverloads
fun autoCheckForUpdatesIfNeededWithSnackbar(activity: AppCompatActivity, snackbarViewIdRes: Int = android.R.id.content) {
if (isAutoCheckForUpdatesEnabled) {
val checker = IntervalChecker()
if (checker.isBeyondNoNewer && checker.isBeyondPostponed && checker.isBeyondAutoChecked) {

View File

@@ -16,6 +16,7 @@ import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.widget.Toast
import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatActivity
@@ -290,6 +291,24 @@ object ViewUtils {
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 {
companion object {

View File

@@ -1,12 +1,12 @@
/*
* Copyright (c) 2009-2013 Panxiaobo
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -95,20 +95,18 @@ public class AxmlWriter extends AxmlVisitor {
for (NodeImpl first : firsts) {
size += first.prepare(this);
}
{
int a = 0;
for (Map.Entry<String, Ns> e : nses.entrySet()) {
Ns ns = e.getValue();
if (ns == null) {
ns = new Ns(null, new StringItem(e.getKey()), 0);
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);
int a = 0;
for (Map.Entry<String, Ns> e : nses.entrySet()) {
Ns ns = e.getValue();
if (ns == null) {
ns = new Ns(null, new StringItem(e.getKey()), 0);
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);
}
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

View File

@@ -6,7 +6,7 @@
android:viewportWidth="128">
<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"/>
<path
android:fillColor="#81CF08"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -25,7 +25,6 @@
<org.autojs.autojs.ui.explorer.ExplorerView
android:id="@+id/script_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fafafa" />
android:layout_height="match_parent" />
</LinearLayout>

Some files were not shown because too many files have changed in this diff Show More