Added errors record clear all function and changed receiver mode

This commit is contained in:
2022-05-12 14:15:19 +08:00
parent 7348ceba94
commit c4d1c42faa
9 changed files with 194 additions and 63 deletions

View File

@@ -52,7 +52,7 @@ Added more features to app's crash dialog, fixed custom rom deleted dialog, the
此项目依然在开发中,现在未解决的问题和包含的问题如下
- 排除列表和异常历史记录导出、清空功能正在开发
- 排除列表和异常历史记录导出功能正在开发
- 后台进程可能依然会弹出崩溃对话框且开发者选项里的设置无效,还在排查

View File

@@ -37,12 +37,15 @@ object Const {
/** 宿主接收广播 */
const val ACTION_HOST_HANDLER_RECEIVER = "host_handler_action"
/** [AppErrorsInfoBean] 控制数据 */
const val TYPE_APP_ERRORS_DATA_CONTROL = "app_errors_data_control_type"
/** 模块与宿主交互数据 */
const val KEY_MODULE_HOST_FETCH = "module_host_data_fetch_key"
/** [AppErrorsInfoBean] 获取数据 */
const val TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA = "app_errors_data_control_get_data_type"
/** 获取 [AppErrorsInfoBean] 数据 */
const val TYPE_APP_ERRORS_DATA_GET = "app_errors_data_get_type"
/** [AppErrorsInfoBean] 获取到的数据内容 */
/** 清空 [AppErrorsInfoBean] 数据 */
const val TYPE_APP_ERRORS_DATA_CLEAR = "app_errors_data_clear_type"
/** 获取到的 [AppErrorsInfoBean] 数据内容 */
const val TAG_APP_ERRORS_DATA_CONTENT = "app_errors_data_content_tag"
}

View File

@@ -76,13 +76,13 @@ object FrameworkHooker : YukiBaseHooker() {
)
/** 已打开的错误对话框数组 */
private var openedErrorsDialogs = HashMap<String, DialogBuilder>()
private var openedErrorsDialogs = hashMapOf<String, DialogBuilder>()
/** 已忽略错误的 APP 数组 - 直到重新解锁 */
private var ignoredErrorsIfUnlockApps = HashSet<String>()
private var ignoredErrorsIfUnlockApps = hashSetOf<String>()
/** 已忽略错误的 APP 数组 - 直到重新启动 */
private var ignoredErrorsIfRestartApps = HashSet<String>()
private var ignoredErrorsIfRestartApps = hashSetOf<String>()
/** 已记录的 APP 异常信息数组 - 直到重新启动 */
private val appErrorsRecords = arrayListOf<AppErrorsInfoBean>()
@@ -115,13 +115,21 @@ object FrameworkHooker : YukiBaseHooker() {
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null) return
when (intent.getStringExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL)) {
Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA ->
context?.sendBroadcast(Intent().apply {
action = Const.ACTION_MODULE_HANDLER_RECEIVER
putExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL, Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA)
putExtra(Const.TAG_APP_ERRORS_DATA_CONTENT, appErrorsRecords)
})
intent.getStringExtra(Const.KEY_MODULE_HOST_FETCH)?.also {
if (it.isNotBlank()) context?.sendBroadcast(Intent().apply {
action = Const.ACTION_MODULE_HANDLER_RECEIVER
when (it) {
Const.TYPE_APP_ERRORS_DATA_GET -> {
putExtra(Const.TAG_APP_ERRORS_DATA_CONTENT, appErrorsRecords)
putExtra(Const.KEY_MODULE_HOST_FETCH, Const.TYPE_APP_ERRORS_DATA_GET)
}
Const.TYPE_APP_ERRORS_DATA_CLEAR -> {
appErrorsRecords.clear()
putExtra(Const.KEY_MODULE_HOST_FETCH, Const.TYPE_APP_ERRORS_DATA_CLEAR)
}
else -> {}
}
})
}
}
}

View File

