From 8d64f9c9898f1af4573bb278feacc9b0efc6c561 Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Sat, 1 Oct 2022 03:32:23 +0800 Subject: [PATCH] Modify merge YukiHookAPI new usage and compatible with API 33 --- .../apperrorstracking/data/DataConst.kt | 1 - .../apperrorstracking/hook/HookEntry.kt | 2 +- .../hook/entity/FrameworkHooker.kt | 60 +++++---- .../ui/activity/base/BaseActivity.kt | 16 +-- .../errors/AppErrorsDetailActivity.kt | 12 +- .../errors/AppErrorsDisplayActivity.kt | 7 +- .../activity/errors/AppErrorsMutedActivity.kt | 8 +- .../errors/AppErrorsRecordActivity.kt | 8 +- .../ui/activity/main/ConfigureActivity.kt | 6 +- .../ui/activity/main/MainActivity.kt | 23 +--- .../utils/factory/DialogBuilderFactory.kt | 90 ++++--------- .../utils/factory/FunctionFactory.kt | 127 +++++++++++++----- .../apperrorsdemo/ui/activity/MainActivity.kt | 2 +- .../ui/activity/base/BaseActivity.kt | 16 +-- 14 files changed, 190 insertions(+), 188 deletions(-) diff --git a/app/src/main/java/com/fankes/apperrorstracking/data/DataConst.kt b/app/src/main/java/com/fankes/apperrorstracking/data/DataConst.kt index 43db55f..e4d5c9d 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/data/DataConst.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/data/DataConst.kt @@ -27,7 +27,6 @@ object DataConst { val SHOW_DEVELOPER_NOTICE = PrefsData("_show_developer_notice", true) - val ENABLE_HIDE_ICON = PrefsData("_hide_icon", false) val ENABLE_ONLY_SHOW_ERRORS_IN_FRONT = PrefsData("_enable_only_show_errors_in_front", false) val ENABLE_ONLY_SHOW_ERRORS_IN_MAIN = PrefsData("_enable_only_show_errors_in_main", false) val ENABLE_ALWAYS_SHOWS_REOPEN_APP_OPTIONS = PrefsData("_enable_always_shows_reopen_app_options", false) diff --git a/app/src/main/java/com/fankes/apperrorstracking/hook/HookEntry.kt b/app/src/main/java/com/fankes/apperrorstracking/hook/HookEntry.kt index 6a68394..4b0468b 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/hook/HookEntry.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/hook/HookEntry.kt @@ -32,7 +32,7 @@ import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit class HookEntry : IYukiHookXposedInit { override fun onInit() = configs { - debugTag = "AppErrorsTracking" + debugLog { tag = "AppErrorsTracking" } isDebug = false isEnableModulePrefsCache = false } diff --git a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt index 5eb5ed1..792b511 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt @@ -27,7 +27,6 @@ import android.app.Dialog import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager import android.os.Build import android.os.Message import androidx.core.graphics.drawable.IconCompat @@ -94,7 +93,7 @@ object FrameworkHooker : YukiBaseHooker() { registerReceiver(Intent.ACTION_LOCALE_CHANGED) { _, _ -> refreshModuleAppResources() } } FrameworkTool.Host.with(instance = this) { - onOpenAppUsedFramework { appContext.openApp(it) } + onOpenAppUsedFramework { appContext?.openApp(it) } onPushAppErrorsInfoData { appErrorsRecords } onRemoveAppErrorsInfoData { appErrorsRecords.remove(it) } onClearAppErrorsInfoData { appErrorsRecords.clear() } @@ -119,19 +118,22 @@ object FrameworkHooker : YukiBaseHooker() { mutedErrorsIfRestartApps.clear() } onPushAppListData { filters -> - arrayListOf().apply { - appContext.packageManager.getInstalledPackages(PackageManager.GET_CONFIGURATIONS).also { info -> - (if (filters.name.isNotBlank()) - info.filter { it.packageName.contains(filters.name) || appContext.appName(it.packageName).contains(filters.name) } - else info).let { result -> - if (filters.isContainsSystem.not()) result.filter { (it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 0 } - else result - }.sortedByDescending { it.lastUpdateTime } - .forEach { add(AppInfoBean(name = appContext.appName(it.packageName), packageName = it.packageName)) } - /** 移除模块自身 */ - removeIf { it.packageName == BuildConfig.APPLICATION_ID } + appContext?.let { context -> + arrayListOf().apply { + context.listOfPackages().also { info -> + (if (filters.name.isNotBlank()) + info.filter { it.packageName.contains(filters.name) || context.appNameOf(it.packageName).contains(filters.name) } + else info).let { result -> + if (filters.isContainsSystem.not()) + result.filter { (it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 0 } + else result + }.sortedByDescending { it.lastUpdateTime } + .forEach { add(AppInfoBean(name = context.appNameOf(it.packageName), packageName = it.packageName)) } + /** 移除模块自身 */ + removeIf { it.packageName == BuildConfig.APPLICATION_ID } + } } - } + } ?: arrayListOf() } } } @@ -191,48 +193,48 @@ object FrameworkHooker : YukiBaseHooker() { val errData = args().first().cast()?.obj /** 当前进程信息 */ - val proc = AppErrorDialog_DataClass.clazz.field { name = "proc" }.get(errData).any() + val proc = AppErrorDialog_DataClass.toClass().field { name = "proc" }.get(errData).any() /** 当前 UserId 信息 */ - val userId = ProcessRecordClass.clazz.field { name = "userId" }.get(proc).int() + val userId = ProcessRecordClass.toClass().field { name = "userId" }.get(proc).int() /** 当前 APP 信息 */ - val appInfo = ProcessRecordClass.clazz.field { name = "info" }.get(proc).cast() + val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(proc).cast() /** 当前进程名称 */ - val processName = ProcessRecordClass.clazz.field { name = "processName" }.get(proc).string() + val processName = ProcessRecordClass.toClass().field { name = "processName" }.get(proc).string() /** 当前 APP、进程 包名 */ val packageName = appInfo?.packageName ?: processName /** 当前 APP 名称 */ - val appName = appInfo?.let { context.appName(it.packageName) } ?: packageName + val appName = appInfo?.let { context.appNameOf(it.packageName) } ?: packageName /** 是否为 APP */ - val isApp = (PackageListClass.clazz.method { + val isApp = (PackageListClass.toClass().method { name = "size" emptyParam() - }.get(if (ProcessRecordClass.clazz.hasMethod { + }.get(if (ProcessRecordClass.toClass().hasMethod { name = "getPkgList" emptyParam() - }) ProcessRecordClass.clazz.method { + }) ProcessRecordClass.toClass().method { name = "getPkgList" emptyParam() - }.get(proc).call() else ProcessRecordClass.clazz.field { + }.get(proc).call() else ProcessRecordClass.toClass().field { name = "pkgList" - }.get(proc).self).int() == 1 && appInfo != null) + }.get(proc).any()).int() == 1 && appInfo != null) /** 是否为主进程 */ val isMainProcess = packageName == processName /** 是否为后台进程 */ - val isBackgroundProcess = UserControllerClass.clazz.method { name = "getCurrentProfileIds" } - .get(ActivityManagerServiceClass.clazz.field { name = "mUserController" } + val isBackgroundProcess = UserControllerClass.toClass().method { name = "getCurrentProfileIds" } + .get(ActivityManagerServiceClass.toClass().field { name = "mUserController" } .get(field { name = "mService" }.get(instance).any()).any()) .invoke()?.takeIf { it.isNotEmpty() }?.any { it != userId } ?: false /** 是否短时内重复错误 */ - val isRepeating = AppErrorDialog_DataClass.clazz.field { name = "repeating" }.get(errData).boolean() + val isRepeating = AppErrorDialog_DataClass.toClass().field { name = "repeating" }.get(errData).boolean() /** 崩溃标题 */ val errorTitle = if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName) @@ -260,7 +262,7 @@ object FrameworkHooker : YukiBaseHooker() { channelName = LocaleString.appName, title = errorTitle, content = LocaleString.appErrorsTip, - icon = IconCompat.createWithBitmap(R.mipmap.ic_notify.drawableOf(moduleAppResources).toBitmap()), + icon = IconCompat.createWithBitmap(moduleAppResources.drawableOf(R.mipmap.ic_notify).toBitmap()), color = 0xFFFF6200.toInt(), intent = AppErrorsRecordActivity.intent() ) @@ -299,7 +301,7 @@ object FrameworkHooker : YukiBaseHooker() { } afterHook { /** 当前 APP 信息 */ - val appInfo = ProcessRecordClass.clazz.field { name = "info" }.get(args().first().any()).cast() + val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(args().first().any()).cast() /** 添加当前异常信息到第一位 */ appErrorsRecords.add(0, AppErrorsInfoBean.clone(appInfo?.packageName, args().last().cast())) } diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt index e3c09e4..f4fccb7 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt @@ -32,9 +32,9 @@ 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.current import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass -import java.lang.reflect.ParameterizedType abstract class BaseActivity : AppCompatActivity() { @@ -43,15 +43,11 @@ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - javaClass.genericSuperclass.also { type -> - if (type is ParameterizedType) { - binding = (type.actualTypeArguments[0] as Class).method { - name = "inflate" - param(LayoutInflaterClass) - }.get().invoke(layoutInflater) ?: error("binding failed") - setContentView(binding.root) - } else error("binding but got wrong type") - } + binding = current().generic()?.argument()?.method { + name = "inflate" + param(LayoutInflaterClass) + }?.get()?.invoke(layoutInflater) ?: error("binding failed") + setContentView(binding.root) /** 隐藏系统的标题栏 */ supportActionBar?.hide() /** 初始化沉浸状态栏 */ diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDetailActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDetailActivity.kt index 40ddb85..17f52ef 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDetailActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDetailActivity.kt @@ -58,7 +58,7 @@ class AppErrorsDetailActivity : BaseActivity() { private var stackTrace = "" override fun onCreate() { - val appErrorsInfo = runCatching { intent?.getSerializableExtra(EXTRA_APP_ERRORS_INFO) as? AppErrorsInfoBean }.getOrNull() + val appErrorsInfo = runCatching { intent?.getSerializableExtraCompat(EXTRA_APP_ERRORS_INFO) }.getOrNull() ?: return toastAndFinish(name = "AppErrorsInfo") binding.appInfoItem.setOnClickListener { openSelfSetting(appErrorsInfo.packageName) } binding.titleBackIcon.setOnClickListener { onBackPressed() } @@ -83,10 +83,10 @@ class AppErrorsDetailActivity : BaseActivity() { putExtra(Intent.EXTRA_TEXT, appErrorsInfo.stackOutputShareContent) }, LocaleString.shareErrorStack)) } - binding.appIcon.setImageDrawable(appIcon(appErrorsInfo.packageName)) - binding.appNameText.text = appName(appErrorsInfo.packageName) - binding.appVersionText.text = appVersion(appErrorsInfo.packageName) - binding.appAbiText.text = appCpuAbi(appErrorsInfo.packageName).ifBlank { LocaleString.noCpuAbi } + binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName)) + binding.appNameText.text = appNameOf(appErrorsInfo.packageName) + binding.appVersionText.text = appVersionBrandOf(appErrorsInfo.packageName) + binding.appAbiText.text = appCpuAbiOf(appErrorsInfo.packageName).ifBlank { LocaleString.noCpuAbi } binding.jvmErrorPanel.isGone = appErrorsInfo.isNativeCrash binding.errorTypeIcon.setImageResource(if (appErrorsInfo.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java) binding.errorInfoText.text = appErrorsInfo.exceptionMessage @@ -98,7 +98,7 @@ class AppErrorsDetailActivity : BaseActivity() { binding.errorRecordTimeText.text = appErrorsInfo.dateTime binding.errorStackText.text = appErrorsInfo.stackTrace binding.appPanelScrollView.setOnScrollChangeListener { _, _, y, _, _ -> - binding.detailTitleText.text = if (y >= 30.dp(context = this)) appName(appErrorsInfo.packageName) else LocaleString.appName + binding.detailTitleText.text = if (y >= 30.dp(context = this)) appNameOf(appErrorsInfo.packageName) else LocaleString.appName } binding.detailTitleText.setOnClickListener { binding.appPanelScrollView.smoothScrollTo(0, 0) } } diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt index 9af92c0..c721cc3 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt @@ -28,10 +28,7 @@ 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.factory.* import com.fankes.apperrorstracking.utils.tool.FrameworkTool class AppErrorsDisplayActivity : BaseActivity() { @@ -56,7 +53,7 @@ class AppErrorsDisplayActivity : BaseActivity() override fun onCreate() { instance?.finish() instance = this - val appErrorsDisplay = runCatching { intent?.getSerializableExtra(EXTRA_APP_ERRORS_DISPLAY) as? AppErrorsDisplayBean }.getOrNull() + val appErrorsDisplay = runCatching { intent?.getSerializableExtraCompat(EXTRA_APP_ERRORS_DISPLAY) }.getOrNull() ?: return toastAndFinish(name = "AppErrorsDisplay") /** 显示对话框 */ showDialog { diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsMutedActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsMutedActivity.kt index e437f3d..35eff10 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsMutedActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsMutedActivity.kt @@ -29,8 +29,8 @@ import com.fankes.apperrorstracking.databinding.ActivityAppErrorsMutedBinding import com.fankes.apperrorstracking.databinding.AdapterAppErrorsMutedBinding 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.appIconOf +import com.fankes.apperrorstracking.utils.factory.appNameOf import com.fankes.apperrorstracking.utils.factory.bindAdapter import com.fankes.apperrorstracking.utils.factory.showDialog import com.fankes.apperrorstracking.utils.tool.FrameworkTool @@ -58,8 +58,8 @@ class AppErrorsMutedActivity : BaseActivity() { onBindDatas { listData } onBindViews { binding, position -> listData[position].also { bean -> - binding.appIcon.setImageDrawable(appIcon(bean.packageName)) - binding.appNameText.text = appName(bean.packageName) + binding.appIcon.setImageDrawable(appIconOf(bean.packageName)) + binding.appNameText.text = appNameOf(bean.packageName) binding.muteTypeText.text = when (bean.type) { MutedErrorsAppBean.MuteType.UNTIL_UNLOCKS -> LocaleString.muteIfUnlock MutedErrorsAppBean.MuteType.UNTIL_REBOOTS -> LocaleString.muteIfRestart diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsRecordActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsRecordActivity.kt index 060c8be..90dbe3e 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsRecordActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsRecordActivity.kt @@ -94,8 +94,8 @@ class AppErrorsRecordActivity : BaseActivity() { title = LocaleString.appErrorsStatistics binding.totalErrorsUnitText.text = LocaleString.totalErrorsUnit(listData.size) binding.totalAppsUnitText.text = LocaleString.totalAppsUnit(it.size) - binding.mostErrorsAppIcon.setImageDrawable(appIcon(mostAppPackageName)) - binding.mostErrorsAppText.text = appName(mostAppPackageName) + binding.mostErrorsAppIcon.setImageDrawable(appIconOf(mostAppPackageName)) + binding.mostErrorsAppText.text = appNameOf(mostAppPackageName) binding.mostErrorsTypeText.text = mostErrorsType binding.totalPptOfErrorsText.text = "$pptCount%" confirmButton(LocaleString.gotIt) @@ -132,8 +132,8 @@ class AppErrorsRecordActivity : BaseActivity() { onBindDatas { listData } onBindViews { binding, position -> listData[position].also { bean -> - binding.appIcon.setImageDrawable(appIcon(bean.packageName)) - binding.appNameText.text = appName(bean.packageName) + binding.appIcon.setImageDrawable(appIconOf(bean.packageName)) + binding.appNameText.text = appNameOf(bean.packageName) binding.errorsTimeText.text = bean.crossTime binding.errorTypeIcon.setImageResource(if (bean.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java) binding.errorTypeText.text = if (bean.isNativeCrash) "Native crash" else bean.exceptionClassName.simpleThwName() diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/main/ConfigureActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/main/ConfigureActivity.kt index 565a469..3615afa 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/main/ConfigureActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/main/ConfigureActivity.kt @@ -31,7 +31,7 @@ import com.fankes.apperrorstracking.databinding.DiaAppsFilterBinding import com.fankes.apperrorstracking.hook.factory.* 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.appIconOf import com.fankes.apperrorstracking.utils.factory.bindAdapter import com.fankes.apperrorstracking.utils.factory.showDialog import com.fankes.apperrorstracking.utils.tool.FrameworkTool @@ -48,7 +48,7 @@ class ConfigureActivity : BaseActivity() { private val listData = ArrayList() override fun onCreate() { - binding.titleBackIcon.setOnClickListener { onBackPressed() } + binding.titleBackIcon.setOnClickListener { finish() } binding.batchIcon.setOnClickListener { showDialog { title = LocaleString.batchOperations @@ -164,7 +164,7 @@ class ConfigureActivity : BaseActivity() { Thread { it.takeIf { e -> e.isNotEmpty() }?.forEach { e -> listData.add(e) - e.icon = appIcon(e.packageName) + e.icon = appIconOf(e.packageName) } runOnUiThread { onChanged?.invoke() diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/main/MainActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/main/MainActivity.kt index 441af2f..f64ea6f 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/main/MainActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/main/MainActivity.kt @@ -23,8 +23,6 @@ package com.fankes.apperrorstracking.ui.activity.main -import android.content.ComponentName -import android.content.pm.PackageManager import android.os.Build import androidx.core.view.isVisible import com.fankes.apperrorstracking.BuildConfig @@ -35,10 +33,7 @@ import com.fankes.apperrorstracking.locale.LocaleString import com.fankes.apperrorstracking.ui.activity.base.BaseActivity import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsMutedActivity import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsRecordActivity -import com.fankes.apperrorstracking.utils.factory.navigate -import com.fankes.apperrorstracking.utils.factory.openBrowser -import com.fankes.apperrorstracking.utils.factory.showDialog -import com.fankes.apperrorstracking.utils.factory.toast +import com.fankes.apperrorstracking.utils.factory.* import com.fankes.apperrorstracking.utils.tool.FrameworkTool import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.hook.factory.modulePrefs @@ -59,17 +54,7 @@ class MainActivity : BaseActivity() { binding.onlyShowErrorsInMainProcessSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_ONLY_SHOW_ERRORS_IN_MAIN) binding.alwaysShowsReopenAppOptionsSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_ALWAYS_SHOWS_REOPEN_APP_OPTIONS) binding.enableAppsConfigsTemplateSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_APP_CONFIG_TEMPLATE) - binding.hideIconInLauncherSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_HIDE_ICON) binding.mgrAppsConfigsTemplateButton.isVisible = modulePrefs.get(DataConst.ENABLE_APP_CONFIG_TEMPLATE) - binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b -> - if (btn.isPressed.not()) return@setOnCheckedChangeListener - modulePrefs.put(DataConst.ENABLE_HIDE_ICON, b) - packageManager.setComponentEnabledSetting( - ComponentName(packageName, "${BuildConfig.APPLICATION_ID}.Home"), - if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED, - PackageManager.DONT_KILL_APP - ) - } binding.onlyShowErrorsInFrontSwitch.setOnCheckedChangeListener { btn, b -> if (btn.isPressed.not()) return@setOnCheckedChangeListener modulePrefs.put(DataConst.ENABLE_ONLY_SHOW_ERRORS_IN_FRONT, b) @@ -87,6 +72,12 @@ class MainActivity : BaseActivity() { binding.mgrAppsConfigsTemplateButton.isVisible = b modulePrefs.put(DataConst.ENABLE_APP_CONFIG_TEMPLATE, b) } + /** 设置桌面图标显示隐藏 */ + binding.hideIconInLauncherSwitch.isChecked = isLauncherIconShowing.not() + binding.hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b -> + if (btn.isPressed.not()) return@setOnCheckedChangeListener + hideOrShowLauncherIcon(b) + } /** 管理应用配置模板按钮点击事件 */ binding.mgrAppsConfigsTemplateButton.setOnClickListener { whenActivated { navigate() } } /** 功能管理按钮点击事件 */ diff --git a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt index fd457ac..70e36d4 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/DialogBuilderFactory.kt @@ -25,9 +25,6 @@ package com.fankes.apperrorstracking.utils.factory import android.app.Dialog import android.content.Context -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 @@ -35,12 +32,13 @@ import android.view.ViewGroup import android.widget.LinearLayout import android.widget.ProgressBar import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AlertDialog import androidx.viewbinding.ViewBinding import com.fankes.apperrorstracking.locale.LocaleString import com.fankes.apperrorstracking.ui.activity.errors.AppErrorsDisplayActivity import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.shape.MaterialShapeDrawable +import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.annotation.CauseProblemsApi import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass @@ -49,7 +47,7 @@ import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass * 构造 [VB] 自定义 View 对话框 * @param initiate 对话框方法体 */ -@JvmName(name = "showDialog-VB") +@JvmName(name = "showDialog_Generics") inline fun Context.showDialog(initiate: DialogBuilder.() -> Unit) = DialogBuilder(context = this, VB::class.java).apply(initiate).show() @@ -66,12 +64,17 @@ inline fun Context.showDialog(initiate: DialogBuilder<*>.() -> Unit) = DialogBui */ class DialogBuilder(val context: Context, private val bindingClass: Class<*>? = null) { - private var instanceAndroidX: androidx.appcompat.app.AlertDialog.Builder? = null // 实例对象 - private var instanceAndroid: android.app.AlertDialog.Builder? = null // 实例对象 + /** 实例对象 */ + private var instance: AlertDialog.Builder? = null - private var onCancel: (() -> Unit)? = null // 对话框取消监听 - private var dialogInstance: Dialog? = null // 对话框实例 - private var customLayoutView: View? = null // 自定义布局 + /** 对话框取消监听 */ + private var onCancel: (() -> Unit)? = null + + /** 对话框实例 */ + private var dialogInstance: Dialog? = null + + /** 自定义布局 */ + private var customLayoutView: View? = null /** * 获取 [DialogBuilder] 绑定布局对象 @@ -86,49 +89,31 @@ class DialogBuilder(val context: Context, private val bindingC } ?: error("This dialog maybe not a custom view dialog") } - /** - * 是否需要使用 AndroidX 风格对话框 - * @return [Boolean] - */ - private val isUsingAndroidX get() = runCatching { context is AppCompatActivity }.getOrNull() ?: false - init { - if (isUsingAndroidX) runCatching { - instanceAndroidX = MaterialAlertDialogBuilder(context).also { builder -> - if (context is AppErrorsDisplayActivity) - builder.background = (builder.background as MaterialShapeDrawable).apply { setCornerSize(15.dpFloat(context)) } - } - } else runCatching { - instanceAndroid = android.app.AlertDialog.Builder( - context, - if (context.isSystemInDarkMode) android.R.style.Theme_Material_Dialog else android.R.style.Theme_Material_Light_Dialog - ) + if (YukiHookAPI.Status.isXposedEnvironment) error("This dialog is not allowed to created in Xposed environment") + instance = MaterialAlertDialogBuilder(context).also { builder -> + if (context is AppErrorsDisplayActivity) + builder.background = (builder.background as MaterialShapeDrawable).apply { setCornerSize(15.dpFloat(context)) } } } /** 设置对话框不可关闭 */ fun noCancelable() { - if (isUsingAndroidX) - runCatching { instanceAndroidX?.setCancelable(false) } - else runCatching { instanceAndroid?.setCancelable(false) } + instance?.setCancelable(false) } /** 设置对话框标题 */ var title get() = "" set(value) { - if (isUsingAndroidX) - runCatching { instanceAndroidX?.setTitle(value) } - else runCatching { instanceAndroid?.setTitle(value) } + instance?.setTitle(value) } /** 设置对话框消息内容 */ var msg get() = "" set(value) { - if (isUsingAndroidX) - runCatching { instanceAndroidX?.setMessage(value) } - else runCatching { instanceAndroid?.setMessage(value) } + instance?.setMessage(value) } /** 设置进度条对话框消息内容 */ @@ -156,9 +141,7 @@ class DialogBuilder(val context: Context, private val bindingC * @param callback 点击事件 */ fun confirmButton(text: String = LocaleString.confirm, callback: () -> Unit = {}) { - if (isUsingAndroidX) - runCatching { instanceAndroidX?.setPositiveButton(text) { _, _ -> callback() } } - else runCatching { instanceAndroid?.setPositiveButton(text) { _, _ -> callback() } } + instance?.setPositiveButton(text) { _, _ -> callback() } } /** @@ -167,9 +150,7 @@ class DialogBuilder(val context: Context, private val bindingC * @param callback 点击事件 */ fun cancelButton(text: String = LocaleString.cancel, callback: () -> Unit = {}) { - if (isUsingAndroidX) - runCatching { instanceAndroidX?.setNegativeButton(text) { _, _ -> callback() } } - else runCatching { instanceAndroid?.setNegativeButton(text) { _, _ -> callback() } } + instance?.setNegativeButton(text) { _, _ -> callback() } } /** @@ -178,9 +159,7 @@ class DialogBuilder(val context: Context, private val bindingC * @param callback 点击事件 */ fun neutralButton(text: String = LocaleString.more, callback: () -> Unit = {}) { - if (isUsingAndroidX) - runCatching { instanceAndroidX?.setNeutralButton(text) { _, _ -> callback() } } - else runCatching { instanceAndroid?.setNeutralButton(text) { _, _ -> callback() } } + instance?.setNeutralButton(text) { _, _ -> callback() } } /** @@ -199,31 +178,12 @@ class DialogBuilder(val context: Context, private val bindingC fun show() { /** 若当前自定义 View 的对话框没有调用 [binding] 将会对其手动调用一次以确保显示布局 */ if (bindingClass != null) binding - if (isUsingAndroidX) runCatching { - instanceAndroidX?.create()?.apply { + runCatching { + instance?.create()?.apply { customLayoutView?.let { setView(it) } dialogInstance = this setOnCancelListener { onCancel?.invoke() } }?.show() - } else runCatching { - instanceAndroid?.create()?.apply { - customLayoutView?.let { setView(it) } - window?.setBackgroundDrawable( - InsetDrawable( - GradientDrawable( - GradientDrawable.Orientation.TOP_BOTTOM, - if (context.isSystemInDarkMode) intArrayOf(0xFF2D2D2D.toInt(), 0xFF2D2D2D.toInt()) - else intArrayOf(Color.WHITE, Color.WHITE) - ).apply { - shape = GradientDrawable.RECTANGLE - gradientType = GradientDrawable.LINEAR_GRADIENT - cornerRadius = 15.dpFloat(this@DialogBuilder.context) - }, 30.dp(context), 0, 30.dp(context), 0 - ) - ) - dialogInstance = this - setOnCancelListener { onCancel?.invoke() } - }?.show() } } } \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt index ced4bda..e78201a 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt @@ -19,22 +19,27 @@ * * This file is Created by fankes on 2022/5/7. */ -@file:Suppress("DEPRECATION", "PrivateApi", "unused", "ObsoleteSdkInt") +@file:Suppress("unused") package com.fankes.apperrorstracking.utils.factory import android.app.* import android.content.* +import android.content.pm.PackageInfo import android.content.pm.PackageManager +import android.content.pm.PackageManager.PackageInfoFlags import android.content.res.Configuration import android.content.res.Resources import android.graphics.Color import android.graphics.drawable.Drawable import android.net.Uri +import android.os.Build import android.provider.Settings import android.widget.Toast +import androidx.annotation.DrawableRes import androidx.core.app.NotificationCompat import androidx.core.content.getSystemService +import androidx.core.content.pm.PackageInfoCompat import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.drawable.IconCompat import com.fankes.apperrorstracking.BuildConfig @@ -44,6 +49,7 @@ import com.google.android.material.snackbar.Snackbar import com.highcapable.yukihookapi.hook.factory.field import com.highcapable.yukihookapi.hook.type.android.ApplicationInfoClass import com.topjohnwu.superuser.Shell +import java.io.Serializable import java.math.RoundingMode import java.text.DecimalFormat @@ -74,54 +80,86 @@ fun Number.dp(context: Context) = dpFloat(context).toInt() fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density /** - * 从 [Int] resId 获取 [Drawable] - * @param resources 使用的 [Resources] + * 获取 [Drawable] + * @param resId 属性资源 ID * @return [Drawable] */ -fun Int.drawableOf(resources: Resources) = ResourcesCompat.getDrawable(resources, this, null) ?: error("Invalid resources") +fun Resources.drawableOf(@DrawableRes resId: Int) = ResourcesCompat.getDrawable(this, resId, null) ?: error("Invalid resources") /** - * 获取 APP 名称 - * @param packageName 包名 - * @return [String] + * 得到 APP 安装包信息 (兼容) + * @param packageName APP 包名 + * @param flag [PackageInfoFlags] + * @return [PackageInfo] or null */ -fun Context.appName(packageName: String) = - runCatching { - packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA) - .applicationInfo.loadLabel(packageManager).toString() - }.getOrNull() ?: packageName +private fun Context.getPackageInfoCompat(packageName: String, flag: Number = 0) = runCatching { + @Suppress("DEPRECATION") + if (Build.VERSION.SDK_INT >= 33) + packageManager?.getPackageInfo(packageName, PackageInfoFlags.of(flag.toLong())) + else packageManager?.getPackageInfo(packageName, flag.toInt()) +}.getOrNull() /** - * 获取 APP 完整版本 - * @param packageName 包名 + * 得到 APP 版本号 (兼容 [PackageInfo.getLongVersionCode]) + * @return [Int] + */ +private val PackageInfo.versionCodeCompat get() = PackageInfoCompat.getLongVersionCode(this) + +/** + * 获取系统中全部已安装应用列表 + * @return [List]<[PackageInfo]> + */ +fun Context.listOfPackages() = runCatching { + @Suppress("DEPRECATION") + if (Build.VERSION.SDK_INT >= 33) + packageManager?.getInstalledPackages(PackageInfoFlags.of(PackageManager.GET_CONFIGURATIONS.toLong())) + else packageManager?.getInstalledPackages(PackageManager.GET_CONFIGURATIONS) +}.getOrNull() ?: emptyList() + +/** + * 得到 APP 名称 + * @param packageName APP 包名 - 默认为当前 APP * @return [String] */ -fun Context.appVersion(packageName: String) = - runCatching { - packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)?.let { "${it.versionName} (${it.versionCode})" } - }.getOrNull() ?: "" +fun Context.appNameOf(packageName: String = getPackageName()) = + getPackageInfoCompat(packageName)?.applicationInfo?.loadLabel(packageManager)?.toString() ?: "" + +/** + * 得到 APP 版本信息与版本号 + * @param packageName APP 包名 - 默认为当前 APP + * @return [String] 无法获取时返回 "unknown" + */ +fun Context.appVersionBrandOf(packageName: String = getPackageName()) = + getPackageInfoCompat(packageName)?.let { "${it.versionName}(${it.versionCodeCompat})" } ?: "unknown" /** * 获取 APP CPU ABI 名称 - * @param packageName 包名 + * @param packageName APP 包名 - 默认为当前 APP * @return [String] */ -fun Context.appCpuAbi(packageName: String) = - runCatching { - ApplicationInfoClass.field { name = "primaryCpuAbi" } - .get(packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)?.applicationInfo).string() - }.getOrNull() ?: "" +fun Context.appCpuAbiOf(packageName: String = getPackageName()) = runCatching { + ApplicationInfoClass.field { name = "primaryCpuAbi" }.get(getPackageInfoCompat(packageName)?.applicationInfo).string() +}.getOrNull() ?: "" /** - * 获取 APP 图标 - * @param packageName 包名 - * @return [Drawable] + * 得到 APP 图标 + * @param packageName APP 包名 - 默认为当前 APP + * @return [Drawable] 无发获取时返回 [R.drawable.ic_android] */ -fun Context.appIcon(packageName: String) = - runCatching { - packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA) - .applicationInfo.loadIcon(packageManager) - }.getOrNull() ?: R.drawable.ic_android.drawableOf(resources) +fun Context.appIconOf(packageName: String = getPackageName()) = + getPackageInfoCompat(packageName)?.applicationInfo?.loadIcon(packageManager) ?: resources.drawableOf(R.drawable.ic_android) + +/** + * 获取 [Serializable] (兼容) + * @param key 键值名称 + * @return [T] or null + */ +inline fun Intent.getSerializableExtraCompat(key: String): T? { + @Suppress("DEPRECATION") + return if (Build.VERSION.SDK_INT >= 33) + getSerializableExtra(key, T::class.java) + else getSerializableExtra(key) as? T? +} /** * 计算与当前时间戳相差的友好时间 @@ -308,4 +346,27 @@ fun execShell(cmd: String, isSu: Boolean = true) = runCatching { (if (isSu) Shell.su(cmd) else Shell.sh(cmd)).exec().out.let { if (it.isNotEmpty()) it[0].trim() else "" } -}.getOrNull() ?: "" \ No newline at end of file +}.getOrNull() ?: "" + +/** + * 隐藏或显示启动器图标 + * + * - 你可能需要 LSPosed 的最新版本以开启高版本系统中隐藏 APP 桌面图标功能 + * @param isShow 是否显示 + */ +fun Context.hideOrShowLauncherIcon(isShow: Boolean) { + packageManager?.setComponentEnabledSetting( + ComponentName(packageName, "${BuildConfig.APPLICATION_ID}.Home"), + if (isShow) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP + ) +} + +/** + * 获取启动器图标状态 + * @return [Boolean] 是否显示 + */ +val Context.isLauncherIconShowing + get() = packageManager?.getComponentEnabledSetting( + ComponentName(packageName, "${BuildConfig.APPLICATION_ID}.Home") + ) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED \ No newline at end of file diff --git a/demo-app/src/main/java/com/fankes/apperrorsdemo/ui/activity/MainActivity.kt b/demo-app/src/main/java/com/fankes/apperrorsdemo/ui/activity/MainActivity.kt index c40c4ad..5682a2c 100644 --- a/demo-app/src/main/java/com/fankes/apperrorsdemo/ui/activity/MainActivity.kt +++ b/demo-app/src/main/java/com/fankes/apperrorsdemo/ui/activity/MainActivity.kt @@ -31,7 +31,7 @@ import com.fankes.apperrorsdemo.ui.activity.base.BaseActivity class MainActivity : BaseActivity() { override fun onCreate() { - binding.titleBackIcon.setOnClickListener { onBackPressed() } + binding.titleBackIcon.setOnClickListener { finish() } binding.throwRuntimeButton.setOnClickListener { Channel.throwRuntimeException() } binding.throwIllegalStateButton.setOnClickListener { Channel.throwIllegalStateException() } binding.throwNullPointerButton.setOnClickListener { Channel.throwNullPointerException() } diff --git a/demo-app/src/main/java/com/fankes/apperrorsdemo/ui/activity/base/BaseActivity.kt b/demo-app/src/main/java/com/fankes/apperrorsdemo/ui/activity/base/BaseActivity.kt index 9da2d9a..2410c87 100644 --- a/demo-app/src/main/java/com/fankes/apperrorsdemo/ui/activity/base/BaseActivity.kt +++ b/demo-app/src/main/java/com/fankes/apperrorsdemo/ui/activity/base/BaseActivity.kt @@ -31,9 +31,9 @@ import androidx.core.view.WindowCompat import androidx.viewbinding.ViewBinding import com.fankes.apperrorsdemo.R import com.fankes.apperrorsdemo.utils.factory.isNotSystemInDarkMode +import com.highcapable.yukihookapi.hook.factory.current import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass -import java.lang.reflect.ParameterizedType abstract class BaseActivity : AppCompatActivity() { @@ -42,15 +42,11 @@ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - javaClass.genericSuperclass.also { type -> - if (type is ParameterizedType) { - binding = (type.actualTypeArguments[0] as Class).method { - name = "inflate" - param(LayoutInflaterClass) - }.get().invoke(layoutInflater) ?: error("binding failed") - setContentView(binding.root) - } else error("binding but got wrong type") - } + binding = current().generic()?.argument()?.method { + name = "inflate" + param(LayoutInflaterClass) + }?.get()?.invoke(layoutInflater) ?: error("binding failed") + setContentView(binding.root) /** 隐藏系统的标题栏 */ supportActionBar?.hide() /** 初始化沉浸状态栏 */