mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-01 16:55:18 +08:00
Merge module and host receiver to new api
This commit is contained in:
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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("MemberVisibilityCanBePrivate")
|
|
||||||
|
|
||||||
package com.fankes.apperrorstracking.const
|
|
||||||
|
|
||||||
import com.fankes.apperrorstracking.BuildConfig
|
|
||||||
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 全局常量
|
|
||||||
*/
|
|
||||||
object Const {
|
|
||||||
|
|
||||||
/** 当前模块的包名 */
|
|
||||||
const val MODULE_PACKAGE_NAME = BuildConfig.APPLICATION_ID
|
|
||||||
|
|
||||||
/** 当前模块的版本名称 */
|
|
||||||
const val MODULE_VERSION_NAME = BuildConfig.VERSION_NAME
|
|
||||||
|
|
||||||
/** 当前模块的版本号 */
|
|
||||||
const val MODULE_VERSION_CODE = BuildConfig.VERSION_CODE
|
|
||||||
|
|
||||||
/** 当前模块的版本校验 */
|
|
||||||
const val MODULE_VERSION_VERIFY = "${MODULE_VERSION_NAME}_${MODULE_VERSION_CODE}_202205141842"
|
|
||||||
|
|
||||||
/** [AppErrorsInfoBean] 传值 */
|
|
||||||
const val EXTRA_APP_ERRORS_INFO = "app_errors_info_extra"
|
|
||||||
|
|
||||||
/** 模块接收广播 */
|
|
||||||
const val ACTION_MODULE_HANDLER_RECEIVER = "module_handler_action"
|
|
||||||
|
|
||||||
/** 宿主接收广播 */
|
|
||||||
const val ACTION_HOST_HANDLER_RECEIVER = "host_handler_action"
|
|
||||||
|
|
||||||
/** 模块与宿主交互数据 */
|
|
||||||
const val KEY_MODULE_HOST_FETCH = "module_host_data_fetch_key"
|
|
||||||
|
|
||||||
/** 校验模块宿主版本一致性 */
|
|
||||||
const val TYPE_MODULE_VERSION_VERIFY = "module_version_verify_type"
|
|
||||||
|
|
||||||
/** 获取 [AppErrorsInfoBean] 数据 */
|
|
||||||
const val TYPE_APP_ERRORS_DATA_GET = "app_errors_data_get_type"
|
|
||||||
|
|
||||||
/** 清空 [AppErrorsInfoBean] 数据 */
|
|
||||||
const val TYPE_APP_ERRORS_DATA_CLEAR = "app_errors_data_clear_type"
|
|
||||||
|
|
||||||
/** 删除指定 [AppErrorsInfoBean] 数据 */
|
|
||||||
const val TYPE_APP_ERRORS_DATA_REMOVE = "app_errors_data_remove_type"
|
|
||||||
|
|
||||||
/** 当前模块的版本校验标签 */
|
|
||||||
const val TAG_MODULE_VERSION_VERIFY = "module_version_verify_tag"
|
|
||||||
|
|
||||||
/** 获取到的 [AppErrorsInfoBean] 数据内容 */
|
|
||||||
const val TAG_APP_ERRORS_DATA_GET_CONTENT = "app_errors_data_get_content_tag"
|
|
||||||
|
|
||||||
/** 指定删除的 [AppErrorsInfoBean] 数据内容 */
|
|
||||||
const val TAG_APP_ERRORS_DATA_REMOVE_CONTENT = "app_errors_data_remove_content_tag"
|
|
||||||
}
|
|
@@ -24,10 +24,8 @@
|
|||||||
package com.fankes.apperrorstracking.hook.entity
|
package com.fankes.apperrorstracking.hook.entity
|
||||||
|
|
||||||
import android.app.ApplicationErrorReport
|
import android.app.ApplicationErrorReport
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Message
|
import android.os.Message
|
||||||
@@ -41,18 +39,17 @@ import android.widget.TextView
|
|||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
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.locale.LocaleString
|
import com.fankes.apperrorstracking.locale.LocaleString
|
||||||
import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsDetailActivity
|
import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsDetailActivity
|
||||||
import com.fankes.apperrorstracking.utils.drawable.drawabletoolbox.DrawableBuilder
|
import com.fankes.apperrorstracking.utils.drawable.drawabletoolbox.DrawableBuilder
|
||||||
import com.fankes.apperrorstracking.utils.factory.*
|
import com.fankes.apperrorstracking.utils.factory.*
|
||||||
|
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
|
||||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||||
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||||
import com.highcapable.yukihookapi.hook.factory.field
|
import com.highcapable.yukihookapi.hook.factory.field
|
||||||
import com.highcapable.yukihookapi.hook.factory.hasMethod
|
import com.highcapable.yukihookapi.hook.factory.hasMethod
|
||||||
import com.highcapable.yukihookapi.hook.factory.method
|
import com.highcapable.yukihookapi.hook.factory.method
|
||||||
import com.highcapable.yukihookapi.hook.log.loggerE
|
import com.highcapable.yukihookapi.hook.log.loggerE
|
||||||
import com.highcapable.yukihookapi.hook.type.android.ActivityThreadClass
|
|
||||||
import com.highcapable.yukihookapi.hook.type.android.MessageClass
|
import com.highcapable.yukihookapi.hook.type.android.MessageClass
|
||||||
|
|
||||||
object FrameworkHooker : YukiBaseHooker() {
|
object FrameworkHooker : YukiBaseHooker() {
|
||||||
@@ -87,75 +84,6 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
/** 已记录的 APP 异常信息数组 - 直到重新启动 */
|
/** 已记录的 APP 异常信息数组 - 直到重新启动 */
|
||||||
private val appErrorsRecords = arrayListOf<AppErrorsInfoBean>()
|
private val appErrorsRecords = arrayListOf<AppErrorsInfoBean>()
|
||||||
|
|
||||||
/** 是否已经注册广播 */
|
|
||||||
private var isRegisterReceiver = false
|
|
||||||
|
|
||||||
/** 用户解锁屏幕广播接收器 */
|
|
||||||
private val userPresentReceiver by lazy {
|
|
||||||
object : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
|
||||||
/** 解锁后清空已记录的忽略错误 APP */
|
|
||||||
ignoredErrorsIfUnlockApps.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 语言区域改变广播接收器 */
|
|
||||||
private val localeChangedReceiver by lazy {
|
|
||||||
object : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
|
||||||
/** 刷新模块 Resources 缓存 */
|
|
||||||
refreshModuleAppResources()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 宿主广播接收器 */
|
|
||||||
private val hostHandlerReceiver 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()) context?.sendBroadcast(Intent().apply {
|
|
||||||
action = Const.ACTION_MODULE_HANDLER_RECEIVER
|
|
||||||
when (it) {
|
|
||||||
Const.TYPE_MODULE_VERSION_VERIFY -> {
|
|
||||||
putExtra(Const.TAG_MODULE_VERSION_VERIFY, Const.MODULE_VERSION_VERIFY)
|
|
||||||
putExtra(Const.KEY_MODULE_HOST_FETCH, Const.TYPE_MODULE_VERSION_VERIFY)
|
|
||||||
}
|
|
||||||
Const.TYPE_APP_ERRORS_DATA_GET -> {
|
|
||||||
putExtra(Const.TAG_APP_ERRORS_DATA_GET_CONTENT, appErrorsRecords)
|
|
||||||
putExtra(Const.KEY_MODULE_HOST_FETCH, Const.TYPE_APP_ERRORS_DATA_GET)
|
|
||||||
}
|
|
||||||
Const.TYPE_APP_ERRORS_DATA_REMOVE -> {
|
|
||||||
runCatching { intent.getSerializableExtra(Const.TAG_APP_ERRORS_DATA_REMOVE_CONTENT) as? AppErrorsInfoBean? }
|
|
||||||
.getOrNull()?.also { e -> appErrorsRecords.remove(e) }
|
|
||||||
putExtra(Const.KEY_MODULE_HOST_FETCH, Const.TYPE_APP_ERRORS_DATA_REMOVE)
|
|
||||||
}
|
|
||||||
Const.TYPE_APP_ERRORS_DATA_CLEAR -> {
|
|
||||||
appErrorsRecords.clear()
|
|
||||||
putExtra(Const.KEY_MODULE_HOST_FETCH, Const.TYPE_APP_ERRORS_DATA_CLEAR)
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册广播接收器
|
|
||||||
* @param context 实例
|
|
||||||
*/
|
|
||||||
private fun registerReceiver(context: Context) {
|
|
||||||
if (isRegisterReceiver) return
|
|
||||||
context.registerReceiver(userPresentReceiver, IntentFilter().apply { addAction(Intent.ACTION_USER_PRESENT) })
|
|
||||||
context.registerReceiver(localeChangedReceiver, IntentFilter().apply { addAction(Intent.ACTION_LOCALE_CHANGED) })
|
|
||||||
context.registerReceiver(hostHandlerReceiver, IntentFilter().apply { addAction(Const.ACTION_HOST_HANDLER_RECEIVER) })
|
|
||||||
isRegisterReceiver = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取最新的 APP 错误信息
|
* 获取最新的 APP 错误信息
|
||||||
* @param packageName 包名
|
* @param packageName 包名
|
||||||
@@ -195,17 +123,24 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
setOnClickListener { it() }
|
setOnClickListener { it() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onHook() {
|
/** 注册 */
|
||||||
/** 注入全局监听 */
|
private fun register() {
|
||||||
ActivityThreadClass.hook {
|
onAppLifecycle {
|
||||||
injectMember {
|
/** 解锁后清空已记录的忽略错误 APP */
|
||||||
method {
|
registerReceiver(Intent.ACTION_USER_PRESENT) { _, _ -> ignoredErrorsIfUnlockApps.clear() }
|
||||||
name = "currentApplication"
|
/** 刷新模块 Resources 缓存 */
|
||||||
emptyParam()
|
registerReceiver(Intent.ACTION_LOCALE_CHANGED) { _, _ -> refreshModuleAppResources() }
|
||||||
}
|
|
||||||
afterHook { result<Context>()?.let { registerReceiver(it) } }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
FrameworkTool.Host.with(instance = this) {
|
||||||
|
onPushAppErrorsInfoData { appErrorsRecords }
|
||||||
|
onRemoveAppErrorsInfoData { appErrorsRecords.remove(it) }
|
||||||
|
onClearAppErrorsInfoData { appErrorsRecords.clear() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onHook() {
|
||||||
|
/** 注册 */
|
||||||
|
register()
|
||||||
/** 干掉原生错误对话框 - 如果有 */
|
/** 干掉原生错误对话框 - 如果有 */
|
||||||
ErrorDialogControllerClass.hook {
|
ErrorDialogControllerClass.hook {
|
||||||
injectMember {
|
injectMember {
|
||||||
@@ -363,19 +298,19 @@ object FrameworkHooker : YukiBaseHooker() {
|
|||||||
(crashInfo.exceptionClassName.lowercase() == "native crash").also { isNativeCrash ->
|
(crashInfo.exceptionClassName.lowercase() == "native crash").also { isNativeCrash ->
|
||||||
appErrorsRecords.add(
|
appErrorsRecords.add(
|
||||||
0, AppErrorsInfoBean(
|
0, AppErrorsInfoBean(
|
||||||
packageName = appInfo?.packageName ?: "",
|
packageName = appInfo?.packageName ?: "null",
|
||||||
isNativeCrash = isNativeCrash,
|
isNativeCrash = isNativeCrash,
|
||||||
exceptionClassName = crashInfo.exceptionClassName ?: "",
|
exceptionClassName = crashInfo.exceptionClassName ?: "null",
|
||||||
exceptionMessage = if (isNativeCrash) crashInfo.stackTrace.let {
|
exceptionMessage = if (isNativeCrash) crashInfo.stackTrace.let {
|
||||||
if (it.contains(other = "Abort message: '"))
|
if (it.contains(other = "Abort message: '"))
|
||||||
runCatching { it.split("Abort message: '")[1].split("'")[0] }.getOrNull()
|
runCatching { it.split("Abort message: '")[1].split("'")[0] }.getOrNull()
|
||||||
?: crashInfo.exceptionMessage ?: "" else crashInfo.exceptionMessage ?: ""
|
?: crashInfo.exceptionMessage ?: "" else crashInfo.exceptionMessage ?: "null"
|
||||||
} else crashInfo.exceptionMessage ?: "",
|
} else crashInfo.exceptionMessage ?: "null",
|
||||||
throwFileName = crashInfo.throwFileName ?: "",
|
throwFileName = crashInfo.throwFileName ?: "null",
|
||||||
throwClassName = crashInfo.throwClassName ?: "",
|
throwClassName = crashInfo.throwClassName ?: "null",
|
||||||
throwMethodName = crashInfo.throwMethodName ?: "",
|
throwMethodName = crashInfo.throwMethodName ?: "null",
|
||||||
throwLineNumber = crashInfo.throwLineNumber,
|
throwLineNumber = crashInfo.throwLineNumber,
|
||||||
stackTrace = crashInfo.stackTrace?.trim() ?: "",
|
stackTrace = crashInfo.stackTrace?.trim() ?: "null",
|
||||||
timestamp = System.currentTimeMillis()
|
timestamp = System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@@ -31,7 +31,6 @@ import androidx.core.view.ViewCompat
|
|||||||
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.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
|
||||||
import java.lang.reflect.ParameterizedType
|
import java.lang.reflect.ParameterizedType
|
||||||
@@ -64,18 +63,10 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
window?.navigationBarColor = it
|
window?.navigationBarColor = it
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) window?.navigationBarDividerColor = it
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) window?.navigationBarDividerColor = it
|
||||||
}
|
}
|
||||||
/** 注册 */
|
|
||||||
FrameworkTool.registerReceiver(context = this)
|
|
||||||
/** 装载子类 */
|
/** 装载子类 */
|
||||||
onCreate()
|
onCreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 回调 [onCreate] 方法 */
|
/** 回调 [onCreate] 方法 */
|
||||||
abstract fun onCreate()
|
abstract fun onCreate()
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
/** 取消 */
|
|
||||||
FrameworkTool.unregisterReceiver(context = this)
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -34,7 +34,6 @@ import android.view.WindowManager
|
|||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
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.ActivityAppErrorsDetailBinding
|
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDetailBinding
|
||||||
import com.fankes.apperrorstracking.locale.LocaleString
|
import com.fankes.apperrorstracking.locale.LocaleString
|
||||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||||
@@ -48,6 +47,9 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
|||||||
/** 请求保存文件回调标识 */
|
/** 请求保存文件回调标识 */
|
||||||
private const val WRITE_REQUEST_CODE = 0
|
private const val WRITE_REQUEST_CODE = 0
|
||||||
|
|
||||||
|
/** [AppErrorsInfoBean] 传值 */
|
||||||
|
private const val EXTRA_APP_ERRORS_INFO = "app_errors_info_extra"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动 [AppErrorsDetailActivity]
|
* 启动 [AppErrorsDetailActivity]
|
||||||
* @param context 实例
|
* @param context 实例
|
||||||
@@ -55,7 +57,7 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
|||||||
* @param isOutSide 是否从外部启动
|
* @param isOutSide 是否从外部启动
|
||||||
*/
|
*/
|
||||||
fun start(context: Context, appErrorsInfo: AppErrorsInfoBean, isOutSide: Boolean = false) =
|
fun start(context: Context, appErrorsInfo: AppErrorsInfoBean, isOutSide: Boolean = false) =
|
||||||
context.navigate<AppErrorsDetailActivity>(isOutSide) { putExtra(Const.EXTRA_APP_ERRORS_INFO, appErrorsInfo) }
|
context.navigate<AppErrorsDetailActivity>(isOutSide) { putExtra(EXTRA_APP_ERRORS_INFO, appErrorsInfo) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 预导出的异常堆栈 */
|
/** 预导出的异常堆栈 */
|
||||||
@@ -86,7 +88,7 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
val appErrorsInfo = runCatching { intent?.getSerializableExtra(Const.EXTRA_APP_ERRORS_INFO) as? AppErrorsInfoBean }.getOrNull()
|
val appErrorsInfo = runCatching { intent?.getSerializableExtra(EXTRA_APP_ERRORS_INFO) as? AppErrorsInfoBean }.getOrNull()
|
||||||
?: return toastAndFinish()
|
?: return toastAndFinish()
|
||||||
binding.appInfoItem.setOnClickListener { openSelfSetting(appErrorsInfo.packageName) }
|
binding.appInfoItem.setOnClickListener { openSelfSetting(appErrorsInfo.packageName) }
|
||||||
binding.titleBackIcon.setOnClickListener { onBackPressed() }
|
binding.titleBackIcon.setOnClickListener { onBackPressed() }
|
||||||
@@ -144,7 +146,7 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
intent?.removeExtra(Const.EXTRA_APP_ERRORS_INFO)
|
intent?.removeExtra(EXTRA_APP_ERRORS_INFO)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -82,7 +82,7 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** 设置列表元素和 Adapter */
|
/** 设置列表元素和 Adapter */
|
||||||
binding.listView.apply {
|
binding.listView.apply { // 改成 recycleview?
|
||||||
adapter = object : BaseAdapter() {
|
adapter = object : BaseAdapter() {
|
||||||
|
|
||||||
override fun getCount() = listData.size
|
override fun getCount() = listData.size
|
||||||
|
@@ -27,8 +27,8 @@ import android.content.ComponentName
|
|||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import com.fankes.apperrorstracking.BuildConfig
|
||||||
import com.fankes.apperrorstracking.R
|
import com.fankes.apperrorstracking.R
|
||||||
import com.fankes.apperrorstracking.const.Const
|
|
||||||
import com.fankes.apperrorstracking.data.DataConst
|
import com.fankes.apperrorstracking.data.DataConst
|
||||||
import com.fankes.apperrorstracking.databinding.ActivityMainBinding
|
import com.fankes.apperrorstracking.databinding.ActivityMainBinding
|
||||||
import com.fankes.apperrorstracking.locale.LocaleString
|
import com.fankes.apperrorstracking.locale.LocaleString
|
||||||
@@ -44,7 +44,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
private var isModuleValied = false
|
private var isModuleValied = false
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
binding.mainTextVersion.text = LocaleString.moduleVersion(Const.MODULE_VERSION_NAME)
|
binding.mainTextVersion.text = LocaleString.moduleVersion(BuildConfig.VERSION_NAME)
|
||||||
binding.mainTextSystemVersion.text =
|
binding.mainTextSystemVersion.text =
|
||||||
LocaleString.systemVersion("${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT}) ${Build.DISPLAY}")
|
LocaleString.systemVersion("${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT}) ${Build.DISPLAY}")
|
||||||
binding.hideIconInLauncherSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_HIDE_ICON)
|
binding.hideIconInLauncherSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_HIDE_ICON)
|
||||||
@@ -52,7 +52,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
if (btn.isPressed.not()) return@setOnCheckedChangeListener
|
||||||
modulePrefs.put(DataConst.ENABLE_HIDE_ICON, b)
|
modulePrefs.put(DataConst.ENABLE_HIDE_ICON, b)
|
||||||
packageManager.setComponentEnabledSetting(
|
packageManager.setComponentEnabledSetting(
|
||||||
ComponentName(packageName, "${Const.MODULE_PACKAGE_NAME}.Home"),
|
ComponentName(packageName, "${BuildConfig.APPLICATION_ID}.Home"),
|
||||||
if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||||
PackageManager.DONT_KILL_APP
|
PackageManager.DONT_KILL_APP
|
||||||
)
|
)
|
||||||
@@ -92,7 +92,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
super.onResume()
|
super.onResume()
|
||||||
/** 刷新模块状态 */
|
/** 刷新模块状态 */
|
||||||
refreshModuleStatus()
|
refreshModuleStatus()
|
||||||
/** 发送广播检查模块激活状态 */
|
/** 检查模块激活状态 */
|
||||||
FrameworkTool.checkingActivated(context = this) { isValied ->
|
FrameworkTool.checkingActivated(context = this) { isValied ->
|
||||||
isModuleValied = isValied
|
isModuleValied = isValied
|
||||||
refreshModuleStatus()
|
refreshModuleStatus()
|
||||||
|
@@ -34,8 +34,8 @@ 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 androidx.core.content.res.ResourcesCompat
|
||||||
|
import com.fankes.apperrorstracking.BuildConfig
|
||||||
import com.fankes.apperrorstracking.R
|
import com.fankes.apperrorstracking.R
|
||||||
import com.fankes.apperrorstracking.const.Const
|
|
||||||
import com.fankes.apperrorstracking.locale.LocaleString
|
import com.fankes.apperrorstracking.locale.LocaleString
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.highcapable.yukihookapi.hook.factory.field
|
import com.highcapable.yukihookapi.hook.factory.field
|
||||||
@@ -135,14 +135,14 @@ fun Context.snake(msg: String, actionText: String = "", callback: () -> Unit = {
|
|||||||
*
|
*
|
||||||
* [T] 为指定的 [Activity]
|
* [T] 为指定的 [Activity]
|
||||||
* @param isOutSide 是否从外部启动
|
* @param isOutSide 是否从外部启动
|
||||||
* @param callback 回调 [Intent] 方法体
|
* @param initiate [Intent] 方法体
|
||||||
*/
|
*/
|
||||||
inline fun <reified T : Activity> Context.navigate(isOutSide: Boolean = false, callback: Intent.() -> Unit = {}) = runCatching {
|
inline fun <reified T : Activity> Context.navigate(isOutSide: Boolean = false, initiate: Intent.() -> Unit = {}) = runCatching {
|
||||||
startActivity((if (isOutSide) Intent() else Intent(if (this is Service) applicationContext else this, T::class.java)).apply {
|
startActivity((if (isOutSide) Intent() else Intent(if (this is Service) applicationContext else this, T::class.java)).apply {
|
||||||
flags = if (this@navigate !is Activity) Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
flags = if (this@navigate !is Activity) Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
else Intent.FLAG_ACTIVITY_NEW_TASK
|
else Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
if (isOutSide) component = ComponentName(Const.MODULE_PACKAGE_NAME, T::class.java.name)
|
if (isOutSide) component = ComponentName(BuildConfig.APPLICATION_ID, T::class.java.name)
|
||||||
callback(this)
|
initiate(this)
|
||||||
})
|
})
|
||||||
}.onFailure { toast(msg = "Start ${T::class.java.name} failed") }
|
}.onFailure { toast(msg = "Start ${T::class.java.name} failed") }
|
||||||
|
|
||||||
|
@@ -23,58 +23,80 @@
|
|||||||
|
|
||||||
package com.fankes.apperrorstracking.utils.tool
|
package com.fankes.apperrorstracking.utils.tool
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.content.IntentFilter
|
|
||||||
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
||||||
import com.fankes.apperrorstracking.const.Const
|
|
||||||
import com.fankes.apperrorstracking.locale.LocaleString
|
import com.fankes.apperrorstracking.locale.LocaleString
|
||||||
import com.fankes.apperrorstracking.utils.factory.execShell
|
import com.fankes.apperrorstracking.utils.factory.execShell
|
||||||
import com.fankes.apperrorstracking.utils.factory.isRootAccess
|
import com.fankes.apperrorstracking.utils.factory.isRootAccess
|
||||||
import com.fankes.apperrorstracking.utils.factory.showDialog
|
import com.fankes.apperrorstracking.utils.factory.showDialog
|
||||||
import com.fankes.apperrorstracking.utils.factory.snake
|
import com.fankes.apperrorstracking.utils.factory.snake
|
||||||
import com.highcapable.yukihookapi.hook.log.loggerE
|
import com.highcapable.yukihookapi.hook.factory.dataChannel
|
||||||
import java.io.Serializable
|
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统框架控制工具
|
* 系统框架控制工具
|
||||||
*/
|
*/
|
||||||
object FrameworkTool {
|
object FrameworkTool {
|
||||||
|
|
||||||
/** 回调模块激活状态 */
|
/** 系统框架包名 */
|
||||||
private var onModuleActiveStatusCallback: ((Boolean) -> Unit)? = null
|
private const val SYSTEM_FRAMEWORK_NAME = "android"
|
||||||
|
|
||||||
/** 回调获取的 APP 异常信息 */
|
private const val CALL_APP_ERRORS_DATA_GET = "call_app_errors_data_get"
|
||||||
private var onAppErrorsInfoDataCallback: ((ArrayList<AppErrorsInfoBean>) -> Unit)? = null
|
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"
|
||||||
|
|
||||||
/** 回调 APP 异常信息是否移除 */
|
private val CALL_APP_ERRORS_DATA_REMOVE = ChannelData<AppErrorsInfoBean>("call_app_errors_data_remove")
|
||||||
private var onRemoveAppErrorsInfoDataCallback: (() -> Unit)? = null
|
private val CALL_APP_ERRORS_DATA_GET_RESULT = ChannelData<ArrayList<AppErrorsInfoBean>>("call_app_errors_data_get_result")
|
||||||
|
|
||||||
/** 回调 APP 异常信息是否清空 */
|
/**
|
||||||
private var onClearAppErrorsInfoDataCallback: (() -> Unit)? = null
|
* 宿主注册监听
|
||||||
|
*/
|
||||||
|
object Host {
|
||||||
|
|
||||||
/** 模块广播接收器 */
|
/** [PackageParam] 实例 */
|
||||||
private val moduleHandlerReceiver by lazy {
|
private var instance: PackageParam? = null
|
||||||
object : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
/**
|
||||||
if (intent == null) return
|
* 注册监听
|
||||||
runCatching {
|
* @param instance 实例
|
||||||
intent.getStringExtra(Const.KEY_MODULE_HOST_FETCH)?.also {
|
* @param initiate 实例方法体
|
||||||
if (it.isNotBlank()) when (it) {
|
* @return [Host]
|
||||||
Const.TYPE_MODULE_VERSION_VERIFY ->
|
*/
|
||||||
onModuleActiveStatusCallback?.invoke(intent.getStringExtra(Const.TAG_MODULE_VERSION_VERIFY) == Const.MODULE_VERSION_VERIFY)
|
fun with(instance: PackageParam, initiate: Host.() -> Unit) = apply { this.instance = instance }.apply(initiate)
|
||||||
Const.TYPE_APP_ERRORS_DATA_GET -> runCatching {
|
|
||||||
onAppErrorsInfoDataCallback?.invoke(
|
/**
|
||||||
intent.getSerializableExtra(Const.TAG_APP_ERRORS_DATA_GET_CONTENT) as ArrayList<AppErrorsInfoBean>
|
* 监听发送 APP 异常信息数组
|
||||||
)
|
* @param result 回调数据
|
||||||
}.onFailure { onAppErrorsInfoDataCallback?.invoke(arrayListOf()) }
|
*/
|
||||||
Const.TYPE_APP_ERRORS_DATA_REMOVE -> onRemoveAppErrorsInfoDataCallback?.invoke()
|
fun onPushAppErrorsInfoData(result: () -> ArrayList<AppErrorsInfoBean>) {
|
||||||
Const.TYPE_APP_ERRORS_DATA_CLEAR -> onClearAppErrorsInfoDataCallback?.invoke()
|
instance?.dataChannel?.with { wait(CALL_APP_ERRORS_DATA_GET) { put(CALL_APP_ERRORS_DATA_GET_RESULT, result()) } }
|
||||||
else -> {}
|
}
|
||||||
}
|
|
||||||
}
|
/**
|
||||||
}.onFailure { loggerE(msg = "Cannot receiver message, please restart system", e = it) }
|
* 监听移除指定 APP 异常信息
|
||||||
|
* @param result 回调数据
|
||||||
|
*/
|
||||||
|
fun onRemoveAppErrorsInfoData(result: (AppErrorsInfoBean) -> Unit) {
|
||||||
|
instance?.dataChannel?.with {
|
||||||
|
wait(CALL_APP_ERRORS_DATA_REMOVE) {
|
||||||
|
result(it)
|
||||||
|
put(CALL_APP_ERRORS_DATA_REMOVE_RESULT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听清空 APP 异常信息数组
|
||||||
|
* @param callback 回调
|
||||||
|
*/
|
||||||
|
fun onClearAppErrorsInfoData(callback: () -> Unit) {
|
||||||
|
instance?.dataChannel?.with {
|
||||||
|
wait(CALL_APP_ERRORS_DATA_CLEAR) {
|
||||||
|
callback()
|
||||||
|
put(CALL_APP_ERRORS_DATA_CLEAR_RESULT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,83 +122,47 @@ object FrameworkTool {
|
|||||||
cancelButton()
|
cancelButton()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送广播
|
|
||||||
* @param context 实例
|
|
||||||
* @param type 类型
|
|
||||||
* @param key 可选传值
|
|
||||||
* @param value 可选传值
|
|
||||||
*/
|
|
||||||
private fun pushReceiver(context: Context, type: String, key: String = "", value: Any = "") {
|
|
||||||
context.sendBroadcast(Intent().apply {
|
|
||||||
action = Const.ACTION_HOST_HANDLER_RECEIVER
|
|
||||||
putExtra(Const.KEY_MODULE_HOST_FETCH, type)
|
|
||||||
if (key.isNotBlank()) putExtra(
|
|
||||||
key, when (value) {
|
|
||||||
is String -> value
|
|
||||||
is Int -> value
|
|
||||||
is Boolean -> value
|
|
||||||
is Serializable -> value
|
|
||||||
else -> error("value is not allowed")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查模块是否激活
|
* 检查模块是否激活
|
||||||
* @param context 实例
|
* @param context 实例
|
||||||
* @param it 成功后回调 - ([Boolean] 是否激活)
|
* @param result 成功后回调
|
||||||
*/
|
*/
|
||||||
fun checkingActivated(context: Context, it: (Boolean) -> Unit) {
|
fun checkingActivated(context: Context, result: (Boolean) -> Unit) = context.dataChannel(SYSTEM_FRAMEWORK_NAME).checkingVersionEquals(result)
|
||||||
onModuleActiveStatusCallback = it
|
|
||||||
pushReceiver(context, Const.TYPE_MODULE_VERSION_VERIFY)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 APP 异常信息数组
|
* 获取 APP 异常信息数组
|
||||||
* @param context 实例
|
* @param context 实例
|
||||||
* @param it 回调数据
|
* @param result 回调数据
|
||||||
*/
|
*/
|
||||||
fun fetchAppErrorsInfoData(context: Context, it: (ArrayList<AppErrorsInfoBean>) -> Unit) {
|
fun fetchAppErrorsInfoData(context: Context, result: (ArrayList<AppErrorsInfoBean>) -> Unit) {
|
||||||
onAppErrorsInfoDataCallback = it
|
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||||
pushReceiver(context, Const.TYPE_APP_ERRORS_DATA_GET)
|
wait(CALL_APP_ERRORS_DATA_GET_RESULT) { result(it) }
|
||||||
|
put(CALL_APP_ERRORS_DATA_GET)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除指定 APP 异常信息
|
* 移除指定 APP 异常信息
|
||||||
* @param context 实例
|
* @param context 实例
|
||||||
* @param appErrorsInfo 指定 APP 异常信息
|
* @param appErrorsInfo 指定 APP 异常信息
|
||||||
* @param it 成功后回调
|
* @param callback 成功后回调
|
||||||
*/
|
*/
|
||||||
fun removeAppErrorsInfoData(context: Context, appErrorsInfo: AppErrorsInfoBean, it: () -> Unit) {
|
fun removeAppErrorsInfoData(context: Context, appErrorsInfo: AppErrorsInfoBean, callback: () -> Unit) {
|
||||||
onRemoveAppErrorsInfoDataCallback = it
|
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||||
pushReceiver(context, Const.TYPE_APP_ERRORS_DATA_REMOVE, Const.TAG_APP_ERRORS_DATA_REMOVE_CONTENT, appErrorsInfo)
|
wait(CALL_APP_ERRORS_DATA_REMOVE_RESULT) { callback() }
|
||||||
|
put(CALL_APP_ERRORS_DATA_REMOVE, appErrorsInfo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清空 APP 异常信息数组
|
* 清空 APP 异常信息数组
|
||||||
* @param context 实例
|
* @param context 实例
|
||||||
* @param it 成功后回调
|
* @param callback 成功后回调
|
||||||
*/
|
*/
|
||||||
fun clearAppErrorsInfoData(context: Context, it: () -> Unit) {
|
fun clearAppErrorsInfoData(context: Context, callback: () -> Unit) {
|
||||||
onClearAppErrorsInfoDataCallback = it
|
context.dataChannel(SYSTEM_FRAMEWORK_NAME).with {
|
||||||
pushReceiver(context, Const.TYPE_APP_ERRORS_DATA_CLEAR)
|
wait(CALL_APP_ERRORS_DATA_CLEAR_RESULT) { callback() }
|
||||||
}
|
put(CALL_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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user