从 AOSP 源码提取工具类修复 💩 MIUI 对 Android 系统源码的破坏导致灰度图标无法判断

This commit is contained in:
2022-02-19 23:46:03 +08:00
parent a4f7c95f84
commit b6920e3ca1
3 changed files with 132 additions and 39 deletions

View File

@@ -30,7 +30,7 @@ android {
buildTypes { buildTypes {
release { release {
minifyEnabled true minifyEnabled false
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }

View File

@@ -51,7 +51,6 @@ import com.highcapable.yukihookapi.hook.log.loggerD
import com.highcapable.yukihookapi.hook.log.loggerW import com.highcapable.yukihookapi.hook.log.loggerW
import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.type.android.ContextClass import com.highcapable.yukihookapi.hook.type.android.ContextClass
import com.highcapable.yukihookapi.hook.type.android.DrawableClass
import com.highcapable.yukihookapi.hook.type.android.ImageViewClass import com.highcapable.yukihookapi.hook.type.android.ImageViewClass
import com.highcapable.yukihookapi.hook.type.java.IntType import com.highcapable.yukihookapi.hook.type.java.IntType
import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy
@@ -76,9 +75,6 @@ class HookEntry : YukiHookXposedInitProxy {
private const val NotificationViewWrapperClass = private const val NotificationViewWrapperClass =
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationViewWrapper" "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationViewWrapper"
/** 原生存在的类 */
private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
/** 未确定是否只有旧版本存在的类 */ /** 未确定是否只有旧版本存在的类 */
private const val ExpandableNotificationRowClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.ExpandableNotificationRow" private const val ExpandableNotificationRowClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.ExpandableNotificationRow"
@@ -98,30 +94,11 @@ class HookEntry : YukiHookXposedInitProxy {
/** /**
* - 这个是修复彩色图标的关键核心代码判断 * - 这个是修复彩色图标的关键核心代码判断
* *
* 判断是否为灰度图标 - 反射执行系统方法 * 判断是否为灰度图标 - [isXmsf] 直接标记为非灰度图标防止 💩 MIUI 乱改
* @param context 实例
* @param drawable 要判断的图标 * @param drawable 要判断的图标
* @return [Boolean] * @return [Boolean]
*/ */
private fun PackageParam.isGrayscaleIcon(context: Context, drawable: Drawable) = safeOfFalse { private fun StatusBarNotification.isGrayscaleIcon(drawable: Drawable) = !isXmsf && BitmapCompatTool.isGrayscaleDrawable(drawable)
ContrastColorUtilClass.clazz.let {
it.method {
name = "isGrayscaleIcon"
param(DrawableClass)
}.get(it.method {
name = "getInstance"
param(ContextClass)
}.get().invoke(context)).invoke<Boolean>(drawable) ?: false
}
}
/**
* 获取当前通知栏的样式
* @return [Boolean]
*/
private fun PackageParam.isShowMiuiStyle() = safeOfFalse {
NotificationUtilClass.clazz.method { name = "showMiuiStyle" }.get().invoke() ?: false
}
/** /**
* 是否为新版本 MIUI 方案 * 是否为新版本 MIUI 方案
@@ -129,8 +106,15 @@ class HookEntry : YukiHookXposedInitProxy {
* 拥有状态栏图标颜色检查功能 * 拥有状态栏图标颜色检查功能
* @return [Boolean] * @return [Boolean]
*/ */
private fun PackageParam.hasIgnoreStatusBarIconColor() = safeOfFalse { private val PackageParam.hasIgnoreStatusBarIconColor
NotificationUtilClass.clazz.hasMethod(name = "ignoreStatusBarIconColor", ExpandedNotificationClass.clazz) get() = NotificationUtilClass.clazz.hasMethod(name = "ignoreStatusBarIconColor", ExpandedNotificationClass.clazz)
/**
* 获取当前通知栏的样式
* @return [Boolean]
*/
private fun PackageParam.isShowMiuiStyle() = safeOfFalse {
NotificationUtilClass.clazz.method { name = "showMiuiStyle" }.get().invoke() ?: false
} }
/** /**
@@ -222,10 +206,11 @@ class HookEntry : YukiHookXposedInitProxy {
if (iconDrawable == null) return@safeRun if (iconDrawable == null) return@safeRun
/** 如果没开启修复 APP 的彩色图标 */ /** 如果没开启修复 APP 的彩色图标 */
if (!prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) return@safeRun if (!prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) return@safeRun
/** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable)
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */ /** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.also { notifyInstance -> expandedNf?.also { notifyInstance ->
/** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = !notifyInstance.isGrayscaleIcon(iconDrawable)
/** 目标彩色通知 APP 图标 */ /** 目标彩色通知 APP 图标 */
var customIcon: Bitmap? = null var customIcon: Bitmap? = null
if (prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)) if (prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true))
@@ -243,12 +228,12 @@ class HookEntry : YukiHookXposedInitProxy {
} }
/** 打印日志 */ /** 打印日志 */
if (prefs.getBoolean(ENABLE_MODULE_LOG)) if (prefs.getBoolean(ENABLE_MODULE_LOG))
loggerD(msg = "hook Icon [${findAppName(notifyInstance)}][${notifyInstance.opPkgName}] custom [${customIcon != null}] grayscale [${!isNotGrayscaleIcon}]") loggerD(msg = "hook Icon [${findAppName(notifyInstance)}][${notifyInstance.opPkgName}] custom [${customIcon != null}] grayscale [${!isNotGrayscaleIcon}] xmsf [${notifyInstance.isXmsf}]")
when { when {
/** 处理自定义通知图标优化 */ /** 处理自定义通知图标优化 */
customIcon != null -> it(customIcon!!) customIcon != null -> it(customIcon!!)
/** 若不是灰度图标自动处理为圆角 */ /** 若不是灰度图标自动处理为圆角 */
isNotGrayscaleIcon -> it(expandedNf.compatNotifyIcon(context, iconDrawable).toBitmap().round(15.dp(context))) isNotGrayscaleIcon -> it(notifyInstance.compatNotifyIcon(context, iconDrawable).toBitmap().round(15.dp(context)))
} }
} }
} }
@@ -295,7 +280,7 @@ class HookEntry : YukiHookXposedInitProxy {
val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context) val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
/** 判断图标风格 */ /** 判断图标风格 */
val isGrayscaleIcon = isGrayscaleIcon(context, iconDrawable) val isGrayscaleIcon = notifyInstance.isGrayscaleIcon(iconDrawable)
/** 自定义默认小图标 */ /** 自定义默认小图标 */
var customIcon: Bitmap? = null var customIcon: Bitmap? = null
@@ -340,7 +325,7 @@ class HookEntry : YukiHookXposedInitProxy {
background = DrawableBuilder().rounded().solidColor(iconColor).build() background = DrawableBuilder().rounded().solidColor(iconColor).build()
} else iconImageView.apply { } else iconImageView.apply {
/** 重新设置图标 */ /** 重新设置图标 */
setImageDrawable(expandedNf.compatNotifyIcon(context, iconDrawable)) setImageDrawable(notifyInstance.compatNotifyIcon(context, iconDrawable))
/** 设置裁切到边界 */ /** 设置裁切到边界 */
clipToOutline = true clipToOutline = true
/** 设置一个圆角轮廓裁切 */ /** 设置一个圆角轮廓裁切 */
@@ -378,7 +363,7 @@ class HookEntry : YukiHookXposedInitProxy {
notifyInstance.notification.smallIcon.loadDrawable(context) notifyInstance.notification.smallIcon.loadDrawable(context)
/** 判断是否不是灰度图标 */ /** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable) val isNotGrayscaleIcon = !notifyInstance.isGrayscaleIcon(iconDrawable)
/** 获取目标修复彩色图标的 APP */ /** 获取目标修复彩色图标的 APP */
var isTargetFixApp = false var isTargetFixApp = false
@@ -431,9 +416,9 @@ class HookEntry : YukiHookXposedInitProxy {
* 因为之前的 MIUI 版本的状态栏图标颜色会全部设置为白色的 - 找不到修复的地方就直接判断版本了 * 因为之前的 MIUI 版本的状态栏图标颜色会全部设置为白色的 - 找不到修复的地方就直接判断版本了
* 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook * 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook
*/ */
replaceAny { if (hasIgnoreStatusBarIconColor()) false else isShowMiuiStyle() } replaceAny { if (hasIgnoreStatusBarIconColor) false else isShowMiuiStyle() }
} }
if (hasIgnoreStatusBarIconColor()) if (hasIgnoreStatusBarIconColor)
injectMember { injectMember {
method { method {
name = "ignoreStatusBarIconColor" name = "ignoreStatusBarIconColor"
@@ -464,7 +449,7 @@ class HookEntry : YukiHookXposedInitProxy {
} }
afterHook { afterHook {
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */ /** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
if (hasIgnoreStatusBarIconColor() || !isShowMiuiStyle()) if (hasIgnoreStatusBarIconColor || !isShowMiuiStyle())
(globalContext ?: args[0] as Context).also { context -> (globalContext ?: args[0] as Context).also { context ->
hookSmallIconOnSet( hookSmallIconOnSet(
context = context, context = context,
@@ -522,7 +507,7 @@ class HookEntry : YukiHookXposedInitProxy {
method { name = "handleHeaderViews" } method { name = "handleHeaderViews" }
afterHook { afterHook {
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */ /** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
if (!hasIgnoreStatusBarIconColor() && isShowMiuiStyle()) return@afterHook if (!hasIgnoreStatusBarIconColor && isShowMiuiStyle()) return@afterHook
/** 获取小图标 */ /** 获取小图标 */
val iconImageView = val iconImageView =

View File

@@ -0,0 +1,108 @@
/*
* MIUINativeNotifyIcon - Fix the native notification bar icon function abandoned by the MIUI development team.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/fankes/MIUINativeNotifyIcon
*
* 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 and our eula as published
* by ferredoxin.
*
* 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/2/19.
*/
package com.fankes.miui.notify.utils
import android.graphics.*
import android.graphics.drawable.Drawable
import android.util.ArrayMap
import androidx.core.graphics.drawable.toBitmap
import kotlin.math.abs
/**
* 这是一个从 AOSP 源码中分离出来的功能
*
* 主要作用于兼容部分 MIUI 魔改颜色判断代码造成判断位图灰度功能失效
*/
object BitmapCompatTool {
/** 缓存已判断的结果防止卡顿 */
private var cachedBitmapGrayscales = ArrayMap<Int, Boolean>()
private var tempBuffer = intArrayOf(0)
private var tempCompactBitmap: Bitmap? = null
private var tempCompactBitmapCanvas: Canvas? = null
private var tempCompactBitmapPaint: Paint? = null
private val tempMatrix = Matrix()
/**
* 判断 [Drawable] 是否为灰度位图
* @param drawable 要判断的 [Drawable]
* @return [Boolean] 是否灰度
*/
fun isGrayscaleDrawable(drawable: Drawable) = safeOfFalse { isGrayscale(drawable.toBitmap()) }
/**
* 判断 [Bitmap] 是否为灰度位图
* @param bitmap 要判断的位图
* @return [Boolean] 是否灰度
*/
private fun isGrayscale(bitmap: Bitmap) =
cachedBitmapGrayscales[bitmap.generationId] ?: let {
var height = bitmap.height
var width = bitmap.width
if (height > 64 || width > 64) {
if (tempCompactBitmap == null) {
tempCompactBitmap = Bitmap.createBitmap(64, 64, Bitmap.Config.ARGB_8888)
.also { tempCompactBitmapCanvas = Canvas(it) }
tempCompactBitmapPaint = Paint(Paint.FILTER_BITMAP_FLAG).apply { isFilterBitmap = true }
}
tempMatrix.reset()
tempMatrix.setScale(64f / width, 64f / height, 0f, 0f)
tempCompactBitmapCanvas?.drawColor(0, PorterDuff.Mode.SRC)
tempCompactBitmapCanvas?.drawBitmap(bitmap, tempMatrix, tempCompactBitmapPaint)
height = 64
width = 64
}
val size = height * width
ensureBufferSize(size)
tempCompactBitmap?.getPixels(tempBuffer, 0, width, 0, 0, width, height)
for (i in 0 until size)
if (!isGrayscale(tempBuffer[i])) {
cachedBitmapGrayscales[bitmap.generationId] = false
return@let false
}
cachedBitmapGrayscales[bitmap.generationId] = true
true
}
/**
* 提纯 [Bitmap] 颜色判断灰度
* @param color 颜色
* @return [Boolean] 是否灰度
*/
private fun isGrayscale(color: Int): Boolean {
if (color shr 24 and 255 < 50) return true
val r = color shr 16 and 255
val g = color shr 8 and 255
val b = color and 255
return !(abs(r - g) >= 20 || abs(r - b) >= 20 || abs(g - b) >= 20)
}
/**
* 计算字节数组
* @param size 大小
*/
private fun ensureBufferSize(size: Int) {
if (tempBuffer.size < size) tempBuffer = IntArray(size)
}
}