6.6.2 - Alpha2 - project.json 支持选项名称宽松匹配及别名兼容
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<ProjectConfig>())
|
||||
.registerTypeAdapter(LaunchConfig.class, new JsonUtils.FuzzyDeserializer<LaunchConfig>())
|
||||
.registerTypeAdapter(ScriptConfig.class, new JsonUtils.FuzzyDeserializer<ScriptConfig>())
|
||||
.registerTypeAdapter(BuildInfo.class, new JsonUtils.FuzzyDeserializer<BuildInfo>())
|
||||
.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<String> 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<Bitmap> mIconBitmapGetter;
|
||||
|
||||
@Nullable
|
||||
@SerializedName(value = "abis", alternate = {"abi", "abiList"})
|
||||
@SerializedName(value = "abis")
|
||||
@SerializedNameCompatible(with = {
|
||||
@With(value = "abi"),
|
||||
@With(value = "abiList"),
|
||||
})
|
||||
private List<String> mAbis = new ArrayList<>();
|
||||
|
||||
@Nullable
|
||||
@SerializedName(value = "libs", alternate = {"lib", "libList"})
|
||||
@SerializedName(value = "libs")
|
||||
@SerializedNameCompatible(with = {
|
||||
@With(value = "lib"),
|
||||
@With(value = "libList"),
|
||||
})
|
||||
private List<String> mLibs = new ArrayList<>();
|
||||
|
||||
@SerializedName("permissions")
|
||||
@SerializedNameCompatible(with = {
|
||||
@With(value = "permission"),
|
||||
@With(value = "permissionList"),
|
||||
})
|
||||
private List<String> 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<String, ScriptConfig> 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<String> mFeatures = new ArrayList<>();
|
||||
|
||||
@SerializedName("ignoredDirs")
|
||||
private final List<File> mIgnoredDirs = new ArrayList<>();
|
||||
@SerializedName("excludedDirs")
|
||||
@SerializedNameCompatible(with = {
|
||||
@With(value = "ignoredDirs", target = {"AutoJs4", "AutoX"}),
|
||||
@With(value = "ignore", target = {"Unknown"}),
|
||||
})
|
||||
private final List<File> 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<String> getFeatures() {
|
||||
if (mFeatures == null) {
|
||||
mFeatures = Collections.emptyList();
|
||||
}
|
||||
return mFeatures;
|
||||
}
|
||||
|
||||
public void setFeatures(List<String> features) {
|
||||
public void setFeatures(@NonNull List<String> 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<String> 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<String> combinedFeatures = getCombinedFeatures(scriptConfig);
|
||||
scriptConfig.setFeatures(combinedFeatures);
|
||||
return scriptConfig;
|
||||
}
|
||||
|
||||
public List<File> getIgnoredDirs() {
|
||||
return mIgnoredDirs;
|
||||
@NotNull
|
||||
public ArrayList<String> getCombinedFeatures(ScriptConfig scriptConfig) {
|
||||
return new ArrayList<>(
|
||||
new HashSet<>() {{
|
||||
addAll(scriptConfig.getFeatures());
|
||||
addAll(mFeatures);
|
||||
}}
|
||||
);
|
||||
}
|
||||
|
||||
public ProjectConfig ignoredDir(File ignoredDir) {
|
||||
mIgnoredDirs.add(ignoredDir);
|
||||
public List<File> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<String> = emptyList(),
|
||||
@SerializedName("useFeatures")
|
||||
@field:SerializedNameCompatible(
|
||||
With(value = "useFeature"),
|
||||
With(value = "useFeatureList"),
|
||||
With(value = "features"),
|
||||
With(value = "feature"),
|
||||
With(value = "featureList"),
|
||||
)
|
||||
var features: List<String> = emptyList(),
|
||||
) {
|
||||
|
||||
fun hasFeature(feature: String) = features.contains(feature)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<T> : JsonDeserializer<T> {
|
||||
|
||||
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<T>
|
||||
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<Pair<String, Boolean>> = (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<Any>(jsonValue, field.genericType)
|
||||
field.set(instance, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sanitizeKey(key: String): String {
|
||||
return key.replace(Regex("[^a-zA-Z0-9]"), "").lowercase()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user