mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-04 02:05:16 +08:00
Added multi-user display app's user id feature
This commit is contained in:
@@ -32,6 +32,7 @@ import java.util.*
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用异常信息 bean
|
* 应用异常信息 bean
|
||||||
|
* @param userId 用户 ID
|
||||||
* @param packageName 包名
|
* @param packageName 包名
|
||||||
* @param isNativeCrash 是否为原生层异常
|
* @param isNativeCrash 是否为原生层异常
|
||||||
* @param exceptionClassName 异常类名
|
* @param exceptionClassName 异常类名
|
||||||
@@ -44,6 +45,7 @@ import java.util.*
|
|||||||
* @param timestamp 记录时间戳
|
* @param timestamp 记录时间戳
|
||||||
*/
|
*/
|
||||||
data class AppErrorsInfoBean(
|
data class AppErrorsInfoBean(
|
||||||
|
@Keep var userId: Int,
|
||||||
@Keep var packageName: String,
|
@Keep var packageName: String,
|
||||||
@Keep var isNativeCrash: Boolean,
|
@Keep var isNativeCrash: Boolean,
|
||||||
@Keep var exceptionClassName: String,
|
@Keep var exceptionClassName: String,
|
||||||
@@ -61,12 +63,14 @@ data class AppErrorsInfoBean(
|
|||||||
/**
|
/**
|
||||||
* 从 [ApplicationErrorReport.CrashInfo] 克隆
|
* 从 [ApplicationErrorReport.CrashInfo] 克隆
|
||||||
* @param packageName APP 包名
|
* @param packageName APP 包名
|
||||||
|
* @param userId APP 用户 ID
|
||||||
* @param crashInfo [ApplicationErrorReport.CrashInfo]
|
* @param crashInfo [ApplicationErrorReport.CrashInfo]
|
||||||
* @return [AppErrorsInfoBean]
|
* @return [AppErrorsInfoBean]
|
||||||
*/
|
*/
|
||||||
fun clone(packageName: String?, crashInfo: ApplicationErrorReport.CrashInfo?) =
|
fun clone(packageName: String?, userId: Int?, crashInfo: ApplicationErrorReport.CrashInfo?) =
|
||||||
(crashInfo?.exceptionClassName?.lowercase() == "native crash").let { isNativeCrash ->
|
(crashInfo?.exceptionClassName?.lowercase() == "native crash").let { isNativeCrash ->
|
||||||
AppErrorsInfoBean(
|
AppErrorsInfoBean(
|
||||||
|
userId = userId ?: 0,
|
||||||
packageName = packageName ?: "unknown",
|
packageName = packageName ?: "unknown",
|
||||||
isNativeCrash = isNativeCrash,
|
isNativeCrash = isNativeCrash,
|
||||||
exceptionClassName = crashInfo?.exceptionClassName ?: "unknown",
|
exceptionClassName = crashInfo?.exceptionClassName ?: "unknown",
|
||||||
@@ -122,6 +126,7 @@ data class AppErrorsInfoBean(
|
|||||||
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
|
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
|
||||||
"[System Locale]: ${Locale.getDefault()}\n" +
|
"[System Locale]: ${Locale.getDefault()}\n" +
|
||||||
"[Package Name]: $packageName\n" +
|
"[Package Name]: $packageName\n" +
|
||||||
|
(if (userId > 0) "[User Id]: $userId\n" else "") +
|
||||||
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
|
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
|
||||||
"[Crash Time]: $dateTime\n" +
|
"[Crash Time]: $dateTime\n" +
|
||||||
"[Stack Trace]:\n" + stackTrace
|
"[Stack Trace]:\n" + stackTrace
|
||||||
@@ -142,6 +147,7 @@ data class AppErrorsInfoBean(
|
|||||||
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
|
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
|
||||||
"[System Locale]: ${Locale.getDefault()}\n" +
|
"[System Locale]: ${Locale.getDefault()}\n" +
|
||||||
"[Package Name]: $packageName\n" +
|
"[Package Name]: $packageName\n" +
|
||||||
|
(if (userId > 0) "[User Id]: $userId\n" else "") +
|
||||||
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
|
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
|
||||||
"[Crash Time]: $dateTime\n" +
|
"[Crash Time]: $dateTime\n" +
|
||||||
"[Stack Trace]:\n" + stackTrace
|
"[Stack Trace]:\n" + stackTrace
|
||||||
|
@@ -75,6 +75,9 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
"com.android.server.am.ErrorDialogController"
|
"com.android.server.am.ErrorDialogController"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** 已记录的 APP 用户 ID */
|
||||||
|
private var appUserIdRecords = HashMap<String, Int>()
|
||||||
|
|
||||||
/** 已忽略错误的 APP 数组 - 直到重新解锁 */
|
/** 已忽略错误的 APP 数组 - 直到重新解锁 */
|
||||||
private var mutedErrorsIfUnlockApps = HashSet<String>()
|
private var mutedErrorsIfUnlockApps = HashSet<String>()
|
||||||
|
|
||||||
@@ -250,10 +253,12 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
|
|
||||||
/** 崩溃标题 */
|
/** 崩溃标题 */
|
||||||
val errorTitle = if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName)
|
val errorTitle = if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName)
|
||||||
|
/** 写入到用户 ID 记录 */
|
||||||
|
appUserIdRecords[proc.toString()] = userId
|
||||||
/** 打印错误日志 */
|
/** 打印错误日志 */
|
||||||
if (isApp) loggerE(
|
if (isApp) loggerE(
|
||||||
msg = "App \"$packageName\"${if (packageName != processName) " --process \"$processName\"" else ""}" +
|
msg = "App \"$packageName\"${if (packageName != processName) " --process \"$processName\"" else ""}" +
|
||||||
" has crashed${if (isRepeating) " again" else ""}"
|
"${if (userId != 0) " --user $userId" else ""} has crashed${if (isRepeating) " again" else ""}"
|
||||||
) else loggerE(msg = "Process \"$processName\" has crashed${if (isRepeating) " again" else ""}")
|
) else loggerE(msg = "Process \"$processName\" has crashed${if (isRepeating) " again" else ""}")
|
||||||
/** 判断是否为模块自身崩溃 */
|
/** 判断是否为模块自身崩溃 */
|
||||||
if (packageName == BuildConfig.APPLICATION_ID) {
|
if (packageName == BuildConfig.APPLICATION_ID) {
|
||||||
@@ -308,10 +313,15 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
paramCount = 2
|
paramCount = 2
|
||||||
}
|
}
|
||||||
afterHook {
|
afterHook {
|
||||||
|
/** 当前进程信息 */
|
||||||
|
val proc = args().first().any()
|
||||||
|
|
||||||
/** 当前 APP 信息 */
|
/** 当前 APP 信息 */
|
||||||
val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(args().first().any()).cast<ApplicationInfo>()
|
val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(proc).cast<ApplicationInfo>()
|
||||||
/** 添加当前异常信息到第一位 */
|
/** 添加当前异常信息到第一位 */
|
||||||
appErrorsRecords.add(0, AppErrorsInfoBean.clone(appInfo?.packageName, args().last().cast()))
|
appErrorsRecords.add(
|
||||||
|
0, AppErrorsInfoBean.clone(appInfo?.packageName, appUserIdRecords[proc.toString()], args().last().cast())
|
||||||
|
)
|
||||||
/** 保存异常记录到本地 */
|
/** 保存异常记录到本地 */
|
||||||
saveAllAppErrorsRecords()
|
saveAllAppErrorsRecords()
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import com.fankes.apperrorstracking.R
|
import com.fankes.apperrorstracking.R
|
||||||
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
||||||
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDetailBinding
|
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDetailBinding
|
||||||
@@ -86,6 +87,8 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
|||||||
binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName))
|
binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName))
|
||||||
binding.appNameText.text = appNameOf(appErrorsInfo.packageName)
|
binding.appNameText.text = appNameOf(appErrorsInfo.packageName)
|
||||||
binding.appVersionText.text = appVersionBrandOf(appErrorsInfo.packageName)
|
binding.appVersionText.text = appVersionBrandOf(appErrorsInfo.packageName)
|
||||||
|
binding.appUserIdText.isVisible = appErrorsInfo.userId > 0
|
||||||
|
binding.appUserIdText.text = LocaleString.userId(appErrorsInfo.userId)
|
||||||
binding.appAbiText.text = appCpuAbiOf(appErrorsInfo.packageName).ifBlank { LocaleString.noCpuAbi }
|
binding.appAbiText.text = appCpuAbiOf(appErrorsInfo.packageName).ifBlank { LocaleString.noCpuAbi }
|
||||||
binding.jvmErrorPanel.isGone = appErrorsInfo.isNativeCrash
|
binding.jvmErrorPanel.isGone = appErrorsInfo.isNativeCrash
|
||||||
binding.errorTypeIcon.setImageResource(if (appErrorsInfo.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
|
binding.errorTypeIcon.setImageResource(if (appErrorsInfo.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
|
||||||
|
@@ -134,6 +134,8 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
|||||||
listData[position].also { bean ->
|
listData[position].also { bean ->
|
||||||
binding.appIcon.setImageDrawable(appIconOf(bean.packageName))
|
binding.appIcon.setImageDrawable(appIconOf(bean.packageName))
|
||||||
binding.appNameText.text = appNameOf(bean.packageName)
|
binding.appNameText.text = appNameOf(bean.packageName)
|
||||||
|
binding.appUserIdText.isVisible = bean.userId > 0
|
||||||
|
binding.appUserIdText.text = LocaleString.userId(bean.userId)
|
||||||
binding.errorsTimeText.text = bean.crossTime
|
binding.errorsTimeText.text = bean.crossTime
|
||||||
binding.errorTypeIcon.setImageResource(if (bean.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
|
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.simpleThwName()
|
binding.errorTypeText.text = if (bean.isNativeCrash) "Native crash" else bean.exceptionClassName.simpleThwName()
|
||||||
|
6
app/src/main/res/drawable/bg_blue_round.xml
Normal file
6
app/src/main/res/drawable/bg_blue_round.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#2196F3" />
|
||||||
|
<corners android:radius="5dp" />
|
||||||
|
</shape>
|
@@ -151,6 +151,23 @@
|
|||||||
android:textColor="@color/colorTextGray"
|
android:textColor="@color/colorTextGray"
|
||||||
android:textSize="15sp" />
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/app_user_id_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="5dp"
|
||||||
|
android:background="@drawable/bg_blue_round"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:paddingLeft="3dp"
|
||||||
|
android:paddingTop="0.5dp"
|
||||||
|
android:paddingRight="3dp"
|
||||||
|
android:paddingBottom="0.5dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/user_id"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="9sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="13dp"
|
android:layout_width="13dp"
|
||||||
android:layout_height="13dp"
|
android:layout_height="13dp"
|
||||||
|
@@ -39,16 +39,39 @@
|
|||||||
android:gravity="center|start"
|
android:gravity="center|start"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/app_name_text"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="5dp"
|
|
||||||
android:layout_weight="0.75"
|
android:layout_weight="0.75"
|
||||||
android:ellipsize="end"
|
android:gravity="center|start">
|
||||||
android:singleLine="true"
|
|
||||||
android:textColor="@color/colorTextGray"
|
<TextView
|
||||||
android:textSize="15sp" />
|
android:id="@+id/app_name_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="5dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/colorTextGray"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/app_user_id_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="5dp"
|
||||||
|
android:background="@drawable/bg_blue_round"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:paddingLeft="3dp"
|
||||||
|
android:paddingTop="0.5dp"
|
||||||
|
android:paddingRight="3dp"
|
||||||
|
android:paddingBottom="0.5dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/user_id"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="9sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/errors_time_text"
|
android:id="@+id/errors_time_text"
|
||||||
|
Reference in New Issue
Block a user