Added notify icon color compat functions and fix fatal error when some bitmap calculations are abnormal

This commit is contained in:
2023-01-28 01:53:55 +08:00
parent 1b4be321ec
commit 53e5292f2e
6 changed files with 253 additions and 12 deletions

View File

@@ -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)

View File

@@ -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<ImageView>()?.let { isGrayscaleIcon(it.context, it.drawable) } }
replaceAny { args().first().cast<ImageView>()?.let { isGrayscaleIcon(it.context, it.drawable) } ?: callOriginal() }
}.ignoredHookingFailure()
}
/** 替换状态栏图标 */

View File

@@ -141,6 +141,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
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<ActivityMainBinding>() {
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)

View File

@@ -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.
* <p>
*
* 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 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<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 {
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)
}
}

View File

@@ -0,0 +1,36 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#00000000"
android:pathData="M20.071,9.586L15.828,5.343C15.047,4.562 13.781,4.562 13,5.343L7.343,11C6.562,11.781 6.562,13.047 7.343,13.828L11.586,18.071"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M28.929,37.414L33.171,41.657C33.952,42.438 35.219,42.438 36,41.657L41.657,36C42.438,35.219 42.438,33.953 41.657,33.172L37.414,28.929"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M36.021,6.322L41.677,11.979A2,2 0,0 1,41.677 14.808L14.807,41.678A2,2 0,0 1,11.979 41.678L6.322,36.021A2,2 132.403,0 1,6.322 33.192L33.192,6.322A2,2 102.008,0 1,36.021 6.322z"
android:strokeWidth="4"
android:strokeColor="#ffffff"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#ffffff"
android:pathData="M24,24m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
<path
android:fillColor="#ffffff"
android:pathData="M20,28m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
<path
android:fillColor="#ffffff"
android:pathData="M28,20m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
</vector>

View File

@@ -380,6 +380,71 @@
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/color_icon_hook_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:layout_marginRight="15dp"
android:background="@drawable/bg_permotion_round"
android:elevation="0dp"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="15dp"
android:paddingRight="15dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center|start">
<androidx.cardview.widget.CardView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="10dp"
app:cardBackgroundColor="#66BB6A"
app:cardCornerRadius="50dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:padding="2.5dp"
android:src="@drawable/ic_modify" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:text="图标调整"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
</LinearLayout>
<com.fankes.coloros.notify.ui.widget.MaterialSwitch
android:id="@+id/color_icon_compat_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="启用兼容模式"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="如果发现通知图标颜色判定不正常可启用兼容模式,一般情况下不建议启用兼容模式,发生问题请关闭兼容模式。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/a12_style_config_item"
android:layout_width="match_parent"