From 443220a07839912fa42526f8a5e28a19c845f09b Mon Sep 17 00:00:00 2001 From: Fankesyooni Date: Thu, 12 May 2022 04:18:00 +0800 Subject: [PATCH] Added function AppErrorsRecordActivity and support native crash message --- README.md | 4 +- app/src/main/AndroidManifest.xml | 24 ++- .../fankes/apperrorstracking/const/Const.kt | 48 ++++++ .../hook/entity/FrameworkHooker.kt | 66 +++++--- .../service/QuickStartTileService.kt | 35 +++++ .../ui/activity/AppErrorsDetailActivity.kt | 8 +- .../ui/activity/AppErrorsRecordActivity.kt | 142 ++++++++++++++++++ .../drawable-night/bg_permotion_ripple.xml | 10 ++ .../main/res/drawable/bg_permotion_ripple.xml | 10 ++ app/src/main/res/drawable/bg_red_round.xml | 6 + app/src/main/res/drawable/ic_clear.xml | 9 ++ app/src/main/res/drawable/ic_debug.xml | 11 ++ .../res/layout/activity_app_errors_record.xml | 93 ++++++++++++ .../res/layout/adapter_app_errors_record.xml | 104 +++++++++++++ app/src/main/res/values-ja/strings.xml | 4 + app/src/main/res/values-zh-rCN/strings.xml | 4 + app/src/main/res/values-zh-rHK/strings.xml | 4 + app/src/main/res/values-zh-rMO/strings.xml | 4 + app/src/main/res/values-zh-rTW/strings.xml | 4 + app/src/main/res/values/strings.xml | 4 + 20 files changed, 571 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/com/fankes/apperrorstracking/const/Const.kt create mode 100644 app/src/main/java/com/fankes/apperrorstracking/service/QuickStartTileService.kt create mode 100644 app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsRecordActivity.kt create mode 100644 app/src/main/res/drawable-night/bg_permotion_ripple.xml create mode 100644 app/src/main/res/drawable/bg_permotion_ripple.xml create mode 100644 app/src/main/res/drawable/bg_red_round.xml create mode 100644 app/src/main/res/drawable/ic_clear.xml create mode 100644 app/src/main/res/drawable/ic_debug.xml create mode 100644 app/src/main/res/layout/activity_app_errors_record.xml create mode 100644 app/src/main/res/layout/adapter_app_errors_record.xml diff --git a/README.md b/README.md index fcd2482..da3dddf 100644 --- a/README.md +++ b/README.md @@ -44,13 +44,15 @@ Added more features to app's crash dialog, fixed custom rom deleted dialog, the - 打印异常堆栈到控制台功能 +- 异常历史记录功能,可通过通知栏磁贴“异常历史记录”进入和模块主界面进入(正在开发) + - 支持 Android 10 及以上系统的深色模式 ## Future 此项目依然在开发中,现在未解决的问题和包含的问题如下 -- 异常历史记录和排除列表正在开发 +- 排除列表和异常历史记录导出、清空功能正在开发 - 后台进程可能依然会弹出崩溃对话框且开发者选项里的设置无效,还在排查 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8c85c35..c565f6c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,6 +31,28 @@ + android:launchMode="singleTask" + android:screenOrientation="behind" + android:taskAffinity=":detail" /> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/const/Const.kt b/app/src/main/java/com/fankes/apperrorstracking/const/Const.kt new file mode 100644 index 0000000..0fa6628 --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/const/Const.kt @@ -0,0 +1,48 @@ +/* + * 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/12. + */ +package com.fankes.apperrorstracking.const + +import com.fankes.apperrorstracking.bean.AppErrorsInfoBean + +/** + * 全局常量 + */ +object Const { + + /** [AppErrorsInfoBean] 传值 */ + const val EXTRA_APP_ERRORS_INFO = "app_errors_info_extra" + + /** 模块接收广播 */ + const val ACTION_MODULE_HANDLER_RECEIVER = "module_handler_action" + + /** 宿主接收广播 */ + const val ACTION_HOST_HANDLER_RECEIVER = "host_handler_action" + + /** [AppErrorsInfoBean] 控制数据 */ + const val TYPE_APP_ERRORS_DATA_CONTROL = "app_errors_data_control_type" + + /** [AppErrorsInfoBean] 获取数据 */ + const val TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA = "app_errors_data_control_get_data_type" + + /** [AppErrorsInfoBean] 获取到的数据内容 */ + const val TAG_APP_ERRORS_DATA_CONTENT = "app_errors_data_content_tag" +} \ 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 10ef196..bcdc516 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 @@ -41,6 +41,7 @@ import android.widget.TextView import androidx.core.content.res.ResourcesCompat import com.fankes.apperrorstracking.R import com.fankes.apperrorstracking.bean.AppErrorsInfoBean +import com.fankes.apperrorstracking.const.Const import com.fankes.apperrorstracking.locale.LocaleString import com.fankes.apperrorstracking.ui.activity.AppErrorsDetailActivity import com.fankes.apperrorstracking.utils.drawable.drawabletoolbox.DrawableBuilder @@ -51,12 +52,11 @@ import com.highcapable.yukihookapi.hook.factory.field import com.highcapable.yukihookapi.hook.factory.hasMethod import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.log.loggerE +import com.highcapable.yukihookapi.hook.type.android.ActivityThreadClass import com.highcapable.yukihookapi.hook.type.android.MessageClass object FrameworkHooker : YukiBaseHooker() { - const val APP_ERRORS_INFO = "app_errors_info" - private const val AppErrorsClass = "com.android.server.am.AppErrors" private const val AppErrorResultClass = "com.android.server.am.AppErrorResult" @@ -110,6 +110,23 @@ object FrameworkHooker : YukiBaseHooker() { } } + /** 宿主广播接收器 */ + private val hostHandlerReceiver by lazy { + object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null) return + when (intent.getStringExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL)) { + Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA -> + context?.sendBroadcast(Intent().apply { + action = Const.ACTION_MODULE_HANDLER_RECEIVER + putExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL, Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA) + putExtra(Const.TAG_APP_ERRORS_DATA_CONTENT, appErrorsRecords) + }) + } + } + } + } + /** * 注册广播接收器 * @param context 实例 @@ -118,6 +135,7 @@ object FrameworkHooker : YukiBaseHooker() { if (isRegisterReceiver) return context.registerReceiver(userPresentReceiver, IntentFilter().apply { addAction(Intent.ACTION_USER_PRESENT) }) context.registerReceiver(localeChangedReceiver, IntentFilter().apply { addAction(Intent.ACTION_LOCALE_CHANGED) }) + context.registerReceiver(hostHandlerReceiver, IntentFilter().apply { addAction(Const.ACTION_HOST_HANDLER_RECEIVER) }) isRegisterReceiver = true } @@ -161,6 +179,16 @@ object FrameworkHooker : YukiBaseHooker() { } override fun onHook() { + /** 注入全局监听 */ + ActivityThreadClass.hook { + injectMember { + method { + name = "currentApplication" + emptyParam() + } + afterHook { result()?.let { registerReceiver(it) } } + } + } /** 干掉原生错误对话框 - 如果有 */ ErrorDialogControllerClass.hook { injectMember { @@ -229,8 +257,6 @@ object FrameworkHooker : YukiBaseHooker() { /** 是否短时内重复错误 */ val isRepeating = AppErrorDialog_DataClass.clazz.field { name = "repeating" }.get(errData).boolean() - /** 注册广播 */ - registerReceiver(context) /** 打印错误日志 */ loggerE(msg = "Process \"$packageName\" has crashed${if (isRepeating) " again" else ""}") /** 判断是否被忽略 - 在后台就不显示对话框 */ @@ -317,20 +343,26 @@ object FrameworkHooker : YukiBaseHooker() { /** 当前异常信息 */ args().last().cast()?.also { crashInfo -> /** 添加到第一位 */ - appErrorsRecords.add( - 0, AppErrorsInfoBean( - packageName = appInfo?.packageName ?: "", - isNativeCrash = crashInfo.exceptionClassName.lowercase() == "native crash", - exceptionClassName = crashInfo.exceptionClassName ?: "", - exceptionMessage = crashInfo.exceptionMessage ?: "", - throwFileName = crashInfo.throwFileName ?: "", - throwClassName = crashInfo.throwClassName ?: "", - throwMethodName = crashInfo.throwMethodName ?: "", - throwLineNumber = crashInfo.throwLineNumber, - stackTrace = crashInfo.stackTrace?.trim() ?: "", - timestamp = System.currentTimeMillis() + (crashInfo.exceptionClassName.lowercase() == "native crash").also { isNativeCrash -> + appErrorsRecords.add( + 0, AppErrorsInfoBean( + packageName = appInfo?.packageName ?: "", + isNativeCrash = isNativeCrash, + exceptionClassName = crashInfo.exceptionClassName ?: "", + exceptionMessage = if (isNativeCrash) crashInfo.stackTrace.let { + if (it.contains(other = "Abort message: '")) + runCatching { it.split("Abort message: '")[1].split("'")[0] }.getOrNull() + ?: crashInfo.exceptionMessage ?: "" else crashInfo.exceptionMessage ?: "" + } else crashInfo.exceptionMessage ?: "", + throwFileName = crashInfo.throwFileName ?: "", + throwClassName = crashInfo.throwClassName ?: "", + throwMethodName = crashInfo.throwMethodName ?: "", + throwLineNumber = crashInfo.throwLineNumber, + stackTrace = crashInfo.stackTrace?.trim() ?: "", + timestamp = System.currentTimeMillis() + ) ) - ) + } } } } diff --git a/app/src/main/java/com/fankes/apperrorstracking/service/QuickStartTileService.kt b/app/src/main/java/com/fankes/apperrorstracking/service/QuickStartTileService.kt new file mode 100644 index 0000000..181b930 --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/service/QuickStartTileService.kt @@ -0,0 +1,35 @@ +/* + * 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. + */ +package com.fankes.apperrorstracking.service + +import android.service.quicksettings.TileService +import com.fankes.apperrorstracking.ui.activity.AppErrorsRecordActivity +import com.fankes.apperrorstracking.utils.factory.navigate + +class QuickStartTileService : TileService() { + + override fun onClick() { + super.onClick() + /** 启动异常历史记录窗口 */ + navigate() + } +} \ 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 de080b1..18de33a 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 @@ -29,8 +29,8 @@ import android.content.Intent import androidx.core.view.isGone import com.fankes.apperrorstracking.R import com.fankes.apperrorstracking.bean.AppErrorsInfoBean +import com.fankes.apperrorstracking.const.Const 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.* @@ -52,7 +52,7 @@ class AppErrorsDetailActivity : BaseActivity() { * @param isOutSide 是否从外部启动 */ fun start(context: Context, appErrorsInfo: AppErrorsInfoBean, isOutSide: Boolean = false) = - context.navigate(isOutSide) { putExtra(FrameworkHooker.APP_ERRORS_INFO, appErrorsInfo) } + context.navigate(isOutSide) { putExtra(Const.EXTRA_APP_ERRORS_INFO, appErrorsInfo) } } /** 预导出的异常堆栈 */ @@ -60,7 +60,7 @@ class AppErrorsDetailActivity : BaseActivity() { override fun onCreate() { val appErrorsInfo = - intent?.getSerializableExtra(FrameworkHooker.APP_ERRORS_INFO) as? AppErrorsInfoBean ?: return toastAndFinish() + intent?.getSerializableExtra(Const.EXTRA_APP_ERRORS_INFO) as? AppErrorsInfoBean ?: return toastAndFinish() /** 创建异常堆栈模板 */ fun createStack() = @@ -113,7 +113,7 @@ class AppErrorsDetailActivity : BaseActivity() { } override fun onBackPressed() { - intent?.removeExtra(FrameworkHooker.APP_ERRORS_INFO) + intent?.removeExtra(Const.EXTRA_APP_ERRORS_INFO) finish() } } \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsRecordActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsRecordActivity.kt new file mode 100644 index 0000000..ccb9107 --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsRecordActivity.kt @@ -0,0 +1,142 @@ +/* + * 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("UNCHECKED_CAST") + +package com.fankes.apperrorstracking.ui.activity + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import androidx.core.view.isVisible +import com.fankes.apperrorstracking.R +import com.fankes.apperrorstracking.bean.AppErrorsInfoBean +import com.fankes.apperrorstracking.const.Const +import com.fankes.apperrorstracking.databinding.ActivityAppErrorsRecordBinding +import com.fankes.apperrorstracking.databinding.AdapterAppErrorsRecordBinding +import com.fankes.apperrorstracking.ui.activity.base.BaseActivity +import com.fankes.apperrorstracking.utils.factory.appIcon +import com.fankes.apperrorstracking.utils.factory.appName +import com.fankes.apperrorstracking.utils.factory.toast +import java.text.SimpleDateFormat +import java.util.* + +class AppErrorsRecordActivity : BaseActivity() { + + /** 回调适配器改变 */ + private var onChanged: (() -> Unit)? = null + + /** 全部的 APP 异常数据 */ + private val listData = ArrayList() + + override fun onCreate() { + binding.titleBackIcon.setOnClickListener { onBackPressed() } + binding.clearAllIcon.setOnClickListener { + // TODO 待实现 + toast(msg = "Coming soon") + } + binding.exportAllIcon.setOnClickListener { + // TODO 待实现 + toast(msg = "Coming soon") + } + /** 设置列表元素和 Adapter */ + binding.listView.apply { + adapter = object : BaseAdapter() { + + override fun getCount() = listData.size + + override fun getItem(position: Int) = listData[position] + + override fun getItemId(position: Int) = position.toLong() + + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + var cView = convertView + val holder: AdapterAppErrorsRecordBinding + if (convertView == null) { + holder = AdapterAppErrorsRecordBinding.inflate(LayoutInflater.from(context)) + cView = holder.root + cView.tag = holder + } else holder = convertView.tag as AdapterAppErrorsRecordBinding + getItem(position).also { + holder.appIcon.setImageDrawable(appIcon(it.packageName)) + holder.appNameText.text = appName(it.packageName) + holder.errorsTimeText.text = SimpleDateFormat.getDateTimeInstance().format(Date(it.timestamp)) + holder.errorTypeIcon.setImageResource(if (it.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java) + holder.errorTypeText.text = if (it.isNativeCrash) "Native crash" else it.exceptionClassName.let { text -> + if (text.contains(other = ".")) text.split(".").let { e -> e[e.lastIndex] } else text + } + holder.errorMsgText.text = it.exceptionMessage + } + return cView!! + } + }.apply { + setOnItemClickListener { _, _, p, _ -> AppErrorsDetailActivity.start(context, listData[p]) } + onChanged = { notifyDataSetChanged() } + } + } + /** 注册广播 */ + registerReceiver(moduleHandlerReceiver, IntentFilter().apply { addAction(Const.ACTION_MODULE_HANDLER_RECEIVER) }) + } + + /** 更新列表数据 */ + private fun refreshData() { + sendBroadcast(Intent().apply { + action = Const.ACTION_HOST_HANDLER_RECEIVER + putExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL, Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA) + }) + } + + /** 模块广播接收器 */ + private val moduleHandlerReceiver by lazy { + object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null) return + when (intent.getStringExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL)) { + Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA -> + (intent.getSerializableExtra(Const.TAG_APP_ERRORS_DATA_CONTENT) as? ArrayList)?.also { + listData.clear() + it.takeIf { e -> e.isNotEmpty() }?.forEach { e -> listData.add(e) } + onChanged?.invoke() + binding.listView.isVisible = listData.isNotEmpty() + binding.listNoDataView.isVisible = listData.isEmpty() + } + } + } + } + } + + override fun onResume() { + super.onResume() + /** 执行更新 */ + refreshData() + } + + override fun onDestroy() { + super.onDestroy() + /** 取消注册 */ + unregisterReceiver(moduleHandlerReceiver) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-night/bg_permotion_ripple.xml b/app/src/main/res/drawable-night/bg_permotion_ripple.xml new file mode 100644 index 0000000..3f27677 --- /dev/null +++ b/app/src/main/res/drawable-night/bg_permotion_ripple.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_permotion_ripple.xml b/app/src/main/res/drawable/bg_permotion_ripple.xml new file mode 100644 index 0000000..260d905 --- /dev/null +++ b/app/src/main/res/drawable/bg_permotion_ripple.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_red_round.xml b/app/src/main/res/drawable/bg_red_round.xml new file mode 100644 index 0000000..ca97bfa --- /dev/null +++ b/app/src/main/res/drawable/bg_red_round.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_clear.xml b/app/src/main/res/drawable/ic_clear.xml new file mode 100644 index 0000000..228564d --- /dev/null +++ b/app/src/main/res/drawable/ic_clear.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_debug.xml b/app/src/main/res/drawable/ic_debug.xml new file mode 100644 index 0000000..4d71d83 --- /dev/null +++ b/app/src/main/res/drawable/ic_debug.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_app_errors_record.xml b/app/src/main/res/layout/activity_app_errors_record.xml new file mode 100644 index 0000000..283c61b --- /dev/null +++ b/app/src/main/res/layout/activity_app_errors_record.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_app_errors_record.xml b/app/src/main/res/layout/adapter_app_errors_record.xml new file mode 100644 index 0000000..08467f5 --- /dev/null +++ b/app/src/main/res/layout/adapter_app_errors_record.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ac0519f..f99058f 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -28,4 +28,8 @@ コンソールに印刷されていました エクスポートされたエラースタックされていました エラースタックのエクスポートに失敗しました + すべてエクスポート + すべてクリア + エラー履歴記錄 + 一時的にエラーなデータレコードはありません \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 5b3c17c..3ac827e 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -28,4 +28,8 @@ 已打印到控制台 已导出异常堆栈 导出异常堆栈失败 + 导出全部 + 清空全部 + 异常历史记录 + 暂时没有异常数据记录 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 4381c3e..cd5c002 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -28,4 +28,8 @@ 已打印到控制台 已導出異常堆棧 導出異常堆棧失敗 + 導出全部 + 清空全部 + 異常歷史記錄 + 暫時沒有異常數據紀錄 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rMO/strings.xml b/app/src/main/res/values-zh-rMO/strings.xml index 2d73e9f..f333430 100644 --- a/app/src/main/res/values-zh-rMO/strings.xml +++ b/app/src/main/res/values-zh-rMO/strings.xml @@ -28,4 +28,8 @@ 已打印到控制台 已導出異常堆棧 導出異常堆棧失敗 + 導出全部 + 清空全部 + 異常歷史記錄 + 暫時沒有異常數據紀錄 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 4789ad6..d3bf8fc 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -28,4 +28,8 @@ 已打印到控制台 已導出異常堆棧 導出異常堆棧失敗 + 導出全部 + 清空全部 + 異常歷史記錄 + 暫時沒有異常數據紀錄 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9e61d6c..681f069 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -27,4 +27,8 @@ Printed to logcat Export exception stack succeeded Failed to export exception stack + Export all + Clear all + AppErrorsRecord + No errors data for now \ No newline at end of file