From 53e5292f2e72b37e03e4dd6b33cf85b0ed5ea16f Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Sat, 28 Jan 2023 01:53:55 +0800 Subject: [PATCH] Added notify icon color compat functions and fix fatal error when some bitmap calculations are abnormal --- .../fankes/coloros/notify/data/DataConst.kt | 1 + .../notify/hook/entity/SystemUIHooker.kt | 26 ++-- .../notify/ui/activity/MainActivity.kt | 18 +++ .../notify/utils/tool/BitmapCompatTool.kt | 119 ++++++++++++++++++ app/src/main/res/drawable/ic_modify.xml | 36 ++++++ app/src/main/res/layout/activity_main.xml | 65 ++++++++++ 6 files changed, 253 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/com/fankes/coloros/notify/utils/tool/BitmapCompatTool.kt create mode 100644 app/src/main/res/drawable/ic_modify.xml diff --git a/app/src/main/java/com/fankes/coloros/notify/data/DataConst.kt b/app/src/main/java/com/fankes/coloros/notify/data/DataConst.kt index 6204b1f..7350346 100644 --- a/app/src/main/java/com/fankes/coloros/notify/data/DataConst.kt +++ b/app/src/main/java/com/fankes/coloros/notify/data/DataConst.kt @@ -30,6 +30,7 @@ object DataConst { val ENABLE_MODULE = PrefsData("_enable_module", true) val ENABLE_MODULE_LOG = PrefsData("_enable_module_log", false) + val ENABLE_COLOR_ICON_COMPAT = PrefsData("_color_icon_compat", false) val ENABLE_ANDROID12_STYLE = PrefsData("_notify_android12_style", isUpperOfAndroidS) val ENABLE_NOTIFY_ICON_FIX = PrefsData("_notify_icon_fix", true) val ENABLE_NOTIFY_ICON_FORCE_APP_ICON = PrefsData("_notify_icon_force_app_icon", false) 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 deb9d87..e4ed8bf 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 @@ -34,7 +34,6 @@ import android.graphics.Outline import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.Icon -import android.graphics.drawable.VectorDrawable import android.os.SystemClock import android.service.notification.StatusBarNotification import android.util.ArrayMap @@ -52,6 +51,7 @@ import com.fankes.coloros.notify.hook.factory.isAppNotifyHookAllOf import com.fankes.coloros.notify.hook.factory.isAppNotifyHookOf import com.fankes.coloros.notify.param.IconPackParams import com.fankes.coloros.notify.utils.factory.* +import com.fankes.coloros.notify.utils.tool.BitmapCompatTool import com.fankes.coloros.notify.utils.tool.IconAdaptationTool import com.fankes.coloros.notify.utils.tool.SystemUITool import com.highcapable.yukihookapi.hook.bean.VariousClass @@ -329,16 +329,18 @@ object SystemUIHooker : YukiBaseHooker() { * @param drawable 要判断的图标 * @return [Boolean] */ - private fun isGrayscaleIcon(context: Context?, drawable: Drawable?) = - ContrastColorUtilClass.toClassOrNull()?.let { - drawable is VectorDrawable || it.method { - name = "isGrayscaleIcon" - param(DrawableClass) - }.get(it.method { - name = "getInstance" - param(ContextClass) - }.get().invoke(context)).boolean(drawable) - } ?: false + private fun isGrayscaleIcon(context: Context, drawable: Drawable) = + if (prefs.get(DataConst.ENABLE_COLOR_ICON_COMPAT).not()) safeOfFalse { + ContrastColorUtilClass.toClass().let { + it.method { + name = "isGrayscaleIcon" + param(DrawableClass) + }.get(it.method { + name = "getInstance" + param(ContextClass) + }.get().invoke(context)).boolean(drawable) + } + } else BitmapCompatTool.isGrayscaleDrawable(drawable) /** * 适配通知栏、状态栏来自系统推送的彩色 APP 图标 @@ -624,7 +626,7 @@ object SystemUIHooker : YukiBaseHooker() { name = "isGrayscaleOplus" param(ImageViewClass, OplusContrastColorUtilClass) } - replaceAny { args().first().cast()?.let { isGrayscaleIcon(it.context, it.drawable) } } + replaceAny { args().first().cast()?.let { isGrayscaleIcon(it.context, it.drawable) } ?: callOriginal() } }.ignoredHookingFailure() } /** 替换状态栏图标 */ diff --git a/app/src/main/java/com/fankes/coloros/notify/ui/activity/MainActivity.kt b/app/src/main/java/com/fankes/coloros/notify/ui/activity/MainActivity.kt index 4dd376d..19ec8bb 100644 --- a/app/src/main/java/com/fankes/coloros/notify/ui/activity/MainActivity.kt +++ b/app/src/main/java/com/fankes/coloros/notify/ui/activity/MainActivity.kt @@ -141,6 +141,7 @@ class MainActivity : BaseActivity() { binding.notifyMediaPanelAutoExpSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP) binding.moduleEnableSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_MODULE) binding.moduleEnableLogSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_MODULE_LOG) + binding.colorIconCompatSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_COLOR_ICON_COMPAT) binding.notifyIconFixSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX) binding.notifyIconForceAppIconSwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FORCE_APP_ICON) binding.notifyIconFixNotifySwitch.isChecked = modulePrefs.get(DataConst.ENABLE_NOTIFY_ICON_FIX_NOTIFY) @@ -170,6 +171,23 @@ class MainActivity : BaseActivity() { modulePrefs.put(DataConst.ENABLE_MODULE_LOG, b) SystemUITool.showNeedRestartSnake(context = this) } + binding.colorIconCompatSwitch.setOnCheckedChangeListener { btn, b -> + if (btn.isPressed.not()) return@setOnCheckedChangeListener + /** 保存当前配置并生效 */ + fun saveConfigs() { + modulePrefs.put(DataConst.ENABLE_COLOR_ICON_COMPAT, b) + SystemUITool.refreshSystemUI(context = this) + } + if (b) showDialog { + title = "启用兼容模式" + msg = "启用兼容模式可修复部分系统版本可能出现无法判定通知图标反色的问题," + + "但是这也可能会导致新的问题,一般情况下不建议开启,确定要继续吗?\n\n" + + "如果系统界面刷新后通知图标颜色发生错误,请尝试重启一次系统界面。" + confirmButton { saveConfigs() } + cancelButton { btn.isChecked = false } + noCancelable() + } else saveConfigs() + } binding.notifyIconFixSwitch.setOnCheckedChangeListener { btn, b -> if (btn.isPressed.not()) return@setOnCheckedChangeListener modulePrefs.put(DataConst.ENABLE_NOTIFY_ICON_FIX, b) diff --git a/app/src/main/java/com/fankes/coloros/notify/utils/tool/BitmapCompatTool.kt b/app/src/main/java/com/fankes/coloros/notify/utils/tool/BitmapCompatTool.kt new file mode 100644 index 0000000..ac29001 --- /dev/null +++ b/app/src/main/java/com/fankes/coloros/notify/utils/tool/BitmapCompatTool.kt @@ -0,0 +1,119 @@ +/* + * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. + * Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com) + * https://github.com/fankes/ColorOSNotifyIcon + * + * 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. + *

+ * + * 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 2023/1/28. + */ +package com.fankes.coloros.notify.utils.tool + +import android.graphics.* +import android.graphics.drawable.AnimationDrawable +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.VectorDrawable +import android.util.ArrayMap +import androidx.core.graphics.drawable.toBitmap +import com.fankes.coloros.notify.utils.factory.safeOfFalse +import kotlin.math.abs + +/** + * 这是一个从 AOSP 源码中分离出来的功能 + * + * 主要作用于兼容部分第三方系统修改颜色判断代码造成判断位图灰度功能失效 + */ +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 { + when (drawable) { + is BitmapDrawable -> isGrayscaleBitmap(drawable.bitmap) + is AnimationDrawable -> !(drawable.numberOfFrames <= 0 || !isGrayscaleBitmap(drawable.getFrame(0).toBitmap())) + is VectorDrawable -> true + else -> isGrayscaleBitmap(drawable.toBitmap()) + } + } + + /** + * 判断 [Bitmap] 是否为灰度位图 + * @param bitmap 要判断的位图 + * @return [Boolean] 是否灰度 + */ + private fun isGrayscaleBitmap(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 (isGrayscaleColor(tempBuffer[i]).not()) { + cachedBitmapGrayscales[bitmap.generationId] = false + return@let false + } + cachedBitmapGrayscales[bitmap.generationId] = true + true + } + + /** + * 提纯 [Bitmap] 颜色判断灰度 + * @param color 颜色 + * @return [Boolean] 是否灰度 + */ + private fun isGrayscaleColor(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 diff --git a/app/src/main/res/drawable/ic_modify.xml b/app/src/main/res/drawable/ic_modify.xml new file mode 100644 index 0000000..5469ce8 --- /dev/null +++ b/app/src/main/res/drawable/ic_modify.xml @@ -0,0 +1,36 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 0eb0c10..eb03471 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -380,6 +380,71 @@ android:textSize="12sp" /> + + + + + + + + + + + + + + + + +