6.6.2 - Alpha2 - project.json 支持 signatureScheme 选项宽松匹配
This commit is contained in:
@@ -55,7 +55,7 @@
|
|||||||
| <span style="word-break:keep-all;white-space:nowrap">[Auto.js Pro 9](https://pro.autojs.org/)</span> | <span style="word-break:keep-all;white-space:nowrap">AutoJsPro</span> | <span style="word-break:keep-all;white-space:nowrap">[hyb1996](https://github.com/hyb1996)</span> | <span style="word-break:keep-all;white-space:nowrap">`2021/03/28`</span> | <span style="word-break:keep-all;white-space:nowrap">`2023/02/09`</span> |
|
| <span style="word-break:keep-all;white-space:nowrap">[Auto.js Pro 9](https://pro.autojs.org/)</span> | <span style="word-break:keep-all;white-space:nowrap">AutoJsPro</span> | <span style="word-break:keep-all;white-space:nowrap">[hyb1996](https://github.com/hyb1996)</span> | <span style="word-break:keep-all;white-space:nowrap">`2021/03/28`</span> | <span style="word-break:keep-all;white-space:nowrap">`2023/02/09`</span> |
|
||||||
| <span style="word-break:keep-all;white-space:nowrap">[AutoJs6](https://github.com/SuperMonster003/AutoJs6)</span> | <span style="word-break:keep-all;white-space:nowrap">[AutoJs6](https://github.com/SuperMonster003/AutoJs6/commit/a8ce1b9acb541e9736c33134be3194c3148a15a3#diff-833a46a97033e77558372a2dce103fd6fee29aaaa899f610022a7aece592ee7bR27)</span> | <span style="word-break:keep-all;white-space:nowrap">[SuperMonster003](https://github.com/SuperMonster003)</span> | <span style="word-break:keep-all;white-space:nowrap">`2021/12/01`</span> | <span style="word-break:keep-all;white-space:nowrap">-</span> |
|
| <span style="word-break:keep-all;white-space:nowrap">[AutoJs6](https://github.com/SuperMonster003/AutoJs6)</span> | <span style="word-break:keep-all;white-space:nowrap">[AutoJs6](https://github.com/SuperMonster003/AutoJs6/commit/a8ce1b9acb541e9736c33134be3194c3148a15a3#diff-833a46a97033e77558372a2dce103fd6fee29aaaa899f610022a7aece592ee7bR27)</span> | <span style="word-break:keep-all;white-space:nowrap">[SuperMonster003](https://github.com/SuperMonster003)</span> | <span style="word-break:keep-all;white-space:nowrap">`2021/12/01`</span> | <span style="word-break:keep-all;white-space:nowrap">-</span> |
|
||||||
| <span style="word-break:keep-all;white-space:nowrap">[AutoX](https://github.com/kkevsekk1/AutoX)</span> | <span style="white-space:pre">[Autox.js v6](https://github.com/kkevsekk1/AutoX/commit/8b6776cff8b0fca4be4a52719b7d7d07c0a058f3#diff-51a0b488f963eb0be6c6599bf5df497313877cf5bdff3950807373912ac1cdc9R130)</span> | <span style="word-break:keep-all;white-space:nowrap">[wilinz](https://github.com/wilinz)</span> | <span style="word-break:keep-all;white-space:nowrap">`2022/05/26`</span> | <span style="word-break:keep-all;white-space:nowrap">-</span> |
|
| <span style="word-break:keep-all;white-space:nowrap">[AutoX](https://github.com/kkevsekk1/AutoX)</span> | <span style="white-space:pre">[Autox.js v6](https://github.com/kkevsekk1/AutoX/commit/8b6776cff8b0fca4be4a52719b7d7d07c0a058f3#diff-51a0b488f963eb0be6c6599bf5df497313877cf5bdff3950807373912ac1cdc9R130)</span> | <span style="word-break:keep-all;white-space:nowrap">[wilinz](https://github.com/wilinz)</span> | <span style="word-break:keep-all;white-space:nowrap">`2022/05/26`</span> | <span style="word-break:keep-all;white-space:nowrap">-</span> |
|
||||||
| <span style="word-break:keep-all;white-space:nowrap">[AutoX](https://github.com/kkevsekk1/AutoX)</span> | <span style="white-space:pre">[Autox.js v7](https://github.com/kkevsekk1/AutoX/commit/484491fd5fe12b8203d0b09c181eb0f471c0ea9f#diff-8cff73265af19c059547b76aca8882cbaa3209291406f52df1dafbbc78e80c46R120)</span> | <span style="word-break:keep-all;white-space:nowrap">[aiselp](https://github.com/aiselp)</span> | <span style="word-break:keep-all;white-space:nowrap">`2024/04/21`</span> | <span style="word-break:keep-all;white-space:nowrap">-</span> |
|
| <span style="word-break:keep-all;white-space:nowrap">[AutoX](https://github.com/aiselp/AutoX)</span> | <span style="white-space:pre">[Autox.js v7](https://github.com/aiselp/AutoX/commit/484491fd5fe12b8203d0b09c181eb0f471c0ea9f#diff-8cff73265af19c059547b76aca8882cbaa3209291406f52df1dafbbc78e80c46R120)</span> | <span style="word-break:keep-all;white-space:nowrap">[aiselp](https://github.com/aiselp)</span> | <span style="word-break:keep-all;white-space:nowrap">`2024/04/21`</span> | <span style="word-break:keep-all;white-space:nowrap">-</span> |
|
||||||
|
|
||||||
{{ p_introduction_related_autojs_table_date_is_estimated }}.
|
{{ p_introduction_related_autojs_table_date_is_estimated }}.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.autojs.autojs.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface DeserializedMethodName {
|
||||||
|
String method();
|
||||||
|
|
||||||
|
Class<?>[] parameterTypes() default {};
|
||||||
|
}
|
||||||
@@ -16,8 +16,9 @@ import org.autojs.autojs.app.GlobalAppContext
|
|||||||
import org.autojs.autojs.engine.encryption.AdvancedEncryptionStandard
|
import org.autojs.autojs.engine.encryption.AdvancedEncryptionStandard
|
||||||
import org.autojs.autojs.pio.PFiles
|
import org.autojs.autojs.pio.PFiles
|
||||||
import org.autojs.autojs.project.BuildInfo
|
import org.autojs.autojs.project.BuildInfo
|
||||||
|
import org.autojs.autojs.project.LaunchConfig
|
||||||
import org.autojs.autojs.project.ProjectConfig
|
import org.autojs.autojs.project.ProjectConfig
|
||||||
import org.autojs.autojs.project.ProjectConfig.DEFAULT_MAIN_SCRIPT_FILE_NAME
|
import org.autojs.autojs.project.ProjectConfig.CONFIG_FILE_NAME
|
||||||
import org.autojs.autojs.script.EncryptedScriptFileHeader.writeHeader
|
import org.autojs.autojs.script.EncryptedScriptFileHeader.writeHeader
|
||||||
import org.autojs.autojs.script.JavaScriptFileSource
|
import org.autojs.autojs.script.JavaScriptFileSource
|
||||||
import org.autojs.autojs.util.FileUtils.TYPE.JAVASCRIPT
|
import org.autojs.autojs.util.FileUtils.TYPE.JAVASCRIPT
|
||||||
@@ -39,7 +40,7 @@ import java.io.InputStream
|
|||||||
* Created by Stardust on Oct 24, 2017.
|
* Created by Stardust on Oct 24, 2017.
|
||||||
* Modified by SuperMonster003 as of Jul 8, 2022.
|
* Modified by SuperMonster003 as of Jul 8, 2022.
|
||||||
*/
|
*/
|
||||||
open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File, private val workspacePath: String) {
|
open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File, private val buildPath: String) {
|
||||||
|
|
||||||
private var mProgressCallback: ProgressCallback? = null
|
private var mProgressCallback: ProgressCallback? = null
|
||||||
private var mArscPackageName: String? = null
|
private var mArscPackageName: String? = null
|
||||||
@@ -49,7 +50,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
|
|
||||||
private lateinit var mProjectConfig: ProjectConfig
|
private lateinit var mProjectConfig: ProjectConfig
|
||||||
|
|
||||||
private val mApkPackager = ApkPackager(apkInputStream, workspacePath)
|
private val mApkPackager = ApkPackager(apkInputStream, buildPath)
|
||||||
|
|
||||||
private val mAssetManager: AssetManager by lazy { globalContext.assets }
|
private val mAssetManager: AssetManager by lazy { globalContext.assets }
|
||||||
|
|
||||||
@@ -57,14 +58,14 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
private var mAssetsFileIncludes = Libs.defaultAssetFilesToInclude.toMutableList()
|
private var mAssetsFileIncludes = Libs.defaultAssetFilesToInclude.toMutableList()
|
||||||
private var mAssetsDirExcludes = Libs.defaultAssetDirsToExclude.toMutableList()
|
private var mAssetsDirExcludes = Libs.defaultAssetDirsToExclude.toMutableList()
|
||||||
|
|
||||||
private var mSplashThemeId = 0
|
private var mSplashThemeId: Int = 0
|
||||||
private var mNoSplashThemeId = 0
|
private var mNoSplashThemeId: Int = 0
|
||||||
|
|
||||||
private val mManifestFile
|
private val mManifestFile
|
||||||
get() = File(workspacePath, "AndroidManifest.xml")
|
get() = File(buildPath, "AndroidManifest.xml")
|
||||||
|
|
||||||
private val mResourcesArscFile
|
private val mResourcesArscFile
|
||||||
get() = File(workspacePath, "resources.arsc")
|
get() = File(buildPath, "resources.arsc")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
PFiles.ensureDir(outApkFile.path)
|
PFiles.ensureDir(outApkFile.path)
|
||||||
@@ -75,7 +76,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun prepare() = also {
|
fun prepare() = also {
|
||||||
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onPrepare(this) } }
|
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onPrepare(this) } }
|
||||||
File(workspacePath).mkdirs()
|
File(buildPath).mkdirs()
|
||||||
mApkPackager.unzip()
|
mApkPackager.unzip()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +98,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun copyDir(srcFile: File, relativeDestPath: String) {
|
fun copyDir(srcFile: File, relativeDestPath: String) {
|
||||||
val destDirFile = File(workspacePath, relativeDestPath).apply { mkdir() }
|
val destDirFile = File(buildPath, relativeDestPath).apply { mkdir() }
|
||||||
srcFile.listFiles()?.forEach { srcChildFile ->
|
srcFile.listFiles()?.forEach { srcChildFile ->
|
||||||
if (srcChildFile.isFile) {
|
if (srcChildFile.isFile) {
|
||||||
if (srcChildFile.name.endsWith(JAVASCRIPT.extensionWithDot)) {
|
if (srcChildFile.name.endsWith(JAVASCRIPT.extensionWithDot)) {
|
||||||
@@ -133,7 +134,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun replaceFile(srcFile: File, relativeDestPath: String) = also {
|
fun replaceFile(srcFile: File, relativeDestPath: String) = also {
|
||||||
val destFile = File(workspacePath, relativeDestPath)
|
val destFile = File(buildPath, relativeDestPath)
|
||||||
if (destFile.name.endsWith(JAVASCRIPT.extensionWithDot)) {
|
if (destFile.name.endsWith(JAVASCRIPT.extensionWithDot)) {
|
||||||
encrypt(srcFile, destFile)
|
encrypt(srcFile, destFile)
|
||||||
} else {
|
} else {
|
||||||
@@ -144,70 +145,99 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun withConfig(config: ProjectConfig) = also {
|
fun withConfig(config: ProjectConfig) = also {
|
||||||
config.also { mProjectConfig = it }.run {
|
config.also { mProjectConfig = it }.run {
|
||||||
if (!launchConfig.isSplashVisible) {
|
retrieveSplashThemeResources(launchConfig)
|
||||||
try {
|
prepareManifestConfiguration(this)
|
||||||
val tableBlock = TableBlock.load(mResourcesArscFile)
|
|
||||||
val packageName = "${GlobalAppContext.get().packageName}.inrt"
|
|
||||||
val packageBlock = tableBlock.getOrCreatePackage(0x7f, packageName).also {
|
|
||||||
tableBlock.currentPackage = it
|
|
||||||
}
|
|
||||||
packageBlock.getEntry("", "style", "AppTheme.Splash")?.let {
|
|
||||||
mSplashThemeId = it.resourceId
|
|
||||||
}
|
|
||||||
packageBlock.getEntry("", "style", "AppTheme.SevereTransparent")?.let {
|
|
||||||
mNoSplashThemeId = it.resourceId
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mManifestEditor = editManifest()
|
|
||||||
.setAppName(name)
|
|
||||||
.setVersionName(versionName)
|
|
||||||
.setVersionCode(versionCode)
|
|
||||||
.setPackageName(packageName)
|
|
||||||
|
|
||||||
setArscPackageName(packageName)
|
setArscPackageName(packageName)
|
||||||
updateProjectConfig(this)
|
updateProjectConfig(this)
|
||||||
copyAssetsRecursively("", File(workspacePath, "assets"))
|
copyAssetsRecursively("", File(buildDir, "assets"))
|
||||||
copyLibrariesByConfig(this)
|
copyLibrariesByConfig(this)
|
||||||
setScriptFile(sourcePath)
|
setScriptFile(sourcePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun prepareManifestConfiguration(config: ProjectConfig) {
|
||||||
|
mManifestEditor = editManifest()
|
||||||
|
.setAppName(config.name)
|
||||||
|
.setVersionName(config.versionName)
|
||||||
|
.setVersionCode(config.versionCode)
|
||||||
|
.setPackageName(config.packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun retrieveSplashThemeResources(launchConfig: LaunchConfig) {
|
||||||
|
if (launchConfig.isSplashVisible) {
|
||||||
|
// @Hint by SuperMonster003 on Jan 23, 2024.
|
||||||
|
// ! Members `mSplashThemeId` and `mNoSplashThemeId` will keep their default values.
|
||||||
|
// ! zh-CN: 成员变量 `mSplashThemeId` 及 `mNoSplashThemeId` 将保持其默认值.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val tableBlock = TableBlock.load(mResourcesArscFile)
|
||||||
|
val packageName = "${GlobalAppContext.get().packageName}.inrt"
|
||||||
|
val packageBlock = tableBlock.getOrCreatePackage(0x7f, packageName).also {
|
||||||
|
tableBlock.currentPackage = it
|
||||||
|
}
|
||||||
|
packageBlock.getEntry("", "style", "AppTheme.Splash")?.let {
|
||||||
|
mSplashThemeId = it.resourceId
|
||||||
|
}
|
||||||
|
packageBlock.getEntry("", "style", "AppTheme.SevereTransparent")?.let {
|
||||||
|
mNoSplashThemeId = it.resourceId
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(FileNotFoundException::class)
|
@Throws(FileNotFoundException::class)
|
||||||
fun editManifest(): ManifestEditor = ManifestEditorWithAuthorities(FileInputStream(mManifestFile)).also { mManifestEditor = it }
|
fun editManifest(): ManifestEditor = ManifestEditorWithAuthorities(FileInputStream(mManifestFile)).also { mManifestEditor = it }
|
||||||
|
|
||||||
private fun updateProjectConfig(config: ProjectConfig) {
|
private fun updateProjectConfig(config: ProjectConfig) {
|
||||||
val projectConfig = when {
|
|
||||||
!PFiles.isDir(config.sourcePath) -> null
|
// 这里为什么要有这样的一个方法? (
|
||||||
else -> ProjectConfig.fromProjectDir(config.sourcePath)?.also {
|
// 会不会是因为有些配置需要写入到文件中, 这些配置包括自增的版本号, 用户的选择或键入值等等
|
||||||
val buildNumber = it.buildInfo.buildNumber
|
// )
|
||||||
it.buildInfo = BuildInfo.generate(buildNumber + 1)
|
// 参数 config 只是获取了一部分的字段用于设置新的 projectConfig, 为什么不直接使用全部的字段? (
|
||||||
PFiles.write(ProjectConfig.configFileOfDir(config.sourcePath), it.toJson())
|
// 因为有些不需要更新. 如果我们需要将所有设置开放到 Activity 页面中, 那么其实所有配置都是需要更新的
|
||||||
|
// )
|
||||||
|
// 像 abis, libs, signatureScheme 等信息就全部丢失了. (
|
||||||
|
// 所以需要添加到这个方法中
|
||||||
|
// )
|
||||||
|
|
||||||
|
val projectConfig = run {
|
||||||
|
if (PFiles.isDir(config.sourcePath)) {
|
||||||
|
// @Hint by SuperMonster003 on Jan 23, 2025.
|
||||||
|
// ! Project directory packaging.
|
||||||
|
// ! zh-CN: 打包项目目录.
|
||||||
|
ProjectConfig.fromProjectDir(config.sourcePath)?.let { sourceProjectConfig ->
|
||||||
|
sourceProjectConfig
|
||||||
|
.setBuildInfo(BuildInfo.generate(sourceProjectConfig.buildInfo.buildNumber + 1))
|
||||||
|
File(ProjectConfig.configFileOfDir(config.sourcePath)).writeText(sourceProjectConfig.toJson())
|
||||||
|
return@run sourceProjectConfig
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} ?: ProjectConfig()
|
// @Hint by SuperMonster003 on Jan 23, 2025.
|
||||||
.setMainScriptFileName(DEFAULT_MAIN_SCRIPT_FILE_NAME)
|
// ! Single file packaging.
|
||||||
.setName(config.name)
|
// ! zh-CN: 打包单独文件.
|
||||||
.setPackageName(config.packageName)
|
return@run ProjectConfig().also { newProjectConfig ->
|
||||||
.setVersionName(config.versionName)
|
newProjectConfig
|
||||||
.setVersionCode(config.versionCode)
|
.setName(config.name)
|
||||||
.also { newProjectConfig ->
|
.setPackageName(config.packageName)
|
||||||
newProjectConfig.buildInfo = BuildInfo.generate(newProjectConfig.versionCode.toLong())
|
.setVersionName(config.versionName)
|
||||||
File(workspacePath, "assets/project/${ProjectConfig.CONFIG_FILE_NAME}").also { file ->
|
.setVersionCode(config.versionCode)
|
||||||
|
.setBuildInfo(BuildInfo.generate(newProjectConfig.versionCode.toLong()))
|
||||||
|
File(buildPath, "assets/project/$CONFIG_FILE_NAME").also { file ->
|
||||||
file.parentFile?.let { parent -> if (!parent.exists()) parent.mkdirs() }
|
file.parentFile?.let { parent -> if (!parent.exists()) parent.mkdirs() }
|
||||||
}.writeText(newProjectConfig.toJson())
|
}.writeText(newProjectConfig.toJson())
|
||||||
}
|
}
|
||||||
|
|
||||||
projectConfig.run {
|
}
|
||||||
mKey = MD5Utils.md5(packageName + versionName + mainScriptFileName)
|
|
||||||
mInitVector = MD5Utils.md5(buildInfo.buildId + name).substring(0, 16)
|
mKey = MD5Utils.md5(projectConfig.run { packageName + versionName + mainScriptFileName })
|
||||||
Libs.entries.forEach { entry ->
|
mInitVector = MD5Utils.md5(projectConfig.run { buildInfo.buildId + name }).take(16)
|
||||||
if (config.libs.contains(entry.label)) {
|
Libs.entries.forEach { entry ->
|
||||||
mLibsIncludes += entry.libsToInclude.toSet()
|
if (config.libs.contains(entry.label)) {
|
||||||
mAssetsFileIncludes += entry.assetFilesToInclude.toSet()
|
mLibsIncludes += entry.libsToInclude.toSet()
|
||||||
mAssetsDirExcludes -= entry.assetDirsToExclude.toSet()
|
mAssetsFileIncludes += entry.assetFilesToInclude.toSet()
|
||||||
}
|
mAssetsDirExcludes -= entry.assetDirsToExclude.toSet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,7 +255,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
val appIcon = packageBlock.getOrCreate("", ICON_RES_DIR, ICON_NAME)
|
val appIcon = packageBlock.getOrCreate("", ICON_RES_DIR, ICON_NAME)
|
||||||
val appIconPath = appIcon.resValue.decodeValue()
|
val appIconPath = appIcon.resValue.decodeValue()
|
||||||
Log.d(TAG, "Icon path: $appIconPath")
|
Log.d(TAG, "Icon path: $appIconPath")
|
||||||
val file = File(workspacePath, appIconPath).also {
|
val file = File(buildPath, appIconPath).also {
|
||||||
if (!it.exists()) {
|
if (!it.exists()) {
|
||||||
File(it.parent!!).mkdirs()
|
File(it.parent!!).mkdirs()
|
||||||
it.createNewFile()
|
it.createNewFile()
|
||||||
@@ -273,19 +303,20 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
fun sign() = also {
|
fun sign() = also {
|
||||||
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onSign(this) } }
|
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onSign(this) } }
|
||||||
val fos = FileOutputStream(outApkFile)
|
val fos = FileOutputStream(outApkFile)
|
||||||
TinySign.sign(File(workspacePath), fos)
|
TinySign.sign(File(buildPath), fos)
|
||||||
fos.close()
|
fos.close()
|
||||||
|
|
||||||
val defaultKeyStoreFile = File(workspacePath, "default_key_store.bks")
|
val defaultKeyStoreFile = File(buildPath, "default_key_store.bks")
|
||||||
val tmpOutputApk = File(workspacePath, "temp.apk")
|
val tmpOutputApk = File(buildPath, "temp.apk")
|
||||||
copyInputStreamToFile(GlobalAppContext.get().assets.open("default_key_store.bks"), defaultKeyStoreFile)
|
copyInputStreamToFile(GlobalAppContext.get().assets.open("default_key_store.bks"), defaultKeyStoreFile)
|
||||||
|
|
||||||
val signer = ApkSigner(outApkFile, tmpOutputApk)
|
val signer = ApkSigner(outApkFile, tmpOutputApk).apply {
|
||||||
signer.useDefaultSignatureVersion = false
|
useDefaultSignatureVersion = false
|
||||||
signer.v1SigningEnabled = mProjectConfig.signatureScheme.contains("V1")
|
v1SigningEnabled = "V1" in mProjectConfig.signatureScheme
|
||||||
signer.v2SigningEnabled = mProjectConfig.signatureScheme.contains("V2")
|
v2SigningEnabled = "V2" in mProjectConfig.signatureScheme
|
||||||
signer.v3SigningEnabled = mProjectConfig.signatureScheme.contains("V3")
|
v3SigningEnabled = "V3" in mProjectConfig.signatureScheme
|
||||||
signer.v4SigningEnabled = mProjectConfig.signatureScheme.contains("V4")
|
v4SigningEnabled = "V4" in mProjectConfig.signatureScheme
|
||||||
|
}
|
||||||
|
|
||||||
var keyStoreFile = defaultKeyStoreFile
|
var keyStoreFile = defaultKeyStoreFile
|
||||||
var password = "AutoJs6"
|
var password = "AutoJs6"
|
||||||
@@ -313,7 +344,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
|
|
||||||
fun cleanWorkspace() = also {
|
fun cleanWorkspace() = also {
|
||||||
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onClean(this) } }
|
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onClean(this) } }
|
||||||
delete(File(workspacePath))
|
delete(File(buildPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
@@ -321,8 +352,8 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun buildArsc() {
|
private fun buildArsc() {
|
||||||
val oldArsc = File(workspacePath, "resources.arsc")
|
val oldArsc = File(buildPath, "resources.arsc")
|
||||||
val newArsc = File(workspacePath, "resources.arsc.new")
|
val newArsc = File(buildPath, "resources.arsc.new")
|
||||||
val decoder = ARSCDecoder(BufferedInputStream(FileInputStream(oldArsc)), null, false)
|
val decoder = ARSCDecoder(BufferedInputStream(FileInputStream(oldArsc)), null, false)
|
||||||
decoder.CloneArsc(FileOutputStream(newArsc), mArscPackageName, true)
|
decoder.CloneArsc(FileOutputStream(newArsc), mArscPackageName, true)
|
||||||
oldArsc.delete()
|
oldArsc.delete()
|
||||||
@@ -345,9 +376,13 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
private inner class ManifestEditorWithAuthorities(manifestInputStream: InputStream?) : ManifestEditor(manifestInputStream) {
|
private inner class ManifestEditorWithAuthorities(manifestInputStream: InputStream?) : ManifestEditor(manifestInputStream) {
|
||||||
override fun onAttr(attr: AxmlWriter.Attr) {
|
override fun onAttr(attr: AxmlWriter.Attr) {
|
||||||
attr.apply {
|
attr.apply {
|
||||||
|
|
||||||
|
// @Reference to aiselp (https://github.com/aiselp) by SuperMonster003 on Jan 18, 2025.
|
||||||
|
// ! https://github.com/aiselp/AutoX/blob/5b3303926082d591a166b1845702357406811aaf/app/src/main/java/org/autojs/autojs/build/ApkBuilder.kt#L175-L188
|
||||||
if (!mProjectConfig.launchConfig.isSplashVisible && mSplashThemeId != 0 && value == mSplashThemeId) {
|
if (!mProjectConfig.launchConfig.isSplashVisible && mSplashThemeId != 0 && value == mSplashThemeId) {
|
||||||
value = mNoSplashThemeId
|
value = mNoSplashThemeId
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.data == "authorities" && value is StringItem) {
|
if (name.data == "authorities" && value is StringItem) {
|
||||||
(value as StringItem).data = "${mProjectConfig.packageName}.fileprovider"
|
(value as StringItem).data = "${mProjectConfig.packageName}.fileprovider"
|
||||||
} else {
|
} else {
|
||||||
@@ -388,7 +423,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
mLibsIncludes.distinct().forEach { libName ->
|
mLibsIncludes.distinct().forEach { libName ->
|
||||||
runCatching {
|
runCatching {
|
||||||
File(srcLibDir, "$abiSrcName/$libName").takeIf { it.exists() }?.copyTo(
|
File(srcLibDir, "$abiSrcName/$libName").takeIf { it.exists() }?.copyTo(
|
||||||
File(workspacePath, "lib/$abiDestName/$libName"),
|
File(buildPath, "lib/$abiDestName/$libName"),
|
||||||
overwrite = true
|
overwrite = true
|
||||||
)
|
)
|
||||||
}.onFailure { it.printStackTrace() }
|
}.onFailure { it.printStackTrace() }
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import androidx.annotation.Nullable;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import org.autojs.autojs.annotation.DeserializedMethodName;
|
||||||
import org.autojs.autojs.annotation.SerializedNameCompatible;
|
import org.autojs.autojs.annotation.SerializedNameCompatible;
|
||||||
import org.autojs.autojs.annotation.SerializedNameCompatible.With;
|
import org.autojs.autojs.annotation.SerializedNameCompatible.With;
|
||||||
import org.autojs.autojs.apkbuilder.keystore.KeyStore;
|
import org.autojs.autojs.apkbuilder.keystore.KeyStore;
|
||||||
@@ -22,14 +23,13 @@ import java.io.File;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Set;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Stardust on Jan 24, 2018.
|
* Created by Stardust on Jan 24, 2018.
|
||||||
@@ -49,18 +49,24 @@ public class ProjectConfig {
|
|||||||
.create();
|
.create();
|
||||||
|
|
||||||
@SerializedName("name")
|
@SerializedName("name")
|
||||||
@SerializedNameCompatible(with = {@With(value = "projectName")})
|
@SerializedNameCompatible(with = {
|
||||||
|
@With(value = "projectName"),
|
||||||
|
})
|
||||||
private String mName;
|
private String mName;
|
||||||
|
|
||||||
@SerializedName("versionName")
|
@SerializedName("versionName")
|
||||||
@SerializedNameCompatible(with = {@With(value = "version")})
|
@SerializedNameCompatible(with = {
|
||||||
|
@With(value = "version"),
|
||||||
|
})
|
||||||
private String mVersionName;
|
private String mVersionName;
|
||||||
|
|
||||||
@SerializedName("versionCode")
|
@SerializedName("versionCode")
|
||||||
private int mVersionCode = -1;
|
private int mVersionCode = 1;
|
||||||
|
|
||||||
@SerializedName("packageName")
|
@SerializedName("packageName")
|
||||||
@SerializedNameCompatible(with = {@With(value = "package")})
|
@SerializedNameCompatible(with = {
|
||||||
|
@With(value = "package"),
|
||||||
|
})
|
||||||
private String mPackageName;
|
private String mPackageName;
|
||||||
|
|
||||||
@SerializedName("main")
|
@SerializedName("main")
|
||||||
@@ -73,7 +79,7 @@ public class ProjectConfig {
|
|||||||
@With(value = "mainFile"),
|
@With(value = "mainFile"),
|
||||||
@With(value = "mainFileName"),
|
@With(value = "mainFileName"),
|
||||||
})
|
})
|
||||||
private String mMainScriptFileName;
|
private String mMainScriptFileName = DEFAULT_MAIN_SCRIPT_FILE_NAME;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@SerializedName(value = "assets")
|
@SerializedName(value = "assets")
|
||||||
@@ -84,15 +90,21 @@ public class ProjectConfig {
|
|||||||
private List<String> mAssets = new ArrayList<>();
|
private List<String> mAssets = new ArrayList<>();
|
||||||
|
|
||||||
@SerializedName("launchConfig")
|
@SerializedName("launchConfig")
|
||||||
@SerializedNameCompatible(with = {@With(value = "launch")})
|
@SerializedNameCompatible(with = {
|
||||||
|
@With(value = "launch"),
|
||||||
|
})
|
||||||
private LaunchConfig mLaunchConfig = new LaunchConfig();
|
private LaunchConfig mLaunchConfig = new LaunchConfig();
|
||||||
|
|
||||||
@SerializedName("build")
|
@SerializedName("build")
|
||||||
@SerializedNameCompatible(with = {@With(value = "buildInfo")})
|
@SerializedNameCompatible(with = {
|
||||||
|
@With(value = "buildInfo"),
|
||||||
|
})
|
||||||
private BuildInfo mBuildInfo = new BuildInfo();
|
private BuildInfo mBuildInfo = new BuildInfo();
|
||||||
|
|
||||||
@SerializedName("icon")
|
@SerializedName("icon")
|
||||||
@SerializedNameCompatible(with = {@With(value = "iconPath")})
|
@SerializedNameCompatible(with = {
|
||||||
|
@With(value = "iconPath"),
|
||||||
|
})
|
||||||
private String mIconPath;
|
private String mIconPath;
|
||||||
|
|
||||||
private transient Callable<Bitmap> mIconBitmapGetter;
|
private transient Callable<Bitmap> mIconBitmapGetter;
|
||||||
@@ -125,19 +137,23 @@ public class ProjectConfig {
|
|||||||
@With(value = "signatureSchemes"),
|
@With(value = "signatureSchemes"),
|
||||||
@With(value = "signature"),
|
@With(value = "signature"),
|
||||||
})
|
})
|
||||||
|
@DeserializedMethodName(method = "normalizeSignatureScheme", parameterTypes = {String.class})
|
||||||
private String mSignatureScheme = "V1 + V2";
|
private String mSignatureScheme = "V1 + V2";
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private transient KeyStore mKeyStore = null;
|
private transient KeyStore mKeyStore = null;
|
||||||
|
|
||||||
@SerializedName("scriptConfigs")
|
// @Commented by SuperMonster003 on Jan 20, 2025.
|
||||||
@SerializedNameCompatible(with = {
|
// ! Unused config options: "scripts".
|
||||||
@With(value = "scriptsConfigs"),
|
// ! zh-CN: 未使用的配置选项: "scripts".
|
||||||
@With(value = "scriptsConfig"),
|
// # @SerializedName("scriptConfigs")
|
||||||
@With(value = "scriptConfig"),
|
// # @SerializedNameCompatible(with = {
|
||||||
@With(value = "scripts", target = {"AutoJs4", "AutoX"}),
|
// # @With(value = "scriptsConfigs"),
|
||||||
})
|
// # @With(value = "scriptsConfig"),
|
||||||
private final Map<String, ScriptConfig> mScriptConfigs = new HashMap<>();
|
// # @With(value = "scriptConfig"),
|
||||||
|
// # @With(value = "scripts", target = {"AutoJs4", "AutoX"}),
|
||||||
|
// # })
|
||||||
|
// # private final Map<String, ScriptConfig> mScriptConfigs = new HashMap<>();
|
||||||
|
|
||||||
@SerializedName(value = "useFeatures")
|
@SerializedName(value = "useFeatures")
|
||||||
@SerializedNameCompatible(with = {
|
@SerializedNameCompatible(with = {
|
||||||
@@ -362,8 +378,9 @@ public class ProjectConfig {
|
|||||||
return mBuildInfo;
|
return mBuildInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBuildInfo(BuildInfo buildInfo) {
|
public ProjectConfig setBuildInfo(BuildInfo buildInfo) {
|
||||||
mBuildInfo = buildInfo;
|
mBuildInfo = buildInfo;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@@ -412,7 +429,7 @@ public class ProjectConfig {
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public String getMainScriptFileName() {
|
public String getMainScriptFileName() {
|
||||||
return mMainScriptFileName != null ? mMainScriptFileName : DEFAULT_MAIN_SCRIPT_FILE_NAME;
|
return mMainScriptFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProjectConfig setMainScriptFileName(String mainScriptFileName) {
|
public ProjectConfig setMainScriptFileName(String mainScriptFileName) {
|
||||||
@@ -420,9 +437,12 @@ public class ProjectConfig {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, ScriptConfig> getScriptConfigs() {
|
// @Commented by SuperMonster003 on Jan 20, 2025.
|
||||||
return mScriptConfigs;
|
// ! Unused config options: "scripts".
|
||||||
}
|
// ! zh-CN: 未使用的配置选项: "scripts".
|
||||||
|
// # public Map<String, ScriptConfig> getScriptConfigs() {
|
||||||
|
// # return mScriptConfigs;
|
||||||
|
// # }
|
||||||
|
|
||||||
public List<String> getAssets() {
|
public List<String> getAssets() {
|
||||||
if (mAssets == null) {
|
if (mAssets == null) {
|
||||||
@@ -516,22 +536,20 @@ public class ProjectConfig {
|
|||||||
mFeatures = features;
|
mFeatures = features;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScriptConfig getScriptConfig(String path) {
|
// @Commented by SuperMonster003 on Jan 20, 2025.
|
||||||
ScriptConfig scriptConfig = Objects.requireNonNull(mScriptConfigs.getOrDefault(path, new ScriptConfig()));
|
// ! Unused config options: "scripts".
|
||||||
List<String> combinedFeatures = getCombinedFeatures(scriptConfig);
|
// ! zh-CN: 未使用的配置选项: "scripts".
|
||||||
scriptConfig.setFeatures(combinedFeatures);
|
// # public ScriptConfig getScriptConfig(String scriptKeyName) {
|
||||||
return scriptConfig;
|
// # ScriptConfig scriptConfig = Objects.requireNonNull(mScriptConfigs.getOrDefault(scriptKeyName, new ScriptConfig()));
|
||||||
}
|
// # List<String> combinedFeatures = new ArrayList<>(
|
||||||
|
// # new HashSet<>() {{
|
||||||
@NotNull
|
// # addAll(scriptConfig.getFeatures());
|
||||||
public ArrayList<String> getCombinedFeatures(ScriptConfig scriptConfig) {
|
// # addAll(mFeatures);
|
||||||
return new ArrayList<>(
|
// # }}
|
||||||
new HashSet<>() {{
|
// # );
|
||||||
addAll(scriptConfig.getFeatures());
|
// # scriptConfig.setFeatures(combinedFeatures);
|
||||||
addAll(mFeatures);
|
// # return scriptConfig;
|
||||||
}}
|
// # }
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<File> getExcludedDirs() {
|
public List<File> getExcludedDirs() {
|
||||||
return mExcludedDirs;
|
return mExcludedDirs;
|
||||||
@@ -566,10 +584,24 @@ public class ProjectConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ProjectConfig setSignatureScheme(String signatureScheme) {
|
public ProjectConfig setSignatureScheme(String signatureScheme) {
|
||||||
mSignatureScheme = signatureScheme;
|
mSignatureScheme = normalizeSignatureScheme(signatureScheme);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String normalizeSignatureScheme(String input) {
|
||||||
|
Pattern pattern = Pattern.compile("v\\d+", Pattern.CASE_INSENSITIVE);
|
||||||
|
Matcher matcher = pattern.matcher(input);
|
||||||
|
Set<String> matches = new HashSet<>();
|
||||||
|
while (matcher.find()) {
|
||||||
|
matches.add(matcher.group().toUpperCase());
|
||||||
|
}
|
||||||
|
if (matches.isEmpty()) {
|
||||||
|
return input.trim();
|
||||||
|
}
|
||||||
|
return matches.stream().sorted().collect(Collectors.joining(" + "));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public KeyStore getKeyStore() {
|
public KeyStore getKeyStore() {
|
||||||
return mKeyStore;
|
return mKeyStore;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.widget.Spinner;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.util.Pair;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import com.afollestad.materialdialogs.MaterialDialog;
|
import com.afollestad.materialdialogs.MaterialDialog;
|
||||||
import com.google.android.flexbox.FlexboxLayout;
|
import com.google.android.flexbox.FlexboxLayout;
|
||||||
@@ -70,6 +71,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import static org.autojs.autojs.apkbuilder.ApkBuilder.TEMPLATE_APK_NAME;
|
import static org.autojs.autojs.apkbuilder.ApkBuilder.TEMPLATE_APK_NAME;
|
||||||
import static org.autojs.autojs.util.StringUtils.key;
|
import static org.autojs.autojs.util.StringUtils.key;
|
||||||
@@ -77,6 +79,8 @@ import static org.autojs.autojs.util.StringUtils.key;
|
|||||||
/**
|
/**
|
||||||
* Created by Stardust on Oct 22, 2017.
|
* Created by Stardust on Oct 22, 2017.
|
||||||
* Modified by SuperMonster003 as of Dec 1, 2023.
|
* Modified by SuperMonster003 as of Dec 1, 2023.
|
||||||
|
*
|
||||||
|
* @noinspection ResultOfMethodCallIgnored
|
||||||
*/
|
*/
|
||||||
public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCallback {
|
public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCallback {
|
||||||
|
|
||||||
@@ -105,14 +109,14 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
|
|
||||||
private static final Map<String, List<String>> LIB_ALIASES = new HashMap<>();
|
private static final Map<String, List<String>> LIB_ALIASES = new HashMap<>();
|
||||||
|
|
||||||
private static final ArrayList<String> SIGNATURE_SCHEMES = new ArrayList<>() {{
|
private static final List<Pair<String, String>> SIGNATURE_SCHEMES = new ArrayList<>() {{
|
||||||
add("V1 + V2");
|
add(new Pair<>("V1 + V2", null));
|
||||||
add("V1 + V3");
|
add(new Pair<>("V1 + V3", null));
|
||||||
add("V1 + V2 + V3");
|
add(new Pair<>("V1 + V2 + V3", null));
|
||||||
add("V1");
|
add(new Pair<>("V1", null));
|
||||||
add("V2 + V3 (Android 7.0+)");
|
add(new Pair<>("V2 + V3", "Android 7.0+"));
|
||||||
add("V2 (Android 7.0+)");
|
add(new Pair<>("V2", "Android 7.0+"));
|
||||||
add("V3 (Android 9.0+)");
|
add(new Pair<>("V3", "Android 9.0+"));
|
||||||
}};
|
}};
|
||||||
|
|
||||||
private final Map<String, Integer> SUPPORTED_PERMISSIONS = new TreeMap<>() {{
|
private final Map<String, Integer> SUPPORTED_PERMISSIONS = new TreeMap<>() {{
|
||||||
@@ -173,16 +177,19 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
ImageView mIconView;
|
ImageView mIconView;
|
||||||
LinearLayout mAppConfigView;
|
LinearLayout mAppConfigView;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private ProjectConfig mProjectConfig;
|
private ProjectConfig mProjectConfig;
|
||||||
|
|
||||||
private MaterialDialog mProgressDialog;
|
private MaterialDialog mProgressDialog;
|
||||||
private String mSource;
|
private String mSource;
|
||||||
private boolean mIsDefaultIcon = true;
|
private boolean mIsDefaultIcon = true;
|
||||||
private boolean mIsProjectLevelBuilding;
|
private boolean mIsProjectLevelBuilding;
|
||||||
private FlexboxLayout mFlexboxAbis;
|
|
||||||
private FlexboxLayout mFlexboxLibs;
|
private FlexboxLayout mFlexboxAbisView;
|
||||||
private Spinner mSignatureSchemes;
|
private FlexboxLayout mFlexboxLibsView;
|
||||||
private Spinner mVerifiedKeyStores;
|
private Spinner mSignatureSchemesView;
|
||||||
private FlexboxLayout mFlexboxPermissions;
|
private Spinner mVerifiedKeyStoresView;
|
||||||
|
private FlexboxLayout mFlexboxPermissionsView;
|
||||||
|
|
||||||
private final ArrayList<String> mInvalidAbis = new ArrayList<>();
|
private final ArrayList<String> mInvalidAbis = new ArrayList<>();
|
||||||
private final ArrayList<String> mUnavailableAbis = new ArrayList<>();
|
private final ArrayList<String> mUnavailableAbis = new ArrayList<>();
|
||||||
@@ -261,35 +268,30 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
|
|
||||||
mAppConfigView = binding.appConfig;
|
mAppConfigView = binding.appConfig;
|
||||||
|
|
||||||
mFlexboxAbis = binding.flexboxAbis;
|
mFlexboxAbisView = binding.flexboxAbis;
|
||||||
initAbisChildren();
|
initAbisChildren();
|
||||||
|
|
||||||
mFlexboxLibs = binding.flexboxLibraries;
|
mFlexboxLibsView = binding.flexboxLibraries;
|
||||||
initLibsChildren();
|
initLibsChildren();
|
||||||
|
|
||||||
mKeyStoreViewModel = new ViewModelProvider(this, new KeyStoreViewModel.Factory(getApplicationContext())).get(KeyStoreViewModel.class);
|
mKeyStoreViewModel = new ViewModelProvider(this, new KeyStoreViewModel.Factory(getApplicationContext())).get(KeyStoreViewModel.class);
|
||||||
mKeyStoreViewModel.updateVerifiedKeyStores();
|
mKeyStoreViewModel.updateVerifiedKeyStores();
|
||||||
|
|
||||||
mSignatureSchemes = binding.spinnerSignatureSchemes;
|
mSignatureSchemesView = binding.spinnerSignatureSchemes;
|
||||||
initSignatureSchemeSpinner();
|
mVerifiedKeyStoresView = binding.spinnerVerifiedKeyStores;
|
||||||
|
mFlexboxPermissionsView = binding.flexboxPermissions;
|
||||||
mVerifiedKeyStores = binding.spinnerVerifiedKeyStores;
|
|
||||||
initVerifiedKeyStoresSpinner();
|
|
||||||
|
|
||||||
mFlexboxPermissions = binding.flexboxPermissions;
|
|
||||||
initPermissionsChildren();
|
|
||||||
|
|
||||||
binding.fab.setOnClickListener(v -> buildApk());
|
binding.fab.setOnClickListener(v -> buildApk());
|
||||||
binding.selectSource.setOnClickListener(v -> selectSourceFilePath());
|
binding.selectSource.setOnClickListener(v -> selectSourceFilePath());
|
||||||
binding.selectOutput.setOnClickListener(v -> selectOutputDirPath());
|
binding.selectOutput.setOnClickListener(v -> selectOutputDirPath());
|
||||||
binding.textAbis.setOnClickListener(v -> toggleAllFlexboxChildren(mFlexboxAbis));
|
binding.textAbis.setOnClickListener(v -> toggleAllFlexboxChildren(mFlexboxAbisView));
|
||||||
binding.textAbis.setOnLongClickListener(v -> {
|
binding.textAbis.setOnLongClickListener(v -> {
|
||||||
syncAbisCheckedStates();
|
syncAbisCheckedStates();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
binding.textLibs.setOnClickListener(v -> toggleAllFlexboxChildren(mFlexboxLibs));
|
binding.textLibs.setOnClickListener(v -> toggleAllFlexboxChildren(mFlexboxLibsView));
|
||||||
binding.manageKeyStore.setOnClickListener(v -> ManageKeyStoreActivity.Companion.startActivity(this));
|
binding.manageKeyStore.setOnClickListener(v -> ManageKeyStoreActivity.Companion.startActivity(this));
|
||||||
binding.textPermissions.setOnClickListener(v -> toggleAllFlexboxChildren(mFlexboxPermissions));
|
binding.textPermissions.setOnClickListener(v -> toggleAllFlexboxChildren(mFlexboxPermissionsView));
|
||||||
|
|
||||||
setToolbarAsBack(R.string.text_build_apk);
|
setToolbarAsBack(R.string.text_build_apk);
|
||||||
mSource = getIntent().getStringExtra(EXTRA_SOURCE);
|
mSource = getIntent().getStringExtra(EXTRA_SOURCE);
|
||||||
@@ -297,6 +299,10 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
setupWithSourceFile(new ScriptFile(mSource));
|
setupWithSourceFile(new ScriptFile(mSource));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initSignatureSchemeSpinner();
|
||||||
|
initVerifiedKeyStoresSpinner();
|
||||||
|
initPermissionsChildren();
|
||||||
|
|
||||||
syncAbisCheckedStates();
|
syncAbisCheckedStates();
|
||||||
syncLibsCheckedStates();
|
syncLibsCheckedStates();
|
||||||
|
|
||||||
@@ -354,7 +360,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
child.setChecked(false);
|
child.setChecked(false);
|
||||||
child.setEnabled(false);
|
child.setEnabled(false);
|
||||||
child.setOnBeingUnavailableListener(this::promptForUnavailability);
|
child.setOnBeingUnavailableListener(this::promptForUnavailability);
|
||||||
mFlexboxAbis.addView(child);
|
mFlexboxAbisView.addView(child);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,8 +403,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
|
|
||||||
private void syncAbisWithDefaultCheckedFilter(Function<String, Boolean> filterForDefaultChecked) {
|
private void syncAbisWithDefaultCheckedFilter(Function<String, Boolean> filterForDefaultChecked) {
|
||||||
List<String> appSupportedAbiList = AndroidUtils.getAppSupportedAbiList();
|
List<String> appSupportedAbiList = AndroidUtils.getAppSupportedAbiList();
|
||||||
for (int i = 0; i < mFlexboxAbis.getChildCount(); i += 1) {
|
for (int i = 0; i < mFlexboxAbisView.getChildCount(); i += 1) {
|
||||||
View child = mFlexboxAbis.getChildAt(i);
|
View child = mFlexboxAbisView.getChildAt(i);
|
||||||
if (child instanceof RoundCheckboxWithText) {
|
if (child instanceof RoundCheckboxWithText) {
|
||||||
CharSequence standardAbi = ((RoundCheckboxWithText) child).getText();
|
CharSequence standardAbi = ((RoundCheckboxWithText) child).getText();
|
||||||
if (standardAbi != null) {
|
if (standardAbi != null) {
|
||||||
@@ -419,7 +425,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
RoundCheckboxWithText child = new RoundCheckboxWithText(this, null);
|
RoundCheckboxWithText child = new RoundCheckboxWithText(this, null);
|
||||||
child.setText(text);
|
child.setText(text);
|
||||||
child.setChecked(false);
|
child.setChecked(false);
|
||||||
mFlexboxLibs.addView(child);
|
mFlexboxLibsView.addView(child);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,8 +438,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
// 创建一个新的副本
|
// 创建一个新的副本
|
||||||
var candidates = new ArrayList<>(configLibs);
|
var candidates = new ArrayList<>(configLibs);
|
||||||
|
|
||||||
for (int i = 0; i < mFlexboxLibs.getChildCount(); i += 1) {
|
for (int i = 0; i < mFlexboxLibsView.getChildCount(); i += 1) {
|
||||||
View child = mFlexboxLibs.getChildAt(i);
|
View child = mFlexboxLibsView.getChildAt(i);
|
||||||
if (child instanceof RoundCheckboxWithText) {
|
if (child instanceof RoundCheckboxWithText) {
|
||||||
CharSequence standardLib = ((RoundCheckboxWithText) child).getText();
|
CharSequence standardLib = ((RoundCheckboxWithText) child).getText();
|
||||||
if (standardLib != null) {
|
if (standardLib != null) {
|
||||||
@@ -447,9 +453,23 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initSignatureSchemeSpinner() {
|
private void initSignatureSchemeSpinner() {
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, SIGNATURE_SCHEMES);
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, SIGNATURE_SCHEMES.stream().map(pair -> {
|
||||||
|
if (pair.second == null || pair.second.isEmpty()) {
|
||||||
|
return pair.first;
|
||||||
|
}
|
||||||
|
return pair.first + " (" + pair.second + ")";
|
||||||
|
}).collect(Collectors.toList()));
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
mSignatureSchemes.setAdapter(adapter);
|
mSignatureSchemesView.setAdapter(adapter);
|
||||||
|
if (mProjectConfig != null) {
|
||||||
|
int initialSelection = IntStream.range(0, SIGNATURE_SCHEMES.size())
|
||||||
|
.filter(i -> SIGNATURE_SCHEMES.get(i).first.equalsIgnoreCase(mProjectConfig.getSignatureScheme()))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(-1);
|
||||||
|
if (initialSelection >= 0) {
|
||||||
|
mSignatureSchemesView.setSelection(initialSelection);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initVerifiedKeyStoresSpinner() {
|
private void initVerifiedKeyStoresSpinner() {
|
||||||
@@ -460,7 +480,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
|
|
||||||
ArrayAdapter<KeyStore> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, verifiedKeyStores);
|
ArrayAdapter<KeyStore> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, verifiedKeyStores);
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
mVerifiedKeyStores.setAdapter(adapter);
|
mVerifiedKeyStoresView.setAdapter(adapter);
|
||||||
|
|
||||||
mKeyStoreViewModel.getVerifiedKeyStores().observe(this, keyStores -> {
|
mKeyStoreViewModel.getVerifiedKeyStores().observe(this, keyStores -> {
|
||||||
// 清空现有的选项,但保留第一个元素,即默认密钥库
|
// 清空现有的选项,但保留第一个元素,即默认密钥库
|
||||||
@@ -487,7 +507,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
int marginInPixels = (int) (8 * getResources().getDisplayMetrics().density);
|
int marginInPixels = (int) (8 * getResources().getDisplayMetrics().density);
|
||||||
checkBox.setPadding(marginInPixels, 0, 0, 0);
|
checkBox.setPadding(marginInPixels, 0, 0, 0);
|
||||||
checkBox.setChecked(false);
|
checkBox.setChecked(false);
|
||||||
mFlexboxPermissions.addView(checkBox);
|
mFlexboxPermissionsView.addView(checkBox);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,8 +664,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkAbis() {
|
private boolean checkAbis() {
|
||||||
for (int i = 0; i < mFlexboxAbis.getChildCount(); i += 1) {
|
for (int i = 0; i < mFlexboxAbisView.getChildCount(); i += 1) {
|
||||||
View child = mFlexboxAbis.getChildAt(i);
|
View child = mFlexboxAbisView.getChildAt(i);
|
||||||
if (child instanceof RoundCheckboxWithText) {
|
if (child instanceof RoundCheckboxWithText) {
|
||||||
if (((RoundCheckboxWithText) child).isChecked() && child.isEnabled()) {
|
if (((RoundCheckboxWithText) child).isChecked() && child.isEnabled()) {
|
||||||
return true;
|
return true;
|
||||||
@@ -774,11 +794,11 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
private void doBuildingApk() {
|
private void doBuildingApk() {
|
||||||
ProjectConfig projectConfig = determineProjectConfig();
|
ProjectConfig projectConfig = determineProjectConfig();
|
||||||
File tmpDir = new File(getCacheDir(), "build/");
|
File buildPath = new File(getCacheDir(), "build/");
|
||||||
File outApk = new File(mOutputPathView.getText().toString(),
|
File outApk = new File(mOutputPathView.getText().toString(),
|
||||||
String.format("%s_v%s.apk", projectConfig.getName(), projectConfig.getVersionName()));
|
String.format("%s_v%s.apk", projectConfig.getName(), projectConfig.getVersionName()));
|
||||||
showProgressDialog();
|
showProgressDialog();
|
||||||
Observable.fromCallable(() -> callApkBuilder(tmpDir, outApk, projectConfig))
|
Observable.fromCallable(() -> callApkBuilder(buildPath, outApk, projectConfig))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(apkBuilder -> {
|
.subscribe(apkBuilder -> {
|
||||||
@@ -791,9 +811,9 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ProjectConfig determineProjectConfig() {
|
private ProjectConfig determineProjectConfig() {
|
||||||
ArrayList<String> abis = collectCheckedItems(mFlexboxAbis);
|
ArrayList<String> abis = collectCheckedItems(mFlexboxAbisView);
|
||||||
ArrayList<String> libs = collectCheckedItems(mFlexboxLibs);
|
ArrayList<String> libs = collectCheckedItems(mFlexboxLibsView);
|
||||||
ArrayList<String> permissions = collectCheckedItems(mFlexboxPermissions);
|
ArrayList<String> permissions = collectCheckedItems(mFlexboxPermissionsView);
|
||||||
|
|
||||||
ProjectConfig projectConfig;
|
ProjectConfig projectConfig;
|
||||||
if (mProjectConfig != null) {
|
if (mProjectConfig != null) {
|
||||||
@@ -814,8 +834,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
return projectConfig
|
return projectConfig
|
||||||
.setAbis(abis)
|
.setAbis(abis)
|
||||||
.setLibs(libs)
|
.setLibs(libs)
|
||||||
.setKeyStore(mVerifiedKeyStores.getSelectedItemPosition() > 0 ? (KeyStore) mVerifiedKeyStores.getSelectedItem() : null)
|
.setKeyStore(mVerifiedKeyStoresView.getSelectedItemPosition() > 0 ? (KeyStore) mVerifiedKeyStoresView.getSelectedItem() : null)
|
||||||
.setSignatureScheme(mSignatureSchemes.getSelectedItem().toString())
|
.setSignatureScheme(mSignatureSchemesView.getSelectedItem().toString())
|
||||||
.setPermissions(permissions);
|
.setPermissions(permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -844,9 +864,9 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
return libs;
|
return libs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ApkBuilder callApkBuilder(File tmpDir, File outApk, ProjectConfig projectConfig) throws Exception {
|
private ApkBuilder callApkBuilder(File buildPath, File outApk, ProjectConfig projectConfig) throws Exception {
|
||||||
InputStream templateApk = getAssets().open(TEMPLATE_APK_NAME);
|
InputStream templateApk = getAssets().open(TEMPLATE_APK_NAME);
|
||||||
return new ApkBuilder(templateApk, outApk, tmpDir.getPath())
|
return new ApkBuilder(templateApk, outApk, buildPath.getPath())
|
||||||
.setProgressCallback(BuildActivity.this)
|
.setProgressCallback(BuildActivity.this)
|
||||||
.prepare()
|
.prepare()
|
||||||
.withConfig(projectConfig)
|
.withConfig(projectConfig)
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ import com.google.gson.JsonDeserializer
|
|||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import org.autojs.autojs.annotation.DeserializedMethodName
|
||||||
import org.autojs.autojs.annotation.SerializedNameCompatible
|
import org.autojs.autojs.annotation.SerializedNameCompatible
|
||||||
import org.json.JSONArray
|
import org.json.JSONTokener
|
||||||
import org.json.JSONObject
|
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,7 +85,34 @@ object JsonUtils {
|
|||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isValidJson(json: String): Boolean = runCatching { JSONObject(json) }.isSuccess || runCatching { JSONArray(json) }.isSuccess
|
/**
|
||||||
|
* This method checks whether the given string is a valid JSON format.
|
||||||
|
*
|
||||||
|
* This method supports the following common JSON formats:
|
||||||
|
* - Objects (e.g., `{"key": "value"}`)
|
||||||
|
* - Arrays (e.g., `[1, 2, 3]`)
|
||||||
|
* - Single values (e.g., `"string"`, `123`, `true`, `null`)
|
||||||
|
*
|
||||||
|
* Note:
|
||||||
|
* 1. Empty strings or strings containing only whitespace are considered invalid JSON.
|
||||||
|
* 2. If the JSON contains comments (e.g., `// comment` or `/* multi-line comment */`),
|
||||||
|
* this method will consider it invalid. Because comments are not allowed in strict JSON standards.
|
||||||
|
*
|
||||||
|
* zh-CN:
|
||||||
|
*
|
||||||
|
* 判断给定的字符串是否是有效的 JSON 格式.
|
||||||
|
*
|
||||||
|
* 支持以下几种常见 JSON 格式:
|
||||||
|
* - 对象 (例如: `{"key": "value"}`)
|
||||||
|
* - 数组 (例如: `[1, 2, 3]`)
|
||||||
|
* - 单一值 (例如: `"string"`, `123`, `true`, `null`)
|
||||||
|
*
|
||||||
|
* 注意:
|
||||||
|
* 1. 空字符串或仅包含空白字符的字符串被视为无效的 JSON.
|
||||||
|
* 2. 如果 JSON 中包含注释 (例如: `// 注释` 或 `/* 多行注释 */`), 此方法会判断为无效. 因为注释不符合严格的 JSON 规范.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun isValidJson(json: String) = json.isNotBlank() && runCatching { JSONTokener(json).nextValue() }.isSuccess
|
||||||
|
|
||||||
class FuzzyDeserializer<T> : JsonDeserializer<T> {
|
class FuzzyDeserializer<T> : JsonDeserializer<T> {
|
||||||
|
|
||||||
@@ -99,7 +127,6 @@ object JsonUtils {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw RuntimeException("Failed to create an instance of ${clazz.name}", e)
|
throw RuntimeException("Failed to create an instance of ${clazz.name}", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json is JsonObject) {
|
if (json is JsonObject) {
|
||||||
clazz.declaredFields.forEach { processField(it, json, instance, context) }
|
clazz.declaredFields.forEach { processField(it, json, instance, context) }
|
||||||
}
|
}
|
||||||
@@ -111,6 +138,7 @@ object JsonUtils {
|
|||||||
|
|
||||||
val serializedNameAnnotation = field.getAnnotation(SerializedName::class.java)
|
val serializedNameAnnotation = field.getAnnotation(SerializedName::class.java)
|
||||||
val compatibleAnnotation = field.getAnnotation(SerializedNameCompatible::class.java)
|
val compatibleAnnotation = field.getAnnotation(SerializedNameCompatible::class.java)
|
||||||
|
val deserializedAnnotation = field.getAnnotation(DeserializedMethodName::class.java)
|
||||||
|
|
||||||
val sanitizedPrimaryName = sanitizeKey(serializedNameAnnotation?.value ?: field.name)
|
val sanitizedPrimaryName = sanitizeKey(serializedNameAnnotation?.value ?: field.name)
|
||||||
val sanitizedAlternateNames = serializedNameAnnotation?.alternate?.map { sanitizeKey(it) } ?: emptyList()
|
val sanitizedAlternateNames = serializedNameAnnotation?.alternate?.map { sanitizeKey(it) } ?: emptyList()
|
||||||
@@ -118,13 +146,16 @@ object JsonUtils {
|
|||||||
|
|
||||||
val serializedNames: List<Pair<String, Boolean>> = (sanitizedAlternateNames + sanitizedPrimaryName).map { it to false } + sanitizedCompatibleNames
|
val serializedNames: List<Pair<String, Boolean>> = (sanitizedAlternateNames + sanitizedPrimaryName).map { it to false } + sanitizedCompatibleNames
|
||||||
|
|
||||||
for ((jsonKey, jsonValue) in json.entrySet()) {
|
json.entrySet().forEach { (jsonKey, jsonValue) ->
|
||||||
val sanitizedJsonKey = sanitizeKey(jsonKey)
|
val sanitizedJsonKey = sanitizeKey(jsonKey)
|
||||||
for (pair in serializedNames) {
|
serializedNames.forEach { pair ->
|
||||||
val (serializedKey, isReversed) = pair
|
val (serializedKey, isReversed) = pair
|
||||||
if (sanitizedJsonKey == serializedKey) {
|
if (sanitizedJsonKey == serializedKey) {
|
||||||
when (field.type) {
|
when {
|
||||||
Boolean::class.javaPrimitiveType, Boolean::class.java -> {
|
deserializedAnnotation != null -> {
|
||||||
|
handleDeserializedMethod(field, instance, jsonValue, deserializedAnnotation)
|
||||||
|
}
|
||||||
|
field.type == Boolean::class.javaPrimitiveType || field.type == Boolean::class.java -> {
|
||||||
if (jsonValue.isJsonPrimitive && jsonValue.asJsonPrimitive.isBoolean) {
|
if (jsonValue.isJsonPrimitive && jsonValue.asJsonPrimitive.isBoolean) {
|
||||||
val value = jsonValue.asBoolean
|
val value = jsonValue.asBoolean
|
||||||
field.set(instance, if (isReversed) !value else value)
|
field.set(instance, if (isReversed) !value else value)
|
||||||
@@ -137,12 +168,33 @@ object JsonUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return@processField
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleDeserializedMethod(field: Field, instance: T, jsonValue: JsonElement, annotation: DeserializedMethodName) {
|
||||||
|
require(jsonValue.isJsonPrimitive && jsonValue.asJsonPrimitive.isString) {
|
||||||
|
"Field with @DeserializedMethodName must map to a JSON string."
|
||||||
|
}
|
||||||
|
val methodName = annotation.method
|
||||||
|
val methodInput = jsonValue.asString
|
||||||
|
val parameterTypes = annotation.parameterTypes.map { it.java }.toTypedArray()
|
||||||
|
val method = runCatching {
|
||||||
|
instance!!::class.java.getMethod(methodName, *parameterTypes)
|
||||||
|
}.getOrElse { e ->
|
||||||
|
throw RuntimeException("Method $methodName with parameter types ${parameterTypes.contentToString()} not found in ${instance!!::class.java.name}", e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val isStatic = Modifier.isStatic(method.modifiers)
|
||||||
|
val result = method.invoke(instance.takeUnless { isStatic }, methodInput)
|
||||||
|
field.set(instance, result)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw RuntimeException("Failed to invoke method $methodName on ${field.name}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun sanitizeKey(key: String): String {
|
private fun sanitizeKey(key: String): String {
|
||||||
return key.replace(Regex("[^a-zA-Z0-9]"), "").lowercase()
|
return key.replace(Regex("[^a-zA-Z0-9]"), "").lowercase()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Mon Jan 20 15:15:12 CST 2025
|
#Wed Feb 05 20:51:07 CST 2025
|
||||||
BUILD_TIME=1737357312345
|
BUILD_TIME=1738759867266
|
||||||
COMPILE_SDK_VERSION=34
|
COMPILE_SDK_VERSION=34
|
||||||
JAVA_VERSION=23
|
JAVA_VERSION=23
|
||||||
JAVA_VERSION_MIN_RADICAL=0
|
JAVA_VERSION_MIN_RADICAL=0
|
||||||
@@ -17,6 +17,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
|
|||||||
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
||||||
TARGET_SDK_VERSION=34
|
TARGET_SDK_VERSION=34
|
||||||
TARGET_SDK_VERSION_INRT=29
|
TARGET_SDK_VERSION_INRT=29
|
||||||
VERSION_BUILD=2979
|
VERSION_BUILD=2982
|
||||||
VERSION_NAME=6.6.2 Alpha2
|
VERSION_NAME=6.6.2 Alpha2
|
||||||
VSCODE_EXT_REQUIRED_VERSION=1.0.8
|
VSCODE_EXT_REQUIRED_VERSION=1.0.8
|
||||||
|
|||||||
Reference in New Issue
Block a user