6.6.2 - Alpha2 - 整合 AppConfig 打包类; project.json 支持更多打包选项 (issue #305, #306)

This commit is contained in:
SuperMonster003
2025-01-20 14:39:11 +08:00
parent 436645e921
commit e8cc12dbb8
13 changed files with 379 additions and 274 deletions

View File

@@ -14,6 +14,7 @@
"精简打包应用模板 APK 文件大小", "精简打包应用模板 APK 文件大小",
"打包页面支持 Pinyin 库选项", "打包页面支持 Pinyin 库选项",
"打包应用主活动页面优化状态栏背景及文字颜色", "打包应用主活动页面优化状态栏背景及文字颜色",
"脚本项目配置文件 project.json 支持更多打包选项 _[`issue #305`](http://issues.autojs6.com/305)_ _[`issue #306`](http://issues.autojs6.com/306)_",
"APK 文件类型信息对话框增加文件大小与签名方案信息", "APK 文件类型信息对话框增加文件大小与签名方案信息",
"APK 文件类型信息对话框增加点击监听器支持文本复制与应用详情跳转", "APK 文件类型信息对话框增加点击监听器支持文本复制与应用详情跳转",
"尝试恢复 com.stardust 前缀包以便提升代码兼容性 _[`issue #290`](http://issues.autojs6.com/290)_", "尝试恢复 com.stardust 前缀包以便提升代码兼容性 _[`issue #290`](http://issues.autojs6.com/290)_",

View File

@@ -0,0 +1,7 @@
package org.autojs.autojs.annotation
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FIELD, AnnotationTarget.FUNCTION)
annotation class SerializedNameCompatible(vararg val with: With) {
annotation class With(val value: String, val target: Array<String> = ["AutoJs6"], val isReversed: Boolean = false)
}

View File

