Added new style in app errors dialog and merge code from FrameworkHooker

This commit is contained in:
2022-06-01 03:06:15 +08:00
parent 698d2dc8e0
commit ded9da730f
14 changed files with 476 additions and 158 deletions

View File

@@ -54,6 +54,16 @@
</intent-filter>
</activity-alias>
<activity
android:name=".ui.activity.errors.AppErrorsDisplayActivity"
android:excludeFromRecents="true"
android:exported="true"
android:label="@string/empty_lable"
android:launchMode="singleTask"
android:screenOrientation="behind"
android:taskAffinity=":display"
android:theme="@style/Theme.AppErrorsTracking.Translucent" />
<activity
android:name=".ui.activity.errors.AppErrorsDetailActivity"
android:exported="true"

View File

@@ -0,0 +1,40 @@
/*
* 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
* <https://www.gnu.org/licenses/>
*
* 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

View File

@@ -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<String, DialogBuilder>()
/** 已忽略错误的 APP 数组 - 直到重新解锁 */
private var ignoredErrorsIfUnlockApps = hashSetOf<String>()
@@ -84,45 +73,6 @@ object FrameworkHooker : YukiBaseHooker() {
/** 已记录的 APP 异常信息数组 - 直到重新启动 */
private val appErrorsRecords = arrayListOf<AppErrorsInfoBean>()
/**
* 获取最新的 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 {

View File

@@ -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<VB : ViewBinding> : AppCompatActivity() {
/** 回调 [onCreate] 方法 */
abstract fun onCreate()
/**
* 弹出提示并退出
* @param name 名称
*/
fun toastAndFinish(name: String) {
toast(msg = "Invalid $name, exit")
finish()
}
}

View File

@@ -54,10 +54,9 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
* 启动 [AppErrorsDetailActivity]
* @param context 实例
* @param appErrorsInfo 应用异常信息
* @param isOutSide 是否从外部启动
*/
fun start(context: Context, appErrorsInfo: AppErrorsInfoBean, isOutSide: Boolean = false) =
context.navigate<AppErrorsDetailActivity>(isOutSide) { putExtra(EXTRA_APP_ERRORS_INFO, appErrorsInfo) }
fun start(context: Context, appErrorsInfo: AppErrorsInfoBean) =
context.navigate<AppErrorsDetailActivity> { putExtra(EXTRA_APP_ERRORS_INFO, appErrorsInfo) }
}
/** 预导出的异常堆栈 */
@@ -89,7 +88,7 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
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<ActivityAppErrorsDetailBinding>() {
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 {

View File

@@ -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
* <https://www.gnu.org/licenses/>
*
* 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<ActivityAppErrorsDisplayBinding>() {
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<AppErrorsDisplayActivity>(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<DiaAppErrorsDisplayBinding>().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
}
}

View File

@@ -82,7 +82,7 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
}
}
/** 设置列表元素和 Adapter */
binding.listView.apply { // 改成 recycleview?
binding.listView.apply {
adapter = object : BaseAdapter() {
override fun getCount() = listData.size

View File

@@ -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
* <https://www.gnu.org/licenses/>
*
* 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()
}
}

View File

@@ -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 <reified T : ViewBinding> bind() =
T::class.java.method {
name = "inflate"
param(LayoutInflaterClass)
}.get().invoke<T>(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()
}
}

View File

@@ -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<String>("call_open_specify_app")
private val CALL_APP_ERRORS_DATA_REMOVE = ChannelData<AppErrorsInfoBean>("call_app_errors_data_remove")
private val CALL_APP_ERRORS_DATA_GET_RESULT = ChannelData<ArrayList<AppErrorsInfoBean>>("call_app_errors_data_get_result")
private val CALL_IGNORED_ERRORS_IF_UNLOCK = ChannelData<String>("call_ignored_errors_if_unlock")
private val CALL_IGNORED_ERRORS_IF_RESTART = ChannelData<String>("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)
}
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@@ -0,0 +1,141 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
android:id="@+id/app_info_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:paddingVertical="15dp">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="15dp"
android:padding="1dp"
android:src="@drawable/ic_baseline_info"
app:tint="@color/colorTextGray" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/app_info"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
android:id="@+id/close_app_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:paddingVertical="15dp">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="15dp"
android:src="@drawable/ic_baseline_close"
app:tint="@color/colorTextGray" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/close_app"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
android:id="@+id/reopen_app_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:paddingVertical="15dp">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="15dp"
android:src="@drawable/ic_baseline_refresh"
app:tint="@color/colorTextGray" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/reopen_app"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
android:id="@+id/error_detail_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:paddingVertical="15dp">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="15dp"
android:src="@drawable/ic_baseline_bug_report"
app:tint="@color/colorTextGray" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/error_detail"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
android:id="@+id/ignore_if_unlock_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:paddingVertical="15dp">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="15dp"
android:src="@drawable/ic_baseline_eject"
app:tint="@color/colorTextGray" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/ignore_if_unlock"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
<com.fankes.apperrorstracking.ui.view.ItemLinearLayout
android:id="@+id/ignore_if_restart_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:paddingVertical="15dp">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="15dp"
android:src="@drawable/ic_baseline_eject"
app:tint="@color/colorTextGray" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/ignore_if_restart"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
</com.fankes.apperrorstracking.ui.view.ItemLinearLayout>
</LinearLayout>

View File

@@ -1,6 +1,7 @@
<resources>
<string name="app_name">AppErrorsTracking</string>
<string name="xposed_desc">Added more features to app\'s crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.</string>
<string name="empty_lable" translatable="false" />
<string name="app_info">App\'s Info</string>
<string name="reopen_app">Reopen App</string>
<string name="error_detail">Error Detail</string>

View File

@@ -13,4 +13,15 @@
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
<!-- Translucent application theme. -->
<style name="Theme.AppErrorsTracking.Translucent" parent="Theme.AppErrorsTracking">
<item name="android:background">@android:color/transparent</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowIsTranslucent">true</item>
</style>
</resources>