From b6920e3ca188eb1f606c826d7f452fbf9e501c9b Mon Sep 17 00:00:00 2001 From: Fankesyooni Date: Sat, 19 Feb 2022 23:46:03 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=8E=20AOSP=20=E6=BA=90=E7=A0=81=E6=8F=90?= =?UTF-8?q?=E5=8F=96=E5=B7=A5=E5=85=B7=E7=B1=BB=E4=BF=AE=E5=A4=8D=20?= =?UTF-8?q?=F0=9F=92=A9=20MIUI=20=E5=AF=B9=20Android=20=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E6=BA=90=E7=A0=81=E7=9A=84=E7=A0=B4=E5=9D=8F=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=81=B0=E5=BA=A6=E5=9B=BE=E6=A0=87=E6=97=A0=E6=B3=95=E5=88=A4?= =?UTF-8?q?=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- .../com/fankes/miui/notify/hook/HookEntry.kt | 61 ++++------ .../miui/notify/utils/BitmapCompatTool.kt | 108 ++++++++++++++++++ 3 files changed, 132 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/com/fankes/miui/notify/utils/BitmapCompatTool.kt diff --git a/app/build.gradle b/app/build.gradle index c42fff7..d9c61df 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,7 +30,7 @@ android { buildTypes { release { - minifyEnabled true + minifyEnabled false signingConfig signingConfigs.debug proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } diff --git a/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt b/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt index c198aaf..f4990c5 100644 --- a/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt +++ b/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt @@ -51,7 +51,6 @@ import com.highcapable.yukihookapi.hook.log.loggerD import com.highcapable.yukihookapi.hook.log.loggerW import com.highcapable.yukihookapi.hook.param.PackageParam 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.java.IntType import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy @@ -76,9 +75,6 @@ class HookEntry : YukiHookXposedInitProxy { private const val NotificationViewWrapperClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationViewWrapper" - /** 原生存在的类 */ - private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil" - /** 未确定是否只有旧版本存在的类 */ private const val ExpandableNotificationRowClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.ExpandableNotificationRow" @@ -98,30 +94,11 @@ class HookEntry : YukiHookXposedInitProxy { /** * - 这个是修复彩色图标的关键核心代码判断 * - * 判断是否为灰度图标 - 反射执行系统方法 - * @param context 实例 + * 判断是否为灰度图标 - [isXmsf] 直接标记为非灰度图标防止 💩 MIUI 乱改 * @param drawable 要判断的图标 * @return [Boolean] */ - private fun PackageParam.isGrayscaleIcon(context: Context, drawable: Drawable) = safeOfFalse { - ContrastColorUtilClass.clazz.let { - it.method { - name = "isGrayscaleIcon" - param(DrawableClass) - }.get(it.method { - name = "getInstance" - param(ContextClass) - }.get().invoke(context)).invoke(drawable) ?: false - } - } - - /** - * 获取当前通知栏的样式 - * @return [Boolean] - */ - private fun PackageParam.isShowMiuiStyle() = safeOfFalse { - NotificationUtilClass.clazz.method { name = "showMiuiStyle" }.get().invoke() ?: false - } + private fun StatusBarNotification.isGrayscaleIcon(drawable: Drawable) = !isXmsf && BitmapCompatTool.isGrayscaleDrawable(drawable) /** * 是否为新版本 MIUI 方案 @@ -129,8 +106,15 @@ class HookEntry : YukiHookXposedInitProxy { * 拥有状态栏图标颜色检查功能 * @return [Boolean] */ - private fun PackageParam.hasIgnoreStatusBarIconColor() = safeOfFalse { - NotificationUtilClass.clazz.hasMethod(name = "ignoreStatusBarIconColor", ExpandedNotificationClass.clazz) + private val PackageParam.hasIgnoreStatusBarIconColor + 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 /** 如果没开启修复 APP 的彩色图标 */ if (!prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) return@safeRun - /** 判断是否不是灰度图标 */ - val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable) /** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */ expandedNf?.also { notifyInstance -> + /** 判断是否不是灰度图标 */ + val isNotGrayscaleIcon = !notifyInstance.isGrayscaleIcon(iconDrawable) + /** 目标彩色通知 APP 图标 */ var customIcon: Bitmap? = null if (prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)) @@ -243,12 +228,12 @@ class HookEntry : YukiHookXposedInitProxy { } /** 打印日志 */ 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 { /** 处理自定义通知图标优化 */ 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 isGrayscaleIcon = isGrayscaleIcon(context, iconDrawable) + val isGrayscaleIcon = notifyInstance.isGrayscaleIcon(iconDrawable) /** 自定义默认小图标 */ var customIcon: Bitmap? = null @@ -340,7 +325,7 @@ class HookEntry : YukiHookXposedInitProxy { background = DrawableBuilder().rounded().solidColor(iconColor).build() } else iconImageView.apply { /** 重新设置图标 */ - setImageDrawable(expandedNf.compatNotifyIcon(context, iconDrawable)) + setImageDrawable(notifyInstance.compatNotifyIcon(context, iconDrawable)) /** 设置裁切到边界 */ clipToOutline = true /** 设置一个圆角轮廓裁切 */ @@ -378,7 +363,7 @@ class HookEntry : YukiHookXposedInitProxy { notifyInstance.notification.smallIcon.loadDrawable(context) /** 判断是否不是灰度图标 */ - val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable) + val isNotGrayscaleIcon = !notifyInstance.isGrayscaleIcon(iconDrawable) /** 获取目标修复彩色图标的 APP */ var isTargetFixApp = false @@ -431,9 +416,9 @@ class HookEntry : YukiHookXposedInitProxy { * 因为之前的 MIUI 版本的状态栏图标颜色会全部设置为白色的 - 找不到修复的地方就直接判断版本了 * 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */ - replaceAny { if (hasIgnoreStatusBarIconColor()) false else isShowMiuiStyle() } + replaceAny { if (hasIgnoreStatusBarIconColor) false else isShowMiuiStyle() } } - if (hasIgnoreStatusBarIconColor()) + if (hasIgnoreStatusBarIconColor) injectMember { method { name = "ignoreStatusBarIconColor" @@ -464,7 +449,7 @@ class HookEntry : YukiHookXposedInitProxy { } afterHook { /** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */ - if (hasIgnoreStatusBarIconColor() || !isShowMiuiStyle()) + if (hasIgnoreStatusBarIconColor || !isShowMiuiStyle()) (globalContext ?: args[0] as Context).also { context -> hookSmallIconOnSet( context = context, @@ -522,7 +507,7 @@ class HookEntry : YukiHookXposedInitProxy { method { name = "handleHeaderViews" } afterHook { /** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */ - if (!hasIgnoreStatusBarIconColor() && isShowMiuiStyle()) return@afterHook + if (!hasIgnoreStatusBarIconColor && isShowMiuiStyle()) return@afterHook /** 获取小图标 */ val iconImageView = diff --git a/app/src/main/java/com/fankes/miui/notify/utils/BitmapCompatTool.kt b/app/src/main/java/com/fankes/miui/notify/utils/BitmapCompatTool.kt new file mode 100644 index 0000000..db3d5fd --- /dev/null +++ b/app/src/main/java/com/fankes/miui/notify/utils/BitmapCompatTool.kt @@ -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 + * + * + * 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() + + 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) + } +} \ No newline at end of file