mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-04 10:15:18 +08:00
Added crossTime and dateTime in AppErrorsInfoBean
This commit is contained in:
@@ -21,7 +21,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.fankes.apperrorstracking.bean
|
package com.fankes.apperrorstracking.bean
|
||||||
|
|
||||||
|
import android.app.ApplicationErrorReport
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import com.fankes.apperrorstracking.locale.LocaleString
|
||||||
|
import com.fankes.apperrorstracking.utils.factory.difference
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -52,11 +55,56 @@ data class AppErrorsInfoBean(
|
|||||||
var timestamp: Long,
|
var timestamp: Long,
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 [ApplicationErrorReport.CrashInfo] 克隆
|
||||||
|
* @param packageName APP 包名
|
||||||
|
* @param crashInfo [ApplicationErrorReport.CrashInfo]
|
||||||
|
* @return [AppErrorsInfoBean]
|
||||||
|
*/
|
||||||
|
fun clone(packageName: String?, crashInfo: ApplicationErrorReport.CrashInfo?) =
|
||||||
|
(crashInfo?.exceptionClassName?.lowercase() == "native crash").let { isNativeCrash ->
|
||||||
|
AppErrorsInfoBean(
|
||||||
|
packageName = packageName ?: "null",
|
||||||
|
isNativeCrash = isNativeCrash,
|
||||||
|
exceptionClassName = crashInfo?.exceptionClassName ?: "null",
|
||||||
|
exceptionMessage = if (isNativeCrash) crashInfo?.stackTrace.let {
|
||||||
|
if (it?.contains(other = "Abort message: '") == true)
|
||||||
|
runCatching { it.split("Abort message: '")[1].split("'")[0] }.getOrNull()
|
||||||
|
?: crashInfo?.exceptionMessage ?: "null"
|
||||||
|
else crashInfo?.exceptionMessage ?: "null"
|
||||||
|
} else crashInfo?.exceptionMessage ?: "null",
|
||||||
|
throwFileName = crashInfo?.throwFileName ?: "null",
|
||||||
|
throwClassName = crashInfo?.throwClassName ?: "null",
|
||||||
|
throwMethodName = crashInfo?.throwMethodName ?: "null",
|
||||||
|
throwLineNumber = crashInfo?.throwLineNumber ?: -1,
|
||||||
|
stackTrace = crashInfo?.stackTrace?.trim() ?: "null",
|
||||||
|
timestamp = System.currentTimeMillis()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取异常本地化时间
|
* 获取异常本地化经过时间
|
||||||
* @return [String]
|
* @return [String]
|
||||||
*/
|
*/
|
||||||
val time get() = SimpleDateFormat.getDateTimeInstance().format(Date(timestamp)) ?: ""
|
val crossTime
|
||||||
|
get() = timestamp.difference(
|
||||||
|
now = LocaleString.momentAgo,
|
||||||
|
second = LocaleString.secondAgo,
|
||||||
|
minute = LocaleString.minuteAgo,
|
||||||
|
hour = LocaleString.hourAgo,
|
||||||
|
day = LocaleString.dayAgo,
|
||||||
|
month = LocaleString.monthAgo,
|
||||||
|
year = LocaleString.yearAgo
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取异常本地化量化时间
|
||||||
|
* @return [String]
|
||||||
|
*/
|
||||||
|
val dateTime get() = SimpleDateFormat.getDateTimeInstance().format(Date(timestamp)) ?: "DateTime not found"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取异常堆栈模板
|
* 获取异常堆栈模板
|
||||||
@@ -74,7 +122,7 @@ data class AppErrorsInfoBean(
|
|||||||
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
|
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
|
||||||
"[Package Name]: $packageName\n" +
|
"[Package Name]: $packageName\n" +
|
||||||
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
|
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
|
||||||
"[Crash Time]: $time\n" +
|
"[Crash Time]: $dateTime\n" +
|
||||||
"[Stack Trace]:\n" +
|
"[Stack Trace]:\n" +
|
||||||
stackTrace
|
stackTrace
|
||||||
}
|
}
|
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
package com.fankes.apperrorstracking.hook.entity
|
package com.fankes.apperrorstracking.hook.entity
|
||||||
|
|
||||||
import android.app.ApplicationErrorReport
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
@@ -187,30 +186,8 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
afterHook {
|
afterHook {
|
||||||
/** 当前 APP 信息 */
|
/** 当前 APP 信息 */
|
||||||
val appInfo = ProcessRecordClass.clazz.field { name = "info" }.get(args().first().any()).cast<ApplicationInfo>()
|
val appInfo = ProcessRecordClass.clazz.field { name = "info" }.get(args().first().any()).cast<ApplicationInfo>()
|
||||||
/** 当前异常信息 */
|
/** 添加当前异常信息到第一位 */
|
||||||
args().last().cast<ApplicationErrorReport.CrashInfo>()?.also { crashInfo ->
|
appErrorsRecords.add(0, AppErrorsInfoBean.clone(appInfo?.packageName, args().last().cast()))
|
||||||
/** 添加到第一位 */
|
|
||||||
(crashInfo.exceptionClassName.lowercase() == "native crash").also { isNativeCrash ->
|
|
||||||
appErrorsRecords.add(
|
|
||||||
0, AppErrorsInfoBean(
|
|
||||||
packageName = appInfo?.packageName ?: "null",
|
|
||||||
isNativeCrash = isNativeCrash,
|
|
||||||
exceptionClassName = crashInfo.exceptionClassName ?: "null",
|
|
||||||
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 ?: "null"
|
|
||||||
} else crashInfo.exceptionMessage ?: "null",
|
|
||||||
throwFileName = crashInfo.throwFileName ?: "null",
|
|
||||||
throwClassName = crashInfo.throwClassName ?: "null",
|
|
||||||
throwMethodName = crashInfo.throwMethodName ?: "null",
|
|
||||||
throwLineNumber = crashInfo.throwLineNumber,
|
|
||||||
stackTrace = crashInfo.stackTrace?.trim() ?: "null",
|
|
||||||
timestamp = System.currentTimeMillis()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -286,4 +286,46 @@ object LocaleString {
|
|||||||
|
|
||||||
/** @string Automatic generated */
|
/** @string Automatic generated */
|
||||||
fun moduleNotFullyActivated(vararg objArrs: Any) = R.string.module_not_fully_activated.bind(*objArrs)
|
fun moduleNotFullyActivated(vararg objArrs: Any) = R.string.module_not_fully_activated.bind(*objArrs)
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
val momentAgo get() = momentAgo()
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
fun momentAgo(vararg objArrs: Any) = R.string.moment_ago.bind(*objArrs)
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
val secondAgo get() = secondAgo()
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
fun secondAgo(vararg objArrs: Any) = R.string.second_ago.bind(*objArrs)
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
val minuteAgo get() = minuteAgo()
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
fun minuteAgo(vararg objArrs: Any) = R.string.minute_ago.bind(*objArrs)
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
val hourAgo get() = hourAgo()
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
fun hourAgo(vararg objArrs: Any) = R.string.hour_ago.bind(*objArrs)
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
val dayAgo get() = dayAgo()
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
fun dayAgo(vararg objArrs: Any) = R.string.day_ago.bind(*objArrs)
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
val monthAgo get() = monthAgo()
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
fun monthAgo(vararg objArrs: Any) = R.string.month_ago.bind(*objArrs)
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
val yearAgo get() = yearAgo()
|
||||||
|
|
||||||
|
/** @string Automatic generated */
|
||||||
|
fun yearAgo(vararg objArrs: Any) = R.string.year_ago.bind(*objArrs)
|
||||||
}
|
}
|
@@ -89,7 +89,7 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
|||||||
binding.errorThrowClassText.text = appErrorsInfo.throwClassName
|
binding.errorThrowClassText.text = appErrorsInfo.throwClassName
|
||||||
binding.errorThrowMethodText.text = appErrorsInfo.throwMethodName
|
binding.errorThrowMethodText.text = appErrorsInfo.throwMethodName
|
||||||
binding.errorLineNumberText.text = appErrorsInfo.throwLineNumber.toString()
|
binding.errorLineNumberText.text = appErrorsInfo.throwLineNumber.toString()
|
||||||
binding.errorRecordTimeText.text = appErrorsInfo.time
|
binding.errorRecordTimeText.text = appErrorsInfo.dateTime
|
||||||
binding.errorStackText.text = appErrorsInfo.stackTrace
|
binding.errorStackText.text = appErrorsInfo.stackTrace
|
||||||
binding.appPanelScrollView.setOnScrollChangeListener { _, _, y, _, _ ->
|
binding.appPanelScrollView.setOnScrollChangeListener { _, _, y, _, _ ->
|
||||||
binding.detailTitleText.text = if (y >= 30.dp(context = this)) appName(appErrorsInfo.packageName) else LocaleString.appName
|
binding.detailTitleText.text = if (y >= 30.dp(context = this)) appName(appErrorsInfo.packageName) else LocaleString.appName
|
||||||
|
@@ -102,7 +102,7 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
|||||||
getItem(position).also {
|
getItem(position).also {
|
||||||
holder.appIcon.setImageDrawable(appIcon(it.packageName))
|
holder.appIcon.setImageDrawable(appIcon(it.packageName))
|
||||||
holder.appNameText.text = appName(it.packageName)
|
holder.appNameText.text = appName(it.packageName)
|
||||||
holder.errorsTimeText.text = it.time
|
holder.errorsTimeText.text = it.crossTime
|
||||||
holder.errorTypeIcon.setImageResource(if (it.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
|
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 ->
|
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
|
if (text.contains(other = ".")) text.split(".").let { e -> e[e.lastIndex] } else text
|
||||||
|
@@ -111,6 +111,34 @@ fun Context.appIcon(packageName: String) =
|
|||||||
.applicationInfo.loadIcon(packageManager)
|
.applicationInfo.loadIcon(packageManager)
|
||||||
}.getOrNull() ?: ResourcesCompat.getDrawable(resources, R.drawable.ic_android, null)
|
}.getOrNull() ?: ResourcesCompat.getDrawable(resources, R.drawable.ic_android, null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算与当前时间戳相差的友好时间
|
||||||
|
* @param now 刚刚
|
||||||
|
* @param second 秒前
|
||||||
|
* @param minute 分钟前
|
||||||
|
* @param hour 小时前
|
||||||
|
* @param day 天前
|
||||||
|
* @param month 月前
|
||||||
|
* @param year 年前
|
||||||
|
* @return [String] 友好时间
|
||||||
|
*/
|
||||||
|
fun Long.difference(now: String, second: String, minute: String, hour: String, day: String, month: String, year: String) =
|
||||||
|
((System.currentTimeMillis() - this) / 1000).toInt().let { diff ->
|
||||||
|
when (diff) {
|
||||||
|
in 0..10 -> now
|
||||||
|
in 11..20 -> "10 $second"
|
||||||
|
in 21..30 -> "20 $second"
|
||||||
|
in 31..40 -> "30 $second"
|
||||||
|
in 41..50 -> "40 $second"
|
||||||
|
in 51..59 -> "50 $second"
|
||||||
|
in 60..3599 -> "${(diff / 60).coerceAtLeast(1)} $minute"
|
||||||
|
in 3600..86399 -> "${diff / 3600} $hour"
|
||||||
|
in 86400..2591999 -> "${diff / 86400} $day"
|
||||||
|
in 2592000..31103999 -> "${diff / 2592000} $month"
|
||||||
|
else -> "${diff / 31104000} $year"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 弹出 [Toast]
|
* 弹出 [Toast]
|
||||||
* @param msg 提示内容
|
* @param msg 提示内容
|
||||||
|
@@ -63,4 +63,11 @@
|
|||||||
<string name="fast_restart">高速リスタート</string>
|
<string name="fast_restart">高速リスタート</string>
|
||||||
<string name="access_root_fail">ルート権限を取得できませんでした</string>
|
<string name="access_root_fail">ルート権限を取得できませんでした</string>
|
||||||
<string name="todo_items">より多くの機能が開発中です</string>
|
<string name="todo_items">より多くの機能が開発中です</string>
|
||||||
|
<string name="moment_ago">現在</string>
|
||||||
|
<string name="second_ago">秒前</string>
|
||||||
|
<string name="minute_ago">分前</string>
|
||||||
|
<string name="hour_ago">時間前</string>
|
||||||
|
<string name="day_ago">日前</string>
|
||||||
|
<string name="month_ago">月前</string>
|
||||||
|
<string name="year_ago">年前</string>
|
||||||
</resources>
|
</resources>
|
@@ -63,4 +63,11 @@
|
|||||||
<string name="fast_restart">快速重启</string>
|
<string name="fast_restart">快速重启</string>
|
||||||
<string name="access_root_fail">获取 Root 权限失败</string>
|
<string name="access_root_fail">获取 Root 权限失败</string>
|
||||||
<string name="todo_items">更多功能正在开发</string>
|
<string name="todo_items">更多功能正在开发</string>
|
||||||
|
<string name="moment_ago">刚刚</string>
|
||||||
|
<string name="second_ago">秒前</string>
|
||||||
|
<string name="minute_ago">分钟前</string>
|
||||||
|
<string name="hour_ago">小时前</string>
|
||||||
|
<string name="day_ago">天前</string>
|
||||||
|
<string name="month_ago">月前</string>
|
||||||
|
<string name="year_ago">年前</string>
|
||||||
</resources>
|
</resources>
|
@@ -63,4 +63,11 @@
|
|||||||
<string name="fast_restart">急速重開</string>
|
<string name="fast_restart">急速重開</string>
|
||||||
<string name="access_root_fail">取得 Root 权利失败</string>
|
<string name="access_root_fail">取得 Root 权利失败</string>
|
||||||
<string name="todo_items">更多機能正在開發</string>
|
<string name="todo_items">更多機能正在開發</string>
|
||||||
|
<string name="moment_ago">剛剛</string>
|
||||||
|
<string name="second_ago">秒前</string>
|
||||||
|
<string name="minute_ago">分鐘前</string>
|
||||||
|
<string name="hour_ago">小時前</string>
|
||||||
|
<string name="day_ago">天前</string>
|
||||||
|
<string name="month_ago">月前</string>
|
||||||
|
<string name="year_ago">年前</string>
|
||||||
</resources>
|
</resources>
|
@@ -63,4 +63,11 @@
|
|||||||
<string name="fast_restart">急速重開</string>
|
<string name="fast_restart">急速重開</string>
|
||||||
<string name="access_root_fail">取得 Root 权利失败</string>
|
<string name="access_root_fail">取得 Root 权利失败</string>
|
||||||
<string name="todo_items">更多機能正在開發</string>
|
<string name="todo_items">更多機能正在開發</string>
|
||||||
|
<string name="moment_ago">剛剛</string>
|
||||||
|
<string name="second_ago">秒前</string>
|
||||||
|
<string name="minute_ago">分鐘前</string>
|
||||||
|
<string name="hour_ago">小時前</string>
|
||||||
|
<string name="day_ago">天前</string>
|
||||||
|
<string name="month_ago">月前</string>
|
||||||
|
<string name="year_ago">年前</string>
|
||||||
</resources>
|
</resources>
|
@@ -63,4 +63,11 @@
|
|||||||
<string name="fast_restart">急速重開</string>
|
<string name="fast_restart">急速重開</string>
|
||||||
<string name="access_root_fail">取得 Root 权利失败</string>
|
<string name="access_root_fail">取得 Root 权利失败</string>
|
||||||
<string name="todo_items">更多機能正在開發</string>
|
<string name="todo_items">更多機能正在開發</string>
|
||||||
|
<string name="moment_ago">剛剛</string>
|
||||||
|
<string name="second_ago">秒前</string>
|
||||||
|
<string name="minute_ago">分鐘前</string>
|
||||||
|
<string name="hour_ago">小時前</string>
|
||||||
|
<string name="day_ago">天前</string>
|
||||||
|
<string name="month_ago">月前</string>
|
||||||
|
<string name="year_ago">年前</string>
|
||||||
</resources>
|
</resources>
|
@@ -63,4 +63,11 @@
|
|||||||
<string name="fast_restart">Fast restart</string>
|
<string name="fast_restart">Fast restart</string>
|
||||||
<string name="access_root_fail">Access Root failed</string>
|
<string name="access_root_fail">Access Root failed</string>
|
||||||
<string name="todo_items">More features are in development</string>
|
<string name="todo_items">More features are in development</string>
|
||||||
|
<string name="moment_ago">moment ago</string>
|
||||||
|
<string name="second_ago">second ago</string>
|
||||||
|
<string name="minute_ago">minutes ago</string>
|
||||||
|
<string name="hour_ago">hour ago</string>
|
||||||
|
<string name="day_ago">days ago</string>
|
||||||
|
<string name="month_ago">month ago</string>
|
||||||
|
<string name="year_ago">year ago</string>
|
||||||
</resources>
|
</resources>
|
Reference in New Issue
Block a user