适配 MIUI 12,修复在 MIUI 12 上不生效的问题,加入最低 MIUI 版本判断

This commit is contained in:
2022-02-05 21:42:35 +08:00
parent 45dde844b7
commit f6c4448eed
3 changed files with 310 additions and 129 deletions

View File

@@ -18,7 +18,7 @@
* *
* This file is Created by fankes on 2022/01/24. * This file is Created by fankes on 2022/01/24.
*/ */
@file:Suppress("SameParameterValue") @file:Suppress("SameParameterValue", "DEPRECATION")
package com.fankes.miui.notify.hook package com.fankes.miui.notify.hook
@@ -47,18 +47,24 @@ class HookMain : IXposedHookLoadPackage {
companion object { companion object {
private const val NotificationUtilClass = private const val SystemUIApplicationClass = "$SYSTEMUI_PACKAGE_NAME.SystemUIApplication"
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationUtil"
private const val NotificationHeaderViewWrapperInjectorClass = private const val NotificationHeaderViewWrapperInjectorClass =
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapperInjector" "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapperInjector"
private const val ExpandedNotificationClass = private const val StatusBarIconViewClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.StatusBarIconView"
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.ExpandedNotification"
private const val SystemUIApplicationClass = "$SYSTEMUI_PACKAGE_NAME.SystemUIApplication"
private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil" private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
private val NotificationUtilClass = Pair(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationUtil",
"$SYSTEMUI_PACKAGE_NAME.miui.statusbar.notification.NotificationUtil"
)
private val ExpandedNotificationClass = Pair(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.ExpandedNotification",
"$SYSTEMUI_PACKAGE_NAME.miui.statusbar.ExpandedNotification"
)
} }
/** 仅作用于替换的 Hook 方法体 */ /** 仅作用于替换的 Hook 方法体 */
@@ -132,6 +138,62 @@ class HookMain : IXposedHookLoadPackage {
Log.e("MIUINativeNotifyIcon", content, e) Log.e("MIUINativeNotifyIcon", content, e)
} }
/**
* 目标类是否存在
* @param name 类名
* @return [Boolean]
*/
private fun XC_LoadPackage.LoadPackageParam.isClassExist(name: String) = try {
classLoader.loadClass(name)
true
} catch (_: Throwable) {
false
}
/**
* 目标方法是否存在
* @param classPair 类数组
* @param name 方法名
* @param param 方法参数类型数组
* @return [Boolean]
*/
private fun XC_LoadPackage.LoadPackageParam.isMethodExist(
classPair: Pair<String, String>,
name: String, vararg param: Class<*>
) = try {
(try {
classLoader.loadClass(classPair.first)
} catch (_: Throwable) {
try {
classLoader.loadClass(classPair.second)
} catch (_: Throwable) {
null
}
})?.getDeclaredMethod(name, *param)
true
} catch (_: Throwable) {
false
}
/**
* 目标方法是否存在
* @param className 类名
* @param name 方法名
* @param param 方法参数类型数组
* @return [Boolean]
*/
private fun XC_LoadPackage.LoadPackageParam.isMethodExist(className: String, name: String, vararg param: Class<*>) =
try {
(try {
classLoader.loadClass(className)
} catch (_: Throwable) {
null
})?.getDeclaredMethod(name, *param)
true
} catch (_: Throwable) {
false
}
/** /**
* 查找目标类 * 查找目标类
* @param name 类名 * @param name 类名
@@ -140,6 +202,40 @@ class HookMain : IXposedHookLoadPackage {
private fun XC_LoadPackage.LoadPackageParam.findClass(name: String) = private fun XC_LoadPackage.LoadPackageParam.findClass(name: String) =
classLoader.loadClass(name) classLoader.loadClass(name)
/**
* 查找目标类 - 两个类都没找到才会报错
* @param pair 类名数组
* @return [Class]
*/
private fun XC_LoadPackage.LoadPackageParam.findClass(pair: Pair<String, String>) = try {
classLoader.loadClass(pair.first)
} catch (_: Throwable) {
try {
classLoader.loadClass(pair.second)
} catch (e: Throwable) {
logE(content = "Cannot find Class ${pair.first} and ${pair.second}", e)
error("[Throwable] Cannot find Class ${pair.first} and ${pair.second}")
}
}
/**
* 存在目标类的类名 - 两个类都没找到会抛出异常
* @param pair 类名数组
* @return [String] 目标类名
*/
private fun XC_LoadPackage.LoadPackageParam.existClass(pair: Pair<String, String>) = try {
classLoader.loadClass(pair.first)
pair.first
} catch (_: Throwable) {
try {
classLoader.loadClass(pair.second)
pair.second
} catch (_: Throwable) {
logE(content = "Cannot find Class ${pair.first} and ${pair.second}")
error("[Throwable] Cannot find Class ${pair.first} and ${pair.second}")
}
}
/** /**
* ⚠️ 这个是修复彩色图标的关键核心代码判断 * ⚠️ 这个是修复彩色图标的关键核心代码判断
* 判断是否为灰度图标 - 反射执行系统方法 * 判断是否为灰度图标 - 反射执行系统方法
@@ -336,6 +432,70 @@ class HookMain : IXposedHookLoadPackage {
} ?: logW(content = "AutoSetAppIconOnSet -> StatusBarNotification got null") } ?: logW(content = "AutoSetAppIconOnSet -> StatusBarNotification got null")
} }
/**
* Hook 通知栏小图标颜色
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
* @param expandedNf 状态栏实例
* @return [Boolean] 是否忽略通知图标颜色
*/
private fun XC_LoadPackage.LoadPackageParam.hookIgnoreStatusBarIconColor(expandedNf: StatusBarNotification?) =
if (HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true))
try {
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.let { notifyInstance ->
/** 获取通知小图标 */
val iconDrawable =
notifyInstance.notification.smallIcon.loadDrawable(globalContext)
/** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = !isGrayscaleIcon(globalContext, iconDrawable)
/** 获取目标修复彩色图标的 APP */
var isTargetApp = false
run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
HookMedium.isAppNotifyHookOf(it)
) {
if (isNotGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it)) isTargetApp = true
return@run
}
}
}
/** 如果开启了修复 APP 的彩色图标 */
if (isTargetApp && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true)) let {
logD(
content = "IgnoreStatusBarIconColor -> " +
"hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${findAppName(notifyInstance)}"
)
false
}
else let {
logD(
content = "IgnoreStatusBarIconColor -> " +
"hook Grayscale[${!isNotGrayscaleIcon}] AppIcon " +
"[pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${findAppName(notifyInstance)}"
)
/** 只要不是灰度就返回彩色图标 */
isNotGrayscaleIcon
}
} ?: let {
logW(content = "IgnoreStatusBarIconColor -> StatusBarNotification got null")
/** 否则不对颜色进行反色处理防止一些系统图标出现异常 */
true
}
} catch (e: Exception) {
logE("Failed to hook ignoreStatusBarIconColor", e)
false
}
else let {
logD(content = "IgnoreStatusBarIconColor -> hook NonColor AppIcon")
false
}
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) { override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
if (lpparam == null) return if (lpparam == null) return
when (lpparam.packageName) { when (lpparam.packageName) {
@@ -358,6 +518,9 @@ class HookMain : IXposedHookLoadPackage {
/** 系统版本低于 Android P 停止 Hook */ /** 系统版本低于 Android P 停止 Hook */
isLowerAndroidP -> isLowerAndroidP ->
logW(content = "Aborted Hook -> This System is lower than Android P") logW(content = "Aborted Hook -> This System is lower than Android P")
/** 不是支持的 MIUI 系统停止 Hook */
isNotSupportMiuiVersion ->
logW(content = "Aborted Hook -> This MIUI Version $miuiVersion not supported")
/** Hook 被手动关闭停止 Hook */ /** Hook 被手动关闭停止 Hook */
!HookMedium.getBoolean(HookMedium.ENABLE_MODULE, default = true) -> !HookMedium.getBoolean(HookMedium.ENABLE_MODULE, default = true) ->
logW(content = "Aborted Hook -> Hook Closed") logW(content = "Aborted Hook -> Hook Closed")
@@ -365,7 +528,7 @@ class HookMain : IXposedHookLoadPackage {
/** 强制回写系统的状态栏图标样式为原生 */ /** 强制回写系统的状态栏图标样式为原生 */
runWithoutError(error = "SubstituteSmallIcon") { runWithoutError(error = "SubstituteSmallIcon") {
XposedHelpers.findAndHookMethod( XposedHelpers.findAndHookMethod(
NotificationUtilClass, lpparam.existClass(NotificationUtilClass),
lpparam.classLoader, lpparam.classLoader,
"shouldSubstituteSmallIcon", "shouldSubstituteSmallIcon",
lpparam.findClass(ExpandedNotificationClass), lpparam.findClass(ExpandedNotificationClass),
@@ -373,76 +536,44 @@ class HookMain : IXposedHookLoadPackage {
) )
} }
/** 修复通知图标为彩色 */ /** 修复通知图标为彩色 */
if (lpparam.isMethodExist(NotificationUtilClass, name = "ignoreStatusBarIconColor"))
runWithoutError(error = "IgnoreStatusBarIconColor") { runWithoutError(error = "IgnoreStatusBarIconColor") {
XposedHelpers.findAndHookMethod( XposedHelpers.findAndHookMethod(
NotificationUtilClass, lpparam.existClass(NotificationUtilClass),
lpparam.classLoader, lpparam.classLoader,
"ignoreStatusBarIconColor", "ignoreStatusBarIconColor",
lpparam.findClass(ExpandedNotificationClass), lpparam.findClass(ExpandedNotificationClass),
object : XC_MethodReplacement() { object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam) = override fun replaceHookedMethod(param: MethodHookParam) =
if (HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true)) lpparam.hookIgnoreStatusBarIconColor(param.args?.get(0) as? StatusBarNotification?)
try { }
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */ )
(param.args?.get(0) as? StatusBarNotification?)?.let { notifyInstance -> }
/** 获取通知小图标 */ else
val iconDrawable = runWithoutError(error = "UpdateIconColor") {
notifyInstance.notification.smallIcon.loadDrawable(lpparam.globalContext) XposedHelpers.findAndHookMethod(
StatusBarIconViewClass,
lpparam.classLoader, "updateIconColor",
object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) =
runWithoutError(error = "UpdateIconColorOnSet") {
/** 是否忽略图标颜色 */
val isIgnoredColor = lpparam.hookIgnoreStatusBarIconColor(
param.thisObject.javaClass.getDeclaredField("mNotification").apply {
isAccessible = true
}[param.thisObject] as? StatusBarNotification?
)
/** 判断是否不是灰度图标 */ /** 当前着色颜色 */
val isNotGrayscaleIcon = val currentColor =
!lpparam.isGrayscaleIcon(lpparam.globalContext, iconDrawable) param.thisObject.javaClass.getDeclaredField("mCurrentSetColor").apply {
isAccessible = true
/** 获取目标修复彩色图标的 APP */ }[param.thisObject] as? Int ?: Color.WHITE
var isTargetApp = false /** 判断并设置颜色 */
run { if (isIgnoredColor)
IconPackParams.iconDatas.forEach { (param.thisObject as? ImageView?)?.colorFilter = null
if ((notifyInstance.opPkgName == it.packageName || else (param.thisObject as? ImageView?)?.setColorFilter(currentColor)
lpparam.findAppName(notifyInstance) == it.appName) && logD(content = "IgnoreStatusBarIconColor[UseOldWay] -> isIgnored[$isIgnoredColor]")
HookMedium.isAppNotifyHookOf(it)
) {
if (isNotGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it))
isTargetApp = true
return@run
}
}
}
/** 如果开启了修复 APP 的彩色图标 */
if (isTargetApp &&
HookMedium.getBoolean(
HookMedium.ENABLE_NOTIFY_ICON_HOOK,
default = true
)
) let {
logD(
content = "IgnoreStatusBarIconColor -> " +
"hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${lpparam.findAppName(notifyInstance)}"
)
false
}
else let {
logD(
content = "IgnoreStatusBarIconColor -> " +
"hook Grayscale[${!isNotGrayscaleIcon}] AppIcon " +
"[pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${lpparam.findAppName(notifyInstance)}"
)
/** 只要不是灰度就返回彩色图标 */
isNotGrayscaleIcon
}
} ?: let {
logW(content = "IgnoreStatusBarIconColor -> StatusBarNotification got null")
/** 否则不对颜色进行反色处理防止一些系统图标出现异常 */
true
}
} catch (e: Exception) {
logE("Failed to hook ignoreStatusBarIconColor", e)
false
}
else let {
logD(content = "IgnoreStatusBarIconColor -> hook NonColor AppIcon")
false
} }
} }
) )
@@ -458,13 +589,22 @@ class HookMain : IXposedHookLoadPackage {
Int::class.java Int::class.java
).apply { isAccessible = true } ).apply { isAccessible = true }
} catch (_: Throwable) { } catch (_: Throwable) {
try {
/** 旧版方法 */ /** 旧版方法 */
lpparam.findClass(NotificationUtilClass) lpparam.findClass(NotificationUtilClass)
.getDeclaredMethod("getSmallIcon", lpparam.findClass(ExpandedNotificationClass)) .getDeclaredMethod("getSmallIcon", lpparam.findClass(ExpandedNotificationClass))
.apply { isAccessible = true } .apply { isAccessible = true }
} catch (_: Throwable) {
/** 超旧版方法 */
lpparam.findClass(NotificationUtilClass)
.getDeclaredMethod(
"getSmallIcon",
Context::class.java,
lpparam.findClass(ExpandedNotificationClass)
).apply { isAccessible = true }
}
}.also { }.also {
XposedBridge.hookMethod(it, object : XC_MethodHook() { XposedBridge.hookMethod(it, object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) { override fun afterHookedMethod(param: MethodHookParam) {
lpparam.hookSmallIconOnSet(param) lpparam.hookSmallIconOnSet(param)
} }
@@ -472,6 +612,7 @@ class HookMain : IXposedHookLoadPackage {
} }
} }
/** 修复下拉通知图标自动设置回 APP 图标的方法 */ /** 修复下拉通知图标自动设置回 APP 图标的方法 */
if (lpparam.isClassExist(NotificationHeaderViewWrapperInjectorClass))
runWithoutError(error = "AutoSetAppIcon") { runWithoutError(error = "AutoSetAppIcon") {
var isNewWay = true var isNewWay = true
try { try {
@@ -501,9 +642,14 @@ class HookMain : IXposedHookLoadPackage {
}) })
} }
} }
else logW(content = "Your MIUI Version $miuiVersion is too old and not support for RowsIcon")
/** 干掉下拉通知图标自动设置回 APP 图标的方法 - Android 12 */ /** 干掉下拉通知图标自动设置回 APP 图标的方法 - Android 12 */
if (isUpperOfAndroidS) if (isUpperOfAndroidS &&
runWithoutError(error = "ResetIconBgAndPaddings") { lpparam.isMethodExist(
NotificationHeaderViewWrapperInjectorClass,
name = "resetIconBgAndPaddings"
)
) runWithoutError(error = "ResetIconBgAndPaddings") {
XposedHelpers.findAndHookMethod( XposedHelpers.findAndHookMethod(
NotificationHeaderViewWrapperInjectorClass, NotificationHeaderViewWrapperInjectorClass,
lpparam.classLoader, lpparam.classLoader,

View File

@@ -53,7 +53,7 @@ class MainActivity : BaseActivity() {
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
/** 设置文本 */ /** 设置文本 */
findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion" findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion"
findViewById<TextView>(R.id.main_text_miui_version).text = "MIUI 版本:$miuiVersion" findViewById<TextView>(R.id.main_text_miui_version).text = "MIUI 版本:$miuiFullVersion"
when { when {
/** 判断是否为 MIUI 系统 */ /** 判断是否为 MIUI 系统 */
isNotMIUI -> isNotMIUI ->
@@ -64,15 +64,24 @@ class MainActivity : BaseActivity() {
confirmButton(text = "退出") { finish() } confirmButton(text = "退出") { finish() }
noCancelable() noCancelable()
} }
/** 判断最低系统版本 */ /** 判断最低 Android 系统版本 */
isLowerAndroidP -> isLowerAndroidP ->
showDialog { showDialog {
title = "系统版本过低" title = "Android 系统版本过低"
msg = "此模块最低支持基于 Android 9 的 MIUI 系统,你的系统版本过低不再进行适配。\n" + msg = "此模块最低支持基于 Android 9 的 MIUI 系统,你的系统版本过低不再进行适配。\n" +
"如有问题请联系 酷安 @星夜不荟" "如有问题请联系 酷安 @星夜不荟"
confirmButton(text = "退出") { finish() } confirmButton(text = "退出") { finish() }
noCancelable() noCancelable()
} }
/** 判断最低 MIUI 版本 */
isNotSupportMiuiVersion ->
showDialog {
title = "MIUI 版本过低"
msg = "此模块最低支持 MIUI 12 系统,你的 MIUI 版本为 ${miuiVersion},不再进行适配。\n" +
"如有问题请联系 酷安 @星夜不荟"
confirmButton(text = "退出") { finish() }
noCancelable()
}
/** 判断是否 Hook */ /** 判断是否 Hook */
isHooked() -> { isHooked() -> {
findViewById<LinearLayout>(R.id.main_lin_status).setBackgroundResource(R.drawable.green_round) findViewById<LinearLayout>(R.id.main_lin_status).setBackgroundResource(R.drawable.green_round)

View File

@@ -91,6 +91,24 @@ val isMIUI by lazy {
*/ */
inline val isNotMIUI get() = !isMIUI inline val isNotMIUI get() = !isMIUI
/**
* 是否为支持的 MIUI 版本
* @return [Boolean]
*/
val isSupportMiuiVersion
get() = when (miuiVersion) {
"12" -> true
"12.5" -> true
"13" -> true
else -> false
}
/**
* 是否不为支持的 MIUI 版本
* @return [Boolean]
*/
inline val isNotSupportMiuiVersion get() = !isSupportMiuiVersion
/** /**
* 获取 MIUI 版本 * 获取 MIUI 版本
* @return [String] * @return [String]
@@ -109,7 +127,15 @@ val miuiVersion
"V13" -> "13" "V13" -> "13"
else -> it.replace(oldValue = "V", newValue = "") else -> it.replace(oldValue = "V", newValue = "")
} }
} + " " + findPropString(key = "ro.system.build.version.incremental") }.trim()
else "NULL"
/**
* 获取 MIUI 完全版本
* @return [String]
*/
val miuiFullVersion
get() = if (isMIUI) (miuiVersion + " " + findPropString(key = "ro.system.build.version.incremental"))
else "不是 MIUI 系统" else "不是 MIUI 系统"
/** /**