6.7.0 - Alpha23 - 优化打包过程对话框显示方式; 打包过程支持操作中止

This commit is contained in:
SuperMonster003
2026-03-07 13:01:14 +08:00
parent a2445495dc
commit 3e9724d7ab
20 changed files with 1875 additions and 198 deletions

View File

@@ -36,14 +36,18 @@ import pxb.android.StringItem
import pxb.android.axml.AxmlWriter
import zhao.arsceditor.ResDecoder.ARSCDecoder
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.concurrent.CancellationException
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
@@ -59,6 +63,9 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
private var mManifestEditor: ManifestEditor? = null
private var mInitVector: String? = null
private var mKey: String? = null
private var mCancelSignal: AtomicBoolean? = null
private var mPendingProjectConfigFile: File? = null
private var mPendingProjectConfigJson: String? = null
private lateinit var mProjectConfig: ProjectConfig
@@ -85,49 +92,157 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
fun setProgressCallback(callback: ProgressCallback?) = also { mProgressCallback = callback }
@Throws(IOException::class)
fun prepare() = also {
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onPrepare(this) } }
File(buildPath).mkdirs()
mApkPackager.unzip()
fun setCancelSignal(cancelSignal: AtomicBoolean?) = also {
mCancelSignal = cancelSignal
mApkPackager.setCancelSignal(cancelSignal)
}
// Throw early when cancellation is requested to avoid partial outputs.
// zh-CN: 当收到取消请求时尽早抛出, 以避免产生部分输出.
private fun ensureNotCancelled() {
if (mCancelSignal?.get() == true) {
throw CancellationException("Build aborted")
}
if (Thread.currentThread().isInterrupted) {
throw CancellationException("Build aborted")
}
}
// Copy streams with cancellation checks to keep abort responsive during large IO.
// zh-CN: 在大 IO 过程中加入取消检查, 保持中止响应.
private fun copyStreamWithCancel(input: InputStream, output: OutputStream, bufferSize: Int = 16 * 1024) {
val buffer = ByteArray(bufferSize)
var len: Int
while (input.read(buffer).also { len = it } > 0) {
ensureNotCancelled()
output.write(buffer, 0, len)
}
}
private fun notifyStepChanged(step: ProgressStep) {
mProgressCallback?.let { callback ->
GlobalAppContext.post {
when (step) {
ProgressStep.PREPARE -> callback.onPrepare(this)
ProgressStep.BUILD -> callback.onBuild(this)
ProgressStep.SIGN -> callback.onSign(this)
ProgressStep.CLEAN -> callback.onClean(this)
}
}
}
}
private fun notifyStepProgress(step: ProgressStep, title: String, detail: String?) {
mProgressCallback?.let { callback ->
GlobalAppContext.post {
callback.onStepProgress(
builder = this,
title = title,
detail = detail?.takeIf { it.isNotBlank() },
)
}
}
}
@Throws(IOException::class)
fun setScriptFile(path: String?) = also {
fun prepare(context: Context) = also {
ensureNotCancelled()
notifyStepChanged(ProgressStep.PREPARE)
notifyStepProgress(
ProgressStep.PREPARE,
context.getString(R.string.text_preparing_workspace),
buildPath,
)
File(buildPath).mkdirs()
notifyStepProgress(
ProgressStep.PREPARE,
context.getString(R.string.text_extracting_template_apk),
buildPath,
)
mApkPackager.unzip()
ensureNotCancelled()
notifyStepProgress(
ProgressStep.PREPARE,
context.getString(R.string.text_prepare_completed),
buildPath,
)
}
@Throws(IOException::class)
fun setScriptFile(context: Context, path: String?) = also {
ensureNotCancelled()
path?.let {
when {
PFiles.isDir(it) -> copyDir(it, "assets/project/")
else -> replaceFile(it, "assets/project/main.js")
PFiles.isDir(it) -> {
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_copying_project_directory),
it,
)
copyDir(context, it, "assets/project/")
}
else -> {
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_copying_script_file),
it,
)
replaceFile(context, it, "assets/project/main.js")
}
}
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_source_processing_completed),
it,
)
}
}
@Throws(IOException::class)
@Suppress("SameParameterValue")
private fun copyDir(srcPath: String, relativeDestPath: String) {
copyDir(File(srcPath), relativeDestPath)
private fun copyDir(context: Context, srcPath: String, relativeDestPath: String) {
copyDir(context, File(srcPath), relativeDestPath)
}
@Throws(IOException::class)
fun copyDir(srcFile: File, relativeDestPath: String) {
fun copyDir(context: Context, srcFile: File, relativeDestPath: String) {
ensureNotCancelled()
val destDirFile = File(buildPath, relativeDestPath).apply { mkdir() }
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_copying_directory),
"${srcFile.path} -> ${destDirFile.path}",
)
srcFile.listFiles()?.forEach { srcChildFile ->
ensureNotCancelled()
if (srcChildFile.isFile) {
if (srcChildFile.name.endsWith(JAVASCRIPT.extensionWithDot)) {
encryptToDir(srcChildFile, destDirFile)
encryptToDir(context, srcChildFile, destDirFile)
} else {
srcChildFile.copyTo(File(destDirFile, srcChildFile.name), true)
val destFile = File(destDirFile, srcChildFile.name)
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_copying_file),
"${srcChildFile.path} -> ${destFile.path}",
)
srcChildFile.copyTo(destFile, true)
}
} else {
if (!mProjectConfig.excludedDirs.contains(srcChildFile)) {
copyDir(srcChildFile, PFiles.join(relativeDestPath, srcChildFile.name + File.separator))
copyDir(context, srcChildFile, PFiles.join(relativeDestPath, srcChildFile.name + File.separator))
}
}
}
}
@Throws(IOException::class)
private fun encrypt(srcFile: File, destFile: File) {
private fun encrypt(context: Context, srcFile: File, destFile: File) {
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_encrypting_script),
"${srcFile.path} -> ${destFile.path}",
)
destFile.outputStream().use { os ->
writeHeader(os, JavaScriptFileSource(srcFile).executionMode.toShort())
AdvancedEncryptionStandard(mKey!!.toByteArray(), mInitVector!!)
@@ -136,34 +251,93 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
}
}
private fun encryptToDir(srcFile: File, destDirFile: File) {
private fun encryptToDir(context: Context, srcFile: File, destDirFile: File) {
val destFile = File(destDirFile, srcFile.name)
encrypt(srcFile, destFile)
encrypt(context, srcFile, destFile)
}
@Throws(IOException::class)
fun replaceFile(srcPath: String, relativeDestPath: String) = replaceFile(File(srcPath), relativeDestPath)
fun replaceFile(context: Context, srcPath: String, relativeDestPath: String) = replaceFile(context, File(srcPath), relativeDestPath)
@Throws(IOException::class)
fun replaceFile(srcFile: File, relativeDestPath: String) = also {
fun replaceFile(context: Context, srcFile: File, relativeDestPath: String) = also {
ensureNotCancelled()
val destFile = File(buildPath, relativeDestPath)
if (destFile.name.endsWith(JAVASCRIPT.extensionWithDot)) {
encrypt(srcFile, destFile)
encrypt(context, srcFile, destFile)
} else {
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_replacing_file),
"${srcFile.path} -> ${destFile.path}",
)
srcFile.copyTo(destFile, true)
}
}
@Throws(IOException::class)
fun withConfig(config: ProjectConfig) = also {
fun withConfig(context: Context, config: ProjectConfig) = also {
notifyStepChanged(ProgressStep.BUILD)
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_processing),
context.getString(R.string.text_preparing_build_config),
)
config.also { mProjectConfig = it }.run {
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_reading_splash_resources),
mResourcesArscFile.path,
)
retrieveSplashThemeResources(launchConfig)
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_configuring_manifest),
mManifestFile.path,
)
prepareManifestConfiguration(this)
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_configuring_package_name),
packageName,
)
setArscPackageName(packageName)
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_processing),
context.getString(R.string.text_updating_project_config),
)
updateProjectConfig(this)
copyAssetsRecursively("", File(buildPath, "assets"))
copyLibrariesByConfig(this)
setScriptFile(sourcePath)
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_copying_assets_to),
File(buildPath, "assets").path,
)
copyAssetsRecursively(context, "", File(buildPath, "assets"))
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_processing),
context.getString(R.string.text_copying_native_libraries),
)
copyLibrariesByConfig(context, this)
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_processing_source),
sourcePath,
)
setScriptFile(context, sourcePath)
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_processing),
context.getString(R.string.text_applying_binary_resources),
)
}
}
@@ -203,6 +377,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
fun editManifest(): ManifestEditor = ManifestEditorWithAuthorities(FileInputStream(mManifestFile)).also { mManifestEditor = it }
private fun updateProjectConfig(config: ProjectConfig) {
ensureNotCancelled()
// 这里为什么要有这样的一个方法? (
// 会不会是因为有些配置需要写入到文件中, 这些配置包括自增的版本号, 用户的选择或键入值等等
@@ -222,7 +397,8 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
ProjectConfig.fromProjectDir(config.sourcePath)?.let { sourceProjectConfig ->
sourceProjectConfig
.setBuildInfo(BuildInfo.generate(sourceProjectConfig.buildInfo.buildNumber + 1))
File(ProjectConfig.configFileOfDir(config.sourcePath)).writeText(sourceProjectConfig.toJson(true))
mPendingProjectConfigFile = File(ProjectConfig.configFileOfDir(config.sourcePath))
mPendingProjectConfigJson = sourceProjectConfig.toJson(true)
return@run sourceProjectConfig
}
}
@@ -254,11 +430,46 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
}
}
// Commit project config changes only after a successful build.
// zh-CN: 仅在构建成功后提交项目配置变更.
fun commitProjectConfigIfNeeded(context: Context) = also {
val pendingFile = mPendingProjectConfigFile
val pendingJson = mPendingProjectConfigJson
if (pendingFile == null || pendingJson == null) {
notifyStepProgress(
ProgressStep.SIGN,
context.getString(R.string.text_processing),
context.getString(R.string.text_sign_stage_completed),
)
return@also
}
ensureNotCancelled()
notifyStepProgress(
ProgressStep.SIGN,
context.getString(R.string.text_writing_project_config),
pendingFile.path,
)
pendingFile.writeText(pendingJson)
mPendingProjectConfigFile = null
mPendingProjectConfigJson = null
notifyStepProgress(
ProgressStep.SIGN,
context.getString(R.string.text_processing),
context.getString(R.string.text_sign_stage_completed),
)
}
@Throws(Exception::class)
fun build() = also {
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onBuild(this) } }
fun build(context: Context) = also {
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_processing),
context.getString(R.string.text_building_resources),
)
mProjectConfig.iconBitmapGetter?.let { callable ->
runCatching {
ensureNotCancelled()
val tableBlock = TableBlock.load(mResourcesArscFile)
val packageName = "${GlobalAppContext.get().packageName}.inrt"
val packageBlock = tableBlock.getOrCreatePackage(0x7f, packageName).also {
@@ -273,17 +484,42 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
it.createNewFile()
}
}
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_writing_app_icon),
file.path,
)
callable.call()?.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream(file))
}.onFailure { throw RuntimeException(it) }
}
mManifestEditor?.let {
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_writing_manifest),
mManifestFile.path,
)
it.commit()
it.writeTo(FileOutputStream(mManifestFile))
}
mArscPackageName?.let { buildArsc() }
mArscPackageName?.let {
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_writing_resources_arsc),
mResourcesArscFile.path,
)
buildArsc()
}
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_processing),
context.getString(R.string.text_build_completed),
)
}
private fun copyAssetsRecursively(assetPath: String, targetFile: File) {
private fun copyAssetsRecursively(context: Context, assetPath: String, targetFile: File) {
ensureNotCancelled()
if (targetFile.isFile && targetFile.exists()) return
val list = mAssetManager.list(assetPath) ?: return
if (list.isEmpty()) /* asset is a file */ {
@@ -292,9 +528,14 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
return
}
}
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_copying_asset),
"assets/$assetPath -> ${targetFile.path}",
)
mAssetManager.open(assetPath).use { input ->
FileOutputStream(targetFile.absolutePath).use { output ->
input.copyTo(output)
copyStreamWithCancel(input, output)
output.flush()
}
}
@@ -302,36 +543,80 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
if (mAssetsDirExcludes.any { assetPath.matches(Regex("$it(/[^/]+)*")) }) {
return
}
val displayPath = if (assetPath.isEmpty()) "/" else "/$assetPath"
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_preparing_assets_dir),
displayPath,
)
targetFile.delete()
targetFile.mkdir()
list.forEach {
ensureNotCancelled()
val sourcePath = if (assetPath.isEmpty()) it else "$assetPath/$it"
copyAssetsRecursively(sourcePath, File(targetFile, it))
copyAssetsRecursively(context, sourcePath, File(targetFile, it))
}
}
}
@Throws(Exception::class)
fun sign() = also {
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onSign(this) } }
val fos = FileOutputStream(outApkFile)
TinySign.sign(File(buildPath), fos)
fos.close()
fun sign(context: Context) = also {
ensureNotCancelled()
notifyStepChanged(ProgressStep.SIGN)
val workspaceDir = File(buildPath)
outApkFile.parentFile?.let { if (!it.exists()) it.mkdirs() }
val unsignedApkFile = outApkFile
val tmpOutputApk = File(outApkFile.parentFile ?: workspaceDir, "${outApkFile.name}.signed.tmp")
if (tmpOutputApk.exists()) {
tmpOutputApk.delete()
}
notifyStepProgress(
ProgressStep.SIGN,
context.getString(R.string.text_creating_unsigned_apk),
unsignedApkFile.path,
)
try {
BufferedOutputStream(FileOutputStream(unsignedApkFile, false), 256 * 1024).use { fos ->
TinySign.sign(workspaceDir, fos)
fos.flush()
}
} catch (e: Exception) {
if (tmpOutputApk.exists() && !tmpOutputApk.delete()) {
Log.w(TAG, "Failed to delete temporary signed apk after unsigned apk creation failure: ${tmpOutputApk.path}")
}
if (e.hasNoSpaceLeft()) {
throw IOException("No space left on device while creating unsigned APK: ${unsignedApkFile.path}", e)
}
throw e
}
notifyStepProgress(
ProgressStep.SIGN,
context.getString(R.string.text_unsigned_apk_created),
unsignedApkFile.path,
)
val defaultKeyStoreFile = File(buildPath, "default_key_store.bks")
val tmpOutputApk = File(buildPath, "temp.apk")
if (mProjectConfig.keyStore == null) {
// Replace FileUtils.copyInputStreamToFile(...).
// zh-CN: 替换 FileUtils.copyInputStreamToFile(...).
defaultKeyStoreFile.parentFile?.let { if (!it.exists()) it.mkdirs() }
GlobalAppContext.get().assets.open("default_key_store.bks").use { input ->
FileOutputStream(defaultKeyStoreFile, false).use { output ->
input.copyTo(output, bufferSize = 16 * 1024)
output.fd.sync()
// Replace FileUtils.copyInputStreamToFile(...).
// zh-CN: 替换 FileUtils.copyInputStreamToFile(...).
ensureNotCancelled()
defaultKeyStoreFile.parentFile?.let { if (!it.exists()) it.mkdirs() }
notifyStepProgress(
ProgressStep.SIGN,
context.getString(R.string.text_preparing_keystore),
defaultKeyStoreFile.path,
)
GlobalAppContext.get().assets.open("default_key_store.bks").use { input ->
FileOutputStream(defaultKeyStoreFile, false).use { output ->
copyStreamWithCancel(input, output)
output.fd.sync()
}
}
}
val signer = ApkSigner(outApkFile, tmpOutputApk).apply {
ensureNotCancelled()
val signer = ApkSigner(unsignedApkFile, tmpOutputApk).apply {
useDefaultSignatureVersion = false
v1SigningEnabled = "V1" in mProjectConfig.signatureScheme
v2SigningEnabled = "V2" in mProjectConfig.signatureScheme
@@ -350,24 +635,84 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
alias = it.alias
aliasPassword = AESUtils.decrypt(it.aliasPassword)
}
notifyStepProgress(
ProgressStep.SIGN,
context.getString(R.string.text_using_keystore),
keyStoreFile.path,
)
// Re-sign using ApkSigner.
// zh-CN: 使用 ApkSigner 重新签名.
if (!signer.signRelease(keyStoreFile, password, alias, aliasPassword)) {
throw java.lang.RuntimeException("Failed to re-sign using ApkSigner")
ensureNotCancelled()
notifyStepProgress(
ProgressStep.SIGN,
context.getString(R.string.text_processing),
context.getString(R.string.text_re_signing_apk),
)
try {
if (!signer.signRelease(keyStoreFile, password, alias, aliasPassword)) {
throw RuntimeException("Failed to re-sign using ApkSigner")
}
} catch (e: Exception) {
if (tmpOutputApk.exists() && !tmpOutputApk.delete()) {
Log.w(TAG, "Failed to delete temporary signed apk after re-sign failure: ${tmpOutputApk.path}")
}
if (e.hasNoSpaceLeft()) {
throw IOException("No space left on device while re-signing APK: ${tmpOutputApk.path}", e)
}
throw e
}
try {
outApkFile.parentFile?.let { if (!it.exists()) it.mkdirs() }
tmpOutputApk.copyTo(outApkFile, overwrite = true)
} catch (e: java.lang.Exception) {
throw java.lang.RuntimeException(e)
ensureNotCancelled()
notifyStepProgress(
ProgressStep.SIGN,
context.getString(R.string.text_writing_signed_apk),
outApkFile.path,
)
if (outApkFile.exists() && !outApkFile.delete()) {
throw IOException("Failed to delete unsigned apk before replace: ${outApkFile.path}")
}
if (!tmpOutputApk.renameTo(outApkFile)) {
copyFileWithLargeBuffer(tmpOutputApk, outApkFile)
}
} catch (e: Exception) {
if (e.hasNoSpaceLeft()) {
throw IOException("No space left on device while writing signed APK: ${outApkFile.path}", e)
}
throw RuntimeException(e)
} finally {
if (tmpOutputApk.exists() && !tmpOutputApk.delete()) {
Log.w(TAG, "Failed to delete temporary signed apk: ${tmpOutputApk.path}")
}
}
notifyStepProgress(
ProgressStep.SIGN,
context.getString(R.string.text_sign_completed),
outApkFile.path,
)
}
fun cleanWorkspace() = also {
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onClean(this) } }
delete(File(buildPath))
fun cleanWorkspace(context: Context) = also {
notifyStepChanged(ProgressStep.CLEAN)
val workspace = File(buildPath)
val totalTargets = countDeleteTargets(workspace).coerceAtLeast(1)
val deletedTargets = intArrayOf(0)
notifyStepProgress(
ProgressStep.CLEAN,
context.getString(R.string.text_cleaning_workspace),
workspace.path,
)
deleteWithProgress(context, workspace, totalTargets, deletedTargets)
notifyStepProgress(
ProgressStep.CLEAN,
context.getString(R.string.text_clean_completed),
workspace.path,
)
}
fun finish() = also {
mProgressCallback?.let { callback -> GlobalAppContext.post { callback.onFinished(this) } }
}
@Throws(IOException::class)
@@ -377,23 +722,86 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
private fun buildArsc() {
val oldArsc = File(buildPath, "resources.arsc")
val newArsc = File(buildPath, "resources.arsc.new")
val decoder = ARSCDecoder(BufferedInputStream(FileInputStream(oldArsc)), null, false)
decoder.CloneArsc(FileOutputStream(newArsc), mArscPackageName, true)
BufferedInputStream(FileInputStream(oldArsc), 256 * 1024).use { input ->
BufferedOutputStream(FileOutputStream(newArsc, false), 256 * 1024).use { output ->
val decoder = ARSCDecoder(input, null, false)
decoder.CloneArsc(output, mArscPackageName, true)
output.flush()
}
}
oldArsc.delete()
newArsc.renameTo(oldArsc)
if (!newArsc.renameTo(oldArsc)) {
copyFileWithLargeBuffer(newArsc, oldArsc)
newArsc.delete()
}
}
private fun delete(file: File) {
file.apply { if (isDirectory) listFiles()?.forEach { delete(it) } }.also { it.delete() }
private fun copyFileWithLargeBuffer(source: File, target: File, bufferSize: Int = 256 * 1024) {
FileInputStream(source).use { input ->
FileOutputStream(target, false).use { output ->
val buffer = ByteArray(bufferSize)
var len: Int
while (input.read(buffer).also { len = it } > 0) {
ensureNotCancelled()
output.write(buffer, 0, len)
}
}
}
}
private fun Throwable.hasNoSpaceLeft(): Boolean {
var current: Throwable? = this
while (current != null) {
val message = current.message.orEmpty()
if (message.contains("ENOSPC", ignoreCase = true) || message.contains("No space left on device", ignoreCase = true)) {
return true
}
current = current.cause
}
return false
}
private fun countDeleteTargets(file: File): Int {
if (!file.exists()) {
return 0
}
return if (file.isDirectory) {
1 + (file.listFiles()?.sumOf(::countDeleteTargets) ?: 0)
} else {
1
}
}
private fun deleteWithProgress(context: Context, file: File, totalTargets: Int, deletedTargets: IntArray) {
ensureNotCancelled()
if (file.isDirectory) {
file.listFiles()?.forEach { child ->
deleteWithProgress(context, child, totalTargets, deletedTargets)
}
}
notifyStepProgress(
ProgressStep.CLEAN,
context.getString(R.string.text_deleting),
file.path,
)
file.delete()
deletedTargets[0] += 1
}
enum class ProgressStep {
PREPARE,
BUILD,
SIGN,
CLEAN,
}
interface ProgressCallback {
fun onPrepare(builder: ApkBuilder)
fun onBuild(builder: ApkBuilder)
fun onSign(builder: ApkBuilder)
fun onClean(builder: ApkBuilder)
fun onStepProgress(builder: ApkBuilder, title: String, detail: String?)
fun onFinished(builder: ApkBuilder)
}
private inner class ManifestEditorWithAuthorities(manifestInputStream: InputStream?) : ManifestEditor(manifestInputStream) {
@@ -422,7 +830,8 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
}
}
private fun copyLibrariesByConfig(config: ProjectConfig) {
private fun copyLibrariesByConfig(context: Context, config: ProjectConfig) {
ensureNotCancelled()
// @Hint by SuperMonster003 on Dec 11, 2023.
// ! The list contains only abi names not matching the canonical name itself.
@@ -434,47 +843,78 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
// Try extracting native libraries from installed plugin APKs if needed.
// zh-CN: 如有需要, 尝试从已安装插件 APK 中解压 native 库文件.
ensureAndExtractPluginLibrariesIfNeeded(config, potentialAbiAliasList)
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_processing),
context.getString(R.string.text_resolving_plugin_native_libraries),
)
ensureAndExtractPluginLibrariesIfNeeded(context, config, potentialAbiAliasList)
config.abis.forEach { abiCanonicalName ->
copyLibrariesByAbi(abiCanonicalName, abiCanonicalName)
ensureNotCancelled()
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_copying_libraries_for_abi),
abiCanonicalName,
)
copyLibrariesByAbi(context, abiCanonicalName, abiCanonicalName)
potentialAbiAliasList[abiCanonicalName]?.let { abiAliasName ->
copyLibrariesByAbi(abiAliasName, abiCanonicalName)
copyLibrariesByAbi(context, abiAliasName, abiCanonicalName)
}
}
}
private fun ensureAndExtractPluginLibrariesIfNeeded(
context: Context,
config: ProjectConfig,
potentialAbiAliasList: Map<String, String>,
) {
ensureNotCancelled()
Lib.entries.mapNotNull {
if (it.isPlugin && config.libs.contains(it.label)) it.toPluginPair() else null
}.forEach { (lib, plugin) ->
ensureNotCancelled()
// Select plugin service by variant.
// zh-CN: 通过 variant 选择插件服务.
val (serviceInfo, selectedVariant) = selectPluginServiceOrThrow(
lib = lib,
action = plugin.action,
)
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_selected_plugin),
"${lib.label} (${selectedVariant.variant}) from ${serviceInfo.packageName}",
)
Log.i(TAG, "Selected ${lib.label} plugin: variant=${selectedVariant.variant}, pkg=${serviceInfo.packageName}")
// Extract libraries from installed plugin APK (variant-aware).
// zh-CN: 从已安装插件 APK 中解压 so 文件, 并按变体裁剪.
extractLibrariesFromPluginApkOrThrow(
context = context,
config = config,
requiredLibNames = selectedVariant.libsToInclude,
serviceInfo = serviceInfo,
potentialAbiAliasList = potentialAbiAliasList,
)
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_extracted_plugin_libraries),
lib.label,
)
// Extract assets (models/labels) from installed plugin APK (variant-aware).
// zh-CN: 从已安装插件 APK 中解压 assets 资源 (models/labels), 并按变体裁剪.
extractAssetsFromPluginApkOrThrow(
context = context,
serviceInfo = serviceInfo,
pluginLibVariant = selectedVariant,
)
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_extracted_plugin_assets),
lib.label,
)
}
}
@@ -482,6 +922,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
lib: Lib,
action: String,
): Pair<ServiceInfo, PluginLibVariant> {
ensureNotCancelled()
val pm = globalContext.packageManager
val moduleLabel = lib.label
val pluginPair = lib.toPluginPair()
@@ -526,6 +967,7 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
}
private fun queryPluginInfoBlocking(context: Context, serviceInfo: ServiceInfo, pluginPair: Pair<Lib, PluginLib>): PluginLibVariant {
ensureNotCancelled()
// Bind service and call getInfo() synchronously (packaging-time only).
// zh-CN: 同步绑定服务并调用 getInfo() (仅打包阶段使用).
val latch = CountDownLatch(1)
@@ -586,9 +1028,11 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
}
private fun extractAssetsFromPluginApkOrThrow(
context: Context,
serviceInfo: ServiceInfo,
pluginLibVariant: PluginLibVariant,
) {
ensureNotCancelled()
val pm = globalContext.packageManager
val appInfo = getApplicationInfoCompat(pm, serviceInfo.packageName)
@@ -603,7 +1047,9 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
// zh-CN: 解压必需前缀.
val missingRequired = mutableListOf<String>()
requiredPrefixes.forEach { prefix ->
ensureNotCancelled()
val ok = extractAssetsByPrefixFromApks(
context = context,
apkPaths = apkPaths,
assetPrefix = prefix,
)
@@ -620,7 +1066,9 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
// Extract optional prefixes (best-effort).
// zh-CN: 解压可选前缀 (尽力而为).
optionalPrefixes.forEach { prefix ->
ensureNotCancelled()
extractAssetsByPrefixFromApks(
context = context,
apkPaths = apkPaths,
assetPrefix = prefix,
)
@@ -628,15 +1076,19 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
}
private fun extractAssetsByPrefixFromApks(
context: Context,
apkPaths: List<String>,
assetPrefix: String,
): Boolean {
ensureNotCancelled()
var extractedAny = false
apkPaths.forEach { apkPath ->
ensureNotCancelled()
runCatching {
ZipFile(apkPath).use { zip ->
val entries = zip.entries()
while (entries.hasMoreElements()) {
ensureNotCancelled()
val entry: ZipEntry = entries.nextElement()
val name = entry.name
if (!name.startsWith(assetPrefix)) continue
@@ -646,10 +1098,15 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
val outFile = File(buildPath, "assets/$relative").apply {
parentFile?.let { parent -> if (!parent.exists()) parent.mkdirs() }
}
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_extracting_plugin_asset),
"$apkPath!/$name -> ${outFile.path}",
)
zip.getInputStream(entry).use { input ->
FileOutputStream(outFile, false).use { output ->
input.copyTo(output, bufferSize = 16 * 1024)
copyStreamWithCancel(input, output)
output.fd.sync()
}
}
@@ -667,11 +1124,13 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
}
private fun extractLibrariesFromPluginApkOrThrow(
context: Context,
config: ProjectConfig,
requiredLibNames: List<String>,
serviceInfo: ServiceInfo,
potentialAbiAliasList: Map<String, String>,
) {
ensureNotCancelled()
val pm = globalContext.packageManager
val appInfo = getApplicationInfoCompat(pm, serviceInfo.packageName)
@@ -683,13 +1142,16 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
val missingPairs = mutableListOf<Pair<String, String>>() // (abi, soName)
config.abis.forEach { abiCanonicalName ->
ensureNotCancelled()
val abiCandidates = buildList {
add(abiCanonicalName)
potentialAbiAliasList[abiCanonicalName]?.let { add(it) }
}.distinct()
requiredLibNames.forEach { soName ->
ensureNotCancelled()
val ok = extractFirstMatchedSoFromApks(
context = context,
apkPaths = apkPaths,
abiCandidates = abiCandidates,
soName = soName,
@@ -718,23 +1180,32 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
}
private fun extractFirstMatchedSoFromApks(
context: Context,
apkPaths: List<String>,
abiCandidates: List<String>,
soName: String,
abiDestName: String,
): Boolean {
ensureNotCancelled()
apkPaths.forEach { apkPath ->
ensureNotCancelled()
runCatching {
ZipFile(apkPath).use { zip ->
abiCandidates.forEach { abiInApk ->
ensureNotCancelled()
val entryName = "lib/$abiInApk/$soName"
val entry = zip.getEntry(entryName) ?: return@forEach
val outFile = File(buildPath, "lib/$abiDestName/$soName").apply {
parentFile?.let { parent -> if (!parent.exists()) parent.mkdirs() }
}
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_extracting_plugin_so),
"$apkPath!/$entryName -> ${outFile.path}",
)
zip.getInputStream(entry).use { input ->
FileOutputStream(outFile, false).use { output ->
input.copyTo(output, bufferSize = 16 * 1024)
copyStreamWithCancel(input, output)
output.fd.sync()
}
}
@@ -760,18 +1231,26 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
}
}
private fun copyLibrariesByAbi(abiSrcName: String, abiDestName: String) {
private fun copyLibrariesByAbi(context: Context, abiSrcName: String, abiDestName: String) {
ensureNotCancelled()
// @Reference to LZX284 (https://github.com/LZX284) by SuperMonster003 on Dec 11, 2023.
// ! http://pr.autojs6.com/187/files#diff-d932ac49867d4610f8eeb21b59306e8e923d016cbca192b254caebd829198856R61
val srcLibDir = File(appApkFile.parent, LIBRARY_DIR).path
mLibsIncludes.distinct().forEach { libName ->
ensureNotCancelled()
runCatching {
File(srcLibDir, "$abiSrcName/$libName").takeIf { it.exists() }?.copyTo(
File(buildPath, "lib/$abiDestName/$libName"),
overwrite = true
)
val srcFile = File(srcLibDir, "$abiSrcName/$libName")
val destFile = File(buildPath, "lib/$abiDestName/$libName")
srcFile.takeIf { it.exists() }?.let {
notifyStepProgress(
ProgressStep.BUILD,
context.getString(R.string.text_copying_library),
"${srcFile.path} -> ${destFile.path}",
)
it.copyTo(destFile, overwrite = true)
}
}.onFailure { it.printStackTrace() }
}
}

View File

@@ -4,12 +4,16 @@ import android.text.TextUtils;
import org.autojs.autojs.apkbuilder.util.StreamUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CancellationException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -20,8 +24,9 @@ import pxb.android.tinysign.TinySign;
*/
public class ApkPackager {
private InputStream mApkInputStream;
private String mWorkspacePath;
private final InputStream mApkInputStream;
private final String mWorkspacePath;
private AtomicBoolean mCancelSignal;
public ApkPackager(InputStream apkInputStream, String workspacePath) {
mApkInputStream = apkInputStream;
@@ -33,20 +38,47 @@ public class ApkPackager {
mWorkspacePath = workspacePath;
}
public ApkPackager setCancelSignal(AtomicBoolean cancelSignal) {
mCancelSignal = cancelSignal;
return this;
}
// Throw early when cancellation is requested to avoid partial outputs.
// zh-CN: 当收到取消请求时尽早抛出, 以避免产生部分输出.
private void ensureNotCancelled() {
if (mCancelSignal != null && mCancelSignal.get()) {
throw new CancellationException("Build aborted");
}
if (Thread.currentThread().isInterrupted()) {
throw new CancellationException("Build aborted");
}
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void unzip() throws IOException {
ZipInputStream zis = new ZipInputStream(mApkInputStream);
for (ZipEntry e = zis.getNextEntry(); e != null; e = zis.getNextEntry()) {
String name = e.getName();
if (!e.isDirectory() && !TextUtils.isEmpty(name)) {
File file = new File(mWorkspacePath, name);
System.out.println(file);
file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file);
StreamUtils.write(zis, fos);
fos.close();
// Buffer IO to reduce syscall overhead during APK extraction.
// zh-CN: 使用缓冲 IO 以减少 APK 解压过程中的系统调用开销.
try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(mApkInputStream, StreamUtils.DEFAULT_BUFFER_SIZE))) {
for (ZipEntry e = zis.getNextEntry(); e != null; e = zis.getNextEntry()) {
ensureNotCancelled();
String name = e.getName();
if (!e.isDirectory() && !TextUtils.isEmpty(name)) {
File file = new File(mWorkspacePath, name);
File parentFile = file.getParentFile();
if (parentFile != null) {
parentFile.mkdirs();
}
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file), StreamUtils.DEFAULT_BUFFER_SIZE)) {
byte[] buffer = new byte[StreamUtils.DEFAULT_BUFFER_SIZE];
int len;
while ((len = zis.read(buffer)) > 0) {
ensureNotCancelled();
bos.write(buffer, 0, len);
}
}
}
}
}
zis.close();
}
public void repackage(String newApkPath) throws Exception {

View File

@@ -10,8 +10,10 @@ import java.io.OutputStream;
*/
public class StreamUtils {
public static final int DEFAULT_BUFFER_SIZE = 16 * 1024;
public static void write(InputStream inputStream, OutputStream out) throws IOException {
byte[] buffer = new byte[4096];
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int len;
while ((len = inputStream.read(buffer)) > 0) {
out.write(buffer, 0, len);
@@ -20,7 +22,7 @@ public class StreamUtils {
public static byte[] readAsBytes(InputStream inputStream) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int len;
while ((len = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, len);

View File

@@ -4,8 +4,10 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.SystemClock;
import android.text.Editable;
import android.text.TextUtils;
import android.text.util.Linkify;
@@ -24,6 +26,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Pair;
import androidx.lifecycle.ViewModelProvider;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.google.android.flexbox.FlexboxLayout;
import com.google.android.material.textfield.TextInputLayout;
@@ -35,14 +38,15 @@ import net.dongliu.apk.parser.bean.ApkMeta;
import org.autojs.autojs.apkbuilder.ApkBuilder;
import org.autojs.autojs.apkbuilder.keystore.KeyStore;
import org.autojs.autojs.core.pref.Language;
import org.autojs.autojs.util.DialogUtils;
import org.autojs.autojs.external.fileprovider.AppFileProvider;
import org.autojs.autojs.model.explorer.Explorers;
import org.autojs.autojs.model.script.ScriptFile;
import org.autojs.autojs.pio.PFiles;
import org.autojs.autojs.project.ProjectConfig;
import org.autojs.autojs.runtime.api.AppUtils;
import org.autojs.autojs.runtime.api.AppUtils.Companion.SimpleVersionInfo;
import org.autojs.autojs.runtime.api.augment.pinyin.Pinyin;
import org.autojs.autojs.theme.ThemeColorManager;
import org.autojs.autojs.ui.BaseActivity;
import org.autojs.autojs.ui.common.NotAskAgainDialog;
import org.autojs.autojs.ui.error.ErrorDialogActivity;
@@ -55,13 +59,17 @@ import org.autojs.autojs.ui.widget.RoundCheckboxWithText;
import org.autojs.autojs.util.AndroidUtils;
import org.autojs.autojs.util.AndroidUtils.Abi;
import org.autojs.autojs.util.BitmapUtils;
import org.autojs.autojs.util.ColorUtils;
import org.autojs.autojs.util.DialogUtils;
import org.autojs.autojs.util.EnvironmentUtils;
import org.autojs.autojs.util.IntentUtils;
import org.autojs.autojs.util.IntentUtils.ToastExceptionHolder;
import org.autojs.autojs.util.StringUtils;
import org.autojs.autojs.util.ViewUtils;
import org.autojs.autojs.util.WorkingDirectoryUtils;
import org.autojs.autojs6.R;
import org.autojs.autojs6.databinding.ActivityBuildBinding;
import org.autojs.autojs6.databinding.DialogBuildProgressBinding;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -70,12 +78,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.CancellationException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.regex.Pattern;
@@ -89,7 +99,7 @@ import static org.autojs.autojs.util.StringUtils.key;
* Created by Stardust on Oct 22, 2017.
* Modified by SuperMonster003 as of Jan 6, 2026.
*
* @noinspection ResultOfMethodCallIgnored
* @noinspection ResultOfMethodCallIgnored, unused
*/
public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCallback {
@@ -224,6 +234,35 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
private ProjectConfig mProjectConfig;
private MaterialDialog mProgressDialog;
private ImageView mProgressPrepareIcon;
private TextView mProgressPrepareText;
private ImageView mProgressBuildIcon;
private TextView mProgressBuildText;
private ImageView mProgressSignIcon;
private TextView mProgressSignText;
private ImageView mProgressCleanIcon;
private TextView mProgressCleanText;
private TextView mProgressPrepareDurationText;
private TextView mProgressBuildDurationText;
private TextView mProgressSignDurationText;
private TextView mProgressCleanDurationText;
private TextView mStateTitleText;
private TextView mStateContentText;
private final EnumMap<BuildStep, Long> mStepDurationMs = new EnumMap<>(BuildStep.class);
@Nullable
private BuildStep mCurrentTimingStep;
private long mCurrentTimingStepStartedAtMs;
// Track cancellation state for cooperative build abort.
// zh-CN: 跟踪取消状态, 用于协作式中止构建.
private final AtomicBoolean mBuildCancelled = new AtomicBoolean(false);
@Nullable
private File mBuildWorkspace;
@Nullable
private File mBuildOutputApk;
@Nullable
private File mBuildOutputApkBackup;
@Nullable
private Thread mBuildThread;
private String mSource;
private boolean mIsDefaultIcon = true;
private boolean mIsProjectLevelBuilding;
@@ -712,7 +751,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
}
@Override
protected void onNewIntent(Intent intent) {
protected void onNewIntent(@NonNull Intent intent) {
super.onNewIntent(intent);
}
@@ -910,18 +949,40 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
block == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
@SuppressLint("CheckResult")
private void doBuildingApk() {
ProjectConfig projectConfig = determineProjectConfig();
File buildPath = new File(getCacheDir(), "build/");
File outApk = new File(mOutputPathView.getText().toString(),
String.format("%s_v%s.apk", projectConfig.getName(), projectConfig.getVersionName()));
File outApkBackup = null;
try {
outApkBackup = backupExistingOutputApkIfNeeded(outApk);
} catch (IOException e) {
Log.e(LOG_TAG, "Failed to backup existing output apk", e);
ErrorDialogActivity.showErrorDialog(this, R.string.text_failed_to_build, e.getMessage());
return;
}
mBuildCancelled.set(false);
mBuildWorkspace = buildPath;
mBuildOutputApk = outApk;
mBuildOutputApkBackup = outApkBackup;
showProgressDialog();
Observable.fromCallable(() -> callApkBuilder(buildPath, outApk, projectConfig))
Observable.fromCallable(() -> {
mBuildThread = Thread.currentThread();
try {
return callApkBuilder(buildPath, outApk, projectConfig);
} finally {
mBuildThread = null;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(apkBuilder -> {
if (mBuildCancelled.get()) {
handleBuildAborted();
return;
}
if (apkBuilder != null) {
onBuildSuccessful(outApk);
} else {
@@ -930,6 +991,34 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
}, this::onBuildFailed);
}
@Nullable
private File backupExistingOutputApkIfNeeded(@NonNull File outApk) throws IOException {
if (!outApk.exists()) {
return null;
}
File parentDir = outApk.getAbsoluteFile().getParentFile();
if (parentDir == null) {
throw new IOException("Invalid output apk path: " + outApk.getPath());
}
File backupApk = new File(parentDir, outApk.getName() + "." + System.nanoTime() + ".bak");
if (backupApk.exists() && !backupApk.delete()) {
throw new IOException("Failed to remove stale output apk backup: " + backupApk.getPath());
}
if (outApk.renameTo(backupApk)) {
return backupApk;
}
if (!PFiles.copy(outApk.getPath(), backupApk.getPath())) {
throw new IOException("Failed to backup existing output apk: " + outApk.getPath());
}
if (!outApk.delete()) {
if (!backupApk.delete()) {
Log.w(LOG_TAG, "Failed to delete incomplete backup apk after backup cleanup failure: " + backupApk.getPath());
}
throw new IOException("Failed to remove original output apk after backup copy: " + outApk.getPath());
}
return backupApk;
}
private ProjectConfig determineProjectConfig() {
ArrayList<String> abis = collectCheckedItems(mFlexboxAbisView);
ArrayList<String> libs = collectCheckedItems(mFlexboxLibsView);
@@ -988,71 +1077,362 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
InputStream templateApk = getAssets().open(TEMPLATE_APK_NAME);
return new ApkBuilder(templateApk, outApk, buildPath.getPath())
.setProgressCallback(BuildActivity.this)
.prepare()
.withConfig(projectConfig)
.build()
.sign()
.cleanWorkspace();
.setCancelSignal(mBuildCancelled)
.prepare(BuildActivity.this)
.withConfig(BuildActivity.this, projectConfig)
.build(BuildActivity.this)
.sign(BuildActivity.this)
.commitProjectConfigIfNeeded(BuildActivity.this)
.cleanWorkspace(BuildActivity.this)
.finish();
}
private enum BuildStep {
PREPARE,
BUILD,
SIGN,
CLEAN,
FINISHED,
}
private void showProgressDialog() {
dismissProgressDialog();
View contentView = DialogBuildProgressBinding.inflate(getLayoutInflater()).getRoot();
mProgressPrepareIcon = contentView.findViewById(R.id.icon_prepare);
mProgressPrepareText = contentView.findViewById(R.id.text_prepare);
mProgressBuildIcon = contentView.findViewById(R.id.icon_build);
mProgressBuildText = contentView.findViewById(R.id.text_build);
mProgressSignIcon = contentView.findViewById(R.id.icon_sign);
mProgressSignText = contentView.findViewById(R.id.text_sign);
mProgressCleanIcon = contentView.findViewById(R.id.icon_clean);
mProgressCleanText = contentView.findViewById(R.id.text_clean);
mProgressPrepareDurationText = contentView.findViewById(R.id.text_prepare_duration);
mProgressBuildDurationText = contentView.findViewById(R.id.text_build_duration);
mProgressSignDurationText = contentView.findViewById(R.id.text_sign_duration);
mProgressCleanDurationText = contentView.findViewById(R.id.text_clean_duration);
mStateTitleText = contentView.findViewById(R.id.text_state_title);
mStateContentText = contentView.findViewById(R.id.text_state_content);
resetStepDurations();
applyThemeColorIcons(mProgressPrepareIcon, mProgressBuildIcon, mProgressSignIcon, mProgressCleanIcon);
mProgressDialog = new MaterialDialog.Builder(this)
.progress(true, 100)
.content(R.string.text_in_progress)
.title(R.string.text_building_apk)
.customView(contentView, false)
.neutralText("")
.negativeText("")
.positiveText(R.string.dialog_button_abort)
.positiveColorRes(R.color.dialog_button_caution)
.onPositive((dialog, which) -> requestBuildAbort())
.autoDismiss(false)
.cancelable(false)
.show();
updateProgressDialog(BuildStep.PREPARE);
mStateTitleText.setText(getString(R.string.text_property_colon, getString(R.string.text_processing)));
}
private void applyThemeColorIcons(ImageView... imageView) {
int backgroundColor = getColor(R.color.window_background);
int adjustedColor = ColorUtils.adjustColorForContrast(backgroundColor, ThemeColorManager.getColorPrimary(), 2.3);
for (ImageView iv : imageView) {
iv.setImageTintList(ColorStateList.valueOf(adjustedColor));
}
}
private void updateProgressDialog(BuildStep currentStep) {
if (mProgressDialog == null) {
return;
}
recordStepDuration(currentStep);
setProgressStep(mProgressPrepareIcon, mProgressPrepareText, mProgressPrepareDurationText, BuildStep.PREPARE, currentStep);
setProgressStep(mProgressBuildIcon, mProgressBuildText, mProgressBuildDurationText, BuildStep.BUILD, currentStep);
setProgressStep(mProgressSignIcon, mProgressSignText, mProgressSignDurationText, BuildStep.SIGN, currentStep);
setProgressStep(mProgressCleanIcon, mProgressCleanText, mProgressCleanDurationText, BuildStep.CLEAN, currentStep);
}
private void setProgressStep(@Nullable ImageView iconView, @Nullable TextView stateView, @Nullable TextView durationView, BuildStep step, BuildStep currentStep) {
if (step.ordinal() < currentStep.ordinal()) {
if (iconView != null) iconView.setImageResource(R.drawable.ic_check_mark);
if (stateView != null) stateView.setText(ensureTextEndsWithDot(stateView.getText()));
if (durationView != null && currentStep.ordinal() - step.ordinal() == 1) {
updateStepDurationText(durationView, step, SystemClock.elapsedRealtime());
}
} else if (step == currentStep) {
if (iconView != null) iconView.setImageResource(R.drawable.ic_right_arrow);
if (stateView != null) stateView.setText(ensureTextEndsWithHalfEllipsis(stateView.getText()));
} else {
if (iconView != null) iconView.setImageResource(R.drawable.transparent);
if (stateView != null) stateView.setText(ensureTextEndsWithoutDot(stateView.getText()));
}
}
private void resetStepDurations() {
mStepDurationMs.clear();
mCurrentTimingStep = null;
mCurrentTimingStepStartedAtMs = 0L;
}
private void recordStepDuration(@NonNull BuildStep currentStep) {
long now = SystemClock.elapsedRealtime();
if (mCurrentTimingStep != null && mCurrentTimingStep != currentStep && isTrackedStep(mCurrentTimingStep)) {
mStepDurationMs.put(mCurrentTimingStep, Math.max(0L, now - mCurrentTimingStepStartedAtMs));
}
if (isTrackedStep(currentStep) && !mStepDurationMs.containsKey(currentStep)) {
if (mCurrentTimingStep != currentStep) {
mCurrentTimingStep = currentStep;
mCurrentTimingStepStartedAtMs = now;
}
} else if (!isTrackedStep(currentStep)) {
mCurrentTimingStep = null;
mCurrentTimingStepStartedAtMs = 0L;
}
}
private boolean isTrackedStep(@NonNull BuildStep step) {
return step == BuildStep.PREPARE
|| step == BuildStep.BUILD
|| step == BuildStep.SIGN
|| step == BuildStep.CLEAN;
}
@Nullable
private Long resolveStepDurationMs(@NonNull BuildStep step, long now) {
Long duration = mStepDurationMs.get(step);
if (duration != null) {
return duration;
}
if (step == mCurrentTimingStep) {
return Math.max(0L, now - mCurrentTimingStepStartedAtMs);
}
return null;
}
private void updateStepDurationText(@NonNull TextView textView, @NonNull BuildStep step, long now) {
Long durationMs = resolveStepDurationMs(step, now);
textView.setText(durationMs == null ? "" : formatStepDuration(durationMs));
}
@NonNull
private String formatStepDuration(long durationMs) {
long safeDurationMs = Math.max(0L, durationMs);
long tenths = Math.round(safeDurationMs / 100.0d);
long integerPart = tenths / 10;
long decimalPart = tenths % 10;
// if (decimalPart == 0L) {
// return "[ " + integerPart + " s ]";
// }
return "[ " + integerPart + "." + decimalPart + " s ]";
}
private String ensureTextEndsWithDot(CharSequence text) {
return ensureTextEndsWithoutDot(text) + ".";
}
private String ensureTextEndsWithHalfEllipsis(CharSequence text) {
return ensureTextEndsWithoutDot(text) + StringUtils.str(R.string.text_half_ellipsis);
}
private String ensureTextEndsWithoutDot(CharSequence text) {
var tmp = text.toString();
while (tmp.length() > 0 && tmp.charAt(tmp.length() - 1) == '.') {
tmp = tmp.subSequence(0, tmp.length() - 1).toString();
}
return tmp;
}
// Abort build cooperatively and trigger cleanup as early as possible.
// zh-CN: 协作式中止构建, 并尽早触发清理.
private void requestBuildAbort() {
if (!mBuildCancelled.compareAndSet(false, true)) {
return;
}
if (mProgressDialog != null) {
mProgressDialog.setTitle(R.string.text_aborting);
View button = mProgressDialog.getActionButton(DialogAction.POSITIVE);
if (button != null) {
button.setEnabled(false);
}
}
Thread buildThread = mBuildThread;
if (buildThread != null) {
buildThread.interrupt();
}
cleanupBuildArtifactsAsync(false);
}
private void cleanupBuildArtifactsAsync(boolean restoreOutputApk) {
File workspace = mBuildWorkspace;
File outApk = mBuildOutputApk;
File outApkBackup = mBuildOutputApkBackup;
if (workspace == null && outApk == null && outApkBackup == null) {
return;
}
Schedulers.io().scheduleDirect(() -> cleanupBuildArtifacts(workspace, outApk, outApkBackup, restoreOutputApk));
}
// Best-effort cleanup for workspace and output artifacts.
// zh-CN: 对工作区和输出产物执行尽力清理.
private void cleanupBuildArtifacts(@Nullable File workspace, @Nullable File outApk, @Nullable File outApkBackup, boolean restoreOutputApk) {
if (workspace != null && workspace.exists()) {
PFiles.deleteRecursively(workspace);
}
if (outApk != null && outApk.exists()) {
outApk.delete();
}
if (restoreOutputApk) {
restoreOutputApkIfNeeded(outApk, outApkBackup);
}
}
private void restoreOutputApkIfNeeded(@Nullable File outApk, @Nullable File outApkBackup) {
if (outApk == null || outApkBackup == null || !outApkBackup.exists()) {
return;
}
if (outApk.exists() && !outApk.delete()) {
Log.w(LOG_TAG, "Failed to delete output apk before restore: " + outApk.getPath());
}
if (!outApkBackup.renameTo(outApk)) {
if (!PFiles.copy(outApkBackup.getPath(), outApk.getPath())) {
Log.w(LOG_TAG, "Failed to restore output apk backup: " + outApk.getPath());
return;
}
if (!outApkBackup.delete()) {
Log.w(LOG_TAG, "Failed to delete output apk backup: " + outApkBackup.getPath());
}
}
}
private void discardOutputApkBackup() {
File outApkBackup = mBuildOutputApkBackup;
if (outApkBackup != null && outApkBackup.exists() && !outApkBackup.delete()) {
Log.w(LOG_TAG, "Failed to delete output apk backup: " + outApkBackup.getPath());
}
}
private void handleBuildAborted() {
dismissProgressDialog();
cleanupBuildArtifactsAsync(true);
ViewUtils.showToast(this, getString(R.string.text_operation_aborted));
finishBuildState();
}
private void finishBuildState() {
mBuildWorkspace = null;
mBuildOutputApk = null;
mBuildOutputApkBackup = null;
mBuildThread = null;
mBuildCancelled.set(false);
}
private void dismissProgressDialog() {
if (mProgressDialog == null) {
return;
}
mProgressDialog.dismiss();
mProgressDialog = null;
mProgressPrepareIcon = null;
mProgressPrepareText = null;
mProgressBuildIcon = null;
mProgressBuildText = null;
mProgressSignIcon = null;
mProgressSignText = null;
mProgressCleanIcon = null;
mProgressCleanText = null;
mProgressPrepareDurationText = null;
mProgressBuildDurationText = null;
mProgressSignDurationText = null;
mProgressCleanDurationText = null;
mStateTitleText = null;
mStateContentText = null;
resetStepDurations();
}
private void onBuildFailed(Throwable error) {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
mProgressDialog = null;
if (mBuildCancelled.get() || error instanceof CancellationException) {
handleBuildAborted();
return;
}
dismissProgressDialog();
restoreOutputApkIfNeeded(mBuildOutputApk, mBuildOutputApkBackup);
ErrorDialogActivity.showErrorDialog(this, R.string.text_failed_to_build, error.getMessage());
Log.e(LOG_TAG, "Failed to build", error);
finishBuildState();
}
private void onBuildSuccessful(File outApk) {
if (mBuildCancelled.get()) {
handleBuildAborted();
return;
}
discardOutputApkBackup();
Explorers.workspace().refreshAll();
mProgressDialog.dismiss();
mProgressDialog = null;
new MaterialDialog.Builder(this)
.title(R.string.text_build_succeeded)
.content(getString(R.string.format_build_succeeded, outApk.getPath()))
.positiveText(R.string.text_install)
.positiveColorRes(R.color.dialog_button_attraction)
.onPositive((dialog, which) -> IntentUtils.installApk(
// dismissProgressDialog();
if (mProgressDialog != null) {
mProgressDialog.setTitle(R.string.text_build_succeeded);
mStateTitleText.setText(getString(R.string.text_property_colon, getString(R.string.text_built_apk_file_path)));
mStateContentText.setText(outApk.getPath());
var positiveButton = mProgressDialog.getActionButton(DialogAction.POSITIVE);
positiveButton.setEnabled(true);
positiveButton.setText(getString(R.string.text_install));
positiveButton.setTextColor(getColor(R.color.dialog_button_attraction));
positiveButton.setOnClickListener(v -> {
IntentUtils.installApk(
BuildActivity.this,
outApk.getPath(),
AppFileProvider.AUTHORITY,
new ToastExceptionHolder(BuildActivity.this)
))
.negativeText(R.string.text_cancel)
.negativeColorRes(R.color.dialog_button_default)
.neutralText(R.string.dialog_button_file_information)
.neutralColorRes(R.color.dialog_button_hint)
.onNeutral((dialog, which) -> ApkInfoDialogManager.showApkInfoDialog(dialog.getContext(), outApk))
.show();
);
dismissProgressDialog();
});
var negativeButton = mProgressDialog.getActionButton(DialogAction.NEGATIVE);
negativeButton.setEnabled(true);
negativeButton.setText(getString(R.string.text_cancel));
negativeButton.setTextColor(getColor(R.color.dialog_button_default));
negativeButton.setOnClickListener(v -> {
dismissProgressDialog();
});
var neutralButton = mProgressDialog.getActionButton(DialogAction.NEUTRAL);
neutralButton.setEnabled(true);
neutralButton.setText(getString(R.string.dialog_button_file_information));
neutralButton.setTextColor(getColor(R.color.dialog_button_hint));
neutralButton.setOnClickListener(v -> {
ApkInfoDialogManager.showApkInfoDialog(this, outApk);
});
}
finishBuildState();
}
@Override
public void onPrepare(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_prepare);
updateProgressDialog(BuildStep.PREPARE);
}
@Override
public void onBuild(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_build);
updateProgressDialog(BuildStep.BUILD);
}
@Override
public void onSign(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_package);
updateProgressDialog(BuildStep.SIGN);
}
@Override
public void onClean(@NonNull ApkBuilder builder) {
mProgressDialog.setContent(R.string.apk_builder_clean);
updateProgressDialog(BuildStep.CLEAN);
}
@Override
public void onStepProgress(@NonNull ApkBuilder builder, @NonNull String title, @Nullable String detail) {
if (mStateTitleText != null) {
mStateTitleText.setText(getString(R.string.text_property_colon, title));
}
if (!TextUtils.isEmpty(detail) && mStateContentText != null) {
mStateContentText.setText(detail);
}
}
@Override
public void onFinished(@NotNull ApkBuilder builder) {
updateProgressDialog(BuildStep.FINISHED);
}
@SuppressWarnings("ResultOfMethodCallIgnored")

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,186 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:paddingHorizontal="@dimen/ref_md_dialog_frame_margin">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/build_progress_row_spacing"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/icon_prepare"
android:layout_width="@dimen/build_progress_icon_size"
android:layout_height="@dimen/build_progress_icon_size"
tools:src="@drawable/ic_check_mark"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerInside" />
<TextView
android:id="@+id/text_prepare"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/build_progress_icon_text_gap"
android:layout_weight="1"
android:textSize="@dimen/build_progress_icon_text_size"
android:text="@string/text_prepare" />
<TextView
android:id="@+id/text_prepare_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:minEms="8"
android:textSize="@dimen/build_progress_icon_text_size"
tools:text="[ 0.3 s ]" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/build_progress_row_spacing"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/icon_build"
android:layout_width="@dimen/build_progress_icon_size"
android:layout_height="@dimen/build_progress_icon_size"
tools:src="@drawable/ic_check_mark"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerInside" />
<TextView
android:id="@+id/text_build"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/build_progress_icon_text_gap"
android:layout_weight="1"
android:textSize="@dimen/build_progress_icon_text_size"
android:text="@string/text_build" />
<TextView
android:id="@+id/text_build_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:minEms="8"
android:textSize="@dimen/build_progress_icon_text_size"
tools:text="[ 10.7 s ]" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/build_progress_row_spacing"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/icon_sign"
android:layout_width="@dimen/build_progress_icon_size"
android:layout_height="@dimen/build_progress_icon_size"
tools:src="@drawable/ic_check_mark"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerInside" />
<TextView
android:id="@+id/text_sign"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/build_progress_icon_text_gap"
android:layout_weight="1"
android:textSize="@dimen/build_progress_icon_text_size"
android:text="@string/text_sign" />
<TextView
android:id="@+id/text_sign_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:minEms="8"
android:textSize="@dimen/build_progress_icon_text_size"
tools:text="[ 23 s ]" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/build_progress_row_spacing"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/icon_clean"
android:layout_width="@dimen/build_progress_icon_size"
android:layout_height="@dimen/build_progress_icon_size"
tools:src="@drawable/ic_check_mark"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerInside" />
<TextView
android:id="@+id/text_clean"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/build_progress_icon_text_gap"
android:layout_weight="1"
android:textSize="@dimen/build_progress_icon_text_size"
android:text="@string/text_clean" />
<TextView
android:id="@+id/text_clean_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:minEms="8"
android:textSize="@dimen/build_progress_icon_text_size"
tools:text="[ 0.1 s ]" />
</LinearLayout>
<View
android:layout_marginVertical="@dimen/build_progress_row_spacing"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/divider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/text_state_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:lines="1"
android:maxLines="1"
android:ellipsize="middle"
android:textSize="@dimen/build_progress_icon_text_size"
tools:text="@string/text_title" />
<TextView
android:id="@+id/text_state_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:gravity="top"
android:lines="2"
android:maxLines="2"
android:lineSpacingMultiplier="1.2"
android:ellipsize="end"
android:textSize="@dimen/build_progress_icon_text_size"
tools:text="@string/text_content" />
</LinearLayout>
</LinearLayout>

View File

@@ -9,11 +9,11 @@
<!-- Proofreader: [ Google Gemini ] -->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">مبنى...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">تنظيف...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">التغليف ...</string>
<string name="apk_builder_build">مبنى</string>
<string name="apk_builder_clean">تنظيف</string>
<string name="apk_builder_package">التغليف </string>
<string name="apk_builder_plugin_version_incompatible">نسخة غير متوافقة من \"APK Builder\".\nتحميله الآن؟</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">خطة...</string>
<string name="apk_builder_prepare">خطة</string>
<string name="apk_info_device_sdk">SDK الجهاز</string>
<string name="apk_info_file_size">حجم الملف</string>
<string name="apk_info_installed_version">مثبت</string>
@@ -536,7 +536,7 @@
<string name="text_app_shortcut_docs_short_label">توثيق</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 سجل</string>
<string name="text_app_shortcut_log_short_label">سجل</string>
<string name="text_app_shortcut_plugin_center_long_label">مركز اضافات AutoJs6</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 الاضافات</string>
<string name="text_app_shortcut_plugin_center_short_label">الاضافات</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 إعدادات</string>
<string name="text_app_shortcut_settings_short_label">إعدادات</string>
@@ -674,7 +674,7 @@
<string name="text_delete_folder">حذف المجلد</string>
<string name="text_delete_line">حذف الخط</string>
<string name="text_delete_revision_confirm">هل تريد حذف هذه المراجعة نهائيا?</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">جارٍ الحذف...</string>
<string name="text_deleting">جارٍ الحذف</string>
<string name="text_description">الوصف</string>
<string name="text_deselect_all">الغاء التحديد</string>
<string name="text_destination">الوجهة</string>
@@ -1086,7 +1086,7 @@
<string name="text_press_back_or_vol_down_to_close_window">لإغلاق، اضغط على \"رجوع\" أو \"خفض الصوت\"</string>
<string name="text_preview">معاينة</string>
<string name="text_process_log">سجل العملية</string>
<string name="text_processing">يعالج</string>
<string name="text_processing">جارٍ المعالجة</string>
<string name="text_project">مشروع</string>
<string name="text_project_location">موقع المشروع</string>
<string name="text_project_media_access">وصول وسائل الإعلام المشروع</string>
@@ -1358,4 +1358,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">الاضافة \"%1$s\" غير مُفوّضة في مركز الاضافات</string>
<string name="error_plugin_not_found_in_installed_apps">لم يتم العثور على الاضافة \"%1$s\" ضمن التطبيقات المثبتة</string>
<string name="error_unsupported_plugin_connection">طريقة اتصال الاضافة غير مدعومة</string>
<string name="text_property_colon">%1$s:</string>
<string name="text_title">العنوان</string>
<string name="text_content">المحتوى</string>
<string name="text_building_apk">جارٍ بناء APK</string>
<string name="text_built_apk_file_path">مسار APK المبني</string>
<string name="text_clean">تنظيف</string>
<string name="text_sign">توقيع</string>
<string name="text_build">بناء</string>
<string name="text_prepare">تحضير</string>
<string name="text_preparing_workspace">جارٍ تحضير مساحة العمل</string>
<string name="text_extracting_template_apk">جارٍ استخراج APK القالب</string>
<string name="text_prepare_completed">اكتمل التحضير</string>
<string name="text_copying_project_directory">جارٍ نسخ مجلد المشروع</string>
<string name="text_copying_script_file">جارٍ نسخ ملف السكربت</string>
<string name="text_source_processing_completed">اكتملت معالجة المصدر</string>
<string name="text_copying_directory">جارٍ نسخ المجلد</string>
<string name="text_copying_file">جارٍ نسخ الملف</string>
<string name="text_encrypting_script">جارٍ تشفير السكربت</string>
<string name="text_replacing_file">جارٍ استبدال الملف</string>
<string name="text_preparing_build_config">جارٍ تحضير اعدادات البناء</string>
<string name="text_reading_splash_resources">جارٍ قراءة موارد شاشة البدء</string>
<string name="text_configuring_manifest">جارٍ ضبط manifest</string>
<string name="text_configuring_package_name">جارٍ ضبط اسم الحزمة</string>
<string name="text_copying_assets_to">جارٍ نسخ assets الى</string>
<string name="text_processing_source">جارٍ معالجة المصدر</string>
<string name="text_applying_binary_resources">جارٍ تطبيق الموارد الثنائية</string>
<string name="text_copying_native_libraries">جارٍ نسخ المكتبات الاصلية</string>
<string name="text_updating_project_config">جارٍ تحديث اعدادات المشروع</string>
<string name="text_sign_stage_completed">اكتملت مرحلة التوقيع</string>
<string name="text_writing_project_config">جارٍ كتابة اعدادات المشروع</string>
<string name="text_building_resources">جارٍ بناء الموارد</string>
<string name="text_build_completed">اكتمل البناء</string>
<string name="text_writing_resources_arsc">جارٍ كتابة resources.arsc</string>
<string name="text_writing_manifest">جارٍ كتابة manifest</string>
<string name="text_writing_app_icon">جارٍ كتابة ايقونة التطبيق</string>
<string name="text_copying_asset">جارٍ نسخ asset</string>
<string name="text_preparing_assets_dir">جارٍ تحضير مجلد assets</string>
<string name="text_copying_library">جارٍ نسخ المكتبة</string>
<string name="text_preparing_keystore">جارٍ تحضير keystore</string>
<string name="text_unsigned_apk_created">تم انشاء APK غير موقّع</string>
<string name="text_creating_unsigned_apk">جارٍ انشاء APK غير موقّع</string>
<string name="text_using_keystore">جارٍ استخدام keystore</string>
<string name="text_re_signing_apk">جارٍ اعادة توقيع APK</string>
<string name="text_writing_signed_apk">جارٍ كتابة APK الموقّع</string>
<string name="text_sign_completed">اكتمل التوقيع</string>
<string name="text_clean_completed">اكتمل التنظيف</string>
<string name="text_cleaning_workspace">جارٍ تنظيف مساحة العمل</string>
<string name="text_resolving_plugin_native_libraries">جارٍ تحليل مكتبات الاضافة الاصلية</string>
<string name="text_copying_libraries_for_abi">جارٍ نسخ المكتبات لـ ABI</string>
<string name="text_selected_plugin">الاضافة المحددة</string>
<string name="text_extracted_plugin_libraries">تم استخراج مكتبات الاضافة</string>
<string name="text_extracted_plugin_assets">تم استخراج assets الاضافة</string>
<string name="text_extracting_plugin_asset">جارٍ استخراج asset الاضافة</string>
<string name="text_extracting_plugin_so">جارٍ استخراج ملف so للاضافة</string>
</resources>

View File

@@ -4,11 +4,11 @@
<!-- Proofreader: [ SuperMonster003 ] -->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">Building...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">Cleaning...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">Packaging...</string>
<string name="apk_builder_build">Building</string>
<string name="apk_builder_clean">Cleaning</string>
<string name="apk_builder_package">Packaging</string>
<string name="apk_builder_plugin_version_incompatible">Incompatible version of \"apk builder\".\nDownload it now?</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">Preparing...</string>
<string name="apk_builder_prepare">Preparing</string>
<string name="apk_info_device_sdk">Device SDK</string>
<string name="apk_info_file_size">File size</string>
<string name="apk_info_installed_version">Installed</string>
@@ -531,7 +531,7 @@
<string name="text_app_shortcut_docs_short_label">Docs</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 Log</string>
<string name="text_app_shortcut_log_short_label">Log</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 Plugin Center</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 Plugins</string>
<string name="text_app_shortcut_plugin_center_short_label">Plugins</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 Settings</string>
<string name="text_app_shortcut_settings_short_label">Settings</string>
@@ -669,7 +669,7 @@
<string name="text_delete_folder">Delete folder</string>
<string name="text_delete_line">Delete line</string>
<string name="text_delete_revision_confirm">Delete this revision permanently?</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">Deleting...</string>
<string name="text_deleting">Deleting</string>
<string name="text_description">Description</string>
<string name="text_deselect_all">Deselect all</string>
<string name="text_destination">Destination</string>
@@ -772,7 +772,7 @@
<string name="text_file_name">File name</string>
<string name="text_file_name_colon_value">File name: %1$s</string>
<string name="text_file_not_exists">File does not exist</string>
<string name="text_file_path">File Path</string>
<string name="text_file_path">File path</string>
<string name="text_file_with_abs_path_is_not_an_executable_script">File \"%1$s\" is not an executable script</string>
<string name="text_filename_cannot_be_empty">Filename cannot be empty</string>
<string name="text_filename_cannot_contain_invalid_character">Filename cannot contain the following characters: \\ / : * ? &quot; &lt; &gt; |</string>
@@ -1353,4 +1353,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">Plugin \"%1$s\" is not authorized in Plugin Center</string>
<string name="error_plugin_not_found_in_installed_apps">Plugin \"%1$s\" not found in installed apps</string>
<string name="error_unsupported_plugin_connection">Unsupported plugin connection</string>
<string name="text_property_colon">%1$s:</string>
<string name="text_title">Title</string>
<string name="text_content">Content</string>
<string name="text_building_apk">Building APK</string>
<string name="text_built_apk_file_path">Built APK file path</string>
<string name="text_clean">Clean</string>
<string name="text_sign">Sign</string>
<string name="text_build">Build</string>
<string name="text_prepare">Prepare</string>
<string name="text_preparing_workspace">Preparing workspace</string>
<string name="text_extracting_template_apk">Extracting template APK</string>
<string name="text_prepare_completed">Prepare completed</string>
<string name="text_copying_project_directory">Copying project directory</string>
<string name="text_copying_script_file">Copying script file</string>
<string name="text_source_processing_completed">Source processing completed</string>
<string name="text_copying_directory">Copying directory</string>
<string name="text_copying_file">Copying file</string>
<string name="text_encrypting_script">Encrypting script</string>
<string name="text_replacing_file">Replacing file</string>
<string name="text_preparing_build_config">Preparing build config</string>
<string name="text_reading_splash_resources">Reading splash resources</string>
<string name="text_configuring_manifest">Configuring manifest</string>
<string name="text_configuring_package_name">Configuring package name</string>
<string name="text_copying_assets_to">Copying assets to</string>
<string name="text_processing_source">Processing source</string>
<string name="text_applying_binary_resources">Applying binary resources</string>
<string name="text_copying_native_libraries">Copying native libraries</string>
<string name="text_updating_project_config">Updating project config</string>
<string name="text_sign_stage_completed">Sign stage completed</string>
<string name="text_writing_project_config">Writing project config</string>
<string name="text_building_resources">Building resources</string>
<string name="text_build_completed">Build completed</string>
<string name="text_writing_resources_arsc">Writing resources.arsc</string>
<string name="text_writing_manifest">Writing manifest</string>
<string name="text_writing_app_icon">Writing app icon</string>
<string name="text_copying_asset">Copying asset</string>
<string name="text_preparing_assets_dir">Preparing assets dir</string>
<string name="text_copying_library">Copying library</string>
<string name="text_preparing_keystore">Preparing keystore</string>
<string name="text_unsigned_apk_created">Unsigned APK created</string>
<string name="text_creating_unsigned_apk">Creating unsigned APK</string>
<string name="text_using_keystore">Using keystore</string>
<string name="text_re_signing_apk">Re-signing APK</string>
<string name="text_writing_signed_apk">Writing signed APK</string>
<string name="text_sign_completed">Sign completed</string>
<string name="text_clean_completed">Clean completed</string>
<string name="text_cleaning_workspace">Cleaning workspace</string>
<string name="text_resolving_plugin_native_libraries">Resolving plugin native libraries</string>
<string name="text_copying_libraries_for_abi">Copying libraries for ABI</string>
<string name="text_selected_plugin">Selected plugin</string>
<string name="text_extracted_plugin_libraries">Extracted plugin libraries</string>
<string name="text_extracted_plugin_assets">Extracted plugin assets</string>
<string name="text_extracting_plugin_asset">Extracting plugin asset</string>
<string name="text_extracting_plugin_so">Extracting plugin so</string>
</resources>

View File

@@ -7,11 +7,11 @@
<!-- Proofreader: [ JetBrains AI Assistant ] -->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">Construir...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">Limpieza...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">Empaquetando...</string>
<string name="apk_builder_build">Construir</string>
<string name="apk_builder_clean">Limpieza</string>
<string name="apk_builder_package">Empaquetando</string>
<string name="apk_builder_plugin_version_incompatible">Versión incompatible de \"apk builder\".\n¿Descargarlo ahora?</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">Preparando...</string>
<string name="apk_builder_prepare">Preparando</string>
<string name="apk_info_device_sdk">SDK del dispositivo</string>
<string name="apk_info_file_size">Tamaño de archivo</string>
<string name="apk_info_installed_version">Instalado</string>
@@ -534,7 +534,7 @@
<string name="text_app_shortcut_docs_short_label">Docs</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 Registrar</string>
<string name="text_app_shortcut_log_short_label">Registrar</string>
<string name="text_app_shortcut_plugin_center_long_label">Centro de plugins de AutoJs6</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 Plugins</string>
<string name="text_app_shortcut_plugin_center_short_label">Plugins</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 Configuración</string>
<string name="text_app_shortcut_settings_short_label">Configuración</string>
@@ -672,7 +672,7 @@
<string name="text_delete_folder">Eliminar carpeta</string>
<string name="text_delete_line">Borrar línea</string>
<string name="text_delete_revision_confirm">¿Eliminar esta revision de forma permanente?</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">Eliminando...</string>
<string name="text_deleting">Eliminando</string>
<string name="text_description">Descripción</string>
<string name="text_deselect_all">Deseleccionar todo</string>
<string name="text_destination">Destino</string>
@@ -1356,4 +1356,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">El plugin \"%1$s\" no esta autorizado en el Centro de plugins</string>
<string name="error_plugin_not_found_in_installed_apps">No se encontro el plugin \"%1$s\" en las apps instaladas</string>
<string name="error_unsupported_plugin_connection">Conexion de plugin no compatible</string>
<string name="text_property_colon">%1$s:</string>
<string name="text_title">Titulo</string>
<string name="text_content">Contenido</string>
<string name="text_building_apk">Compilando APK</string>
<string name="text_built_apk_file_path">Ruta del APK generado</string>
<string name="text_clean">Limpiar</string>
<string name="text_sign">Firmar</string>
<string name="text_build">Compilar</string>
<string name="text_prepare">Preparar</string>
<string name="text_preparing_workspace">Preparando el espacio de trabajo</string>
<string name="text_extracting_template_apk">Extrayendo APK plantilla</string>
<string name="text_prepare_completed">Preparacion completada</string>
<string name="text_copying_project_directory">Copiando directorio del proyecto</string>
<string name="text_copying_script_file">Copiando archivo de script</string>
<string name="text_source_processing_completed">Procesamiento del codigo fuente completado</string>
<string name="text_copying_directory">Copiando directorio</string>
<string name="text_copying_file">Copiando archivo</string>
<string name="text_encrypting_script">Cifrando script</string>
<string name="text_replacing_file">Reemplazando archivo</string>
<string name="text_preparing_build_config">Preparando configuracion de compilacion</string>
<string name="text_reading_splash_resources">Leyendo recursos de inicio</string>
<string name="text_configuring_manifest">Configurando manifest</string>
<string name="text_configuring_package_name">Configurando nombre de paquete</string>
<string name="text_copying_assets_to">Copiando assets a</string>
<string name="text_processing_source">Procesando codigo fuente</string>
<string name="text_applying_binary_resources">Aplicando recursos binarios</string>
<string name="text_copying_native_libraries">Copiando bibliotecas nativas</string>
<string name="text_updating_project_config">Actualizando configuracion del proyecto</string>
<string name="text_sign_stage_completed">Etapa de firma completada</string>
<string name="text_writing_project_config">Escribiendo configuracion del proyecto</string>
<string name="text_building_resources">Compilando recursos</string>
<string name="text_build_completed">Compilacion completada</string>
<string name="text_writing_resources_arsc">Escribiendo resources.arsc</string>
<string name="text_writing_manifest">Escribiendo manifest</string>
<string name="text_writing_app_icon">Escribiendo icono de la app</string>
<string name="text_copying_asset">Copiando asset</string>
<string name="text_preparing_assets_dir">Preparando dir de assets</string>
<string name="text_copying_library">Copiando biblioteca</string>
<string name="text_preparing_keystore">Preparando keystore</string>
<string name="text_unsigned_apk_created">APK sin firmar creado</string>
<string name="text_creating_unsigned_apk">Creando APK sin firmar</string>
<string name="text_using_keystore">Usando keystore</string>
<string name="text_re_signing_apk">Re-firmando APK</string>
<string name="text_writing_signed_apk">Escribiendo APK firmado</string>
<string name="text_sign_completed">Firma completada</string>
<string name="text_clean_completed">Limpieza completada</string>
<string name="text_cleaning_workspace">Limpiando el espacio de trabajo</string>
<string name="text_resolving_plugin_native_libraries">Resolviendo bibliotecas nativas del plugin</string>
<string name="text_copying_libraries_for_abi">Copiando bibliotecas para ABI</string>
<string name="text_selected_plugin">Plugin seleccionado</string>
<string name="text_extracted_plugin_libraries">Bibliotecas del plugin extraidas</string>
<string name="text_extracted_plugin_assets">Assets del plugin extraidos</string>
<string name="text_extracting_plugin_asset">Extrayendo asset del plugin</string>
<string name="text_extracting_plugin_so">Extrayendo archivo so del plugin</string>
</resources>

View File

@@ -7,11 +7,11 @@
<!-- Proofreader : [ JetBrains AI Assistant ] -->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">Construire...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">Nettoyage...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">Emballage...</string>
<string name="apk_builder_build">Construire</string>
<string name="apk_builder_clean">Nettoyage</string>
<string name="apk_builder_package">Emballage</string>
<string name="apk_builder_plugin_version_incompatible">Version incompatible de \"apk builder\".\nTéléchargez-la maintenant ?</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">Préparation...</string>
<string name="apk_builder_prepare">Préparation</string>
<string name="apk_info_device_sdk">SDK de l\'appareil</string>
<string name="apk_info_file_size">Taille du fichier</string>
<string name="apk_info_installed_version">Installé</string>
@@ -534,7 +534,7 @@
<string name="text_app_shortcut_docs_short_label">Docs</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 Journal</string>
<string name="text_app_shortcut_log_short_label">Journal</string>
<string name="text_app_shortcut_plugin_center_long_label">Centre des plugins AutoJs6</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 Plugins</string>
<string name="text_app_shortcut_plugin_center_short_label">Plugins</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 Réglages</string>
<string name="text_app_shortcut_settings_short_label">Réglages</string>
@@ -672,7 +672,7 @@
<string name="text_delete_folder">Supprimer le dossier</string>
<string name="text_delete_line">Supprimer la ligne</string>
<string name="text_delete_revision_confirm">Supprimer definitivement cette revision?</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">Suppression...</string>
<string name="text_deleting">Suppression</string>
<string name="text_description">Description</string>
<string name="text_deselect_all">Tout deselectionner</string>
<string name="text_destination">Destination</string>
@@ -1356,4 +1356,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">Le plugin \"%1$s\" n\'est pas autorise dans le Centre des plugins</string>
<string name="error_plugin_not_found_in_installed_apps">Plugin \"%1$s\" introuvable parmi les applis installees</string>
<string name="error_unsupported_plugin_connection">Connexion de plugin non prise en charge</string>
<string name="text_property_colon">%1$s :</string>
<string name="text_title">Titre</string>
<string name="text_content">Contenu</string>
<string name="text_building_apk">Compilation de l\'APK</string>
<string name="text_built_apk_file_path">Chemin de l\'APK genere</string>
<string name="text_clean">Nettoyer</string>
<string name="text_sign">Signer</string>
<string name="text_build">Compiler</string>
<string name="text_prepare">Preparer</string>
<string name="text_preparing_workspace">Preparation de l\'espace de travail</string>
<string name="text_extracting_template_apk">Extraction de l\'APK modele</string>
<string name="text_prepare_completed">Preparation terminee</string>
<string name="text_copying_project_directory">Copie du repertoire du projet</string>
<string name="text_copying_script_file">Copie du fichier de script</string>
<string name="text_source_processing_completed">Traitement du code source termine</string>
<string name="text_copying_directory">Copie du repertoire</string>
<string name="text_copying_file">Copie du fichier</string>
<string name="text_encrypting_script">Chiffrement du script</string>
<string name="text_replacing_file">Remplacement du fichier</string>
<string name="text_preparing_build_config">Preparation de la config de build</string>
<string name="text_reading_splash_resources">Lecture des ressources d\'ecran de demarrage</string>
<string name="text_configuring_manifest">Configuration du manifest</string>
<string name="text_configuring_package_name">Configuration du nom de package</string>
<string name="text_copying_assets_to">Copie des assets vers</string>
<string name="text_processing_source">Traitement du code source</string>
<string name="text_applying_binary_resources">Application des ressources binaires</string>
<string name="text_copying_native_libraries">Copie des bibliotheques natives</string>
<string name="text_updating_project_config">Mise a jour de la config du projet</string>
<string name="text_sign_stage_completed">Etape de signature terminee</string>
<string name="text_writing_project_config">Ecriture de la config du projet</string>
<string name="text_building_resources">Compilation des ressources</string>
<string name="text_build_completed">Build termine</string>
<string name="text_writing_resources_arsc">Ecriture de resources.arsc</string>
<string name="text_writing_manifest">Ecriture du manifest</string>
<string name="text_writing_app_icon">Ecriture de l\'icone de l\'appli</string>
<string name="text_copying_asset">Copie d\'asset</string>
<string name="text_preparing_assets_dir">Preparation du dir des assets</string>
<string name="text_copying_library">Copie de bibliotheque</string>
<string name="text_preparing_keystore">Preparation du keystore</string>
<string name="text_unsigned_apk_created">APK non signe cree</string>
<string name="text_creating_unsigned_apk">Creation de l\'APK non signe</string>
<string name="text_using_keystore">Utilisation du keystore</string>
<string name="text_re_signing_apk">Re-signature de l\'APK</string>
<string name="text_writing_signed_apk">Ecriture de l\'APK signe</string>
<string name="text_sign_completed">Signature terminee</string>
<string name="text_clean_completed">Nettoyage termine</string>
<string name="text_cleaning_workspace">Nettoyage de l\'espace de travail</string>
<string name="text_resolving_plugin_native_libraries">Resolution des bibliotheques natives du plugin</string>
<string name="text_copying_libraries_for_abi">Copie des bibliotheques pour ABI</string>
<string name="text_selected_plugin">Plugin selectionne</string>
<string name="text_extracted_plugin_libraries">Bibliotheques du plugin extraites</string>
<string name="text_extracted_plugin_assets">Assets du plugin extraits</string>
<string name="text_extracting_plugin_asset">Extraction d\'asset du plugin</string>
<string name="text_extracting_plugin_so">Extraction du fichier so du plugin</string>
</resources>

View File

@@ -8,11 +8,11 @@
<!-- Proofreader: [ Google Gemini ] -->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">建築...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">洗浄...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">パッケージング...</string>
<string name="apk_builder_build">建築</string>
<string name="apk_builder_clean">洗浄</string>
<string name="apk_builder_package">パッケージング</string>
<string name="apk_builder_plugin_version_incompatible">apk builder の非互換バージョン.\n今すぐダウンロードしますか?</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">準備中...</string>
<string name="apk_builder_prepare">準備中</string>
<string name="apk_info_device_sdk">デバイス SDK</string>
<string name="apk_info_file_size">ファイルサイズ</string>
<string name="apk_info_installed_version">インストール済み</string>
@@ -535,7 +535,7 @@
<string name="text_app_shortcut_docs_short_label">文書</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 ログ</string>
<string name="text_app_shortcut_log_short_label">ログ</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 プラグインセンター</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 プラグイン</string>
<string name="text_app_shortcut_plugin_center_short_label">プラグイン</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 設定</string>
<string name="text_app_shortcut_settings_short_label">設定</string>
@@ -673,7 +673,7 @@
<string name="text_delete_folder">フォルダーを削除</string>
<string name="text_delete_line">行削除</string>
<string name="text_delete_revision_confirm">この履歴を完全に削除しますか?</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">削除中...</string>
<string name="text_deleting">削除中</string>
<string name="text_description">説明</string>
<string name="text_deselect_all">全て解除</string>
<string name="text_destination">宛先</string>
@@ -1357,4 +1357,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">プラグイン \"%1$s\" はプラグインセンターで許可されていません</string>
<string name="error_plugin_not_found_in_installed_apps">インストール済みアプリにプラグイン \"%1$s\" が見つかりません</string>
<string name="error_unsupported_plugin_connection">サポートされていないプラグイン接続方式です</string>
<string name="text_property_colon">%1$s:</string>
<string name="text_title">タイトル</string>
<string name="text_content">内容</string>
<string name="text_building_apk">APK をビルド中</string>
<string name="text_built_apk_file_path">生成された APK のパス</string>
<string name="text_clean">クリーン</string>
<string name="text_sign">署名</string>
<string name="text_build">ビルド</string>
<string name="text_prepare">準備</string>
<string name="text_preparing_workspace">ワークスペースを準備中</string>
<string name="text_extracting_template_apk">テンプレート APK を展開中</string>
<string name="text_prepare_completed">準備完了</string>
<string name="text_copying_project_directory">プロジェクトディレクトリをコピー中</string>
<string name="text_copying_script_file">スクリプトファイルをコピー中</string>
<string name="text_source_processing_completed">ソース処理完了</string>
<string name="text_copying_directory">ディレクトリをコピー中</string>
<string name="text_copying_file">ファイルをコピー中</string>
<string name="text_encrypting_script">スクリプトを暗号化中</string>
<string name="text_replacing_file">ファイルを置換中</string>
<string name="text_preparing_build_config">ビルド設定を準備中</string>
<string name="text_reading_splash_resources">スプラッシュ資源を読み込み中</string>
<string name="text_configuring_manifest">manifest を設定中</string>
<string name="text_configuring_package_name">パッケージ名を設定中</string>
<string name="text_copying_assets_to">assets をコピー中:</string>
<string name="text_processing_source">ソースを処理中</string>
<string name="text_applying_binary_resources">バイナリ資源を適用中</string>
<string name="text_copying_native_libraries">ネイティブライブラリをコピー中</string>
<string name="text_updating_project_config">プロジェクト設定を更新中</string>
<string name="text_sign_stage_completed">署名ステージ完了</string>
<string name="text_writing_project_config">プロジェクト設定を書き込み中</string>
<string name="text_building_resources">資源をビルド中</string>
<string name="text_build_completed">ビルド完了</string>
<string name="text_writing_resources_arsc">resources.arsc を書き込み中</string>
<string name="text_writing_manifest">manifest を書き込み中</string>
<string name="text_writing_app_icon">アプリアイコンを書き込み中</string>
<string name="text_copying_asset">asset をコピー中</string>
<string name="text_preparing_assets_dir">assets ディレクトリを準備中</string>
<string name="text_copying_library">ライブラリをコピー中</string>
<string name="text_preparing_keystore">keystore を準備中</string>
<string name="text_unsigned_apk_created">未署名 APK を作成しました</string>
<string name="text_creating_unsigned_apk">未署名 APK を作成中</string>
<string name="text_using_keystore">keystore を使用中</string>
<string name="text_re_signing_apk">APK を再署名中</string>
<string name="text_writing_signed_apk">署名済み APK を書き込み中</string>
<string name="text_sign_completed">署名完了</string>
<string name="text_clean_completed">クリーン完了</string>
<string name="text_cleaning_workspace">ワークスペースをクリーン中</string>
<string name="text_resolving_plugin_native_libraries">プラグインのネイティブライブラリを解決中</string>
<string name="text_copying_libraries_for_abi">ABI 用ライブラリをコピー中</string>
<string name="text_selected_plugin">選択したプラグイン</string>
<string name="text_extracted_plugin_libraries">プラグインのライブラリを抽出しました</string>
<string name="text_extracted_plugin_assets">プラグインの資源を抽出しました</string>
<string name="text_extracting_plugin_asset">プラグイン資源を抽出中</string>
<string name="text_extracting_plugin_so">プラグインの so を抽出中</string>
</resources>

View File

@@ -9,11 +9,11 @@
<!-- Proofreader: [ Google Gemini ] -->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">건물...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">청소...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">포장...</string>
<string name="apk_builder_build">건물</string>
<string name="apk_builder_clean">청소</string>
<string name="apk_builder_package">포장</string>
<string name="apk_builder_plugin_version_incompatible">\"APK Builder\" 의 호환되지 않는 버전.\n지금 다운로드 하시겠습니까?</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">준비...</string>
<string name="apk_builder_prepare">준비</string>
<string name="apk_info_device_sdk">디바이스 SDK</string>
<string name="apk_info_file_size">파일 크기</string>
<string name="apk_info_installed_version">설치됨</string>
@@ -536,7 +536,7 @@
<string name="text_app_shortcut_docs_short_label">문서</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 통나무</string>
<string name="text_app_shortcut_log_short_label">통나무</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 플러그인 센터</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 플러그인</string>
<string name="text_app_shortcut_plugin_center_short_label">플러그인</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 설정</string>
<string name="text_app_shortcut_settings_short_label">설정</string>
@@ -674,7 +674,7 @@
<string name="text_delete_folder">폴더 삭제</string>
<string name="text_delete_line">라인 삭제</string>
<string name="text_delete_revision_confirm">이 버전을 영구 삭제할까요?</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">삭제 중...</string>
<string name="text_deleting">삭제 중</string>
<string name="text_description">설명</string>
<string name="text_deselect_all">전체 해제</string>
<string name="text_destination">대상</string>
@@ -1086,7 +1086,7 @@
<string name="text_press_back_or_vol_down_to_close_window">창을 닫으려면 \"뒤로\" 또는 \"볼륨 감소\" 버튼을 누르세요</string>
<string name="text_preview">시사</string>
<string name="text_process_log">프로세스 로그</string>
<string name="text_processing">처리</string>
<string name="text_processing">처리</string>
<string name="text_project">프로젝트</string>
<string name="text_project_location">프로젝트 위치</string>
<string name="text_project_media_access">프로젝트 미디어 액세스</string>
@@ -1358,4 +1358,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">플러그인 \"%1$s\" 이 (가) 플러그인 센터에서 권한이 부여되지 않았습니다</string>
<string name="error_plugin_not_found_in_installed_apps">설치된 앱에서 플러그인 \"%1$s\" 을 (를) 찾을 수 없습니다</string>
<string name="error_unsupported_plugin_connection">지원되지 않는 플러그인 연결 방식입니다</string>
<string name="text_property_colon">%1$s:</string>
<string name="text_title">제목</string>
<string name="text_content">내용</string>
<string name="text_building_apk">APK 빌드 중</string>
<string name="text_built_apk_file_path">생성된 APK 경로</string>
<string name="text_clean">정리</string>
<string name="text_sign">서명</string>
<string name="text_build">빌드</string>
<string name="text_prepare">준비</string>
<string name="text_preparing_workspace">작업 공간 준비 중</string>
<string name="text_extracting_template_apk">템플릿 APK 추출 중</string>
<string name="text_prepare_completed">준비 완료</string>
<string name="text_copying_project_directory">프로젝트 디렉터리 복사 중</string>
<string name="text_copying_script_file">스크립트 파일 복사 중</string>
<string name="text_source_processing_completed">소스 처리 완료</string>
<string name="text_copying_directory">디렉터리 복사 중</string>
<string name="text_copying_file">파일 복사 중</string>
<string name="text_encrypting_script">스크립트 암호화 중</string>
<string name="text_replacing_file">파일 교체 중</string>
<string name="text_preparing_build_config">빌드 설정 준비 중</string>
<string name="text_reading_splash_resources">스플래시 리소스 읽는 중</string>
<string name="text_configuring_manifest">manifest 설정 중</string>
<string name="text_configuring_package_name">패키지 이름 설정 중</string>
<string name="text_copying_assets_to">assets 복사 중:</string>
<string name="text_processing_source">소스 처리 중</string>
<string name="text_applying_binary_resources">바이너리 리소스 적용 중</string>
<string name="text_copying_native_libraries">네이티브 라이브러리 복사 중</string>
<string name="text_updating_project_config">프로젝트 설정 업데이트 중</string>
<string name="text_sign_stage_completed">서명 단계 완료</string>
<string name="text_writing_project_config">프로젝트 설정 기록 중</string>
<string name="text_building_resources">리소스 빌드 중</string>
<string name="text_build_completed">빌드 완료</string>
<string name="text_writing_resources_arsc">resources.arsc 기록 중</string>
<string name="text_writing_manifest">manifest 기록 중</string>
<string name="text_writing_app_icon">앱 아이콘 기록 중</string>
<string name="text_copying_asset">asset 복사 중</string>
<string name="text_preparing_assets_dir">assets 디렉터리 준비 중</string>
<string name="text_copying_library">라이브러리 복사 중</string>
<string name="text_preparing_keystore">keystore 준비 중</string>
<string name="text_unsigned_apk_created">서명되지 않은 APK가 생성됨</string>
<string name="text_creating_unsigned_apk">서명되지 않은 APK 생성 중</string>
<string name="text_using_keystore">keystore 사용 중</string>
<string name="text_re_signing_apk">APK 재서명 중</string>
<string name="text_writing_signed_apk">서명된 APK 기록 중</string>
<string name="text_sign_completed">서명 완료</string>
<string name="text_clean_completed">정리 완료</string>
<string name="text_cleaning_workspace">작업 공간 정리 중</string>
<string name="text_resolving_plugin_native_libraries">플러그인 네이티브 라이브러리 확인 중</string>
<string name="text_copying_libraries_for_abi">ABI용 라이브러리 복사 중</string>
<string name="text_selected_plugin">선택된 플러그인</string>
<string name="text_extracted_plugin_libraries">플러그인 라이브러리를 추출함</string>
<string name="text_extracted_plugin_assets">플러그인 assets를 추출함</string>
<string name="text_extracting_plugin_asset">플러그인 asset 추출 중</string>
<string name="text_extracting_plugin_so">플러그인 so 파일 추출 중</string>
</resources>

View File

@@ -7,11 +7,11 @@
<!-- Proofreader: [ JetBrains AI Assistant ] -->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">Строительство...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">Очистка...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">Упаковка...</string>
<string name="apk_builder_build">Строительство</string>
<string name="apk_builder_clean">Очистка</string>
<string name="apk_builder_package">Упаковка</string>
<string name="apk_builder_plugin_version_incompatible">Несовместимая версия \"apk builder\".\nЗагрузить ее сейчас?</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">Подготовка...</string>
<string name="apk_builder_prepare">Подготовка</string>
<string name="apk_info_device_sdk">SDK устройства</string>
<string name="apk_info_file_size">Размер файла</string>
<string name="apk_info_installed_version">Установлено</string>
@@ -534,7 +534,7 @@
<string name="text_app_shortcut_docs_short_label">документы</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 Журнал</string>
<string name="text_app_shortcut_log_short_label">Журнал</string>
<string name="text_app_shortcut_plugin_center_long_label">Центр плагинов AutoJs6</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 Плагины</string>
<string name="text_app_shortcut_plugin_center_short_label">Плагины</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 Настройки</string>
<string name="text_app_shortcut_settings_short_label">Настройки</string>
@@ -672,7 +672,7 @@
<string name="text_delete_folder">Удалить папку</string>
<string name="text_delete_line">Удалить строку</string>
<string name="text_delete_revision_confirm">Удалить эту ревизию навсегда?</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">Удаление...</string>
<string name="text_deleting">Удаление</string>
<string name="text_description">Описание</string>
<string name="text_deselect_all">Снять выделение</string>
<string name="text_destination">Назначение</string>
@@ -1356,4 +1356,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">Плагин \"%1$s\" не авторизован в Центре плагинов</string>
<string name="error_plugin_not_found_in_installed_apps">Плагин \"%1$s\" не найден среди установленных приложений</string>
<string name="error_unsupported_plugin_connection">Неподдерживаемое подключение плагина</string>
<string name="text_property_colon">%1$s:</string>
<string name="text_title">Заголовок</string>
<string name="text_content">Содержимое</string>
<string name="text_building_apk">Сборка APK</string>
<string name="text_built_apk_file_path">Путь к собранному APK</string>
<string name="text_clean">Очистить</string>
<string name="text_sign">Подписать</string>
<string name="text_build">Собрать</string>
<string name="text_prepare">Подготовить</string>
<string name="text_preparing_workspace">Подготовка рабочей области</string>
<string name="text_extracting_template_apk">Извлечение шаблонного APK</string>
<string name="text_prepare_completed">Подготовка завершена</string>
<string name="text_copying_project_directory">Копирование каталога проекта</string>
<string name="text_copying_script_file">Копирование файла скрипта</string>
<string name="text_source_processing_completed">Обработка исходников завершена</string>
<string name="text_copying_directory">Копирование каталога</string>
<string name="text_copying_file">Копирование файла</string>
<string name="text_encrypting_script">Шифрование скрипта</string>
<string name="text_replacing_file">Замена файла</string>
<string name="text_preparing_build_config">Подготовка конфигурации сборки</string>
<string name="text_reading_splash_resources">Чтение ресурсов заставки</string>
<string name="text_configuring_manifest">Настройка manifest</string>
<string name="text_configuring_package_name">Настройка имени пакета</string>
<string name="text_copying_assets_to">Копирование assets в</string>
<string name="text_processing_source">Обработка исходников</string>
<string name="text_applying_binary_resources">Применение бинарных ресурсов</string>
<string name="text_copying_native_libraries">Копирование нативных библиотек</string>
<string name="text_updating_project_config">Обновление конфигурации проекта</string>
<string name="text_sign_stage_completed">Этап подписи завершен</string>
<string name="text_writing_project_config">Запись конфигурации проекта</string>
<string name="text_building_resources">Сборка ресурсов</string>
<string name="text_build_completed">Сборка завершена</string>
<string name="text_writing_resources_arsc">Запись resources.arsc</string>
<string name="text_writing_manifest">Запись manifest</string>
<string name="text_writing_app_icon">Запись значка приложения</string>
<string name="text_copying_asset">Копирование asset</string>
<string name="text_preparing_assets_dir">Подготовка каталога assets</string>
<string name="text_copying_library">Копирование библиотеки</string>
<string name="text_preparing_keystore">Подготовка keystore</string>
<string name="text_unsigned_apk_created">Неподписанный APK создан</string>
<string name="text_creating_unsigned_apk">Создание неподписанного APK</string>
<string name="text_using_keystore">Использование keystore</string>
<string name="text_re_signing_apk">Повторная подпись APK</string>
<string name="text_writing_signed_apk">Запись подписанного APK</string>
<string name="text_sign_completed">Подпись завершена</string>
<string name="text_clean_completed">Очистка завершена</string>
<string name="text_cleaning_workspace">Очистка рабочей области</string>
<string name="text_resolving_plugin_native_libraries">Разбор нативных библиотек плагина</string>
<string name="text_copying_libraries_for_abi">Копирование библиотек для ABI</string>
<string name="text_selected_plugin">Выбранный плагин</string>
<string name="text_extracted_plugin_libraries">Библиотеки плагина извлечены</string>
<string name="text_extracted_plugin_assets">Assets плагина извлечены</string>
<string name="text_extracting_plugin_asset">Извлечение asset плагина</string>
<string name="text_extracting_plugin_so">Извлечение файла so плагина</string>
</resources>

View File

@@ -4,11 +4,11 @@
<!-- Proofreader: [ SuperMonster003 ] -->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">構建中...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">清理臨時文件...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">打包中...</string>
<string name="apk_builder_build">構建中</string>
<string name="apk_builder_clean">清理臨時文件</string>
<string name="apk_builder_package">打包中</string>
<string name="apk_builder_plugin_version_incompatible">打包插件需更新\n是否下載</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">準備文件...</string>
<string name="apk_builder_prepare">準備文件</string>
<string name="apk_info_device_sdk">設備 SDK</string>
<string name="apk_info_file_size">文件大小</string>
<string name="apk_info_installed_version">已安裝</string>
@@ -530,7 +530,7 @@
<string name="text_app_shortcut_docs_short_label">文檔</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 日誌</string>
<string name="text_app_shortcut_log_short_label">日誌</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 插件中心</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 插件</string>
<string name="text_app_shortcut_plugin_center_short_label">插件</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 設置</string>
<string name="text_app_shortcut_settings_short_label">設置</string>
@@ -668,7 +668,7 @@
<string name="text_delete_folder">刪除文件夾</string>
<string name="text_delete_line">刪除行</string>
<string name="text_delete_revision_confirm">是否永久刪除此版本記錄</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">正在刪除...</string>
<string name="text_deleting">正在刪除</string>
<string name="text_description">描述</string>
<string name="text_deselect_all">取消全選</string>
<string name="text_destination">目標</string>
@@ -1080,7 +1080,7 @@
<string name="text_press_back_or_vol_down_to_close_window">如需關閉窗口, 可按 \"返回鍵\" 或 \"音量減鍵\"</string>
<string name="text_preview">預覽</string>
<string name="text_process_log">流程日誌</string>
<string name="text_processing">處理</string>
<string name="text_processing">正在處理</string>
<string name="text_project">項目</string>
<string name="text_project_location">項目位置</string>
<string name="text_project_media_access">投影媒體權限</string>
@@ -1352,4 +1352,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">插件 \"%1$s\" 未在插件中心獲得授權</string>
<string name="error_plugin_not_found_in_installed_apps">已安裝應用中未找到插件 \"%1$s\"</string>
<string name="error_unsupported_plugin_connection">不支持的插件連接方式</string>
<string name="text_property_colon">%1$s:</string>
<string name="text_title">標題</string>
<string name="text_content">內容</string>
<string name="text_building_apk">正在打包應用</string>
<string name="text_built_apk_file_path">打包應用文件路徑</string>
<string name="text_clean">清理</string>
<string name="text_sign">簽名</string>
<string name="text_build">構建</string>
<string name="text_prepare">準備</string>
<string name="text_preparing_workspace">正在準備工作區</string>
<string name="text_extracting_template_apk">正在提取模板 APK</string>
<string name="text_prepare_completed">準備完成</string>
<string name="text_copying_project_directory">正在複製項目目錄</string>
<string name="text_copying_script_file">正在複製腳本文件</string>
<string name="text_source_processing_completed">源代碼處理完成</string>
<string name="text_copying_directory">正在複製目錄</string>
<string name="text_copying_file">正在複製文件</string>
<string name="text_encrypting_script">正在加密腳本</string>
<string name="text_replacing_file">正在替換文件</string>
<string name="text_preparing_build_config">正在準備構建配置</string>
<string name="text_reading_splash_resources">正在讀取啓動頁資源</string>
<string name="text_configuring_manifest">正在配置清單文件</string>
<string name="text_configuring_package_name">正在配置包名</string>
<string name="text_copying_assets_to">正在複製資源到</string>
<string name="text_processing_source">正在處理源代碼</string>
<string name="text_applying_binary_resources">正在應用二進制資源</string>
<string name="text_copying_native_libraries">正在複製原生庫</string>
<string name="text_updating_project_config">正在更新項目配置</string>
<string name="text_sign_stage_completed">簽名階段完成</string>
<string name="text_writing_project_config">正在寫入項目配置</string>
<string name="text_building_resources">正在構建資源</string>
<string name="text_build_completed">構建完成</string>
<string name="text_writing_resources_arsc">正在寫入 resources.arsc</string>
<string name="text_writing_manifest">正在寫入清單文件</string>
<string name="text_writing_app_icon">正在寫入應用圖標</string>
<string name="text_copying_asset">正在複製資源</string>
<string name="text_preparing_assets_dir">正在準備資源目錄</string>
<string name="text_copying_library">正在複製庫文件</string>
<string name="text_preparing_keystore">正在準備密鑰庫</string>
<string name="text_unsigned_apk_created">未簽名 APK 已創建</string>
<string name="text_creating_unsigned_apk">正在創建未簽名 APK</string>
<string name="text_using_keystore">正在使用密鑰庫</string>
<string name="text_re_signing_apk">正在重新簽名 APK</string>
<string name="text_writing_signed_apk">正在寫入已簽名 APK</string>
<string name="text_sign_completed">簽名完成</string>
<string name="text_clean_completed">清理完成</string>
<string name="text_cleaning_workspace">正在清理工作區</string>
<string name="text_resolving_plugin_native_libraries">正在解析插件原生庫</string>
<string name="text_copying_libraries_for_abi">正在複製 ABI 庫文件</string>
<string name="text_selected_plugin">已選擇插件</string>
<string name="text_extracted_plugin_libraries">已提取插件庫文件</string>
<string name="text_extracted_plugin_assets">已提取插件資源</string>
<string name="text_extracting_plugin_asset">正在提取插件資源</string>
<string name="text_extracting_plugin_so">正在提取插件 so 文件</string>
</resources>

View File

@@ -4,11 +4,11 @@
<!-- Proofreader: [ SuperMonster003 ] -->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">構建中...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">清理臨時檔案...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">打包中...</string>
<string name="apk_builder_build">構建中</string>
<string name="apk_builder_clean">清理臨時檔案</string>
<string name="apk_builder_package">打包中</string>
<string name="apk_builder_plugin_version_incompatible">打包外掛需更新\n是否下載</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">準備檔案...</string>
<string name="apk_builder_prepare">準備檔案</string>
<string name="apk_info_device_sdk">裝置 SDK</string>
<string name="apk_info_file_size">檔案大小</string>
<string name="apk_info_installed_version">已安裝</string>
@@ -530,7 +530,7 @@
<string name="text_app_shortcut_docs_short_label">文件</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 日誌</string>
<string name="text_app_shortcut_log_short_label">日誌</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 外掛中心</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 外掛</string>
<string name="text_app_shortcut_plugin_center_short_label">外掛</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 設定</string>
<string name="text_app_shortcut_settings_short_label">設定</string>
@@ -668,7 +668,7 @@
<string name="text_delete_folder">刪除資料夾</string>
<string name="text_delete_line">刪除行</string>
<string name="text_delete_revision_confirm">是否永久刪除此版本記錄</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">正在刪除...</string>
<string name="text_deleting">正在刪除</string>
<string name="text_description">描述</string>
<string name="text_deselect_all">取消全選</string>
<string name="text_destination">目標</string>
@@ -1080,7 +1080,7 @@
<string name="text_press_back_or_vol_down_to_close_window">如需關閉視窗, 可按 \"返回鍵\" 或 \"音量減鍵\"</string>
<string name="text_preview">預覽</string>
<string name="text_process_log">流程日誌</string>
<string name="text_processing">處理</string>
<string name="text_processing">正在處理</string>
<string name="text_project">專案</string>
<string name="text_project_location">專案位置</string>
<string name="text_project_media_access">投影媒體許可權</string>
@@ -1352,4 +1352,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">外掛 \"%1$s\" 未在外掛中心獲得授權</string>
<string name="error_plugin_not_found_in_installed_apps">已安裝應用中未找到外掛 \"%1$s\"</string>
<string name="error_unsupported_plugin_connection">不支援的外掛連線方式</string>
<string name="text_property_colon">%1$s:</string>
<string name="text_title">標題</string>
<string name="text_content">內容</string>
<string name="text_building_apk">正在打包應用</string>
<string name="text_built_apk_file_path">打包應用檔案路徑</string>
<string name="text_clean">清理</string>
<string name="text_sign">簽名</string>
<string name="text_build">構建</string>
<string name="text_prepare">準備</string>
<string name="text_preparing_workspace">正在準備工作區</string>
<string name="text_extracting_template_apk">正在提取模板 APK</string>
<string name="text_prepare_completed">準備完成</string>
<string name="text_copying_project_directory">正在複製專案目錄</string>
<string name="text_copying_script_file">正在複製指令碼檔案</string>
<string name="text_source_processing_completed">原始碼處理完成</string>
<string name="text_copying_directory">正在複製目錄</string>
<string name="text_copying_file">正在複製檔案</string>
<string name="text_encrypting_script">正在加密指令碼</string>
<string name="text_replacing_file">正在替換檔案</string>
<string name="text_preparing_build_config">正在準備構建配置</string>
<string name="text_reading_splash_resources">正在讀取啟動頁資源</string>
<string name="text_configuring_manifest">正在配置清單檔案</string>
<string name="text_configuring_package_name">正在配置包名</string>
<string name="text_copying_assets_to">正在複製資源到</string>
<string name="text_processing_source">正在處理原始碼</string>
<string name="text_applying_binary_resources">正在應用二進位制資源</string>
<string name="text_copying_native_libraries">正在複製原生庫</string>
<string name="text_updating_project_config">正在更新專案配置</string>
<string name="text_sign_stage_completed">簽名階段完成</string>
<string name="text_writing_project_config">正在寫入專案配置</string>
<string name="text_building_resources">正在構建資源</string>
<string name="text_build_completed">構建完成</string>
<string name="text_writing_resources_arsc">正在寫入 resources.arsc</string>
<string name="text_writing_manifest">正在寫入清單檔案</string>
<string name="text_writing_app_icon">正在寫入應用圖示</string>
<string name="text_copying_asset">正在複製資源</string>
<string name="text_preparing_assets_dir">正在準備資源目錄</string>
<string name="text_copying_library">正在複製庫檔案</string>
<string name="text_preparing_keystore">正在準備金鑰庫</string>
<string name="text_unsigned_apk_created">未簽名 APK 已建立</string>
<string name="text_creating_unsigned_apk">正在建立未簽名 APK</string>
<string name="text_using_keystore">正在使用金鑰庫</string>
<string name="text_re_signing_apk">正在重新簽名 APK</string>
<string name="text_writing_signed_apk">正在寫入已簽名 APK</string>
<string name="text_sign_completed">簽名完成</string>
<string name="text_clean_completed">清理完成</string>
<string name="text_cleaning_workspace">正在清理工作區</string>
<string name="text_resolving_plugin_native_libraries">正在解析外掛原生庫</string>
<string name="text_copying_libraries_for_abi">正在複製 ABI 庫檔案</string>
<string name="text_selected_plugin">已選擇外掛</string>
<string name="text_extracted_plugin_libraries">已提取外掛庫檔案</string>
<string name="text_extracted_plugin_assets">已提取外掛資源</string>
<string name="text_extracting_plugin_asset">正在提取外掛資源</string>
<string name="text_extracting_plugin_so">正在提取外掛 so 檔案</string>
</resources>

View File

@@ -4,11 +4,11 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">构建中...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">清理临时文件...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">打包中...</string>
<string name="apk_builder_build">构建中</string>
<string name="apk_builder_clean">清理临时文件</string>
<string name="apk_builder_package">打包中</string>
<string name="apk_builder_plugin_version_incompatible">打包插件需更新\n是否下载</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">准备文件...</string>
<string name="apk_builder_prepare">准备文件</string>
<string name="apk_info_device_sdk">设备 SDK</string>
<string name="apk_info_file_size">文件大小</string>
<string name="apk_info_installed_version">已安装</string>
@@ -531,7 +531,7 @@
<string name="text_app_shortcut_docs_short_label">文档</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 日志</string>
<string name="text_app_shortcut_log_short_label">日志</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 插件中心</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 插件</string>
<string name="text_app_shortcut_plugin_center_short_label">插件</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 设置</string>
<string name="text_app_shortcut_settings_short_label">设置</string>
@@ -669,7 +669,7 @@
<string name="text_delete_folder">删除文件夹</string>
<string name="text_delete_line">删除行</string>
<string name="text_delete_revision_confirm">是否永久删除此版本记录</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">正在删除...</string>
<string name="text_deleting">正在删除</string>
<string name="text_description">描述</string>
<string name="text_deselect_all">取消全选</string>
<string name="text_destination">目标</string>
@@ -1081,7 +1081,7 @@
<string name="text_press_back_or_vol_down_to_close_window">如需关闭窗口, 可按 \"返回键\" 或 \"音量减键\"</string>
<string name="text_preview">预览</string>
<string name="text_process_log">流程日志</string>
<string name="text_processing">处理</string>
<string name="text_processing">正在处理</string>
<string name="text_project">项目</string>
<string name="text_project_location">项目位置</string>
<string name="text_project_media_access">投影媒体权限</string>
@@ -1353,4 +1353,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">插件 \"%1$s\" 未在插件中心获得授权</string>
<string name="error_plugin_not_found_in_installed_apps">已安装应用中未找到插件 \"%1$s\"</string>
<string name="error_unsupported_plugin_connection">不支持的插件连接方式</string>
<string name="text_property_colon">%1$s:</string>
<string name="text_title">标题</string>
<string name="text_content">内容</string>
<string name="text_building_apk">正在打包应用</string>
<string name="text_built_apk_file_path">打包应用文件路径</string>
<string name="text_clean">清理</string>
<string name="text_sign">签名</string>
<string name="text_build">构建</string>
<string name="text_prepare">准备</string>
<string name="text_preparing_workspace">正在准备工作区</string>
<string name="text_extracting_template_apk">正在提取模板 APK</string>
<string name="text_prepare_completed">准备完成</string>
<string name="text_copying_project_directory">正在复制项目目录</string>
<string name="text_copying_script_file">正在复制脚本文件</string>
<string name="text_source_processing_completed">源代码处理完成</string>
<string name="text_copying_directory">正在复制目录</string>
<string name="text_copying_file">正在复制文件</string>
<string name="text_encrypting_script">正在加密脚本</string>
<string name="text_replacing_file">正在替换文件</string>
<string name="text_preparing_build_config">正在准备构建配置</string>
<string name="text_reading_splash_resources">正在读取启动页资源</string>
<string name="text_configuring_manifest">正在配置清单文件</string>
<string name="text_configuring_package_name">正在配置包名</string>
<string name="text_copying_assets_to">正在复制资源到</string>
<string name="text_processing_source">正在处理源代码</string>
<string name="text_applying_binary_resources">正在应用二进制资源</string>
<string name="text_copying_native_libraries">正在复制原生库</string>
<string name="text_updating_project_config">正在更新项目配置</string>
<string name="text_sign_stage_completed">签名阶段完成</string>
<string name="text_writing_project_config">正在写入项目配置</string>
<string name="text_building_resources">正在构建资源</string>
<string name="text_build_completed">构建完成</string>
<string name="text_writing_resources_arsc">正在写入 resources.arsc</string>
<string name="text_writing_manifest">正在写入清单文件</string>
<string name="text_writing_app_icon">正在写入应用图标</string>
<string name="text_copying_asset">正在复制资源</string>
<string name="text_preparing_assets_dir">正在准备资源目录</string>
<string name="text_copying_library">正在复制库文件</string>
<string name="text_preparing_keystore">正在准备密钥库</string>
<string name="text_unsigned_apk_created">未签名 APK 已创建</string>
<string name="text_creating_unsigned_apk">正在创建未签名 APK</string>
<string name="text_using_keystore">正在使用密钥库</string>
<string name="text_re_signing_apk">正在重新签名 APK</string>
<string name="text_writing_signed_apk">正在写入已签名 APK</string>
<string name="text_sign_completed">签名完成</string>
<string name="text_clean_completed">清理完成</string>
<string name="text_cleaning_workspace">正在清理工作区</string>
<string name="text_resolving_plugin_native_libraries">正在解析插件原生库</string>
<string name="text_copying_libraries_for_abi">正在复制 ABI 库文件</string>
<string name="text_selected_plugin">已选择插件</string>
<string name="text_extracted_plugin_libraries">已提取插件库文件</string>
<string name="text_extracted_plugin_assets">已提取插件资源</string>
<string name="text_extracting_plugin_asset">正在提取插件资源</string>
<string name="text_extracting_plugin_so">正在提取插件 so 文件</string>
</resources>

View File

@@ -69,5 +69,9 @@
<dimen name="ref_md_listitem_textsize">16sp</dimen>
<dimen name="ref_md_listitem_height">48dp</dimen>
<dimen name="toolbar_menu_item_width">40dp</dimen>
<dimen name="build_progress_icon_size">24dp</dimen>
<dimen name="build_progress_icon_text_gap">12dp</dimen>
<dimen name="build_progress_icon_text_size">16sp</dimen>
<dimen name="build_progress_row_spacing">12dp</dimen>
</resources>

View File

@@ -277,11 +277,11 @@
<!-- Dividing line between translatable and non-translatable -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<string name="apk_builder_build" tools:ignore="TypographyEllipsis">Building...</string>
<string name="apk_builder_clean" tools:ignore="TypographyEllipsis">Cleaning...</string>
<string name="apk_builder_package" tools:ignore="TypographyEllipsis">Packaging...</string>
<string name="apk_builder_build">Building</string>
<string name="apk_builder_clean">Cleaning</string>
<string name="apk_builder_package">Packaging</string>
<string name="apk_builder_plugin_version_incompatible">Incompatible version of \"apk builder\".\nDownload it now?</string>
<string name="apk_builder_prepare" tools:ignore="TypographyEllipsis">Preparing...</string>
<string name="apk_builder_prepare">Preparing</string>
<string name="apk_info_device_sdk">Device SDK</string>
<string name="apk_info_file_size">File size</string>
<string name="apk_info_installed_version">Installed</string>
@@ -807,7 +807,7 @@
<string name="text_app_shortcut_docs_short_label">Docs</string>
<string name="text_app_shortcut_log_long_label">AutoJs6 Log</string>
<string name="text_app_shortcut_log_short_label">Log</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 Plugin Center</string>
<string name="text_app_shortcut_plugin_center_long_label">AutoJs6 Plugins</string>
<string name="text_app_shortcut_plugin_center_short_label">Plugins</string>
<string name="text_app_shortcut_settings_long_label">AutoJs6 Settings</string>
<string name="text_app_shortcut_settings_short_label">Settings</string>
@@ -945,7 +945,7 @@
<string name="text_delete_folder">Delete folder</string>
<string name="text_delete_line">Delete line</string>
<string name="text_delete_revision_confirm">Delete this revision permanently?</string>
<string name="text_deleting" tools:ignore="TypographyEllipsis">Deleting...</string>
<string name="text_deleting">Deleting</string>
<string name="text_description">Description</string>
<string name="text_deselect_all">Deselect all</string>
<string name="text_destination">Destination</string>
@@ -1048,7 +1048,7 @@
<string name="text_file_name">File name</string>
<string name="text_file_name_colon_value">File name: %1$s</string>
<string name="text_file_not_exists">File does not exist</string>
<string name="text_file_path">File Path</string>
<string name="text_file_path">File path</string>
<string name="text_file_with_abs_path_is_not_an_executable_script">File \"%1$s\" is not an executable script</string>
<string name="text_filename_cannot_be_empty">Filename cannot be empty</string>
<string name="text_filename_cannot_contain_invalid_character">Filename cannot contain the following characters: \\ / : * ? &quot; &lt; &gt; |</string>
@@ -1629,4 +1629,58 @@
<string name="error_plugin_is_not_authorized_in_plugin_center">Plugin \"%1$s\" is not authorized in Plugin Center</string>
<string name="error_plugin_not_found_in_installed_apps">Plugin \"%1$s\" not found in installed apps</string>
<string name="error_unsupported_plugin_connection">Unsupported plugin connection</string>
<string name="text_property_colon">%1$s:</string>
<string name="text_title">Title</string>
<string name="text_content">Content</string>
<string name="text_building_apk">Building APK</string>
<string name="text_built_apk_file_path">Built APK file path</string>
<string name="text_clean">Clean</string>
<string name="text_sign">Sign</string>
<string name="text_build">Build</string>
<string name="text_prepare">Prepare</string>
<string name="text_preparing_workspace">Preparing workspace</string>
<string name="text_extracting_template_apk">Extracting template APK</string>
<string name="text_prepare_completed">Prepare completed</string>
<string name="text_copying_project_directory">Copying project directory</string>
<string name="text_copying_script_file">Copying script file</string>
<string name="text_source_processing_completed">Source processing completed</string>
<string name="text_copying_directory">Copying directory</string>
<string name="text_copying_file">Copying file</string>
<string name="text_encrypting_script">Encrypting script</string>
<string name="text_replacing_file">Replacing file</string>
<string name="text_preparing_build_config">Preparing build config</string>
<string name="text_reading_splash_resources">Reading splash resources</string>
<string name="text_configuring_manifest">Configuring manifest</string>
<string name="text_configuring_package_name">Configuring package name</string>
<string name="text_copying_assets_to">Copying assets to</string>
<string name="text_processing_source">Processing source</string>
<string name="text_applying_binary_resources">Applying binary resources</string>
<string name="text_copying_native_libraries">Copying native libraries</string>
<string name="text_updating_project_config">Updating project config</string>
<string name="text_sign_stage_completed">Sign stage completed</string>
<string name="text_writing_project_config">Writing project config</string>
<string name="text_building_resources">Building resources</string>
<string name="text_build_completed">Build completed</string>
<string name="text_writing_resources_arsc">Writing resources.arsc</string>
<string name="text_writing_manifest">Writing manifest</string>
<string name="text_writing_app_icon">Writing app icon</string>
<string name="text_copying_asset">Copying asset</string>
<string name="text_preparing_assets_dir">Preparing assets dir</string>
<string name="text_copying_library">Copying library</string>
<string name="text_preparing_keystore">Preparing keystore</string>
<string name="text_unsigned_apk_created">Unsigned APK created</string>
<string name="text_creating_unsigned_apk">Creating unsigned APK</string>
<string name="text_using_keystore">Using keystore</string>
<string name="text_re_signing_apk">Re-signing APK</string>
<string name="text_writing_signed_apk">Writing signed APK</string>
<string name="text_sign_completed">Sign completed</string>
<string name="text_clean_completed">Clean completed</string>
<string name="text_cleaning_workspace">Cleaning workspace</string>
<string name="text_resolving_plugin_native_libraries">Resolving plugin native libraries</string>
<string name="text_copying_libraries_for_abi">Copying libraries for ABI</string>
<string name="text_selected_plugin">Selected plugin</string>
<string name="text_extracted_plugin_libraries">Extracted plugin libraries</string>
<string name="text_extracted_plugin_assets">Extracted plugin assets</string>
<string name="text_extracting_plugin_asset">Extracting plugin asset</string>
<string name="text_extracting_plugin_so">Extracting plugin so</string>
</resources>