6.7.0 - Alpha20 - 修复部分设备无法正常启用插件的问题
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package org.autojs.autojs.core.plugin.center
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -14,6 +15,9 @@ import java.io.File
|
||||
* Local installed plugin discovery (based on existing PaddleOcrPluginHost.discover).
|
||||
*
|
||||
* zh-CN: 本地已安装插件发现 (基于现有的 PaddleOcrPluginHost.discover).
|
||||
*
|
||||
* Modified by JetBrains AI Assistant (GPT-5.2-Codex (xhigh)) as of Feb 13, 2026.
|
||||
* Modified by SuperMonster003 as of Feb 13, 2026.
|
||||
*/
|
||||
class InstalledPluginRepository {
|
||||
|
||||
@@ -29,6 +33,8 @@ class InstalledPluginRepository {
|
||||
val lastUpdateTime: Long?,
|
||||
val icon: Drawable?,
|
||||
val pluginInfo: PluginInfo?,
|
||||
val bindError: Throwable?,
|
||||
val isStopped: Boolean,
|
||||
)
|
||||
|
||||
suspend fun discoverInstalled(context: Context): List<InstalledPlugin> = withContext(Dispatchers.IO) {
|
||||
@@ -41,6 +47,7 @@ class InstalledPluginRepository {
|
||||
val appInfo = runCatching { pm.getApplicationInfo(packageName, 0) }.getOrNull()
|
||||
val appLabel = appInfo?.loadLabel(pm)?.toString()
|
||||
val icon = appInfo?.loadIcon(pm)
|
||||
val isStopped = appInfo != null && (appInfo.flags and ApplicationInfo.FLAG_STOPPED) != 0
|
||||
|
||||
val pkgInfo = runCatching { pm.getPackageInfo(packageName, 0) }.getOrNull()
|
||||
val versionName = pkgInfo?.versionName ?: d.pluginInfo?.versionName ?: context.getString(R.string.text_unknown)
|
||||
@@ -70,6 +77,8 @@ class InstalledPluginRepository {
|
||||
lastUpdateTime = lastUpdateTime,
|
||||
icon = icon,
|
||||
pluginInfo = d.pluginInfo,
|
||||
bindError = d.error,
|
||||
isStopped = isStopped,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,15 @@ import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.DividerItemDecoration.VERTICAL
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.autojs.autojs.core.plugin.ocr.PaddleOcrPluginHost
|
||||
import org.autojs.autojs.util.ClipboardUtils
|
||||
import org.autojs.autojs.util.IntentUtils.startSafely
|
||||
import org.autojs.autojs.util.ViewUtils
|
||||
import org.autojs.autojs.util.ViewUtils.excludePaddingClippableViewFromBottomNavigationBar
|
||||
import org.autojs.autojs6.R
|
||||
import org.autojs.autojs6.databinding.FragmentPluginCenterBinding
|
||||
@@ -62,8 +67,75 @@ class PluginCenterFragment : Fragment(R.layout.fragment_plugin_center) {
|
||||
|
||||
adapter = PluginCenterItemAdapter(object : PluginCenterItemAdapter.Listener {
|
||||
override fun onToggleEnable(item: PluginCenterItem, enabled: Boolean) {
|
||||
vm.setEnabled(contextRef, item.packageName, enabled)
|
||||
item.isEnabled = enabled
|
||||
if (!enabled) {
|
||||
vm.setEnabled(contextRef, item.packageName, false)
|
||||
item.isEnabled = false
|
||||
return
|
||||
}
|
||||
|
||||
vm.setEnabled(contextRef, item.packageName, true)
|
||||
item.isEnabled = true
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
val error = runCatching {
|
||||
PaddleOcrPluginHost.probe(contextRef, item.packageName)
|
||||
}.exceptionOrNull()
|
||||
if (error != null && error !is CancellationException) {
|
||||
if (isAdded) {
|
||||
val pm = contextRef.packageManager
|
||||
val launchIntent = pm.getLaunchIntentForPackage(item.packageName)
|
||||
val wakeIntent = PluginWakeManager.buildWakeIntent(contextRef, item.packageName)
|
||||
|
||||
val errorBody = error.message ?: error.toString()
|
||||
val message = listOf(
|
||||
contextRef.getString(R.string.text_exception_info) + contextRef.getString(R.string.symbol_colon_with_blank),
|
||||
errorBody,
|
||||
contextRef.getString(R.string.text_hint) + contextRef.getString(R.string.symbol_colon_with_blank),
|
||||
getString(R.string.hint_try_clicking_the_activate_button_to_activate_the_plugin)
|
||||
).joinToString("\n\n")
|
||||
|
||||
MaterialDialog.Builder(contextRef)
|
||||
.title(R.string.error_failed_to_enable_the_plugin)
|
||||
.content(message)
|
||||
.neutralText(R.string.dialog_button_copy)
|
||||
.neutralColorRes(R.color.dialog_button_hint)
|
||||
.onNeutral { d, _ ->
|
||||
ClipboardUtils.setClip(contextRef, errorBody)
|
||||
ViewUtils.showSnack(d.view, R.string.text_already_copied_to_clip, false)
|
||||
}
|
||||
.negativeText(R.string.dialog_button_dismiss)
|
||||
.negativeColorRes(R.color.dialog_button_default)
|
||||
.onNegative { d, _ -> d.dismiss() }
|
||||
.apply positive@{
|
||||
positiveText(R.string.dialog_button_activate)
|
||||
|
||||
val openIntent = wakeIntent ?: launchIntent ?: run {
|
||||
positiveColorRes(R.color.dialog_button_unavailable)
|
||||
onPositive { d, _ ->
|
||||
ViewUtils.showSnack(d.view, R.string.text_unavailable, false)
|
||||
}
|
||||
return@positive
|
||||
}
|
||||
|
||||
positiveColorRes(R.color.dialog_button_attraction)
|
||||
onPositive { d, _ ->
|
||||
val started = openIntent.startSafely(contextRef, true)
|
||||
d.dismiss()
|
||||
if (started) {
|
||||
ViewUtils.showToast(contextRef, getString(R.string.text_activated_successfully), true)
|
||||
tryEnableAfterWake(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
.cancelable(false)
|
||||
.autoDismiss(false)
|
||||
.show()
|
||||
}
|
||||
vm.setEnabled(contextRef, item.packageName, false)
|
||||
item.isEnabled = false
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUninstall(item: PluginCenterItem) {
|
||||
@@ -292,6 +364,29 @@ class PluginCenterFragment : Fragment(R.layout.fragment_plugin_center) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryEnableAfterWake(item: PluginCenterItem) {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
val delays = longArrayOf(300L, 800L, 1500L)
|
||||
for (delayMs in delays) {
|
||||
delay(delayMs)
|
||||
val error = runCatching {
|
||||
PaddleOcrPluginHost.probe(contextRef, item.packageName)
|
||||
}.exceptionOrNull()
|
||||
if (error == null) {
|
||||
if (isAdded && _binding != null) {
|
||||
vm.setEnabled(contextRef, item.packageName, true)
|
||||
item.isEnabled = true
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
if (error is CancellationException) {
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
pkgReceiver ?: run registerPackageReceiver@{
|
||||
@@ -324,6 +419,7 @@ class PluginCenterFragment : Fragment(R.layout.fragment_plugin_center) {
|
||||
// Package uninstallation (pre-update phase) does not record "Recently uninstalled" when replacing.
|
||||
// zh-CN: 替换卸载 (更新前阶段) 不记录 "最近卸载".
|
||||
if (!replacing) {
|
||||
PluginWakeManager.clearAutoWakeAttempt(packageName)
|
||||
PluginRecentStore.setLastUninstalled(packageName)
|
||||
vm.load(context, forceRefreshIndex = false)
|
||||
return
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.autojs.autojs6.R
|
||||
*
|
||||
* Created by JetBrains AI Assistant (GPT-5.2) on Nov 26, 2025.
|
||||
* Modified by SuperMonster003 as of Jan 17, 2026.
|
||||
* Modified by JetBrains AI Assistant (GPT-5.2-Codex (xhigh)) as of Feb 13, 2026.
|
||||
*/
|
||||
class PluginCenterViewModel : ViewModel() {
|
||||
|
||||
@@ -92,6 +93,15 @@ class PluginCenterViewModel : ViewModel() {
|
||||
return@launch
|
||||
}
|
||||
|
||||
installed.forEach { local ->
|
||||
if (local.bindError != null) {
|
||||
enableStore.setEnabled(context, local.packageName, false)
|
||||
}
|
||||
if (local.isStopped) {
|
||||
PluginWakeManager.tryAutoWakeIfNeeded(context, local.packageName)
|
||||
}
|
||||
}
|
||||
|
||||
// Render list using "local only".
|
||||
// zh-CN: 使用 "仅本地" 渲染列表.
|
||||
val onlyLocalItems = installed.mapNotNull { local ->
|
||||
@@ -217,4 +227,4 @@ class PluginCenterViewModel : ViewModel() {
|
||||
private const val TAG = "PluginCenterViewModel"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.autojs.autojs.core.plugin.center
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.core.content.edit
|
||||
import org.autojs.autojs.app.GlobalAppContext
|
||||
import org.autojs.autojs.util.IntentUtils.startSafely
|
||||
|
||||
/**
|
||||
* Created by JetBrains AI Assistant (GPT-5.2-Codex (xhigh)) on Feb 13, 2026.
|
||||
*/
|
||||
object PluginWakeManager {
|
||||
|
||||
private const val SP = "plugin_center_wake"
|
||||
private const val ACTION_OCR_WAKE = "org.autojs.plugin.PADDLE_OCR.WAKE"
|
||||
private const val WAKE_ACTIVITY_META = "org.autojs.plugin.WAKE_ACTIVITY"
|
||||
|
||||
private val storeContext by lazy { GlobalAppContext.get() }
|
||||
|
||||
fun tryAutoWakeIfNeeded(context: Context, packageName: String): Boolean {
|
||||
if (isAutoWakeAttempted(packageName)) return false
|
||||
val intent = buildWakeIntent(context, packageName) ?: return false
|
||||
val started = intent.startSafely(context, true)
|
||||
markAutoWakeAttempted(packageName)
|
||||
return started
|
||||
}
|
||||
|
||||
fun buildWakeIntent(context: Context, packageName: String): Intent? {
|
||||
val pm = context.packageManager
|
||||
val appInfo = runCatching { pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA) }.getOrNull()
|
||||
val metaWakeActivity = appInfo?.metaData?.getString(WAKE_ACTIVITY_META)
|
||||
val metaComponent = metaWakeActivity?.let { className ->
|
||||
val fullName = if (className.startsWith(".")) packageName + className else className
|
||||
ComponentName(packageName, fullName)
|
||||
}
|
||||
val wakeComponent = metaComponent ?: run {
|
||||
val implicitIntent = Intent(ACTION_OCR_WAKE).setPackage(packageName)
|
||||
val info = pm.queryIntentActivities(implicitIntent, 0).firstOrNull()
|
||||
info?.activityInfo?.let { ComponentName(it.packageName, it.name) }
|
||||
}
|
||||
return when {
|
||||
wakeComponent != null -> Intent().setComponent(wakeComponent)
|
||||
else -> Intent(ACTION_OCR_WAKE).setPackage(packageName).takeIf { it.resolveActivity(pm) != null }
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAutoWakeAttempted(packageName: String): Boolean {
|
||||
val sp = storeContext.getSharedPreferences(SP, Context.MODE_PRIVATE)
|
||||
return sp.getBoolean(key(packageName), false)
|
||||
}
|
||||
|
||||
private fun markAutoWakeAttempted(packageName: String) {
|
||||
val sp = storeContext.getSharedPreferences(SP, Context.MODE_PRIVATE)
|
||||
sp.edit { putBoolean(key(packageName), true) }
|
||||
}
|
||||
|
||||
fun clearAutoWakeAttempt(packageName: String) {
|
||||
val sp = storeContext.getSharedPreferences(SP, Context.MODE_PRIVATE)
|
||||
sp.edit { remove(key(packageName)) }
|
||||
}
|
||||
|
||||
private fun key(packageName: String) = "key_\$_auto_wake_attempted_\$_$packageName"
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.graphics.Bitmap
|
||||
@@ -28,6 +29,9 @@ import org.autojs.plugin.paddle.ocr.api.PluginInfo
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
/**
|
||||
* Modified by JetBrains AI Assistant (GPT-5.2-Codex (xhigh)) as of Feb 13, 2026.
|
||||
*/
|
||||
object PaddleOcrPluginHost {
|
||||
|
||||
private const val TAG = "PaddleOcrPluginHost"
|
||||
@@ -37,29 +41,36 @@ object PaddleOcrPluginHost {
|
||||
private const val DEFAULT_BIND_TIMEOUT_MS = 60_000L
|
||||
private const val DEFAULT_CALL_TIMEOUT_MS = 60_000L
|
||||
|
||||
private val externalServiceFlag = runCatching { ServiceInfo::class.java.getField("FLAG_EXTERNAL_SERVICE").getInt(null) }.getOrNull() ?: 0
|
||||
private val bindExternalServiceFlag = runCatching { Context::class.java.getField("BIND_EXTERNAL_SERVICE").getInt(null) }.getOrNull() ?: 0
|
||||
|
||||
data class Discovered(
|
||||
val serviceInfo: ServiceInfo,
|
||||
val pluginInfo: PluginInfo?,
|
||||
val error: Throwable?,
|
||||
)
|
||||
|
||||
suspend fun discover(context: Context): List<Discovered> {
|
||||
val pm = context.packageManager
|
||||
val resolveList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
pm.queryIntentServices(Intent(ACTION_OCR), PackageManager.ResolveInfoFlags.of(0))
|
||||
} else {
|
||||
pm.queryIntentServices(Intent(ACTION_OCR), 0)
|
||||
}
|
||||
val services = resolveList.mapNotNull { it.serviceInfo }
|
||||
val services = queryOcrServices(context)
|
||||
Log.i(TAG, "discover: services=${services.size}")
|
||||
return services.map { svc ->
|
||||
Log.i(TAG, "discover: ${svc.packageName}/${svc.name}")
|
||||
val info = runCatching { withService(context, svc, DEFAULT_BIND_TIMEOUT_MS) { it.getInfo() } }
|
||||
.onFailure { e -> Log.w(TAG, "getInfo failed: ${e.message}") }
|
||||
.getOrNull()
|
||||
Discovered(svc, info)
|
||||
val result = runCatching { withService(context, svc, DEFAULT_BIND_TIMEOUT_MS) { it.getInfo() } }
|
||||
val info = result.getOrNull()
|
||||
val error = result.exceptionOrNull()
|
||||
if (error != null) {
|
||||
Log.w(TAG, "getInfo failed: ${error.message}")
|
||||
}
|
||||
Discovered(svc, info, error)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun probe(context: Context, packageName: String): PluginInfo {
|
||||
val serviceInfo = queryOcrServices(context, packageName).firstOrNull()
|
||||
?: error("No OCR service found for package: $packageName")
|
||||
return withService(context, serviceInfo, DEFAULT_BIND_TIMEOUT_MS) { it.getInfo() }
|
||||
}
|
||||
|
||||
suspend fun recognizeText(
|
||||
context: Context,
|
||||
target: Discovered,
|
||||
@@ -141,6 +152,40 @@ object PaddleOcrPluginHost {
|
||||
return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY)
|
||||
}
|
||||
|
||||
private fun queryOcrServices(context: Context, packageName: String? = null): List<ServiceInfo> {
|
||||
val pm = context.packageManager
|
||||
val intent = Intent(ACTION_OCR).apply {
|
||||
if (!packageName.isNullOrBlank()) {
|
||||
setPackage(packageName)
|
||||
}
|
||||
}
|
||||
val resolveList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
pm.queryIntentServices(intent, PackageManager.ResolveInfoFlags.of(0))
|
||||
} else {
|
||||
pm.queryIntentServices(intent, 0)
|
||||
}
|
||||
return resolveList.mapNotNull { it.serviceInfo }
|
||||
}
|
||||
|
||||
private fun buildBindFlags(serviceInfo: ServiceInfo): Int {
|
||||
var flags = Context.BIND_AUTO_CREATE
|
||||
if (externalServiceFlag != 0 && bindExternalServiceFlag != 0 && (serviceInfo.flags and externalServiceFlag) != 0) {
|
||||
flags = flags or bindExternalServiceFlag
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
private fun buildBindFailureMessage(context: Context, serviceInfo: ServiceInfo, cn: ComponentName, reason: String? = null): String {
|
||||
val pm = context.packageManager
|
||||
val perm = serviceInfo.permission
|
||||
val hasPerm = perm.isNullOrBlank() || pm.checkPermission(perm, context.packageName) == PackageManager.PERMISSION_GRANTED
|
||||
val appInfo = serviceInfo.applicationInfo
|
||||
val appStopped = (appInfo.flags and ApplicationInfo.FLAG_STOPPED) != 0
|
||||
val isExternal = externalServiceFlag != 0 && (serviceInfo.flags and externalServiceFlag) != 0
|
||||
val reasonText = reason?.let { " | reason=$it" } ?: ""
|
||||
return "bindService failed: $cn. exported=${serviceInfo.exported} enabled=${serviceInfo.enabled} appEnabled=${appInfo.enabled} stopped=$appStopped perm=$perm hasPerm=$hasPerm external=$isExternal flags=0x${Integer.toHexString(serviceInfo.flags)}$reasonText"
|
||||
}
|
||||
|
||||
private suspend fun <T> withService(
|
||||
context: Context,
|
||||
serviceInfo: ServiceInfo,
|
||||
@@ -180,9 +225,10 @@ object PaddleOcrPluginHost {
|
||||
}
|
||||
}
|
||||
|
||||
val ok = try {
|
||||
val bindFlags = buildBindFlags(serviceInfo)
|
||||
var ok = try {
|
||||
Log.i(TAG, "bindService: $cn")
|
||||
appCtx.bindService(intent, conn, Context.BIND_AUTO_CREATE)
|
||||
appCtx.bindService(intent, conn, bindFlags)
|
||||
} catch (se: SecurityException) {
|
||||
Log.e(TAG, "bindService SecurityException: $cn | ${se.message}")
|
||||
cont.resumeWith(
|
||||
@@ -194,9 +240,25 @@ object PaddleOcrPluginHost {
|
||||
)
|
||||
return@suspendCancellableCoroutine
|
||||
}
|
||||
if (!ok && bindFlags != Context.BIND_AUTO_CREATE) {
|
||||
ok = try {
|
||||
appCtx.bindService(intent, conn, Context.BIND_AUTO_CREATE)
|
||||
} catch (se: SecurityException) {
|
||||
Log.e(TAG, "bindService SecurityException: $cn | ${se.message}")
|
||||
cont.resumeWith(
|
||||
Result.failure(
|
||||
IllegalStateException(
|
||||
"bindService SecurityException: $cn. Please make sure the plugin declares <uses-permission android:name=\"org.autojs.permission.PLUGIN\"/> and the Service uses this permission.", se
|
||||
)
|
||||
)
|
||||
)
|
||||
return@suspendCancellableCoroutine
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
Log.e(TAG, "bindService failed: $cn")
|
||||
cont.resumeWith(Result.failure(IllegalStateException("bindService failed: $cn")))
|
||||
val msg = buildBindFailureMessage(appCtx, serviceInfo, cn)
|
||||
Log.e(TAG, msg)
|
||||
cont.resumeWith(Result.failure(IllegalStateException(msg)))
|
||||
return@suspendCancellableCoroutine
|
||||
}
|
||||
|
||||
|
||||
@@ -1333,4 +1333,11 @@
|
||||
<string name="warn_exit_without_saving_settings">لم يتم حفظ الاعدادات. هل تريد الخروج?</string>
|
||||
<string name="error_failed_to_save">فشل الحفظ</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">جارٍ الحفظ...</string>
|
||||
<string name="symbol_colon_with_blank">": "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">فشل في تفعيل الاضافة</string>
|
||||
<string name="text_exception_info">معلومات الاستثناء</string>
|
||||
<string name="text_hint">تلميح</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">جرّب الضغط على زر \"تفعيل\" لتفعيل الاضافة مرة واحدة.\nاذا نجح التفعيل، فسيتم تشغيل الزر تلقائيا خلال فترة زمنية معينة.</string>
|
||||
<string name="dialog_button_activate">تفعيل</string>
|
||||
<string name="text_activated_successfully">تم التفعيل بنجاح</string>
|
||||
</resources>
|
||||
@@ -1328,4 +1328,11 @@
|
||||
<string name="warn_exit_without_saving_settings">The settings has not been saved, are you sure to exit?</string>
|
||||
<string name="error_failed_to_save">Failed to save</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">Saving...</string>
|
||||
<string name="symbol_colon_with_blank">": "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">Failed to enable the plugin</string>
|
||||
<string name="text_exception_info">Exception info</string>
|
||||
<string name="text_hint">Hint</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">Try clicking the \"Activate\" button to activate the plugin once.\nIf activation is successful, the button will automatically turn on within a certain period of time.</string>
|
||||
<string name="dialog_button_activate">Activate</string>
|
||||
<string name="text_activated_successfully">Activated successfully</string>
|
||||
</resources>
|
||||
@@ -1331,4 +1331,11 @@
|
||||
<string name="warn_exit_without_saving_settings">La configuracion no se ha guardado. ¿Seguro que quieres salir?</string>
|
||||
<string name="error_failed_to_save">Error al guardar</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">Guardando...</string>
|
||||
<string name="symbol_colon_with_blank">": "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">No se pudo activar el plugin</string>
|
||||
<string name="text_exception_info">Info de la excepcion</string>
|
||||
<string name="text_hint">Sugerencia</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">Prueba a pulsar el boton \"Activar\" para activar el plugin una vez.\nSi la activacion se realiza correctamente, el boton se activara automaticamente en un periodo de tiempo.</string>
|
||||
<string name="dialog_button_activate">Activar</string>
|
||||
<string name="text_activated_successfully">Activado correctamente</string>
|
||||
</resources>
|
||||
@@ -1331,4 +1331,11 @@
|
||||
<string name="warn_exit_without_saving_settings">Les parametres ne sont pas enregistres. Quitter?</string>
|
||||
<string name="error_failed_to_save">Echec de l\'enregistrement</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">Enregistrement...</string>
|
||||
<string name="symbol_colon_with_blank">" : "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">Echec de l\'activation du plugin</string>
|
||||
<string name="text_exception_info">Infos de l\'exception</string>
|
||||
<string name="text_hint">Astuce</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">Essayez d\'appuyer sur le bouton \"Activer\" pour activer le plugin une fois.\nSi l\'activation reussit, le bouton s\'activera automatiquement dans un certain delai.</string>
|
||||
<string name="dialog_button_activate">Activer</string>
|
||||
<string name="text_activated_successfully">Activation reussie</string>
|
||||
</resources>
|
||||
@@ -1332,4 +1332,11 @@
|
||||
<string name="warn_exit_without_saving_settings">設定が保存されていません. 終了しますか?</string>
|
||||
<string name="error_failed_to_save">保存に失敗しました</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">保存中...</string>
|
||||
<string name="symbol_colon_with_blank">": "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">プラグインの有効化に失敗しました</string>
|
||||
<string name="text_exception_info">例外情報</string>
|
||||
<string name="text_hint">ヒント</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">\"有効化\" ボタンをタップして, プラグインを一度有効化してみてください.\n有効化に成功すると, 一定時間内にボタンが自動的にオンになります.</string>
|
||||
<string name="dialog_button_activate">有効化</string>
|
||||
<string name="text_activated_successfully">有効化しました</string>
|
||||
</resources>
|
||||
@@ -1333,4 +1333,11 @@
|
||||
<string name="warn_exit_without_saving_settings">설정이 저장되지 않았습니다. 종료할까요?</string>
|
||||
<string name="error_failed_to_save">저장에 실패했습니다</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">저장 중...</string>
|
||||
<string name="symbol_colon_with_blank">": "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">플러그인을 활성화하지 못했습니다</string>
|
||||
<string name="text_exception_info">예외 정보</string>
|
||||
<string name="text_hint">힌트</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">\"활성화\" 버튼을 눌러 플러그인을 한 번 활성화해 보세요.\n활성화에 성공하면 일정 시간 내에 버튼이 자동으로 켜집니다.</string>
|
||||
<string name="dialog_button_activate">활성화</string>
|
||||
<string name="text_activated_successfully">활성화되었습니다</string>
|
||||
</resources>
|
||||
@@ -1331,4 +1331,11 @@
|
||||
<string name="warn_exit_without_saving_settings">Настройки не сохранены. Выйти?</string>
|
||||
<string name="error_failed_to_save">Не удалось сохранить</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">Сохранение...</string>
|
||||
<string name="symbol_colon_with_blank">": "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">Не удалось включить плагин</string>
|
||||
<string name="text_exception_info">Сведения об исключении</string>
|
||||
<string name="text_hint">Подсказка</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">Попробуйте нажать кнопку \"Активировать\", чтобы активировать плагин один раз.\nЕсли активация пройдет успешно, кнопка автоматически включится в течение некоторого времени.</string>
|
||||
<string name="dialog_button_activate">Активировать</string>
|
||||
<string name="text_activated_successfully">Активация выполнена</string>
|
||||
</resources>
|
||||
@@ -1327,4 +1327,11 @@
|
||||
<string name="warn_exit_without_saving_settings">設置尚未保存, 確定要退出嗎</string>
|
||||
<string name="error_failed_to_save">保存失敗</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">正在保存...</string>
|
||||
<string name="symbol_colon_with_blank">": "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">插件啓用失敗</string>
|
||||
<string name="text_exception_info">異常信息</string>
|
||||
<string name="text_hint">提示</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">可嘗試點擊 \"激活\" 按鈕激活一次插件.\n如激活成功, 按鈕將在一定時間內自動開啓.</string>
|
||||
<string name="dialog_button_activate">激活</string>
|
||||
<string name="text_activated_successfully">激活成功</string>
|
||||
</resources>
|
||||
@@ -1327,4 +1327,11 @@
|
||||
<string name="warn_exit_without_saving_settings">設定尚未儲存, 確定要退出嗎</string>
|
||||
<string name="error_failed_to_save">儲存失敗</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">正在儲存...</string>
|
||||
<string name="symbol_colon_with_blank">": "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">外掛啟用失敗</string>
|
||||
<string name="text_exception_info">異常資訊</string>
|
||||
<string name="text_hint">提示</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">可嘗試點選 \"啟用\" 按鈕啟用一次外掛.\n如啟用成功, 按鈕將在一定時間內自動開啟.</string>
|
||||
<string name="dialog_button_activate">啟用</string>
|
||||
<string name="text_activated_successfully">啟用成功</string>
|
||||
</resources>
|
||||
@@ -1328,4 +1328,11 @@
|
||||
<string name="warn_exit_without_saving_settings">设置尚未保存, 确定要退出吗</string>
|
||||
<string name="error_failed_to_save">保存失败</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">正在保存...</string>
|
||||
<string name="symbol_colon_with_blank">": "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">插件启用失败</string>
|
||||
<string name="text_exception_info">异常信息</string>
|
||||
<string name="text_hint">提示</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">可尝试点击 \"激活\" 按钮激活一次插件.\n如激活成功, 按钮将在一定时间内自动开启.</string>
|
||||
<string name="dialog_button_activate">激活</string>
|
||||
<string name="text_activated_successfully">激活成功</string>
|
||||
</resources>
|
||||
@@ -1602,4 +1602,11 @@
|
||||
<string name="warn_exit_without_saving_settings">The settings has not been saved, are you sure to exit?</string>
|
||||
<string name="error_failed_to_save">Failed to save</string>
|
||||
<string name="text_saving" tools:ignore="TypographyEllipsis">Saving...</string>
|
||||
<string name="symbol_colon_with_blank">": "</string>
|
||||
<string name="error_failed_to_enable_the_plugin">Failed to enable the plugin</string>
|
||||
<string name="text_exception_info">Exception info</string>
|
||||
<string name="text_hint">Hint</string>
|
||||
<string name="hint_try_clicking_the_activate_button_to_activate_the_plugin">Try clicking the \"Activate\" button to activate the plugin once.\nIf activation is successful, the button will automatically turn on within a certain period of time.</string>
|
||||
<string name="dialog_button_activate">Activate</string>
|
||||
<string name="text_activated_successfully">Activated successfully</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user