From 08c64bf248ba3ede92fe59e43ac3b04cd971283f Mon Sep 17 00:00:00 2001 From: SuperMonster003 Date: Mon, 20 Jan 2025 20:21:45 +0800 Subject: [PATCH] =?UTF-8?q?6.6.2=20-=20Alpha2=20-=20project.json=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=80=89=E9=A1=B9=E5=90=8D=E7=A7=B0=E5=AE=BD?= =?UTF-8?q?=E6=9D=BE=E5=8C=B9=E9=85=8D=E5=8F=8A=E5=88=AB=E5=90=8D=E5=85=BC?= =?UTF-8?q?=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changelog/lang_zh-Hans.json | 1 + .../autojs/autojs/apkbuilder/ApkBuilder.kt | 15 +- .../inrt/launch/AssetsProjectLauncher.kt | 4 +- .../autojs/model/project/ProjectTemplate.java | 2 +- .../org/autojs/autojs/project/BuildInfo.java | 31 ++-- .../autojs/autojs/project/ProjectConfig.java | 153 +++++++++++++----- .../autojs/project/ProjectLauncher.java | 2 +- .../org/autojs/autojs/project/ScriptConfig.kt | 12 +- .../autojs/ui/project/BuildActivity.java | 4 +- .../ui/project/ProjectConfigActivity.kt | 4 +- .../java/org/autojs/autojs/util/JsonUtils.kt | 73 ++++++++- version.properties | 4 +- 12 files changed, 230 insertions(+), 75 deletions(-) diff --git a/.changelog/lang_zh-Hans.json b/.changelog/lang_zh-Hans.json index 773207e1..15211f52 100644 --- a/.changelog/lang_zh-Hans.json +++ b/.changelog/lang_zh-Hans.json @@ -15,6 +15,7 @@ "打包页面支持 Pinyin 库选项", "打包应用主活动页面优化状态栏背景及文字颜色", "脚本项目配置文件 project.json 支持更多打包选项 _[`issue #305`](http://issues.autojs6.com/305)_ _[`issue #306`](http://issues.autojs6.com/306)_", + "脚本项目配置文件 project.json 支持选项名称宽松匹配及别名兼容", "APK 文件类型信息对话框增加文件大小与签名方案信息", "APK 文件类型信息对话框增加点击监听器支持文本复制与应用详情跳转", "尝试恢复 com.stardust 前缀包以便提升代码兼容性 _[`issue #290`](http://issues.autojs6.com/290)_", diff --git a/app/src/main/java/org/autojs/autojs/apkbuilder/ApkBuilder.kt b/app/src/main/java/org/autojs/autojs/apkbuilder/ApkBuilder.kt index 4b9d2a95..06ba38d1 100644 --- a/app/src/main/java/org/autojs/autojs/apkbuilder/ApkBuilder.kt +++ b/app/src/main/java/org/autojs/autojs/apkbuilder/ApkBuilder.kt @@ -17,6 +17,7 @@ import org.autojs.autojs.engine.encryption.AdvancedEncryptionStandard import org.autojs.autojs.pio.PFiles import org.autojs.autojs.project.BuildInfo import org.autojs.autojs.project.ProjectConfig +import org.autojs.autojs.project.ProjectConfig.DEFAULT_MAIN_SCRIPT_FILE_NAME import org.autojs.autojs.script.EncryptedScriptFileHeader.writeHeader import org.autojs.autojs.script.JavaScriptFileSource import org.autojs.autojs.util.FileUtils.TYPE.JAVASCRIPT @@ -105,7 +106,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File srcChildFile.copyTo(File(destDirFile, srcChildFile.name), true) } } else { - if (!mProjectConfig.ignoredDirs.contains(srcChildFile)) { + if (!mProjectConfig.excludedDirs.contains(srcChildFile)) { copyDir(srcChildFile, PFiles.join(relativeDestPath, srcChildFile.name + File.separator)) } } @@ -186,7 +187,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File PFiles.write(ProjectConfig.configFileOfDir(config.sourcePath), it.toJson()) } } ?: ProjectConfig() - .setMainScriptFile("main.js") + .setMainScriptFileName(DEFAULT_MAIN_SCRIPT_FILE_NAME) .setName(config.name) .setPackageName(config.packageName) .setVersionName(config.versionName) @@ -199,7 +200,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File } projectConfig.run { - mKey = MD5Utils.md5(packageName + versionName + mainScriptFile) + mKey = MD5Utils.md5(packageName + versionName + mainScriptFileName) mInitVector = MD5Utils.md5(buildInfo.buildId + name).substring(0, 16) Libs.entries.forEach { entry -> if (config.libs.contains(entry.label)) { @@ -281,10 +282,10 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File val signer = ApkSigner(outApkFile, tmpOutputApk) signer.useDefaultSignatureVersion = false - signer.v1SigningEnabled = mProjectConfig.signatureSchemes.contains("V1") - signer.v2SigningEnabled = mProjectConfig.signatureSchemes.contains("V2") - signer.v3SigningEnabled = mProjectConfig.signatureSchemes.contains("V3") - signer.v4SigningEnabled = mProjectConfig.signatureSchemes.contains("V4") + signer.v1SigningEnabled = mProjectConfig.signatureScheme.contains("V1") + signer.v2SigningEnabled = mProjectConfig.signatureScheme.contains("V2") + signer.v3SigningEnabled = mProjectConfig.signatureScheme.contains("V3") + signer.v4SigningEnabled = mProjectConfig.signatureScheme.contains("V4") var keyStoreFile = defaultKeyStoreFile var password = "AutoJs6" diff --git a/app/src/main/java/org/autojs/autojs/inrt/launch/AssetsProjectLauncher.kt b/app/src/main/java/org/autojs/autojs/inrt/launch/AssetsProjectLauncher.kt index 8e8882ba..28f07fa4 100644 --- a/app/src/main/java/org/autojs/autojs/inrt/launch/AssetsProjectLauncher.kt +++ b/app/src/main/java/org/autojs/autojs/inrt/launch/AssetsProjectLauncher.kt @@ -26,7 +26,7 @@ import java.io.IOException open class AssetsProjectLauncher(private val mAssetsProjectDir: String, private val mActivity: Context) { private val mProjectDir: String = File(mActivity.filesDir, "project/").path private val mProjectConfig: ProjectConfig = ProjectConfig.fromAssets(mActivity, ProjectConfig.configFileOfDir(mAssetsProjectDir)) - private val mMainScriptFile: File = File(mProjectDir, mProjectConfig.mainScriptFile) + private val mMainScriptFile: File = File(mProjectDir, mProjectConfig.mainScriptFileName) private val mHandler: Handler = Handler(Looper.getMainLooper()) private var mScriptExecution: ScriptExecution? = null @@ -95,7 +95,7 @@ open class AssetsProjectLauncher(private val mAssetsProjectDir: String, private } private fun initKey(projectConfig: ProjectConfig) { - val key = MD5.md5(projectConfig.packageName + projectConfig.versionName + projectConfig.mainScriptFile) + val key = MD5.md5(projectConfig.packageName + projectConfig.versionName + projectConfig.mainScriptFileName) val vec = MD5.md5(projectConfig.buildInfo.buildId + projectConfig.name).substring(0, 16) try { val fieldKey = ScriptEncryption::class.java.getDeclaredField("mKey") diff --git a/app/src/main/java/org/autojs/autojs/model/project/ProjectTemplate.java b/app/src/main/java/org/autojs/autojs/model/project/ProjectTemplate.java index 1aad8b1e..fd71bfdc 100644 --- a/app/src/main/java/org/autojs/autojs/model/project/ProjectTemplate.java +++ b/app/src/main/java/org/autojs/autojs/model/project/ProjectTemplate.java @@ -26,7 +26,7 @@ public class ProjectTemplate { return Observable.fromCallable(() -> { mProjectDir.mkdirs(); PFiles.write(ProjectConfig.configFileOfDir(mProjectDir.getPath()), mProjectConfig.toJson()); - new File(mProjectDir, mProjectConfig.getMainScriptFile()).createNewFile(); + new File(mProjectDir, mProjectConfig.getMainScriptFileName()).createNewFile(); return mProjectDir; }) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/org/autojs/autojs/project/BuildInfo.java b/app/src/main/java/org/autojs/autojs/project/BuildInfo.java index ca9e9687..4e53e636 100644 --- a/app/src/main/java/org/autojs/autojs/project/BuildInfo.java +++ b/app/src/main/java/org/autojs/autojs/project/BuildInfo.java @@ -1,20 +1,33 @@ package org.autojs.autojs.project; import com.google.gson.annotations.SerializedName; +import org.autojs.autojs.annotation.SerializedNameCompatible; +import org.autojs.autojs.annotation.SerializedNameCompatible.With; import java.util.zip.CRC32; public class BuildInfo { - @SerializedName(value = "buildTime", alternate = {"build_time"}) - private long mBuildTime; - - @SerializedName(value = "buildId", alternate = {"build_id"}) + @SerializedName(value = "id") + @SerializedNameCompatible(with = {@With(value = "build_id", target = {"AutoJs4", "AutoX"})}) private String mBuildId; - @SerializedName(value = "buildNumber", alternate = {"build_number"}) + @SerializedName(value = "number") + @SerializedNameCompatible(with = {@With(value = "build_number", target = {"AutoJs4", "AutoX"})}) private long mBuildNumber; + @SerializedName(value = "time") + @SerializedNameCompatible(with = {@With(value = "build_time", target = {"AutoJs4", "AutoX"})}) + private long mBuildTime; + + public String getBuildId() { + return mBuildId; + } + + public void setBuildId(String buildId) { + mBuildId = buildId; + } + public long getBuildNumber() { return mBuildNumber; } @@ -31,14 +44,6 @@ public class BuildInfo { mBuildTime = buildTime; } - public String getBuildId() { - return mBuildId; - } - - public void setBuildId(String buildId) { - mBuildId = buildId; - } - public static BuildInfo generate(long buildNumber) { BuildInfo info = new BuildInfo(); info.setBuildNumber(buildNumber); diff --git a/app/src/main/java/org/autojs/autojs/project/ProjectConfig.java b/app/src/main/java/org/autojs/autojs/project/ProjectConfig.java index 01851bed..d6cf7189 100644 --- a/app/src/main/java/org/autojs/autojs/project/ProjectConfig.java +++ b/app/src/main/java/org/autojs/autojs/project/ProjectConfig.java @@ -9,6 +9,8 @@ import androidx.annotation.Nullable; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.annotations.SerializedName; +import org.autojs.autojs.annotation.SerializedNameCompatible; +import org.autojs.autojs.annotation.SerializedNameCompatible.With; import org.autojs.autojs.apkbuilder.keystore.KeyStore; import org.autojs.autojs.model.explorer.ExplorerPage; import org.autojs.autojs.pio.PFiles; @@ -21,8 +23,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -34,65 +38,123 @@ public class ProjectConfig { public static final String CONFIG_FILE_NAME = "project.json"; + public static final String DEFAULT_MAIN_SCRIPT_FILE_NAME = "main.js"; + private static final Gson sGson = new GsonBuilder() + .registerTypeAdapter(ProjectConfig.class, new JsonUtils.FuzzyDeserializer()) + .registerTypeAdapter(LaunchConfig.class, new JsonUtils.FuzzyDeserializer()) + .registerTypeAdapter(ScriptConfig.class, new JsonUtils.FuzzyDeserializer()) + .registerTypeAdapter(BuildInfo.class, new JsonUtils.FuzzyDeserializer()) .setPrettyPrinting() .create(); @SerializedName("name") + @SerializedNameCompatible(with = {@With(value = "projectName")}) private String mName; @SerializedName("versionName") + @SerializedNameCompatible(with = {@With(value = "version")}) private String mVersionName; @SerializedName("versionCode") private int mVersionCode = -1; @SerializedName("packageName") + @SerializedNameCompatible(with = {@With(value = "package")}) private String mPackageName; @SerializedName("main") - private String mMainScriptFile; + @SerializedNameCompatible(with = { + @With(value = "mainName"), + @With(value = "mainScript"), + @With(value = "mainScriptName"), + @With(value = "mainScriptFile"), + @With(value = "mainScriptFileName"), + @With(value = "mainFile"), + @With(value = "mainFileName"), + }) + private String mMainScriptFileName; @Nullable - @SerializedName(value = "assets", alternate = {"asset", "assetList"}) + @SerializedName(value = "assets") + @SerializedNameCompatible(with = { + @With(value = "asset"), + @With(value = "assetList"), + }) private List mAssets = new ArrayList<>(); @SerializedName("launchConfig") + @SerializedNameCompatible(with = {@With(value = "launch")}) private LaunchConfig mLaunchConfig = new LaunchConfig(); @SerializedName("build") + @SerializedNameCompatible(with = {@With(value = "buildInfo")}) private BuildInfo mBuildInfo = new BuildInfo(); @SerializedName("icon") + @SerializedNameCompatible(with = {@With(value = "iconPath")}) private String mIconPath; private transient Callable mIconBitmapGetter; @Nullable - @SerializedName(value = "abis", alternate = {"abi", "abiList"}) + @SerializedName(value = "abis") + @SerializedNameCompatible(with = { + @With(value = "abi"), + @With(value = "abiList"), + }) private List mAbis = new ArrayList<>(); @Nullable - @SerializedName(value = "libs", alternate = {"lib", "libList"}) + @SerializedName(value = "libs") + @SerializedNameCompatible(with = { + @With(value = "lib"), + @With(value = "libList"), + }) private List mLibs = new ArrayList<>(); @SerializedName("permissions") + @SerializedNameCompatible(with = { + @With(value = "permission"), + @With(value = "permissionList"), + }) private List mPermissions = new ArrayList<>(); - @SerializedName("signatureSchemes") - private String mSignatureSchemes = "V1 + V2"; + @SerializedName("signatureScheme") + @SerializedNameCompatible(with = { + @With(value = "signatureSchemes"), + @With(value = "signature"), + }) + private String mSignatureScheme = "V1 + V2"; @Nullable private transient KeyStore mKeyStore = null; - @SerializedName("scripts") + @SerializedName("scriptConfigs") + @SerializedNameCompatible(with = { + @With(value = "scriptsConfigs"), + @With(value = "scriptsConfig"), + @With(value = "scriptConfig"), + @With(value = "scripts", target = {"AutoJs4", "AutoX"}), + }) private final Map mScriptConfigs = new HashMap<>(); - @SerializedName(value = "useFeatures", alternate = {"useFeature", "useFeatureList"}) + @SerializedName(value = "useFeatures") + @SerializedNameCompatible(with = { + @With(value = "useFeature"), + @With(value = "useFeatureList"), + @With(value = "features"), + @With(value = "feature"), + @With(value = "featureList"), + }) private List mFeatures = new ArrayList<>(); - @SerializedName("ignoredDirs") - private final List mIgnoredDirs = new ArrayList<>(); + @SerializedName("excludedDirs") + @SerializedNameCompatible(with = { + @With(value = "ignoredDirs", target = {"AutoJs4", "AutoX"}), + @With(value = "ignore", target = {"Unknown"}), + }) + private final List mExcludedDirs = new ArrayList<>(); @Nullable private transient String mSourcePath = null; @@ -118,7 +180,7 @@ public class ProjectConfig { if (TextUtils.isEmpty(config.getVersionName())) { return false; } - if (TextUtils.isEmpty(config.getMainScriptFile())) { + if (TextUtils.isEmpty(config.getMainScriptFileName())) { return false; } return config.getVersionCode() != -1; @@ -179,7 +241,7 @@ public class ProjectConfig { setFieldIfMatches(versionNamePattern, s, projectConfig::setVersionName); setFieldForIntIfMatches(versionCodePattern, s, projectConfig::setVersionCode); setFieldIfMatches(packageNamePattern, s, projectConfig::setPackageName); - setFieldIfMatches(mainPattern, s, projectConfig::setMainScriptFile); + setFieldIfMatches(mainPattern, s, projectConfig::setMainScriptFileName); setFieldIfMatches(iconPattern, s, projectConfig::setIconPath); setListIfMatches(assetsPattern, s, projectConfig::setAssets); @@ -340,13 +402,21 @@ public class ProjectConfig { return this; } - @NonNull + /** + * @deprecated This method is deprecated. Use {@link #getMainScriptFileName()} instead. + */ + @Deprecated public String getMainScriptFile() { - return mMainScriptFile != null ? mMainScriptFile : "main.js"; + return getMainScriptFileName(); } - public ProjectConfig setMainScriptFile(String mainScriptFile) { - mMainScriptFile = mainScriptFile; + @NonNull + public String getMainScriptFileName() { + return mMainScriptFileName != null ? mMainScriptFileName : DEFAULT_MAIN_SCRIPT_FILE_NAME; + } + + public ProjectConfig setMainScriptFileName(String mainScriptFileName) { + mMainScriptFileName = mainScriptFileName; return this; } @@ -439,40 +509,36 @@ public class ProjectConfig { @NonNull public List getFeatures() { - if (mFeatures == null) { - mFeatures = Collections.emptyList(); - } return mFeatures; } - public void setFeatures(List features) { + public void setFeatures(@NonNull List features) { mFeatures = features; } public ScriptConfig getScriptConfig(String path) { - ScriptConfig config = mScriptConfigs.get(path); - if (config == null) { - config = new ScriptConfig(); - } - if (mFeatures.isEmpty()) { - return config; - } - ArrayList features = new ArrayList<>(config.getFeatures()); - for (String feature : mFeatures) { - if (!features.contains(feature)) { - features.add(feature); - } - } - config.setFeatures(features); - return config; + ScriptConfig scriptConfig = Objects.requireNonNull(mScriptConfigs.getOrDefault(path, new ScriptConfig())); + List combinedFeatures = getCombinedFeatures(scriptConfig); + scriptConfig.setFeatures(combinedFeatures); + return scriptConfig; } - public List getIgnoredDirs() { - return mIgnoredDirs; + @NotNull + public ArrayList getCombinedFeatures(ScriptConfig scriptConfig) { + return new ArrayList<>( + new HashSet<>() {{ + addAll(scriptConfig.getFeatures()); + addAll(mFeatures); + }} + ); } - public ProjectConfig ignoredDir(File ignoredDir) { - mIgnoredDirs.add(ignoredDir); + public List getExcludedDirs() { + return mExcludedDirs; + } + + public ProjectConfig excludeDir(File dirToExclude) { + mExcludedDirs.add(dirToExclude); return this; } @@ -495,12 +561,12 @@ public class ProjectConfig { return this; } - public String getSignatureSchemes() { - return mSignatureSchemes; + public String getSignatureScheme() { + return mSignatureScheme; } - public ProjectConfig setSignatureSchemes(String signatureSchemes) { - mSignatureSchemes = signatureSchemes; + public ProjectConfig setSignatureScheme(String signatureScheme) { + mSignatureScheme = signatureScheme; return this; } @@ -513,4 +579,5 @@ public class ProjectConfig { mKeyStore = keyStore; return this; } + } diff --git a/app/src/main/java/org/autojs/autojs/project/ProjectLauncher.java b/app/src/main/java/org/autojs/autojs/project/ProjectLauncher.java index 172ff813..f97b9a60 100644 --- a/app/src/main/java/org/autojs/autojs/project/ProjectLauncher.java +++ b/app/src/main/java/org/autojs/autojs/project/ProjectLauncher.java @@ -23,7 +23,7 @@ public class ProjectLauncher { public ProjectLauncher(@NonNull String projectDir) { mProjectDir = projectDir; mProjectConfig = ProjectConfig.fromProjectDir(projectDir); - mMainScriptFile = new File(mProjectDir, mProjectConfig == null ? "main.js" : mProjectConfig.getMainScriptFile()); + mMainScriptFile = new File(mProjectDir, mProjectConfig == null ? ProjectConfig.DEFAULT_MAIN_SCRIPT_FILE_NAME : mProjectConfig.getMainScriptFileName()); } public void launch(ScriptEngineService service) { diff --git a/app/src/main/java/org/autojs/autojs/project/ScriptConfig.kt b/app/src/main/java/org/autojs/autojs/project/ScriptConfig.kt index 7fcd80ba..3678b237 100644 --- a/app/src/main/java/org/autojs/autojs/project/ScriptConfig.kt +++ b/app/src/main/java/org/autojs/autojs/project/ScriptConfig.kt @@ -1,9 +1,19 @@ package org.autojs.autojs.project import com.google.gson.annotations.SerializedName +import org.autojs.autojs.annotation.SerializedNameCompatible +import org.autojs.autojs.annotation.SerializedNameCompatible.With data class ScriptConfig @JvmOverloads constructor( - @SerializedName("useFeatures") var features: List = emptyList(), + @SerializedName("useFeatures") + @field:SerializedNameCompatible( + With(value = "useFeature"), + With(value = "useFeatureList"), + With(value = "features"), + With(value = "feature"), + With(value = "featureList"), + ) + var features: List = emptyList(), ) { fun hasFeature(feature: String) = features.contains(feature) diff --git a/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java b/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java index 53821d5f..aad29a68 100644 --- a/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java @@ -798,7 +798,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa ProjectConfig projectConfig; if (mProjectConfig != null) { projectConfig = mProjectConfig - .ignoredDir(new File(mSource, mProjectConfig.getBuildDir())) + .excludeDir(new File(mSource, mProjectConfig.getBuildDir())) .setSourcePath(mSource) .setIconPath(mProjectConfig.getIconPath() == null ? null : new File(mSource, mProjectConfig.getIconPath()).getPath()); } else { @@ -815,7 +815,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa .setAbis(abis) .setLibs(libs) .setKeyStore(mVerifiedKeyStores.getSelectedItemPosition() > 0 ? (KeyStore) mVerifiedKeyStores.getSelectedItem() : null) - .setSignatureSchemes(mSignatureSchemes.getSelectedItem().toString()) + .setSignatureScheme(mSignatureSchemes.getSelectedItem().toString()) .setPermissions(permissions); } diff --git a/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.kt b/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.kt index 13fd547e..e327fc68 100644 --- a/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.kt +++ b/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.kt @@ -125,7 +125,7 @@ class ProjectConfigActivity : BaseActivity() { mVersionCode.setText(config.versionCode.coerceAtLeast(0).toString()) mPackageName.setText(config.packageName) mVersionName.setText(config.versionName) - mMainFileName.setText(config.mainScriptFile) + mMainFileName.setText(config.mainScriptFileName) mProjectLocationWrapper.visibility = View.GONE config.iconPath?.let { icon -> File(mDirectory, icon).takeIf { it.exists() }?.let { iconFile -> @@ -197,7 +197,7 @@ class ProjectConfigActivity : BaseActivity() { it.name = mAppName.text.toString() it.versionCode = mVersionCode.text.toString().toInt() it.versionName = mVersionName.text.toString() - it.mainScriptFile = mMainFileName.text.toString() + it.mainScriptFileName = mMainFileName.text.toString() it.packageName = mPackageName.text.toString() } if (mNewProject) { diff --git a/app/src/main/java/org/autojs/autojs/util/JsonUtils.kt b/app/src/main/java/org/autojs/autojs/util/JsonUtils.kt index 7f055fd9..57193021 100644 --- a/app/src/main/java/org/autojs/autojs/util/JsonUtils.kt +++ b/app/src/main/java/org/autojs/autojs/util/JsonUtils.kt @@ -1,7 +1,15 @@ package org.autojs.autojs.util +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.annotations.SerializedName +import org.autojs.autojs.annotation.SerializedNameCompatible import org.json.JSONArray import org.json.JSONObject +import java.lang.reflect.Field +import java.lang.reflect.Type /** * Created by SuperMonster003 on Nov 20, 2024. @@ -70,7 +78,7 @@ object JsonUtils { closeBraces++ } while (closeBraces > openBraces) { - json = '{' + json + json = "{$json" openBraces++ } return json @@ -78,4 +86,67 @@ object JsonUtils { fun isValidJson(json: String): Boolean = runCatching { JSONObject(json) }.isSuccess || runCatching { JSONArray(json) }.isSuccess + class FuzzyDeserializer : JsonDeserializer { + + override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): T { + require(typeOfT is Class<*>) { + "Expected parameter typeOfT to be of type Class, but got: ${typeOfT?.javaClass?.name}" + } + @Suppress("UNCHECKED_CAST") + val clazz = typeOfT as Class + val instance = try { + clazz.getDeclaredConstructor().newInstance() + } catch (e: Exception) { + throw RuntimeException("Failed to create an instance of ${clazz.name}", e) + } + + if (json is JsonObject) { + clazz.declaredFields.forEach { processField(it, json, instance, context) } + } + return instance + } + + private fun processField(field: Field, json: JsonObject, instance: T, context: JsonDeserializationContext?) { + field.isAccessible = true + + val serializedNameAnnotation = field.getAnnotation(SerializedName::class.java) + val compatibleAnnotation = field.getAnnotation(SerializedNameCompatible::class.java) + + val sanitizedPrimaryName = sanitizeKey(serializedNameAnnotation?.value ?: field.name) + val sanitizedAlternateNames = serializedNameAnnotation?.alternate?.map { sanitizeKey(it) } ?: emptyList() + val sanitizedCompatibleNames = compatibleAnnotation?.with?.map { sanitizeKey(it.value) to it.isReversed } ?: emptyList() + + val serializedNames: List> = (sanitizedAlternateNames + sanitizedPrimaryName).map { it to false } + sanitizedCompatibleNames + + for ((jsonKey, jsonValue) in json.entrySet()) { + val sanitizedJsonKey = sanitizeKey(jsonKey) + for (pair in serializedNames) { + val (serializedKey, isReversed) = pair + if (sanitizedJsonKey == serializedKey) { + when (field.type) { + Boolean::class.javaPrimitiveType, Boolean::class.java -> { + if (jsonValue.isJsonPrimitive && jsonValue.asJsonPrimitive.isBoolean) { + val value = jsonValue.asBoolean + field.set(instance, if (isReversed) !value else value) + } + } + else -> { + if (!jsonValue.isJsonNull) { + val value = context?.deserialize(jsonValue, field.genericType) + field.set(instance, value) + } + } + } + return + } + } + } + } + + private fun sanitizeKey(key: String): String { + return key.replace(Regex("[^a-zA-Z0-9]"), "").lowercase() + } + + } + } \ No newline at end of file diff --git a/version.properties b/version.properties index 23ae2cb4..b6feca74 100644 --- a/version.properties +++ b/version.properties @@ -1,5 +1,5 @@ -#Mon Jan 20 14:32:52 CST 2025 -BUILD_TIME=1737354772708 +#Mon Jan 20 15:15:12 CST 2025 +BUILD_TIME=1737357312345 COMPILE_SDK_VERSION=34 JAVA_VERSION=23 JAVA_VERSION_MIN_RADICAL=0