mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-04 10:15:18 +08:00
Fix restart button show in cannot opened app's and fix error dialog repeating showing
This commit is contained in:
@@ -36,8 +36,6 @@ Added more features to app's crash dialog, fixed custom rom deleted dialog, the
|
|||||||
|
|
||||||
- 后台进程可能依然会弹出崩溃对话框且开发者选项里的设置无效,还在排查
|
- 后台进程可能依然会弹出崩溃对话框且开发者选项里的设置无效,还在排查
|
||||||
|
|
||||||
- 无法启动后台进程和服务,是否在一定条件隐藏“重新打开”按钮的问题
|
|
||||||
|
|
||||||
- 暂不支持国际化语言,Chinese only
|
- 暂不支持国际化语言,Chinese only
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
@@ -37,10 +37,7 @@ import android.widget.LinearLayout
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.fankes.apperrorstracking.R
|
import com.fankes.apperrorstracking.R
|
||||||
import com.fankes.apperrorstracking.utils.drawable.drawabletoolbox.DrawableBuilder
|
import com.fankes.apperrorstracking.utils.drawable.drawabletoolbox.DrawableBuilder
|
||||||
import com.fankes.apperrorstracking.utils.factory.dp
|
import com.fankes.apperrorstracking.utils.factory.*
|
||||||
import com.fankes.apperrorstracking.utils.factory.isSystemInDarkMode
|
|
||||||
import com.fankes.apperrorstracking.utils.factory.openApp
|
|
||||||
import com.fankes.apperrorstracking.utils.factory.openSelfSetting
|
|
||||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||||
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||||
import com.highcapable.yukihookapi.hook.factory.field
|
import com.highcapable.yukihookapi.hook.factory.field
|
||||||
@@ -58,11 +55,16 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
|
|
||||||
private const val ProcessRecordClass = "com.android.server.am.ProcessRecord"
|
private const val ProcessRecordClass = "com.android.server.am.ProcessRecord"
|
||||||
|
|
||||||
|
private const val PackageListClass = "com.android.server.am.PackageList"
|
||||||
|
|
||||||
private val ErrorDialogControllerClass = VariousClass(
|
private val ErrorDialogControllerClass = VariousClass(
|
||||||
"com.android.server.am.ProcessRecord\$ErrorDialogController",
|
"com.android.server.am.ProcessRecord\$ErrorDialogController",
|
||||||
"com.android.server.am.ErrorDialogController"
|
"com.android.server.am.ErrorDialogController"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** 已打开的错误对话框数组 */
|
||||||
|
private var openedErrorsDialogs = HashMap<String, AlertDialog>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建对话框按钮
|
* 创建对话框按钮
|
||||||
* @param context 实例
|
* @param context 实例
|
||||||
@@ -128,48 +130,82 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
val errResult = AppErrorResultClass.clazz.method {
|
val errResult = AppErrorResultClass.clazz.method {
|
||||||
name = "get"
|
name = "get"
|
||||||
emptyParam()
|
emptyParam()
|
||||||
}.get(AppErrorDialog_DataClass.clazz.field {
|
}.get(AppErrorDialog_DataClass.clazz.field { name = "result" }.get(errData).any()).int()
|
||||||
name = "result"
|
|
||||||
}.get(errData).any()).int()
|
/** 当前进程信息 */
|
||||||
|
val proc = AppErrorDialog_DataClass.clazz.field { name = "proc" }.get(errData).any()
|
||||||
|
|
||||||
/** 当前 APP 信息 */
|
/** 当前 APP 信息 */
|
||||||
val appInfo = ProcessRecordClass.clazz.field { name = "info" }
|
val appInfo = ProcessRecordClass.clazz.field { name = "info" }.get(proc).cast<ApplicationInfo>()
|
||||||
.get(AppErrorDialog_DataClass.clazz.field { name = "proc" }
|
|
||||||
.get(errData).any()).cast<ApplicationInfo>() ?: ApplicationInfo()
|
/** 当前进程名称 */
|
||||||
|
val processName = ProcessRecordClass.clazz.field { name = "processName" }.get(proc).string()
|
||||||
|
|
||||||
|
/** 当前 APP、进程 包名 */
|
||||||
|
val packageName = appInfo?.packageName ?: processName
|
||||||
|
|
||||||
|
/** 当前 APP 名称 */
|
||||||
|
val appName = appInfo?.let { context.packageManager.getApplicationLabel(it) } ?: packageName
|
||||||
|
|
||||||
|
/** 是否为 APP */
|
||||||
|
val isApp = (PackageListClass.clazz.method { name = "size" }
|
||||||
|
.get(ProcessRecordClass.clazz.method { name = "getPkgList" }.get(proc).call()).int() == 1) && appInfo != null
|
||||||
|
|
||||||
/** 是否短时内重复错误 */
|
/** 是否短时内重复错误 */
|
||||||
val isRepeating = AppErrorDialog_DataClass.clazz.field { name = "repeating" }.get(errData).boolean()
|
val isRepeating = AppErrorDialog_DataClass.clazz.field { name = "repeating" }.get(errData).boolean()
|
||||||
/** 判断在后台就不显示对话框 */
|
/** 判断在后台就不显示对话框 */
|
||||||
if (errResult == -2) return@afterHook
|
if (errResult == -2) return@afterHook
|
||||||
|
/** 关闭重复的对话框 */
|
||||||
|
openedErrorsDialogs[packageName]?.cancel()
|
||||||
/** 创建自定义对话框 */
|
/** 创建自定义对话框 */
|
||||||
AlertDialog.Builder(
|
AlertDialog.Builder(
|
||||||
context, if (context.isSystemInDarkMode)
|
context, if (context.isSystemInDarkMode)
|
||||||
android.R.style.Theme_Material_Dialog
|
android.R.style.Theme_Material_Dialog
|
||||||
else android.R.style.Theme_Material_Light_Dialog
|
else android.R.style.Theme_Material_Light_Dialog
|
||||||
).create().apply {
|
).create().apply {
|
||||||
setTitle("${appInfo.loadLabel(context.packageManager)} ${if (isRepeating) "屡次停止运行" else "已停止运行"}")
|
setTitle("$appName ${if (isRepeating) "屡次停止运行" else "已停止运行"}")
|
||||||
setView(LinearLayout(context).apply {
|
setView(LinearLayout(context).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
addView(createButtonItem(context, R.drawable.ic_baseline_info, content = "应用信息") {
|
/** 应用信息按钮 */
|
||||||
cancel()
|
val appInfoButton =
|
||||||
context.openSelfSetting(packageName = appInfo.packageName)
|
createButtonItem(context, R.drawable.ic_baseline_info, content = "应用信息") {
|
||||||
})
|
cancel()
|
||||||
if (isRepeating)
|
context.openSelfSetting(packageName)
|
||||||
addView(createButtonItem(context, R.drawable.ic_baseline_close, content = "关闭应用") { cancel() })
|
}
|
||||||
else addView(createButtonItem(context, R.drawable.ic_baseline_refresh, content = "重新打开") {
|
|
||||||
cancel()
|
/** 关闭应用按钮 */
|
||||||
context.openApp(appInfo.packageName)
|
val closeAppButton =
|
||||||
})
|
createButtonItem(context, R.drawable.ic_baseline_close, content = "关闭应用") { cancel() }
|
||||||
addView(createButtonItem(context, R.drawable.ic_baseline_bug_report, content = "错误详情") {
|
|
||||||
// TODO 待开发
|
/** 重新打开按钮 */
|
||||||
})
|
val reOpenButton =
|
||||||
|
createButtonItem(context, R.drawable.ic_baseline_refresh, content = "重新打开") {
|
||||||
|
cancel()
|
||||||
|
context.openApp(packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 错误详情按钮 */
|
||||||
|
val errorDetailButton =
|
||||||
|
createButtonItem(context, R.drawable.ic_baseline_bug_report, content = "错误详情") {
|
||||||
|
// TODO 待开发
|
||||||
|
}
|
||||||
|
/** 判断进程是否为 APP */
|
||||||
|
if (isApp) {
|
||||||
|
addView(appInfoButton)
|
||||||
|
addView(if (isRepeating.not() && context.isAppCanOpened(packageName)) reOpenButton else closeAppButton)
|
||||||
|
} else addView(closeAppButton)
|
||||||
|
/** 始终添加错误详情按钮 */
|
||||||
|
addView(errorDetailButton)
|
||||||
|
/** 设置边距 */
|
||||||
setPadding(6.dp(context), 15.dp(context), 6.dp(context), 6.dp(context))
|
setPadding(6.dp(context), 15.dp(context), 6.dp(context), 6.dp(context))
|
||||||
})
|
})
|
||||||
/** 只有 SystemUid 才能响应系统级别的对话框 */
|
/** 只有 SystemUid 才能响应系统级别的对话框 */
|
||||||
window?.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
|
window?.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
|
||||||
|
/** 记录实例 */
|
||||||
|
openedErrorsDialogs[packageName] = this
|
||||||
}.show()
|
}.show()
|
||||||
/** 打印错误日志 */
|
/** 打印错误日志 */
|
||||||
loggerE(msg = "Process \"${appInfo.packageName}\" has crashed, isRepeating --> $isRepeating")
|
loggerE(msg = "Process \"$packageName\" has crashed${if (isRepeating) " again" else ""}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -68,6 +68,13 @@ fun Context.openSelfSetting(packageName: String = this.packageName) = runCatchin
|
|||||||
})
|
})
|
||||||
}.onFailure { Toast.makeText(this, "无法打开 $packageName 的设置界面", Toast.LENGTH_SHORT).show() }
|
}.onFailure { Toast.makeText(this, "无法打开 $packageName 的设置界面", Toast.LENGTH_SHORT).show() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前 APP 是否可被启动
|
||||||
|
* @param packageName 包名
|
||||||
|
*/
|
||||||
|
fun Context.isAppCanOpened(packageName: String = this.packageName) =
|
||||||
|
runCatching { packageManager?.getLaunchIntentForPackage(packageName) != null }.getOrNull() ?: false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动指定 APP
|
* 启动指定 APP
|
||||||
* @param packageName 包名
|
* @param packageName 包名
|
||||||
|
Reference in New Issue
Block a user