@@ -19,14 +19,8 @@
*
* This file is Created by fankes on 2022/5/11.
*/
@file:Suppress("UNCHECKED_CAST")
package com.fankes.apperrorstracking.ui.activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -34,13 +28,15 @@ import android.widget.BaseAdapter
import androidx.core.view.isVisible
import com.fankes.apperrorstracking.R
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
import com.fankes.apperrorstracking.const.Const
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsRecordBinding
import com.fankes.apperrorstracking.databinding.AdapterAppErrorsRecordBinding
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
import com.fankes.apperrorstracking.utils.factory.appIcon
import com.fankes.apperrorstracking.utils.factory.appName
import com.fankes.apperrorstracking.utils.factory.showDialog
import com.fankes.apperrorstracking.utils.factory.toast
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
import java.text.SimpleDateFormat
import java.util.*
@@ -49,14 +45,23 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
/** 回调适配器改变 */
private var onChanged: (() -> Unit)? = null
/** 全部的 APP 异常数据 */
private val listData = ArrayList<AppErrorsInfoBean>()
/** 全部的 APP 异常信息 */
private val listData = arrayListOf<AppErrorsInfoBean>()
override fun onCreate() {
binding.titleBackIcon.setOnClickListener { onBackPressed() }
binding.clearAllIcon.setOnClickListener {
// TODO 待实现
toast(msg = "Coming soon")
showDialog {
title = LocaleString.notice
msg = LocaleString.areYouSureClearErrors
confirmButton {
FrameworkTool.clearAppErrorsInfoData(context) {
refreshData()
toast(LocaleString.allErrorsClearSuccess)
}
}
cancelButton()
}
}
binding.exportAllIcon.setOnClickListener {
// TODO 待实现
@@ -97,34 +102,18 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
onChanged = { notifyDataSetChanged() }
}
}
/** 注册广播 */
registerReceiver(moduleHandlerReceiver, IntentFilter().apply { addAction(Const.ACTION_MODULE_HANDLER_RECEIVER) })
}
/** 更新列表数据 */
private fun refreshData() {
sendBroadcast(Intent().apply {
action = Const.ACTION_HOST_HANDLER_RECEIVER
putExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL, Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA)
})
}
/** 模块广播接收器 */
private val moduleHandlerReceiver by lazy {
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null) return
when (intent.getStringExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL)) {
Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA ->
(intent.getSerializableExtra(Const.TAG_APP_ERRORS_DATA_CONTENT) as? ArrayList<AppErrorsInfoBean>)?.also {
listData.clear()
it.takeIf { e -> e.isNotEmpty() }?.forEach { e -> listData.add(e) }
onChanged?.invoke()
binding.listView.isVisible = listData.isNotEmpty()
binding.listNoDataView.isVisible = listData.isEmpty()
}
}
}
FrameworkTool.fetchAppErrorsInfoData(context = this) {
listData.clear()
it.takeIf { e -> e.isNotEmpty() }?.forEach { e -> listData.add(e) }
onChanged?.invoke()
binding.clearAllIcon.isVisible = listData.isNotEmpty()
binding.exportAllIcon.isVisible = listData.isNotEmpty()
binding.listView.isVisible = listData.isNotEmpty()
binding.listNoDataView.isVisible = listData.isEmpty()
}
}
@@ -133,10 +122,4 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
/** 执行更新 */
refreshData()
}
override fun onDestroy() {
super.onDestroy()
/** 取消注册 */
unregisterReceiver(moduleHandlerReceiver)
}
}

View File

@@ -28,6 +28,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.fankes.apperrorstracking.R
import com.fankes.apperrorstracking.utils.factory.isNotSystemInDarkMode
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
import com.gyf.immersionbar.ktx.immersionBar
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
@@ -60,10 +61,18 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
navigationBarDarkIcon(isNotSystemInDarkMode)
fitsSystemWindows(true)
}
/** 注册 */
FrameworkTool.registerReceiver(context = this)
/** 装载子类 */
onCreate()
}
/** 回调 [onCreate] 方法 */
abstract fun onCreate()
override fun onDestroy() {
super.onDestroy()
/** 取消 */
FrameworkTool.unregisterReceiver(context = this)
}
}

View File

