From e267541e6d966a7a463a6bcfc0e8e46f6b11185c Mon Sep 17 00:00:00 2001 From: Timeline <53483352+Nep-Timeline@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:25:01 +0800 Subject: [PATCH 1/5] fix: Intercept ColorOS icon color The icon provided by initIcon will no longer be overridden, and the MediaNotification check will be removed to avoid performance issues. --- .../notify/hook/entity/SystemUIHooker.kt | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt index 2a6878a..9ff08b9 100644 --- a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt +++ b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt @@ -81,6 +81,7 @@ import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources import com.highcapable.yukihookapi.hook.log.YLog import de.robv.android.xposed.XposedHelpers import top.defaults.drawabletoolbox.DrawableBuilder +import java.util.WeakHashMap /** * 系统界面核心 Hook 类 @@ -278,6 +279,9 @@ object SystemUIHooker : YukiBaseHooker() { /** 是否已经使用过缓存刷新功能 */ private var isUsingCachingMethod = false + + /** 记录已处理过的图标 [ImageView] - 值 true 表示本模块接管,false 表示交还系统;用于拦截判断与避免重复处理 */ + private val moduleStyledIcons = WeakHashMap() /** * 判断通知是否来自系统推送 @@ -531,12 +535,16 @@ object SystemUIHooker : YukiBaseHooker() { compatCustomIcon(context, isGrayscaleIcon, if (nf.isOplusPush) nf.packageName else packageName).also { customTriple -> when { ConfigData.isEnableNotifyIconForceAppIcon -> iconView.apply { + /** 标记为已处理但非本模块接管 */ + moduleStyledIcons[this] = false /** 重新设置图标 */ setImageDrawable(appIcons[packageName] ?: context.appIconOf(packageName)) /** 设置默认样式 */ setDefaultNotifyIconViewStyle() } (customTriple.first != null && customTriple.third.not()) || isGrayscaleIcon -> iconView.apply { + /** 标记为本模块接管(兜底判断/防止重复处理用) */ + moduleStyledIcons[this] = true /** 设置不要裁切到边界 */ clipToOutline = false /** 重新设置图标 */ @@ -593,6 +601,8 @@ object SystemUIHooker : YukiBaseHooker() { } } else -> iconView.apply { + /** 标记为已处理但非本模块接管 */ + moduleStyledIcons[this] = false /** 重新设置图标 */ setImageDrawable(nf.compatPushingIcon(drawable)) /** 设置默认样式 */ @@ -706,22 +716,22 @@ object SystemUIHooker : YukiBaseHooker() { } /** 获取媒体会话管理器 */ - fun getMediaSessionManager(context: Context): MediaSessionManager { + /*fun getMediaSessionManager(context: Context): MediaSessionManager { if (mediaSessionManager == null) mediaSessionManager = context.getSystemService(Context.MEDIA_SESSION_SERVICE) as MediaSessionManager return mediaSessionManager!! - } + }*/ /** 判断是否为媒体通知 */ - fun isMediaNotificationAOSP(notification: Notification?): Boolean { + /*fun isMediaNotificationAOSP(notification: Notification?): Boolean { if (notification == null) return false return notification.javaClass.resolve().firstMethod { name = "isMediaNotification" }.of(notification).invoke() == true - } + }*/ /** 通过媒体会话和AOSP方法判断是否为媒体通知 */ - fun isMediaNotification(context: Context, notification: Notification, packageName: String): Boolean { + /*fun isMediaNotification(context: Context, notification: Notification, packageName: String): Boolean { if (isMediaNotificationAOSP(notification)) return true val mediaSessionManager: MediaSessionManager = getMediaSessionManager(context) @@ -731,7 +741,7 @@ object SystemUIHooker : YukiBaseHooker() { return true return false - } + }*/ /** * 刷新缓存数据 @@ -819,7 +829,7 @@ object SystemUIHooker : YukiBaseHooker() { NotificationEntryClass.resolve().optional().firstMethodOrNull { name = "getSbn" }?.of(args().first().any())?.invokeQuietly()?.also { nf -> - if (!isMediaNotification(context, nf.notification, nf.packageName)) nf.notification.smallIcon.loadDrawable(context)?.also { iconDrawable -> + nf.notification.smallIcon.loadDrawable(context)?.also { iconDrawable -> compatStatusIcon( context = context, nf = nf, @@ -953,7 +963,7 @@ object SystemUIHooker : YukiBaseHooker() { }?.invoke() }.also { nf -> nf?.notification?.also { - if (!isMediaNotification(context, it, context.packageName)) it.smallIcon.loadDrawable(context)?.also { iconDrawable -> + it.smallIcon.loadDrawable(context)?.also { iconDrawable -> compatNotifyIcon( context = context, nf = nf, @@ -987,7 +997,7 @@ object SystemUIHooker : YukiBaseHooker() { }?.invoke() }.also { nf -> nf?.notification?.also { - if (!isMediaNotification(context, it, context.packageName)) it.smallIcon.loadDrawable(context)?.also { iconDrawable -> + it.smallIcon.loadDrawable(context)?.also { iconDrawable -> compatNotifyIcon( context = context, nf = nf, @@ -1002,19 +1012,25 @@ object SystemUIHooker : YukiBaseHooker() { } } } + firstMethodOrNull { + name = "updateIconColor" + emptyParameters() + }?.hook()?.before { + if (moduleStyledIcons[headerIconOf(instance)] == true) resultFalse() + } } OplusNotificationGroupTemplateWrapperClass?.resolve()?.optional()?.apply { firstMethodOrNull { name = "initIcon" }?.hook()?.before { - val instanceContext = firstFieldOrNull { + /*val instanceContext = firstFieldOrNull { name = "context" }?.of(instance)?.get() as Context? if (instanceContext == null) - return@before + return@before*/ resultNull() - NotificationHeaderViewWrapperClass.resolve().optional().firstFieldOrNull { name = "mIcon" }?.of(instance)?.get()?.apply { + /*NotificationHeaderViewWrapperClass.resolve().optional().firstFieldOrNull { name = "mIcon" }?.of(instance)?.get()?.apply { ExpandableNotificationRowClass.resolve().optional() .firstMethodOrNull { name = "getEntry" } ?.of(NotificationViewWrapperClass.resolve().optional().firstFieldOrNull { @@ -1043,7 +1059,7 @@ object SystemUIHooker : YukiBaseHooker() { } } } - } + }*/ } } } else { @@ -1104,7 +1120,7 @@ object SystemUIHooker : YukiBaseHooker() { }?.invoke() }.also { nf -> nf?.notification?.also { - if (!isMediaNotification(context, it, context.packageName)) it.smallIcon.loadDrawable(context)?.also { iconDrawable -> + it.smallIcon.loadDrawable(context)?.also { iconDrawable -> /** 执行替换 */ compatNotifyIcon( context = context, @@ -1135,7 +1151,7 @@ object SystemUIHooker : YukiBaseHooker() { }?.invoke() }.also { nf -> nf?.notification?.also { - if (!isMediaNotification(context, it, context.packageName)) it.smallIcon.loadDrawable(context)?.also { iconDrawable -> + it.smallIcon.loadDrawable(context)?.also { iconDrawable -> /** 执行替换 */ fun doParse() { compatNotifyIcon( From d6092f02ac16b37775aab0f558e210caebcc5574 Mon Sep 17 00:00:00 2001 From: Timeline <53483352+Nep-Timeline@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:37:33 +0800 Subject: [PATCH 2/5] Update SystemUIHooker.kt --- .../coloros/notify/hook/entity/SystemUIHooker.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt index 9ff08b9..1a1b454 100644 --- a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt +++ b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt @@ -300,6 +300,14 @@ object SystemUIHooker : YukiBaseHooker() { parameterCount = 2 } != null + /** + * 取通知头部包装器的图标 [ImageView] + * @param headerWrapperExImp [OplusNotificationHeaderViewWrapperExImpClass] 实例 + * @return [ImageView] or null + */ + private fun headerIconOf(headerWrapperExImp: Any?) = + safeOfNull { XposedHelpers.getObjectField(XposedHelpers.callMethod(headerWrapperExImp, "getBase"), "mIcon") as? ImageView } + /** * 判断通知是否为新版本 * @return [Boolean] @@ -987,8 +995,7 @@ object SystemUIHooker : YukiBaseHooker() { name = "proxyOnContentUpdated" parameterCount = 1 }?.hook()?.after { - val imageView = XposedHelpers.getObjectField(XposedHelpers.callMethod(instance, "getBase"), "mIcon") as ImageView - imageView.apply { + headerIconOf(instance).apply { ExpandableNotificationRowClass.resolve().optional() .firstMethodOrNull { name = "getEntry" } ?.of(args[0])?.invokeQuietly()?.let { From 96ed5005dd7325ee6617b1c54a4746a558917bf6 Mon Sep 17 00:00:00 2001 From: Timeline <53483352+Nep-Timeline@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:37:54 +0800 Subject: [PATCH 3/5] Fix null safety in headerIconOf method call --- .../com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt index 1a1b454..6b83938 100644 --- a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt +++ b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt @@ -995,7 +995,7 @@ object SystemUIHooker : YukiBaseHooker() { name = "proxyOnContentUpdated" parameterCount = 1 }?.hook()?.after { - headerIconOf(instance).apply { + headerIconOf(instance)?.apply { ExpandableNotificationRowClass.resolve().optional() .firstMethodOrNull { name = "getEntry" } ?.of(args[0])?.invokeQuietly()?.let { From cd3312c517e810a8b2a06b47f394b040903d78a7 Mon Sep 17 00:00:00 2001 From: Timeline <53483352+Nep-Timeline@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:46:33 +0800 Subject: [PATCH 4/5] Modify SystemUIHooker with new import and comment out code Commented out a method block and added an import for safeOfNull. --- .../com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt index 6b83938..733c32d 100644 --- a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt +++ b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt @@ -67,6 +67,7 @@ import com.fankes.coloros.notify.utils.factory.isUpperOfAndroidS import com.fankes.coloros.notify.utils.factory.runInSafe import com.fankes.coloros.notify.utils.factory.safeOf import com.fankes.coloros.notify.utils.factory.safeOfFalse +import com.fankes.coloros.notify.utils.factory.safeOfNull import com.fankes.coloros.notify.utils.factory.systemAccentColor import com.fankes.coloros.notify.utils.tool.ActivationPromptTool import com.fankes.coloros.notify.utils.tool.BitmapCompatTool @@ -1113,7 +1114,7 @@ object SystemUIHooker : YukiBaseHooker() { /** 替换通知图标和样式 */ NotificationHeaderViewWrapperClass.resolve().optional().apply { - method { + /*method { name { it == "updateExpandability" || it == "setExpanded" } }.hookAll().before { firstFieldOrNull { name = "mIcon" }?.of(instance)?.get()?.apply { @@ -1142,7 +1143,7 @@ object SystemUIHooker : YukiBaseHooker() { } } } - } + }*/ method { name { it == "resolveHeaderViews" || it == "onContentUpdated" } From 956fa1126830c469161e0f66cc8078293516146d Mon Sep 17 00:00:00 2001 From: Timeline <53483352+Nep-Timeline@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:19:39 +0800 Subject: [PATCH 5/5] fix: collapse notification package name --- .../coloros/notify/hook/entity/SystemUIHooker.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt index 733c32d..491a665 100644 --- a/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt +++ b/app/src/main/java/com/fankes/coloros/notify/hook/entity/SystemUIHooker.kt @@ -290,6 +290,14 @@ object SystemUIHooker : YukiBaseHooker() { */ private val StatusBarNotification.isOplusPush get() = opPkg == PackageName.SYSTEM_FRAMEWORK && opPkg != packageName + /** + * 判断通知是否由系统生成 (ColorOS 16.1折叠通知) + * @return [Boolean] + */ + private fun isCollapseNotification(nfPackageName: String, contextPackageName: String) : Boolean { + return contextPackageName == PackageName.SYSTEMUI && nfPackageName != contextPackageName + } + /** * 判断通知背景是否为旧版本 * @return [Boolean] @@ -541,13 +549,14 @@ object SystemUIHooker : YukiBaseHooker() { iconView: ImageView, header: Boolean = false ) = runInSafe { - compatCustomIcon(context, isGrayscaleIcon, if (nf.isOplusPush) nf.packageName else packageName).also { customTriple -> + val realPackageName = if (nf.isOplusPush || isCollapseNotification(nf.packageName, packageName)) nf.packageName else packageName + compatCustomIcon(context, isGrayscaleIcon, realPackageName).also { customTriple -> when { ConfigData.isEnableNotifyIconForceAppIcon -> iconView.apply { /** 标记为已处理但非本模块接管 */ moduleStyledIcons[this] = false /** 重新设置图标 */ - setImageDrawable(appIcons[packageName] ?: context.appIconOf(packageName)) + setImageDrawable(appIcons[realPackageName] ?: context.appIconOf(realPackageName)) /** 设置默认样式 */ setDefaultNotifyIconViewStyle() }