diff --git a/README.md b/README.md index ed8ec16..cc21c0a 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ Added more features to app's crash dialog, fixed custom rom deleted dialog, the - 异常历史记录功能,可通过通知栏磁贴“异常历史记录”进入和模块主界面进入 +- 应用异常统计功能 + - 多进程 APP 的异常将会显示异常的进程名 - 支持 Android 10 及以上系统的深色模式 @@ -54,7 +56,7 @@ Added more features to app's crash dialog, fixed custom rom deleted dialog, the 此项目依然在开发中,正在开发的功能如下 -- 应用异常统计信息功能 +- 是否始终允许错误对话框显示“重新打开”按钮 (计划中) ## License diff --git a/app/src/main/java/com/fankes/apperrorstracking/locale/LocaleString.kt b/app/src/main/java/com/fankes/apperrorstracking/locale/LocaleString.kt index addaf41..8ebb1f7 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/locale/LocaleString.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/locale/LocaleString.kt @@ -388,4 +388,28 @@ object LocaleString { /** @string Automatic generated */ fun showNothing(vararg objArrs: Any) = R.string.show_nothing.bind(*objArrs) + + /** @string Automatic generated */ + val appErrorsStatistics get() = appErrorsStatistics() + + /** @string Automatic generated */ + fun appErrorsStatistics(vararg objArrs: Any) = R.string.app_errors_statistics.bind(*objArrs) + + /** @string Automatic generated */ + val totalErrorsUnit get() = totalErrorsUnit() + + /** @string Automatic generated */ + fun totalErrorsUnit(vararg objArrs: Any) = R.string.total_errors_unit.bind(*objArrs) + + /** @string Automatic generated */ + val totalAppsUnit get() = totalAppsUnit() + + /** @string Automatic generated */ + fun totalAppsUnit(vararg objArrs: Any) = R.string.total_apps_unit.bind(*objArrs) + + /** @string Automatic generated */ + val generatingStatistics get() = generatingStatistics() + + /** @string Automatic generated */ + fun generatingStatistics(vararg objArrs: Any) = R.string.generating_statistics.bind(*objArrs) } \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsRecordActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsRecordActivity.kt index 3307bbb..6b36fda 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsRecordActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsRecordActivity.kt @@ -19,7 +19,7 @@ * * This file is Created by fankes on 2022/5/11. */ -@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") +@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION", "SetTextI18n") package com.fankes.apperrorstracking.ui.activity.errors @@ -32,8 +32,10 @@ import android.widget.AdapterView.AdapterContextMenuInfo import androidx.core.view.isVisible import com.fankes.apperrorstracking.R import com.fankes.apperrorstracking.bean.AppErrorsInfoBean +import com.fankes.apperrorstracking.bean.AppFiltersBean import com.fankes.apperrorstracking.databinding.ActivityAppErrorsRecordBinding import com.fankes.apperrorstracking.databinding.AdapterAppErrorsRecordBinding +import com.fankes.apperrorstracking.databinding.DiaAppErrorsStatisticsBinding import com.fankes.apperrorstracking.locale.LocaleString import com.fankes.apperrorstracking.ui.activity.base.BaseActivity import com.fankes.apperrorstracking.utils.factory.* @@ -61,6 +63,40 @@ class AppErrorsRecordActivity : BaseActivity() { override fun onCreate() { binding.titleBackIcon.setOnClickListener { onBackPressed() } + binding.appErrorSisIcon.setOnClickListener { + showDialog { + title = LocaleString.notice + progressContent = LocaleString.generatingStatistics + noCancelable() + FrameworkTool.fetchAppListData(context, AppFiltersBean(isContainsSystem = true)) { + Thread { + val errorsApps = listData.groupBy { it.packageName } + .map { it.key to it.value.size } + .sortedByDescending { it.second } + .takeIf { it.isNotEmpty() } + val mostAppPackageName = errorsApps?.get(0)?.first ?: "" + val mostErrorsType = listData.groupBy { it.exceptionClassName } + .map { it.key to it.value.size } + .sortedByDescending { it.second } + .takeIf { it.isNotEmpty() }?.get(0)?.first?.simpleThwName() ?: "" + val pptCount = (((errorsApps?.size?.toFloat() ?: 0f) * 100f) / it.size.toFloat()).decimal() + runOnUiThread { + cancel() + showDialog { + title = LocaleString.appErrorsStatistics + binding.totalErrorsUnitText.text = LocaleString.totalErrorsUnit(listData.size) + binding.totalAppsUnitText.text = LocaleString.totalAppsUnit(it.size) + binding.mostErrorsAppIcon.setImageDrawable(appIcon(mostAppPackageName)) + binding.mostErrorsAppText.text = appName(mostAppPackageName) + binding.mostErrorsTypeText.text = mostErrorsType + binding.totalPptOfErrorsText.text = "$pptCount%" + confirmButton(LocaleString.gotIt) + } + } + }.start() + } + } + } binding.clearAllIcon.setOnClickListener { showDialog { title = LocaleString.notice @@ -92,9 +128,7 @@ class AppErrorsRecordActivity : BaseActivity() { binding.appNameText.text = appName(bean.packageName) binding.errorsTimeText.text = bean.crossTime binding.errorTypeIcon.setImageResource(if (bean.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java) - binding.errorTypeText.text = if (bean.isNativeCrash) "Native crash" else bean.exceptionClassName.let { text -> - if (text.contains(other = ".")) text.split(".").let { e -> e[e.lastIndex] } else text - } + binding.errorTypeText.text = if (bean.isNativeCrash) "Native crash" else bean.exceptionClassName.simpleThwName() binding.errorMsgText.text = bean.exceptionMessage } } @@ -110,6 +144,7 @@ class AppErrorsRecordActivity : BaseActivity() { listData.clear() it.takeIf { e -> e.isNotEmpty() }?.forEach { e -> listData.add(e) } onChanged?.invoke() + binding.appErrorSisIcon.isVisible = listData.isNotEmpty() binding.clearAllIcon.isVisible = listData.isNotEmpty() binding.exportAllIcon.isVisible = listData.isNotEmpty() binding.listView.isVisible = listData.isNotEmpty() @@ -143,6 +178,13 @@ class AppErrorsRecordActivity : BaseActivity() { cacheDir.mkdirs() } + /** + * 获取异常的精简名称 + * @return [String] + */ + private fun String.simpleThwName() = + let { text -> if (text.contains(other = ".")) text.split(".").let { e -> e[e.lastIndex] } else text } + override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) { menuInflater.inflate(R.menu.menu_list_detail_action, menu) super.onCreateContextMenu(menu, v, menuInfo) 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 0c9a2d5..32ec02d 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 @@ -41,6 +41,8 @@ import com.google.android.material.snackbar.Snackbar import com.highcapable.yukihookapi.hook.factory.field import com.highcapable.yukihookapi.hook.type.android.ApplicationInfoClass import com.topjohnwu.superuser.Shell +import java.math.RoundingMode +import java.text.DecimalFormat /** * 系统深色模式是否开启 @@ -139,6 +141,27 @@ fun Long.difference(now: String, second: String, minute: String, hour: String, d } } +/** + * 保留小数 + * @param count 要保留的位数 - 默认 2 位 - 最多 7 位 + * @return [String] 得到的字符串数字 - 格式化失败返回原始数字的字符串 + */ +fun Number.decimal(count: Int = 2) = runCatching { + DecimalFormat( + when (count) { + 0 -> "0" + 1 -> "0.0" + 2 -> "0.00" + 3 -> "0.000" + 4 -> "0.0000" + 5 -> "0.00000" + 6 -> "0.000000" + 7 -> "0.0000000" + else -> "0.0" + } + ).apply { roundingMode = RoundingMode.HALF_UP }.format(this) ?: toString() +}.getOrNull() ?: this + /** * 弹出 [Toast] * @param msg 提示内容 diff --git a/app/src/main/res/drawable/ic_statistics.xml b/app/src/main/res/drawable/ic_statistics.xml new file mode 100644 index 0000000..c5a3721 --- /dev/null +++ b/app/src/main/res/drawable/ic_statistics.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_app_errors_record.xml b/app/src/main/res/layout/activity_app_errors_record.xml index 727bcd4..82d987d 100644 --- a/app/src/main/res/layout/activity_app_errors_record.xml +++ b/app/src/main/res/layout/activity_app_errors_record.xml @@ -40,6 +40,16 @@ android:textSize="19sp" android:textStyle="bold" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 fa78209..f2684c9 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -99,4 +99,13 @@ エラーダイアログを表示 エラー Toast を表示 何も表示されません + アプリのエラー統計 + エラーの総数 + アプリの総数 + エラーが最も多いアプリ + エラーが最も多いタイプ + エラーの合計割合 + %1$s 個 + %1$s 個 (システムアプリを含む) + 統計が生成されています \ 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 027f8a5..a530d75 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -99,4 +99,13 @@ 显示错误对话框 显示错误 Toast 提示 不显示任何提示 + 应用异常统计 + 异常总数 + 应用总数 + 最多异常的应用 + 最多异常的类型 + 异常总占比 + %1$s 条 + %1$s 个 (含系统应用) + 正在生成统计数据 \ 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 d1cbbc7..156dead 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -99,4 +99,13 @@ 顯示錯誤對話框 顯示錯誤 Toast 提示 不顯示任何提示 + 程式異常統計 + 異常總數 + 程式總數 + 最多異常的程式 + 最多異常的類型 + 異常總佔比 + %1$s 條 + %1$s 個 (含系統程式) + 統計數據生成中 \ 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 7df7e72..ff51c76 100644 --- a/app/src/main/res/values-zh-rMO/strings.xml +++ b/app/src/main/res/values-zh-rMO/strings.xml @@ -99,4 +99,13 @@ 顯示錯誤對話框 顯示錯誤 Toast 提示 不顯示任何提示 + 程式異常統計 + 異常總數 + 程式總數 + 最多異常的程式 + 最多異常的類型 + 異常總佔比 + %1$s 條 + %1$s 個 (含系統程式) + 統計數據生成中 \ 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 9e831bd..09b10b7 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -99,4 +99,13 @@ 顯示錯誤對話框 顯示錯誤 Toast 提示 不顯示任何提示 + 程式異常統計 + 異常總數 + 程式總數 + 最多異常的程式 + 最多異常的類型 + 異常總佔比 + %1$s 條 + %1$s 個 (含系統程式) + 統計數據生成中 \ 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 74e8e83..b9bea35 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -99,4 +99,13 @@ Show errors dialog Show errors Toast Don\'t show anything + App errors statistics + Total errors + Total apps + The most errors app + The most errors type + Total proportion of errors + %1$s + %1$s (including system apps) + Generating statistics \ No newline at end of file