@@ -28,13 +28,13 @@ import android.app.Service
import android.content.*
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.provider.Settings
import android.widget.Toast
import androidx.core.content.res.ResourcesCompat
import com.fankes.apperrorstracking.BuildConfig
import com.fankes.apperrorstracking.R
import com.fankes.apperrorstracking.locale.LocaleString
/**
@@ -82,7 +82,7 @@ fun Context.appName(packageName: String) =
fun Context.appVersion(packageName: String) =
runCatching {
packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)?.let { "${it.versionName} (${it.versionCode})" }
}.getOrNull() ?: "unknown"
}.getOrNull() ?: "<unknown>"
/**
* 获取 APP 图标
@@ -93,7 +93,7 @@ fun Context.appIcon(packageName: String) =
runCatching {
packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)
.applicationInfo.loadIcon(packageManager)
}.getOrNull() ?: ColorDrawable(Color.WHITE)
}.getOrNull() ?: ResourcesCompat.getDrawable(resources, R.drawable.ic_android, null)
/**
* 弹出 [Toast]

View File

@@ -0,0 +1,111 @@
/*
* 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/12.
*/
@file:Suppress("UNCHECKED_CAST")
package com.fankes.apperrorstracking.utils.tool
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
import com.fankes.apperrorstracking.const.Const
/**
* 系统框架控制工具
*/
object FrameworkTool {
/** 回调获取的 APP 异常信息 */
private var onAppErrorsInfoDataCallback: ((ArrayList<AppErrorsInfoBean>) -> Unit)? = null
/** 回调 APP 异常信息是否清空 */
private var onClearAppErrorsInfoDataCallback: (() -> Unit)? = null
/** 模块广播接收器 */
private val moduleHandlerReceiver by lazy {
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null) return
intent.getStringExtra(Const.KEY_MODULE_HOST_FETCH)?.also {
if (it.isNotBlank()) when (it) {
Const.TYPE_APP_ERRORS_DATA_GET -> runCatching {
onAppErrorsInfoDataCallback?.invoke(
intent.getSerializableExtra(Const.TAG_APP_ERRORS_DATA_CONTENT) as ArrayList<AppErrorsInfoBean>
)
}.onFailure { onAppErrorsInfoDataCallback?.invoke(arrayListOf()) }
Const.TYPE_APP_ERRORS_DATA_CLEAR -> onClearAppErrorsInfoDataCallback?.invoke()
else -> {}
}
}
}
}
}
/**
* 发送广播
* @param context 实例
* @param type 类型
*/
private fun pushReceiver(context: Context, type: String) {
context.sendBroadcast(Intent().apply {
action = Const.ACTION_HOST_HANDLER_RECEIVER
putExtra(Const.KEY_MODULE_HOST_FETCH, type)
})
}
/**
* 获取 APP 异常信息数组
* @param context 实例
* @param it 回调数据
*/
fun fetchAppErrorsInfoData(context: Context, it: (ArrayList<AppErrorsInfoBean>) -> Unit) {
onAppErrorsInfoDataCallback = it
pushReceiver(context, Const.TYPE_APP_ERRORS_DATA_GET)
}
/**
* 清空 APP 异常信息数组
* @param context 实例
* @param it 成功后回调
*/
fun clearAppErrorsInfoData(context: Context, it: () -> Unit) {
onClearAppErrorsInfoDataCallback = it
pushReceiver(context, Const.TYPE_APP_ERRORS_DATA_CLEAR)
}
/**
* 注册广播
* @param context 实例
*/
fun registerReceiver(context: Context) = runCatching {
context.registerReceiver(moduleHandlerReceiver, IntentFilter().apply { addAction(Const.ACTION_MODULE_HANDLER_RECEIVER) })
}
/**
* 取消注册
* @param context 实例
*/
fun unregisterReceiver(context: Context) = runCatching {
context.unregisterReceiver(moduleHandlerReceiver)
}
}

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="250dp"
android:height="250dp"
android:viewportWidth="1024"
android:viewportHeight="1024"
tools:ignore="VectorRaster">
<path
android:fillColor="#A0C538"
android:pathData="M142.2,56.9h739.6a85.3,85.3 0,0 1,85.3 85.3v739.6a85.3,85.3 0,0 1,-85.3 85.3H142.2a85.3,85.3 0,0 1,-85.3 -85.3V142.2a85.3,85.3 0,0 1,85.3 -85.3z" />
<path
android:fillColor="#FFFFFF"
android:pathData="M636.5,561.5c0,14.5 11.6,26.7 25.7,26.7 14.4,0 25.7,-12.1 25.7,-26.7v-102.7c0,-14.7 -11.4,-26.4 -25.7,-26.4 -14.2,0 -25.7,11.7 -25.7,26.4v102.7zM544.6,371.8c0,-6.9 5.8,-12.6 12.6,-12.6 7.1,0 12.9,5.6 12.9,12.6a12.7,12.7 0,1 1,-25.5 0zM446.9,371.8a12.5,12.5 0,0 1,25.1 0,12.6 12.6,0 0,1 -12.6,12.8 12.6,12.6 0,0 1,-12.4 -12.8zM414.1,641.7h22.7v57.2c0,14.3 11.1,26.4 25.7,26.4 13.9,0 25.7,-12.1 25.7,-26.4v-57.2h39.9v57.2c0,14.3 10.9,26.4 25.5,26.4 14.4,0 25.7,-12.1 25.7,-26.4v-57.2h22.7c11.3,0 20.1,-9.1 20.1,-20.4L622.2,433.1L394,433.1v188.3c0,11.2 8.8,20.4 20.2,20.4zM621.7,416.6c-3.4,-35.5 -26.6,-65.2 -60,-80.8l21,-31c1.4,-2 1.1,-4.6 -0.6,-5.4 -1.7,-1.1 -3.6,-0.4 -5.1,1.3l-22.1,32.1c-14.8,-5.4 -30.4,-8.9 -47.3,-8.9s-33,3.5 -47.6,8.9l-21.6,-32.1c-1.5,-1.7 -3.4,-2.8 -5.1,-1.3 -1.7,1.1 -2.2,3.4 -0.9,5.4l21.2,31c-33.4,16 -57,45.1 -60,80.8h228.2zM327.1,561.5c0,14.5 11.2,26.7 25.7,26.7 13.9,0 25.7,-12.1 25.7,-26.7v-102.7c0,-14.7 -11.8,-26.4 -25.7,-26.4 -14.6,0 -25.7,11.7 -25.7,26.4v102.7z"
tools:ignore="VectorPath" />
</vector>

View File

@@ -47,7 +47,8 @@
android:layout_marginEnd="15dp"
android:src="@drawable/ic_clear"
android:tint="@color/colorTextGray"
android:tooltipText="@string/clear_all" />
android:tooltipText="@string/clear_all"
android:visibility="gone" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/export_all_icon"
@@ -56,7 +57,8 @@
android:layout_marginEnd="10dp"
android:src="@drawable/ic_export"
android:tint="@color/colorTextGray"
android:tooltipText="@string/export_all" />
android:tooltipText="@string/export_all"
android:visibility="gone" />
</LinearLayout>
<FrameLayout