@@ -5,7 +5,6 @@ import android.content.pm.PackageManager.ApplicationInfoFlags
import android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES import android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES
import android.content.res.AssetManager import android.content.res.AssetManager
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import com.mcal.apksigner.ApkSigner import com.mcal.apksigner.ApkSigner
@@ -13,7 +12,6 @@ import com.reandroid.arsc.chunk.TableBlock
import org.apache.commons.io.FileUtils.copyFile import org.apache.commons.io.FileUtils.copyFile
import org.apache.commons.io.FileUtils.copyInputStreamToFile import org.apache.commons.io.FileUtils.copyInputStreamToFile
import org.autojs.autojs.apkbuilder.keystore.AESUtils import org.autojs.autojs.apkbuilder.keystore.AESUtils
import org.autojs.autojs.apkbuilder.keystore.KeyStore
import org.autojs.autojs.app.GlobalAppContext import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.engine.encryption.AdvancedEncryptionStandard import org.autojs.autojs.engine.encryption.AdvancedEncryptionStandard
import org.autojs.autojs.pio.PFiles import org.autojs.autojs.pio.PFiles
@@ -35,7 +33,6 @@ import java.io.FileNotFoundException
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.util.concurrent.Callable
/** /**
* Created by Stardust on Oct 24, 2017. * Created by Stardust on Oct 24, 2017.
@@ -49,7 +46,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
private var mInitVector: String? = null private var mInitVector: String? = null
private var mKey: String? = null private var mKey: String? = null
private lateinit var mAppConfig: AppConfig private lateinit var mProjectConfig: ProjectConfig
private val mApkPackager = ApkPackager(apkInputStream, workspacePath) private val mApkPackager = ApkPackager(apkInputStream, workspacePath)
@@ -59,6 +56,9 @@ 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 mNoSplashThemeId = 0
private val mManifestFile private val mManifestFile
get() = File(workspacePath, "AndroidManifest.xml") get() = File(workspacePath, "AndroidManifest.xml")
@@ -105,7 +105,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
srcChildFile.copyTo(File(destDirFile, srcChildFile.name), true) srcChildFile.copyTo(File(destDirFile, srcChildFile.name), true)
} }
} else { } else {
if (!mAppConfig.ignoredDirs.contains(srcChildFile)) { if (!mProjectConfig.ignoredDirs.contains(srcChildFile)) {
copyDir(srcChildFile, PFiles.join(relativeDestPath, srcChildFile.name + File.separator)) copyDir(srcChildFile, PFiles.join(relativeDestPath, srcChildFile.name + File.separator))
} }
} }
@@ -141,10 +141,27 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
} }
@Throws(IOException::class) @Throws(IOException::class)
fun withConfig(config: AppConfig) = also { fun withConfig(config: ProjectConfig) = also {
config.also { mAppConfig = it }.run { config.also { mProjectConfig = it }.run {
if (!launchConfig.isSplashVisible) {
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()
}
}
mManifestEditor = editManifest() mManifestEditor = editManifest()
.setAppName(appName) .setAppName(name)
.setVersionName(versionName) .setVersionName(versionName)
.setVersionCode(versionCode) .setVersionCode(versionCode)
.setPackageName(packageName) .setPackageName(packageName)
@@ -160,32 +177,32 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
@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(appConfig: AppConfig) { private fun updateProjectConfig(config: ProjectConfig) {
val projectConfig = when { val projectConfig = when {
!PFiles.isDir(appConfig.sourcePath) -> null !PFiles.isDir(config.sourcePath) -> null
else -> ProjectConfig.fromProjectDir(appConfig.sourcePath)?.also { else -> ProjectConfig.fromProjectDir(config.sourcePath)?.also {
val buildNumber = it.buildInfo.buildNumber val buildNumber = it.buildInfo.buildNumber
it.buildInfo = BuildInfo.generate(buildNumber + 1) it.buildInfo = BuildInfo.generate(buildNumber + 1)
PFiles.write(ProjectConfig.configFileOfDir(appConfig.sourcePath), it.toJson()) PFiles.write(ProjectConfig.configFileOfDir(config.sourcePath), it.toJson())
} }
} ?: ProjectConfig() } ?: ProjectConfig()
.setMainScriptFile("main.js") .setMainScriptFile("main.js")
.setName(appConfig.appName) .setName(config.name)
.setPackageName(appConfig.packageName) .setPackageName(config.packageName)
.setVersionName(appConfig.versionName) .setVersionName(config.versionName)
.setVersionCode(appConfig.versionCode) .setVersionCode(config.versionCode)
.also { config -> .also { newProjectConfig ->
config.buildInfo = BuildInfo.generate(appConfig.versionCode.toLong()) newProjectConfig.buildInfo = BuildInfo.generate(newProjectConfig.versionCode.toLong())
File(workspacePath, "assets/project/${ProjectConfig.CONFIG_FILE_NAME}").also { file -> File(workspacePath, "assets/project/${ProjectConfig.CONFIG_FILE_NAME}").also { file ->
file.parentFile?.let { parent -> if (!parent.exists()) parent.mkdirs() } file.parentFile?.let { parent -> if (!parent.exists()) parent.mkdirs() }
}.writeText(config.toJson()) }.writeText(newProjectConfig.toJson())
} }
projectConfig.run { projectConfig.run {
mKey = MD5Utils.md5(packageName + versionName + mainScriptFile) mKey = MD5Utils.md5(packageName + versionName + mainScriptFile)
mInitVector = MD5Utils.md5(buildInfo.buildId + name).substring(0, 16) mInitVector = MD5Utils.md5(buildInfo.buildId + name).substring(0, 16)
Libs.entries.forEach { entry -> Libs.entries.forEach { entry ->
if (appConfig.libs.contains(entry.label)) { if (config.libs.contains(entry.label)) {
mLibsIncludes += entry.libsToInclude.toSet() mLibsIncludes += entry.libsToInclude.toSet()
mAssetsFileIncludes += entry.assetFilesToInclude.toSet() mAssetsFileIncludes += entry.assetFilesToInclude.toSet()
mAssetsDirExcludes -= entry.assetDirsToExclude.toSet() mAssetsDirExcludes -= entry.assetDirsToExclude.toSet()
@@ -197,7 +214,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
@Throws(Exception::class) @Throws(Exception::class)
fun build() = also { fun build() = also {
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onBuild(this) } } mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onBuild(this) } }
mAppConfig.icon?.let { callable -> mProjectConfig.iconBitmapGetter?.let { callable ->
runCatching { runCatching {
val tableBlock = TableBlock.load(mResourcesArscFile) val tableBlock = TableBlock.load(mResourcesArscFile)
val packageName = "${GlobalAppContext.get().packageName}.inrt" val packageName = "${GlobalAppContext.get().packageName}.inrt"
@@ -264,17 +281,17 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
val signer = ApkSigner(outApkFile, tmpOutputApk) val signer = ApkSigner(outApkFile, tmpOutputApk)
signer.useDefaultSignatureVersion = false signer.useDefaultSignatureVersion = false
signer.v1SigningEnabled = mAppConfig.signatureSchemes.contains("V1") signer.v1SigningEnabled = mProjectConfig.signatureSchemes.contains("V1")
signer.v2SigningEnabled = mAppConfig.signatureSchemes.contains("V2") signer.v2SigningEnabled = mProjectConfig.signatureSchemes.contains("V2")
signer.v3SigningEnabled = mAppConfig.signatureSchemes.contains("V3") signer.v3SigningEnabled = mProjectConfig.signatureSchemes.contains("V3")
signer.v4SigningEnabled = mAppConfig.signatureSchemes.contains("V4") signer.v4SigningEnabled = mProjectConfig.signatureSchemes.contains("V4")
var keyStoreFile = defaultKeyStoreFile var keyStoreFile = defaultKeyStoreFile
var password = "AutoJs6" var password = "AutoJs6"
var alias = "AutoJs6" var alias = "AutoJs6"
var aliasPassword = "AutoJs6" var aliasPassword = "AutoJs6"
mAppConfig.keyStore?.let { mProjectConfig.keyStore?.let {
keyStoreFile = File(it.absolutePath) keyStoreFile = File(it.absolutePath)
password = AESUtils.decrypt(it.password) password = AESUtils.decrypt(it.password)
alias = it.alias alias = it.alias
@@ -324,76 +341,14 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
} }
class AppConfig {
var appName: String? = null
private set
var versionName: String? = null
private set
var versionCode = 0
private set
var sourcePath: String? = null
private set
var packageName: String? = null
private set
var ignoredDirs = ArrayList<File>()
var icon: Callable<Bitmap>? = null
private set
var abis: List<String> = emptyList()
private set
var libs: List<String> = emptyList()
private set
var keyStore: KeyStore? = null
private set
var signatureSchemes: String = "V1 + V2"
private set
var permissions: List<String> = emptyList()
private set
fun ignoreDir(dir: File) = also { ignoredDirs.add(dir) }
fun setAppName(appName: String?) = also { appName?.let { this.appName = it } }
fun setVersionName(versionName: String?) = also { versionName?.let { this.versionName = it } }
fun setVersionCode(versionCode: Int?) = also { versionCode?.let { this.versionCode = it } }
fun setSourcePath(sourcePath: String?) = also { sourcePath?.let { this.sourcePath = it } }
fun setPackageName(packageName: String?) = also { packageName?.let { this.packageName = it } }
fun setIcon(icon: Callable<Bitmap>?) = also { icon?.let { this.icon = it } }
fun setIcon(iconPath: String?) = also { iconPath?.let { this.icon = Callable { BitmapFactory.decodeFile(it) } } }
fun setAbis(abis: List<String>) = also { this.abis = abis }
fun setLibs(libs: List<String>) = also { this.libs = libs }
fun setKeyStore(keyStore: KeyStore?) = also { this.keyStore = keyStore }
fun setSignatureSchemes(signatureSchemes: String) = also { this.signatureSchemes = signatureSchemes }
fun setPermissions(permissions: List<String>) = also { this.permissions = permissions }
companion object {
@JvmStatic
fun fromProjectConfig(projectDir: String?, projectConfig: ProjectConfig) = AppConfig()
.setAppName(projectConfig.name)
.setPackageName(projectConfig.packageName)
.ignoreDir(File(projectDir, projectConfig.buildDir))
.setVersionCode(projectConfig.versionCode)
.setVersionName(projectConfig.versionName)
.setSourcePath(projectDir)
.setIcon(projectConfig.icon?.let { File(projectDir, it).path })
}
}
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 {
if (!mProjectConfig.launchConfig.isSplashVisible && mSplashThemeId != 0 && value == mSplashThemeId) {
value = mNoSplashThemeId
}
if (name.data == "authorities" && value is StringItem) { if (name.data == "authorities" && value is StringItem) {
(value as StringItem).data = "${mAppConfig.packageName}.fileprovider" (value as StringItem).data = "${mProjectConfig.packageName}.fileprovider"
} else { } else {
super.onAttr(this) super.onAttr(this)
} }
@@ -401,11 +356,11 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
} }
override fun isPermissionRequired(permissionName: String): Boolean { override fun isPermissionRequired(permissionName: String): Boolean {
return mAppConfig.permissions.contains(permissionName) return mProjectConfig.permissions.contains(permissionName)
} }
} }
private fun copyLibrariesByConfig(config: AppConfig) { private fun copyLibrariesByConfig(config: ProjectConfig) {
// @Hint by SuperMonster003 on Dec 11, 2023. // @Hint by SuperMonster003 on Dec 11, 2023.
// ! The list contains only abi names not matching the canonical name itself. // ! The list contains only abi names not matching the canonical name itself.
@@ -461,6 +416,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
} }
@Suppress("SpellCheckingInspection")
enum class Libs( enum class Libs(
@JvmField val label: String, @JvmField val label: String,
@JvmField val aliases: List<String> = emptyList(), @JvmField val aliases: List<String> = emptyList(),

View File

@@ -10,10 +10,11 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import org.autojs.autojs.inrt.autojs.AutoJs import org.autojs.autojs.inrt.autojs.AutoJs
import org.autojs.autojs.inrt.launch.GlobalProjectLauncher import org.autojs.autojs.inrt.launch.GlobalProjectLauncher
import org.autojs.autojs.project.ProjectConfig
import org.autojs.autojs.ui.splash.SplashActivity.Companion.INIT_TIMEOUT import org.autojs.autojs.ui.splash.SplashActivity.Companion.INIT_TIMEOUT
import org.autojs.autojs6.R
import org.autojs.autojs6.databinding.ActivitySplashInrtBinding import org.autojs.autojs6.databinding.ActivitySplashInrtBinding
import kotlin.concurrent.thread import kotlin.concurrent.thread
@@ -24,14 +25,29 @@ import kotlin.concurrent.thread
class SplashActivity : AppCompatActivity() { class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_splash_inrt) val binding = ActivitySplashInrtBinding.inflate(layoutInflater).apply {
val binding = ActivitySplashInrtBinding.inflate(layoutInflater) setContentView(root)
binding.slug.typeface = Typeface.createFromAsset(assets, "roboto_medium.ttf") }
val handler = Handler(Looper.myLooper()!!)
val projectConfig = ProjectConfig.fromAssets(this, ProjectConfig.configFileOfDir("project"))
var timeout = INIT_TIMEOUT
if (!projectConfig.launchConfig.isSplashVisible) {
timeout = 0L
} else {
handler.post {
binding.slug.typeface = Typeface.createFromAsset(assets, "roboto_medium.ttf")
binding.slug.isVisible = true
binding.icon.isVisible = true
}
}
if (!Pref.isFirstUsing) { if (!Pref.isFirstUsing) {
main() main()
} else { } else {
Handler(Looper.myLooper()!!).postDelayed({ this@SplashActivity.main() }, INIT_TIMEOUT) handler.postDelayed({ this@SplashActivity.main() }, timeout)
} }
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }

View File

@@ -36,7 +36,7 @@ open class AssetsProjectLauncher(private val mAssetsProjectDir: String, private
fun launch(activity: Activity) { fun launch(activity: Activity) {
// 如果需要隐藏日志界面, 则直接运行脚本 // 如果需要隐藏日志界面, 则直接运行脚本
if (mProjectConfig.launchConfig.shouldHideLogs() || Pref.shouldHideLogs()) { if (!mProjectConfig.launchConfig.isLogsVisible || Pref.shouldHideLogs()) {
runScript(activity) runScript(activity)
} else { } else {
// 如果不隐藏日志界面 // 如果不隐藏日志界面

View File

@@ -6,13 +6,13 @@ import java.util.zip.CRC32;
public class BuildInfo { public class BuildInfo {
@SerializedName("build_time") @SerializedName(value = "buildTime", alternate = {"build_time"})
private long mBuildTime; private long mBuildTime;
@SerializedName("build_id") @SerializedName(value = "buildId", alternate = {"build_id"})
private String mBuildId; private String mBuildId;
@SerializedName("build_number") @SerializedName(value = "buildNumber", alternate = {"build_number"})
private long mBuildNumber; private long mBuildNumber;
public long getBuildNumber() { public long getBuildNumber() {

View File

@@ -1,21 +1,57 @@
package org.autojs.autojs.project; package org.autojs.autojs.project
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName
import org.autojs.autojs.annotation.SerializedNameCompatible
import org.autojs.autojs.annotation.SerializedNameCompatible.With
import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs6.R
/** /**
* Created by Stardust on Jan 25, 2018. * Created by Stardust on Jan 25, 2018.
* Modified by SuperMonster003 as of Jan 15, 2025.
* Created by SuperMonster003 on Jan 15, 2025.
*/ */
public class LaunchConfig { class LaunchConfig {
@SerializedName("hideLogs") @Transient
private boolean mHideLogs = false; private val mContext = GlobalAppContext.get()
public boolean shouldHideLogs() { @SerializedName("logsVisible")
return mHideLogs; @field:SerializedNameCompatible(
} With(value = "showLogs"),
With(value = "hideLogs", target = ["AutoJs4", "AutoX"], isReversed = true),
With(value = "displayLogs"),
)
var isLogsVisible = true
public void setHideLogs(boolean hideLogs) { @SerializedName("splashVisible")
mHideLogs = hideLogs; @field:SerializedNameCompatible(
} With(value = "showSplash"),
With(value = "hideSplash", isReversed = true),
With(value = "displaySplash", target = ["AutoX"]),
)
var isSplashVisible = true
@SerializedName("launcherVisible")
@field:SerializedNameCompatible(
With(value = "showLauncher"),
With(value = "hideLauncher", target = ["AutoX"], isReversed = true),
With(value = "displayLauncher"),
)
var isLauncherVisible = true
@SerializedName("slug")
@field:SerializedNameCompatible(
With(value = "slugText"),
With(value = "splashText", target = ["AutoX"]),
)
var slug = mContext.getString(R.string.text_powered_by_autojs)
@SerializedName("permissions")
@field:SerializedNameCompatible(
With(value = "permission"),
With(value = "permissionList"),
)
var permissions = emptyList<String>()
} }

