Added App errors statistics function

This commit is contained in:
2022-06-11 01:31:03 +08:00
parent f5bbc312e5
commit d0f976f843
13 changed files with 338 additions and 5 deletions

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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<ActivityAppErrorsRecordBinding>() {
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<DiaAppErrorsStatisticsBinding> {
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<ActivityAppErrorsRecordBinding>() {
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<ActivityAppErrorsRecordBinding>() {
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<ActivityAppErrorsRecordBinding>() {
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)

View File

@@ -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 提示内容

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#ffffff"
android:pathData="M446.2,566.7h369.3c-2.9,103.8 -39.4,190.7 -109.4,260.7s-156.6,106.4 -259.9,109.4c-103.8,-2.9 -190.7,-39.4 -260.7,-109.4s-106.4,-156.9 -109.4,-260.7c2.9,-103.2 39.4,-189.9 109.4,-259.9S342.4,200.4 446.2,197.5v369.2zM829.5,183.5c70,70 106.4,156.9 109.4,260.7L568.7,444.2L568.7,74.1c103.9,2.9 190.8,39.4 260.8,109.4z" />
</vector>

View File

@@ -40,6 +40,16 @@
android:textSize="19sp"
android:textStyle="bold" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/app_error_sis_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="15dp"
android:src="@drawable/ic_statistics"
android:tint="@color/colorTextGray"
android:tooltipText="@string/app_errors_statistics"
android:visibility="gone" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/clear_all_icon"
android:layout_width="26dp"

View File

@@ -0,0 +1,169 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="15dp"
android:paddingRight="15dp"
tools:ignore="ContentDescription,SmallSp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8.5dp"
android:layout_marginRight="8.5dp"
android:layout_marginBottom="15dp"
android:gravity="center|start">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/total_errors"
android:textSize="14sp" />
<TextView
android:id="@+id/total_errors_unit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/total_errors_unit"
android:textSize="14sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8.5dp"
android:layout_marginRight="8.5dp"
android:layout_marginBottom="15dp"
android:gravity="center|start">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/total_apps"
android:textSize="14sp" />
<TextView
android:id="@+id/total_apps_unit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/total_apps_unit"
android:textSize="14sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8.5dp"
android:layout_marginRight="8.5dp"
android:layout_marginBottom="15dp"
android:gravity="center|start">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/most_errors_app"
android:textSize="14sp" />
<androidx.cardview.widget.CardView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="5dp"
app:cardBackgroundColor="@color/trans"
app:cardCornerRadius="3.5dp"
app:cardElevation="0dp">
<ImageView
android:id="@+id/most_errors_app_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_android" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/most_errors_app_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textSize="14sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8.5dp"
android:layout_marginRight="8.5dp"
android:layout_marginBottom="15dp"
android:gravity="center|start">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/most_errors_type"
android:textSize="14sp" />
<TextView
android:id="@+id/most_errors_type_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/bg_red_round"
android:ellipsize="end"
android:paddingLeft="3dp"
android:paddingTop="0.5dp"
android:paddingRight="3dp"
android:paddingBottom="0.5dp"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="10sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8.5dp"
android:layout_marginRight="8.5dp"
android:gravity="center|start">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/total_proportion_of_errors"
android:textSize="14sp" />
<TextView
android:id="@+id/total_ppt_of_errors_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textSize="14sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>

View File

@@ -99,4 +99,13 @@
<string name="show_errors_dialog">エラーダイアログを表示</string>
<string name="show_errors_toast">エラー Toast を表示</string>
<string name="show_nothing">何も表示されません</string>
<string name="app_errors_statistics">アプリのエラー統計</string>
<string name="total_errors">エラーの総数</string>
<string name="total_apps">アプリの総数</string>
<string name="most_errors_app">エラーが最も多いアプリ</string>
<string name="most_errors_type">エラーが最も多いタイプ</string>
<string name="total_proportion_of_errors">エラーの合計割合</string>
<string name="total_errors_unit">%1$s 個</string>
<string name="total_apps_unit">%1$s 個 (システムアプリを含む)</string>
<string name="generating_statistics">統計が生成されています</string>
</resources>

View File

@@ -99,4 +99,13 @@
<string name="show_errors_dialog">显示错误对话框</string>
<string name="show_errors_toast">显示错误 Toast 提示</string>
<string name="show_nothing">不显示任何提示</string>
<string name="app_errors_statistics">应用异常统计</string>
<string name="total_errors">异常总数</string>
<string name="total_apps">应用总数</string>
<string name="most_errors_app">最多异常的应用</string>
<string name="most_errors_type">最多异常的类型</string>
<string name="total_proportion_of_errors">异常总占比</string>
<string name="total_errors_unit">%1$s 条</string>
<string name="total_apps_unit">%1$s 个 (含系统应用)</string>
<string name="generating_statistics">正在生成统计数据</string>
</resources>

View File

@@ -99,4 +99,13 @@
<string name="show_errors_dialog">顯示錯誤對話框</string>
<string name="show_errors_toast">顯示錯誤 Toast 提示</string>
<string name="show_nothing">不顯示任何提示</string>
<string name="app_errors_statistics">程式異常統計</string>
<string name="total_errors">異常總數</string>
<string name="total_apps">程式總數</string>
<string name="most_errors_app">最多異常的程式</string>
<string name="most_errors_type">最多異常的類型</string>
<string name="total_proportion_of_errors">異常總佔比</string>
<string name="total_errors_unit">%1$s 條</string>
<string name="total_apps_unit">%1$s 個 (含系統程式)</string>
<string name="generating_statistics">統計數據生成中</string>
</resources>

View File

@@ -99,4 +99,13 @@
<string name="show_errors_dialog">顯示錯誤對話框</string>
<string name="show_errors_toast">顯示錯誤 Toast 提示</string>
<string name="show_nothing">不顯示任何提示</string>
<string name="app_errors_statistics">程式異常統計</string>
<string name="total_errors">異常總數</string>
<string name="total_apps">程式總數</string>
<string name="most_errors_app">最多異常的程式</string>
<string name="most_errors_type">最多異常的類型</string>
<string name="total_proportion_of_errors">異常總佔比</string>
<string name="total_errors_unit">%1$s 條</string>
<string name="total_apps_unit">%1$s 個 (含系統程式)</string>
<string name="generating_statistics">統計數據生成中</string>
</resources>

View File

@@ -99,4 +99,13 @@
<string name="show_errors_dialog">顯示錯誤對話框</string>
<string name="show_errors_toast">顯示錯誤 Toast 提示</string>
<string name="show_nothing">不顯示任何提示</string>
<string name="app_errors_statistics">程式異常統計</string>
<string name="total_errors">異常總數</string>
<string name="total_apps">程式總數</string>
<string name="most_errors_app">最多異常的程式</string>
<string name="most_errors_type">最多異常的類型</string>
<string name="total_proportion_of_errors">異常總佔比</string>
<string name="total_errors_unit">%1$s 條</string>
<string name="total_apps_unit">%1$s 個 (含系統程式)</string>
<string name="generating_statistics">統計數據生成中</string>
</resources>

View File

@@ -99,4 +99,13 @@
<string name="show_errors_dialog">Show errors dialog</string>
<string name="show_errors_toast">Show errors Toast</string>
<string name="show_nothing">Don\'t show anything</string>
<string name="app_errors_statistics">App errors statistics</string>
<string name="total_errors">Total errors</string>
<string name="total_apps">Total apps</string>
<string name="most_errors_app">The most errors app</string>
<string name="most_errors_type">The most errors type</string>
<string name="total_proportion_of_errors">Total proportion of errors</string>
<string name="total_errors_unit">%1$s</string>
<string name="total_apps_unit">%1$s (including system apps)</string>
<string name="generating_statistics">Generating statistics</string>
</resources>