diff --git a/app/src/main/java/com/fankes/apperrorstracking/application/AppErrorsApplication.kt b/app/src/main/java/com/fankes/apperrorstracking/application/AppErrorsApplication.kt index 731b3fa..ab07144 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/application/AppErrorsApplication.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/application/AppErrorsApplication.kt @@ -22,6 +22,7 @@ package com.fankes.apperrorstracking.application import androidx.appcompat.app.AppCompatDelegate +import com.fankes.apperrorstracking.locale.LocaleString import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication class AppErrorsApplication : ModuleApplication() { @@ -30,5 +31,7 @@ class AppErrorsApplication : ModuleApplication() { super.onCreate() /** 跟随系统夜间模式 */ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + /** 绑定 I18n */ + LocaleString.bind(instance = this) } } \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/hook/HookEntry.kt b/app/src/main/java/com/fankes/apperrorstracking/hook/HookEntry.kt index 0d41f91..9b9483a 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/hook/HookEntry.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/hook/HookEntry.kt @@ -22,6 +22,7 @@ package com.fankes.apperrorstracking.hook import com.fankes.apperrorstracking.hook.entity.FrameworkHooker +import com.fankes.apperrorstracking.locale.LocaleString import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed import com.highcapable.yukihookapi.hook.factory.configs import com.highcapable.yukihookapi.hook.factory.encase @@ -35,5 +36,10 @@ class HookEntry : IYukiHookXposedInit { isDebug = false } - override fun onHook() = encase { loadSystem(FrameworkHooker) } + override fun onHook() = encase { + loadSystem { + LocaleString.bind(instance = this) + loadHooker(FrameworkHooker) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt index fec1416..9e3b3d6 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt @@ -42,6 +42,7 @@ import android.widget.LinearLayout import android.widget.TextView import com.fankes.apperrorstracking.R import com.fankes.apperrorstracking.bean.AppErrorsInfoBean +import com.fankes.apperrorstracking.locale.LocaleString import com.fankes.apperrorstracking.ui.activity.AppErrorsDetailActivity import com.fankes.apperrorstracking.utils.drawable.drawabletoolbox.DrawableBuilder import com.fankes.apperrorstracking.utils.factory.* @@ -129,14 +130,6 @@ object FrameworkHooker : YukiBaseHooker() { private fun lastAppErrorsInfo(packageName: String) = appErrorsRecords.takeIf { it.isNotEmpty() }?.filter { it.packageName == packageName }?.get(0) - /** - * 获取 I18n 字符串 - * @param resId 模块资源 Id - * @param objArrs 格式化数组 - * @return [String] - */ - private fun string(resId: Int, vararg objArrs: Any) = moduleAppResources.getString(resId, *objArrs) - /** * 创建对话框按钮 * @param context 实例 @@ -252,30 +245,30 @@ object FrameworkHooker : YukiBaseHooker() { android.R.style.Theme_Material_Dialog else android.R.style.Theme_Material_Light_Dialog ).create().apply { - setTitle(string(if (isRepeating) R.string.aerr_repeated_title else R.string.aerr_title, appName)) + setTitle(if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName)) setView(LinearLayout(context).apply { orientation = LinearLayout.VERTICAL /** 应用信息按钮 */ val appInfoButton = - createButtonItem(context, R.drawable.ic_baseline_info, string(R.string.app_info)) { + createButtonItem(context, R.drawable.ic_baseline_info, LocaleString.appInfo) { cancel() context.openSelfSetting(packageName) } /** 关闭应用按钮 */ val closeAppButton = - createButtonItem(context, R.drawable.ic_baseline_close, string(R.string.close_app)) { cancel() } + createButtonItem(context, R.drawable.ic_baseline_close, LocaleString.closeApp) { cancel() } /** 重新打开按钮 */ val reOpenButton = - createButtonItem(context, R.drawable.ic_baseline_refresh, string(R.string.reopen_app)) { + createButtonItem(context, R.drawable.ic_baseline_refresh, LocaleString.reopenApp) { cancel() context.openApp(packageName) } /** 错误详情按钮 */ val errorDetailButton = - createButtonItem(context, R.drawable.ic_baseline_bug_report, string(R.string.error_detail)) { + createButtonItem(context, R.drawable.ic_baseline_bug_report, LocaleString.errorDetail) { cancel() lastAppErrorsInfo(packageName)?.let { AppErrorsDetailActivity.start(context, it, isOutSide = true) } ?: context.toast(msg = "Invalid AppErrorsInfo") @@ -283,18 +276,18 @@ object FrameworkHooker : YukiBaseHooker() { /** 忽略按钮 - 直到解锁 */ val ignoredUntilUnlockButton = - createButtonItem(context, R.drawable.ic_baseline_eject, string(R.string.ignore_if_unlock)) { + createButtonItem(context, R.drawable.ic_baseline_eject, LocaleString.ignoreIfUnlock) { cancel() ignoredErrorsIfUnlockApps.add(packageName) - context.toast(string(R.string.ignore_if_unlock_tip, appName)) + context.toast(LocaleString.ignoreIfUnlockTip(appName)) } /** 忽略按钮 - 直到重启 */ val ignoredUntilRestartButton = - createButtonItem(context, R.drawable.ic_baseline_eject, string(R.string.ignore_if_restart)) { + createButtonItem(context, R.drawable.ic_baseline_eject, LocaleString.ignoreIfRestart) { cancel() ignoredErrorsIfRestartApps.add(packageName) - context.toast(string(R.string.ignore_if_restart_tip, appName)) + context.toast(LocaleString.ignoreIfRestartTip(appName)) } /** 判断进程是否为 APP */ if (isApp) { diff --git a/app/src/main/java/com/fankes/apperrorstracking/locale/LocaleString.kt b/app/src/main/java/com/fankes/apperrorstracking/locale/LocaleString.kt new file mode 100644 index 0000000..ce870ee --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/locale/LocaleString.kt @@ -0,0 +1,163 @@ +/* + * AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer. + * Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) + * https://github.com/KitsunePie/AppErrorsTracking + * + * This software is non-free but opensource software: you can redistribute it + * and/or modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * and eula along with this software. If not, see + * + * + * This file is Created by fankes on 2022/5/11. + */ +@file:Suppress("MemberVisibilityCanBePrivate", "StaticFieldLeak", "unused") + +package com.fankes.apperrorstracking.locale + +import android.content.Context +import android.content.res.Resources +import com.fankes.apperrorstracking.R +import com.highcapable.yukihookapi.hook.param.PackageParam + +/** + * I18n 字符串实例 + */ +object LocaleString { + + /** 当前的 [Context] */ + private var baseContext: Context? = null + + /** 当前的 [PackageParam] */ + private var basePackageParam: PackageParam? = null + + /** 当前的 [Resources] */ + private var baseResources: Resources? = null + + /** + * 当前的 [Resources] + * @return [Resources] + * @throws IllegalStateException 如果 [LocaleString] 没有被绑定 + */ + private val resources + get() = baseContext?.resources ?: basePackageParam?.moduleAppResources ?: baseResources + ?: error("LocaleString must bind an instance first") + + /** + * 绑定并初始化 + * @param instance 可以是 [Context]、[PackageParam]、[Resources] + */ + fun bind(instance: Any) { + when (instance) { + is Context -> baseContext = instance + is PackageParam -> basePackageParam = instance + is Resources -> baseResources = instance + else -> error("LocaleString bind an unknown instance") + } + } + + /** + * 根据资源 Id 获取字符串 + * @param objArrs 格式化实例 + * @return [String] + */ + private fun Int.bind(vararg objArrs: Any) = resources.getString(this, *objArrs) + + /** @string Automatic generated */ + val copied get() = copied() + + /** @string Automatic generated */ + fun copied(vararg objArrs: Any) = R.string.copied.bind(*objArrs) + + /** @string Automatic generated */ + val copyFail get() = copyFail() + + /** @string Automatic generated */ + fun copyFail(vararg objArrs: Any) = R.string.copy_fail.bind(*objArrs) + + /** @string Automatic generated */ + val printToLogcatSuccess get() = printToLogcatSuccess() + + /** @string Automatic generated */ + fun printToLogcatSuccess(vararg objArrs: Any) = R.string.print_to_logcat_success.bind(*objArrs) + + /** @string Automatic generated */ + val outputStackSuccess get() = outputStackSuccess() + + /** @string Automatic generated */ + fun outputStackSuccess(vararg objArrs: Any) = R.string.output_stack_success.bind(*objArrs) + + /** @string Automatic generated */ + val outputStackFail get() = outputStackFail() + + /** @string Automatic generated */ + fun outputStackFail(vararg objArrs: Any) = R.string.output_stack_fail.bind(*objArrs) + + /** @string Automatic generated */ + val aerrTitle get() = aerrTitle() + + /** @string Automatic generated */ + fun aerrTitle(vararg objArrs: Any) = R.string.aerr_title.bind(*objArrs) + + /** @string Automatic generated */ + val aerrRepeatedTitle get() = aerrRepeatedTitle() + + /** @string Automatic generated */ + fun aerrRepeatedTitle(vararg objArrs: Any) = R.string.aerr_repeated_title.bind(*objArrs) + + /** @string Automatic generated */ + val appInfo get() = appInfo() + + /** @string Automatic generated */ + fun appInfo(vararg objArrs: Any) = R.string.app_info.bind(*objArrs) + + /** @string Automatic generated */ + val closeApp get() = closeApp() + + /** @string Automatic generated */ + fun closeApp(vararg objArrs: Any) = R.string.close_app.bind(*objArrs) + + /** @string Automatic generated */ + val reopenApp get() = reopenApp() + + /** @string Automatic generated */ + fun reopenApp(vararg objArrs: Any) = R.string.reopen_app.bind(*objArrs) + + /** @string Automatic generated */ + val errorDetail get() = errorDetail() + + /** @string Automatic generated */ + fun errorDetail(vararg objArrs: Any) = R.string.error_detail.bind(*objArrs) + + /** @string Automatic generated */ + val ignoreIfUnlock get() = ignoreIfUnlock() + + /** @string Automatic generated */ + fun ignoreIfUnlock(vararg objArrs: Any) = R.string.ignore_if_unlock.bind(*objArrs) + + /** @string Automatic generated */ + val ignoreIfRestart get() = ignoreIfRestart() + + /** @string Automatic generated */ + fun ignoreIfRestart(vararg objArrs: Any) = R.string.ignore_if_restart.bind(*objArrs) + + /** @string Automatic generated */ + val ignoreIfUnlockTip get() = ignoreIfUnlockTip() + + /** @string Automatic generated */ + fun ignoreIfUnlockTip(vararg objArrs: Any) = R.string.ignore_if_unlock_tip.bind(*objArrs) + + /** @string Automatic generated */ + val ignoreIfRestartTip get() = ignoreIfRestartTip() + + /** @string Automatic generated */ + fun ignoreIfRestartTip(vararg objArrs: Any) = R.string.ignore_if_restart_tip.bind(*objArrs) +} \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsDetailActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsDetailActivity.kt index 9b39ae6..41974ce 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsDetailActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsDetailActivity.kt @@ -33,6 +33,7 @@ import com.fankes.apperrorstracking.R import com.fankes.apperrorstracking.bean.AppErrorsInfoBean import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDetailBinding import com.fankes.apperrorstracking.hook.entity.FrameworkHooker +import com.fankes.apperrorstracking.locale.LocaleString import com.fankes.apperrorstracking.ui.activity.base.BaseActivity import com.fankes.apperrorstracking.utils.factory.* import com.highcapable.yukihookapi.hook.log.loggerE @@ -55,7 +56,8 @@ class AppErrorsDetailActivity : BaseActivity() { fun start(context: Context, appErrorsInfo: AppErrorsInfoBean, isOutSide: Boolean = false) { runCatching { context.startActivity((if (isOutSide) Intent() else Intent(context, AppErrorsDetailActivity::class.java)).apply { - if (context !is Activity) flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + flags = if (context !is Activity) Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + else Intent.FLAG_ACTIVITY_NEW_TASK if (isOutSide) component = ComponentName(BuildConfig.APPLICATION_ID, AppErrorsDetailActivity::class.java.name) putExtra(FrameworkHooker.APP_ERRORS_INFO, appErrorsInfo) }) @@ -76,7 +78,7 @@ class AppErrorsDetailActivity : BaseActivity() { binding.titleBackIcon.setOnClickListener { onBackPressed() } binding.printIcon.setOnClickListener { loggerE(msg = createStack()) - toast(getString(R.string.print_to_logcat_success)) + toast(LocaleString.printToLogcatSuccess) } binding.copyIcon.setOnClickListener { copyToClipboard(appErrorsInfo.stackTrace) } binding.exportIcon.setOnClickListener { @@ -115,9 +117,9 @@ class AppErrorsDetailActivity : BaseActivity() { if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) runCatching { data?.data?.let { contentResolver?.openOutputStream(it)?.apply { write(stackTrace.toByteArray()) }?.close() - toast(getString(R.string.output_stack_success)) - } ?: toast(getString(R.string.output_stack_fail)) - }.onFailure { toast(getString(R.string.output_stack_fail)) } + toast(LocaleString.outputStackSuccess) + } ?: toast(LocaleString.outputStackFail) + }.onFailure { toast(LocaleString.outputStackFail) } } override fun onBackPressed() { diff --git a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt index 3c2ca58..d40195b 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt @@ -35,7 +35,7 @@ import android.graphics.drawable.Drawable import android.net.Uri import android.provider.Settings import android.widget.Toast -import com.fankes.apperrorstracking.R +import com.fankes.apperrorstracking.locale.LocaleString /** * 系统深色模式是否开启 @@ -109,7 +109,7 @@ fun Context.copyToClipboard(content: String) = runCatching { (getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).apply { setPrimaryClip(ClipData.newPlainText(null, content)) (primaryClip?.getItemAt(0)?.text ?: "").also { - if (it != content) toast(getString(R.string.copy_fail)) else toast(getString(R.string.copied)) + if (it != content) toast(LocaleString.copyFail) else toast(LocaleString.copied) } } }