View File

@@ -1,12 +1,15 @@
package org.autojs.autojs.project; package org.autojs.autojs.project;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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.apkbuilder.keystore.KeyStore;
import org.autojs.autojs.model.explorer.ExplorerPage; import org.autojs.autojs.model.explorer.ExplorerPage;
import org.autojs.autojs.pio.PFiles; import org.autojs.autojs.pio.PFiles;
import org.autojs.autojs.util.JsonUtils; import org.autojs.autojs.util.JsonUtils;
@@ -20,6 +23,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
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;
@@ -30,7 +34,9 @@ public class ProjectConfig {
public static final String CONFIG_FILE_NAME = "project.json"; public static final String CONFIG_FILE_NAME = "project.json";
private static final Gson sGson = new GsonBuilder().setPrettyPrinting().create(); private static final Gson sGson = new GsonBuilder()
.setPrettyPrinting()
.create();
@SerializedName("name") @SerializedName("name")
private String mName; private String mName;
@@ -52,13 +58,15 @@ public class ProjectConfig {
private List<String> mAssets = new ArrayList<>(); private List<String> mAssets = new ArrayList<>();
@SerializedName("launchConfig") @SerializedName("launchConfig")
private LaunchConfig mLaunchConfig; private LaunchConfig mLaunchConfig = new LaunchConfig();
@SerializedName("build") @SerializedName("build")
private BuildInfo mBuildInfo = new BuildInfo(); private BuildInfo mBuildInfo = new BuildInfo();
@SerializedName("icon") @SerializedName("icon")
private String mIcon; private String mIconPath;
private transient Callable<Bitmap> mIconBitmapGetter;
@Nullable @Nullable
@SerializedName(value = "abis", alternate = {"abi", "abiList"}) @SerializedName(value = "abis", alternate = {"abi", "abiList"})
@@ -68,12 +76,27 @@ public class ProjectConfig {
@SerializedName(value = "libs", alternate = {"lib", "libList"}) @SerializedName(value = "libs", alternate = {"lib", "libList"})
private List<String> mLibs = new ArrayList<>(); private List<String> mLibs = new ArrayList<>();
@SerializedName("permissions")
private List<String> mPermissions = new ArrayList<>();
@SerializedName("signatureSchemes")
private String mSignatureSchemes = "V1 + V2";
@Nullable
private transient KeyStore mKeyStore = null;
@SerializedName("scripts") @SerializedName("scripts")
private final Map<String, ScriptConfig> mScriptConfigs = new HashMap<>(); private final Map<String, ScriptConfig> mScriptConfigs = new HashMap<>();
@SerializedName(value = "useFeatures", alternate = {"useFeature", "useFeatureList"}) @SerializedName(value = "useFeatures", alternate = {"useFeature", "useFeatureList"})
private List<String> mFeatures = new ArrayList<>(); private List<String> mFeatures = new ArrayList<>();
@SerializedName("ignoredDirs")
private final List<File> mIgnoredDirs = new ArrayList<>();
@Nullable
private transient String mSourcePath = null;
public static ProjectConfig fromJson(String json) { public static ProjectConfig fromJson(String json) {
if (json == null) { if (json == null) {
return null; return null;
@@ -117,7 +140,7 @@ public class ProjectConfig {
return fromJson(fileContents); return fromJson(fileContents);
} catch (Exception e1) { } catch (Exception e1) {
if (fileContents == null) return null; if (fileContents == null) return null;
try { try {
return fromJson(JsonUtils.repairJson(fileContents)); return fromJson(JsonUtils.repairJson(fileContents));
} catch (Exception e2) { } catch (Exception e2) {
return tryReadCrucialData(fileContents, path); return tryReadCrucialData(fileContents, path);
@@ -126,10 +149,9 @@ public class ProjectConfig {
} }
private static ProjectConfig tryReadCrucialData(String s, String jsonFilePath) { private static ProjectConfig tryReadCrucialData(String s, String jsonFilePath) {
ProjectConfig config = new ProjectConfig(); ProjectConfig projectConfig = new ProjectConfig();
BuildInfo buildInfo = new BuildInfo();
ScriptConfig scriptConfig = new ScriptConfig();
LaunchConfig launchConfig = new LaunchConfig(); LaunchConfig launchConfig = new LaunchConfig();
BuildInfo buildInfo = new BuildInfo();
Pattern namePattern = Pattern.compile(stringPattern("name")); Pattern namePattern = Pattern.compile(stringPattern("name"));
Pattern versionNamePattern = Pattern.compile(stringPattern("versionName")); Pattern versionNamePattern = Pattern.compile(stringPattern("versionName"));
@@ -147,40 +169,47 @@ public class ProjectConfig {
Pattern buildNumberPattern = Pattern.compile(numberPattern("buildNumber")); Pattern buildNumberPattern = Pattern.compile(numberPattern("buildNumber"));
Pattern buildIdPattern = Pattern.compile(stringPattern("buildId")); Pattern buildIdPattern = Pattern.compile(stringPattern("buildId"));
Pattern launchConfigPattern = Pattern.compile(booleanPattern("hideLogs")); Pattern launchConfigHideLogsPattern = Pattern.compile(booleanPattern("hideLogs"));
Pattern launchConfigLogsVisiblePattern = Pattern.compile(booleanPattern("logsVisible"));
Pattern scriptsUiModePattern = Pattern.compile(booleanPattern("uiMode")); Pattern launchConfigDisplaySplashPattern = Pattern.compile(booleanPattern("displaySplash"));
Pattern launchConfigSplashVisiblePattern = Pattern.compile(booleanPattern("splashVisible"));
setFieldIfMatches(namePattern, s, config::setName); setFieldIfMatches(namePattern, s, projectConfig::setName);
setFieldIfMatches(versionNamePattern, s, config::setVersionName); setFieldIfMatches(versionNamePattern, s, projectConfig::setVersionName);
setFieldForIntIfMatches(versionCodePattern, s, config::setVersionCode); setFieldForIntIfMatches(versionCodePattern, s, projectConfig::setVersionCode);
setFieldIfMatches(packageNamePattern, s, config::setPackageName); setFieldIfMatches(packageNamePattern, s, projectConfig::setPackageName);
setFieldIfMatches(mainPattern, s, config::setMainScriptFile); setFieldIfMatches(mainPattern, s, projectConfig::setMainScriptFile);
setFieldIfMatches(iconPattern, s, config::setIcon); setFieldIfMatches(iconPattern, s, projectConfig::setIconPath);
setListIfMatches(assetsPattern, s, config::setAssets); setListIfMatches(assetsPattern, s, projectConfig::setAssets);
setListIfMatches(abisPattern, s, config::setAbis); setListIfMatches(abisPattern, s, projectConfig::setAbis);
setListIfMatches(libsPattern, s, config::setLibs); setListIfMatches(libsPattern, s, projectConfig::setLibs);
setListIfMatches(useFeaturesPattern, s, config::setFeatures); setListIfMatches(useFeaturesPattern, s, projectConfig::setFeatures);
setFieldForIntIfMatches(buildTimePattern, s, buildInfo::setBuildTime); setFieldForIntIfMatches(buildTimePattern, s, buildInfo::setBuildTime);
setFieldForIntIfMatches(buildNumberPattern, s, buildInfo::setBuildNumber); setFieldForIntIfMatches(buildNumberPattern, s, buildInfo::setBuildNumber);
setFieldIfMatches(buildIdPattern, s, buildInfo::setBuildId); setFieldIfMatches(buildIdPattern, s, buildInfo::setBuildId);
setFieldForBooleanIfMatches(launchConfigPattern, s, launchConfig::setHideLogs); setFieldForBooleanIfMatches(launchConfigHideLogsPattern, s, value -> launchConfig.setLogsVisible(!value));
setFieldForBooleanIfMatches(launchConfigLogsVisiblePattern, s, launchConfig::setLogsVisible); /* 优先. */
setFieldForBooleanIfMatches(scriptsUiModePattern, s, scriptConfig::setUiMode); setFieldForBooleanIfMatches(launchConfigDisplaySplashPattern, s, launchConfig::setSplashVisible);
setFieldForBooleanIfMatches(launchConfigSplashVisiblePattern, s, launchConfig::setSplashVisible); /* 优先. */
if (config.getName() == null || config.getName().isBlank()) { if (projectConfig.getName() == null || projectConfig.getName().isBlank()) {
if (jsonFilePath.endsWith(CONFIG_FILE_NAME)) { if (jsonFilePath.endsWith(CONFIG_FILE_NAME)) {
File parentFile = new File(jsonFilePath).getParentFile(); File parentFile = new File(jsonFilePath).getParentFile();
if (parentFile != null) { if (parentFile != null) {
config.setName(parentFile.getName()); projectConfig.setName(parentFile.getName());
} }
} }
} }
return config; projectConfig.setBuildInfo(buildInfo);
projectConfig.setLaunchConfig(launchConfig);
return projectConfig;
} }
@NotNull @NotNull
@@ -350,13 +379,10 @@ public class ProjectConfig {
} }
public LaunchConfig getLaunchConfig() { public LaunchConfig getLaunchConfig() {
if (mLaunchConfig == null) {
mLaunchConfig = new LaunchConfig();
}
return mLaunchConfig; return mLaunchConfig;
} }
public void setLaunchConfig(LaunchConfig launchConfig) { public void setLaunchConfig(@NonNull LaunchConfig launchConfig) {
mLaunchConfig = launchConfig; mLaunchConfig = launchConfig;
} }
@@ -364,12 +390,23 @@ public class ProjectConfig {
return sGson.toJson(this); return sGson.toJson(this);
} }
public String getIcon() { public String getIconPath() {
return mIcon; return mIconPath;
} }
public void setIcon(String icon) { public ProjectConfig setIconPath(String iconPath) {
mIcon = icon; mIconPath = iconPath;
mIconBitmapGetter = () -> BitmapFactory.decodeFile(iconPath);
return this;
}
public Callable<Bitmap> getIconBitmapGetter() {
return mIconBitmapGetter;
}
public ProjectConfig setIconGetter(@Nullable Callable<Bitmap> getter) {
mIconBitmapGetter = getter;
return this;
} }
public List<String> getAbis() { public List<String> getAbis() {
@@ -379,8 +416,9 @@ public class ProjectConfig {
return mAbis; return mAbis;
} }
public void setAbis(@Nullable List<String> abis) { public ProjectConfig setAbis(@Nullable List<String> abis) {
mAbis = abis; mAbis = abis;
return this;
} }
public List<String> getLibs() { public List<String> getLibs() {
@@ -390,8 +428,9 @@ public class ProjectConfig {
return mLibs; return mLibs;
} }
public void setLibs(@Nullable List<String> libs) { public ProjectConfig setLibs(@Nullable List<String> libs) {
mLibs = libs; mLibs = libs;
return this;
} }
public String getBuildDir() { public String getBuildDir() {
@@ -427,4 +466,51 @@ public class ProjectConfig {
config.setFeatures(features); config.setFeatures(features);
return config; return config;
} }
public List<File> getIgnoredDirs() {
return mIgnoredDirs;
}
public ProjectConfig ignoredDir(File ignoredDir) {
mIgnoredDirs.add(ignoredDir);
return this;
}
@Nullable
public String getSourcePath() {
return mSourcePath;
}
public ProjectConfig setSourcePath(@Nullable String sourcePath) {
mSourcePath = sourcePath;
return this;
}
public List<String> getPermissions() {
return mPermissions;
}
public ProjectConfig setPermissions(List<String> permissions) {
mPermissions = permissions;
return this;
}
public String getSignatureSchemes() {
return mSignatureSchemes;
}
public ProjectConfig setSignatureSchemes(String signatureSchemes) {
mSignatureSchemes = signatureSchemes;
return this;
}
@Nullable
public KeyStore getKeyStore() {
return mKeyStore;
}
public ProjectConfig setKeyStore(@Nullable KeyStore keyStore) {
mKeyStore = keyStore;
return this;
}
} }

View File

@@ -2,17 +2,14 @@ package org.autojs.autojs.project
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
data class ScriptConfig( data class ScriptConfig @JvmOverloads constructor(
@SerializedName("useFeatures") var features: List<String>, @SerializedName("useFeatures") var features: List<String> = emptyList(),
@SerializedName("uiMode") var uiMode: Boolean
) { ) {
constructor() : this(emptyList(), false)
fun hasFeature(feature: String): Boolean { fun hasFeature(feature: String) = features.contains(feature)
return features.contains(feature)
}
companion object { companion object {
const val FEATURE_CONTINUATION = "continuation" const val FEATURE_CONTINUATION = "continuation"
} }
} }

View File

@@ -160,18 +160,18 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
put("moe.shizuku.manager.permission.API_V23", R.string.text_permission_shizuku); put("moe.shizuku.manager.permission.API_V23", R.string.text_permission_shizuku);
}}; }};
EditText mSourcePath; EditText mSourcePathView;
View mSourcePathContainer; View mSourcePathContainerView;
EditText mOutputPath; EditText mOutputPathView;
EditText mAppName; EditText mAppNameView;
EditText mPackageName; EditText mPackageNameView;
TextInputLayout mPackageNameParent; TextInputLayout mPackageNameParentView;
EditText mVersionName; EditText mVersionNameView;
TextInputLayout mVersionNameParent; TextInputLayout mVersionNameParentView;
EditText mVersionCode; EditText mVersionCodeView;
TextInputLayout mVersionCodeParent; TextInputLayout mVersionCodeParentView;
ImageView mIcon; ImageView mIconView;
LinearLayout mAppConfig; LinearLayout mAppConfigView;
private ProjectConfig mProjectConfig; private ProjectConfig mProjectConfig;
private MaterialDialog mProgressDialog; private MaterialDialog mProgressDialog;
@@ -209,24 +209,24 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
ActivityBuildBinding binding = ActivityBuildBinding.inflate(getLayoutInflater()); ActivityBuildBinding binding = ActivityBuildBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
mSourcePath = binding.sourcePath; mSourcePathView = binding.sourcePath;
mSourcePath.setOnKeyListener((v, keyCode, event) -> { mSourcePathView.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_ENTER) { if (keyCode == KeyEvent.KEYCODE_ENTER) {
if (event.getAction() == KeyEvent.ACTION_UP) { if (event.getAction() == KeyEvent.ACTION_UP) {
mOutputPath.requestFocus(); mOutputPathView.requestFocus();
} }
return true; return true;
} }
return false; return false;
}); });
mSourcePathContainer = binding.sourcePathContainer; mSourcePathContainerView = binding.sourcePathContainer;
mOutputPath = binding.outputPath; mOutputPathView = binding.outputPath;
mOutputPath.setOnKeyListener((v, keyCode, event) -> { mOutputPathView.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_ENTER) { if (keyCode == KeyEvent.KEYCODE_ENTER) {
if (event.getAction() == KeyEvent.ACTION_UP) { if (event.getAction() == KeyEvent.ACTION_UP) {
TextView nextField = (TextView) mOutputPath.focusSearch(View.FOCUS_DOWN); TextView nextField = (TextView) mOutputPathView.focusSearch(View.FOCUS_DOWN);
if (nextField != null) { if (nextField != null) {
nextField.requestFocus(); nextField.requestFocus();
} }
@@ -236,30 +236,30 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
return false; return false;
}); });
mAppName = binding.appName; mAppNameView = binding.appName;
mPackageName = binding.packageName; mPackageNameView = binding.packageName;
mPackageName.setOnKeyListener((v, keyCode, event) -> { mPackageNameView.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_ENTER) { if (keyCode == KeyEvent.KEYCODE_ENTER) {
if (event.getAction() == KeyEvent.ACTION_UP) { if (event.getAction() == KeyEvent.ACTION_UP) {
mVersionName.requestFocus(); mVersionNameView.requestFocus();
} }
return true; return true;
} }
return false; return false;
}); });
mPackageNameParent = binding.packageNameParent; mPackageNameParentView = binding.packageNameParent;
mVersionName = binding.versionName; mVersionNameView = binding.versionName;
mVersionNameParent = binding.versionNameParent; mVersionNameParentView = binding.versionNameParent;
mVersionCode = binding.versionCode; mVersionCodeView = binding.versionCode;
mVersionCodeParent = binding.versionCodeParent; mVersionCodeParentView = binding.versionCodeParent;
mIcon = binding.appIcon; mIconView = binding.appIcon;
mIcon.setVisibility(View.INVISIBLE); mIconView.setVisibility(View.INVISIBLE);
mIcon.setOnClickListener(v -> selectIcon()); mIconView.setOnClickListener(v -> selectIcon());
mAppConfig = binding.appConfig; mAppConfigView = binding.appConfig;
mFlexboxAbis = binding.flexboxAbis; mFlexboxAbis = binding.flexboxAbis;
initAbisChildren(); initAbisChildren();
@@ -515,8 +515,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
if (dir != null && dir.startsWith(getFilesDir().getPath())) { if (dir != null && dir.startsWith(getFilesDir().getPath())) {
dir = WorkingDirectoryUtils.getPath(); dir = WorkingDirectoryUtils.getPath();
} }
mOutputPath.setText(dir); mOutputPathView.setText(dir);
mAppName.setText(file.getSimplifiedName()); mAppNameView.setText(file.getSimplifiedName());
Observable.fromCallable(() -> { Observable.fromCallable(() -> {
String packageNameSuffix = generatePackageNameSuffix(file); String packageNameSuffix = generatePackageNameSuffix(file);
@@ -525,34 +525,34 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(packageName -> { .subscribe(packageName -> {
mPackageName.setText(packageName); mPackageNameView.setText(packageName);
mPackageNameParent.setHint(R.string.text_package_name); mPackageNameParentView.setHint(R.string.text_package_name);
SimpleVersionInfo nextVersionInfo = AppUtils.generateNextVersionInfo(packageName); SimpleVersionInfo nextVersionInfo = AppUtils.generateNextVersionInfo(packageName);
if (nextVersionInfo != null) { if (nextVersionInfo != null) {
mVersionName.setText(nextVersionInfo.versionName); mVersionNameView.setText(nextVersionInfo.versionName);
mVersionCode.setText(nextVersionInfo.versionCodeString); mVersionCodeView.setText(nextVersionInfo.versionCodeString);
} else { } else {
mVersionName.setText(R.string.default_build_apk_version_name); mVersionNameView.setText(R.string.default_build_apk_version_name);
mVersionCode.setText(R.string.default_build_apk_version_code); mVersionCodeView.setText(R.string.default_build_apk_version_code);
} }
mVersionNameParent.setHint(R.string.text_version_name); mVersionNameParentView.setHint(R.string.text_version_name);
mVersionCodeParent.setHint(R.string.text_version_code); mVersionCodeParentView.setHint(R.string.text_version_code);
Drawable iconDrawable = AppUtils.getInstalledAppIcon(packageName); Drawable iconDrawable = AppUtils.getInstalledAppIcon(packageName);
if (iconDrawable != null) { if (iconDrawable != null) {
mIcon.setImageDrawable(iconDrawable); mIconView.setImageDrawable(iconDrawable);
mIsDefaultIcon = false; mIsDefaultIcon = false;
} }
mIcon.setVisibility(View.VISIBLE); mIconView.setVisibility(View.VISIBLE);
}, throwable -> { }, throwable -> {
mPackageName.setText(getString(R.string.format_default_package_name, file.getSimplifiedName().toLowerCase(Language.getPrefLanguage().getLocale()))); mPackageNameView.setText(getString(R.string.format_default_package_name, file.getSimplifiedName().toLowerCase(Language.getPrefLanguage().getLocale())));
mVersionName.setText(R.string.default_build_apk_version_name); mVersionNameView.setText(R.string.default_build_apk_version_name);
mVersionCode.setText(R.string.default_build_apk_version_code); mVersionCodeView.setText(R.string.default_build_apk_version_code);
mPackageNameParent.setHint(R.string.text_package_name); mPackageNameParentView.setHint(R.string.text_package_name);
mVersionNameParent.setHint(R.string.text_version_name); mVersionNameParentView.setHint(R.string.text_version_name);
mVersionCodeParent.setHint(R.string.text_version_code); mVersionCodeParentView.setHint(R.string.text_version_code);
mIcon.setVisibility(View.VISIBLE); mIconView.setVisibility(View.VISIBLE);
}); });
setSource(file); setSource(file);
@@ -579,7 +579,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
} }
void selectSourceFilePath() { void selectSourceFilePath() {
String initialDir = new File(mSourcePath.getText().toString()).getParent(); String initialDir = new File(mSourcePathView.getText().toString()).getParent();
new FileChooserDialogBuilder(this) new FileChooserDialogBuilder(this)
.title(R.string.text_source_file_path) .title(R.string.text_source_file_path)
.dir(EnvironmentUtils.getExternalStoragePath(), .dir(EnvironmentUtils.getExternalStoragePath(),
@@ -590,7 +590,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
private void setSource(File file) { private void setSource(File file) {
if (!file.isDirectory()) { if (!file.isDirectory()) {
mSourcePath.setText(file.getPath()); mSourcePathView.setText(file.getPath());
return; return;
} }
mProjectConfig = ProjectConfig.fromProjectDir(file.getPath()); mProjectConfig = ProjectConfig.fromProjectDir(file.getPath());
@@ -598,20 +598,20 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
return; return;
} }
mIsProjectLevelBuilding = true; mIsProjectLevelBuilding = true;
mOutputPath.setText(new File(mSource, mProjectConfig.getBuildDir()).getPath()); mOutputPathView.setText(new File(mSource, mProjectConfig.getBuildDir()).getPath());
mAppConfig.setVisibility(View.GONE); mAppConfigView.setVisibility(View.GONE);
mSourcePathContainer.setVisibility(View.GONE); mSourcePathContainerView.setVisibility(View.GONE);
} }
void selectOutputDirPath() { void selectOutputDirPath() {
String initialDir = new File(mOutputPath.getText().toString()).exists() String initialDir = new File(mOutputPathView.getText().toString()).exists()
? mOutputPath.getText().toString() ? mOutputPathView.getText().toString()
: WorkingDirectoryUtils.getPath(); : WorkingDirectoryUtils.getPath();
new FileChooserDialogBuilder(this) new FileChooserDialogBuilder(this)
.title(R.string.text_output_apk_path) .title(R.string.text_output_apk_path)
.dir(initialDir) .dir(initialDir)
.chooseDir() .chooseDir()
.singleChoice(dir -> mOutputPath.setText(dir.getPath())) .singleChoice(dir -> mOutputPathView.setText(dir.getPath()))
.show(); .show();
} }
@@ -633,14 +633,14 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
private boolean checkInputs() { private boolean checkInputs() {
if (mIsProjectLevelBuilding) { if (mIsProjectLevelBuilding) {
return checkNotEmpty(mOutputPath); return checkNotEmpty(mOutputPathView);
} }
return checkNotEmpty(mSourcePath) return checkNotEmpty(mSourcePathView)
& checkNotEmpty(mOutputPath) & checkNotEmpty(mOutputPathView)
& checkNotEmpty(mAppName) & checkNotEmpty(mAppNameView)
& checkNotEmpty(mVersionCode) & checkNotEmpty(mVersionCodeView)
& checkNotEmpty(mVersionName) & checkNotEmpty(mVersionNameView)
& checkPackageNameValid(mPackageName); & checkPackageNameValid(mPackageNameView);
} }
private boolean checkAbis() { private boolean checkAbis() {
@@ -773,12 +773,12 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
private void doBuildingApk() { private void doBuildingApk() {
ApkBuilder.AppConfig appConfig = createAppConfig(); ProjectConfig projectConfig = determineProjectConfig();
File tmpDir = new File(getCacheDir(), "build/"); File tmpDir = new File(getCacheDir(), "build/");
File outApk = new File(mOutputPath.getText().toString(), File outApk = new File(mOutputPathView.getText().toString(),
String.format("%s_v%s.apk", appConfig.getAppName(), appConfig.getVersionName())); String.format("%s_v%s.apk", projectConfig.getName(), projectConfig.getVersionName()));
showProgressDialog(); showProgressDialog();
Observable.fromCallable(() -> callApkBuilder(tmpDir, outApk, appConfig)) Observable.fromCallable(() -> callApkBuilder(tmpDir, outApk, projectConfig))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(apkBuilder -> { .subscribe(apkBuilder -> {
@@ -790,32 +790,33 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
}, this::onBuildFailed); }, this::onBuildFailed);
} }
private ApkBuilder.AppConfig createAppConfig() { private ProjectConfig determineProjectConfig() {
ArrayList<String> abis = collectCheckedItems(mFlexboxAbis); ArrayList<String> abis = collectCheckedItems(mFlexboxAbis);
ArrayList<String> libs = collectCheckedItems(mFlexboxLibs); ArrayList<String> libs = collectCheckedItems(mFlexboxLibs);
ArrayList<String> permissions = collectCheckedItems(mFlexboxPermissions); ArrayList<String> permissions = collectCheckedItems(mFlexboxPermissions);
ApkBuilder.AppConfig appConfig = mProjectConfig != null ProjectConfig projectConfig;
? ApkBuilder.AppConfig.fromProjectConfig(mSource, mProjectConfig) if (mProjectConfig != null) {
: new ApkBuilder.AppConfig() projectConfig = mProjectConfig
.setAppName(mAppName.getText().toString()) .ignoredDir(new File(mSource, mProjectConfig.getBuildDir()))
.setSourcePath(mSourcePath.getText().toString()) .setSourcePath(mSource)
.setPackageName(mPackageName.getText().toString()) .setIconPath(mProjectConfig.getIconPath() == null ? null : new File(mSource, mProjectConfig.getIconPath()).getPath());
.setVersionName(mVersionName.getText().toString())
.setVersionCode(Integer.parseInt(mVersionCode.getText().toString()))
.setIcon(mIsDefaultIcon ? null : () -> BitmapUtils.drawableToBitmap(mIcon.getDrawable()));
appConfig.setAbis(abis);
appConfig.setLibs(libs);
appConfig.setSignatureSchemes(mSignatureSchemes.getSelectedItem().toString());
if (mVerifiedKeyStores.getSelectedItemPosition() > 0) {
appConfig.setKeyStore((KeyStore) mVerifiedKeyStores.getSelectedItem());
} else { } else {
appConfig.setKeyStore(null); projectConfig = new ProjectConfig()
.setName(mAppNameView.getText().toString())
.setSourcePath(mSourcePathView.getText().toString())
.setPackageName(mPackageNameView.getText().toString())
.setVersionName(mVersionNameView.getText().toString())
.setVersionCode(Integer.parseInt(mVersionCodeView.getText().toString()))
.setIconGetter(mIsDefaultIcon ? null : () -> BitmapUtils.drawableToBitmap(mIconView.getDrawable()));
} }
appConfig.setPermissions(permissions);
return appConfig; return projectConfig
.setAbis(abis)
.setLibs(libs)
.setKeyStore(mVerifiedKeyStores.getSelectedItemPosition() > 0 ? (KeyStore) mVerifiedKeyStores.getSelectedItem() : null)
.setSignatureSchemes(mSignatureSchemes.getSelectedItem().toString())
.setPermissions(permissions);
} }
@NotNull @NotNull
@@ -843,12 +844,12 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
return libs; return libs;
} }
private ApkBuilder callApkBuilder(File tmpDir, File outApk, ApkBuilder.AppConfig appConfig) throws Exception { private ApkBuilder callApkBuilder(File tmpDir, 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, tmpDir.getPath())
.setProgressCallback(BuildActivity.this) .setProgressCallback(BuildActivity.this)
.prepare() .prepare()
.withConfig(appConfig) .withConfig(projectConfig)
.build() .build()
.sign() .sign()
.cleanWorkspace(); .cleanWorkspace();
@@ -920,7 +921,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(drawable -> { .subscribe(drawable -> {
mIcon.setImageDrawable(drawable); mIconView.setImageDrawable(drawable);
mIsDefaultIcon = false; mIsDefaultIcon = false;
}, Throwable::printStackTrace); }, Throwable::printStackTrace);
} }

View File

@@ -122,12 +122,12 @@ class ProjectConfigActivity : BaseActivity() {
} else { } else {
mAppName.setText(config.name) mAppName.setText(config.name)
setToolbarAsBack(config.name) setToolbarAsBack(config.name)
mVersionCode.setText(config.versionCode.toString()) mVersionCode.setText(config.versionCode.coerceAtLeast(0).toString())
mPackageName.setText(config.packageName) mPackageName.setText(config.packageName)
mVersionName.setText(config.versionName) mVersionName.setText(config.versionName)
mMainFileName.setText(config.mainScriptFile) mMainFileName.setText(config.mainScriptFile)
mProjectLocationWrapper.visibility = View.GONE mProjectLocationWrapper.visibility = View.GONE
config.icon?.let { icon -> config.iconPath?.let { icon ->
File(mDirectory, icon).takeIf { it.exists() }?.let { iconFile -> File(mDirectory, icon).takeIf { it.exists() }?.let { iconFile ->
Glide.with(this) Glide.with(this)
.load(iconFile) .load(iconFile)
@@ -259,7 +259,7 @@ class ProjectConfigActivity : BaseActivity() {
private fun saveIcon(b: Bitmap): Observable<String> { private fun saveIcon(b: Bitmap): Observable<String> {
return Observable.just(b) return Observable.just(b)
.map { bitmap: Bitmap -> .map { bitmap: Bitmap ->
var iconPath = mProjectConfig!!.icon var iconPath = mProjectConfig!!.iconPath
if (iconPath == null) { if (iconPath == null) {
iconPath = "res/logo.png" iconPath = "res/logo.png"
} }
@@ -272,7 +272,7 @@ class ProjectConfigActivity : BaseActivity() {
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnNext { iconPath: String? -> mProjectConfig!!.icon = iconPath } .doOnNext { iconPath: String? -> mProjectConfig!!.iconPath = iconPath }
} }
override fun onDestroy() { override fun onDestroy() {

View File

@@ -1,27 +1,32 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="128dp" android:layout_height="128dp"
android:layout_gravity="center|bottom" android:layout_gravity="center|bottom"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<ImageView <ImageView
android:layout_width="72dp" android:id="@+id/icon"
android:layout_height="72dp" android:visibility="gone"
android:layout_gravity="center" tools:visibility="visible"
android:src="@mipmap/ic_launcher" /> android:layout_width="72dp"
android:layout_height="72dp"
android:layout_gravity="center"
android:src="@mipmap/ic_launcher" />
<TextView <TextView
android:id="@+id/slug" android:id="@+id/slug"
android:layout_width="wrap_content" android:visibility="gone"
android:layout_height="wrap_content" tools:visibility="visible"
android:layout_gravity="center" android:layout_width="wrap_content"
android:layout_marginTop="20dp" android:layout_height="wrap_content"
android:text="@string/text_powered_by_autojs" android:layout_gravity="center"
android:textColor="@color/day_night_full" android:layout_marginTop="20dp"
android:textSize="16sp" /> android:text="@string/text_powered_by_autojs"
android:textColor="@color/day_night_full"
android:textSize="16sp" />
</LinearLayout> </LinearLayout>

View File

@@ -1,5 +1,5 @@
#Sat Jan 18 23:47:18 CST 2025 #Mon Jan 20 14:32:52 CST 2025
BUILD_TIME=1737215238545 BUILD_TIME=1737354772708
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=2977 VERSION_BUILD=2979
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