新增打包签名配置和密钥库管理
- 打包功能新增签名配置功能,支持设置签名密钥和签名方案(V1、V1 + V2等) - 新增密钥库管理,支持创建和删除自定义签名密钥库等
This commit is contained in:
@@ -193,6 +193,14 @@ dependencies /* Unclassified */ {
|
|||||||
// Toaster
|
// Toaster
|
||||||
implementation("com.github.getActivity:Toaster:12.6")
|
implementation("com.github.getActivity:Toaster:12.6")
|
||||||
implementation("com.github.getActivity:EasyWindow:10.3")
|
implementation("com.github.getActivity:EasyWindow:10.3")
|
||||||
|
|
||||||
|
// apksigner
|
||||||
|
implementation("com.github.TimScriptov:apksigner:1.2.0")
|
||||||
|
|
||||||
|
// room
|
||||||
|
implementation("androidx.room:room-runtime:2.6.1")
|
||||||
|
implementation("androidx.room:room-ktx:2.6.1")
|
||||||
|
ksp("androidx.room:room-compiler:2.6.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies /* MIME */ {
|
dependencies /* MIME */ {
|
||||||
|
|||||||
@@ -256,6 +256,9 @@
|
|||||||
|
|
||||||
<activity android:name="org.autojs.autojs.ui.project.ProjectConfigActivity" />
|
<activity android:name="org.autojs.autojs.ui.project.ProjectConfigActivity" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="org.autojs.autojs.ui.keystore.ManageKeyStoreActivity" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.autojs.autojs.ui.log.LogActivity"
|
android:name="org.autojs.autojs.ui.log.LogActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
BIN
app/src/main/assets/default_key_store.bks
Normal file
BIN
app/src/main/assets/default_key_store.bks
Normal file
Binary file not shown.
@@ -8,7 +8,10 @@ import android.graphics.Bitmap
|
|||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.mcal.apksigner.ApkSigner
|
||||||
import com.reandroid.arsc.chunk.TableBlock
|
import com.reandroid.arsc.chunk.TableBlock
|
||||||
|
import org.apache.commons.io.FileUtils.copyFile
|
||||||
|
import org.apache.commons.io.FileUtils.copyInputStreamToFile
|
||||||
import org.autojs.autojs.app.GlobalAppContext
|
import org.autojs.autojs.app.GlobalAppContext
|
||||||
import org.autojs.autojs.engine.encryption.AdvancedEncryptionStandard
|
import org.autojs.autojs.engine.encryption.AdvancedEncryptionStandard
|
||||||
import org.autojs.autojs.pio.PFiles
|
import org.autojs.autojs.pio.PFiles
|
||||||
@@ -16,6 +19,7 @@ import org.autojs.autojs.project.BuildInfo
|
|||||||
import org.autojs.autojs.project.ProjectConfig
|
import org.autojs.autojs.project.ProjectConfig
|
||||||
import org.autojs.autojs.script.EncryptedScriptFileHeader.writeHeader
|
import org.autojs.autojs.script.EncryptedScriptFileHeader.writeHeader
|
||||||
import org.autojs.autojs.script.JavaScriptFileSource
|
import org.autojs.autojs.script.JavaScriptFileSource
|
||||||
|
import org.autojs.autojs.apkbuilder.keystore.KeyStore
|
||||||
import org.autojs.autojs.util.FileUtils.TYPE.JAVASCRIPT
|
import org.autojs.autojs.util.FileUtils.TYPE.JAVASCRIPT
|
||||||
import org.autojs.autojs.util.MD5Utils
|
import org.autojs.autojs.util.MD5Utils
|
||||||
import org.autojs.autojs6.BuildConfig
|
import org.autojs.autojs6.BuildConfig
|
||||||
@@ -269,6 +273,40 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
val fos = FileOutputStream(outApkFile)
|
val fos = FileOutputStream(outApkFile)
|
||||||
TinySign.sign(File(workspacePath), fos)
|
TinySign.sign(File(workspacePath), fos)
|
||||||
fos.close()
|
fos.close()
|
||||||
|
|
||||||
|
val defaultKeyStoreFile = File(workspacePath, "default_key_store.bks")
|
||||||
|
val tmpOutputApk = File(workspacePath, "temp.apk")
|
||||||
|
copyInputStreamToFile(GlobalAppContext.get().assets.open("default_key_store.bks"), defaultKeyStoreFile)
|
||||||
|
|
||||||
|
val signer = ApkSigner(outApkFile, tmpOutputApk)
|
||||||
|
signer.useDefaultSignatureVersion = false
|
||||||
|
signer.v1SigningEnabled = mAppConfig.signatureSchemes.contains("V1")
|
||||||
|
signer.v2SigningEnabled = mAppConfig.signatureSchemes.contains("V2")
|
||||||
|
signer.v3SigningEnabled = mAppConfig.signatureSchemes.contains("V3")
|
||||||
|
signer.v4SigningEnabled = mAppConfig.signatureSchemes.contains("V4")
|
||||||
|
|
||||||
|
var keyStoreFile = defaultKeyStoreFile
|
||||||
|
var password = "AutoJs6"
|
||||||
|
var alias = "AutoJs6"
|
||||||
|
var aliasPassword = "AutoJs6"
|
||||||
|
|
||||||
|
mAppConfig.keyStore?.let {
|
||||||
|
keyStoreFile = File(it.absolutePath)
|
||||||
|
password = it.password
|
||||||
|
alias = it.alias
|
||||||
|
aliasPassword = it.aliasPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 ApkSigner 重新签名
|
||||||
|
if (!signer.signRelease(keyStoreFile, password, alias, aliasPassword)) {
|
||||||
|
throw java.lang.RuntimeException("Failed to re-sign using ApkSigner")
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
copyFile(tmpOutputApk, outApkFile)
|
||||||
|
} catch (e: java.lang.Exception) {
|
||||||
|
throw java.lang.RuntimeException(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cleanWorkspace() = also {
|
fun cleanWorkspace() = also {
|
||||||
@@ -321,6 +359,10 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
private set
|
private set
|
||||||
var libs: List<String> = emptyList()
|
var libs: List<String> = emptyList()
|
||||||
private set
|
private set
|
||||||
|
var keyStore: KeyStore? = null
|
||||||
|
private set
|
||||||
|
var signatureSchemes: String = "V1 + V2"
|
||||||
|
private set
|
||||||
|
|
||||||
fun ignoreDir(dir: File) = also { ignoredDirs.add(dir) }
|
fun ignoreDir(dir: File) = also { ignoredDirs.add(dir) }
|
||||||
|
|
||||||
@@ -342,6 +384,10 @@ open class ApkBuilder(apkInputStream: InputStream?, private val outApkFile: File
|
|||||||
|
|
||||||
fun setLibs(libs: List<String>) = also { this.libs = libs }
|
fun setLibs(libs: List<String>) = also { this.libs = libs }
|
||||||
|
|
||||||
|
fun setKeyStore(keyStore: KeyStore?) = also { this.keyStore = keyStore }
|
||||||
|
|
||||||
|
fun setSignatureSchemes(signatureSchemes: String) = also { this.signatureSchemes = signatureSchemes }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun fromProjectConfig(projectDir: String?, projectConfig: ProjectConfig) = AppConfig()
|
fun fromProjectConfig(projectDir: String?, projectConfig: ProjectConfig) = AppConfig()
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.autojs.autojs.apkbuilder.keystore
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class KeyStore(
|
||||||
|
@PrimaryKey val absolutePath: String, // 密钥库绝对路径
|
||||||
|
@ColumnInfo(name = "filename") val filename: String = "", // 文件名
|
||||||
|
@ColumnInfo(name = "password") val password: String = "", // 密码
|
||||||
|
@ColumnInfo(name = "alias") val alias: String = "", // 别名
|
||||||
|
@ColumnInfo(name = "alias_password") val aliasPassword: String = "", // 别名密码
|
||||||
|
@ColumnInfo(name = "verified") val verified: Boolean = false, // 验证状态
|
||||||
|
) {
|
||||||
|
override fun toString(): String = filename
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.autojs.autojs.apkbuilder.keystore
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Upsert
|
||||||
|
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface KeyStoreDao {
|
||||||
|
|
||||||
|
@Query("SELECT * FROM keystore WHERE absolutePath = :absolutePath LIMIT 1")
|
||||||
|
suspend fun getByAbsolutePath(absolutePath: String): KeyStore?
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
suspend fun upsert(vararg keyStores: KeyStore)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM keystore")
|
||||||
|
suspend fun getAll(): List<KeyStore>
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
suspend fun delete(vararg keyStores: KeyStore)
|
||||||
|
|
||||||
|
@Query("DELETE FROM keystore WHERE absolutePath = :absolutePath")
|
||||||
|
suspend fun deleteByAbsolutePath(absolutePath: String): Int
|
||||||
|
|
||||||
|
@Query("DELETE FROM keystore")
|
||||||
|
suspend fun deleteAll()
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.autojs.autojs.apkbuilder.keystore
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
|
||||||
|
@Database(entities = [KeyStore::class], version = 1, exportSchema = false)
|
||||||
|
abstract class KeyStoreDatabase : RoomDatabase() {
|
||||||
|
abstract fun keyStoreDao(): KeyStoreDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
private var INSTANCE: KeyStoreDatabase? = null
|
||||||
|
|
||||||
|
fun getDatabase(context: Context): KeyStoreDatabase {
|
||||||
|
return INSTANCE ?: synchronized(this) {
|
||||||
|
val instance = Room.databaseBuilder(
|
||||||
|
context.applicationContext,
|
||||||
|
KeyStoreDatabase::class.java,
|
||||||
|
"keystore-database"
|
||||||
|
).build()
|
||||||
|
INSTANCE = instance
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package org.autojs.autojs.apkbuilder.keystore
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class KeyStoreRepository(context: Context) {
|
||||||
|
|
||||||
|
private var dao: KeyStoreDao
|
||||||
|
|
||||||
|
init {
|
||||||
|
val keyStoreDatabase = KeyStoreDatabase.getDatabase(context)
|
||||||
|
dao = keyStoreDatabase.keyStoreDao()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有 KeyStore
|
||||||
|
suspend fun getAllKeyStores(): List<KeyStore> {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
dao.getAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插入或更新 KeyStore
|
||||||
|
suspend fun upsertKeyStores(vararg keyStores: KeyStore) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
dao.upsert(*keyStores)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据绝对路径获取 KeyStore
|
||||||
|
suspend fun getKeyStoreAbsolutePath(absolutePath: String): KeyStore? {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
dao.getByAbsolutePath(absolutePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 KeyStore
|
||||||
|
suspend fun deleteKeyStores(vararg keyStores: KeyStore) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
dao.delete(*keyStores)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除所有 KeyStore
|
||||||
|
suspend fun deleteAllKeyStores() {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
dao.deleteAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -178,7 +178,7 @@ public class ConsoleView extends FrameLayout implements ConsoleImpl.LogListener
|
|||||||
return /* default text size */ 14;
|
return /* default text size */ 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTextColors(@NotNull Integer[] colors) {
|
public void setTextColors(Integer[] colors) {
|
||||||
Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter();
|
Adapter adapter = (Adapter) mLogListRecyclerView.getAdapter();
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
adapter.setTextColors(colors);
|
adapter.setTextColors(colors);
|
||||||
|
|||||||
@@ -172,6 +172,11 @@ object Pref {
|
|||||||
@ScriptInterfaceCompatible
|
@ScriptInterfaceCompatible
|
||||||
fun getScriptDirPath() = WorkingDirectoryUtils.path
|
fun getScriptDirPath() = WorkingDirectoryUtils.path
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getKeyStorePath(): String {
|
||||||
|
return getScriptDirPath() + "/.KeyStore/"
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun registerOnSharedPreferenceChangeListener(listener: OnSharedPreferenceChangeListener) {
|
fun registerOnSharedPreferenceChangeListener(listener: OnSharedPreferenceChangeListener) {
|
||||||
sPref.registerOnSharedPreferenceChangeListener(listener)
|
sPref.registerOnSharedPreferenceChangeListener(listener)
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package org.autojs.autojs.ui.keystore
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import org.autojs.autojs.apkbuilder.keystore.KeyStore
|
||||||
|
import org.autojs.autojs6.R
|
||||||
|
import org.autojs.autojs6.databinding.ItemKeyStoreBinding
|
||||||
|
|
||||||
|
class KeyStoreAdaptor(
|
||||||
|
private val keyStoreAdapterCallback: KeyStoreAdapterCallback,
|
||||||
|
) : ListAdapter<KeyStore, KeyStoreAdaptor.KeyStoreViewHolder>(KeyStoreDiffCallback()) {
|
||||||
|
|
||||||
|
class KeyStoreDiffCallback : DiffUtil.ItemCallback<KeyStore>() {
|
||||||
|
override fun areItemsTheSame(oldItem: KeyStore, newItem: KeyStore): Boolean {
|
||||||
|
return oldItem.absolutePath == newItem.absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: KeyStore, newItem: KeyStore): Boolean {
|
||||||
|
return oldItem.filename == newItem.filename &&
|
||||||
|
oldItem.password == newItem.password &&
|
||||||
|
oldItem.alias == newItem.alias &&
|
||||||
|
oldItem.aliasPassword == newItem.aliasPassword &&
|
||||||
|
oldItem.verified == newItem.verified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): KeyStoreViewHolder {
|
||||||
|
val binding = ItemKeyStoreBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context), parent, false
|
||||||
|
)
|
||||||
|
return KeyStoreViewHolder(binding).apply {
|
||||||
|
binding.delete.setOnClickListener {
|
||||||
|
if (bindingAdapterPosition != RecyclerView.NO_POSITION) {
|
||||||
|
keyStoreAdapterCallback.onDeleteButtonClicked(getItem(bindingAdapterPosition))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.verify.setOnClickListener {
|
||||||
|
if (bindingAdapterPosition != RecyclerView.NO_POSITION) {
|
||||||
|
keyStoreAdapterCallback.onVerifyButtonClicked(getItem(bindingAdapterPosition))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itemView.setOnClickListener {
|
||||||
|
if (bindingAdapterPosition != RecyclerView.NO_POSITION) {
|
||||||
|
keyStoreAdapterCallback.onVerifyButtonClicked(getItem(bindingAdapterPosition))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: KeyStoreViewHolder, position: Int) {
|
||||||
|
holder.bind(getItem(position))
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class KeyStoreViewHolder(private val binding: ItemKeyStoreBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
fun bind(item: KeyStore) {
|
||||||
|
binding.apply {
|
||||||
|
filename.text = itemView.context.getString(
|
||||||
|
R.string.text_str_colon_space_str_formatter,
|
||||||
|
itemView.context.getString(R.string.text_file_name),
|
||||||
|
item.filename
|
||||||
|
)
|
||||||
|
alias.text = itemView.context.getString(
|
||||||
|
R.string.text_str_colon_space_str_formatter,
|
||||||
|
itemView.context.getString(R.string.text_key_alias),
|
||||||
|
item.alias
|
||||||
|
)
|
||||||
|
verify.setImageResource(
|
||||||
|
if (item.verified) R.drawable.ic_key_store_verified else R.drawable.ic_key_store_unverified
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KeyStoreAdapterCallback {
|
||||||
|
fun onDeleteButtonClicked(keyStore: KeyStore)
|
||||||
|
fun onVerifyButtonClicked(keyStore: KeyStore)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
package org.autojs.autojs.ui.keystore
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.afollestad.materialdialogs.DialogAction
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.mcal.apksigner.CertCreator
|
||||||
|
import com.mcal.apksigner.utils.DistinguishedNameValues
|
||||||
|
import com.mcal.apksigner.utils.KeyStoreHelper
|
||||||
|
import org.autojs.autojs.apkbuilder.keystore.KeyStore
|
||||||
|
import org.autojs.autojs.core.pref.Pref
|
||||||
|
import org.autojs.autojs6.R
|
||||||
|
import org.autojs.autojs6.databinding.ActivityManageKeyStoreBinding
|
||||||
|
import org.autojs.autojs.ui.BaseActivity
|
||||||
|
import org.autojs.autojs.ui.keystore.NewKeyStoreDialog.NewKeyStoreConfigs
|
||||||
|
import org.autojs.autojs.ui.viewmodel.KeyStoreViewModel
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
|
||||||
|
class ManageKeyStoreActivity : BaseActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityManageKeyStoreBinding
|
||||||
|
private lateinit var keyStoreAdapter: KeyStoreAdaptor
|
||||||
|
private lateinit var keyStoreViewModel: KeyStoreViewModel
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun startActivity(context: Context) {
|
||||||
|
Intent(context, ManageKeyStoreActivity::class.java).apply {}.also {
|
||||||
|
ContextCompat.startActivity(context, it, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val newKeyStoreDialogCallback = object : NewKeyStoreDialog.Callback {
|
||||||
|
override fun onConfirmButtonClicked(configs: NewKeyStoreConfigs) {
|
||||||
|
createKeyStore(configs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val verifyKeyStoreDialog = object : VerifyKeyStoreDialog.Callback {
|
||||||
|
override fun onVerifyButtonClicked(
|
||||||
|
configs: VerifyKeyStoreDialog.VerifyKeyStoreConfigs, keyStore: KeyStore,
|
||||||
|
) {
|
||||||
|
verifyKeyStore(configs, keyStore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val keyStoreAdapterCallback = object : KeyStoreAdaptor.KeyStoreAdapterCallback {
|
||||||
|
override fun onDeleteButtonClicked(keyStore: KeyStore) {
|
||||||
|
MaterialDialog.Builder(this@ManageKeyStoreActivity)
|
||||||
|
.title(getString(R.string.text_confirm_to_delete))
|
||||||
|
.positiveText(R.string.text_ok).negativeText(R.string.text_cancel)
|
||||||
|
.onPositive { _: MaterialDialog, _: DialogAction ->
|
||||||
|
deleteKeyStore(keyStore)
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onVerifyButtonClicked(keyStore: KeyStore) {
|
||||||
|
VerifyKeyStoreDialog(verifyKeyStoreDialog, keyStore).show(supportFragmentManager, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityManageKeyStoreBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
setToolbarAsBack(getString(R.string.text_manage_key_store))
|
||||||
|
|
||||||
|
keyStoreViewModel =
|
||||||
|
ViewModelProvider(this, KeyStoreViewModel.Factory(this))[KeyStoreViewModel::class.java]
|
||||||
|
|
||||||
|
binding.fab.setOnClickListener {
|
||||||
|
NewKeyStoreDialog(newKeyStoreDialogCallback).show(supportFragmentManager, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyStoreAdapter = KeyStoreAdaptor(keyStoreAdapterCallback)
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
adapter = keyStoreAdapter
|
||||||
|
layoutManager = LinearLayoutManager(this@ManageKeyStoreActivity)
|
||||||
|
itemAnimator = DefaultItemAnimator()
|
||||||
|
}
|
||||||
|
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||||
|
loadKeyStores()
|
||||||
|
binding.recyclerView.postDelayed({
|
||||||
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
|
}, 800)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyStoreViewModel.allKeyStores.observe(this@ManageKeyStoreActivity) {
|
||||||
|
keyStoreAdapter.submitList(it.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
loadKeyStores()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
loadKeyStores()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
menuInflater.inflate(R.menu.menu_manage_key_store, menu)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.action_delete_all -> {
|
||||||
|
MaterialDialog.Builder(this@ManageKeyStoreActivity)
|
||||||
|
.title(getString(R.string.text_delete_all))
|
||||||
|
.positiveText(R.string.text_ok).negativeText(R.string.text_cancel)
|
||||||
|
.onPositive { _: MaterialDialog, _: DialogAction ->
|
||||||
|
deleteAllKeyStores()
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadKeyStores() {
|
||||||
|
val path = File(Pref.getKeyStorePath())
|
||||||
|
if (!path.isDirectory) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val filteredFiles = path.listFiles { _, name ->
|
||||||
|
name.endsWith(".bks") || name.endsWith(".jks")
|
||||||
|
} ?: emptyArray()
|
||||||
|
|
||||||
|
keyStoreViewModel.updateAllKeyStoresFromFiles(filteredFiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createKeyStore(configs: NewKeyStoreConfigs) {
|
||||||
|
val keyStorePath = File(Pref.getKeyStorePath())
|
||||||
|
keyStorePath.mkdirs()
|
||||||
|
val file = File(keyStorePath, configs.filename)
|
||||||
|
|
||||||
|
val distinguishedNameValues = DistinguishedNameValues().apply {
|
||||||
|
setCommonName(configs.firstAndLastName)
|
||||||
|
setOrganization(configs.organization)
|
||||||
|
setOrganizationalUnit(configs.organizationalUnit)
|
||||||
|
setCountry(configs.countryCode)
|
||||||
|
setState(configs.stateOrProvince)
|
||||||
|
setLocality(configs.cityOrLocality)
|
||||||
|
setStreet(configs.street)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
CertCreator.createKeystoreAndKey(
|
||||||
|
file,
|
||||||
|
configs.password.toCharArray(),
|
||||||
|
"RSA",
|
||||||
|
2048,
|
||||||
|
configs.alias,
|
||||||
|
configs.aliasPassword.toCharArray(),
|
||||||
|
configs.signatureAlgorithm,
|
||||||
|
configs.validityYears,
|
||||||
|
distinguishedNameValues
|
||||||
|
)
|
||||||
|
val newKeyStore = KeyStore(
|
||||||
|
absolutePath = file.absolutePath,
|
||||||
|
filename = file.name,
|
||||||
|
password = configs.password,
|
||||||
|
alias = configs.alias,
|
||||||
|
aliasPassword = configs.aliasPassword,
|
||||||
|
verified = true
|
||||||
|
)
|
||||||
|
keyStoreViewModel.upsertKeyStore(newKeyStore)
|
||||||
|
showToast(R.string.text_successfully_created_key_store)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
showToast(getString(R.string.text_failed_to_create_key_store) + " " + e.message)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showToast(getString(R.string.text_failed_to_create_key_store) + " " + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteKeyStore(keyStore: KeyStore) {
|
||||||
|
val keyStorePath = keyStore.absolutePath
|
||||||
|
val keyStoreFile = File(keyStorePath)
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (keyStoreFile.delete()) {
|
||||||
|
keyStoreViewModel.deleteKeyStore(keyStore)
|
||||||
|
showToast(getString(R.string.text_already_deleted) + " " + keyStore.filename)
|
||||||
|
} else {
|
||||||
|
showToast(getString(R.string.text_failed_to_delete))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showToast(getString(R.string.text_failed_to_delete) + ": " + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteAllKeyStores() {
|
||||||
|
val path = File(Pref.getKeyStorePath())
|
||||||
|
if (!path.isDirectory) return
|
||||||
|
|
||||||
|
val files = path.listFiles { _, name -> name.endsWith(".bks") || name.endsWith(".jks") }
|
||||||
|
files?.forEach { file ->
|
||||||
|
file.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
keyStoreViewModel.deleteAllKeyStores()
|
||||||
|
showToast(getString(R.string.text_already_deleted))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyKeyStore(
|
||||||
|
configs: VerifyKeyStoreDialog.VerifyKeyStoreConfigs, keyStore: KeyStore,
|
||||||
|
) {
|
||||||
|
// 验证密钥库密码
|
||||||
|
val tmpKeyStore = try {
|
||||||
|
KeyStoreHelper.loadKeyStore(File(keyStore.absolutePath), configs.password.toCharArray())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmpKeyStore == null) {
|
||||||
|
showToast(R.string.text_verify_failed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证别名和别名密码
|
||||||
|
val tmpKey = try {
|
||||||
|
tmpKeyStore.getKey(configs.alias, configs.aliasPassword.toCharArray())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmpKey == null) {
|
||||||
|
showToast(R.string.text_verify_failed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val verifiedKeyStore = KeyStore(
|
||||||
|
absolutePath = keyStore.absolutePath,
|
||||||
|
filename = keyStore.filename,
|
||||||
|
password = configs.password,
|
||||||
|
alias = configs.alias,
|
||||||
|
aliasPassword = configs.aliasPassword,
|
||||||
|
verified = true
|
||||||
|
)
|
||||||
|
keyStoreViewModel.upsertKeyStore(verifiedKeyStore)
|
||||||
|
showToast(R.string.text_verify_success)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showToast(@StringRes messageResId: Int) {
|
||||||
|
Toast.makeText(this, getString(messageResId), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showToast(message: String) {
|
||||||
|
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
package org.autojs.autojs.ui.keystore
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import org.autojs.autojs6.R
|
||||||
|
import org.autojs.autojs6.databinding.DialogNewKeyStoreBinding
|
||||||
|
|
||||||
|
open class NewKeyStoreDialog(
|
||||||
|
private val callback: Callback,
|
||||||
|
) : DialogFragment() {
|
||||||
|
|
||||||
|
private lateinit var binding: DialogNewKeyStoreBinding
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
|
||||||
|
): View {
|
||||||
|
binding = DialogNewKeyStoreBinding.inflate(inflater)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
dialog?.window?.setLayout(
|
||||||
|
(resources.displayMetrics.widthPixels * 0.85f).toInt(),
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
dialog?.setCanceledOnTouchOutside(true)
|
||||||
|
|
||||||
|
val signatureAlgorithms = arrayOf("MD5withRSA", "SHA1withRSA", "SHA256withRSA", "SHA512withRSA")
|
||||||
|
val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, signatureAlgorithms)
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||||
|
binding.signatureAlgorithms.adapter = adapter
|
||||||
|
|
||||||
|
binding.confirm.setOnClickListener {
|
||||||
|
var error = false
|
||||||
|
val filename = binding.filename.text.toString()
|
||||||
|
val password = binding.password.text.toString()
|
||||||
|
val alias = binding.alias.text.toString()
|
||||||
|
val aliasPassword = binding.aliasPassword.text.toString()
|
||||||
|
var valvalidityYears = 25
|
||||||
|
|
||||||
|
// 检查文件名是否符合Android命名规格
|
||||||
|
when {
|
||||||
|
filename.isEmpty() -> {
|
||||||
|
binding.filenameTextInputLayout.error = getString(R.string.text_filename_cannot_be_empty)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
!containsSpecialCharacters(filename) -> {
|
||||||
|
binding.filenameTextInputLayout.error = getString(R.string.text_filename_cannot_contain_invalid_character)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
filename.length > 255 -> {
|
||||||
|
binding.filenameTextInputLayout.error = getString(R.string.text_filename_is_too_long)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> binding.filenameTextInputLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查密码是否符合要求
|
||||||
|
when {
|
||||||
|
password.isEmpty() -> {
|
||||||
|
binding.passwordTextInputLayout.error = getString(R.string.text_password_cannot_be_empty)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
password.length < 6 -> {
|
||||||
|
binding.passwordTextInputLayout.error = getString(R.string.text_password_requires_at_least_n_characters, 6)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> binding.passwordTextInputLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查别名密码是否符合要求
|
||||||
|
when {
|
||||||
|
aliasPassword.isEmpty() -> {
|
||||||
|
binding.aliasPasswordTextInputLayout.error = getString(R.string.text_password_cannot_be_empty)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
aliasPassword.length < 6 -> {
|
||||||
|
binding.aliasPasswordTextInputLayout.error = getString(R.string.text_password_requires_at_least_n_characters, 6)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> binding.aliasPasswordTextInputLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查别名是否符合要求
|
||||||
|
if (alias.isEmpty()) {
|
||||||
|
binding.aliasTextInputLayout.error = getString(R.string.text_alias_cannot_be_empty)
|
||||||
|
error = true
|
||||||
|
} else {
|
||||||
|
binding.aliasTextInputLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查有效期是否符合要求
|
||||||
|
if (binding.validityYears.text.toString().isEmpty()) {
|
||||||
|
binding.validityYearsTextInputLayout.error = getString(R.string.text_validity_years_cannot_be_empty)
|
||||||
|
error = true
|
||||||
|
} else {
|
||||||
|
val years = binding.validityYears.text.toString().toInt()
|
||||||
|
if (years == 0) {
|
||||||
|
binding.validityYearsTextInputLayout.error = getString(R.string.text_validity_years_cannot_be_zero)
|
||||||
|
error = true
|
||||||
|
} else {
|
||||||
|
binding.validityYearsTextInputLayout.error = null
|
||||||
|
valvalidityYears = years
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val firstAndLastName = binding.firstAndLastName.text.toString()
|
||||||
|
|
||||||
|
val organization = binding.organization.text.toString()
|
||||||
|
val organizationalUnit = binding.organizationalUnit.text.toString()
|
||||||
|
|
||||||
|
val countryCode = binding.countryCode.text.toString()
|
||||||
|
val stateOrProvince = binding.stateOrProvince.text.toString()
|
||||||
|
val cityOrLocality = binding.cityOrLocality.text.toString()
|
||||||
|
val street = binding.street.text.toString()
|
||||||
|
|
||||||
|
if (firstAndLastName.isEmpty() && organization.isEmpty() &&
|
||||||
|
organizationalUnit.isEmpty() && stateOrProvince.isEmpty() &&
|
||||||
|
cityOrLocality.isEmpty() && street.isEmpty() && countryCode.isEmpty()
|
||||||
|
) {
|
||||||
|
binding.firstAndLastNameTextInputLayout.error = getString(R.string.text_at_least_one_certificate_issuer_field_is_not_empty)
|
||||||
|
binding.organizationTextInputLayout.error = getString(R.string.text_at_least_one_certificate_issuer_field_is_not_empty)
|
||||||
|
binding.organizationalUnitTextInputLayout.error = getString(R.string.text_at_least_one_certificate_issuer_field_is_not_empty)
|
||||||
|
binding.countryCodeTextInputLayout.error = getString(R.string.text_at_least_one_certificate_issuer_field_is_not_empty)
|
||||||
|
binding.stateOrProvinceTextInputLayout.error = getString(R.string.text_at_least_one_certificate_issuer_field_is_not_empty)
|
||||||
|
binding.cityOrLocalityTextInputLayout.error = getString(R.string.text_at_least_one_certificate_issuer_field_is_not_empty)
|
||||||
|
binding.streetTextInputLayout.error = getString(R.string.text_at_least_one_certificate_issuer_field_is_not_empty)
|
||||||
|
error = true
|
||||||
|
} else {
|
||||||
|
binding.firstAndLastNameTextInputLayout.error = null
|
||||||
|
binding.organizationTextInputLayout.error = null
|
||||||
|
binding.organizationalUnitTextInputLayout.error = null
|
||||||
|
binding.countryCodeTextInputLayout.error = null
|
||||||
|
binding.stateOrProvinceTextInputLayout.error = null
|
||||||
|
binding.cityOrLocalityTextInputLayout.error = null
|
||||||
|
binding.streetTextInputLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查国家代码是否符合要求 (ISO3166-1-Alpha-2: https://countrycodedata.com/)
|
||||||
|
val countryCodeRegex = "^[A-Z]{2}$".toRegex()
|
||||||
|
if (countryCode.isNotEmpty() && !countryCodeRegex.matches(countryCode)) {
|
||||||
|
binding.countryCodeTextInputLayout.error = getString(R.string.text_country_code_must_be_two_capital_letters)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) return@setOnClickListener
|
||||||
|
|
||||||
|
val suffix = getString(
|
||||||
|
if (binding.typeJks.isChecked) R.string.text_jks
|
||||||
|
else R.string.text_bks
|
||||||
|
).lowercase()
|
||||||
|
|
||||||
|
val signatureAlgorithm = binding.signatureAlgorithms.selectedItem.toString()
|
||||||
|
|
||||||
|
val configs = NewKeyStoreConfigs(
|
||||||
|
filename = "$filename.$suffix",
|
||||||
|
password = password,
|
||||||
|
alias = alias,
|
||||||
|
aliasPassword = aliasPassword,
|
||||||
|
signatureAlgorithm = signatureAlgorithm,
|
||||||
|
validityYears = valvalidityYears,
|
||||||
|
firstAndLastName = firstAndLastName,
|
||||||
|
organization = organization,
|
||||||
|
organizationalUnit = organizationalUnit,
|
||||||
|
countryCode = countryCode,
|
||||||
|
stateOrProvince = stateOrProvince,
|
||||||
|
cityOrLocality = cityOrLocality,
|
||||||
|
street = street
|
||||||
|
)
|
||||||
|
callback.onConfirmButtonClicked(configs)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.cancel.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.moreOptions.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
binding.moreOptionsContainer.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
return super.onCreateDialog(savedInstanceState).apply {
|
||||||
|
window?.apply {
|
||||||
|
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||||
|
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun containsSpecialCharacters(fileName: String): Boolean {
|
||||||
|
// 定义不允许的字符
|
||||||
|
val invalidCharacters = listOf("\\", "/", ":", "*", "?", "\"", "<", ">", "|")
|
||||||
|
|
||||||
|
// 检查文件名是否包含无效字符
|
||||||
|
for (char in invalidCharacters) {
|
||||||
|
if (fileName.contains(char)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
data class NewKeyStoreConfigs(
|
||||||
|
val filename: String,
|
||||||
|
val password: String,
|
||||||
|
val alias: String,
|
||||||
|
val aliasPassword: String,
|
||||||
|
val signatureAlgorithm: String,
|
||||||
|
val validityYears: Int,
|
||||||
|
val firstAndLastName: String,
|
||||||
|
val organizationalUnit: String,
|
||||||
|
val organization: String,
|
||||||
|
val countryCode: String,
|
||||||
|
val stateOrProvince: String,
|
||||||
|
val cityOrLocality: String,
|
||||||
|
val street: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
fun onConfirmButtonClicked(configs: NewKeyStoreConfigs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package org.autojs.autojs.ui.keystore
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import org.autojs.autojs.apkbuilder.keystore.KeyStore
|
||||||
|
import org.autojs.autojs6.R
|
||||||
|
import org.autojs.autojs6.databinding.DialogVerifyKeyStoreBinding
|
||||||
|
|
||||||
|
open class VerifyKeyStoreDialog(
|
||||||
|
private val callback: Callback,
|
||||||
|
private val keyStore: KeyStore,
|
||||||
|
) : DialogFragment() {
|
||||||
|
|
||||||
|
private lateinit var binding: DialogVerifyKeyStoreBinding
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
|
||||||
|
): View {
|
||||||
|
binding = DialogVerifyKeyStoreBinding.inflate(inflater)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
dialog?.window?.setLayout(
|
||||||
|
(resources.displayMetrics.widthPixels * 0.85f).toInt(),
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
dialog?.setCanceledOnTouchOutside(true)
|
||||||
|
|
||||||
|
binding.filePath.text = keyStore.absolutePath
|
||||||
|
|
||||||
|
if (keyStore.verified) {
|
||||||
|
binding.imgVerifyState.setImageResource(R.drawable.ic_key_store_verified)
|
||||||
|
binding.textVerifyState.text = getString(R.string.text_verified)
|
||||||
|
binding.password.setText(keyStore.password)
|
||||||
|
binding.alias.setText(keyStore.alias)
|
||||||
|
binding.aliasPassword.setText(keyStore.aliasPassword)
|
||||||
|
} else {
|
||||||
|
binding.imgVerifyState.setImageResource(R.drawable.ic_key_store_unverified)
|
||||||
|
binding.textVerifyState.text = getString(R.string.text_unverified)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.verify.setOnClickListener {
|
||||||
|
var error = false
|
||||||
|
val password = binding.password.text.toString()
|
||||||
|
val alias = binding.alias.text.toString()
|
||||||
|
val aliasPassword = binding.aliasPassword.text.toString()
|
||||||
|
|
||||||
|
// 检查密码是否符合要求
|
||||||
|
when {
|
||||||
|
password.isEmpty() -> {
|
||||||
|
binding.passwordTextInputLayout.error = getString(R.string.text_password_cannot_be_empty)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
password.length < 6 -> {
|
||||||
|
binding.passwordTextInputLayout.error = getString(R.string.text_password_requires_at_least_n_characters, 6)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> binding.passwordTextInputLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查别名密码是否符合要求
|
||||||
|
when {
|
||||||
|
aliasPassword.isEmpty() -> {
|
||||||
|
binding.aliasPasswordTextInputLayout.error = getString(R.string.text_password_cannot_be_empty)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
aliasPassword.length < 6 -> {
|
||||||
|
binding.aliasPasswordTextInputLayout.error = getString(R.string.text_password_requires_at_least_n_characters, 6)
|
||||||
|
error = true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> binding.aliasPasswordTextInputLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查别名是否符合要求
|
||||||
|
if (alias.isEmpty()) {
|
||||||
|
binding.aliasTextInputLayout.error = getString(R.string.text_alias_cannot_be_empty)
|
||||||
|
error = true
|
||||||
|
} else {
|
||||||
|
binding.aliasTextInputLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) return@setOnClickListener
|
||||||
|
|
||||||
|
val configs = VerifyKeyStoreConfigs(
|
||||||
|
password = password,
|
||||||
|
alias = alias,
|
||||||
|
aliasPassword = aliasPassword,
|
||||||
|
)
|
||||||
|
callback.onVerifyButtonClicked(configs, keyStore)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.cancel.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
return super.onCreateDialog(savedInstanceState).apply {
|
||||||
|
window?.apply {
|
||||||
|
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||||
|
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class VerifyKeyStoreConfigs(
|
||||||
|
val password: String,
|
||||||
|
val alias: String,
|
||||||
|
val aliasPassword: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
fun onVerifyButtonClicked(configs: VerifyKeyStoreConfigs, keyStore: KeyStore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -11,12 +11,16 @@ import android.text.util.Linkify;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import com.afollestad.materialdialogs.MaterialDialog;
|
import com.afollestad.materialdialogs.MaterialDialog;
|
||||||
import com.google.android.flexbox.FlexboxLayout;
|
import com.google.android.flexbox.FlexboxLayout;
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
@@ -34,6 +38,9 @@ import org.autojs.autojs.runtime.api.AppUtils.Companion.SimpleVersionInfo;
|
|||||||
import org.autojs.autojs.ui.BaseActivity;
|
import org.autojs.autojs.ui.BaseActivity;
|
||||||
import org.autojs.autojs.ui.common.NotAskAgainDialog;
|
import org.autojs.autojs.ui.common.NotAskAgainDialog;
|
||||||
import org.autojs.autojs.ui.filechooser.FileChooserDialogBuilder;
|
import org.autojs.autojs.ui.filechooser.FileChooserDialogBuilder;
|
||||||
|
import org.autojs.autojs.apkbuilder.keystore.KeyStore;
|
||||||
|
import org.autojs.autojs.ui.viewmodel.KeyStoreViewModel;
|
||||||
|
import org.autojs.autojs.ui.keystore.ManageKeyStoreActivity;
|
||||||
import org.autojs.autojs.ui.shortcut.AppsIconSelectActivity;
|
import org.autojs.autojs.ui.shortcut.AppsIconSelectActivity;
|
||||||
import org.autojs.autojs.ui.widget.RoundCheckboxWithText;
|
import org.autojs.autojs.ui.widget.RoundCheckboxWithText;
|
||||||
import org.autojs.autojs.util.AndroidUtils;
|
import org.autojs.autojs.util.AndroidUtils;
|
||||||
@@ -109,6 +116,16 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
put(ApkBuilder.Constants.MLKIT_BARCODE, /* MLKit Barcode */ List.of("barcode", "mlkit-barcode", "mlkit_barcode"));
|
put(ApkBuilder.Constants.MLKIT_BARCODE, /* MLKit Barcode */ List.of("barcode", "mlkit-barcode", "mlkit_barcode"));
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
private static final ArrayList<String> SIGNATURE_SCHEMES = new ArrayList<>() {{
|
||||||
|
add("V1 + V2");
|
||||||
|
add("V1 + V3");
|
||||||
|
add("V1 + V2 + V3");
|
||||||
|
add("V1");
|
||||||
|
add("V2 + V3 (Android 7.0+)");
|
||||||
|
add("V2 (Android 7.0+)");
|
||||||
|
add("V3 (Android 9.0+)");
|
||||||
|
}};
|
||||||
|
|
||||||
EditText mSourcePath;
|
EditText mSourcePath;
|
||||||
View mSourcePathContainer;
|
View mSourcePathContainer;
|
||||||
EditText mOutputPath;
|
EditText mOutputPath;
|
||||||
@@ -126,6 +143,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
private boolean mIsProjectLevelBuilding;
|
private boolean mIsProjectLevelBuilding;
|
||||||
private FlexboxLayout mFlexboxAbis;
|
private FlexboxLayout mFlexboxAbis;
|
||||||
private FlexboxLayout mFlexboxLibs;
|
private FlexboxLayout mFlexboxLibs;
|
||||||
|
private Spinner mSignatureSchemes;
|
||||||
|
private Spinner mVerifiedKeyStores;
|
||||||
|
|
||||||
private final ArrayList<String> mInvalidAbis = new ArrayList<>();
|
private final ArrayList<String> mInvalidAbis = new ArrayList<>();
|
||||||
private final ArrayList<String> mUnavailableAbis = new ArrayList<>();
|
private final ArrayList<String> mUnavailableAbis = new ArrayList<>();
|
||||||
@@ -133,6 +152,8 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
private final ArrayList<String> mInvalidLibs = new ArrayList<>();
|
private final ArrayList<String> mInvalidLibs = new ArrayList<>();
|
||||||
private final ArrayList<String> mUnavailableLibs = new ArrayList<>();
|
private final ArrayList<String> mUnavailableLibs = new ArrayList<>();
|
||||||
|
|
||||||
|
private KeyStoreViewModel mKeyStoreViewModel;
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
@@ -195,6 +216,15 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
mFlexboxLibs = binding.flexboxLibraries;
|
mFlexboxLibs = binding.flexboxLibraries;
|
||||||
initLibsChildren();
|
initLibsChildren();
|
||||||
|
|
||||||
|
mKeyStoreViewModel = new ViewModelProvider(this, new KeyStoreViewModel.Factory(getApplicationContext())).get(KeyStoreViewModel.class);
|
||||||
|
mKeyStoreViewModel.updateVerifiedKeyStores();
|
||||||
|
|
||||||
|
mSignatureSchemes = binding.spinnerSignatureSchemes;
|
||||||
|
initSignatureSchemeSpinner();
|
||||||
|
|
||||||
|
mVerifiedKeyStores = binding.spinnerVerifiedKeyStores;
|
||||||
|
initVerifiedKeyStoresSpinner();
|
||||||
|
|
||||||
binding.fab.setOnClickListener(v -> buildApk());
|
binding.fab.setOnClickListener(v -> buildApk());
|
||||||
binding.selectSource.setOnClickListener(v -> selectSourceFilePath());
|
binding.selectSource.setOnClickListener(v -> selectSourceFilePath());
|
||||||
binding.selectOutput.setOnClickListener(v -> selectOutputDirPath());
|
binding.selectOutput.setOnClickListener(v -> selectOutputDirPath());
|
||||||
@@ -204,6 +234,7 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
binding.textLibs.setOnClickListener(v -> toggleAllFlexboxChildren(mFlexboxLibs));
|
binding.textLibs.setOnClickListener(v -> toggleAllFlexboxChildren(mFlexboxLibs));
|
||||||
|
binding.manageKeyStore.setOnClickListener(v -> ManageKeyStoreActivity.Companion.startActivity(this));
|
||||||
|
|
||||||
setToolbarAsBack(R.string.text_build_apk);
|
setToolbarAsBack(R.string.text_build_apk);
|
||||||
mSource = getIntent().getStringExtra(EXTRA_SOURCE);
|
mSource = getIntent().getStringExtra(EXTRA_SOURCE);
|
||||||
@@ -217,6 +248,12 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
showHintDialogIfNeeded();
|
showHintDialogIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
mKeyStoreViewModel.updateVerifiedKeyStores();
|
||||||
|
}
|
||||||
|
|
||||||
private void toggleAllFlexboxChildren(FlexboxLayout mFlexboxLibs) {
|
private void toggleAllFlexboxChildren(FlexboxLayout mFlexboxLibs) {
|
||||||
boolean isAllChecked = true;
|
boolean isAllChecked = true;
|
||||||
for (int i = 0; i < mFlexboxLibs.getChildCount(); i += 1) {
|
for (int i = 0; i < mFlexboxLibs.getChildCount(); i += 1) {
|
||||||
@@ -341,6 +378,32 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
mInvalidLibs.addAll(candidates);
|
mInvalidLibs.addAll(candidates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initSignatureSchemeSpinner() {
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, SIGNATURE_SCHEMES);
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
mSignatureSchemes.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initVerifiedKeyStoresSpinner() {
|
||||||
|
ArrayList<KeyStore> verifiedKeyStores = new ArrayList<>();
|
||||||
|
// 添加 默认密钥库 下拉选项
|
||||||
|
KeyStore defaultKeyStore = new KeyStore("", getString(R.string.text_default_key_store), "", "", "", false); // 仅用于显示下拉列表
|
||||||
|
verifiedKeyStores.add(defaultKeyStore);
|
||||||
|
|
||||||
|
ArrayAdapter<KeyStore> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, verifiedKeyStores);
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
mVerifiedKeyStores.setAdapter(adapter);
|
||||||
|
|
||||||
|
mKeyStoreViewModel.getVerifiedKeyStores().observe(this, keyStores -> {
|
||||||
|
// 清空现有的选项,但保留第一个元素,即默认密钥库
|
||||||
|
if (verifiedKeyStores.size() > 1) {
|
||||||
|
verifiedKeyStores.subList(1, verifiedKeyStores.size()).clear();
|
||||||
|
}
|
||||||
|
verifiedKeyStores.addAll(keyStores);
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isAliasMatching(Map<String, List<String>> aliases, String aliasKey, List<String> candidates) {
|
private boolean isAliasMatching(Map<String, List<String>> aliases, String aliasKey, List<String> candidates) {
|
||||||
AtomicBoolean result = new AtomicBoolean(false);
|
AtomicBoolean result = new AtomicBoolean(false);
|
||||||
var aliasList = aliases.getOrDefault(aliasKey, Collections.emptyList());
|
var aliasList = aliases.getOrDefault(aliasKey, Collections.emptyList());
|
||||||
@@ -632,6 +695,12 @@ public class BuildActivity extends BaseActivity implements ApkBuilder.ProgressCa
|
|||||||
|
|
||||||
appConfig.setAbis(abis);
|
appConfig.setAbis(abis);
|
||||||
appConfig.setLibs(libs);
|
appConfig.setLibs(libs);
|
||||||
|
appConfig.setSignatureSchemes(mSignatureSchemes.getSelectedItem().toString());
|
||||||
|
if (mVerifiedKeyStores.getSelectedItemPosition() > 0) {
|
||||||
|
appConfig.setKeyStore((KeyStore) mVerifiedKeyStores.getSelectedItem());
|
||||||
|
} else {
|
||||||
|
appConfig.setKeyStore(null);
|
||||||
|
}
|
||||||
|
|
||||||
return appConfig;
|
return appConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package org.autojs.autojs.ui.viewmodel
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.autojs.autojs.apkbuilder.keystore.KeyStore
|
||||||
|
import org.autojs.autojs.apkbuilder.keystore.KeyStoreRepository
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class KeyStoreViewModel(context: Context) : ViewModel() {
|
||||||
|
private val keyStoreRepository: KeyStoreRepository = KeyStoreRepository(context)
|
||||||
|
|
||||||
|
private val _allKeyStores = MutableLiveData<List<KeyStore>>()
|
||||||
|
val allKeyStores: LiveData<List<KeyStore>> get() = _allKeyStores
|
||||||
|
|
||||||
|
private val _verifiedKeyStores = MutableLiveData<List<KeyStore>>()
|
||||||
|
val verifiedKeyStores: LiveData<List<KeyStore>> get() = _verifiedKeyStores
|
||||||
|
|
||||||
|
init {
|
||||||
|
updateVerifiedKeyStores()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateVerifiedKeyStores() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val keyStores = keyStoreRepository.getAllKeyStores()
|
||||||
|
val validKeyStores = mutableListOf<KeyStore>()
|
||||||
|
|
||||||
|
keyStores.forEach { keyStore ->
|
||||||
|
val file = File(keyStore.absolutePath)
|
||||||
|
if (file.exists()) {
|
||||||
|
validKeyStores.add(keyStore)
|
||||||
|
} else {
|
||||||
|
keyStoreRepository.deleteKeyStores(keyStore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_verifiedKeyStores.value = validKeyStores
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateAllKeyStoresFromFiles(files: Array<File>) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val updatedKeyStores = files.map { file ->
|
||||||
|
keyStoreRepository.getKeyStoreAbsolutePath(file.absolutePath) ?: KeyStore(
|
||||||
|
absolutePath = file.absolutePath,
|
||||||
|
filename = file.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_allKeyStores.value = updatedKeyStores
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun upsertKeyStore(keyStore: KeyStore) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
keyStoreRepository.upsertKeyStores(keyStore)
|
||||||
|
|
||||||
|
val currentKeyStores = _allKeyStores.value ?: emptyList()
|
||||||
|
|
||||||
|
val updatedKeyStores =
|
||||||
|
if (currentKeyStores.any { it.absolutePath == keyStore.absolutePath }) {
|
||||||
|
currentKeyStores.map {
|
||||||
|
if (keyStore.absolutePath == it.absolutePath) {
|
||||||
|
keyStore
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentKeyStores + keyStore
|
||||||
|
}
|
||||||
|
|
||||||
|
_allKeyStores.value = updatedKeyStores
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteKeyStore(keyStore: KeyStore) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
keyStoreRepository.deleteKeyStores(keyStore)
|
||||||
|
|
||||||
|
val currentKeyStores = _allKeyStores.value ?: emptyList()
|
||||||
|
val updatedKeyStores = currentKeyStores.filter { it != keyStore }
|
||||||
|
_allKeyStores.value = updatedKeyStores
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteAllKeyStores() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
keyStoreRepository.deleteAllKeyStores()
|
||||||
|
_allKeyStores.value = emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
class Factory(private val context: Context) : ViewModelProvider.Factory {
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
|
return KeyStoreViewModel(context) as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/src/main/res/drawable/ic_delete_all.xml
Normal file
19
app/src/main/res/drawable/ic_delete_all.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M16.9,25.2h0c-0.2,0.8 -0.6,2.1 -1.1,3.6h2.3c-0.6,-1.7 -1,-2.9 -1.2,-3.6Z" />
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M32.1,5.5l-2.3,-2.3l-11.6,0l-2.3,2.3l-8.1,0l0,4.6l32.4,0l0,-4.6l-8.1,0z" />
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M10.1,40.2c0,2.6 2.1,4.6 4.6,4.6h18.5c2.6,0 4.6,-2.1 4.6,-4.6V12.4H10.1v27.7ZM29.1,23.9h1.8v6.8h4.1l-0.2,1.5h-5.7v-8.3ZM22.1,23.9h1.8v6.8h4.1l-0.2,1.5h-5.7v-8.3ZM15.8,23.9h2.2l2.9,8.3h-1.9l-0.7,-1.9h-3l-0.6,1.9h-1.8l2.8,-8.3Z" />
|
||||||
|
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/ic_key_store_delete.xml
Normal file
5
app/src/main/res/drawable/ic_key_store_delete.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="48dp" android:tint="#999999" android:viewportHeight="24" android:viewportWidth="24" android:width="48dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_key_store_unverified.xml
Normal file
9
app/src/main/res/drawable/ic_key_store_unverified.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="1024"
|
||||||
|
android:viewportHeight="1024">
|
||||||
|
<path
|
||||||
|
android:pathData="M512.2,32L947.2,155.8v261c0,253.3 -162.4,478.7 -404.9,565.1l-17,5.8 -13.2,4.3 -13.2,-4.3c-246.1,-79.9 -414.3,-301.3 -421.8,-553.3L76.8,416.8L76.8,155.8L512.2,32zM506,668.9a56.4,56.4 0,0 0,-41.2 15.6,51.6 51.6,0 0,0 -16.6,39.9c0,15.6 5.6,28.9 16.6,39.9a57,57 0,0 0,41.2 16.4c15.9,0 30.1,-5.5 41.2,-15.7a55.2,55.2 0,0 0,17.4 -40.6,51.6 51.6,0 0,0 -16.6,-39.9c-11.1,-10.9 -25.3,-15.6 -41.9,-15.6zM520.3,211.2c-58.6,0 -104.4,16.4 -137.7,50 -33.3,32.9 -49.9,77.4 -49.9,134.6h83.1c0,-34.4 7.1,-61 21.4,-79.8 15.8,-22.7 41.9,-33.7 77.5,-33.7 28.5,0 51.5,7.9 67.3,23.5 15,15.7 23,36.8 23,64.2 0,20.4 -7.9,39.1 -22.2,57.1l-13.4,14.8C520.3,485.1 490.2,517.1 480,539c-11.1,20.4 -15.8,45.4 -15.8,74.4v13.3h84.7v-13.3c0,-18.8 4,-36 12.7,-51.6 7,-14.1 18.2,-28.2 33.2,-40.7 37.1,-32.1 59.3,-52.4 66.4,-61 19.8,-25.1 30.1,-57.2 30.1,-95.5 0,-46.9 -15.8,-84.5 -47.5,-111.9 -31.6,-28.2 -72.8,-41.5 -123.4,-41.5z"
|
||||||
|
android:fillColor="#2D9AFF"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_key_store_verified.xml
Normal file
9
app/src/main/res/drawable/ic_key_store_verified.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="1024"
|
||||||
|
android:viewportHeight="1024">
|
||||||
|
<path
|
||||||
|
android:pathData="M512.2,32L947.2,155.8v261c0,253.3 -162.4,478.7 -404.9,565.1l-17,5.8 -13.2,4.3 -13.2,-4.3c-246.1,-79.9 -414.3,-301.3 -421.8,-553.3L76.8,416.8V155.8L512.2,32zM726.4,320L459.8,582 304,428.9l-60.8,62.2L459.8,704 787.2,382.3 726.4,320z"
|
||||||
|
android:fillColor="#00C25A"/>
|
||||||
|
</vector>
|
||||||
@@ -315,6 +315,88 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="0dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_apk_sign_configs"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:text="@string/text_signature_configuration"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||||
|
android:textColor="?android:textColorSecondary" />
|
||||||
|
|
||||||
|
<GridLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="2"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:rowCount="3">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_row="0"
|
||||||
|
android:layout_column="0"
|
||||||
|
android:text="@string/text_signature_scheme" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinner_signature_schemes"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_row="0"
|
||||||
|
android:layout_column="1"
|
||||||
|
android:layout_columnWeight="1"
|
||||||
|
android:paddingVertical="4dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_row="1"
|
||||||
|
android:layout_column="0"
|
||||||
|
android:text="@string/text_key_store" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinner_verified_key_stores"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_row="1"
|
||||||
|
android:layout_column="1"
|
||||||
|
android:layout_columnWeight="1"
|
||||||
|
android:paddingVertical="4dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_row="2"
|
||||||
|
android:layout_column="0"
|
||||||
|
android:text="@string/text_others" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manage_key_store"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_row="2"
|
||||||
|
android:layout_column="1"
|
||||||
|
android:layout_columnWeight="1"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingVertical="4dp"
|
||||||
|
android:text="@string/text_manage_key_store"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</GridLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="72dp"
|
android:layout_height="72dp"
|
||||||
|
|||||||
58
app/src/main/res/layout/activity_manage_key_store.xml
Normal file
58
app/src/main/res/layout/activity_manage_key_store.xml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/AppTheme.AppBarOverlay">
|
||||||
|
|
||||||
|
<org.autojs.autojs.theme.widget.ThemeColorToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/ToolBarStyle" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<org.autojs.autojs.theme.widget.ThemeColorSwipeRefreshLayout
|
||||||
|
android:id="@+id/swipe_refresh_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</org.autojs.autojs.theme.widget.ThemeColorSwipeRefreshLayout>
|
||||||
|
|
||||||
|
<org.autojs.autojs.theme.widget.ThemeColorFloatingActionButton
|
||||||
|
android:id="@+id/fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end|bottom"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:alpha="0.9"
|
||||||
|
android:src="@drawable/ic_add_white_48dp"
|
||||||
|
android:tint="@color/toolbar_text"
|
||||||
|
app:backgroundTint="?attr/colorPrimary"
|
||||||
|
app:layout_anchor="@id/viewpager"
|
||||||
|
app:layout_anchorGravity="bottom|end"
|
||||||
|
app:layout_behavior="org.autojs.autojs.ui.widget.ScrollAwareFABBehavior" />
|
||||||
|
</FrameLayout>
|
||||||
|
</LinearLayout>
|
||||||
317
app/src/main/res/layout/dialog_new_key_store.xml
Normal file
317
app/src/main/res/layout/dialog_new_key_store.xml
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@+id/type_radio_group"
|
||||||
|
android:text="@string/text_new_key_store"
|
||||||
|
android:textColor="@color/text_color_primary"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/type_radio_group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
tools:ignore="RelativeOverlap">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/type_jks"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="true"
|
||||||
|
android:text="@string/text_jks"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/type_bks"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:text="@string/text_bks"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/filename_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_file_name">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/filename"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/password_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_key_store_password"
|
||||||
|
app:endIconMode="password_toggle">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/password"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/alias_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_key_alias">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/alias"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/text_default_alias"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/alias_password_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_alias_password"
|
||||||
|
app:endIconMode="password_toggle">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/alias_password"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
android:id="@+id/more_options"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:gravity="center_vertical|start"
|
||||||
|
android:text="@string/text_more"
|
||||||
|
android:textAlignment="viewStart" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/more_options_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:text="@string/text_signature_algorithm"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatSpinner
|
||||||
|
android:id="@+id/signature_algorithms"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="-4dp"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textColor="@color/text_color_primary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/validity_years_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_validity_years">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/validity_years"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number"
|
||||||
|
android:text="@string/text_default_validity_years"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/first_and_last_name_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_first_and_last_name">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/first_and_last_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/organization_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_organization">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/organization"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/organizational_unit_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_organizational_unit">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/organizational_unit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/country_code_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_country_code">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/country_code"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/text_default_country_code"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/state_or_province_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_state_or_province">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/state_or_province"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/city_or_locality_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_city_or_locality">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/city_or_locality"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/street_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_street">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/street"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:gravity="end"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/cancel"
|
||||||
|
style="@style/Base.Widget.AppCompat.Button.Borderless.Colored"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:elegantTextHeight="true"
|
||||||
|
android:text="@string/text_cancel" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/confirm"
|
||||||
|
style="@style/Base.Widget.AppCompat.Button.Borderless.Colored"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:elegantTextHeight="true"
|
||||||
|
android:text="@string/text_ok" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
163
app/src/main/res/layout/dialog_verify_key_store.xml
Normal file
163
app/src/main/res/layout/dialog_verify_key_store.xml
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@+id/container_verify_state"
|
||||||
|
android:text="@string/text_verify_key_store"
|
||||||
|
android:textColor="@color/text_color_primary"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_verify_state"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:ignore="RelativeOverlap,UseCompoundDrawables">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/img_verify_state"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/text_verify_key_store"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:src="@drawable/ic_key_store_unverified"
|
||||||
|
tools:src="@drawable/ic_key_store_verified" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_verify_state"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/text_unverified"
|
||||||
|
android:textColor="@color/text_color_primary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:text="@string/text_file_path"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/file_path"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="/storage/emulated/0/脚本/.KeyStore/default_key_store.jks" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginVertical="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/password_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_key_store_password"
|
||||||
|
app:endIconMode="password_toggle">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/password"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/alias_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_key_alias">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/alias"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/text_default_alias"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/alias_password_text_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/text_alias_password"
|
||||||
|
app:endIconMode="password_toggle">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/alias_password"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:textColor="@color/text_color_primary" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:gravity="end"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/cancel"
|
||||||
|
style="@style/Base.Widget.AppCompat.Button.Borderless.Colored"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:elegantTextHeight="true"
|
||||||
|
android:text="@string/text_cancel" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/verify"
|
||||||
|
style="@style/Base.Widget.AppCompat.Button.Borderless.Colored"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:elegantTextHeight="true"
|
||||||
|
android:text="@string/text_verify" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
81
app/src/main/res/layout/item_key_store.xml
Normal file
81
app/src/main/res/layout/item_key_store.xml
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_marginTop="3dp"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_marginBottom="3dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
app:cardCornerRadius="4dp"
|
||||||
|
app:cardElevation="1dp"
|
||||||
|
app:cardUseCompatPadding="true">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:ignore="RelativeOverlap">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/filename"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_color_primary"
|
||||||
|
tools:text="filename" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/alias"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_color_primary"
|
||||||
|
tools:text="filename" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/verify"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:clickable="true"
|
||||||
|
android:contentDescription="@string/text_verify_key_store"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="6dp"
|
||||||
|
android:src="@drawable/ic_key_store_unverified"
|
||||||
|
tools:src="@drawable/ic_key_store_verified" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/delete"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:clickable="true"
|
||||||
|
android:contentDescription="@string/text_delete"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:src="@drawable/ic_key_store_delete"
|
||||||
|
app:tint="@color/md_red_500" />
|
||||||
|
</LinearLayout>
|
||||||
|
</RelativeLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
9
app/src/main/res/menu/menu_manage_key_store.xml
Normal file
9
app/src/main/res/menu/menu_manage_key_store.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_delete_all"
|
||||||
|
android:icon="@drawable/ic_delete_all"
|
||||||
|
android:title="@string/text_delete_all"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
</menu>
|
||||||
@@ -759,6 +759,45 @@
|
|||||||
<string name="text_supported_abis_short_32bit">ABIs (32 بت)</string>
|
<string name="text_supported_abis_short_32bit">ABIs (32 بت)</string>
|
||||||
<string name="text_supported_abis_short_64bit">ABIs (64 بت)</string>
|
<string name="text_supported_abis_short_64bit">ABIs (64 بت)</string>
|
||||||
<string name="text_supported_libraries_short">المكتبات</string>
|
<string name="text_supported_libraries_short">المكتبات</string>
|
||||||
|
<string name="text_signature_configuration">تكوين التوقيع</string>
|
||||||
|
<string name="text_key_store">مخزن المفاتيح</string>
|
||||||
|
<string name="text_default_key_store">مخزن المفاتيح الافتراضي</string>
|
||||||
|
<string name="text_manage_key_store">إدارة مخزن المفاتيح</string>
|
||||||
|
<string name="text_signature_scheme">خطة التوقيع</string>
|
||||||
|
<string name="text_new_key_store">إنشاء مفتاح التوقيع</string>
|
||||||
|
<string name="text_key_store_password">كلمة مرور مخزن المفاتيح</string>
|
||||||
|
<string name="text_key_alias">الاسم المستعار</string>
|
||||||
|
<string name="text_alias_password">كلمة مرور الاسم المستعار</string>
|
||||||
|
<string name="text_signature_algorithm">خوارزمية التوقيع</string>
|
||||||
|
<string name="text_validity_years">المدة (سنوات)</string>
|
||||||
|
<string name="text_first_and_last_name">الاسم</string>
|
||||||
|
<string name="text_organization">اسم المنظمة</string>
|
||||||
|
<string name="text_organizational_unit">وحدة التنظيم</string>
|
||||||
|
<string name="text_country_code">رمز الدولة (XX)</string>
|
||||||
|
<string name="text_state_or_province">الولاية أو المقاطعة</string>
|
||||||
|
<string name="text_city_or_locality">المدينة أو المنطقة</string>
|
||||||
|
<string name="text_street">الشارع</string>
|
||||||
|
<string name="text_filename_cannot_be_empty">اسم الملف لا يمكن أن يكون فارغًا</string>
|
||||||
|
<string name="text_filename_cannot_contain_invalid_character">اسم الملف لا يمكن أن يحتوي على أي من الأحرف التالية: \\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">اسم الملف طويل جدًا</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">يجب أن يحتوي كلمة المرور على %d على الأقل من الأحرف</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">الاسم المستعار لا يمكن أن يكون فارغًا</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">مدة الصلاحية لا يمكن أن تكون فارغة</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">مدة الصلاحية لا يمكن أن تكون صفرًا</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">رمز الدولة يجب أن يكون حرفين كبيرين</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">يجب ملء حقل واحد على الأقل من "الاسم، اسم المنظمة، وحدة التنظيم، رمز الدولة، الولاية أو المقاطعة، المدينة أو المنطقة، الشارع"</string>
|
||||||
|
<string name="text_successfully_created_key_store">تم إنشاء مفتاح التوقيع بنجاح</string>
|
||||||
|
<string name="text_failed_to_create_key_store">فشل في إنشاء مفتاح التوقيع</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">لم يتم التحقق من المخزن</string>
|
||||||
|
<string name="text_key_store_has_been_verified">تم التحقق من المخزن</string>
|
||||||
|
<string name="text_verify">التحقق</string>
|
||||||
|
<string name="text_delete_all">حذف الكل</string>
|
||||||
|
<string name="text_unverified">لم يتم التحقق</string>
|
||||||
|
<string name="text_verified">تم التحقق</string>
|
||||||
|
<string name="text_verify_key_store">التحقق من مخزن المفاتيح</string>
|
||||||
|
<string name="text_verify_success">تم التحقق بنجاح</string>
|
||||||
|
<string name="text_verify_failed">فشل التحقق</string>
|
||||||
|
<string name="text_file_path">مسار الملف</string>
|
||||||
<string name="text_switch_window">تبديل النافذة</string>
|
<string name="text_switch_window">تبديل النافذة</string>
|
||||||
<string name="text_symbols_settings">إعدادات الرموز</string>
|
<string name="text_symbols_settings">إعدادات الرموز</string>
|
||||||
<string name="text_tailor_made_developer">مطور مصممة خصيصا</string>
|
<string name="text_tailor_made_developer">مطور مصممة خصيصا</string>
|
||||||
|
|||||||
@@ -755,6 +755,45 @@
|
|||||||
<string name="text_supported_abis_short_32bit">ABIs (32-bit)</string>
|
<string name="text_supported_abis_short_32bit">ABIs (32-bit)</string>
|
||||||
<string name="text_supported_abis_short_64bit">ABIs (64-bit)</string>
|
<string name="text_supported_abis_short_64bit">ABIs (64-bit)</string>
|
||||||
<string name="text_supported_libraries_short">Libraries</string>
|
<string name="text_supported_libraries_short">Libraries</string>
|
||||||
|
<string name="text_signature_configuration">Signature Configuration</string>
|
||||||
|
<string name="text_key_store">Keystore</string>
|
||||||
|
<string name="text_default_key_store">Default Keystore</string>
|
||||||
|
<string name="text_manage_key_store">Manage Keystore</string>
|
||||||
|
<string name="text_signature_scheme">Signature Scheme</string>
|
||||||
|
<string name="text_new_key_store">New Key Store</string>
|
||||||
|
<string name="text_key_store_password">Key Store Password</string>
|
||||||
|
<string name="text_key_alias">Alias</string>
|
||||||
|
<string name="text_alias_password">Alias Password</string>
|
||||||
|
<string name="text_signature_algorithm">Signature Algorithm</string>
|
||||||
|
<string name="text_validity_years">Validity (Years)</string>
|
||||||
|
<string name="text_first_and_last_name">Full Name</string>
|
||||||
|
<string name="text_organization">Organization Name</string>
|
||||||
|
<string name="text_organizational_unit">Organizational Unit</string>
|
||||||
|
<string name="text_country_code">Country Code (XX)</string>
|
||||||
|
<string name="text_state_or_province">State or Province</string>
|
||||||
|
<string name="text_city_or_locality">City or Locality</string>
|
||||||
|
<string name="text_street">Street</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: \\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">Filename is too long</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">Password must be at least %d characters long</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">Alias cannot be empty</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">Validity years cannot be empty</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">Validity years cannot be zero</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">Country code must be two capital letters</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">At least one field from \"Full Name, Organization Name, Organizational Unit, Country Code, State or Province, City or Locality, Street\" must be filled</string>
|
||||||
|
<string name="text_successfully_created_key_store">Successfully created signature key</string>
|
||||||
|
<string name="text_failed_to_create_key_store">Failed to create signature key</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">Keystore has not been verified</string>
|
||||||
|
<string name="text_key_store_has_been_verified">Keystore has been verified</string>
|
||||||
|
<string name="text_verify">Verify</string>
|
||||||
|
<string name="text_delete_all">Delete All</string>
|
||||||
|
<string name="text_unverified">Unverified</string>
|
||||||
|
<string name="text_verified">Verified</string>
|
||||||
|
<string name="text_verify_key_store">Verify Keystore</string>
|
||||||
|
<string name="text_verify_success">Verification successful</string>
|
||||||
|
<string name="text_verify_failed">Verification failed</string>
|
||||||
|
<string name="text_file_path">File Path</string>
|
||||||
<string name="text_switch_window">Switch window</string>
|
<string name="text_switch_window">Switch window</string>
|
||||||
<string name="text_symbols_settings">Symbols settings</string>
|
<string name="text_symbols_settings">Symbols settings</string>
|
||||||
<string name="text_tailor_made_developer">Tailor-made developer</string>
|
<string name="text_tailor_made_developer">Tailor-made developer</string>
|
||||||
|
|||||||
@@ -758,6 +758,45 @@
|
|||||||
<string name="text_supported_abis_short_32bit">ABIs (32 bits)</string>
|
<string name="text_supported_abis_short_32bit">ABIs (32 bits)</string>
|
||||||
<string name="text_supported_abis_short_64bit">ABIs (64 bits)</string>
|
<string name="text_supported_abis_short_64bit">ABIs (64 bits)</string>
|
||||||
<string name="text_supported_libraries_short">Bibliotecas</string>
|
<string name="text_supported_libraries_short">Bibliotecas</string>
|
||||||
|
<string name="text_signature_configuration">Configuración de firma</string>
|
||||||
|
<string name="text_key_store">Almacenamiento de claves</string>
|
||||||
|
<string name="text_default_key_store">Almacenamiento de claves predeterminado</string>
|
||||||
|
<string name="text_manage_key_store">Gestionar almacenamiento de claves</string>
|
||||||
|
<string name="text_signature_scheme">Esquema de firma</string>
|
||||||
|
<string name="text_new_key_store">Generar clave de firma</string>
|
||||||
|
<string name="text_key_store_password">Contraseña del almacén de claves</string>
|
||||||
|
<string name="text_key_alias">Alias</string>
|
||||||
|
<string name="text_alias_password">Contraseña del alias</string>
|
||||||
|
<string name="text_signature_algorithm">Algoritmo de firma</string>
|
||||||
|
<string name="text_validity_years">Validez (años)</string>
|
||||||
|
<string name="text_first_and_last_name">Nombre completo</string>
|
||||||
|
<string name="text_organization">Nombre de la organización</string>
|
||||||
|
<string name="text_organizational_unit">Unidad organizacional</string>
|
||||||
|
<string name="text_country_code">Código de país (XX)</string>
|
||||||
|
<string name="text_state_or_province">Estado o provincia</string>
|
||||||
|
<string name="text_city_or_locality">Ciudad o localidad</string>
|
||||||
|
<string name="text_street">Calle</string>
|
||||||
|
<string name="text_filename_cannot_be_empty">El nombre de archivo no puede estar vacío</string>
|
||||||
|
<string name="text_filename_cannot_contain_invalid_character">El nombre del archivo no puede contener ninguno de los siguientes caracteres: \\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">El nombre del archivo es demasiado largo</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">La contraseña debe tener al menos %d caracteres</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">El alias no puede estar vacío</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">El número de años de validez no puede estar vacío</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">El número de años de validez no puede ser cero</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">El código del país debe ser de dos letras mayúsculas</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">Al menos uno de los campos “Nombre completo, Nombre de la organización, Unidad organizacional, Código de país, Estado o provincia, Ciudad o localidad, Calle” debe estar lleno</string>
|
||||||
|
<string name="text_successfully_created_key_store">Clave de firma creada con éxito</string>
|
||||||
|
<string name="text_failed_to_create_key_store">Error al crear la clave de firma</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">El almacén de claves no ha sido verificado</string>
|
||||||
|
<string name="text_key_store_has_been_verified">El almacén de claves ha sido verificado</string>
|
||||||
|
<string name="text_verify">Verificar</string>
|
||||||
|
<string name="text_delete_all">Eliminar todo</string>
|
||||||
|
<string name="text_unverified">No verificado</string>
|
||||||
|
<string name="text_verified">Verificado</string>
|
||||||
|
<string name="text_verify_key_store">Verificar almacén de claves</string>
|
||||||
|
<string name="text_verify_success">Verificación exitosa</string>
|
||||||
|
<string name="text_verify_failed">Error en la verificación</string>
|
||||||
|
<string name="text_file_path">Ruta del archivo</string>
|
||||||
<string name="text_switch_window">Cambiar ventana</string>
|
<string name="text_switch_window">Cambiar ventana</string>
|
||||||
<string name="text_symbols_settings">Config de símbolos</string>
|
<string name="text_symbols_settings">Config de símbolos</string>
|
||||||
<string name="text_tailor_made_developer">Desarrollador a medida</string>
|
<string name="text_tailor_made_developer">Desarrollador a medida</string>
|
||||||
|
|||||||
@@ -758,6 +758,45 @@
|
|||||||
<string name="text_supported_abis_short_32bit">ABIs (32-bit)</string>
|
<string name="text_supported_abis_short_32bit">ABIs (32-bit)</string>
|
||||||
<string name="text_supported_abis_short_64bit">ABIs (64-bit)</string>
|
<string name="text_supported_abis_short_64bit">ABIs (64-bit)</string>
|
||||||
<string name="text_supported_libraries_short">Bibliothèques</string>
|
<string name="text_supported_libraries_short">Bibliothèques</string>
|
||||||
|
<string name="text_signature_configuration">Configuration de la signature</string>
|
||||||
|
<string name="text_key_store">Magasin de clés</string>
|
||||||
|
<string name="text_default_key_store">Magasin de clés par défaut</string>
|
||||||
|
<string name="text_manage_key_store">Gérer le magasin de clés</string>
|
||||||
|
<string name="text_signature_scheme">Schéma de signature</string>
|
||||||
|
<string name="text_new_key_store">Générer une clé de signature</string>
|
||||||
|
<string name="text_key_store_password">Mot de passe du magasin de clés</string>
|
||||||
|
<string name="text_key_alias">Alias</string>
|
||||||
|
<string name="text_alias_password">Mot de passe de l\'alias</string>
|
||||||
|
<string name="text_signature_algorithm">Algorithme de signature</string>
|
||||||
|
<string name="text_validity_years">Validité (années)</string>
|
||||||
|
<string name="text_first_and_last_name">Nom complet</string>
|
||||||
|
<string name="text_organization">Nom de l\'organisation</string>
|
||||||
|
<string name="text_organizational_unit">Unité organisationnelle</string>
|
||||||
|
<string name="text_country_code">Code du pays (XX)</string>
|
||||||
|
<string name="text_state_or_province">État ou province</string>
|
||||||
|
<string name="text_city_or_locality">Ville ou localité</string>
|
||||||
|
<string name="text_street">Rue</string>
|
||||||
|
<string name="text_filename_cannot_be_empty">Le nom de fichier ne peut pas être vide</string>
|
||||||
|
<string name="text_filename_cannot_contain_invalid_character">Le nom du fichier ne peut pas contenir les caractères suivants : \\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">Le nom du fichier est trop long</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">Le mot de passe doit contenir au moins %d caractères</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">L\'alias ne peut pas être vide</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">Les années de validité ne peuvent pas être vides</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">Les années de validité ne peuvent pas être égales à zéro</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">Le code du pays doit être composé de deux lettres majuscules</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">Au moins un champ parmi « Nom complet, Nom de l\'organisation, Unité organisationnelle, Code du pays, État ou province, Ville ou localité, Rue » doit être renseigné</string>
|
||||||
|
<string name="text_successfully_created_key_store">Clé de signature créée avec succès</string>
|
||||||
|
<string name="text_failed_to_create_key_store">Échec de la création de la clé de signature</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">Le magasin de clés n\'a pas été vérifié</string>
|
||||||
|
<string name="text_key_store_has_been_verified">Le magasin de clés a été vérifié</string>
|
||||||
|
<string name="text_verify">Vérifier</string>
|
||||||
|
<string name="text_delete_all">Tout supprimer</string>
|
||||||
|
<string name="text_unverified">Non vérifié</string>
|
||||||
|
<string name="text_verified">Vérifié</string>
|
||||||
|
<string name="text_verify_key_store">Vérifier le magasin de clés</string>
|
||||||
|
<string name="text_verify_success">Vérification réussie</string>
|
||||||
|
<string name="text_verify_failed">Échec de la vérification</string>
|
||||||
|
<string name="text_file_path">Chemin du fichier</string>
|
||||||
<string name="text_switch_window">Changer de fenêtre</string>
|
<string name="text_switch_window">Changer de fenêtre</string>
|
||||||
<string name="text_symbols_settings">Param des symboles</string>
|
<string name="text_symbols_settings">Param des symboles</string>
|
||||||
<string name="text_tailor_made_developer">Développeur sur mesure</string>
|
<string name="text_tailor_made_developer">Développeur sur mesure</string>
|
||||||
|
|||||||
@@ -758,6 +758,45 @@
|
|||||||
<string name="text_supported_abis_short_32bit">ABI (32ビット)</string>
|
<string name="text_supported_abis_short_32bit">ABI (32ビット)</string>
|
||||||
<string name="text_supported_abis_short_64bit">ABI (64ビット)</string>
|
<string name="text_supported_abis_short_64bit">ABI (64ビット)</string>
|
||||||
<string name="text_supported_libraries_short">ライブラリー</string>
|
<string name="text_supported_libraries_short">ライブラリー</string>
|
||||||
|
<string name="text_signature_configuration">署名設定</string>
|
||||||
|
<string name="text_key_store">キーストア</string>
|
||||||
|
<string name="text_default_key_store">デフォルトのキーストア</string>
|
||||||
|
<string name="text_manage_key_store">キーストアを管理</string>
|
||||||
|
<string name="text_signature_scheme">署名スキーム</string>
|
||||||
|
<string name="text_new_key_store">署名キーを生成</string>
|
||||||
|
<string name="text_key_store_password">キーストアのパスワード</string>
|
||||||
|
<string name="text_key_alias">エイリアス</string>
|
||||||
|
<string name="text_alias_password">エイリアスのパスワード</string>
|
||||||
|
<string name="text_signature_algorithm">署名アルゴリズム</string>
|
||||||
|
<string name="text_validity_years">有効期限(年)</string>
|
||||||
|
<string name="text_first_and_last_name">氏名</string>
|
||||||
|
<string name="text_organization">組織名</string>
|
||||||
|
<string name="text_organizational_unit">組織単位</string>
|
||||||
|
<string name="text_country_code">国コード(XX)</string>
|
||||||
|
<string name="text_state_or_province">州または省</string>
|
||||||
|
<string name="text_city_or_locality">市区町村</string>
|
||||||
|
<string name="text_street">街</string>
|
||||||
|
<string name="text_filename_cannot_be_empty">ファイル名は空にできません</string>
|
||||||
|
<string name="text_filename_cannot_contain_invalid_character">ファイル名に以下の文字を含めることはできません:\\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">ファイル名が長すぎます</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">パスワードは少なくとも%d文字以上である必要があります</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">エイリアスは空にできません</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">有効期限の年数は空にできません</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">有効期限の年数は0にできません</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">国コードは2文字の大文字でなければなりません</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">「氏名、組織名、組織単位、国コード、州または省、市区町村、街」のいずれかの項目を入力する必要があります</string>
|
||||||
|
<string name="text_successfully_created_key_store">署名キーが正常に作成されました</string>
|
||||||
|
<string name="text_failed_to_create_key_store">署名キーの作成に失敗しました</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">キーストアは確認されていません</string>
|
||||||
|
<string name="text_key_store_has_been_verified">キーストアは確認されました</string>
|
||||||
|
<string name="text_verify">確認</string>
|
||||||
|
<string name="text_delete_all">すべて削除</string>
|
||||||
|
<string name="text_unverified">未確認</string>
|
||||||
|
<string name="text_verified">確認済み</string>
|
||||||
|
<string name="text_verify_key_store">キーストアを確認</string>
|
||||||
|
<string name="text_verify_success">確認成功</string>
|
||||||
|
<string name="text_verify_failed">確認失敗</string>
|
||||||
|
<string name="text_file_path">ファイルパス</string>
|
||||||
<string name="text_switch_window">ウィンドウの切り替え</string>
|
<string name="text_switch_window">ウィンドウの切り替え</string>
|
||||||
<string name="text_symbols_settings">シンボル設定</string>
|
<string name="text_symbols_settings">シンボル設定</string>
|
||||||
<string name="text_tailor_made_developer">テーラーメイド開発者</string>
|
<string name="text_tailor_made_developer">テーラーメイド開発者</string>
|
||||||
|
|||||||
@@ -759,6 +759,45 @@
|
|||||||
<string name="text_supported_abis_short_32bit">ABI (32 비트)</string>
|
<string name="text_supported_abis_short_32bit">ABI (32 비트)</string>
|
||||||
<string name="text_supported_abis_short_64bit">ABI (64 비트)</string>
|
<string name="text_supported_abis_short_64bit">ABI (64 비트)</string>
|
||||||
<string name="text_supported_libraries_short">라이브러리</string>
|
<string name="text_supported_libraries_short">라이브러리</string>
|
||||||
|
<string name="text_signature_configuration">서명 설정</string>
|
||||||
|
<string name="text_key_store">키 저장소</string>
|
||||||
|
<string name="text_default_key_store">기본 키 저장소</string>
|
||||||
|
<string name="text_manage_key_store">키 저장소 관리</string>
|
||||||
|
<string name="text_signature_scheme">서명 스킴</string>
|
||||||
|
<string name="text_new_key_store">서명 키 생성</string>
|
||||||
|
<string name="text_key_store_password">키 저장소 비밀번호</string>
|
||||||
|
<string name="text_key_alias">별칭</string>
|
||||||
|
<string name="text_alias_password">별칭 비밀번호</string>
|
||||||
|
<string name="text_signature_algorithm">서명 알고리즘</string>
|
||||||
|
<string name="text_validity_years">유효 기간 (년)</string>
|
||||||
|
<string name="text_first_and_last_name">성명</string>
|
||||||
|
<string name="text_organization">조직명</string>
|
||||||
|
<string name="text_organizational_unit">조직 단위</string>
|
||||||
|
<string name="text_country_code">국가 코드 (XX)</string>
|
||||||
|
<string name="text_state_or_province">주 또는 도</string>
|
||||||
|
<string name="text_city_or_locality">시 또는 지역</string>
|
||||||
|
<string name="text_street">거리</string>
|
||||||
|
<string name="text_filename_cannot_be_empty">파일 이름은 비워둘 수 없습니다</string>
|
||||||
|
<string name="text_filename_cannot_contain_invalid_character">파일 이름에 다음 문자를 포함할 수 없습니다: \\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">파일 이름이 너무 깁니다</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">비밀번호는 최소 %d자 이상이어야 합니다</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">별칭은 비워둘 수 없습니다</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">유효 기간을 입력해야 합니다</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">유효 기간은 0일 수 없습니다</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">국가 코드는 두 개의 대문자로 입력해야 합니다</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">"성명, 조직명, 조직 단위, 국가 코드, 주 또는 도, 시 또는 지역, 거리" 중 하나 이상을 입력해야 합니다</string>
|
||||||
|
<string name="text_successfully_created_key_store">서명 키가 성공적으로 생성되었습니다</string>
|
||||||
|
<string name="text_failed_to_create_key_store">서명 키 생성에 실패했습니다</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">키 저장소가 확인되지 않았습니다</string>
|
||||||
|
<string name="text_key_store_has_been_verified">키 저장소가 확인되었습니다</string>
|
||||||
|
<string name="text_verify">확인</string>
|
||||||
|
<string name="text_delete_all">모두 삭제</string>
|
||||||
|
<string name="text_unverified">미확인</string>
|
||||||
|
<string name="text_verified">확인됨</string>
|
||||||
|
<string name="text_verify_key_store">키 저장소 확인</string>
|
||||||
|
<string name="text_verify_success">확인 성공</string>
|
||||||
|
<string name="text_verify_failed">확인 실패</string>
|
||||||
|
<string name="text_file_path">파일 경로</string>
|
||||||
<string name="text_switch_window">창 전환</string>
|
<string name="text_switch_window">창 전환</string>
|
||||||
<string name="text_symbols_settings">기호 설정</string>
|
<string name="text_symbols_settings">기호 설정</string>
|
||||||
<string name="text_tailor_made_developer">맞춤형 개발자</string>
|
<string name="text_tailor_made_developer">맞춤형 개발자</string>
|
||||||
|
|||||||
@@ -758,6 +758,45 @@
|
|||||||
<string name="text_supported_abis_short_32bit">ABIs (32-bit)</string>
|
<string name="text_supported_abis_short_32bit">ABIs (32-bit)</string>
|
||||||
<string name="text_supported_abis_short_64bit">ABIs (64-bit)</string>
|
<string name="text_supported_abis_short_64bit">ABIs (64-bit)</string>
|
||||||
<string name="text_supported_libraries_short">Библиотеки</string>
|
<string name="text_supported_libraries_short">Библиотеки</string>
|
||||||
|
<string name="text_signature_configuration">Конфигурация подписи</string>
|
||||||
|
<string name="text_key_store">Хранилище ключей</string>
|
||||||
|
<string name="text_default_key_store">По умолчанию хранилище ключей</string>
|
||||||
|
<string name="text_manage_key_store">Управление хранилищем ключей</string>
|
||||||
|
<string name="text_signature_scheme">Схема подписи</string>
|
||||||
|
<string name="text_new_key_store">Создать ключ подписи</string>
|
||||||
|
<string name="text_key_store_password">Пароль хранилища ключей</string>
|
||||||
|
<string name="text_key_alias">Псевдоним</string>
|
||||||
|
<string name="text_alias_password">Пароль псевдонима</string>
|
||||||
|
<string name="text_signature_algorithm">Алгоритм подписи</string>
|
||||||
|
<string name="text_validity_years">Срок действия (годы)</string>
|
||||||
|
<string name="text_first_and_last_name">Полное имя</string>
|
||||||
|
<string name="text_organization">Название организации</string>
|
||||||
|
<string name="text_organizational_unit">Организационная единица</string>
|
||||||
|
<string name="text_country_code">Код страны (XX)</string>
|
||||||
|
<string name="text_state_or_province">Штат или провинция</string>
|
||||||
|
<string name="text_city_or_locality">Город или населенный пункт</string>
|
||||||
|
<string name="text_street">Улица</string>
|
||||||
|
<string name="text_filename_cannot_be_empty">Имя файла не может быть пустым</string>
|
||||||
|
<string name="text_filename_cannot_contain_invalid_character">Имя файла не может содержать следующие символы: \\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">Имя файла слишком длинное</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">Пароль должен содержать не менее %d символов</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">Псевдоним не может быть пустым</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">Срок действия не может быть пустым</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">Срок действия не может быть равен нулю</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">Код страны должен состоять из двух заглавных букв</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">Необходимо заполнить хотя бы одно поле из: «Полное имя, Название организации, Организационная единица, Код страны, Штат или провинция, Город или населенный пункт, Улица»</string>
|
||||||
|
<string name="text_successfully_created_key_store">Ключ подписи успешно создан</string>
|
||||||
|
<string name="text_failed_to_create_key_store">Не удалось создать ключ подписи</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">Хранилище ключей не подтверждено</string>
|
||||||
|
<string name="text_key_store_has_been_verified">Хранилище ключей подтверждено</string>
|
||||||
|
<string name="text_verify">Проверить</string>
|
||||||
|
<string name="text_delete_all">Удалить все</string>
|
||||||
|
<string name="text_unverified">Не подтверждено</string>
|
||||||
|
<string name="text_verified">Подтверждено</string>
|
||||||
|
<string name="text_verify_key_store">Проверить хранилище ключей</string>
|
||||||
|
<string name="text_verify_success">Проверка успешна</string>
|
||||||
|
<string name="text_verify_failed">Ошибка проверки</string>
|
||||||
|
<string name="text_file_path">Путь к файлу</string>
|
||||||
<string name="text_switch_window">Переключить окно</string>
|
<string name="text_switch_window">Переключить окно</string>
|
||||||
<string name="text_symbols_settings">Настройки символов</string>
|
<string name="text_symbols_settings">Настройки символов</string>
|
||||||
<string name="text_tailor_made_developer">Индивидуальный разработчик</string>
|
<string name="text_tailor_made_developer">Индивидуальный разработчик</string>
|
||||||
|
|||||||
@@ -757,6 +757,45 @@
|
|||||||
<string name="text_supported_abis_short_32bit">支持 ABI (32 位)</string>
|
<string name="text_supported_abis_short_32bit">支持 ABI (32 位)</string>
|
||||||
<string name="text_supported_abis_short_64bit">支持 ABI (64 位)</string>
|
<string name="text_supported_abis_short_64bit">支持 ABI (64 位)</string>
|
||||||
<string name="text_supported_libraries_short">支援庫</string>
|
<string name="text_supported_libraries_short">支援庫</string>
|
||||||
|
<string name="text_signature_configuration">簽名配置</string>
|
||||||
|
<string name="text_key_store">密鑰庫</string>
|
||||||
|
<string name="text_default_key_store">預設密鑰庫</string>
|
||||||
|
<string name="text_manage_key_store">管理密鑰庫</string>
|
||||||
|
<string name="text_signature_scheme">簽名方案</string>
|
||||||
|
<string name="text_new_key_store">生成簽名密鑰</string>
|
||||||
|
<string name="text_key_store_password">密鑰庫密碼</string>
|
||||||
|
<string name="text_key_alias">別名</string>
|
||||||
|
<string name="text_alias_password">別名密碼</string>
|
||||||
|
<string name="text_signature_algorithm">簽名算法</string>
|
||||||
|
<string name="text_validity_years">有效期 (年)</string>
|
||||||
|
<string name="text_first_and_last_name">姓名</string>
|
||||||
|
<string name="text_organization">組織名稱</string>
|
||||||
|
<string name="text_organizational_unit">組織單位</string>
|
||||||
|
<string name="text_country_code">國家代碼 (XX)</string>
|
||||||
|
<string name="text_state_or_province">州或省份</string>
|
||||||
|
<string name="text_city_or_locality">城市或區域</string>
|
||||||
|
<string name="text_street">街道</string>
|
||||||
|
<string name="text_filename_cannot_be_empty">文件名不能為空</string>
|
||||||
|
<string name="text_filename_cannot_contain_invalid_character">文件名不能包含以下字符: \\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">文件名太長</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">密碼至少需要%d位字符</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">別名不能為空</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">有效年份不能為空</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">有效年份不能為0</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">國家代碼必須為兩個大寫字母</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">“姓名、組織名稱、組織單位、國家代碼、州或省份、城市或區域、街道”至少填寫一個</string>
|
||||||
|
<string name="text_successfully_created_key_store">成功生成簽名密鑰</string>
|
||||||
|
<string name="text_failed_to_create_key_store">生成簽名密鑰失敗</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">密鑰尚未驗證</string>
|
||||||
|
<string name="text_key_store_has_been_verified">密鑰已驗證</string>
|
||||||
|
<string name="text_verify">驗證</string>
|
||||||
|
<string name="text_delete_all">刪除全部</string>
|
||||||
|
<string name="text_unverified">未驗證</string>
|
||||||
|
<string name="text_verified">已驗證</string>
|
||||||
|
<string name="text_verify_key_store">驗證簽名密鑰</string>
|
||||||
|
<string name="text_verify_success">驗證成功</string>
|
||||||
|
<string name="text_verify_failed">驗證失敗</string>
|
||||||
|
<string name="text_file_path">文件路徑</string>
|
||||||
<string name="text_switch_window">切換窗口</string>
|
<string name="text_switch_window">切換窗口</string>
|
||||||
<string name="text_symbols_settings">符號設置</string>
|
<string name="text_symbols_settings">符號設置</string>
|
||||||
<string name="text_tailor_made_developer">二次開發者</string>
|
<string name="text_tailor_made_developer">二次開發者</string>
|
||||||
|
|||||||
@@ -757,6 +757,45 @@
|
|||||||
<string name="text_supported_abis_short_32bit">支援 ABI (32 位)</string>
|
<string name="text_supported_abis_short_32bit">支援 ABI (32 位)</string>
|
||||||
<string name="text_supported_abis_short_64bit">支援 ABI (64 位)</string>
|
<string name="text_supported_abis_short_64bit">支援 ABI (64 位)</string>
|
||||||
<string name="text_supported_libraries_short">支援庫</string>
|
<string name="text_supported_libraries_short">支援庫</string>
|
||||||
|
<string name="text_signature_configuration">簽名設定</string>
|
||||||
|
<string name="text_key_store">密鑰庫</string>
|
||||||
|
<string name="text_default_key_store">預設密鑰庫</string>
|
||||||
|
<string name="text_manage_key_store">管理密鑰庫</string>
|
||||||
|
<string name="text_signature_scheme">簽名方案</string>
|
||||||
|
<string name="text_new_key_store">生成簽名密鑰</string>
|
||||||
|
<string name="text_key_store_password">密鑰庫密碼</string>
|
||||||
|
<string name="text_key_alias">別名</string>
|
||||||
|
<string name="text_alias_password">別名密碼</string>
|
||||||
|
<string name="text_signature_algorithm">簽名演算法</string>
|
||||||
|
<string name="text_validity_years">有效期 (年)</string>
|
||||||
|
<string name="text_first_and_last_name">姓名</string>
|
||||||
|
<string name="text_organization">組織名稱</string>
|
||||||
|
<string name="text_organizational_unit">組織單位</string>
|
||||||
|
<string name="text_country_code">國家代碼 (XX)</string>
|
||||||
|
<string name="text_state_or_province">州或省份</string>
|
||||||
|
<string name="text_city_or_locality">城市或區域</string>
|
||||||
|
<string name="text_street">街道</string>
|
||||||
|
<string name="text_filename_cannot_be_empty">檔案名稱不能為空</string>
|
||||||
|
<string name="text_filename_cannot_contain_invalid_character">檔案名稱不能包含以下字符: \\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">檔案名稱太長</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">密碼至少需要%d個字元</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">別名不能為空</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">有效年份不能為空</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">有效年份不能為0</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">國家代碼必須是兩個大寫字母</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">“姓名、組織名稱、組織單位、國家代碼、州或省份、城市或區域、街道”至少填寫一個</string>
|
||||||
|
<string name="text_successfully_created_key_store">成功生成簽名密鑰</string>
|
||||||
|
<string name="text_failed_to_create_key_store">生成簽名密鑰失敗</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">密鑰尚未驗證</string>
|
||||||
|
<string name="text_key_store_has_been_verified">密鑰已驗證</string>
|
||||||
|
<string name="text_verify">驗證</string>
|
||||||
|
<string name="text_delete_all">刪除全部</string>
|
||||||
|
<string name="text_unverified">未驗證</string>
|
||||||
|
<string name="text_verified">已驗證</string>
|
||||||
|
<string name="text_verify_key_store">驗證簽名密鑰</string>
|
||||||
|
<string name="text_verify_success">驗證成功</string>
|
||||||
|
<string name="text_verify_failed">驗證失敗</string>
|
||||||
|
<string name="text_file_path">檔案路徑</string>
|
||||||
<string name="text_switch_window">切換視窗</string>
|
<string name="text_switch_window">切換視窗</string>
|
||||||
<string name="text_symbols_settings">符號設定</string>
|
<string name="text_symbols_settings">符號設定</string>
|
||||||
<string name="text_tailor_made_developer">二次開發者</string>
|
<string name="text_tailor_made_developer">二次開發者</string>
|
||||||
|
|||||||
@@ -753,6 +753,45 @@
|
|||||||
<string name="text_supported_abis_short_32bit">支持 ABI (32 位)</string>
|
<string name="text_supported_abis_short_32bit">支持 ABI (32 位)</string>
|
||||||
<string name="text_supported_abis_short_64bit">支持 ABI (64 位)</string>
|
<string name="text_supported_abis_short_64bit">支持 ABI (64 位)</string>
|
||||||
<string name="text_supported_libraries_short">支持库</string>
|
<string name="text_supported_libraries_short">支持库</string>
|
||||||
|
<string name="text_signature_configuration">签名配置</string>
|
||||||
|
<string name="text_key_store">密钥库</string>
|
||||||
|
<string name="text_default_key_store">默认密钥库</string>
|
||||||
|
<string name="text_manage_key_store">管理密钥库</string>
|
||||||
|
<string name="text_signature_scheme">签名方案</string>
|
||||||
|
<string name="text_new_key_store">生成签名密钥</string>
|
||||||
|
<string name="text_key_store_password">密钥库密码</string>
|
||||||
|
<string name="text_key_alias">别名</string>
|
||||||
|
<string name="text_alias_password">别名密码</string>
|
||||||
|
<string name="text_signature_algorithm">签名算法</string>
|
||||||
|
<string name="text_validity_years">有效期 (年)</string>
|
||||||
|
<string name="text_first_and_last_name">姓名</string>
|
||||||
|
<string name="text_organization">组织名称</string>
|
||||||
|
<string name="text_organizational_unit">组织单位</string>
|
||||||
|
<string name="text_country_code">国家代码 (XX)</string>
|
||||||
|
<string name="text_state_or_province">州或省份</string>
|
||||||
|
<string name="text_city_or_locality">城市或区域</string>
|
||||||
|
<string name="text_street">街道</string>
|
||||||
|
<string name="text_filename_cannot_be_empty">文件名不能为空</string>
|
||||||
|
<string name="text_filename_cannot_contain_invalid_character">文件名不能包含下列任何字符: \\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">文件名太长</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">密码至少需要%d位字符</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">别名不能为空</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">有效年份不能为空</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">有效年份不能为0</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">国家代码必须为两个大写字母</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">“姓名、组织名称、组织单位、国家代码、州或省份、城市或区域、街道”至少填写一个</string>
|
||||||
|
<string name="text_successfully_created_key_store">成功生成签名密钥</string>
|
||||||
|
<string name="text_failed_to_create_key_store">生成签名密钥失败</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">密钥尚未验证</string>
|
||||||
|
<string name="text_key_store_has_been_verified">密钥已验证</string>
|
||||||
|
<string name="text_verify">验证</string>
|
||||||
|
<string name="text_delete_all">删除全部</string>
|
||||||
|
<string name="text_unverified">未验证</string>
|
||||||
|
<string name="text_verified">已验证</string>
|
||||||
|
<string name="text_verify_key_store">验证签名密钥</string>
|
||||||
|
<string name="text_verify_success">验证成功</string>
|
||||||
|
<string name="text_verify_failed">验证失败</string>
|
||||||
|
<string name="text_file_path">文件路径</string>
|
||||||
<string name="text_switch_window">切换窗口</string>
|
<string name="text_switch_window">切换窗口</string>
|
||||||
<string name="text_symbols_settings">符号设置</string>
|
<string name="text_symbols_settings">符号设置</string>
|
||||||
<string name="text_tailor_made_developer">二次开发者</string>
|
<string name="text_tailor_made_developer">二次开发者</string>
|
||||||
|
|||||||
@@ -966,6 +966,51 @@
|
|||||||
<string name="text_supported_abis_short_32bit">ABIs (32-bit)</string>
|
<string name="text_supported_abis_short_32bit">ABIs (32-bit)</string>
|
||||||
<string name="text_supported_abis_short_64bit">ABIs (64-bit)</string>
|
<string name="text_supported_abis_short_64bit">ABIs (64-bit)</string>
|
||||||
<string name="text_supported_libraries_short">Libraries</string>
|
<string name="text_supported_libraries_short">Libraries</string>
|
||||||
|
<string name="text_jks" translatable="false">JKS</string>
|
||||||
|
<string name="text_bks" translatable="false">BKS</string>
|
||||||
|
<string name="text_default_alias" translatable="false">key0</string>
|
||||||
|
<string name="text_default_validity_years" translatable="false">25</string>
|
||||||
|
<string name="text_default_country_code" translatable="false">CN</string>
|
||||||
|
<string name="text_str_colon_space_str_formatter" translatable="false">%s: %s</string>
|
||||||
|
<string name="text_signature_configuration">Signature Configuration</string>
|
||||||
|
<string name="text_key_store">Keystore</string>
|
||||||
|
<string name="text_default_key_store">Default Keystore</string>
|
||||||
|
<string name="text_manage_key_store">Manage Keystore</string>
|
||||||
|
<string name="text_signature_scheme">Signature Scheme</string>
|
||||||
|
<string name="text_new_key_store">New Key Store</string>
|
||||||
|
<string name="text_key_store_password">Key Store Password</string>
|
||||||
|
<string name="text_key_alias">Alias</string>
|
||||||
|
<string name="text_alias_password">Alias Password</string>
|
||||||
|
<string name="text_signature_algorithm">Signature Algorithm</string>
|
||||||
|
<string name="text_validity_years">Validity (Years)</string>
|
||||||
|
<string name="text_first_and_last_name">Full Name</string>
|
||||||
|
<string name="text_organization">Organization Name</string>
|
||||||
|
<string name="text_organizational_unit">Organizational Unit</string>
|
||||||
|
<string name="text_country_code">Country Code (XX)</string>
|
||||||
|
<string name="text_state_or_province">State or Province</string>
|
||||||
|
<string name="text_city_or_locality">City or Locality</string>
|
||||||
|
<string name="text_street">Street</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: \\ / : * ? " < > |</string>
|
||||||
|
<string name="text_filename_is_too_long">Filename is too long</string>
|
||||||
|
<string name="text_password_requires_at_least_n_characters">Password must be at least %d characters long</string>
|
||||||
|
<string name="text_alias_cannot_be_empty">Alias cannot be empty</string>
|
||||||
|
<string name="text_validity_years_cannot_be_empty">Validity years cannot be empty</string>
|
||||||
|
<string name="text_validity_years_cannot_be_zero">Validity years cannot be zero</string>
|
||||||
|
<string name="text_country_code_must_be_two_capital_letters">Country code must be two capital letters</string>
|
||||||
|
<string name="text_at_least_one_certificate_issuer_field_is_not_empty">At least one field from \"Full Name, Organization Name, Organizational Unit, Country Code, State or Province, City or Locality, Street\" must be filled</string>
|
||||||
|
<string name="text_successfully_created_key_store">Successfully created signature key</string>
|
||||||
|
<string name="text_failed_to_create_key_store">Failed to create signature key</string>
|
||||||
|
<string name="text_key_store_has_not_been_verified">Keystore has not been verified</string>
|
||||||
|
<string name="text_key_store_has_been_verified">Keystore has been verified</string>
|
||||||
|
<string name="text_verify">Verify</string>
|
||||||
|
<string name="text_delete_all">Delete All</string>
|
||||||
|
<string name="text_unverified">Unverified</string>
|
||||||
|
<string name="text_verified">Verified</string>
|
||||||
|
<string name="text_verify_key_store">Verify Keystore</string>
|
||||||
|
<string name="text_verify_success">Verification successful</string>
|
||||||
|
<string name="text_verify_failed">Verification failed</string>
|
||||||
|
<string name="text_file_path">File Path</string>
|
||||||
<string name="text_switch_window">Switch window</string>
|
<string name="text_switch_window">Switch window</string>
|
||||||
<string name="text_symbols_settings">Symbols settings</string>
|
<string name="text_symbols_settings">Symbols settings</string>
|
||||||
<string name="text_tailor_made_developer">Tailor-made developer</string>
|
<string name="text_tailor_made_developer">Tailor-made developer</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user