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