From ded9da730f84511b46c036e7e9ba68782976bafe Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Wed, 1 Jun 2022 03:06:15 +0800 Subject: [PATCH] Added new style in app errors dialog and merge code from FrameworkHooker --- app/src/main/AndroidManifest.xml | 10 ++ .../bean/AppErrorsDisplayBean.kt | 40 +++++ .../hook/entity/FrameworkHooker.kt | 141 +++--------------- .../ui/activity/base/BaseActivity.kt | 10 ++ .../errors/AppErrorsDetailActivity.kt | 13 +- .../errors/AppErrorsDisplayActivity.kt | 108 ++++++++++++++ .../errors/AppErrorsRecordActivity.kt | 2 +- .../ui/view/ItemLinearLayout.kt | 42 ++++++ .../utils/factory/DialogBuilderFactory.kt | 40 ++--- .../utils/tool/FrameworkTool.kt | 71 +++++++++ .../layout/activity_app_errors_display.xml | 4 + .../res/layout/dia_app_errors_display.xml | 141 ++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/themes.xml | 11 ++ 14 files changed, 476 insertions(+), 158 deletions(-) create mode 100644 app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsDisplayBean.kt create mode 100644 app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt create mode 100644 app/src/main/java/com/fankes/apperrorstracking/ui/view/ItemLinearLayout.kt create mode 100644 app/src/main/res/layout/activity_app_errors_display.xml create mode 100644 app/src/main/res/layout/dia_app_errors_display.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f6e9a1f..5e65874 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -54,6 +54,16 @@ + + + * + * This file is Created by fankes on 2022/5/10. + */ +package com.fankes.apperrorstracking.bean + +import java.io.Serializable + +/** + * 应用异常信息显示 bean + * @param packageName APP 包名 + * @param appName APP 名称 + * @param title 标题 + * @param isApp 是否为 APP + * @param isShowReopenButton 是否显示重新打开按钮 + */ +data class AppErrorsDisplayBean( + var packageName: String, + var appName: String, + var title: String, + var isApp: Boolean, + var isShowReopenButton: Boolean +) : Serializable \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt index 3be0652..4e1b7b2 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt @@ -27,22 +27,14 @@ import android.app.ApplicationErrorReport import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo -import android.graphics.Color import android.os.Message -import android.text.TextUtils -import android.view.Gravity -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView -import androidx.core.content.res.ResourcesCompat -import com.fankes.apperrorstracking.R +import com.fankes.apperrorstracking.bean.AppErrorsDisplayBean import com.fankes.apperrorstracking.bean.AppErrorsInfoBean import com.fankes.apperrorstracking.locale.LocaleString -import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsDetailActivity -import com.fankes.apperrorstracking.utils.drawable.drawabletoolbox.DrawableBuilder -import com.fankes.apperrorstracking.utils.factory.* +import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsDisplayActivity +import com.fankes.apperrorstracking.utils.factory.appName +import com.fankes.apperrorstracking.utils.factory.isAppCanOpened +import com.fankes.apperrorstracking.utils.factory.openApp import com.fankes.apperrorstracking.utils.tool.FrameworkTool import com.highcapable.yukihookapi.hook.bean.VariousClass import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker @@ -72,9 +64,6 @@ object FrameworkHooker : YukiBaseHooker() { "com.android.server.am.ErrorDialogController" ) - /** 已打开的错误对话框数组 */ - private var openedErrorsDialogs = hashMapOf() - /** 已忽略错误的 APP 数组 - 直到重新解锁 */ private var ignoredErrorsIfUnlockApps = hashSetOf() @@ -84,45 +73,6 @@ object FrameworkHooker : YukiBaseHooker() { /** 已记录的 APP 异常信息数组 - 直到重新启动 */ private val appErrorsRecords = arrayListOf() - /** - * 获取最新的 APP 错误信息 - * @param packageName 包名 - * @return [AppErrorsInfoBean] or null - */ - private fun lastAppErrorsInfo(packageName: String) = - appErrorsRecords.takeIf { it.isNotEmpty() }?.filter { it.packageName == packageName }?.get(0) - - /** - * 创建对话框按钮 - * @param context 实例 - * @param drawableId 按钮图标 - * @param content 按钮文本 - * @param it 点击事件回调 - * @return [LinearLayout] - */ - private fun createButtonItem(context: Context, drawableId: Int, content: String, it: () -> Unit) = - LinearLayout(context).apply { - background = DrawableBuilder().rounded().cornerRadius(15.dp(context)).ripple().rippleColor(0xFFAAAAAA.toInt()).build() - gravity = Gravity.CENTER or Gravity.START - layoutParams = - ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) - addView(ImageView(context).apply { - setImageDrawable(ResourcesCompat.getDrawable(moduleAppResources, drawableId, null)) - layoutParams = ViewGroup.LayoutParams(25.dp(context), 25.dp(context)) - setColorFilter(if (context.isSystemInDarkMode) Color.WHITE else Color.BLACK) - }) - addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(15.dp(context), 0) }) - addView(TextView(context).apply { - text = content - textSize = 16f - ellipsize = TextUtils.TruncateAt.END - setSingleLine() - setTextColor(if (context.isSystemInDarkMode) 0xFFDDDDDD.toInt() else 0xFF777777.toInt()) - }) - setPadding(19.dp(context), 16.dp(context), 19.dp(context), 16.dp(context)) - setOnClickListener { it() } - } - /** 注册 */ private fun register() { onAppLifecycle { @@ -132,9 +82,12 @@ object FrameworkHooker : YukiBaseHooker() { registerReceiver(Intent.ACTION_LOCALE_CHANGED) { _, _ -> refreshModuleAppResources() } } FrameworkTool.Host.with(instance = this) { + onOpenAppUsedFramework { appContext.openApp(it) } onPushAppErrorsInfoData { appErrorsRecords } onRemoveAppErrorsInfoData { appErrorsRecords.remove(it) } onClearAppErrorsInfoData { appErrorsRecords.clear() } + onIgnoredErrorsIfUnlock { ignoredErrorsIfUnlockApps.add(it) } + onIgnoredErrorsIfRestart { ignoredErrorsIfRestartApps.add(it) } } } @@ -214,74 +167,16 @@ object FrameworkHooker : YukiBaseHooker() { /** 判断是否被忽略 - 在后台就不显示对话框 */ if (ignoredErrorsIfUnlockApps.contains(packageName) || ignoredErrorsIfRestartApps.contains(packageName) || errResult == -2) return@afterHook - /** 关闭重复的对话框 */ - openedErrorsDialogs[packageName]?.cancel() - /** 创建自定义对话框 */ - context.showDialog { - title = if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName) - view = LinearLayout(context).apply { - orientation = LinearLayout.VERTICAL - /** 应用信息按钮 */ - val appInfoButton = - createButtonItem(context, R.drawable.ic_baseline_info, LocaleString.appInfo) { - cancel() - context.openSelfSetting(packageName) - } - - /** 关闭应用按钮 */ - val closeAppButton = - createButtonItem(context, R.drawable.ic_baseline_close, LocaleString.closeApp) { cancel() } - - /** 重新打开按钮 */ - val reOpenButton = - createButtonItem(context, R.drawable.ic_baseline_refresh, LocaleString.reopenApp) { - cancel() - context.openApp(packageName) - } - - /** 错误详情按钮 */ - val errorDetailButton = - createButtonItem(context, R.drawable.ic_baseline_bug_report, LocaleString.errorDetail) { - cancel() - lastAppErrorsInfo(packageName)?.let { AppErrorsDetailActivity.start(context, it, isOutSide = true) } - ?: context.toast(msg = "Invalid AppErrorsInfo") - } - - /** 忽略按钮 - 直到解锁 */ - val ignoredUntilUnlockButton = - createButtonItem(context, R.drawable.ic_baseline_eject, LocaleString.ignoreIfUnlock) { - cancel() - ignoredErrorsIfUnlockApps.add(packageName) - context.toast(LocaleString.ignoreIfUnlockTip(appName)) - } - - /** 忽略按钮 - 直到重启 */ - val ignoredUntilRestartButton = - createButtonItem(context, R.drawable.ic_baseline_eject, LocaleString.ignoreIfRestart) { - cancel() - ignoredErrorsIfRestartApps.add(packageName) - context.toast(LocaleString.ignoreIfRestartTip(appName)) - } - /** 判断进程是否为 APP */ - if (isApp) { - addView(appInfoButton) - addView(if (isRepeating.not() && context.isAppCanOpened(packageName)) reOpenButton else closeAppButton) - } else addView(closeAppButton) - /** 始终添加错误详情按钮 */ - addView(errorDetailButton) - /** 始终添加忽略按钮 */ - addView(ignoredUntilUnlockButton) - addView(ignoredUntilRestartButton) - /** 设置边距 */ - setPadding(6.dp(context), 15.dp(context), 6.dp(context), 6.dp(context)) - } - /** 设置取消对话框监听 */ - onCancel { openedErrorsDialogs.remove(packageName) } - /** 记录实例 */ - openedErrorsDialogs[packageName] = this - /** 只有 SystemUid 才能响应系统级别的对话框 */ - makeSystemAlert() - } + /** 启动错误对话框显示窗口 */ + AppErrorsDisplayActivity.start( + context, AppErrorsDisplayBean( + packageName = packageName, + appName = appName, + title = if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName), + isApp = isApp, + isShowReopenButton = isRepeating.not() && context.isAppCanOpened(packageName) + ) + ) } } injectMember { diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt index 057f40c..c2d027d 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt @@ -31,6 +31,7 @@ import androidx.core.view.ViewCompat import androidx.viewbinding.ViewBinding import com.fankes.apperrorstracking.R import com.fankes.apperrorstracking.utils.factory.isNotSystemInDarkMode +import com.fankes.apperrorstracking.utils.factory.toast import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass import java.lang.reflect.ParameterizedType @@ -69,4 +70,13 @@ abstract class BaseActivity : AppCompatActivity() { /** 回调 [onCreate] 方法 */ abstract fun onCreate() + + /** + * 弹出提示并退出 + * @param name 名称 + */ + fun toastAndFinish(name: String) { + toast(msg = "Invalid $name, exit") + finish() + } } \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDetailActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDetailActivity.kt index d80ad03..89836e7 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDetailActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDetailActivity.kt @@ -54,10 +54,9 @@ class AppErrorsDetailActivity : BaseActivity() { * 启动 [AppErrorsDetailActivity] * @param context 实例 * @param appErrorsInfo 应用异常信息 - * @param isOutSide 是否从外部启动 */ - fun start(context: Context, appErrorsInfo: AppErrorsInfoBean, isOutSide: Boolean = false) = - context.navigate(isOutSide) { putExtra(EXTRA_APP_ERRORS_INFO, appErrorsInfo) } + fun start(context: Context, appErrorsInfo: AppErrorsInfoBean) = + context.navigate { putExtra(EXTRA_APP_ERRORS_INFO, appErrorsInfo) } } /** 预导出的异常堆栈 */ @@ -89,7 +88,7 @@ class AppErrorsDetailActivity : BaseActivity() { override fun onCreate() { val appErrorsInfo = runCatching { intent?.getSerializableExtra(EXTRA_APP_ERRORS_INFO) as? AppErrorsInfoBean }.getOrNull() - ?: return toastAndFinish() + ?: return toastAndFinish(name = "AppErrorsInfo") binding.appInfoItem.setOnClickListener { openSelfSetting(appErrorsInfo.packageName) } binding.titleBackIcon.setOnClickListener { onBackPressed() } binding.printIcon.setOnClickListener { @@ -129,12 +128,6 @@ class AppErrorsDetailActivity : BaseActivity() { contentResolver?.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, observer) } - /** 弹出提示并退出 */ - private fun toastAndFinish() { - toast(msg = "Invalid AppErrorsInfo, exit") - finish() - } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) runCatching { diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt new file mode 100644 index 0000000..06ffd7a --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt @@ -0,0 +1,108 @@ +/* + * AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer. + * Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) + * https://github.com/KitsunePie/AppErrorsTracking + * + * This software is non-free but opensource software: you can redistribute it + * and/or modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * and eula along with this software. If not, see + * + * + * This file is Created by fankes on 2022/6/1. + */ +package com.fankes.apperrorstracking.ui.activity.errors + +import android.content.Context +import androidx.core.view.isVisible +import com.fankes.apperrorstracking.bean.AppErrorsDisplayBean +import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDisplayBinding +import com.fankes.apperrorstracking.databinding.DiaAppErrorsDisplayBinding +import com.fankes.apperrorstracking.locale.LocaleString +import com.fankes.apperrorstracking.ui.activity.base.BaseActivity +import com.fankes.apperrorstracking.utils.factory.navigate +import com.fankes.apperrorstracking.utils.factory.openSelfSetting +import com.fankes.apperrorstracking.utils.factory.showDialog +import com.fankes.apperrorstracking.utils.factory.toast +import com.fankes.apperrorstracking.utils.tool.FrameworkTool + +class AppErrorsDisplayActivity : BaseActivity() { + + companion object { + + /** 当前实例 - 单例运行 */ + private var instance: AppErrorsDisplayActivity? = null + + /** [AppErrorsDisplayBean] 传值 */ + private const val EXTRA_APP_ERRORS_DISPLAY = "app_errors_display_extra" + + /** + * 启动 [AppErrorsDisplayActivity] + * @param context 实例 + * @param appErrorsDisplay 应用异常信息显示 + */ + fun start(context: Context, appErrorsDisplay: AppErrorsDisplayBean) = + context.navigate(isOutSide = true) { putExtra(EXTRA_APP_ERRORS_DISPLAY, appErrorsDisplay) } + } + + override fun onCreate() { + instance?.finish() + instance = this + val appErrorsDisplay = runCatching { intent?.getSerializableExtra(EXTRA_APP_ERRORS_DISPLAY) as? AppErrorsDisplayBean }.getOrNull() + ?: return toastAndFinish(name = "AppErrorsDisplay") + /** 显示对话框 */ + showDialog { + title = appErrorsDisplay.title + bind().apply { + appInfoItem.isVisible = appErrorsDisplay.isApp + reopenAppItem.isVisible = appErrorsDisplay.isShowReopenButton + closeAppItem.isVisible = appErrorsDisplay.isShowReopenButton.not() + appInfoItem.setOnClickListener { + cancel() + openSelfSetting(appErrorsDisplay.packageName) + } + closeAppItem.setOnClickListener { cancel() } + reopenAppItem.setOnClickListener { + FrameworkTool.openAppUsedFramework(context, appErrorsDisplay.packageName) + cancel() + } + errorDetailItem.setOnClickListener { + FrameworkTool.fetchAppErrorsInfoData(context) { appErrorsInfos -> + appErrorsInfos.takeIf { it.isNotEmpty() } + ?.filter { it.packageName == appErrorsDisplay.packageName } + ?.takeIf { it.isNotEmpty() }?.get(0)?.let { + AppErrorsDetailActivity.start(context, it) + cancel() + } ?: toast(msg = "No errors founded") + } + } + ignoreIfUnlockItem.setOnClickListener { + FrameworkTool.ignoredErrorsIfUnlock(context, appErrorsDisplay.packageName) { + toast(LocaleString.ignoreIfUnlockTip(appErrorsDisplay.appName)) + cancel() + } + } + ignoreIfRestartItem.setOnClickListener { + FrameworkTool.ignoredErrorsIfRestart(context, appErrorsDisplay.packageName) { + toast(LocaleString.ignoreIfRestartTip(appErrorsDisplay.appName)) + cancel() + } + } + } + onCancel { finish() } + } + } + + override fun onDestroy() { + super.onDestroy() + instance = null + } +} \ 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 5f5784d..d5f0f31 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 @@ -82,7 +82,7 @@ class AppErrorsRecordActivity : BaseActivity() { } } /** 设置列表元素和 Adapter */ - binding.listView.apply { // 改成 recycleview? + binding.listView.apply { adapter = object : BaseAdapter() { override fun getCount() = listData.size diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/view/ItemLinearLayout.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/view/ItemLinearLayout.kt new file mode 100644 index 0000000..9c3ac30 --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/view/ItemLinearLayout.kt @@ -0,0 +1,42 @@ +/* + * AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer. + * Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) + * https://github.com/KitsunePie/AppErrorsTracking + * + * This software is non-free but opensource software: you can redistribute it + * and/or modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * and eula along with this software. If not, see + * + * + * This file is Created by fankes on 2022/6/1. + */ +package com.fankes.apperrorstracking.ui.view + +import android.content.Context +import android.util.AttributeSet +import android.view.Gravity +import android.widget.LinearLayout +import com.fankes.apperrorstracking.utils.drawable.drawabletoolbox.DrawableBuilder +import com.fankes.apperrorstracking.utils.factory.dp + +class ItemLinearLayout(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { + + init { + gravity = Gravity.CENTER or Gravity.START + background = DrawableBuilder() + .rounded() + .cornerRadius(15.dp(context)) + .ripple() + .rippleColor(0xFFAAAAAA.toInt()) + .build() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt index 29aabad..9c247f9 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt @@ -19,7 +19,7 @@ * * This file is Created by fankes on 2022/5/12. */ -@file:Suppress("unused", "DEPRECATION") +@file:Suppress("unused", "DEPRECATION", "OPT_IN_USAGE", "EXPERIMENTAL_API_USAGE") package com.fankes.apperrorstracking.utils.factory @@ -29,16 +29,20 @@ import android.graphics.Color import android.graphics.drawable.GradientDrawable import android.graphics.drawable.InsetDrawable import android.view.Gravity +import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.WindowManager import android.widget.LinearLayout import android.widget.ProgressBar import android.widget.TextView import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding import com.fankes.apperrorstracking.locale.LocaleString import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.shape.MaterialShapeDrawable +import com.highcapable.yukihookapi.annotation.CauseProblemsApi +import com.highcapable.yukihookapi.hook.factory.method +import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass /** * 构造对话框 @@ -55,13 +59,12 @@ class DialogBuilder(val context: Context) { private var instanceAndroidX: androidx.appcompat.app.AlertDialog.Builder? = null // 实例对象 private var instanceAndroid: android.app.AlertDialog.Builder? = null // 实例对象 - private var isSystemAlert = false // 标识为系统级别的对话框 - private var onCancel: (() -> Unit)? = null // 对话框取消监听 private var dialogInstance: Dialog? = null // 对话框实例 - private var customLayoutView: View? = null // 自定义布局 + @CauseProblemsApi + var customLayoutView: View? = null // 自定义布局 /** * 是否需要使用 AndroidX 风格对话框 @@ -89,15 +92,6 @@ class DialogBuilder(val context: Context) { else runCatching { instanceAndroid?.setCancelable(false) } } - /** - * 设置为系统级别对话框 - * - * - ❗仅可在系统级别的 APP 中生效 - */ - fun makeSystemAlert() { - isSystemAlert = true - } - /** 设置对话框标题 */ var title get() = "" @@ -137,13 +131,15 @@ class DialogBuilder(val context: Context) { /** * 设置对话框自定义布局 - * @return [customLayoutView] + * @return [ViewBinding] */ - var view - get() = customLayoutView - set(value) { - customLayoutView = value - } + inline fun bind() = + T::class.java.method { + name = "inflate" + param(LayoutInflaterClass) + }.get().invoke(LayoutInflater.from(context))?.apply { + customLayoutView = root + } ?: error("binding failed") /** * 设置对话框确定按钮 @@ -196,8 +192,6 @@ class DialogBuilder(val context: Context) { customLayoutView?.let { setView(it) } dialogInstance = this setOnCancelListener { onCancel?.invoke() } - /** 只有 SystemUid 才能响应系统级别的对话框 */ - if (isSystemAlert) runCatching { window?.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT) } }?.show() } else runCatching { instanceAndroid?.create()?.apply { @@ -217,8 +211,6 @@ class DialogBuilder(val context: Context) { ) dialogInstance = this setOnCancelListener { onCancel?.invoke() } - /** 只有 SystemUid 才能响应系统级别的对话框 */ - if (isSystemAlert) runCatching { window?.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT) } }?.show() } } diff --git a/app/src/main/java/com/fankes/apperrorstracking/utils/tool/FrameworkTool.kt b/app/src/main/java/com/fankes/apperrorstracking/utils/tool/FrameworkTool.kt index a1fdfa6..1034f27 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/utils/tool/FrameworkTool.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/utils/tool/FrameworkTool.kt @@ -46,9 +46,14 @@ object FrameworkTool { private const val CALL_APP_ERRORS_DATA_REMOVE_RESULT = "call_app_errors_data_remove_result" private const val CALL_APP_ERRORS_DATA_CLEAR = "call_app_errors_data_clear" private const val CALL_APP_ERRORS_DATA_CLEAR_RESULT = "call_app_errors_data_clear_result" + private const val CALL_IGNORED_ERRORS_IF_UNLOCK_RESULT = "call_ignored_errors_if_unlock_result" + private const val CALL_IGNORED_ERRORS_IF_RESTART_RESULT = "call_ignored_errors_if_restart_result" + private val CALL_OPEN_SPECIFY_APP = ChannelData("call_open_specify_app") private val CALL_APP_ERRORS_DATA_REMOVE = ChannelData("call_app_errors_data_remove") private val CALL_APP_ERRORS_DATA_GET_RESULT = ChannelData>("call_app_errors_data_get_result") + private val CALL_IGNORED_ERRORS_IF_UNLOCK = ChannelData("call_ignored_errors_if_unlock") + private val CALL_IGNORED_ERRORS_IF_RESTART = ChannelData("call_ignored_errors_if_restart") /** * 宿主注册监听 @@ -66,6 +71,12 @@ object FrameworkTool { */ fun with(instance: PackageParam, initiate: Host.() -> Unit) = apply { this.instance = instance }.apply(initiate) + /** + * 监听使用系统框架打开 APP + * @param result 回调包名 + */ + fun onOpenAppUsedFramework(result: (String) -> Unit) = instance?.dataChannel?.wait(CALL_OPEN_SPECIFY_APP) { result(it) } + /** * 监听发送 APP 异常信息数组 * @param result 回调数据 @@ -99,6 +110,32 @@ object FrameworkTool { } } } + + /** + * 监听忽略 APP 的错误直到设备重新解锁 + * @param result 回调包名 + */ + fun onIgnoredErrorsIfUnlock(result: (String) -> Unit) { + instance?.dataChannel?.with { + wait(CALL_IGNORED_ERRORS_IF_UNLOCK) { + result(it) + put(CALL_IGNORED_ERRORS_IF_UNLOCK_RESULT) + } + } + } + + /** + * 监听忽略 APP 的错误直到设备重新启动 + * @param result 回调包名 + */ + fun onIgnoredErrorsIfRestart(result: (String) -> Unit) { + instance?.dataChannel?.with { + wait(CALL_IGNORED_ERRORS_IF_RESTART) { + result(it) + put(CALL_IGNORED_ERRORS_IF_RESTART_RESULT) + } + } + } } /** @@ -129,6 +166,14 @@ object FrameworkTool { */ fun checkingActivated(context: Context, result: (Boolean) -> Unit) = context.dataChannel(SYSTEM_FRAMEWORK_NAME).checkingVersionEquals(result) + /** + * 使用系统框架打开 [packageName] + * @param context 实例 + * @param packageName APP 包名 + */ + fun openAppUsedFramework(context: Context, packageName: String) = + context.dataChannel(SYSTEM_FRAMEWORK_NAME).put(CALL_OPEN_SPECIFY_APP, packageName) + /** * 获取 APP 异常信息数组 * @param context 实例 @@ -165,4 +210,30 @@ object FrameworkTool { put(CALL_APP_ERRORS_DATA_CLEAR) } } + + /** + * 忽略 [packageName] 的错误直到设备重新解锁 + * @param context 实例 + * @param packageName APP 包名 + * @param callback 成功后回调 + */ + fun ignoredErrorsIfUnlock(context: Context, packageName: String, callback: () -> Unit) { + context.dataChannel(SYSTEM_FRAMEWORK_NAME).with { + wait(CALL_IGNORED_ERRORS_IF_UNLOCK_RESULT) { callback() } + put(CALL_IGNORED_ERRORS_IF_UNLOCK, packageName) + } + } + + /** + * 忽略 [packageName] 的错误直到设备重新启动 + * @param context 实例 + * @param packageName APP 包名 + * @param callback 成功后回调 + */ + fun ignoredErrorsIfRestart(context: Context, packageName: String, callback: () -> Unit) { + context.dataChannel(SYSTEM_FRAMEWORK_NAME).with { + wait(CALL_IGNORED_ERRORS_IF_RESTART_RESULT) { callback() } + put(CALL_IGNORED_ERRORS_IF_RESTART, packageName) + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_app_errors_display.xml b/app/src/main/res/layout/activity_app_errors_display.xml new file mode 100644 index 0000000..98f2dc5 --- /dev/null +++ b/app/src/main/res/layout/activity_app_errors_display.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/dia_app_errors_display.xml b/app/src/main/res/layout/dia_app_errors_display.xml new file mode 100644 index 0000000..dc08da5 --- /dev/null +++ b/app/src/main/res/layout/dia_app_errors_display.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 8680cca..232a50c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,7 @@ AppErrorsTracking Added more features to app\'s crash dialog, fixed custom rom deleted dialog, the best experience to Android developer. + App\'s Info Reopen App Error Detail diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index a8d6a1c..603bd03 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -13,4 +13,15 @@ ?attr/colorPrimaryVariant + + + \ No newline at end of file