44 Commits
1.6 ... 1.8

Author SHA1 Message Date
07fa6965ad Update version to 1.8,fix more bugs. 2022-02-25 03:08:16 +08:00
4c88a5fbc6 Update version to 1.8,fix more bugs. 2022-02-25 03:07:32 +08:00
da61a52c13 将通知优化图标迁移至云端 2022-02-25 03:05:21 +08:00
3314f9fb57 bulit in online config test 2022-02-24 23:47:23 +08:00
ebb1bec36f 适配“主题壁纸”通知优化图标 2022-02-24 23:36:48 +08:00
Fankesyooni
d5d2e2107c Merge pull request #35 from sddpljx/master
修复 小米钱包 包名
2022-02-24 22:35:44 +08:00
sddpljx
c39d03f8c9 修复 小米钱包 包名 2022-02-24 22:23:32 +08:00
Fankesyooni
e20dfab5b1 Merge pull request #34 from sddpljx/master
适配 铁路12306 中国移动 中国联通 权限管理服务 小米智能卡 小米钱包 阿里巴巴 图标
2022-02-24 22:09:14 +08:00
sddpljx
ffc9be3721 修复 小米智能卡图标颜色 2022-02-24 21:06:20 +08:00
sddpljx
126aedabfd 增加 铁路12306 中国移动 中国联通 权限管理服务 小米智能卡 小米钱包 阿里巴巴 图标 2022-02-24 21:03:38 +08:00
56661b03e6 Update README.md 2022-02-20 16:14:36 +08:00
Fankesyooni
c45658be68 Update issue templates 2022-02-20 16:13:13 +08:00
e0443ff97c Update README.md 2022-02-20 14:13:01 +08:00
9af735e252 Update README.md 2022-02-20 14:01:45 +08:00
c67fe2fa31 Update version to 1.71,fix more bugs 2022-02-20 13:57:46 +08:00
b03cf30e0a 取消圆角彩色图标优化 2022-02-20 13:52:40 +08:00
09191802ad Update version to 1.7,fix more bugs 2022-02-20 12:42:45 +08:00
64f252986b 增加通知图标兼容模式,修改回之前的方案 2022-02-20 12:36:13 +08:00
c073d350d0 修改文案 2022-02-20 12:14:17 +08:00
cbeaaa8733 优化代码 2022-02-20 12:04:52 +08:00
3b5e066e2d Merge code 2022-02-20 03:44:34 +08:00
79843eb0d0 修复一个彩色图标判断失误的问题,重新发布版本 1.67 2022-02-20 01:22:56 +08:00
7a4e254230 Update version to 1.66,fix more bugs 2022-02-20 00:24:07 +08:00
0534ab9218 新增“QQ浏览器”、“QQ阅读”通知优化图标 2022-02-20 00:20:19 +08:00
5cee229a99 优化日志打印文本 2022-02-19 23:48:32 +08:00
b6920e3ca1 从 AOSP 源码提取工具类修复 💩 MIUI 对 Android 系统源码的破坏导致灰度图标无法判断 2022-02-19 23:46:03 +08:00
Fankesyooni
a4f7c95f84 Update issue templates 2022-02-19 22:26:16 +08:00
38803846cd Merge code 2022-02-19 03:12:04 +08:00
fb44c4e945 Update version to 1.65,fix more bugs 2022-02-18 23:24:04 +08:00
a2fc95d765 Update version to 1.65,fix more bugs 2022-02-18 23:22:53 +08:00
0e3ef5418c 优化“哔哩哔哩漫画”通知优化图标、增加“讯飞输入法”、“讯飞输入法小米版”通知优化图标 2022-02-18 23:20:30 +08:00
4e5e2cee09 优化代码 2022-02-18 04:10:17 +08:00
7b4b728523 移除不必要的 Hook,修复 MIUI 12 下依然卡顿的问题 2022-02-18 04:04:04 +08:00
9b6540df65 更新 YukiHookAPI 版本 2022-02-18 03:58:22 +08:00
3e8982aad0 Update IconPackParams.kt 2022-02-17 21:59:32 +08:00
085f2498c8 Update IconPackParams.kt 2022-02-17 21:58:09 +08:00
a1e01d1d33 Merge to pending 2022-02-17 21:54:18 +08:00
03aa550487 Update IconPackParams.kt 2022-02-17 21:52:22 +08:00
ea632b8892 Merge code 2022-02-17 21:51:10 +08:00
Fankesyooni
69847a0b0b Merge pull request #29 from sddpljx/master
优化 Jump 图标,增加 斗鱼 图标
2022-02-17 21:44:57 +08:00
sddpljx
4cdbfa944d 优化Jump图标,增加斗鱼图标 2022-02-17 16:21:29 +08:00
Fankesyooni
9edb04cf08 Merge pull request #28 from naicfeng/master
优化 工商银行 网易邮箱大师 小米音乐 图标
2022-02-17 14:39:19 +08:00
naicfeng
071c98e8f3 优化 工商银行 网易邮箱大师 小米音乐 图标 2022-02-17 09:18:47 +08:00
Fankesyooni
a317cc07f5 Update issue templates 2022-02-17 05:16:52 +08:00
43 changed files with 1773 additions and 3548 deletions

View File

