Modify change module debug logs record method and optimize some code in HookEntry, SystemUIHooker, SystemUITool, MainActivity, activity_main

This commit is contained in:
2023-02-05 04:51:15 +08:00
parent 4c8b1b0d53
commit 63e4dd8299
5 changed files with 154 additions and 55 deletions

View File

@@ -39,7 +39,11 @@ import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
object HookEntry : IYukiHookXposedInit { object HookEntry : IYukiHookXposedInit {
override fun onInit() = configs { override fun onInit() = configs {
debugLog { tag = "MIUINativeNotifyIcon" } debugLog {
tag = "MIUINativeNotifyIcon"
isRecord = true
elements(PRIORITY)
}
isDebug = false isDebug = false
} }

View File

@@ -231,22 +231,22 @@ object SystemUIHooker : YukiBaseHooker() {
* 打印日志 * 打印日志
* @param tag 标识 * @param tag 标识
* @param context 实例 * @param context 实例
* @param expandedNf 通知实例 * @param nf 通知实例
* @param isCustom 是否为通知优化生效图标 * @param isCustom 是否为通知优化生效图标
* @param isGrayscale 是否为灰度图标 * @param isGrayscale 是否为灰度图标
*/ */
private fun printLogcat( private fun loggerDebug(tag: String, context: Context, nf: StatusBarNotification?, isCustom: Boolean, isGrayscale: Boolean) {
tag: String,
context: Context,
expandedNf: StatusBarNotification?,
isCustom: Boolean,
isGrayscale: Boolean
) {
if (ConfigData.isEnableModuleLog) loggerD( if (ConfigData.isEnableModuleLog) loggerD(
msg = "$tag --> [${context.appNameOf(packageName = expandedNf?.nfPkgName ?: "")}][${expandedNf?.nfPkgName}] " + msg = "(Processing $tag) ↓\n" +
"custom [$isCustom] " + "[Title]: ${nf?.notification?.extras?.getString(Notification.EXTRA_TITLE)}\n" +
"grayscale [$isGrayscale] " + "[Content]: ${nf?.notification?.extras?.getString(Notification.EXTRA_TEXT)}\n" +
"xmsf [${expandedNf?.isXmsf}]" "[App Name]: ${context.appNameOf(packageName = nf?.packageName ?: "")}\n" +
"[Package Name]: ${nf?.packageName}\n" +
"[Sender Package Name]: ${nf?.compatOpPkgName}\n" +
"[Custom Icon]: $isCustom\n" +
"[Grayscale Icon]: $isGrayscale\n" +
"[From Xmsf]: ${nf?.isXmsf}\n" +
"[String]: ${nf?.notification}"
) )
} }
@@ -372,53 +372,55 @@ object SystemUIHooker : YukiBaseHooker() {
* *
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook * 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
* @param context 实例 * @param context 实例
* @param expandedNf 通知实例 * @param nf 通知实例
* @param iconDrawable 小图标 [Drawable] * @param iconDrawable 小图标 [Drawable]
* @return [Pair] 回调小图标 - ([Drawable] 小图标,[Boolean] 是否替换) * @return [Pair] 回调小图标 - ([Drawable] 小图标,[Boolean] 是否替换)
*/ */
private fun compatStatusIcon(context: Context, expandedNf: StatusBarNotification?, iconDrawable: Drawable?) = private fun compatStatusIcon(context: Context, nf: StatusBarNotification?, iconDrawable: Drawable?) = nf?.let { notifyInstance ->
expandedNf?.let { notifyInstance -> if (iconDrawable == null) return@let Pair(null, false)
if (iconDrawable == null) return@let Pair(null, false) /** 判断是否不是灰度图标 */
/** 判断是否不是灰度图标 */ val isGrayscaleIcon = notifyInstance.isXmsf.not() && isGrayscaleIcon(context, iconDrawable, notifyInstance)
val isGrayscaleIcon = notifyInstance.isXmsf.not() && isGrayscaleIcon(context, iconDrawable, notifyInstance)
/** 目标彩色通知 APP 图标 */ /** 目标彩色通知 APP 图标 */
val customIcon = compatCustomIcon(context, isGrayscaleIcon, notifyInstance.nfPkgName).first val customTriple = compatCustomIcon(context, isGrayscaleIcon, notifyInstance.nfPkgName)
/** 打印日志 */
printLogcat(tag = "StatusIcon", context, notifyInstance, isCustom = customIcon != null, isGrayscaleIcon) /** 是否为通知优化生效图标 */
when { val isCustom = customTriple.first != null && customTriple.third.not()
/** 处理自定义通知图标优化 */ /** 打印日志 */
customIcon != null -> Pair(customIcon, true) loggerDebug(tag = "Status Bar Icon", context, notifyInstance, isCustom = isCustom, isGrayscaleIcon)
/** 若不是灰度图标自动处理为圆角 */ when {
isGrayscaleIcon.not() -> Pair(notifyInstance.compatPushingIcon(context, iconDrawable).rounded(context), true) /** 处理自定义通知图标优化 */
/** 否则返回原始小图标 */ customTriple.first != null -> Pair(customTriple.first, true)
else -> Pair(notifyInstance.notification.smallIcon.loadDrawable(context), false) /** 若不是灰度图标自动处理为圆角 */
} isGrayscaleIcon.not() -> Pair(notifyInstance.compatPushingIcon(context, iconDrawable).rounded(context), true)
} ?: Pair(null, false) /** 否则返回原始小图标 */
else -> Pair(notifyInstance.notification.smallIcon.loadDrawable(context), false)
}
} ?: Pair(null, false)
/** /**
* Hook 通知栏小图标 * Hook 通知栏小图标
* *
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook * 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
* @param context 实例 * @param context 实例
* @param expandedNf 通知实例 * @param nf 通知实例
* @param iconImageView 通知图标实例 * @param iconView 通知图标实例
* @param isExpanded 通知是否展开 - 可做最小化通知处理 - 默认:是 * @param isExpanded 通知是否展开 - 可做最小化通知处理 - 默认:是
* @param isUseMaterial3Style 是否使用 Material 3 通知图标风格 - 默认跟随系统版本决定 * @param isUseMaterial3Style 是否使用 Material 3 通知图标风格 - 默认跟随系统版本决定
*/ */
private fun compatNotifyIcon( private fun compatNotifyIcon(
context: Context, context: Context,
expandedNf: StatusBarNotification?, nf: StatusBarNotification?,
iconImageView: ImageView, iconView: ImageView,
isExpanded: Boolean = true, isExpanded: Boolean = true,
isUseMaterial3Style: Boolean = isUpperOfAndroidS, isUseMaterial3Style: Boolean = isUpperOfAndroidS
) = runInSafe(msg = "compatNotifyIcon") { ) = runInSafe(msg = "compatNotifyIcon") {
/** /**
* 设置默认通知图标 * 设置默认通知图标
* @param drawable 通知图标 * @param drawable 通知图标
*/ */
fun setDefaultNotifyIcon(drawable: Drawable?) { fun setDefaultNotifyIcon(drawable: Drawable?) {
iconImageView.apply { iconView.apply {
/** 重新设置图标 */ /** 重新设置图标 */
setImageDrawable(drawable) setImageDrawable(drawable)
/** 设置裁切到边界 */ /** 设置裁切到边界 */
@@ -443,7 +445,7 @@ object SystemUIHooker : YukiBaseHooker() {
} }
} }
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */ /** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.let { notifyInstance -> nf?.let { notifyInstance ->
/** 新版风格反色 */ /** 新版风格反色 */
val newStyle = if (context.isSystemInDarkMode) 0xFF2D2D2D.toInt() else Color.WHITE val newStyle = if (context.isSystemInDarkMode) 0xFF2D2D2D.toInt() else Color.WHITE
@@ -486,7 +488,7 @@ object SystemUIHooker : YukiBaseHooker() {
(it.second.takeIf { e -> e != 0 } ?: (if (isUseMaterial3Style) context.systemAccentColor else 0)) else 0 (it.second.takeIf { e -> e != 0 } ?: (if (isUseMaterial3Style) context.systemAccentColor else 0)) else 0
} }
/** 打印日志 */ /** 打印日志 */
printLogcat(tag = "NotifyIcon", context, notifyInstance, isCustom = customIcon != null, isGrayscaleIcon) loggerDebug(tag = "Notification Panel Icon", context, notifyInstance, isCustom = customIcon != null, isGrayscaleIcon)
/** 处理自定义通知图标优化 */ /** 处理自定义通知图标优化 */
when { when {
ConfigData.isEnableNotifyIconForceAppIcon -> { ConfigData.isEnableNotifyIconForceAppIcon -> {
@@ -494,7 +496,7 @@ object SystemUIHooker : YukiBaseHooker() {
val miuiAppIcon = notifyInstance.notification?.extras?.getParcelable<Icon?>("miui.appIcon") val miuiAppIcon = notifyInstance.notification?.extras?.getParcelable<Icon?>("miui.appIcon")
setDefaultNotifyIcon(drawable = miuiAppIcon?.loadDrawable(context) ?: context.appIconOf(notifyInstance.nfPkgName)) setDefaultNotifyIcon(drawable = miuiAppIcon?.loadDrawable(context) ?: context.appIconOf(notifyInstance.nfPkgName))
} }
customIcon != null -> iconImageView.apply { customIcon != null -> iconView.apply {
/** 设置不要裁切到边界 */ /** 设置不要裁切到边界 */
clipToOutline = false clipToOutline = false
/** 设置自定义小图标 */ /** 设置自定义小图标 */
@@ -513,9 +515,9 @@ object SystemUIHooker : YukiBaseHooker() {
} }
else -> { else -> {
/** 重新设置图标 - 防止系统更改它 */ /** 重新设置图标 - 防止系统更改它 */
iconImageView.setImageDrawable(iconDrawable) iconView.setImageDrawable(iconDrawable)
/** 判断如果是灰度图标就给他设置一个白色颜色遮罩 */ /** 判断如果是灰度图标就给他设置一个白色颜色遮罩 */
if (isGrayscaleIcon) iconImageView.apply { if (isGrayscaleIcon) iconView.apply {
/** 设置不要裁切到边界 */ /** 设置不要裁切到边界 */
clipToOutline = false clipToOutline = false
/** 设置图标着色 */ /** 设置图标着色 */
@@ -559,7 +561,6 @@ object SystemUIHooker : YukiBaseHooker() {
* 只要不是灰度就返回彩色图标 * 只要不是灰度就返回彩色图标
* 否则不对颜色进行反色处理防止一些系统图标出现异常 * 否则不对颜色进行反色处理防止一些系统图标出现异常
*/ */
printLogcat(tag = "IconColor", context, notifyInstance, isTargetFixApp, isGrayscaleIcon)
return isTargetFixApp || isGrayscaleIcon return isTargetFixApp || isGrayscaleIcon
} }
@@ -930,8 +931,8 @@ object SystemUIHooker : YukiBaseHooker() {
field { name = "mAppIcon" }.get(instance).cast<ImageView>()?.clone { field { name = "mAppIcon" }.get(instance).cast<ImageView>()?.clone {
compatNotifyIcon( compatNotifyIcon(
context = context, context = context,
expandedNf = instance.getRowPair().second.getSbn(), nf = instance.getRowPair().second.getSbn(),
iconImageView = this, iconView = this,
isUseMaterial3Style = true isUseMaterial3Style = true
) )
} }
@@ -950,7 +951,7 @@ object SystemUIHooker : YukiBaseHooker() {
field { name = "mAppIcon" }.get(instance).cast<ImageView>()?.apply { field { name = "mAppIcon" }.get(instance).cast<ImageView>()?.apply {
compatNotifyIcon(context, NotificationChildrenContainerClass.toClassOrNull()?.field { compatNotifyIcon(context, NotificationChildrenContainerClass.toClassOrNull()?.field {
name = "mContainingNotification" name = "mContainingNotification"
}?.get(instance)?.any()?.getSbn(), iconImageView = this, isUseMaterial3Style = true) }?.get(instance)?.any()?.getSbn(), iconView = this, isUseMaterial3Style = true)
} }
} }
} }

View File

@@ -164,6 +164,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.moduleEnableSwitch.bind(ConfigData.ENABLE_MODULE) { binding.moduleEnableSwitch.bind(ConfigData.ENABLE_MODULE) {
onInitialize { onInitialize {
binding.moduleEnableLogSwitch.isVisible = it binding.moduleEnableLogSwitch.isVisible = it
binding.expAllDebugLogButton.isVisible = it && ConfigData.isEnableModuleLog
binding.colorIconHookItem.isVisible = it binding.colorIconHookItem.isVisible = it
binding.statusIconCountItem.isVisible = isLowerAndroidR.not() && it binding.statusIconCountItem.isVisible = isLowerAndroidR.not() && it
binding.notifyStyleConfigItem.isVisible = it binding.notifyStyleConfigItem.isVisible = it
@@ -176,7 +177,11 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
} }
} }
binding.moduleEnableLogSwitch.bind(ConfigData.ENABLE_MODULE_LOG) { binding.moduleEnableLogSwitch.bind(ConfigData.ENABLE_MODULE_LOG) {
onChanged { SystemUITool.showNeedRestartSnake(context = this@MainActivity) } onInitialize { binding.expAllDebugLogButton.isVisible = it && ConfigData.isEnableModule }
onChanged {
reinitialize()
SystemUITool.refreshSystemUI(context = this@MainActivity, isRefreshCacheOnly = true)
}
} }
binding.statusIconCountSwitch.bind(ConfigData.ENABLE_LIFTED_STATUS_ICON_COUNT) { binding.statusIconCountSwitch.bind(ConfigData.ENABLE_LIFTED_STATUS_ICON_COUNT) {
onInitialize { binding.statusIconCountChildItem.isVisible = it } onInitialize { binding.statusIconCountChildItem.isVisible = it }
@@ -275,6 +280,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.notifyIconCustomCornerSeekbar.bind(ConfigData.NOTIFY_ICON_CORNER_SIZE, binding.notifyIconCustomCornerText, suffix = " dp") { binding.notifyIconCustomCornerSeekbar.bind(ConfigData.NOTIFY_ICON_CORNER_SIZE, binding.notifyIconCustomCornerText, suffix = " dp") {
SystemUITool.refreshSystemUI(context = this) SystemUITool.refreshSystemUI(context = this)
} }
/** 导出全部日志按钮点击事件 */
binding.expAllDebugLogButton.setOnClickListener { SystemUITool.obtainAndExportDebugLogs(context = this) }
/** MIUI 通知显示设置按钮点击事件 */ /** MIUI 通知显示设置按钮点击事件 */
binding.miuiNotifyStyleButton.setOnClickListener { SystemUITool.openMiuiNotificationDisplaySettings(context = this) } binding.miuiNotifyStyleButton.setOnClickListener { SystemUITool.openMiuiNotificationDisplaySettings(context = this) }
/** 通知图标优化名单按钮点击事件 */ /** 通知图标优化名单按钮点击事件 */
@@ -339,6 +346,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
if (btn.isPressed.not()) return@setOnCheckedChangeListener if (btn.isPressed.not()) return@setOnCheckedChangeListener
hideOrShowLauncherIcon(b) hideOrShowLauncherIcon(b)
} }
/** 注册导出调试日志启动器 */
SystemUITool.registerExportDebugLogsLauncher(activity = this)
} }
/** 前往项目地址 */ /** 前往项目地址 */

View File

@@ -25,19 +25,22 @@ package com.fankes.miui.notify.utils.tool
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.fankes.miui.notify.const.PackageName import com.fankes.miui.notify.const.PackageName
import com.fankes.miui.notify.data.ConfigData import com.fankes.miui.notify.data.ConfigData
import com.fankes.miui.notify.ui.activity.MainActivity import com.fankes.miui.notify.ui.activity.MainActivity
import com.fankes.miui.notify.utils.factory.delayedRun import com.fankes.miui.notify.utils.factory.*
import com.fankes.miui.notify.utils.factory.execShell
import com.fankes.miui.notify.utils.factory.showDialog
import com.fankes.miui.notify.utils.factory.snake
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.factory.dataChannel import com.highcapable.yukihookapi.hook.factory.dataChannel
import com.highcapable.yukihookapi.hook.log.YukiHookLogger
import com.highcapable.yukihookapi.hook.log.YukiLoggerData
import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData
import java.util.*
/** /**
* 系统界面工具 * 系统界面工具
@@ -47,6 +50,12 @@ object SystemUITool {
private val CALL_HOST_REFRESH_CACHING = ChannelData("call_host_refresh_caching", false) private val CALL_HOST_REFRESH_CACHING = ChannelData("call_host_refresh_caching", false)
private val CALL_MODULE_REFRESH_RESULT = ChannelData("call_module_refresh_result", false) private val CALL_MODULE_REFRESH_RESULT = ChannelData("call_module_refresh_result", false)
/** 当前全部调试日志 */
private var debugLogs = ArrayList<YukiLoggerData>()
/** 当前启动器实例 */
private var launcher: ActivityResultLauncher<String>? = null
/** /**
* 宿主注册监听 * 宿主注册监听
*/ */
@@ -70,6 +79,61 @@ object SystemUITool {
fun checkingActivated(context: Context, result: (Boolean) -> Unit) = fun checkingActivated(context: Context, result: (Boolean) -> Unit) =
context.dataChannel(PackageName.SYSTEMUI).checkingVersionEquals(result = result) context.dataChannel(PackageName.SYSTEMUI).checkingVersionEquals(result = result)
/**
* 注册导出调试日志启动器到 [AppCompatActivity]
* @param activity 实例
*/
fun registerExportDebugLogsLauncher(activity: AppCompatActivity) {
launcher = activity.registerForActivityResult(ActivityResultContracts.CreateDocument("*/application")) { result ->
runCatching {
result?.let { e ->
val content = "" +
"================================================================\n" +
" Generated by MIUINativeNotifyIcon\n" +
" Project Url: https://github.com/fankes/MIUINativeNotifyIcon\n" +
"================================================================\n\n" +
"[Device Brand]: ${Build.BRAND}\n" +
"[Device Model]: ${Build.MODEL}\n" +
"[Display]: ${Build.DISPLAY}\n" +
"[Android Version]: ${Build.VERSION.RELEASE}\n" +
"[Android API Level]: ${Build.VERSION.SDK_INT}\n" +
"[MIUI Version]: $miuiFullVersion\n" +
"[System Locale]: ${Locale.getDefault()}\n\n" + YukiHookLogger.contents(debugLogs).trim()
activity.contentResolver?.openOutputStream(e)?.apply { write(content.toByteArray()) }?.close()
activity.snake(msg = "导出完成")
} ?: activity.snake(msg = "已取消操作")
}.onFailure { activity.snake(msg = "导出过程发生错误") }
}
}
/**
* 获取并导出全部调试日志
* @param context 实例
*/
fun obtainAndExportDebugLogs(context: Context) {
/** 执行导出操作 */
fun doExport() {
context.dataChannel(PackageName.SYSTEMUI).obtainLoggerInMemoryData {
if (it.isNotEmpty()) {
debugLogs = it
runCatching { launcher?.launch("miui_notification_icons_processing_logs.log") }
.onFailure { context.snake(msg = "启动系统文件选择器失败") }
} else context.snake(msg = "暂无调试日志")
}
}
if (YukiHookAPI.Status.isXposedModuleActive)
context.showDialog {
title = "导出全部调试日志"
msg = "调试日志中会包含当前系统推送的全部通知内容,其中可能包含你的个人隐私," +
"你可以在导出后的日志文件中选择将这些敏感信息模糊化处理再进行共享," +
"开发者使用并查看你导出的调试日志仅为排查与修复问题,并且在之后会及时销毁这些日志。\n\n" +
"继续导出即代表你已阅读并知悉上述内容。"
confirmButton(text = "继续") { doExport() }
cancelButton()
}
else context.snake(msg = "模块没有激活,请先激活模块")
}
/** 当 Root 权限获取失败时显示对话框 */ /** 当 Root 权限获取失败时显示对话框 */
private fun Context.showWhenAccessRootFail() = private fun Context.showWhenAccessRootFail() =
showDialog { showDialog {

View File

@@ -211,13 +211,13 @@
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="15dp" android:paddingTop="15dp">
android:paddingTop="15dp"
android:paddingRight="15dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center|start"> android:gravity="center|start">
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
@@ -250,7 +250,9 @@
android:id="@+id/module_enable_switch" android:id="@+id/module_enable_switch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:layout_marginRight="15dp"
android:text="启用模块" android:text="启用模块"
android:textColor="@color/colorTextGray" android:textColor="@color/colorTextGray"
android:textSize="15sp" /> android:textSize="15sp" />
@@ -259,14 +261,33 @@
android:id="@+id/module_enable_log_switch" android:id="@+id/module_enable_log_switch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="35dp" android:layout_height="35dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:text="启用调试日志" android:text="启用调试日志"
android:textColor="@color/colorTextGray" android:textColor="@color/colorTextGray"
android:textSize="15sp" /> android:textSize="15sp" />
<TextView <TextView
android:id="@+id/exp_all_debug_log_button"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="13dp"
android:layout_marginRight="13dp"
android:layout_marginBottom="10dp"
android:background="@drawable/bg_button_round"
android:gravity="center"
android:padding="10dp"
android:singleLine="true"
android:text="导出全部调试日志"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.6" android:alpha="0.6"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"