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" 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] 获取数据 */ /** 获取 [AppErrorsInfoBean] 数据 */
const val TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA = "app_errors_data_control_get_data_type" 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" 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 数组 - 直到重新解锁 */ /** 已忽略错误的 APP 数组 - 直到重新解锁 */
private var ignoredErrorsIfUnlockApps = HashSet<String>() private var ignoredErrorsIfUnlockApps = hashSetOf<String>()
/** 已忽略错误的 APP 数组 - 直到重新启动 */ /** 已忽略错误的 APP 数组 - 直到重新启动 */
private var ignoredErrorsIfRestartApps = HashSet<String>() private var ignoredErrorsIfRestartApps = hashSetOf<String>()
/** 已记录的 APP 异常信息数组 - 直到重新启动 */ /** 已记录的 APP 异常信息数组 - 直到重新启动 */
private val appErrorsRecords = arrayListOf<AppErrorsInfoBean>() private val appErrorsRecords = arrayListOf<AppErrorsInfoBean>()
@@ -115,13 +115,21 @@ object FrameworkHooker : YukiBaseHooker() {
object : BroadcastReceiver() { object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null) return if (intent == null) return
when (intent.getStringExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL)) { intent.getStringExtra(Const.KEY_MODULE_HOST_FETCH)?.also {
Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA -> if (it.isNotBlank()) context?.sendBroadcast(Intent().apply {
context?.sendBroadcast(Intent().apply { action = Const.ACTION_MODULE_HANDLER_RECEIVER
action = Const.ACTION_MODULE_HANDLER_RECEIVER when (it) {
putExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL, Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA) Const.TYPE_APP_ERRORS_DATA_GET -> {
putExtra(Const.TAG_APP_ERRORS_DATA_CONTENT, appErrorsRecords) 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. * This file is Created by fankes on 2022/5/11.
*/ */
@file:Suppress("UNCHECKED_CAST")
package com.fankes.apperrorstracking.ui.activity 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.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -34,13 +28,15 @@ import android.widget.BaseAdapter
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.fankes.apperrorstracking.R import com.fankes.apperrorstracking.R
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
import com.fankes.apperrorstracking.const.Const
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsRecordBinding import com.fankes.apperrorstracking.databinding.ActivityAppErrorsRecordBinding
import com.fankes.apperrorstracking.databinding.AdapterAppErrorsRecordBinding import com.fankes.apperrorstracking.databinding.AdapterAppErrorsRecordBinding
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
import com.fankes.apperrorstracking.utils.factory.appIcon import com.fankes.apperrorstracking.utils.factory.appIcon
import com.fankes.apperrorstracking.utils.factory.appName 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.factory.toast
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@@ -49,14 +45,23 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
/** 回调适配器改变 */ /** 回调适配器改变 */
private var onChanged: (() -> Unit)? = null private var onChanged: (() -> Unit)? = null
/** 全部的 APP 异常数据 */ /** 全部的 APP 异常信息 */
private val listData = ArrayList<AppErrorsInfoBean>() private val listData = arrayListOf<AppErrorsInfoBean>()
override fun onCreate() { override fun onCreate() {
binding.titleBackIcon.setOnClickListener { onBackPressed() } binding.titleBackIcon.setOnClickListener { onBackPressed() }
binding.clearAllIcon.setOnClickListener { binding.clearAllIcon.setOnClickListener {
// TODO 待实现 showDialog {
toast(msg = "Coming soon") title = LocaleString.notice
msg = LocaleString.areYouSureClearErrors
confirmButton {
FrameworkTool.clearAppErrorsInfoData(context) {
refreshData()
toast(LocaleString.allErrorsClearSuccess)
}
}
cancelButton()
}
} }
binding.exportAllIcon.setOnClickListener { binding.exportAllIcon.setOnClickListener {
// TODO 待实现 // TODO 待实现
@@ -97,34 +102,18 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
onChanged = { notifyDataSetChanged() } onChanged = { notifyDataSetChanged() }
} }
} }
/** 注册广播 */
registerReceiver(moduleHandlerReceiver, IntentFilter().apply { addAction(Const.ACTION_MODULE_HANDLER_RECEIVER) })
} }
/** 更新列表数据 */ /** 更新列表数据 */
private fun refreshData() { private fun refreshData() {
sendBroadcast(Intent().apply { FrameworkTool.fetchAppErrorsInfoData(context = this) {
action = Const.ACTION_HOST_HANDLER_RECEIVER listData.clear()
putExtra(Const.TYPE_APP_ERRORS_DATA_CONTROL, Const.TYPE_APP_ERRORS_DATA_CONTROL_GET_DATA) 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()
private val moduleHandlerReceiver by lazy { binding.listNoDataView.isVisible = listData.isEmpty()
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()
}
}
}
} }
} }
@@ -133,10 +122,4 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
/** 执行更新 */ /** 执行更新 */
refreshData() refreshData()
} }
override fun onDestroy() {
super.onDestroy()
/** 取消注册 */
unregisterReceiver(moduleHandlerReceiver)
}
} }