@@ -9,7 +9,7 @@ assignees: ''
**需要适配的 APP 名称/包名/通知图标颜色 (必填)**
* (示例:小米音乐/com.miui.player/0xfff16033)
* (示例:小米音乐/com.miui.player/#fff16033)
*
@@ -23,6 +23,7 @@ assignees: ''
* (若直接在附件提交这里可不填)
<!--- 提交时请将示例内容全部删除 --->
<!--- 提交时请将括号内容包括括号全部删除,填入你自己的内容 --->
<!--- 请保留模板原始标题 --->
<!--- 不按规定提交的 issues 将直接被关闭 --->
<!--- Create by Template --->

View File

@@ -25,7 +25,7 @@ assignees: fankes
**使用的 Xposed 框架名称与框架版本(必填)**
*
* (例如LSPosed Zygisk/Riru 版本号)
**同时使用的带有系统界面作用域的 Xposed 模块(选填)**
@@ -46,6 +46,9 @@ assignees: fankes
</code></pre></details>
<!--- 提交时请将示例内容全部删除 --->
<!--- 注意:只接受 MIUI 正规官方版本系统,如果你正在使用 MIUI 官改(第三方改版)请不要提交任何 BUG 与问题,开发者无义务解决 --->
<!--- 提交时请将括号内容包括括号全部删除,填入你自己的内容 --->
<!--- 请保留模板原始标题 --->
<!--- 不按规定提交的 issues 将直接被关闭 --->
<!--- Create by Template --->

2
.idea/misc.xml generated
View File

@@ -10,7 +10,7 @@
<entry key="app/src/main/res/drawable/white_round.xml" value="0.256" />
<entry key="app/src/main/res/layout/activity_config.xml" value="0.42168674698795183" />
<entry key="app/src/main/res/layout/activity_login.xml" value="0.4375" />
<entry key="app/src/main/res/layout/activity_main.xml" value="0.37516748548459133" />
<entry key="app/src/main/res/layout/activity_main.xml" value="0.36160137752905724" />
<entry key="app/src/main/res/layout/adapter_config.xml" value="0.4375" />
<entry key="app/src/main/res/layout/dia_icon_search.xml" value="0.4307692307692308" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.44871794871794873" />

View File

@@ -13,27 +13,33 @@
## 贡献方法
- 在下方的类中添加新的 APP 通知图标适配条目
- [IconPackParams.kt](https://github.com/fankes/MIUINativeNotifyIcon/blob/master/app/src/main/java/com/fankes/miui/notify/params/IconPackParams.kt)
- 2022.02.25 适配数据已变更为云端更新
- 在下方的 JSON 文件中添加新的 APP 通知图标适配条目
- [NotifyIconsSupportConfig.json](https://github.com/fankes/MIUINativeNotifyIcon/blob/master/iconPack/NotifyIconsSupportConfig.json)
- 使用灰度位图转 Base64 来得到 Base64 的位图数据字符串
- [BitmapToBase64](https://github.com/fankes/BitmapToBase64)
- 新增条目的模板如下所示
```kotlin
IconDataBean(
isEnabled = true, // 是否默认启用替换彩色图标 - 关闭后将全部停止替换
isEnabledAll = false, // 是否默认启用替换全部图标
appName = "", // APP 名称
packageName = "", // APP 包名
iconBitmap = ("").bitmap, // 位图数据 Base64
iconColor = 0, // 通知栏中显示的图标颜色 - 设置为 0 使用系统默认颜色 (不设置颜色可不写)
contributorName = "" // 贡献者昵称
)
```json
{
"appName": "", // APP 名称
"packageName": "", // APP 包名
"isEnabled": true, // 是否默认启用替换彩色图标 - 关闭后将全部停止替换
"isEnabledAll": false, // 是否默认启用替换全部图标
"iconBitmap": "", // 位图数据 Base64 字符串
"iconColor": "#ff232323", // 通知栏中显示的图标颜色 - 设置使用系统默认颜色 (不设置颜色可删除此项)
"contributorName": "" // 贡献者昵称
}
```
- 图标大小建议保持在 50x50
- 提交时请将后方的注释删除,否则不予合并代码
## 同步列表地址
- Github 直连地址 [Raw](https://raw.githubusercontent.com/fankes/MIUINativeNotifyIcon/master/iconPack/NotifyIconsSupportConfig.json)
- 数据将在稍后同步至 [Surge](https://fankes.mnn.surge.sh/NotifyIconsSupportConfig.json)
## 其它要求
- 1.调试性质或大批量注释代码,禁止提交

View File

@@ -2,7 +2,7 @@
![Eclipse Marketplace](https://img.shields.io/badge/build-passing-brightgreen)
![Eclipse Marketplace](https://img.shields.io/badge/license-AGPL3.0-blue)
![Eclipse Marketplace](https://img.shields.io/badge/version-v1.6-green)
![Eclipse Marketplace](https://img.shields.io/badge/version-v1.71-green)
<br/><br/>
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/app/src/main/ic_launcher-playstore.png" width = "100" height = "100"/>
<br/>
@@ -12,12 +12,14 @@ Fix the native notification bar icon function abandoned by the MIUI development
# 开始使用
点击下载最新版本
<a href='https://github.com/fankes/MIUINativeNotifyIcon/releases'>![Eclipse Marketplace](https://img.shields.io/badge/download-v1.6-green)</a>
<a href='https://github.com/fankes/MIUINativeNotifyIcon/releases'>![Eclipse Marketplace](https://img.shields.io/badge/download-v1.71-green)</a>
<br/><br/>
⚠️ 适配说明<br/>
- 此模块仅支持 LSPosed(作用域“系统界面”)、~~EdXposed(随时停止支持)~~、不支持太极无极
- 请确保你使用的是 MIUI 官方版本,任何第三方官改包发生的问题,开发者没有义务去解决和修复,请自求多福
- 目前最低支持基于 Android 9 版本的 MIUI 12 或 MIUI 12.5(最低建议)
- 建议最低从 MIUI 12.5 `2021-5-18` 开发版以后开始使用模块,之前的版本可能或多或少存在 MIUI 自身 BUG 不生效、黑白块的问题
- 请始终保持最新版本的 LSPosed旧版本可能会出现 Hook 不生效的问题
# 请勿用于非法用途
@@ -34,6 +36,10 @@ Fix the native notification bar icon function abandoned by the MIUI development
- [CONTRIBUTING](https://github.com/fankes/MIUINativeNotifyIcon/blob/master/CONTRIBUTING.md)
# 通知测试
你可以 [点击这里](https://github.com/fankes/MIUINativeNotifyIcon/blob/master/tool/NotifyTester.apk) 下载工具测试通知图标是否生效。
# 历史背景
这个模块诞生来源于 MIUI 的乱改和不规范,本来 MIUI 9 之后,官方给出了原生通知图标样式,后面由于用户反应通知栏经常出现黑白块。<br/><br/>

View File

@@ -51,9 +51,13 @@ android {
tasks.whenTaskAdded {
task -> if (task.name == "lintVitalRelease") task.enabled = false
}
/** 移除无效耗时 lint Task */
tasks.whenTaskAdded {
task -> if (task.name == "lintVitalAnalyzeRelease") task.enabled = false
}
/** 移除无效耗时 lint Task */
tasks.whenTaskAdded {
task -> if (task.name == "lintVitalReportRelease") task.enabled = false
}
@@ -64,10 +68,11 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
compileOnly 'de.robv.android.xposed:api:82'
implementation 'com.highcapable.yukihookapi:api:1.0.1'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.1'
implementation 'com.highcapable.yukihookapi:api:1.0.2'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.2'
implementation 'com.geyifeng.immersionbar:immersionbar:3.2.0'
implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'

View File

@@ -3,6 +3,8 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.fankes.miui.notify">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".application.MNNApplication"
android:allowBackup="true"
@@ -18,7 +20,7 @@
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="MIUI 状态栏原生图标,修复 12.5、13 后期被破坏的彩色图标。\n开发者酷安 @星夜不荟" />
android:value="MIUI 原生通知图标,修复 12.5、13 后期被破坏的彩色图标。\n开发者酷安 @星夜不荟" />
<meta-data
android:name="xposedminversion"
android:value="93" />

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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
@@ -30,7 +30,9 @@ object HookConst {
const val ENABLE_MODULE_LOG = "_enable_module_log"
const val ENABLE_HIDE_ICON = "_hide_icon"
const val ENABLE_COLOR_ICON_HOOK = "_color_icon_hook"
const val ENABLE_COLOR_ICON_COMPAT = "_color_icon_compat"
const val ENABLE_NOTIFY_ICON_FIX = "_notify_icon_fix"
const val NOTIFY_ICON_DATAS = "_notify_icon_datas"
const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
}

View File

@@ -6,8 +6,8 @@
* 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.
* 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
@@ -34,6 +34,8 @@ import android.view.View
import android.view.ViewOutlineProvider
import android.widget.ImageView
import androidx.core.graphics.drawable.toBitmap
import com.fankes.miui.notify.bean.IconDataBean
import com.fankes.miui.notify.hook.HookConst.ENABLE_COLOR_ICON_COMPAT
import com.fankes.miui.notify.hook.HookConst.ENABLE_COLOR_ICON_HOOK
import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE
import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE_LOG
@@ -76,15 +78,12 @@ class HookEntry : YukiHookXposedInitProxy {
private const val NotificationViewWrapperClass =
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationViewWrapper"
/** 原生存在的类 */
private const val StatusBarIconViewClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.StatusBarIconView"
/** 未确定是否只有旧版本存在的类 */
private const val ExpandableNotificationRowClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.ExpandableNotificationRow"
/** 原生存在的类 */
private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
/** 未确定是否只有旧版本存在的类 */
private const val ExpandableNotificationRowClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.ExpandableNotificationRow"
/** 根据多个版本存在不同的包名相同的类 */
private val NotificationUtilClass = VariousClass(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationUtil",
@@ -98,6 +97,9 @@ class HookEntry : YukiHookXposedInitProxy {
)
}
/** 缓存的通知优化图标数组 */
private var iconDatas = ArrayList<IconDataBean>()
/**
* - 这个是修复彩色图标的关键核心代码判断
*
@@ -106,20 +108,18 @@ class HookEntry : YukiHookXposedInitProxy {
* @param drawable 要判断的图标
* @return [Boolean]
*/
private fun PackageParam.isGrayscaleIcon(context: Context, drawable: Drawable) = safeOfFalse {
ContrastColorUtilClass.clazz.let {
it.method(name = "isGrayscaleIcon", DrawableClass)
?.call<Boolean>(it.method(name = "getInstance", ContextClass)?.callStatic(context), drawable) ?: false
}
}
/**
* 获取当前通知栏的样式
* @return [Boolean]
*/
private fun PackageParam.isShowMiuiStyle() = safeOfFalse {
NotificationUtilClass.clazz.method(name = "showMiuiStyle")?.callStatic() ?: false
}
private fun PackageParam.isGrayscaleIcon(context: Context, drawable: Drawable) =
if (!prefs.getBoolean(ENABLE_COLOR_ICON_COMPAT)) safeOfFalse {
ContrastColorUtilClass.clazz.let {
it.method {
name = "isGrayscaleIcon"
param(DrawableClass)
}.get(it.method {
name = "getInstance"
param(ContextClass)
}.get().invoke(context)).invoke<Boolean>(drawable) ?: false
}
} else BitmapCompatTool.isGrayscaleDrawable(drawable)
/**
* 是否为新版本 MIUI 方案
@@ -127,9 +127,19 @@ class HookEntry : YukiHookXposedInitProxy {
* 拥有状态栏图标颜色检查功能
* @return [Boolean]
*/
private fun PackageParam.hasIgnoreStatusBarIconColor() = safeOfFalse {
NotificationUtilClass.clazz.hasMethod(name = "ignoreStatusBarIconColor", ExpandedNotificationClass.clazz)
}
private val PackageParam.hasIgnoreStatusBarIconColor
get() = safeOfFalse {
NotificationUtilClass.clazz.hasMethod(name = "ignoreStatusBarIconColor", ExpandedNotificationClass.clazz)
}
/**
* 获取当前通知栏的样式
* @return [Boolean]
*/
private val PackageParam.isShowMiuiStyle
get() = safeOfFalse {
NotificationUtilClass.clazz.method { name = "showMiuiStyle" }.get().invoke() ?: false
}
/**
* 获取 [ExpandedNotificationClass] 的应用名称
@@ -137,7 +147,7 @@ class HookEntry : YukiHookXposedInitProxy {
* @return [String]
*/
private fun PackageParam.findAppName(instance: Any?) = safeOf(default = "<unknown>") {
ExpandedNotificationClass.clazz.method(name = "getAppName")?.call(instance) ?: "<empty>"
ExpandedNotificationClass.clazz.method { name = "getAppName" }.get(instance).invoke() ?: "<empty>"
}
/**
@@ -197,7 +207,9 @@ class HookEntry : YukiHookXposedInitProxy {
*/
private val PackageParam.globalContext
get() = safeOfNull {
SystemUIApplicationClass.clazz.method(name = "getContext")?.callStatic<Context>()
if (SystemUIApplicationClass.clazz.hasMethod(name = "getContext"))
SystemUIApplicationClass.clazz.method { name = "getContext" }.get().invoke<Context>()
else null
}
/**
@@ -218,33 +230,35 @@ 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.isXmsf || !isGrayscaleIcon(context, iconDrawable)
/** 目标彩色通知 APP 图标 */
var customIcon: Bitmap? = null
if (prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true))
run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
isAppNotifyHookOf(it)
) {
if (isNotGrayscaleIcon || isAppNotifyHookAllOf(it))
customIcon = it.iconBitmap
return@run
if (iconDatas.isNotEmpty())
iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
isAppNotifyHookOf(it)
) {
if (isNotGrayscaleIcon || isAppNotifyHookAllOf(it))
customIcon = it.iconBitmap
return@run
}
}
}
}
/** 打印日志 */
if (prefs.getBoolean(ENABLE_MODULE_LOG))
loggerD(msg = "hook Icon [${findAppName(notifyInstance)}][${notifyInstance.opPkgName}] custom [${customIcon != null}] grayscale [${!isNotGrayscaleIcon}]")
loggerD(msg = "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())
}
}
}
@@ -291,7 +305,7 @@ class HookEntry : YukiHookXposedInitProxy {
val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
/** 判断图标风格 */
val isGrayscaleIcon = isGrayscaleIcon(context, iconDrawable)
val isGrayscaleIcon = !notifyInstance.isXmsf && isGrayscaleIcon(context, iconDrawable)
/** 自定义默认小图标 */
var customIcon: Bitmap? = null
@@ -300,18 +314,19 @@ class HookEntry : YukiHookXposedInitProxy {
var customIconColor = 0
if (isNotifyIconFix) run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
isAppNotifyHookOf(it)
) {
if (!isGrayscaleIcon || isAppNotifyHookAllOf(it)) {
customIcon = it.iconBitmap
customIconColor = it.iconColor
return@run
if (iconDatas.isNotEmpty())
iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
isAppNotifyHookOf(it)
) {
if (!isGrayscaleIcon || isAppNotifyHookAllOf(it)) {
customIcon = it.iconBitmap
customIconColor = it.iconColor
return@run
}
}
}
}
}
/** 处理自定义通知图标优化 */
if (customIcon != null)
@@ -336,7 +351,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
/** 设置一个圆角轮廓裁切 */
@@ -374,22 +389,23 @@ class HookEntry : YukiHookXposedInitProxy {
notifyInstance.notification.smallIcon.loadDrawable(context)
/** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable)
val isNotGrayscaleIcon = notifyInstance.isXmsf || !isGrayscaleIcon(context, iconDrawable)
/** 获取目标修复彩色图标的 APP */
var isTargetFixApp = false
/** 如果开启了自定义通知图标优化 */
if (prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true))
run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
isAppNotifyHookOf(it)
) {
if (isNotGrayscaleIcon || isAppNotifyHookAllOf(it)) isTargetFixApp = true
return@run
if (iconDatas.isNotEmpty())
iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
isAppNotifyHookOf(it)
) {
if (isNotGrayscaleIcon || isAppNotifyHookAllOf(it)) isTargetFixApp = true
return@run
}
}
}
}
/**
* 只要不是灰度就返回彩色图标
@@ -416,6 +432,9 @@ class HookEntry : YukiHookXposedInitProxy {
!prefs.getBoolean(ENABLE_MODULE, default = true) -> loggerW(msg = "Aborted Hook -> Hook Closed")
/** 开始 Hook */
else -> {
/** 缓存图标数据 */
iconDatas = IconPackParams(param = this).iconDatas
/** 执行 Hook */
NotificationUtilClass.hook {
/** 强制回写系统的状态栏图标样式为原生 */
injectMember {
@@ -427,9 +446,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"
@@ -460,7 +479,7 @@ class HookEntry : YukiHookXposedInitProxy {
}
afterHook {
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
if (hasIgnoreStatusBarIconColor() || !isShowMiuiStyle())
if (hasIgnoreStatusBarIconColor || !isShowMiuiStyle)
(globalContext ?: args[0] as Context).also { context ->
hookSmallIconOnSet(
context = context,
@@ -471,35 +490,8 @@ class HookEntry : YukiHookXposedInitProxy {
}
}
}
findClass(StatusBarIconViewClass).hook {
/** 修复通知图标为彩色 - MIPUSH 修复 */
injectMember {
method { name = "updateIconColor" }
afterHook {
/** 获取自身 */
val iconImageView = instance<ImageView?>() ?: return@afterHook
/** 获取通知实例 */
val expandedNf = field { name = "mNotification" }.of<StatusBarNotification>(instance)
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
if (!hasIgnoreStatusBarIconColor() && isShowMiuiStyle()) return@afterHook
/**
* 强制重新进行设置图标
* 防止 MIPUSH 不生效
*/
if (expandedNf?.isXmsf == true)
hookSmallIconOnSet(
context = iconImageView.context,
expandedNf,
expandedNf.notification?.smallIcon?.loadDrawable(iconImageView.context),
) { icon -> iconImageView.setImageBitmap(icon) }
}
}
}
if (NotificationHeaderViewWrapperInjectorClass.hasClass)
findClass(NotificationHeaderViewWrapperInjectorClass).hook {
NotificationHeaderViewWrapperInjectorClass.hook {
/** 修复下拉通知图标自动设置回 APP 图标的方法 */
injectMember {
var isUseLegacy = false
@@ -539,30 +531,30 @@ class HookEntry : YukiHookXposedInitProxy {
}
}
else
findClass(NotificationHeaderViewWrapperClass).hook {
NotificationHeaderViewWrapperClass.hook {
/** 之前的版本解决方案 */
injectMember {
method { name = "handleHeaderViews" }
afterHook {
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
if (!hasIgnoreStatusBarIconColor() && isShowMiuiStyle()) return@afterHook
if (!hasIgnoreStatusBarIconColor && isShowMiuiStyle) return@afterHook
/** 获取小图标 */
val iconImageView = field {
classSet = NotificationHeaderViewWrapperClass.clazz
name = "mIcon"
}.of<ImageView>(instance) ?: return@afterHook
/** 从父类中得到 mRow 变量 - [ExpandableNotificationRowClass] */
field {
classSet = NotificationViewWrapperClass.clazz
name = "mRow"
}.get(instance).apply {
/** 获取其中的得到通知方法 */
val expandedNf =
ExpandableNotificationRowClass.clazz.method(name = "getStatusBarNotification")
?.call<StatusBarNotification>(instance = self)
/** 执行 Hook */
hookNotifyIconOnSet(iconImageView.context, expandedNf, iconImageView)
}
val iconImageView =
NotificationHeaderViewWrapperClass.clazz
.field { name = "mIcon" }.of<ImageView>(instance) ?: return@afterHook
/**
* 从父类中得到 mRow 变量 - [ExpandableNotificationRowClass]
* 获取其中的得到通知方法
*/
val expandedNf =
ExpandableNotificationRowClass.clazz
.method { name = "getStatusBarNotification" }
.get(NotificationViewWrapperClass.clazz.field { name = "mRow" }.get(instance).self)
.invoke<StatusBarNotification>()
/** 执行 Hook */
hookNotifyIconOnSet(iconImageView.context, expandedNf, iconImageView)
}
}
}

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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
@@ -24,6 +24,7 @@
package com.fankes.miui.notify.ui
import android.app.ProgressDialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
@@ -36,18 +37,17 @@ import android.widget.TextView
import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.core.view.isVisible
import com.fankes.miui.notify.R
import com.fankes.miui.notify.bean.IconDataBean
import com.fankes.miui.notify.hook.factory.isAppNotifyHookAllOf
import com.fankes.miui.notify.hook.factory.isAppNotifyHookOf
import com.fankes.miui.notify.hook.factory.putAppNotifyHookAllOf
import com.fankes.miui.notify.hook.factory.putAppNotifyHookOf
import com.fankes.miui.notify.params.IconPackParams
import com.fankes.miui.notify.ui.base.BaseActivity
import com.fankes.miui.notify.utils.SystemUITool
import com.fankes.miui.notify.utils.showDialog
import com.fankes.miui.notify.utils.snake
import com.fankes.miui.notify.utils.toast
import com.fankes.miui.notify.utils.*
import com.fankes.miui.notify.view.MaterialSwitch
import com.google.android.material.textfield.TextInputEditText
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
class ConfigureActivity : BaseActivity() {
@@ -60,9 +60,22 @@ class ConfigureActivity : BaseActivity() {
/** 回调滚动事件改变 */
private var onScrollEvent: ((Boolean) -> Unit)? = null
/** 全部的通知优化图标数据 */
private var iconAllDatas = ArrayList<IconDataBean>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_config)
/** 检查激活状态 */
if (!YukiHookModuleStatus.isActive()) {
showDialog {
title = "模块没有激活"
msg = "模块没有激活,你无法使用这里的功能,请先激活模块。"
confirmButton(text = "我知道了") { finish() }
noCancelable()
}
return
}
/** 返回按钮点击事件 */
findViewById<View>(R.id.title_back_icon).setOnClickListener { onBackPressed() }
/** 刷新适配器结果相关 */
@@ -94,7 +107,6 @@ class ConfigureActivity : BaseActivity() {
confirmButton {
if (editText.text.toString().isNotBlank()) {
filterText = editText.text.toString().trim()
onChanged?.invoke()
refreshAdapterResult()
} else {
toast(msg = "条件不能为空")
@@ -105,11 +117,12 @@ class ConfigureActivity : BaseActivity() {
if (filterText.isNotBlank())
neutralButton(text = "清除条件") {
filterText = ""
onChanged?.invoke()
refreshAdapterResult()
}
}
}
/** 设置同步列表按钮点击事件 */
findViewById<View>(R.id.config_title_sync).setOnClickListener { onStartRefresh() }
/** 设置列表元素和 Adapter */
findViewById<ListView>(R.id.config_list_view).apply {
adapter = object : BaseAdapter() {
@@ -182,7 +195,7 @@ class ConfigureActivity : BaseActivity() {
runCatching {
startActivity(Intent().apply {
action = "android.intent.action.VIEW"
data = Uri.parse("https://github.com/fankes/MIUINativeNotifyIcon")
data = Uri.parse("https://github.com/fankes/MIUINativeNotifyIcon/blob/master/CONTRIBUTING.md")
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
@@ -190,14 +203,78 @@ class ConfigureActivity : BaseActivity() {
toast(msg = "无法启动系统默认浏览器")
}
}
/** 装载数据 */
mockLocalData()
/** 更新数据 */
onStartRefresh()
}
/** 装载或刷新本地数据 */
private fun mockLocalData() {
iconAllDatas = IconPackParams(context = this).iconDatas
refreshAdapterResult()
}
/** 首次进入或更新数据 */
private fun onStartRefresh() =
showDialog {
title = if (iconAllDatas.isNotEmpty()) "同步列表" else "初始化"
msg = (if (iconAllDatas.isNotEmpty()) "建议定期从云端拉取数据以获得最新的通知图标优化名单适配数据。\n\n"
else "首次装载需要从云端下载最新适配数据,后续可继续前往这里检查更新。\n\n") +
"[Github] 同步最新数据,无法连接可能需要魔法上网。\n\n[Surge] 缓存 CDN 数据,可以直连,但数据可能会有更新延迟。\n\n" +
"如果以上地址均无法使用,建议魔法上网或修改 Host 文件直连。"
confirmButton(text = "Surge") {
onRefreshing(url = "https://fankes.mnn.surge.sh/NotifyIconsSupportConfig.json")
}
cancelButton(text = "Github") {
onRefreshing(url = "https://raw.githubusercontent.com/fankes/MIUINativeNotifyIcon/master/iconPack/NotifyIconsSupportConfig.json")
}
neutralButton(text = "取消")
}
/**
* 开始更新数据
* @param url 使用的地址
*/
private fun onRefreshing(url: String) {
ProgressDialog(this).apply {
setDefaultStyle(context = this@ConfigureActivity)
setCancelable(false)
setTitle("同步中")
setMessage("正在同步云端数据")
show()
}.also {
ClientRequestTool.wait(context = this, url) { isDone, content ->
it.cancel()
IconPackParams(context = this).also { params ->
if (isDone)
if (params.isCompareDifferent(content)) {
params.save(content)
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this)
} else snake(msg = "列表数据已是最新")
else
showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n$content"
confirmButton(text = "我知道了")
}
}
}
}
}
/** 刷新适配器结果相关 */
private fun refreshAdapterResult() {
onChanged?.invoke()
findViewById<TextView>(R.id.config_title_count_text).text =
if (filterText.isBlank()) "已适配 ${iconDatas.size} 个 APP 的通知图标"
else "${filterText}” 匹配到 ${iconDatas.size} 个结果"
findViewById<View>(R.id.config_list_no_data_view).isVisible = iconDatas.isEmpty()
findViewById<TextView>(R.id.config_list_no_data_view).apply {
text = if (iconAllDatas.isEmpty()) "噫,竟然什么都没有~\n请点击右上角同步按钮获取云端数据" else "噫,竟然什么都没找到~"
isVisible = iconDatas.isEmpty()
}
}
/**
@@ -205,8 +282,8 @@ class ConfigureActivity : BaseActivity() {
* @return [Array]
*/
private val iconDatas
get() = if (filterText.isBlank()) IconPackParams.iconDatas
else IconPackParams.iconDatas.filter {
get() = if (filterText.isBlank()) iconAllDatas
else iconAllDatas.filter {
it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase())
}.toTypedArray()
}
}

View File

@@ -6,8 +6,8 @@
* 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.
* 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
@@ -37,6 +37,7 @@ import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.core.view.isVisible
import com.fankes.miui.notify.BuildConfig
import com.fankes.miui.notify.R
import com.fankes.miui.notify.hook.HookConst.ENABLE_COLOR_ICON_COMPAT
import com.fankes.miui.notify.hook.HookConst.ENABLE_COLOR_ICON_HOOK
import com.fankes.miui.notify.hook.HookConst.ENABLE_HIDE_ICON
import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE
@@ -115,10 +116,14 @@ class MainActivity : BaseActivity() {
val notifyIconConfigItem = findViewById<View>(R.id.config_item_notify)
val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch)
val colorIconHookSwitch = findViewById<SwitchCompat>(R.id.color_icon_fix_switch)
val colorIconCompatSwitch = findViewById<SwitchCompat>(R.id.color_icon_compat_switch)
val colorIconCompatText = findViewById<View>(R.id.color_icon_compat_text)
val notifyIconFixSwitch = findViewById<SwitchCompat>(R.id.notify_icon_fix_switch)
val notifyIconFixButton = findViewById<View>(R.id.config_notify_app_button)
/** 获取 Sp 存储的信息 */
colorIconHookItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
colorIconCompatSwitch.isVisible = modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
colorIconCompatText.isVisible = modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
notifyIconConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true) &&
modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
moduleEnableLogSwitch.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
@@ -127,6 +132,7 @@ class MainActivity : BaseActivity() {
moduleEnableLogSwitch.isChecked = modulePrefs.getBoolean(ENABLE_MODULE_LOG, default = false)
hideIconInLauncherSwitch.isChecked = modulePrefs.getBoolean(ENABLE_HIDE_ICON)
colorIconHookSwitch.isChecked = modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
colorIconCompatSwitch.isChecked = modulePrefs.getBoolean(ENABLE_COLOR_ICON_COMPAT)
notifyIconFixSwitch.isChecked = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)
moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
@@ -154,6 +160,13 @@ class MainActivity : BaseActivity() {
if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_COLOR_ICON_HOOK, b)
notifyIconConfigItem.isVisible = b
colorIconCompatSwitch.isVisible = b
colorIconCompatText.isVisible = b
SystemUITool.showNeedRestartSnake(context = this)
}
colorIconCompatSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_COLOR_ICON_COMPAT, b)
SystemUITool.showNeedRestartSnake(context = this)
}
notifyIconFixSwitch.setOnCheckedChangeListener { btn, b ->

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -0,0 +1,118 @@
/*
* 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.
* <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 2022/2/19.
*/
package com.fankes.miui.notify.utils
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 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 {
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])) {
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,110 @@
/*
* 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.
* <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 2022/2/25.
*/
@file:Suppress("TrustAllX509TrustManager", "CustomX509TrustManager")
package com.fankes.miui.notify.utils
import android.app.Activity
import com.highcapable.yukihookapi.hook.log.loggerD
import okhttp3.*
import java.io.IOException
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.*
/**
* 网络请求管理类
*/
object ClientRequestTool {
/**
* 发送 GET 请求内容并等待
* @param context 实例
* @param url 请求地址
* @param it 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息)
*/
fun wait(context: Activity, url: String, it: (Boolean, String) -> Unit) {
OkHttpClient().newBuilder().apply {
SSLSocketClient.sSLSocketFactory?.let { sslSocketFactory(it, SSLSocketClient.trustManager) }
hostnameVerifier(SSLSocketClient.hostnameVerifier)
}.build().newCall(
Request.Builder()
.url(url)
.get()
.build()
).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
context.runOnUiThread { it(false, e.toString()) }
}
override fun onResponse(call: Call, response: Response) {
val bodyString = response.body?.string() ?: ""
context.runOnUiThread { it(true, bodyString) }
}
})
}
/**
* 自动信任 SSL 证书
*
* 放行全部加密 SSL 请求
*/
object SSLSocketClient {
/**
* 格式化实例
* @return [SSLSocketFactory] or null
*/
val sSLSocketFactory
get() = safeOfNull {
SSLContext.getInstance("TLS").let {
it.init(null, arrayOf<TrustManager>(trustManager), SecureRandom())
it.socketFactory
}
}
/**
* 使用的实例
* @return [HostnameVerifier]
*/
val hostnameVerifier get() = HostnameVerifier { _, _ -> true }
/**
* 信任管理者
* @return [X509TrustManager]
*/
val trustManager
get() = object : X509TrustManager {
override fun checkClientTrusted(chain: Array<X509Certificate?>?, authType: String?) {
loggerD(msg = "TrustX509 --> $authType")
}
override fun checkServerTrusted(chain: Array<X509Certificate?>?, authType: String?) {
loggerD(msg = "TrustX509 --> $authType")
}
override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
}
}
}

View File

@@ -6,8 +6,8 @@
* 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.
* 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
@@ -26,8 +26,6 @@ package com.fankes.miui.notify.utils
import android.app.AlertDialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
@@ -109,14 +107,7 @@ class DialogBuilder(private val context: Context) {
internal fun show() = instance?.create()?.apply {
val dm = DisplayMetrics()
(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getMetrics(dm)
customLayoutView?.let { setView(it.apply { minimumWidth = round(dm.widthPixels / 1.3).toInt() }) }
window?.setBackgroundDrawable(GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
intArrayOf(Color.WHITE, Color.WHITE)
).apply {
shape = GradientDrawable.RECTANGLE
gradientType = GradientDrawable.LINEAR_GRADIENT
cornerRadius = 15.dp(this@DialogBuilder.context)
})
customLayoutView?.let { setView(it.apply { minimumWidth = round(x = dm.widthPixels / 1.3).toInt() }) }
setDefaultStyle(context = this@DialogBuilder.context)
}?.show()
}

View File

@@ -6,8 +6,8 @@
* 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.
* 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
@@ -58,4 +58,13 @@ object SystemUITool {
if (YukiHookModuleStatus.isActive())
context.snake(msg = "设置需要重启系统界面才能生效", actionText = "立即重启") { restartSystemUI(context) }
else context.snake(msg = "模块没有激活,更改不会生效")
/**
* 显示更新数据后需要重启系统界面的 [Snackbar]
* @param context 实例
*/
fun showNeedUpdateApplySnake(context: Context) =
if (YukiHookModuleStatus.isActive())
context.snake(msg = "数据已更新,请重启系统界面使更改生效", actionText = "立即重启") { restartSystemUI(context) }
else context.snake(msg = "模块没有激活,更改不会生效")
}

View File

@@ -6,8 +6,8 @@
* 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.
* 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
@@ -25,19 +25,21 @@
package com.fankes.miui.notify.utils
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.*
import android.graphics.Bitmap.createBitmap
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.os.Build
import android.provider.Settings
import android.util.Base64
import android.widget.Toast
import com.fankes.miui.notify.application.MNNApplication.Companion.appContext
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.factory.callStatic
import com.highcapable.yukihookapi.hook.factory.classOf
import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.factory.method
@@ -216,23 +218,19 @@ val ByteArray.bitmap: Bitmap get() = BitmapFactory.decodeByteArray(this, 0, size
val String.bitmap: Bitmap get() = unbase64.bitmap
/**
* 圆角图片
* @param radius 圆角度
* @return [Bitmap] 圆角后的位图 - 失败会返回处理之前的位图
* 设置对话框默认风格
* @param context 使用的实例
*/
fun Bitmap.round(radius: Float): Bitmap =
createBitmap(width, height, Bitmap.Config.ARGB_8888).also { out ->
Canvas(out).also { canvas ->
Paint().also { paint ->
paint.isAntiAlias = true
canvas.drawARGB(0, 0, 0, 0)
paint.color = Color.WHITE
canvas.drawRoundRect(RectF(Rect(0, 0, width, height)), radius, radius, paint)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(this, Rect(0, 0, width, height), Rect(0, 0, width, height), paint)
}
}
}
fun AlertDialog.setDefaultStyle(context: Context) =
window?.setBackgroundDrawable(
GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
intArrayOf(Color.WHITE, Color.WHITE)
).apply {
shape = GradientDrawable.RECTANGLE
gradientType = GradientDrawable.LINEAR_GRADIENT
cornerRadius = 15.dp(context)
})
/**
* 获取系统 Prop 值
@@ -241,9 +239,10 @@ fun Bitmap.round(radius: Float): Bitmap =
* @return [String]
*/
fun findPropString(key: String, default: String = "") = safeOf(default) {
(classOf(name = "android.os.SystemProperties").method(
name = "get", StringType, StringType
)?.callStatic(key, default)) ?: default
(classOf(name = "android.os.SystemProperties").method {
name = "get"
param(StringType, StringType)
}.get().invoke(key, default)) ?: default
}
/**

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -6,8 +6,8 @@
* 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.
* 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

View File

@@ -52,7 +52,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="..."
android:text="适配列表正在等待装载"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
@@ -61,7 +61,7 @@
android:id="@+id/config_title_up"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginEnd="10dp"
android:layout_marginEnd="5dp"
android:src="@mipmap/ic_page_top"
android:tint="@color/colorTextGray"
android:tooltipText="滚动到顶部" />
@@ -70,7 +70,7 @@
android:id="@+id/config_title_down"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginEnd="17dp"
android:layout_marginEnd="15dp"
android:src="@mipmap/ic_page_bottom"
android:tint="@color/colorTextGray"
android:tooltipText="滚动到底部" />
@@ -83,6 +83,15 @@
android:src="@mipmap/ic_filter"
android:tint="@color/colorTextGray"
android:tooltipText="按条件过滤" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/config_title_sync"
android:layout_width="23dp"
android:layout_height="23dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_sync"
android:tint="@color/colorTextGray"
android:tooltipText="同步列表" />
</LinearLayout>
<LinearLayout
@@ -126,7 +135,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="噫,竟然什么都没找到~"
android:gravity="center"
android:lineSpacingExtra="6dp"
android:text="没有数据"
android:textColor="@color/colorTextDark"
android:textSize="17sp"
android:visibility="gone" />

View File

@@ -220,6 +220,26 @@
android:text="此选项默认开启,开启后将自动对 APP 通知图标进行判断,保持高版本 API 的 APP 不规范的彩色图标不被着色为白、黑色块并对图标进行圆角优化,关闭后将按照 Android API 规范对 APP 通知进行图标着色,可能会出现着色为黑白、色块情况。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<com.fankes.miui.notify.view.MaterialSwitch
android:id="@+id/color_icon_compat_switch"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginBottom="5dp"
android:text="启用兼容模式"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:id="@+id/color_icon_compat_text"
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
@@ -260,6 +280,18 @@
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="首次安装请打开名单列表从云端更新数据,后期适配的内容也请手动打开名单列表重新拉取数据以检查更新,数据更新后重启系统界面即可生效。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -367,7 +399,7 @@
android:layout_marginBottom="10dp"
android:alpha="0.8"
android:lineSpacingExtra="6dp"
android:text="Q.哪些是已知问题?\nA.以下是问题描述列表:\n(1) 动态小图标可能会在高版本系统中闪烁,这是 MIUI 自身就存在的问题,后期只能等官方修复。\n(2) 请始终保持最新版本的 LSPosed旧版本可能会出现 Hook 不生效的问题。"
android:text="Q.哪些是已知问题?\nA.以下是问题描述列表:\n(1) 动态小图标可能会在高版本系统中闪烁,这是 MIUI 自身就存在的问题,后期只能等官方修复。\n(2) 请始终保持最新版本的 LSPosed旧版本可能会出现 Hook 不生效的问题。\n(3) 建议最低从 MIUI 12.5 “2021-5-18” 开发版以后开始使用,之前的版本可能或多或少存在 MIUI 自身 BUG 不生效、黑白块问题,若你的系统不存在原生图标问题不建议使用模块。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -2,16 +2,13 @@ package com.fankes.miui.notify
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
fun main() = println("Hello world")
}

View File

@@ -5,8 +5,8 @@ plugins {
}
ext {
appVersionName = "1.6"
appVersionCode = 11
appVersionName = "1.8"
appVersionCode = 17
}
task clean(type: Delete) {

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,8 @@ dependencyResolutionManagement {
repositories {
google()
maven { url "https://api.xposed.info/" }
maven { url 'https://www.jitpack.io' }
maven { url "https://www.jitpack.io" }
maven { url "https://s01.oss.sonatype.org/content/repositories/releases" }
mavenCentral()
}
}

BIN
tool/NotifyTester.apk Normal file

Binary file not shown.