mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-01 16:55:18 +08:00
Added errors record clear all function and changed receiver mode
This commit is contained in:
@@ -52,7 +52,7 @@ Added more features to app's crash dialog, fixed custom rom deleted dialog, the
|
||||
|
||||
此项目依然在开发中,现在未解决的问题和包含的问题如下
|
||||
|
||||
- 排除列表和异常历史记录导出、清空功能正在开发
|
||||
- 排除列表和异常历史记录导出功能正在开发
|
||||
|
||||
- 后台进程可能依然会弹出崩溃对话框且开发者选项里的设置无效,还在排查
|
||||
|
||||
|
@@ -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"
|
||||
}
|
@@ -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 -> {}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
@@ -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]
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
15
app/src/main/res/drawable/ic_android.xml
Normal file
15
app/src/main/res/drawable/ic_android.xml
Normal 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>
|
@@ -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
|
||||
|
Reference in New Issue
Block a user