View File

@@ -28,6 +28,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.fankes.apperrorstracking.R import com.fankes.apperrorstracking.R
import com.fankes.apperrorstracking.utils.factory.isNotSystemInDarkMode import com.fankes.apperrorstracking.utils.factory.isNotSystemInDarkMode
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
import com.gyf.immersionbar.ktx.immersionBar import com.gyf.immersionbar.ktx.immersionBar
import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
@@ -60,10 +61,18 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
navigationBarDarkIcon(isNotSystemInDarkMode) navigationBarDarkIcon(isNotSystemInDarkMode)
fitsSystemWindows(true) fitsSystemWindows(true)
} }
/** 注册 */
FrameworkTool.registerReceiver(context = this)
/** 装载子类 */ /** 装载子类 */
onCreate() onCreate()
} }
/** 回调 [onCreate] 方法 */ /** 回调 [onCreate] 方法 */
abstract fun 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.*
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.provider.Settings import android.provider.Settings
import android.widget.Toast import android.widget.Toast
import androidx.core.content.res.ResourcesCompat
import com.fankes.apperrorstracking.BuildConfig import com.fankes.apperrorstracking.BuildConfig
import com.fankes.apperrorstracking.R
import com.fankes.apperrorstracking.locale.LocaleString import com.fankes.apperrorstracking.locale.LocaleString
/** /**
@@ -82,7 +82,7 @@ fun Context.appName(packageName: String) =
fun Context.appVersion(packageName: String) = fun Context.appVersion(packageName: String) =
runCatching { runCatching {
packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)?.let { "${it.versionName} (${it.versionCode})" } packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)?.let { "${it.versionName} (${it.versionCode})" }
}.getOrNull() ?: "unknown" }.getOrNull() ?: "<unknown>"
/** /**
* 获取 APP 图标 * 获取 APP 图标
@@ -93,7 +93,7 @@ fun Context.appIcon(packageName: String) =
runCatching { runCatching {
packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA) packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)
.applicationInfo.loadIcon(packageManager) .applicationInfo.loadIcon(packageManager)
}.getOrNull() ?: ColorDrawable(Color.WHITE) }.getOrNull() ?: ResourcesCompat.getDrawable(resources, R.drawable.ic_android, null)
/** /**
* 弹出 [Toast] * 弹出 [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:layout_marginEnd="15dp"
android:src="@drawable/ic_clear" android:src="@drawable/ic_clear"
android:tint="@color/colorTextGray" android:tint="@color/colorTextGray"
android:tooltipText="@string/clear_all" /> android:tooltipText="@string/clear_all"
android:visibility="gone" />
<androidx.constraintlayout.utils.widget.ImageFilterView <androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/export_all_icon" android:id="@+id/export_all_icon"
@@ -56,7 +57,8 @@
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:src="@drawable/ic_export" android:src="@drawable/ic_export"
android:tint="@color/colorTextGray" android:tint="@color/colorTextGray"
android:tooltipText="@string/export_all" /> android:tooltipText="@string/export_all"
android:visibility="gone" />
</LinearLayout> </LinearLayout>
<FrameLayout <FrameLayout