146 Commits
1.3 ... 1.95

Author SHA1 Message Date
00bf1acdb8 Update version to 1.95,fix more bugs. 2022-03-06 03:06:46 +08:00
099886b0b2 支持导入数组和非数组格式的 JSON 自定义规则,并加入支持合并非全部覆盖的功能 2022-03-06 02:59:32 +08:00
4109a25b5d Update YukiHookAPI 2022-03-06 02:18:53 +08:00
db93f8d48e 尝试修复部分 MIUI 状态栏图标反色不生效的问题 2022-03-06 02:12:58 +08:00
e6cff940a7 Update version to 1.9,fix more bugs. 2022-03-05 00:25:50 +08:00
3f7ea97812 ... 2022-03-05 00:20:25 +08:00
8f8810e92a 修改 MIUI 版本显示方式 2022-03-05 00:19:59 +08:00
e65624c044 修改 MIUI 版本显示方式 2022-03-05 00:19:37 +08:00
eabac2bd2d Update version 2022-03-05 00:11:30 +08:00
bde952c72c 更新新的在线规则地址并加入自定义功能 2022-03-04 23:22:29 +08:00
Fankesyooni
3e362591b6 Merge pull request #41 from pzcn/master
use CDN
2022-03-04 15:20:11 +08:00
pzcn
ea29c089f6 use CDN 2022-03-04 14:57:06 +08:00
cff1eb1958 Update README.md 2022-03-03 03:43:57 +08:00
83c98b2ad3 Update version to 1.87,fix more bugs. 2022-03-02 03:14:33 +08:00
0d7f323232 Update version to 1.87,fix more bugs. 2022-03-02 03:08:57 +08:00
0d63b0a14d Update version to 1.87,fix more bugs. 2022-03-02 03:08:28 +08:00
6684d7f376 更新 YukiHookAPI 2022-03-02 03:07:38 +08:00
89321af1ce 优化代码,加入保姆级别网络问题解答 2022-03-02 01:00:55 +08:00
d7a9f1e413 Update version to 1.86,fix more bugs. 2022-03-01 02:52:08 +08:00
edf199d9a6 Update version to 1.86,fix more bugs. 2022-03-01 02:51:09 +08:00
8d6a05e02c 修复 MIUI 在 Android 11 上最小化优先级图标的着色问题 2022-03-01 02:50:30 +08:00
c7d9b2661b 修复一个可能崩溃的问题 2022-02-27 23:29:43 +08:00
d0a32e08d9 Merge code 2022-02-27 22:48:01 +08:00
b0f9e44f8a Update version to 1.85,fix more bugs. 2022-02-27 00:51:24 +08:00
1e749d2f64 Update version to 1.85,fix more bugs. 2022-02-27 00:50:41 +08:00
c886bbd6cf Update version to 1.85,fix more bugs. 2022-02-27 00:49:32 +08:00
ec45839029 合并代码到新项目 2022-02-27 00:45:28 +08:00
6fd2294529 Merge remote-tracking branch 'origin/master' 2022-02-27 00:05:00 +08:00
Fankesyooni
5913da8183 Merge pull request #37 from naicfeng/patch-1
适配 航班管家 图标 #36
2022-02-27 00:04:38 +08:00
浅秋枫影
fcf2889668 fix iconBitmap 2022-02-27 00:01:03 +08:00
浅秋枫影
6d784f1283 适配 航班管家 图标 #36
适配 航班管家 图标 #36
2022-02-26 23:56:45 +08:00
cd8ceec765 完全修改 Hook 方案,加入新的日志打印方式 2022-02-26 22:57:11 +08:00
5325ac37d6 Update version to 1.8,fix more bugs. 2022-02-25 03:10:47 +08:00
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
ef0392a168 Update version to 1.6,fix more bugs 2022-02-17 05:10:53 +08:00
32e9ee3c8b 修正调整部分功能和文案 2022-02-17 05:03:23 +08:00
296f12d07a 修复一个设置逻辑错误的问题 2022-02-17 04:47:15 +08:00
df99256231 优化“浏览器”、“小米视频”通知优化图标 2022-02-17 04:29:33 +08:00
60128bcb1a 完全修复 MIUI 12 状态栏小图标无法对 MIPUSH 生效的问题,优化 MIPUSH 未经过适配的图标为 APP 图标包的图标 2022-02-17 04:18:50 +08:00
54aec1051f Merge code 2022-02-17 03:08:25 +08:00
121170ba65 增加“皮皮虾”、“智联招聘”、“京东金融”、“慢慢买”、“识货”、“BOSS直聘”通知优化图标 2022-02-17 02:53:44 +08:00
a2d282c3f1 增加“应用商店”、“百度贴吧”通知优化图标 2022-02-17 02:28:13 +08:00
4d0098f1b8 优化大量通知图标适配颜色 2022-02-17 02:17:33 +08:00
49f6bc921c 优化调试日志处理方式 2022-02-17 01:45:16 +08:00
f94286351b 优化部分代码 2022-02-17 01:25:31 +08:00
faad964359 fix 2022-02-17 01:15:05 +08:00
78308ac558 通知图标优化界面增加快速滚动按钮 2022-02-17 01:14:05 +08:00
03fd389e13 完善贡献者昵称 2022-02-17 00:44:12 +08:00
fb067577af 优化阿里云盘图标 2022-02-17 00:34:57 +08:00
da5d4eba1b 合并代码 2022-02-17 00:17:08 +08:00
Fankesyooni
aa22ee05c6 Update issue templates 2022-02-17 00:06:38 +08:00
08873b3e1f 更新贡献文案 2022-02-16 23:37:34 +08:00
7bfd2046ca 更新贡献者名称 2022-02-16 23:31:12 +08:00
f57e658900 Merge remote-tracking branch 'origin/master' 2022-02-16 23:30:32 +08:00
Fankesyooni
b7ec4559cf Merge pull request #26 from naicfeng/master
适配 网易邮箱大师 小米音乐
2022-02-16 23:30:14 +08:00
c707f8a3f7 更新贡献者名称 2022-02-16 23:29:34 +08:00
naicfeng
350823f4ec 适配 小米音乐 2022-02-16 23:22:44 +08:00
naicfeng
26c6241d38 适配 网易邮箱大师 2022-02-16 23:17:25 +08:00
Fankesyooni
6e0c42638f Merge pull request #25 from naicfeng/master
修改 工商银行 图标增加边界,适配 Jump/虎扑/IT之家 图标 #24
2022-02-16 22:45:18 +08:00
naicfeng
8c6d7d7258 修改 工商银行 图标增加边界,适配 Jump/虎扑/IT之家 图标 #24 2022-02-16 22:29:21 +08:00
8b9ecc1d87 规范资源文件命名 2022-02-16 03:38:37 +08:00
df1df6aabf Update version to 1.51,fix more bugs 2022-02-16 02:26:17 +08:00
e618efe29e Update version to 1.51,fix more bugs 2022-02-16 02:12:58 +08:00
9468c0b696 修复通知栏单色图标颜色不显示的问题,增加 MIPUSH 单色颜色填充图标优化改进 2022-02-16 02:09:15 +08:00
9bf843eef4 修复通知栏单色图标颜色不显示的问题,增加 MIPUSH 单色颜色填充图标优化改进 2022-02-16 02:08:59 +08:00
a0f40012c6 修复通知栏单色图标颜色不显示的问题,增加 MIPUSH 单色颜色填充图标优化改进 2022-02-16 02:08:20 +08:00
b82e6d8bff 增加“中国大学MOOC”通知优化图标 2022-02-15 21:58:48 +08:00
d117a9a6e9 增加通知优化图标过滤搜索功能,更新版本到 1.5 2022-02-15 21:52:10 +08:00
850a382025 增加多个通知优化图标 2022-02-15 20:14:23 +08:00
5623f5f261 Merge code 2022-02-15 13:29:46 +08:00
c706ee377a Merge new version 2022-02-15 13:24:47 +08:00
547d90c01f 预发布版本 2022-02-15 05:31:46 +08:00
f0391b1875 修复 MIUI 12 的通知栏严重卡顿的问题 2022-02-15 05:30:50 +08:00
7faa4ede7d Enable R8 2022-02-15 05:17:25 +08:00
c865b39c97 Refactor to YukiHookAPI https://github.com/fankes/YukiHookAPI 2022-02-15 05:14:06 +08:00
b8b23c3398 修复 commit 文件丢失问题 2022-02-13 01:16:40 +08:00
bdd53eba88 修复 MIPUSH 图标相关的问题 2022-02-12 21:50:15 +08:00
83a15171e2 Update README.md 2022-02-12 00:57:14 +08:00
Fankesyooni
12485db355 Update README.md 2022-02-12 00:53:47 +08:00
1d0b97e8a3 移除 LSPosed 通知优化图标 2022-02-11 20:06:36 +08:00
18ce5f66c5 加了点没用的东西 2022-02-11 20:03:48 +08:00
8725dabe85 更新文案,更新许可证至 A-GPL3.0 2022-02-10 20:03:13 +08:00
44e68ceca1 更新文案,更新许可证至 A-GPL3.0 2022-02-10 20:01:05 +08:00
5497ac7912 Update version to 1.36,fix more bugs. 2022-02-08 20:58:03 +08:00
60115b9ecf 加入白痴文案,修改部分设置,加入提示 2022-02-08 20:53:48 +08:00
b43975e4e8 重新修改“原神”、“崩坏3”、“最右”通知优化图标,增加“小米云服务”、“小米运动”、“钉钉”、“剪映”、“贝壳找房”通知优化图标 2022-02-08 19:34:50 +08:00
d346b9c0d2 Update version to 1.35,fix more bugs. 2022-02-08 01:09:43 +08:00
8f8c260069 Update version to 1.35,fix more bugs. 2022-02-08 00:49:22 +08:00
3461a28881 修复一个严重的判断问题,尝试修复 MIPUSH 图标不能 Hook 成功的问题 2022-02-08 00:45:34 +08:00
b1b99ccdfe 为 LSPosed 模块激活状态添加状态栏、通知栏图标 2022-02-08 00:21:46 +08:00
3aa7ee2a6a 为 LSPosed 模块激活状态添加状态栏、通知栏图标 2022-02-08 00:20:29 +08:00
1829a44eb3 预增加搜索功能 2022-02-07 23:46:40 +08:00
b5219a00cc 适配“虎牙直播”、“虎牙直播 Play版”、“云闪付”、“中国工商银行”、“中国农业银行”、“墨•状态栏歌词”、“车来了”、“腾讯微云”、“最右”、“学习通”、“波洞”、“哔哩哔哩漫画”、“航旅纵横”、“米游社”、“原神”、“崩坏3”通知优化图标 2022-02-07 23:40:19 +08:00
da81e12a60 修复 MIUI 12 MIPUSH 通知图标问题,修改文案 2022-02-07 22:55:02 +08:00
57a14fdaf1 预发布版本 2022-02-07 22:40:08 +08:00
be18502fd8 优化代码,加入适配说明 2022-02-07 22:38:31 +08:00
4914ae20b2 将 su 命令更换为 Magisk 的开源库 libsu 2022-02-07 21:54:28 +08:00
3b338214fc 修正文案 2022-02-07 21:44:44 +08:00
3a484e4a63 加入旧版状态栏小图标 Hook 方式日志记录 TAG 2022-02-07 21:36:23 +08:00
199ad9fc66 加入旧版状态栏小图标 Hook 方式日志记录 TAG 2022-02-07 21:34:38 +08:00
51e3eb8784 修复一个 Hook 异常疑似系统界面崩溃问题 2022-02-07 21:31:25 +08:00
e05d65e7e5 修正文案 2022-02-07 21:16:26 +08:00
2fb557afd8 Update version to 1.3,fix more bugs. 2022-02-07 00:46:59 +08:00
71 changed files with 3039 additions and 3733 deletions

29
.github/ISSUE_TEMPLATE/----------.md vendored Normal file
View File

@@ -0,0 +1,29 @@
---
name: 通知优化图标适配反馈
about: 提交通知图标优化适配必须使用此模板提交
title: "[通知优化图标适配反馈]"
labels: To be adapted
assignees: ''
---
**需要适配的 APP 名称/包名/通知图标颜色 (必填)**
* (示例:小米音乐/com.miui.player/#fff16033)
*
**提供相关 APP 的下载渠道截图以及简要说明用途 (必填)**
*
**提供相关 APP 的通知单色图标适配素材 大小 50x50 (选填)**
* (可填写资源下载地址或直接添加附件提交,不接受百度网盘、天翼云盘以及各种快传、私有云盘)
* (若直接在附件提交这里可不填)
<!--- 提交时请将括号内容包括括号全部删除,填入你自己的内容 --->
<!--- 请保留模板原始标题 --->
<!--- 不按规定提交的 issues 将直接被关闭 --->
<!--- Create by Template --->

54
.github/ISSUE_TEMPLATE/----bug---.md vendored Normal file
View File

@@ -0,0 +1,54 @@
---
name: 问题与 BUG 反馈
about: 问题反馈必须使用此模板进行提交
title: "[问题与 BUG 反馈] *简要描述问题原因*"
labels: bug
assignees: fankes
---
**MIUI 版本(必填)**
*
**MIUI 版本类型(请保留一个)**
* 公测版/内测版/稳定版
**Android 版本(必填)**
*
**模块版本(必填)**
*
**使用的 Xposed 框架名称与框架版本(必填)**
* (例如LSPosed Zygisk/Riru 版本号)
**同时使用的带有系统界面作用域的 Xposed 模块(选填)**
* (没有可空)
**问题的具体描述**
* (复现步骤、前提以及详细截图和录屏演示)
**提供模块问题 Log 或必要 Log**
* (LSPosed 可在日志管理中查看并筛选包含 `MIUINativeNotifyIcon` 的日志)
<details><summary>展开查看</summary><pre><code>
此处粘贴问题Log
</code></pre></details>
<!--- 注意:只接受 MIUI 正规官方版本系统,如果你正在使用 MIUI 官改(第三方改版)请不要提交任何 BUG 与问题,开发者无义务解决 --->
<!--- 提交时请将括号内容包括括号全部删除,填入你自己的内容 --->
<!--- 请保留模板原始标题 --->
<!--- 不按规定提交的 issues 将直接被关闭 --->
<!--- Create by Template --->

12
.idea/misc.xml generated
View File

@@ -6,11 +6,19 @@
<entry key="app/src/main/res/drawable-night/dark_round.xml" value="0.256" /> <entry key="app/src/main/res/drawable-night/dark_round.xml" value="0.256" />
<entry key="app/src/main/res/drawable-night/permotion_round.xml" value="0.256" /> <entry key="app/src/main/res/drawable-night/permotion_round.xml" value="0.256" />
<entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.44871794871794873" /> <entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.44871794871794873" />
<entry key="app/src/main/res/drawable/bg_green_round.xml" value="0.255" />
<entry key="app/src/main/res/drawable/bg_yellow_round.xml" value="0.255" />
<entry key="app/src/main/res/drawable/permotion_round.xml" value="0.256" /> <entry key="app/src/main/res/drawable/permotion_round.xml" value="0.256" />
<entry key="app/src/main/res/drawable/white_round.xml" value="0.256" /> <entry key="app/src/main/res/drawable/white_round.xml" value="0.256" />
<entry key="app/src/main/res/layout-w1240dp/dia_source_from.xml" value="0.36484375" />
<entry key="app/src/main/res/layout-w1240dp/dia_source_from_string.xml" value="0.36484375" />
<entry key="app/src/main/res/layout/activity_config.xml" value="0.42168674698795183" /> <entry key="app/src/main/res/layout/activity_config.xml" value="0.42168674698795183" />
<entry key="app/src/main/res/layout/activity_main.xml" value="0.4036346245815399" /> <entry key="app/src/main/res/layout/activity_login.xml" value="0.4375" />
<entry key="app/src/main/res/layout/adapter_config.xml" value="0.4375" /> <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.375" />
<entry key="app/src/main/res/layout/dia_icon_filter.xml" value="0.4307692307692308" />
<entry key="app/src/main/res/layout/dia_icon_search.xml" value="0.4307692307692308" />
<entry key="app/src/main/res/layout/dia_source_from_string.xml" value="0.4307692307692308" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.44871794871794873" /> <entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.44871794871794873" />
</map> </map>
</option> </option>

View File

@@ -1,42 +0,0 @@
# 开始贡献
欢迎为通知图标优化名单贡献宝贵资源!<br/>
## 分支规定
不管是直接 Push 代码还是提交 Pull Request都必须使 commit 指向 master 分支。
## 代码格式规范
- 1.全部提交代码必须使用 IDE(Android Studio 或 IDEA) 进行格式化,未经格式化的代码将拒绝合并提交请求
- 2.代码必须使用 4 spaces 缩进格式化
## 贡献方法
- 在下方的类中添加新的 APP 通知图标适配条目
- [IconPackParams.kt](https://github.com/fankes/MIUINativeNotifyIcon/blob/master/app/src/main/java/com/fankes/miui/notify/params/IconPackParams.kt)
- 使用灰度位图转 Base64 来得到 Base64 的位图数据字符串
- [BitmapToBase64](https://github.com/fankes/BitmapToBase64)
- 新增条目的模板如下所示
```kotlin
IconDataBean(
isEnabled = true, // 是否默认启用替换彩色图标 - 关闭后将全部停止替换
isEnabledAll = false, // 是否默认启用替换全部图标
appName = "", // APP 名称
packageName = "", // APP 包名
iconBitmap = ("").bitmap, // 位图数据 Base64
contributorName = "" // 贡献者昵称
)
```
- 提交时请将后方的注释删除,否则不予合并代码
## 其它要求
- 1.调试性质或大批量注释代码,禁止提交
- 2.类名和方法名仅能由开发者进行修改和提交,禁止随意修改项目名称、方法名称以及类名
- 3.禁止随意更新项目依赖以及增加新的依赖,有问题请提前提交到 issues 进行说明
- 4.禁止更新项目版本号,版本号交由开发者合并代码并发布 release 版本
- 5.代码语言要求,请统一使用 Kotlin除特殊情况外不接受其他语言的提交
- 6.以上

632
LICENSE
View File

@@ -1,107 +1,100 @@
GNU GENERAL PUBLIC LICENSE # GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Version 3, 19 November 2007
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble Copyright (C) 2007 Free Software Foundation, Inc.
<https://fsf.org/>
The GNU General Public License is a free, copyleft license for Everyone is permitted to copy and distribute verbatim copies of this
software and other kinds of works. license document, but changing it is not allowed.
The licenses for most software and other practical works are designed ### Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast, to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free share and change all versions of a program--to make sure it remains
software for all its users. We, the Free Software Foundation, use the free software for all its users.
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things. free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you Developers that use our General Public Licenses protect your rights
these rights or asking you to surrender the rights. Therefore, you have with two steps: (1) assert copyright on the software, and (2) offer
certain responsibilities if you distribute copies of the software, or if you this License which gives you legal permission to copy, distribute
you modify it: responsibilities to respect the freedom of others. and/or modify the software.
For example, if you distribute copies of such a program, whether A secondary benefit of defending all users' freedom is that
gratis or for a fee, you must pass on to the recipients the same improvements made in alternate versions of the program, if they
freedoms that you received. You must make sure that they, too, receive receive widespread use, become available for other developers to
or can get the source code. And you must show them these terms so they incorporate. Many developers of free software are heartened and
know their rights. encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
Developers that use the GNU GPL protect your rights with two steps: The GNU Affero General Public License is designed specifically to
(1) assert copyright on the software, and (2) offer you this License ensure that, in such cases, the modified source code becomes available
giving you legal permission to copy, distribute and/or modify it. to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
For the developers' and authors' protection, the GPL clearly explains An older license, called the Affero General Public License and
that there is no warranty for this free software. For both users' and published by Affero, was designed to accomplish similar goals. This is
authors' sake, the GPL requires that modified versions be marked as a different license, not a version of the Affero GPL, but Affero has
changed, so that their problems will not be attributed erroneously to released a new version of the Affero GPL which permits relicensing
authors of previous versions. under this license.
Some devices are designed to deny users access to install or run The precise terms and conditions for copying, distribution and
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow. modification follow.
TERMS AND CONDITIONS ### TERMS AND CONDITIONS
0. Definitions. #### 0. Definitions.
"This License" refers to version 3 of the GNU General Public License. "This License" refers to version 3 of the GNU Affero General Public
License.
"Copyright" also means copyright-like laws that apply to other kinds of "Copyright" also means copyright-like laws that apply to other kinds
works, such as semiconductor masks. of works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this "The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations. "recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an in a fashion requiring copyright permission, other than the making of
exact copy. The resulting work is called a "modified version" of the an exact copy. The resulting work is called a "modified version" of
earlier work or a work "based on" the earlier work. the earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based A "covered work" means either the unmodified Program or a work based
on the Program. on the Program.
To "propagate" a work means to do anything with it that, without To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying, computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the distribution (with or without modification), making available to the
public, and in some countries other activities as well. public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through parties to make or receive copies. Mere interaction with a user
a computer network, with no transfer of a copy, is not conveying. through a computer network, with no transfer of a copy, is not
conveying.
An interactive user interface displays "Appropriate Legal Notices" An interactive user interface displays "Appropriate Legal Notices" to
to the extent that it includes a convenient and prominently visible the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2) feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the extent that warranties are provided), that licensees may convey the
@@ -109,18 +102,18 @@ work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion. menu, a prominent item in the list meets this criterion.
1. Source Code. #### 1. Source Code.
The "source code" for a work means the preferred form of the work The "source code" for a work means the preferred form of the work for
for making modifications to it. "Object code" means any non-source making modifications to it. "Object code" means any non-source form of
form of a work. a work.
A "Standard Interface" means an interface that either is an official A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that interfaces specified for a particular programming language, one that
is widely used among developers working in that language. is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that Component, and (b) serves only to enable use of the work with that
@@ -131,7 +124,7 @@ implementation is available to the public in source code form. A
(if any) on which the executable work runs, or a compiler used to (if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it. produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's control those activities. However, it does not include the work's
@@ -144,16 +137,15 @@ linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those such as by intimate data communication or control flow between those
subprograms and other parts of the work. subprograms and other parts of the work.
The Corresponding Source need not include anything that users The Corresponding Source need not include anything that users can
can regenerate automatically from other parts of the Corresponding regenerate automatically from other parts of the Corresponding Source.
Source.
The Corresponding Source for a work in source code form is that The Corresponding Source for a work in source code form is that same
same work. work.
2. Basic Permissions. #### 2. Basic Permissions.
All rights granted under this License are granted for the term of All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a permission to run the unmodified Program. The output from running a
@@ -161,40 +153,40 @@ covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law. rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not You may make, run and propagate covered works that you do not convey,
convey, without conditions so long as your license otherwise remains without conditions so long as your license otherwise remains in force.
in force. You may convey covered works to others for the sole purpose You may convey covered works to others for the sole purpose of having
of having them make modifications exclusively for you, or provide you them make modifications exclusively for you, or provide you with
with facilities for running those works, provided that you comply with facilities for running those works, provided that you comply with the
the terms of this License in conveying all material for which you do terms of this License in conveying all material for which you do not
not control copyright. Those thus making or running the covered works control copyright. Those thus making or running the covered works for
for you must do so exclusively on your behalf, under your direction you must do so exclusively on your behalf, under your direction and
and control, on terms that prohibit them from making any copies of control, on terms that prohibit them from making any copies of your
your copyrighted material outside their relationship with you. copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under Conveying under any other circumstances is permitted solely under the
the conditions stated below. Sublicensing is not allowed; section 10 conditions stated below. Sublicensing is not allowed; section 10 makes
makes it unnecessary. it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law. #### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or 11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such similar laws prohibiting or restricting circumvention of such
measures. measures.
When you convey a covered work, you waive any legal power to forbid When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention circumvention of technological measures to the extent such
is effected by exercising rights under this License with respect to circumvention is effected by exercising rights under this License with
the covered work, and you disclaim any intention to limit operation or respect to the covered work, and you disclaim any intention to limit
modification of the work as a means of enforcing, against the work's operation or modification of the work as a means of enforcing, against
users, your or third parties' legal rights to forbid circumvention of the work's users, your or third parties' legal rights to forbid
technological measures. circumvention of technological measures.
4. Conveying Verbatim Copies. #### 4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice; appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any keep intact all notices stating that this License and any
@@ -202,37 +194,35 @@ non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program. recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey, You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee. and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions. #### 5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions: terms of section 4, provided that you also meet all of these
conditions:
a) The work must carry prominent notices stating that you modified - a) The work must carry prominent notices stating that you modified
it, and giving a relevant date. it, and giving a relevant date.
- b) The work must carry prominent notices stating that it is
b) The work must carry prominent notices stating that it is released under this License and any conditions added under
released under this License and any conditions added under section section 7. This requirement modifies the requirement in section 4
7. This requirement modifies the requirement in section 4 to to "keep intact all notices".
"keep intact all notices". - c) You must license the entire work, as a whole, under this
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7 License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts, additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it. invalidate such permission if you have separately received it.
- d) If the work has interactive user interfaces, each must display
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your interfaces that do not display Appropriate Legal Notices, your
work need not make them do so. work need not make them do so.
A compilation of a covered work with other separate and independent A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work, works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program, and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an in or on a volume of a storage or distribution medium, is called an
@@ -242,19 +232,18 @@ beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other in an aggregate does not cause this License to apply to the other
parts of the aggregate. parts of the aggregate.
6. Conveying Non-Source Forms. #### 6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms You may convey a covered work in object code form under the terms of
of sections 4 and 5, provided that you also convey the sections 4 and 5, provided that you also convey the machine-readable
machine-readable Corresponding Source under the terms of this License, Corresponding Source under the terms of this License, in one of these
in one of these ways: ways:
a) Convey the object code in, or embodied in, a physical product - a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the (including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium Corresponding Source fixed on a durable physical medium
customarily used for software interchange. customarily used for software interchange.
- b) Convey the object code in, or embodied in, a physical product
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a (including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product long as you offer spare parts or customer support for that product
@@ -263,16 +252,14 @@ in one of these ways:
product that is covered by this License, on a durable physical product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the conveying of source, or (2) access to copy the Corresponding
Corresponding Source from a network server at no charge. Source from a network server at no charge.
- c) Convey individual copies of the object code with a copy of the
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord only if you received the object code with such an offer, in accord
with subsection 6b. with subsection 6b.
- d) Convey the object code by offering access from a designated
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the further charge. You need not require recipients to copy the
@@ -284,38 +271,38 @@ in one of these ways:
Corresponding Source. Regardless of what server hosts the Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements. available for as long as needed to satisfy these requirements.
- e) Convey the object code using peer-to-peer transmission,
provided you inform other peers where the object code and
Corresponding Source of the work are being offered to the general
public at no charge under subsection 6d.
e) Convey the object code using peer-to-peer transmission, provided A separable portion of the object code, whose source code is excluded
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be from the Corresponding Source as a System Library, need not be
included in conveying the object code work. included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family, tangible personal property which is normally used for personal,
or household purposes, or (2) anything designed or sold for incorporation family, or household purposes, or (2) anything designed or sold for
into a dwelling. In determining whether a product is a consumer product, incorporation into a dwelling. In determining whether a product is a
doubtful cases shall be resolved in favor of coverage. For a particular consumer product, doubtful cases shall be resolved in favor of
product received by a particular user, "normally used" refers to a coverage. For a particular product received by a particular user,
typical or common use of that class of product, regardless of the status "normally used" refers to a typical or common use of that class of
of the particular user or of the way in which the particular user product, regardless of the status of the particular user or of the way
actually uses, or expects or is expected to use, the product. A product in which the particular user actually uses, or expects or is expected
is a consumer product regardless of whether the product has substantial to use, the product. A product is a consumer product regardless of
commercial, industrial or non-consumer uses, unless such uses represent whether the product has substantial commercial, industrial or
the only significant mode of use of the product. non-consumer uses, unless such uses represent the only significant
mode of use of the product.
"Installation Information" for a User Product means any methods, "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install procedures, authorization keys, or other information required to
and execute modified versions of a covered work in that User Product from install and execute modified versions of a covered work in that User
a modified version of its Corresponding Source. The information must Product from a modified version of its Corresponding Source. The
suffice to ensure that the continued functioning of the modified object information must suffice to ensure that the continued functioning of
code is in no case prevented or interfered with solely because the modified object code is in no case prevented or interfered with
modification has been made. solely because modification has been made.
If you convey an object code work under this section in, or with, or If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a User Product is transferred to the recipient in perpetuity or for a
@@ -326,23 +313,24 @@ if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has modified object code on the User Product (for example, the work has
been installed in ROM). been installed in ROM).
The requirement to provide Installation Information does not include a The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates requirement to continue to provide support service, warranty, or
for a work that has been modified or installed by the recipient, or for updates for a work that has been modified or installed by the
the User Product in which it has been modified or installed. Access to a recipient, or for the User Product in which it has been modified or
network may be denied when the modification itself materially and installed. Access to a network may be denied when the modification
adversely affects the operation of the network or violates the rules and itself materially and adversely affects the operation of the network
protocols for communication across the network. or violates the rules and protocols for communication across the
network.
Corresponding Source conveyed, and Installation Information provided, Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in documented (and with an implementation available to the public in
source code form), and must require no special password or key for source code form), and must require no special password or key for
unpacking, reading or copying. unpacking, reading or copying.
7. Additional Terms. #### 7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions. License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent be treated as though they were included in this License, to the extent
@@ -351,41 +339,36 @@ apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions. this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work, additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission. for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of add to a covered work, you may (if authorized by the copyright holders
that material) supplement the terms of this License with terms: of that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the - a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or terms of sections 15 and 16 of this License; or
- b) Requiring preservation of specified reasonable legal notices or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or Notices displayed by works containing it; or
- c) Prohibiting misrepresentation of the origin of that material,
c) Prohibiting misrepresentation of the origin of that material, or or requiring that modified versions of such material be marked in
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or reasonable ways as different from the original version; or
- d) Limiting the use for publicity purposes of names of licensors
d) Limiting the use for publicity purposes of names of licensors or or authors of the material; or
authors of the material; or - e) Declining to grant rights under trademark law for use of some
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or trade names, trademarks, or service marks; or
- f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions
of it) with contractual assumptions of liability to the recipient,
for any liability that these contractual assumptions directly
impose on those licensors and authors.
f) Requiring indemnification of licensors and authors of that All other non-permissive additional terms are considered "further
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further governed by this License along with a term that is a further
@@ -395,47 +378,47 @@ License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does of that license document, provided that the further restriction does
not survive such relicensing or conveying. not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating additional terms that apply to those files, or a notice indicating
where to find the applicable terms. where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions; form of a separately written license, or stated as exceptions; the
the above requirements apply either way. above requirements apply either way.
8. Termination. #### 8. Termination.
You may not propagate or modify a covered work except as expressly You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third this License (including any patent licenses granted under the third
paragraph of section 11). paragraph of section 11).
However, if you cease all violation of this License, then your However, if you cease all violation of this License, then your license
license from a particular copyright holder is reinstated (a) from a particular copyright holder is reinstated (a) provisionally,
provisionally, unless and until the copyright holder explicitly and unless and until the copyright holder explicitly and finally
finally terminates your license, and (b) permanently, if the copyright terminates your license, and (b) permanently, if the copyright holder
holder fails to notify you of the violation by some reasonable means fails to notify you of the violation by some reasonable means prior to
prior to 60 days after the cessation. 60 days after the cessation.
Moreover, your license from a particular copyright holder is Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice. your receipt of the notice.
Termination of your rights under this section does not terminate the Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same reinstated, you do not qualify to receive new licenses for the same
material under section 10. material under section 10.
9. Acceptance Not Required for Having Copies. #### 9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or You are not required to accept this License in order to receive or run
run a copy of the Program. Ancillary propagation of a covered work a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However, to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or nothing other than this License grants you permission to propagate or
@@ -443,14 +426,14 @@ modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so. covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients. #### 10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License. for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that work results from an entity transaction, each party to that
@@ -460,7 +443,7 @@ give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts. the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation rights granted under this License, and you may not initiate litigation
@@ -468,14 +451,14 @@ rights granted under this License, and you may not initiate litigation
any patent claim is infringed by making, using, selling, offering for any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it. sale, or importing the Program or any portion of it.
11. Patents. #### 11. Patents.
A "contributor" is a copyright holder who authorizes use under this A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version". work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims A contributor's "essential patent claims" are all patent claims owned
owned or controlled by the contributor, whether already acquired or or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version, by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a but do not include claims that would be infringed only as a
@@ -484,19 +467,19 @@ purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of patent sublicenses in a manner consistent with the requirements of
this License. this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version. propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to (such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a party means to make such an agreement or commitment not to enforce a
patent against the party. patent against the party.
If you convey a covered work, knowingly relying on a patent license, If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means, publicly available network server or other readily accessible means,
@@ -510,7 +493,7 @@ covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid. country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify receiving the covered work authorizing them to use, propagate, modify
@@ -518,157 +501,160 @@ or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered you grant is automatically extended to all recipients of the covered
work and works based on it. work and works based on it.
A patent license is "discriminatory" if it does not include within A patent license is "discriminatory" if it does not include within the
the scope of its coverage, prohibits the exercise of, or is scope of its coverage, prohibits the exercise of, or is conditioned on
conditioned on the non-exercise of one or more of the rights that are the non-exercise of one or more of the rights that are specifically
specifically granted under this License. You may not convey a covered granted under this License. You may not convey a covered work if you
work if you are a party to an arrangement with a third party that is are a party to an arrangement with a third party that is in the
in the business of distributing software, under which you make payment business of distributing software, under which you make payment to the
to the third party based on the extent of your activity of conveying third party based on the extent of your activity of conveying the
the work, and under which the third party grants, to any of the work, and under which the third party grants, to any of the parties
parties who would receive the covered work from you, a discriminatory who would receive the covered work from you, a discriminatory patent
patent license (a) in connection with copies of the covered work license (a) in connection with copies of the covered work conveyed by
conveyed by you (or copies made from those copies), or (b) primarily you (or copies made from those copies), or (b) primarily for and in
for and in connection with specific products or compilations that connection with specific products or compilations that contain the
contain the covered work, unless you entered into that arrangement, covered work, unless you entered into that arrangement, or that patent
or that patent license was granted, prior to 28 March 2007. license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law. otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom. #### 12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this covered work so as to satisfy simultaneously your obligations under
License and any other pertinent obligations, then as a consequence you may this License and any other pertinent obligations, then as a
not convey it at all. For example, if you agree to terms that obligate you consequence you may not convey it at all. For example, if you agree to
to collect a royalty for further conveying from those to whom you convey terms that obligate you to collect a royalty for further conveying
the Program, the only way you could satisfy both those terms and this from those to whom you convey the Program, the only way you could
License would be to refrain entirely from conveying the Program. satisfy both those terms and this License would be to refrain entirely
from conveying the Program.
13. Use with the GNU Affero General Public License. #### 13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, you have Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your
version supports such interaction) an opportunity to receive the
Corresponding Source of your version by providing access to the
Corresponding Source from a network server at no charge, through some
standard or customary means of facilitating copying of software. This
Corresponding Source shall include the Corresponding Source for any
work covered by version 3 of the GNU General Public License that is
incorporated pursuant to the following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work, License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License, but the work with which it is combined will remain governed by version
section 13, concerning interaction through a network will apply to the 3 of the GNU General Public License.
combination as such.
14. Revised Versions of this License. #### 14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of The Free Software Foundation may publish revised and/or new versions
the GNU General Public License from time to time. Such new versions will of the GNU Affero General Public License from time to time. Such new
be similar in spirit to the present version, but may differ in detail to versions will be similar in spirit to the present version, but may
address new problems or concerns. differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Each version is given a distinguishing version number. If the Program
Program specifies that a certain numbered version of the GNU General specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published GNU Affero General Public License, you may choose any version ever
by the Free Software Foundation. published by the Free Software Foundation.
If the Program specifies that a proxy can decide which future If the Program specifies that a proxy can decide which future versions
versions of the GNU General Public License can be used, that proxy's of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you public statement of acceptance of a version permanently authorizes you
to choose that version for the Program. to choose that version for the Program.
Later license versions may give you additional or different Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a author or copyright holder as a result of your choosing to follow a
later version. later version.
15. Disclaimer of Warranty. #### 15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.
16. Limitation of Liability. #### 16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
SUCH DAMAGES. PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
17. Interpretation of Sections 15 and 16. #### 17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms, above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee. copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs ### How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms. free software which everyone can redistribute and change under these
terms.
To do so, attach the following notices to the program. It is safest To do so, attach the following notices to the program. It is safest to
to attach them to the start of each source file to most effectively attach them to the start of each source file to most effectively state
state the exclusion of warranty; and each file should have at least the exclusion of warranty; and each file should have at least the
the "copyright" line and a pointer to where the full notice is found. "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.} <one line to give the program's name and a brief idea of what it does.>
Copyright (C) {year} {name of author} Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU Affero General Public License as
the Free Software Foundation, either version 3 of the License, or published by the Free Software Foundation, either version 3 of the
(at your option) any later version. License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper
mail.
If the program does terminal interaction, make it output a short If your software can interact with users remotely through a computer
notice like this when it starts in an interactive mode: network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for
the specific requirements.
{project} Copyright (C) {year} {fullname} You should also get your employer (if you work as a programmer) or
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. school, if any, to sign a "copyright disclaimer" for the program, if
This is free software, and you are welcome to redistribute it necessary. For more information on this, and how to apply and follow
under certain conditions; type `show c' for details. the GNU AGPL, see <https://www.gnu.org/licenses/>.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

100
README.md
View File

@@ -1,8 +1,8 @@
# MIUI 原生通知图标 # MIUI 原生通知图标
![Eclipse Marketplace](https://img.shields.io/badge/build-passing-brightgreen) ![Eclipse Marketplace](https://img.shields.io/badge/build-passing-brightgreen)
![Eclipse Marketplace](https://img.shields.io/badge/license-GPL3.0-blue) ![Eclipse Marketplace](https://img.shields.io/badge/license-AGPL3.0-blue)
![Eclipse Marketplace](https://img.shields.io/badge/version-v1.3-green) ![Eclipse Marketplace](https://img.shields.io/badge/version-v1.95-green)
<br/><br/> <br/><br/>
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/app/src/main/ic_launcher-playstore.png" width = "100" height = "100"/> <img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/app/src/main/ic_launcher-playstore.png" width = "100" height = "100"/>
<br/> <br/>
@@ -12,83 +12,97 @@ 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.3-green)</a> <a href='https://github.com/fankes/MIUINativeNotifyIcon/releases'>![Eclipse Marketplace](https://img.shields.io/badge/download-v1.95-green)</a>
<br/><br/> <br/><br/>
⚠️ 适配说明<br/> ⚠️ 适配说明<br/>
- 此模块仅支持 Lsposed(作用域“系统界面”)、EdXposed(不推荐)、不支持太极无极(阴) - 此模块仅支持 LSPosed(作用域“系统界面”)、~~EdXposed(随时停止支持)~~、不支持太极无极
- 目前最低支持基于 Android 9 版本的 MIUI12 或 MIUI12.5(建议) - 请确保你使用的是 MIUI 官方版本,任何第三方官改包发生的问题,开发者没有义务去解决和修复,请自求多福
- 使用 Zygisk 方式运行的 Lsposed 可能会发生 Hook 不生效的问题,若出现问题请使用 Ramdisk 版本的 Lsposed - 目前最低支持基于 Android 9 版本的 MIUI 12 或 MIUI 12.5(最低建议)
- 建议最低从 MIUI 12.5 `2021-5-18` 开发版以后开始使用模块,之前的版本可能或多或少存在 MIUI 自身 BUG 不生效、黑白块的问题
- 请始终保持最新版本的 LSPosed旧版本可能会出现 Hook 不生效的问题
# 禁止任何商业用途 # 请勿用于非法用途
本模块完全开源免费,如果好用你可以打赏支持开发,严禁未经许可进行二改贩卖,违者必惩必究 - 本模块完全开源免费,如果好用你可以打赏支持开发,但是请不要用于非法用途
- 本模块发布地址仅有 [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.miui.notify/releases)、
[Release](https://github.com/fankes/MIUINativeNotifyIcon/releases)
及[蓝奏云](https://fankes.lanzouy.com/b030o2e8h),从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
# 开始贡献 # 贡献通知图标优化名单
由于国内厂商 APP 的不规范彩色图标影响整体图标的美观,现在开放第三方 APP 的通知图标适配<br/> 此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方<br/>
欢迎为通知图标优化名单贡献宝贵资源!<br/>
- [CONTRIBUTING](https://github.com/fankes/MIUINativeNotifyIcon/blob/master/CONTRIBUTING.md) - [Android 通知图标规范适配计划](https://github.com/fankes/AndroidNotifyIconAdapt)
# 历史背景 # 历史背景
这个模块诞生来源于 MIUI 的乱改和不规范,本来 MIUI 9 之后,官方给出了原生通知图标样式,后面由于用户反应通知栏经常出现黑白块。 这当然不是系统的错,而是国内 APP 极其不规范的通知图标设计,于是 MIUI 这个模块诞生来源于 MIUI 的乱改和不规范,本来 MIUI 9 之后,官方给出了原生通知图标样式,后面由于用户反应通知栏经常出现黑白块。<br/><br/>
选择直接忽略这个问题把全部图标都改成了 APP 的彩色图标, 使得之前拥有自有样式的原生图标也被破坏通知中“setSmallIcon”不再有效这个模块就是为了修复被 MIUI 开发组忽略的图标问题, 并完美地给 MIUI 修复了黑白块图标的问题。 这当然不是系统的错,而是国内 APP 和 `MIPUSH` 的通知极其不规范的通知图标设计。<br/><br/>
但是呢,接到反馈后 MIUI 开发组选择直接忽略这个问题,在 `2021-5-18` 的开发版开始,把全部通知图标都改成了 APP 的彩色图标,使得之前拥有自有样式的原生图标也被破坏。<br/><br/>
对于 Android 开发者来说,官方文档中的 `setSmallIcon` 不再适用于魔改后的 MIUI这将会严重破坏非常多的状态图标。<br/><br/>
当然,国内的手机生态除了 `MIPUSH` 的营销通知就是社交软件的通知,可能大部分人都不会在意这件事情。<br/><br/>
但是,这个模块就是为了修复被 MIUI 开发组忽略的图标问题才诞生的,并完美地给 MIUI 修复了黑白块图标的问题。
<br/> <br/>
# 探索历程 # 探索历程
- 原生 Android 的小图标和通知图标具有状态性<br/> 原生 Android 的小图标和通知图标具有状态性<br/><br/>
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/native.jpg" height = "35"/><br/> <img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/native.jpg" height = "35"/><br/><br/>
- 而 MIUI 最近的版本直接破坏了这一状态性,全部设置为 APP 的图标,不仅难看而且你无法下拉通知栏区别这些图标代表什么<br/> 而 MIUI 最近的版本直接破坏了这一状态性,全部设置为 APP 的图标,不仅难看而且你无法下拉通知栏区别这些图标代表什么<br/><br/>
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/miui.jpg" height = "40"/><br/> <img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/miui.jpg" height = "40"/><br/><br/>
- 同样地,通知面板的图标同样遵守这一状态性<br/> 同样地,通知面板的图标同样遵守这一状态性<br/><br/>
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/native_n_1.jpg" height = "100"/><br/> <img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/native_n_1.jpg" height = "100"/><br/>
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/native_n_2.jpg" height = "100"/><br/> <img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/native_n_2.jpg" height = "100"/><br/><br/>
- 而 MIUI 做了什么呢<br/> 而 MIUI 做了什么呢<br/><br/>
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/miui_n_1.jpg" height = "100"/><br/> <img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/miui_n_1.jpg" height = "100"/><br/>
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/miui_n_2.jpg" height = "100"/><br/> <img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/images/miui_n_2.jpg" height = "100"/><br/><br/>
- 不曾记得是什么版本开始MIUI 把通知图标改成了这个鬼样子,寻找开发组提案也是无人问津,最后转念一想,自己干吧。 不曾记得是什么版本开始MIUI 把通知图标改成了这个鬼样子,寻找开发组提案也是无人问津,最后转念一想,自己干吧。<br/><br/>
- 由于目前大量通知图标都来自 MIPUSH 发出的营销通知,而 MIPUSH 的图标都是统一的彩色应用图标,很多应用也没有适配这一特性, 在通知栏广告满天飞的情况下MIUI 由于目前大量通知图标都来自 `MIPUSH` 发出的营销通知,而 `MIPUSH` 的图标都是统一的彩色应用图标,很多应用也没有适配这一特性, 在通知栏广告满天飞的情况下MIUI
选择放弃原生通知功能,而做出这种违反原生通知规则的做法,而这些彩色图标被设置为单色调图标,也确实会发生黑白块的问题,但是同时又会破坏遵守规范的图标。 选择放弃原生通知功能,而做出这种违反原生通知规则的做法,而这些彩色图标被设置为单色调图标,也确实会发生黑白块的问题,但是同时又会破坏遵守规范的图标。<br/><br/>
- 真的没有办法了吗?在不断探索下,我找到了原生支持色彩判断的类 真的没有办法了吗?在不断探索下,我找到了原生支持色彩判断的类
``` ```
com.android.internal.util.ContrastColorUtil com.android.internal.util.ContrastColorUtil
``` ```
- 这个类中有一个方法可以拿出来判断图标的灰度效果 这个类中有一个方法可以拿出来判断图标的灰度效果
``` ```
ContrastColorUtil.getInstance().isGrayscaleIcon(drawable); ContrastColorUtil.getInstance().isGrayscaleIcon(drawable)
``` ```
- 问题就被解决了,顺便修了一下被 MIUI 破坏的通知图标以及优化了一下应用本身方块图标的圆角... 问题就被解决了,顺便修了一下被 MIUI 破坏的通知图标以及优化了一下应用本身方块图标的圆角......<br/><br/>
- 最后,我想大声问一句 MIUI 开发组:“就这?” 就这么简单的问题为什么拖了这么长时间也没有结论,还要交给用户去修复,这真的是一种负责任的表现吗? 最后,我想大声问一句 MIUI 开发组:“就这?” 就这么简单的问题为什么拖了这么长时间也没有结论,还要交给用户去修复,这真的是一种负责任的表现吗?<br/><br/>
- 后来一想,也是啊,被国内生态毒害的用户,怎么可能会去想到这些问题呢,最后只能是我自作多情,还对 MIUI 留有一点情怀吧。 后来一想,也是啊,被国内生态毒害的用户,怎么可能会去想到这些问题呢,最后只能是我自作多情,还对 MIUI 留有一点情怀吧。<br/><br/>
- ——来自一个无可奈何的 MIUI 老用户 ——来自一个无可奈何的 MIUI 老用户
# 后记 # 后记
- 近期重新适配了 MIUI 12、12.5、13 版本,每个版本的图标设置方法都不一样,而且改的乱七八糟的,我都要无语了,只能用了很多折中方案,毕竟我也没有那么大精力每个版本去修复,实在是累了 近期重新适配了 MIUI 12、12.5、13 版本,每个版本的图标设置方法都不一样,而且改的乱七八糟的,我都要无语了,只能用了很多折中方案,毕竟我也没有那么大精力每个版本去修复,实在是累了<br/><br/>
- 特地的把自己能有的小米手机刷成各种 MIUI 版本去为酷友做专项适配,我也是很累了,也希望你们能够多多支持,也能让 MIUI 做得更好 特地的把自己能有的小米手机刷成各种 MIUI 版本去为酷友做专项适配,我也是很累了,也希望你们能够多多支持,也能让 MIUI 做得更好<br/><br/>
- MIUI 再不重写,怕是永远会变成安卓之光。雷军,金凡!! MIUI 再不重写,怕是永远会变成安卓之光。雷军,金凡!!
# 许可证 # 许可证
- [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.html) - [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)
``` ```
Copyright (C) 2020-2022 Fankes Studio(qzmmcn@163.com) Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU Affero General Public License as
the Free Software Foundation, either version 3 of the License, or published by the Free Software Foundation, either version 3 of the
(at your option) any later version. License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
``` ```
Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)<br/><br/>
版权所有 © 2019-2022 Fankes Studio(qzmmcn@163.com)

View File

@@ -1,6 +1,7 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'kotlin-android' id 'kotlin-android'
id 'com.google.devtools.ksp' version '1.6.10-1.0.2'
} }
android { android {
@@ -19,10 +20,10 @@ android {
defaultConfig { defaultConfig {
applicationId "com.fankes.miui.notify" applicationId "com.fankes.miui.notify"
minSdk 26 minSdk 28
targetSdk 26 targetSdk 31
versionCode 6 versionCode rootProject.ext.appVersionCode
versionName "1.3" versionName rootProject.ext.appVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -41,22 +42,40 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
} }
buildFeatures {
viewBinding true
}
}
/** 移除无效耗时 lint Task */
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
} }
dependencies { dependencies {
compileOnly 'de.robv.android.xposed:api:82' compileOnly 'de.robv.android.xposed:api:82'
// 基础依赖包 implementation 'com.highcapable.yukihookapi:api:1.0.4'
implementation 'com.gyf.immersionbar:immersionbar:3.0.0' ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.4'
// Fragment 快速实现 implementation "com.github.topjohnwu.libsu:core:3.1.2"
implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' implementation 'androidx.annotation:annotation:1.3.0'
// Kotlin 扩展 implementation 'com.geyifeng.immersionbar:immersionbar:3.2.0'
implementation 'com.gyf.immersionbar:immersionbar-ktx:3.0.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.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0' implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3' implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
//noinspection GradleDynamicVersion testImplementation 'junit:junit:4.13.2'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
} }

View File

@@ -3,6 +3,8 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="com.fankes.miui.notify"> package="com.fankes.miui.notify">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:name=".application.MNNApplication" android:name=".application.MNNApplication"
android:allowBackup="true" android:allowBackup="true"
@@ -13,48 +15,43 @@
android:theme="@style/Theme.MIUINativeNotifyIcon" android:theme="@style/Theme.MIUINativeNotifyIcon"
tools:ignore="AllowBackup"> tools:ignore="AllowBackup">
<!-- 是否是xposed模块 -->
<meta-data <meta-data
android:name="xposedmodule" android:name="xposedmodule"
android:value="true" /> android:value="true" />
<!-- 模块描述 -->
<meta-data <meta-data
android:name="xposeddescription" android:name="xposeddescription"
android:value="MIUI 状态栏原生图标,修复 12.5、13 后期被破坏的彩色图标。\n开发者酷安 @星夜不荟" /> android:value="为金凡教我做事的 MIUI 修复 12.5、13 后期被破坏的彩色图标。\n开发者酷安 @星夜不荟" />
<!-- 最低xposed版本号 -->
<meta-data <meta-data
android:name="xposedminversion" android:name="xposedminversion"
android:value="82" /> android:value="93" />
<activity <activity
android:name="com.fankes.miui.notify.ui.MainActivity" android:name=".ui.MainActivity"
android:exported="true" android:exported="true"
android:screenOrientation="behind"> android:screenOrientation="behind">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="de.robv.android.xposed.category.MODULE_SETTINGS" /> <category android:name="de.robv.android.xposed.category.MODULE_SETTINGS" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity-alias <activity-alias
android:name="com.fankes.miui.notify.Home" android:name=".Home"
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
android:screenOrientation="behind" android:screenOrientation="behind"
android:targetActivity="com.fankes.miui.notify.ui.MainActivity"> android:targetActivity=".ui.MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity-alias> </activity-alias>
<activity <activity
android:name="com.fankes.miui.notify.ui.ConfigureActivity" android:name=".ui.ConfigureActivity"
android:exported="false" android:exported="false"
android:screenOrientation="behind" /> android:screenOrientation="behind" />
</application> </application>

View File

@@ -1 +1 @@
com.fankes.miui.notify.hook.HookMain com.fankes.miui.notify.hook.HookEntry_YukiHookXposedInit

View File

@@ -0,0 +1 @@
com.fankes.miui.notify.hook.HookEntry

View File

@@ -1,22 +1,24 @@
/** /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/01/24. * This file is Created by fankes on 2022/1/24.
*/ */
@file:Suppress("unused") @file:Suppress("unused")

View File

@@ -1,20 +1,22 @@
/** /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/30. * This file is Created by fankes on 2022/1/30.
*/ */
@@ -26,9 +28,10 @@ import java.io.Serializable
/** /**
* 通知栏小图标 bean * 通知栏小图标 bean
* @param appName APP 名称 - 仅限默认语言区域 * @param appName APP 名称
* @param packageName 包名 * @param packageName 包名
* @param iconBitmap 图标位图 * @param iconBitmap 图标位图
* @param iconColor 通知栏中显示的图标颜色 - 设置为 0 使用系统默认颜色
* @param contributorName 贡献者昵称 * @param contributorName 贡献者昵称
* @param isEnabled 是否默认启用替换彩色图标 - 关闭后将全部停止替换 * @param isEnabled 是否默认启用替换彩色图标 - 关闭后将全部停止替换
* @param isEnabledAll 是否默认启用替换全部图标 * @param isEnabledAll 是否默认启用替换全部图标
@@ -37,10 +40,20 @@ data class IconDataBean(
var appName: String, var appName: String,
var packageName: String, var packageName: String,
var iconBitmap: Bitmap, var iconBitmap: Bitmap,
var iconColor: Int = 0,
var contributorName: String, var contributorName: String,
var isEnabled: Boolean, var isEnabled: Boolean,
var isEnabledAll: Boolean, var isEnabledAll: Boolean,
) : Serializable { ) : Serializable {
fun toEnabledName() = ("$appName$packageName").base64 + "_enable" fun toEnabledName() = ("$appName$packageName").base64 + "_enable"
fun toEnabledAllName() = ("$appName$packageName").base64 + "_enable_all" fun toEnabledAllName() = ("$appName$packageName").base64 + "_enable_all"
override fun toString() = "{\n" +
" \"appName\": \"$appName\",\n" +
" \"packageName\": \"$packageName\",\n" +
" \"iconBitmap\": \"${iconBitmap.base64}\",\n" +
" \"iconColor\": \"#${Integer.toHexString(iconColor)}\",\n" +
" \"contributorName\": \"$contributorName\",\n" +
" \"isEnabled\": $isEnabled,\n" +
" \"isEnabledAll\": $isEnabledAll\n" +
" }"
} }

View File

@@ -0,0 +1,45 @@
/*
* 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/1/24.
*/
@file:Suppress("DEPRECATION", "SetWorldReadable")
package com.fankes.miui.notify.hook
object HookConst {
const val ENABLE_MODULE = "_enable_module"
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 SOURCE_SYNC_WAY = "_source_sync_way"
const val SOURCE_SYNC_WAY_CUSTOM_URL = "_source_sync_way_custom_url"
const val TYPE_SOURCE_SYNC_WAY_1 = 1000
const val TYPE_SOURCE_SYNC_WAY_2 = 2000
const val TYPE_SOURCE_SYNC_WAY_3 = 3000
const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
}

View File

@@ -0,0 +1,613 @@
/*
* 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/15.
*/
package com.fankes.miui.notify.hook
import android.app.NotificationManager
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Outline
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.os.Build
import android.service.notification.StatusBarNotification
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
import com.fankes.miui.notify.hook.HookConst.ENABLE_NOTIFY_ICON_FIX
import com.fankes.miui.notify.hook.HookConst.SYSTEMUI_PACKAGE_NAME
import com.fankes.miui.notify.hook.factory.isAppNotifyHookAllOf
import com.fankes.miui.notify.hook.factory.isAppNotifyHookOf
import com.fankes.miui.notify.params.IconPackParams
import com.fankes.miui.notify.utils.*
import com.fankes.miui.notify.utils.drawable.drawabletoolbox.DrawableBuilder
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.factory.*
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.BooleanType
import com.highcapable.yukihookapi.hook.type.java.IntType
import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy
@InjectYukiHookWithXposed
class HookEntry : YukiHookXposedInitProxy {
companion object {
/** MIUI 新版本存在的类 */
private const val SystemUIApplicationClass = "$SYSTEMUI_PACKAGE_NAME.SystemUIApplication"
/** MIUI 新版本存在的类 */
private const val NotificationHeaderViewWrapperInjectorClass =
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapperInjector"
/** 原生存在的类 */
private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
/** 原生存在的类 */
private const val StatusBarIconViewClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.StatusBarIconView"
/** 根据多个版本存在不同的包名相同的类 */
private val ExpandableNotificationRowClass = VariousClass(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.ExpandableNotificationRow",
"$SYSTEMUI_PACKAGE_NAME.statusbar.ExpandableNotificationRow"
)
/** 根据多个版本存在不同的包名相同的类 */
private val NotificationViewWrapperClass = VariousClass(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationViewWrapper",
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationViewWrapper"
)
/** 根据多个版本存在不同的包名相同的类 */
private val NotificationHeaderViewWrapperClass = VariousClass(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper",
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationHeaderViewWrapper"
)
/** 根据多个版本存在不同的包名相同的类 */
private val NotificationUtilClass = VariousClass(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationUtil",
"$SYSTEMUI_PACKAGE_NAME.miui.statusbar.notification.NotificationUtil"
)
/** 根据多个版本存在不同的包名相同的类 */
private val ExpandedNotificationClass = VariousClass(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.ExpandedNotification",
"$SYSTEMUI_PACKAGE_NAME.miui.statusbar.ExpandedNotification"
)
}
/** 缓存的通知优化图标数组 */
private var iconDatas = ArrayList<IconDataBean>()
/**
* - 这个是修复彩色图标的关键核心代码判断
*
* 判断是否为灰度图标 - 反射执行系统方法
* @param context 实例
* @param drawable 要判断的图标
* @return [Boolean]
*/
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 方案
*
* 拥有 “handleHeaderViews” 方法
* @return [Boolean]
*/
private val PackageParam.hasHandleHeaderViews
get() = safeOfFalse {
NotificationHeaderViewWrapperClass.clazz.hasMethod(name = "handleHeaderViews")
}
/**
* 获取当前通知栏的样式
*
* - ❗新版本可能不存在这个方法
* @return [Boolean]
*/
private val PackageParam.isShowMiuiStyle
get() = safeOfFalse {
NotificationUtilClass.clazz.method { name = "showMiuiStyle" }.get().invoke() ?: false
}
/**
* 适配通知栏、状态栏图标
*
* 适配第三方图标包对系统包管理器更换图标后的彩色图标
*
* 自动识别 MIPUSH 图标
* @param context 实例
* @param iconDrawable 原始图标
* @return [Drawable] 适配的图标
*/
private fun StatusBarNotification.compatNotifyIcon(context: Context, iconDrawable: Drawable) = safeOf(iconDrawable) {
/** 给 MIPUSH 设置 APP 自己的图标 */
if (isXmsf && opPkgName.isNotBlank())
findAppIcon(context)
else iconDrawable
}
/**
* 获取推送通知的应用名称
* @param context 实例
* @return [String]
*/
private fun StatusBarNotification.findAppName(context: Context) = safeOf(default = "<unknown>") {
context.packageManager.getPackageInfo(opPkgName, 0).applicationInfo.loadLabel(context.packageManager)
}
/**
* 获取通知栏、状态栏 APP 图标
* @param context 实例
* @return [Drawable] 适配的图标
*/
private fun StatusBarNotification.findAppIcon(context: Context) = safeOf(notification.smallIcon.loadDrawable(context)) {
context.packageManager.getPackageInfo(opPkgName, 0).applicationInfo.loadIcon(context.packageManager)
}
/**
* 打印日志
* @param tag 标识
* @param context 实例
* @param expandedNf 通知实例
* @param isCustom 是否为通知优化生效图标
* @param isGrayscale 是否为灰度图标
*/
private fun PackageParam.printLogcat(
tag: String,
context: Context,
expandedNf: StatusBarNotification?,
isCustom: Boolean,
isGrayscale: Boolean
) {
if (prefs.getBoolean(ENABLE_MODULE_LOG)) loggerD(
msg = "$tag --> [${expandedNf?.findAppName(context)}][${expandedNf?.opPkgName}] " +
"custom [$isCustom] " +
"grayscale [$isGrayscale] " +
"xmsf [${expandedNf?.isXmsf}]"
)
}
/**
* 获取推送通知的包名
*
* 自动兼容旧版本系统
* @return [String]
*/
private val StatusBarNotification.compatOpPkgName
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) opPkg else packageName ?: ""
/**
* 判断通知是否来自 MIPUSH
* @return [Boolean]
*/
private val StatusBarNotification.isXmsf get() = compatOpPkgName == "com.xiaomi.xmsf"
/**
* 获取推送通知的包名
*
* 自动判断 MIPUSH
* @return [String]
*/
private val StatusBarNotification.opPkgName get() = if (isXmsf) xmsfPkgName else compatOpPkgName
/**
* 获取 MIPUSH 通知真实包名
* @return [String]
*/
private val StatusBarNotification.xmsfPkgName: String
get() {
val xmsfPkg = notification.extras.getString("xmsf_target_package") ?: ""
val targetPkg = notification.extras.getString("target_package") ?: ""
return xmsfPkg.ifBlank { targetPkg.ifBlank { compatOpPkgName } }
}
/**
* 获取全局上下文
* @return [Context] or null
*/
private val PackageParam.globalContext
get() = safeOfNull {
SystemUIApplicationClass.clazz.method { name = "getContext" }.ignoredError().get().invoke<Context>()
}
/**
* 自动适配状态栏、通知栏自定义小图标
* @param isGrayscaleIcon 是否为灰度图标
* @param packageName APP 包名
* @return [Pair] - ([Bitmap] 位图,[Int] 颜色)
*/
private fun PackageParam.compatCustomIcon(isGrayscaleIcon: Boolean, packageName: String): Pair<Bitmap?, Int> {
var customPair: Pair<Bitmap?, Int>? = null
if (prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true))
run {
if (iconDatas.isNotEmpty())
iconDatas.forEach {
if (packageName == it.packageName && isAppNotifyHookOf(it)) {
if (!isGrayscaleIcon || isAppNotifyHookAllOf(it))
customPair = Pair(it.iconBitmap, it.iconColor)
return@run
}
}
}
return customPair ?: Pair(null, 0)
}
/**
* Hook 状态栏小图标
*
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
* @param context 实例
* @param expandedNf 通知实例
* @param iconDrawable 小图标 [Drawable]
* @param it 回调小图标 - ([Bitmap] 小图标)
*/
private fun PackageParam.hookSmallIconOnSet(
context: Context,
expandedNf: StatusBarNotification?,
iconDrawable: Drawable?,
it: (Bitmap) -> Unit
) = safeRun(msg = "GetSmallIconOnSet") {
if (iconDrawable == null) return@safeRun
/** 如果没开启修复 APP 的彩色图标 */
if (!prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) return@safeRun
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.also { notifyInstance ->
/** 判断是 MIUI 样式就停止 Hook */
if (context.isMiuiNotifyStyle) {
it(notifyInstance.findAppIcon(context).toBitmap())
return@safeRun
}
/** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = notifyInstance.isXmsf || !isGrayscaleIcon(context, iconDrawable)
/** 目标彩色通知 APP 图标 */
val customIcon = compatCustomIcon(!isNotGrayscaleIcon, notifyInstance.opPkgName).first
/** 打印日志 */
printLogcat(tag = "StatusIcon", context, notifyInstance, isCustom = customIcon != null, !isNotGrayscaleIcon)
when {
/** 处理自定义通知图标优化 */
customIcon != null -> it(customIcon)
/** 若不是灰度图标自动处理为圆角 */
isNotGrayscaleIcon -> it(notifyInstance.compatNotifyIcon(context, iconDrawable).toBitmap())
}
}
}
/**
* Hook 通知栏小图标
*
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
* @param context 实例
* @param expandedNf 通知实例
* @param iconImageView 通知图标实例
* @param isExpanded 通知是否展开 - 可做最小化通知处理
*/
private fun PackageParam.hookNotifyIconOnSet(
context: Context,
expandedNf: StatusBarNotification?,
iconImageView: ImageView,
isExpanded: Boolean
) = safeRun(msg = "AutoSetAppIconOnSet") {
/** 判断是 MIUI 样式就停止 Hook */
if (context.isMiuiNotifyStyle) return@safeRun
/** 如果没开启修复 APP 的彩色图标 */
if (!prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) return@safeRun
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.let { notifyInstance ->
/** 新版风格反色 */
val newStyle = if (context.isSystemInDarkMode) 0xFF2D2D2D.toInt() else Color.WHITE
/** 旧版风格反色 */
val oldStyle = if (context.isNotSystemInDarkMode) 0xFF707070.toInt() else Color.WHITE
/** 通知图标原始颜色 */
val iconColor = notifyInstance.notification.color
/** 是否有通知栏图标颜色 */
val hasIconColor = iconColor != 0
/** 通知图标适配颜色 */
val supportColor = iconColor.let {
when {
isUpperOfAndroidS -> newStyle
it == 0 || !isExpanded -> oldStyle
else -> it
}
}
/** 获取通知小图标 */
val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
/** 判断图标风格 */
val isGrayscaleIcon = !notifyInstance.isXmsf && isGrayscaleIcon(context, iconDrawable)
/** 自定义默认小图标 */
var customIcon: Bitmap?
/** 自定义默认小图标颜色 */
var customIconColor: Int
compatCustomIcon(isGrayscaleIcon, notifyInstance.opPkgName).also {
customIcon = it.first
customIconColor = if (isUpperOfAndroidS || isExpanded) it.second else 0
}
/** 打印日志 */
printLogcat(tag = "NotifyIcon", context, notifyInstance, isCustom = customIcon != null, isGrayscaleIcon)
/** 处理自定义通知图标优化 */
if (customIcon != null)
iconImageView.apply {
/** 设置自定义小图标 */
setImageBitmap(customIcon)
/** 上色 */
setColorFilter(if (isUpperOfAndroidS || customIconColor == 0) supportColor else customIconColor)
/** Android 12 设置图标外圈颜色 */
if (isUpperOfAndroidS && customIconColor != 0)
background = DrawableBuilder().rounded().solidColor(customIconColor).build()
}
else {
/** 重新设置图标 - 防止系统更改它 */
iconImageView.setImageDrawable(iconDrawable)
/** 判断如果是灰度图标就给他设置一个白色颜色遮罩 */
if (isGrayscaleIcon) iconImageView.apply {
/** 设置图标着色 */
setColorFilter(supportColor)
/** Android 12 设置图标外圈颜色 */
if (isUpperOfAndroidS && hasIconColor)
background = DrawableBuilder().rounded().solidColor(iconColor).build()
} else iconImageView.apply {
/** 重新设置图标 */
setImageDrawable(notifyInstance.compatNotifyIcon(context, iconDrawable))
/** 设置裁切到边界 */
clipToOutline = true
/** 设置一个圆角轮廓裁切 */
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, out: Outline) {
out.setRoundRect(
0, 0,
view.width, view.height, 5.dp(context)
)
}
}
/** 清除原生的背景边距设置 */
if (isUpperOfAndroidS) setPadding(0, 0, 0, 0)
/** 清除原生的主题色背景圆圈颜色 */
if (isUpperOfAndroidS) background = null
}
}
}
}
/**
* 判断状态栏小图标颜色以及反射的核心方法
*
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
* @param context 实例
* @param expandedNf 状态栏实例
* @return [Boolean] 是否忽略通知图标颜色
*/
private fun PackageParam.hasIgnoreStatusBarIconColor(context: Context, expandedNf: StatusBarNotification?) =
if (!context.isMiuiNotifyStyle)
if (prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) safeOfFalse {
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.let { notifyInstance ->
/** 获取通知小图标 */
val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
/** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = notifyInstance.isXmsf || !isGrayscaleIcon(context, iconDrawable)
/** 获取目标修复彩色图标的 APP */
val isTargetFixApp = compatCustomIcon(!isNotGrayscaleIcon, notifyInstance.opPkgName).first != null
/**
* 只要不是灰度就返回彩色图标
* 否则不对颜色进行反色处理防止一些系统图标出现异常
*/
(if (isTargetFixApp) false else isNotGrayscaleIcon).also {
printLogcat(tag = "IconColor", context, expandedNf, isTargetFixApp, !isNotGrayscaleIcon)
}
} ?: true.also { printLogcat(tag = "IconColor", context, expandedNf, isCustom = false, isGrayscale = false) }
} else false.also { printLogcat(tag = "IconColor", context, expandedNf, isCustom = false, isGrayscale = true) }
else true.also { printLogcat(tag = "IconColor", context, expandedNf, isCustom = false, isGrayscale = false) }
override fun onHook() {
runConfig()
runHook()
}
/** 配置 Hook */
private fun runConfig() = configs {
debugTag = "MIUINativeNotifyIcon"
isDebug = false
}
/** 开始 Hook */
private fun runHook() = encase {
loadApp(SYSTEMUI_PACKAGE_NAME) {
when {
/** 不是 MIUI 系统停止 Hook */
isNotMIUI -> loggerW(msg = "Aborted Hook -> This System is not MIUI")
/** 系统版本低于 Android P 停止 Hook */
isLowerAndroidP -> loggerW(msg = "Aborted Hook -> This System is lower than Android P")
/** 不是支持的 MIUI 系统停止 Hook */
isNotSupportMiuiVersion -> loggerW(msg = "Aborted Hook -> This MIUI Version $miuiVersion not supported")
/** Hook 被手动关闭停止 Hook */
!prefs.getBoolean(ENABLE_MODULE, default = true) -> loggerW(msg = "Aborted Hook -> Hook Closed")
/** 开始 Hook */
else -> {
/** 缓存图标数据 */
iconDatas = IconPackParams(param = this).iconDatas
/** 执行 Hook */
NotificationUtilClass.hook {
/** 强制回写系统的状态栏图标样式为原生 */
injectMember {
method {
name = "shouldSubstituteSmallIcon"
param(ExpandedNotificationClass.clazz)
}
/**
* 为了防止 MIUI 自身的版本不同造成的各种 BUG
* 判断是 MIUI 样式就停止 Hook
*/
replaceAny { globalContext?.isMiuiNotifyStyle ?: isShowMiuiStyle }
}
/** 强制回写系统的状态栏图标样式为原生 */
injectMember {
var isUseLegacy = false
method {
name = "getSmallIcon"
param(ExpandedNotificationClass.clazz, IntType)
}.remedys {
method {
name = "getSmallIcon"
param(ExpandedNotificationClass.clazz)
}
method {
name = "getSmallIcon"
param(ContextClass, ExpandedNotificationClass.clazz)
}.onFind { isUseLegacy = true }
}
afterHook {
(globalContext ?: firstArgs as Context).also { context ->
hookSmallIconOnSet(
context = context,
args[if (isUseLegacy) 1 else 0] as? StatusBarNotification?,
(result as Icon).loadDrawable(context)
) { icon -> result = Icon.createWithBitmap(icon) }
}
}
}
}
StatusBarIconViewClass.hook {
/** Hook 状态栏图标的颜色 */
injectMember {
method { name = "updateIconColor" }
afterHook {
instance<ImageView>().also {
if (hasIgnoreStatusBarIconColor(it.context, field { name = "mNotification" }
.of<StatusBarNotification>(instance))) it.colorFilter = null
else it.setColorFilter(field { name = "mCurrentSetColor" }.of<Int>(instance) ?: 0)
}
}
}
}
NotificationHeaderViewWrapperClass.hook {
/** 修复下拉通知图标自动设置回 APP 图标的方法 */
injectMember {
if (hasHandleHeaderViews)
method { name = "handleHeaderViews" }
else method { name = "resolveHeaderViews" }
afterHook {
/** 获取小图标 */
val iconImageView =
NotificationHeaderViewWrapperClass.clazz
.field { name = "mIcon" }.of<ImageView>(instance) ?: return@afterHook
/** 通知是否展开 */
var isExpanded = false
/**
* 从父类中得到 mRow 变量 - [ExpandableNotificationRowClass]
* 获取其中的得到通知方法
*/
val expandedNf = ExpandableNotificationRowClass.clazz
.method { name = "getEntry" }
.get(NotificationViewWrapperClass.clazz.field {
name = "mRow"
}.get(instance).self?.also {
isExpanded = ExpandableNotificationRowClass.clazz.method {
name = "isExpanded"
returnType = BooleanType
}.get(it).invoke<Boolean>() == true
}).call()?.let {
it.javaClass.method {
name = "getSbn"
}.get(it).invoke<StatusBarNotification>()
} ?: ExpandableNotificationRowClass.clazz
.method { name = "getStatusBarNotification" }
.get(NotificationViewWrapperClass.clazz.field { name = "mRow" }.get(instance).self)
.invoke<StatusBarNotification>()
/** 获取优先级 */
val importance =
(iconImageView.context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager?)
?.getNotificationChannel(expandedNf?.notification?.channelId)?.importance ?: 0
/** 非最小化优先级的通知全部设置为展开状态 */
if (importance != 1) isExpanded = true
/** 执行 Hook */
hookNotifyIconOnSet(iconImageView.context, expandedNf, iconImageView, isExpanded)
}
}
}
/** 干掉下拉通知图标自动设置回 APP 图标的方法 */
NotificationHeaderViewWrapperInjectorClass.hook {
injectMember {
method {
name = "setAppIcon"
param(ContextClass, ImageViewClass, ExpandedNotificationClass.clazz)
}.remedys {
method {
name = "setAppIcon"
param(ImageViewClass, ExpandedNotificationClass.clazz)
}
}
intercept()
}.ignoredHookingFailure()
injectMember {
method {
name = "resetIconBgAndPaddings"
param(ImageViewClass, ExpandedNotificationClass.clazz)
}
intercept()
}.ignoredHookingFailure()
}.ignoredHookClassNotFoundFailure()
}
}
}
}
}

View File

@@ -1,790 +0,0 @@
/**
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com)
*
* This file is part of MIUINativeNotifyIcon.
*
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by fankes on 2022/01/24.
*/
@file:Suppress("SameParameterValue", "DEPRECATION")
package com.fankes.miui.notify.hook
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Outline
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.service.notification.StatusBarNotification
import android.util.Log
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.ImageView
import androidx.annotation.Keep
import androidx.core.graphics.drawable.toBitmap
import com.fankes.miui.notify.hook.HookMedium.SELF_PACKAGE_NAME
import com.fankes.miui.notify.hook.HookMedium.SYSTEMUI_PACKAGE_NAME
import com.fankes.miui.notify.params.IconPackParams
import com.fankes.miui.notify.utils.*
import de.robv.android.xposed.*
import de.robv.android.xposed.callbacks.XC_LoadPackage
@Keep
class HookMain : IXposedHookLoadPackage {
companion object {
/** 一直存在的类 */
private const val SystemUIApplicationClass = "$SYSTEMUI_PACKAGE_NAME.SystemUIApplication"
/** MIUI 新版本存在的类 */
private const val NotificationHeaderViewWrapperInjectorClass =
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapperInjector"
/** 一直存在的类 */
private const val NotificationHeaderViewWrapperClass =
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationHeaderViewWrapper"
/** 一直存在的类 */
private const val NotificationViewWrapperClass =
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationViewWrapper"
/** 原生存在的类 */
private const val StatusBarIconViewClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.StatusBarIconView"
/** 原生存在的类 */
private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
/** 根据多个版本存在不同的包名相同的类 */
private val NotificationUtilClass = Pair(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationUtil",
"$SYSTEMUI_PACKAGE_NAME.miui.statusbar.notification.NotificationUtil"
)
/** 根据多个版本存在不同的包名相同的类 */
private val ExpandedNotificationClass = Pair(
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.ExpandedNotification",
"$SYSTEMUI_PACKAGE_NAME.miui.statusbar.ExpandedNotification"
)
}
/** 仅作用于替换的 Hook 方法体 */
private val replaceToNull = object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam?): Any? {
return null
}
}
/** 仅作用于替换的 Hook 方法体 */
private val replaceToTrue = object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam?): Any {
return true
}
}
/**
* 忽略异常运行
* @param error 错误信息
* @param it 正常回调
*/
private fun runWithoutError(error: String = "", it: () -> Unit) {
try {
it()
} catch (e: Throwable) {
logE(content = "hookFailed: $error", e)
}
}
/**
* Print the log
* @param content
* @param it 继续执行的方法
*/
private fun logD(content: String, it: () -> Unit = {}) {
it()
if (!HookMedium.getBoolean(HookMedium.ENABLE_MODULE_LOG, default = false)) return
XposedBridge.log("[MIUINativeNotifyIcon][D]>$content")
Log.d("MIUINativeNotifyIcon", content)
}
/**
* Print the log
* @param content
* @param it 继续执行的方法
*/
private fun logW(content: String, it: () -> Unit = {}) {
it()
if (!HookMedium.getBoolean(HookMedium.ENABLE_MODULE_LOG, default = false)) return
XposedBridge.log("[MIUINativeNotifyIcon][W]>$content")
Log.d("MIUINativeNotifyIcon", content)
}
/**
* Print the log
* @param content
* @param e 异常
* @param it 继续执行的方法
*/
private fun logE(content: String, e: Throwable? = null, it: () -> Unit = {}) {
it()
XposedBridge.log("[MIUINativeNotifyIcon][E]>$content")
XposedBridge.log(e)
Log.e("MIUINativeNotifyIcon", content, e)
}
/**
* 目标类是否存在
* @param name 类名
* @return [Boolean]
*/
private fun XC_LoadPackage.LoadPackageParam.isClassExist(name: String) = try {
classLoader.loadClass(name)
true
} catch (_: Throwable) {
false
}
/**
* 目标方法是否存在
* @param classPair 类数组
* @param name 方法名
* @param param 方法参数类型数组
* @return [Boolean]
*/
private fun XC_LoadPackage.LoadPackageParam.isMethodExist(
classPair: Pair<String, String>,
name: String, vararg param: Class<*>
) = try {
(try {
classLoader.loadClass(classPair.first)
} catch (_: Throwable) {
try {
classLoader.loadClass(classPair.second)
} catch (_: Throwable) {
null
}
})?.getDeclaredMethod(name, *param)
true
} catch (_: Throwable) {
false
}
/**
* 目标方法是否存在
* @param className 类名
* @param name 方法名
* @param param 方法参数类型数组
* @return [Boolean]
*/
private fun XC_LoadPackage.LoadPackageParam.isMethodExist(className: String, name: String, vararg param: Class<*>) =
try {
(try {
classLoader.loadClass(className)
} catch (_: Throwable) {
null
})?.getDeclaredMethod(name, *param)
true
} catch (_: Throwable) {
false
}
/**
* 查找目标类
* @param name 类名
* @return [Class]
*/
private fun XC_LoadPackage.LoadPackageParam.findClass(name: String) =
classLoader.loadClass(name)
/**
* 查找目标类 - 两个类都没找到才会报错
* @param pair 类名数组
* @return [Class]
*/
private fun XC_LoadPackage.LoadPackageParam.findClass(pair: Pair<String, String>) = try {
classLoader.loadClass(pair.first)
} catch (_: Throwable) {
try {
classLoader.loadClass(pair.second)
} catch (e: Throwable) {
logE(content = "Cannot find Class ${pair.first} and ${pair.second}", e)
error("[Throwable] Cannot find Class ${pair.first} and ${pair.second}")
}
}
/**
* 存在目标类的类名 - 两个类都没找到会抛出异常
* @param pair 类名数组
* @return [String] 目标类名
*/
private fun XC_LoadPackage.LoadPackageParam.existClass(pair: Pair<String, String>) = try {
classLoader.loadClass(pair.first)
pair.first
} catch (_: Throwable) {
try {
classLoader.loadClass(pair.second)
pair.second
} catch (_: Throwable) {
logE(content = "Cannot find Class ${pair.first} and ${pair.second}")
error("[Throwable] Cannot find Class ${pair.first} and ${pair.second}")
}
}
/**
* - 这个是修复彩色图标的关键核心代码判断
*
* 判断是否为灰度图标 - 反射执行系统方法
* @param context 实例
* @param icon 要判断的图标
* @return [Boolean]
*/
private fun XC_LoadPackage.LoadPackageParam.isGrayscaleIcon(context: Context, icon: Drawable) =
findClass(ContrastColorUtilClass).let {
val instance = it.getDeclaredMethod("getInstance", Context::class.java)
.apply { isAccessible = true }.invoke(null, context)
it.getDeclaredMethod("isGrayscaleIcon", Drawable::class.java)
.apply { isAccessible = true }.invoke(instance, icon) as Boolean
}
/**
* 获取当前通知栏的样式
*
* 判断是否为灰度图标 - 反射执行系统方法
* @return [Boolean]
*/
private fun XC_LoadPackage.LoadPackageParam.isShowMiuiStyle() = try {
findClass(NotificationUtilClass).let {
it.getDeclaredMethod("showMiuiStyle")
.apply { isAccessible = true }.invoke(null) as Boolean
}
} catch (_: Throwable) {
false
}
/**
* 是否为新版本 MIUI 方案
*
* 拥有状态栏图标颜色检查功能
* @return [Boolean]
*/
private fun XC_LoadPackage.LoadPackageParam.hasIgnoreStatusBarIconColor() =
isMethodExist(NotificationUtilClass, name = "ignoreStatusBarIconColor")
/**
* 获取 [ExpandedNotificationClass] 的应用名称
* @param instance 通知实例
* @return [String]
*/
private fun XC_LoadPackage.LoadPackageParam.findAppName(instance: Any?) = try {
findClass(ExpandedNotificationClass).getDeclaredMethod("getAppName").let {
it.isAccessible = true
it.invoke(instance) as? String ?: "unknown"
}
} catch (_: Throwable) {
"unknown"
}
/**
* 获取全局上下文
* @return [Context] or null
*/
private val XC_LoadPackage.LoadPackageParam.globalContext
get() = try {
findClass(SystemUIApplicationClass)
.getDeclaredMethod("getContext").apply { isAccessible = true }
.invoke(null) as? Context?
} catch (_: Throwable) {
null
}
/**
* Hook 状态栏小图标
*
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
* @param context 实例
* @param expandedNf 通知实例
* @param param Hook Param
*/
private fun XC_LoadPackage.LoadPackageParam.hookSmallIconOnSet(
context: Context,
expandedNf: StatusBarNotification?,
param: XC_MethodHook.MethodHookParam
) {
runWithoutError(error = "GetSmallIconOnSet") {
/** 获取通知小图标 */
val iconDrawable = (param.result as Icon).loadDrawable(context)
/** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable)
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.also { notifyInstance ->
/** 目标彩色通知 APP 图标 */
var customIcon: Icon? = null
if (HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true))
run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
HookMedium.isAppNotifyHookOf(it)
) {
if (isNotGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it))
customIcon = Icon.createWithBitmap(it.iconBitmap)
return@run
}
}
}
when {
/** 如果开启了修复 APP 的彩色图标 */
customIcon != null && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true) ->
logD(
content = "GetSmallIconOnSet -> " +
"hook Custom AppIcon [pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${findAppName(notifyInstance)}"
) { param.result = customIcon }
/** 若不是灰度图标自动处理为圆角 */
isNotGrayscaleIcon ->
logD(
content = "GetSmallIconOnSet -> " +
"hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${findAppName(notifyInstance)}"
) {
param.result = Icon.createWithBitmap(
iconDrawable.toBitmap().round(15.dp(context))
)
}
}
} ?: logW(content = "GetSmallIconOnSet -> StatusBarNotification got null")
}
}
/**
* Hook 通知栏小图标
*
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
* @param context 实例
* @param expandedNf 通知实例
* @param iconImageView 通知图标实例
*/
private fun XC_LoadPackage.LoadPackageParam.hookNotifyIconOnSet(
context: Context,
expandedNf: StatusBarNotification?,
iconImageView: ImageView
) {
runWithoutError(error = "AutoSetAppIconOnSet") {
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.let { notifyInstance ->
/** 是否 Hook 彩色通知图标 */
val isHookColorIcon = HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true)
/** 新版风格反色 */
val newStyle = if (context.isSystemInDarkMode) 0xFF2D2D2D.toInt() else Color.WHITE
/** 旧版风格反色 */
val oldStyle = if (context.isNotSystemInDarkMode) 0xFF707070.toInt() else Color.WHITE
/** 获取通知小图标 */
val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
/** 判断图标风格 */
val isGrayscaleIcon = isGrayscaleIcon(context, iconDrawable)
/** 自定义默认小图标 */
var customIcon: Bitmap? = null
if (isHookColorIcon)
run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
HookMedium.isAppNotifyHookOf(it)
) {
if (!isGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it))
customIcon = it.iconBitmap
return@run
}
}
}
/** 如果开启了修复 APP 的彩色图标 */
if (customIcon != null && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true))
iconImageView.apply {
/** 设置自定义小图标 */
setImageBitmap(customIcon)
/** 上色 */
setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle)
/** 输出调试日志 */
logD(
content = "AutoSetAppIconOnSet -> " +
"hook Custom AppIcon [pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${findAppName(notifyInstance)}"
)
}
else {
/** 重新设置图标 - 防止系统更改它 */
iconImageView.setImageDrawable(iconDrawable)
/** 判断是否开启 Hook 彩色图标 */
if (isHookColorIcon) {
/** 判断如果是灰度图标就给他设置一个白色颜色遮罩 */
if (isGrayscaleIcon)
logD(
content = "AutoSetAppIconOnSet -> " +
"hook Grayscale AppIcon [pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${findAppName(notifyInstance)}"
) { iconImageView.setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle) }
else
iconImageView.apply {
clipToOutline = true
/** 设置一个圆角轮廓裁切 */
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, out: Outline) {
out.setRoundRect(
0, 0,
view.width, view.height, 5.dp(context)
)
}
}
/** 清除原生的背景边距设置 */
if (isUpperOfAndroidS) setPadding(0, 0, 0, 0)
/** 清除原生的主题色背景圆圈颜色 */
if (isUpperOfAndroidS) background = null
/** 输出调试日志 */
logD(
content = "AutoSetAppIconOnSet -> " +
"hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${findAppName(notifyInstance)}"
)
}
/** 否则一律设置灰度图标 */
} else
logD(
content = "AutoSetAppIconOnSet -> " +
"hook NonColor AppIcon [pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${findAppName(notifyInstance)}"
) { iconImageView.setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle) }
}
} ?: logW(content = "AutoSetAppIconOnSet -> StatusBarNotification got null")
}
}
/**
* Hook 通知栏小图标颜色
*
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
* @param context 实例
* @param expandedNf 状态栏实例
* @return [Boolean] 是否忽略通知图标颜色
*/
private fun XC_LoadPackage.LoadPackageParam.hookIgnoreStatusBarIconColor(
context: Context,
expandedNf: StatusBarNotification?
) = if (HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true))
try {
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.let { notifyInstance ->
/** 获取通知小图标 */
val iconDrawable =
notifyInstance.notification.smallIcon.loadDrawable(context)
/** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable)
/** 获取目标修复彩色图标的 APP */
var isTargetApp = false
run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
HookMedium.isAppNotifyHookOf(it)
) {
if (isNotGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it)) isTargetApp = true
return@run
}
}
}
/** 如果开启了修复 APP 的彩色图标 */
if (isTargetApp && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true)) let {
logD(
content = "IgnoreStatusBarIconColor -> " +
"hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${findAppName(notifyInstance)}"
)
false
}
else let {
logD(
content = "IgnoreStatusBarIconColor -> " +
"hook Grayscale[${!isNotGrayscaleIcon}] AppIcon " +
"[pkgName] ${notifyInstance.opPkgName} " +
"[appName] ${findAppName(notifyInstance)}"
)
/** 只要不是灰度就返回彩色图标 */
isNotGrayscaleIcon
}
} ?: let {
logW(content = "IgnoreStatusBarIconColor -> StatusBarNotification got null")
/** 否则不对颜色进行反色处理防止一些系统图标出现异常 */
true
}
} catch (e: Exception) {
logE("Failed to hook ignoreStatusBarIconColor", e)
false
}
else let {
logD(content = "IgnoreStatusBarIconColor -> hook NonColor AppIcon")
false
}
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
if (lpparam == null) return
when (lpparam.packageName) {
/** Hook 自身 */
SELF_PACKAGE_NAME ->
runWithoutError(error = "HookModuleSelf") {
XposedHelpers.findAndHookMethod(
"$SELF_PACKAGE_NAME.hook.HookMedium",
lpparam.classLoader,
"isHooked",
replaceToTrue
)
}
/** Hook 系统 UI */
SYSTEMUI_PACKAGE_NAME ->
when {
/** 不是 MIUI 系统停止 Hook */
isNotMIUI ->
logW(content = "Aborted Hook -> This System is not MIUI")
/** 系统版本低于 Android P 停止 Hook */
isLowerAndroidP ->
logW(content = "Aborted Hook -> This System is lower than Android P")
/** 不是支持的 MIUI 系统停止 Hook */
isNotSupportMiuiVersion ->
logW(content = "Aborted Hook -> This MIUI Version $miuiVersion not supported")
/** Hook 被手动关闭停止 Hook */
!HookMedium.getBoolean(HookMedium.ENABLE_MODULE, default = true) ->
logW(content = "Aborted Hook -> Hook Closed")
else -> {
/** 强制回写系统的状态栏图标样式为原生 */
runWithoutError(error = "SubstituteSmallIcon") {
XposedHelpers.findAndHookMethod(
lpparam.existClass(NotificationUtilClass),
lpparam.classLoader,
"shouldSubstituteSmallIcon",
lpparam.findClass(ExpandedNotificationClass),
object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam) =
/**
* 因为之前的 MIUI 版本的状态栏图标颜色会全部设置为白色的 - 找不到修复的地方就直接判断版本了
* 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook
*/
if (lpparam.hasIgnoreStatusBarIconColor()) false else lpparam.isShowMiuiStyle()
}
)
}
/** 修复通知图标为彩色 */
if (lpparam.hasIgnoreStatusBarIconColor())
runWithoutError(error = "IgnoreStatusBarIconColor") {
XposedHelpers.findAndHookMethod(
lpparam.existClass(NotificationUtilClass),
lpparam.classLoader,
"ignoreStatusBarIconColor",
lpparam.findClass(ExpandedNotificationClass),
object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam) =
lpparam.hookIgnoreStatusBarIconColor(
context = lpparam.globalContext ?: error("GlobalContext got null"),
param.args?.get(0) as? StatusBarNotification?
)
}
)
}
/** 之前的版本解决方案 */
else runWithoutError(error = "UpdateIconColor") {
XposedHelpers.findAndHookMethod(
StatusBarIconViewClass,
lpparam.classLoader, "updateIconColor",
object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) =
runWithoutError(error = "UpdateIconColorOnSet") hook@{
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
if (lpparam.isShowMiuiStyle()) return@hook
/** 获取自身 */
val iconImageView = param.thisObject as ImageView
/** 是否忽略图标颜色 */
val isIgnoredColor = lpparam.hookIgnoreStatusBarIconColor(
iconImageView.context,
param.thisObject.javaClass.getDeclaredField("mNotification").apply {
isAccessible = true
}[param.thisObject] as? StatusBarNotification?
)
/** 当前着色颜色 */
val currentColor =
param.thisObject.javaClass.getDeclaredField("mCurrentSetColor").apply {
isAccessible = true
}[param.thisObject] as? Int ?: Color.WHITE
/** 判断并设置颜色 */
if (isIgnoredColor)
iconImageView.colorFilter = null
else iconImageView.setColorFilter(currentColor)
logD(content = "IgnoreStatusBarIconColor[UseOldWay] -> isIgnored[$isIgnoredColor]")
}
}
)
}
/** 强制回写系统的状态栏图标样式为原生 */
runWithoutError(error = "GetSmallIcon") {
var isTooOld: Boolean
try {
isTooOld = false
/** 新版方法 */
lpparam.findClass(NotificationUtilClass)
.getDeclaredMethod(
"getSmallIcon",
lpparam.findClass(ExpandedNotificationClass),
Int::class.java
).apply { isAccessible = true }
} catch (_: Throwable) {
try {
isTooOld = false
/** 旧版方法 */
lpparam.findClass(NotificationUtilClass)
.getDeclaredMethod("getSmallIcon", lpparam.findClass(ExpandedNotificationClass))
.apply { isAccessible = true }
} catch (_: Throwable) {
isTooOld = true
/** 超旧版方法 */
lpparam.findClass(NotificationUtilClass)
.getDeclaredMethod(
"getSmallIcon",
Context::class.java,
lpparam.findClass(ExpandedNotificationClass)
).apply { isAccessible = true }
}
}.also {
XposedBridge.hookMethod(it, object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
if (!lpparam.hasIgnoreStatusBarIconColor() && lpparam.isShowMiuiStyle()) return
lpparam.hookSmallIconOnSet(
context = lpparam.globalContext ?: param.args[0] as Context,
param.args?.get(if (isTooOld) 1 else 0) as? StatusBarNotification?, param
)
}
})
}
}
/** 修复下拉通知图标自动设置回 APP 图标的方法 */
if (lpparam.isClassExist(NotificationHeaderViewWrapperInjectorClass))
runWithoutError(error = "AutoSetAppIcon") {
var isNewWay = true
try {
/** 新版方法 */
lpparam.findClass(NotificationHeaderViewWrapperInjectorClass)
.getDeclaredMethod(
"setAppIcon",
Context::class.java,
ImageView::class.java,
lpparam.findClass(ExpandedNotificationClass)
).apply { isAccessible = true }
} catch (_: Throwable) {
isNewWay = false
/** 旧版方法 */
lpparam.findClass(NotificationHeaderViewWrapperInjectorClass)
.getDeclaredMethod(
"setAppIcon",
ImageView::class.java,
lpparam.findClass(ExpandedNotificationClass)
).apply { isAccessible = true }
}.also {
XposedBridge.hookMethod(it, object : XC_MethodReplacement() {
override fun replaceHookedMethod(param: MethodHookParam): Any? {
if (isNewWay)
lpparam.hookNotifyIconOnSet(
context = param.args?.get(0) as? Context ?: lpparam.globalContext
?: error("GlobalContext got null"),
param.args?.get(2) as? StatusBarNotification?,
param.args?.get(1) as ImageView
)
else
lpparam.hookNotifyIconOnSet(
context = lpparam.globalContext ?: error("GlobalContext got null"),
param.args?.get(1) as? StatusBarNotification?,
param.args?.get(0) as ImageView
)
return null
}
})
}
}
/** 之前的版本解决方案 */
else runWithoutError(error = "AutoSetAppIconOldWay") {
XposedHelpers.findAndHookMethod(
NotificationHeaderViewWrapperClass,
lpparam.classLoader, "handleHeaderViews",
object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
runWithoutError(error = "AutoSetAppIconOldWayOnSet") hook@{
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
if (!lpparam.hasIgnoreStatusBarIconColor() && lpparam.isShowMiuiStyle()) return@hook
/** 从父类中得到 mRow 变量 */
lpparam.findClass(NotificationViewWrapperClass).getDeclaredField("mRow")
.apply {
isAccessible = true
}[param.thisObject].apply {
/** 获取小图标 */
val iconImageView = lpparam.findClass(NotificationHeaderViewWrapperClass)
.getDeclaredField("mIcon")
.apply {
isAccessible = true
}[param.thisObject] as ImageView
/** 获取其中的得到通知方法 */
val expandedNf =
javaClass.getDeclaredMethod("getStatusBarNotification").apply {
isAccessible = true
}.invoke(this) as? StatusBarNotification?
/** 执行 Hook */
lpparam.hookNotifyIconOnSet(
iconImageView.context,
expandedNf, iconImageView
)
}
}
}
}
)
}
/** 干掉下拉通知图标自动设置回 APP 图标的方法 - Android 12 */
if (isUpperOfAndroidS &&
lpparam.isMethodExist(
NotificationHeaderViewWrapperInjectorClass,
name = "resetIconBgAndPaddings"
)
) runWithoutError(error = "ResetIconBgAndPaddings") {
XposedHelpers.findAndHookMethod(
NotificationHeaderViewWrapperInjectorClass,
lpparam.classLoader,
"resetIconBgAndPaddings",
ImageView::class.java,
lpparam.findClass(ExpandedNotificationClass),
replaceToNull
)
}
logD(content = "hook Completed!")
}
}
}
}
}

View File

@@ -1,139 +0,0 @@
/**
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com)
*
* This file is part of MIUINativeNotifyIcon.
*
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by fankes on 2022/1/24.
*/
@file:Suppress("DEPRECATION", "SetWorldReadable")
package com.fankes.miui.notify.hook
import android.content.Context
import android.os.Handler
import android.util.Log
import android.widget.Toast
import androidx.annotation.Keep
import com.fankes.miui.notify.application.MNNApplication.Companion.appContext
import com.fankes.miui.notify.application.MNNApplication.Companion.isMineStarted
import com.fankes.miui.notify.bean.IconDataBean
import com.fankes.miui.notify.utils.FileUtils
import com.fankes.miui.notify.utils.XPrefUtils
import java.io.File
@Keep
object HookMedium {
const val ENABLE_MODULE = "_enable_module"
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_NOTIFY_ICON_HOOK = "_notify_icon_hook"
const val SELF_PACKAGE_NAME = "com.fankes.miui.notify"
const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
/**
* 判断模块是否激活
*
* 在 [HookMain] 中 Hook 掉此方法
* @return [Boolean] 激活状态
*/
fun isHooked(): Boolean {
Log.d("MIUINativeNotifyIcon", "isHooked: true")
return false
}
/**
* 获取此 APP 的通知图标是否被 Hook
* @param bean 图标 bean
*/
fun isAppNotifyHookOf(bean: IconDataBean) = getBoolean(key = bean.toEnabledName(), default = bean.isEnabled)
/**
* 设置 Hook 此 APP 的通知图标
* @param bean 图标 bean
* @param isHook 是否 Hook
*/
fun putAppNotifyHookOf(bean: IconDataBean, isHook: Boolean) = putBoolean(key = bean.toEnabledName(), bool = isHook)
/**
* 获取此 APP 的通知图标是否被全部 Hook
* @param bean 图标 bean
*/
fun isAppNotifyHookAllOf(bean: IconDataBean) = getBoolean(key = bean.toEnabledAllName(), default = bean.isEnabledAll)
/**
* 设置全部 Hook 此 APP 的通知图标
* @param bean 图标 bean
* @param isHook 是否 Hook
*/
fun putAppNotifyHookAllOf(bean: IconDataBean, isHook: Boolean) = putBoolean(key = bean.toEnabledAllName(), bool = isHook)
/**
* 获取保存的值
* @param key 名称
* @param default 默认值
* @return [Boolean] 保存的值
*/
fun getBoolean(key: String, default: Boolean = false) =
if (isMineStarted)
appContext.getSharedPreferences(
appContext.packageName + "_preferences",
Context.MODE_PRIVATE
).getBoolean(key, default)
else XPrefUtils.getBoolean(key, default)
/**
* 保存值
* @param key 名称
* @param bool 值
*/
fun putBoolean(key: String, bool: Boolean) {
appContext.getSharedPreferences(
appContext.packageName + "_preferences",
Context.MODE_PRIVATE
).edit().putBoolean(key, bool).apply()
setWorldReadable(appContext)
/** 延迟继续设置强制允许 SP 可读可写 */
Handler().postDelayed({ setWorldReadable(appContext) }, 500)
Handler().postDelayed({ setWorldReadable(appContext) }, 1000)
Handler().postDelayed({ setWorldReadable(appContext) }, 1500)
}
/**
* 强制设置 Sp 存储为全局可读可写
*
* 以供模块使用
* @param context 实例
*/
fun setWorldReadable(context: Context) {
try {
if (FileUtils.getDefaultPrefFile(context).exists()) {
for (file in arrayOf<File>(
FileUtils.getDataDir(context),
FileUtils.getPrefDir(context),
FileUtils.getDefaultPrefFile(context)
)) {
file.setReadable(true, false)
file.setExecutable(true, false)
}
}
} catch (_: Exception) {
Toast.makeText(context, "无法写入模块设置,请检查权限\n如果此提示一直显示,请不要双开模块", Toast.LENGTH_SHORT).show()
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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/15.
*/
package com.fankes.miui.notify.hook.factory
import android.content.Context
import com.fankes.miui.notify.bean.IconDataBean
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.param.PackageParam
/**
* 获取此 APP 的通知图标是否被 Hook
* @param bean 图标 bean
*/
fun PackageParam.isAppNotifyHookOf(bean: IconDataBean) = prefs.getBoolean(key = bean.toEnabledName(), default = bean.isEnabled)
/**
* 获取此 APP 的通知图标是否被 Hook
* @param bean 图标 bean
*/
fun Context.isAppNotifyHookOf(bean: IconDataBean) = modulePrefs.getBoolean(key = bean.toEnabledName(), default = bean.isEnabled)
/**
* 设置 Hook 此 APP 的通知图标
* @param bean 图标 bean
* @param isHook 是否 Hook
*/
fun Context.putAppNotifyHookOf(bean: IconDataBean, isHook: Boolean) =
modulePrefs.putBoolean(key = bean.toEnabledName(), value = isHook)
/**
* 获取此 APP 的通知图标是否被全部 Hook
* @param bean 图标 bean
*/
fun PackageParam.isAppNotifyHookAllOf(bean: IconDataBean) =
prefs.getBoolean(key = bean.toEnabledAllName(), default = bean.isEnabledAll)
/**
* 获取此 APP 的通知图标是否被全部 Hook
* @param bean 图标 bean
*/
fun Context.isAppNotifyHookAllOf(bean: IconDataBean) =
modulePrefs.getBoolean(key = bean.toEnabledAllName(), default = bean.isEnabledAll)
/**
* 设置全部 Hook 此 APP 的通知图标
* @param bean 图标 bean
* @param isHook 是否 Hook
*/
fun Context.putAppNotifyHookAllOf(bean: IconDataBean, isHook: Boolean) =
modulePrefs.putBoolean(key = bean.toEnabledAllName(), value = isHook)

View File

@@ -1,29 +1,30 @@
/** /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/01/30. * This file is Created by fankes on 2022/1/30.
*/ */
@file:Suppress("SetTextI18n", "InflateParams") @file:Suppress("SetTextI18n", "InflateParams", "DEPRECATION")
package com.fankes.miui.notify.ui package com.fankes.miui.notify.ui
import android.content.Intent import android.app.ProgressDialog
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@@ -31,32 +32,112 @@ import android.view.ViewGroup
import android.widget.BaseAdapter import android.widget.BaseAdapter
import android.widget.ListView import android.widget.ListView
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.utils.widget.ImageFilterView import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import com.fankes.miui.notify.R import com.fankes.miui.notify.R
import com.fankes.miui.notify.hook.HookMedium import com.fankes.miui.notify.bean.IconDataBean
import com.fankes.miui.notify.hook.HookConst.SOURCE_SYNC_WAY
import com.fankes.miui.notify.hook.HookConst.SOURCE_SYNC_WAY_CUSTOM_URL
import com.fankes.miui.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_1
import com.fankes.miui.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_2
import com.fankes.miui.notify.hook.HookConst.TYPE_SOURCE_SYNC_WAY_3
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.params.IconPackParams
import com.fankes.miui.notify.ui.base.BaseActivity import com.fankes.miui.notify.ui.base.BaseActivity
import com.fankes.miui.notify.utils.*
import com.fankes.miui.notify.view.MaterialSwitch import com.fankes.miui.notify.view.MaterialSwitch
import com.google.android.material.radiobutton.MaterialRadioButton
import com.google.android.material.textfield.TextInputEditText
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
class ConfigureActivity : BaseActivity() { class ConfigureActivity : BaseActivity() {
/** 当前筛选条件 */
private var filterText = ""
/** 回调适配器改变 */
private var onChanged: (() -> Unit)? = null
/** 回调滚动事件改变 */
private var onScrollEvent: ((Boolean) -> Unit)? = null
/** 全部的通知优化图标数据 */
private var iconAllDatas = ArrayList<IconDataBean>()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_config) 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() } findViewById<View>(R.id.title_back_icon).setOnClickListener { onBackPressed() }
/** 设置标题个数文本 */ /** 刷新适配器结果相关 */
findViewById<TextView>(R.id.config_title_count_text).text = "已适配 ${IconPackParams.iconDatas.size} 个 APP 的通知图标" refreshAdapterResult()
/** 设置上下按钮点击事件 */
findViewById<View>(R.id.config_title_up).setOnClickListener {
snake(msg = "滚动到顶部")
onScrollEvent?.invoke(false)
}
findViewById<View>(R.id.config_title_down).setOnClickListener {
snake(msg = "滚动到底部")
onScrollEvent?.invoke(true)
}
/** 设置过滤按钮点击事件 */
findViewById<View>(R.id.config_title_filter).setOnClickListener {
showDialog {
title = "按条件过滤"
var editText: TextInputEditText
addView(R.layout.dia_icon_filter).apply {
editText = findViewById<TextInputEditText>(R.id.dia_icon_filter_input_edit).apply {
requestFocus()
invalidate()
if (filterText.isNotBlank()) {
setText(filterText)
setSelection(filterText.length)
}
}
}
confirmButton {
if (editText.text.toString().isNotBlank()) {
filterText = editText.text.toString().trim()
refreshAdapterResult()
} else {
toast(msg = "条件不能为空")
it.performClick()
}
}
cancelButton()
if (filterText.isNotBlank())
neutralButton(text = "清除条件") {
filterText = ""
refreshAdapterResult()
}
}
}
/** 设置同步列表按钮点击事件 */
findViewById<View>(R.id.config_title_sync).setOnClickListener { onStartRefresh() }
/** 设置列表元素和 Adapter */ /** 设置列表元素和 Adapter */
findViewById<ListView>(R.id.config_list_view).apply { findViewById<ListView>(R.id.config_list_view).apply {
adapter = object : BaseAdapter() { adapter = object : BaseAdapter() {
private val inflater = LayoutInflater.from(context) private val inflater = LayoutInflater.from(context)
override fun getCount() = IconPackParams.iconDatas.size override fun getCount() = iconDatas.size
override fun getItem(position: Int) = IconPackParams.iconDatas[position] override fun getItem(position: Int) = iconDatas[position]
override fun getItemId(position: Int) = position.toLong() override fun getItemId(position: Int) = position.toLong()
@@ -77,22 +158,28 @@ class ConfigureActivity : BaseActivity() {
} else holder = convertView.tag as ViewHolder } else holder = convertView.tag as ViewHolder
getItem(position).also { getItem(position).also {
holder.appIcon.setImageBitmap(it.iconBitmap) holder.appIcon.setImageBitmap(it.iconBitmap)
(if (it.iconColor != 0) it.iconColor else resources.getColor(R.color.colorTextGray)).also { color ->
holder.appIcon.setColorFilter(color)
holder.appName.setTextColor(color)
}
holder.appName.text = it.appName holder.appName.text = it.appName
holder.pkgName.text = it.packageName holder.pkgName.text = it.packageName
holder.cbrName.text = "贡献者:" + it.contributorName holder.cbrName.text = "贡献者:" + it.contributorName
HookMedium.isAppNotifyHookOf(it).also { e -> isAppNotifyHookOf(it).also { e ->
holder.switchOpen.isChecked = e holder.switchOpen.isChecked = e
holder.switchAll.isEnabled = e holder.switchAll.isEnabled = e
} }
holder.switchOpen.setOnCheckedChangeListener { btn, b -> holder.switchOpen.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
HookMedium.putAppNotifyHookOf(it, b) putAppNotifyHookOf(it, b)
holder.switchAll.isEnabled = b holder.switchAll.isEnabled = b
SystemUITool.showNeedRestartSnake(context = this@ConfigureActivity)
} }
holder.switchAll.isChecked = HookMedium.isAppNotifyHookAllOf(it) holder.switchAll.isChecked = isAppNotifyHookAllOf(it)
holder.switchAll.setOnCheckedChangeListener { btn, b -> holder.switchAll.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
HookMedium.putAppNotifyHookAllOf(it, b) putAppNotifyHookAllOf(it, b)
SystemUITool.showNeedRestartSnake(context = this@ConfigureActivity)
} }
} }
return cView!! return cView!!
@@ -106,20 +193,255 @@ class ConfigureActivity : BaseActivity() {
lateinit var switchOpen: MaterialSwitch lateinit var switchOpen: MaterialSwitch
lateinit var switchAll: MaterialSwitch lateinit var switchAll: MaterialSwitch
} }
}.apply {
setOnItemLongClickListener { _, _, p, _ ->
showDialog {
title = "复制“${iconDatas[p].appName}”的规则"
msg = "是否复制单条规则到剪贴板?"
confirmButton { copyToClipboard(iconDatas[p].toString()) }
cancelButton()
} }
true
}
onChanged = { notifyDataSetChanged() }
}
onScrollEvent = { post { setSelection(if (it) iconDatas.lastIndex else 0) } }
} }
/** 设置点击事件 */ /** 设置点击事件 */
findViewById<View>(R.id.config_cbr_button).setOnClickListener { findViewById<View>(R.id.config_cbr_button).setOnClickListener {
try { openBrowser(url = "https://github.com/fankes/AndroidNotifyIconAdapt/blob/main/CONTRIBUTING.md")
startActivity(Intent().apply { }
action = "android.intent.action.VIEW" /** 装载数据 */
data = Uri.parse("https://github.com/fankes/MIUINativeNotifyIcon") mockLocalData()
/** 防止顶栈一样重叠在自己的 APP 中 */ /** 更新数据 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK onStartRefresh()
}) }
} catch (e: Exception) {
Toast.makeText(this, "无法启动系统默认浏览器", Toast.LENGTH_SHORT).show() /** 装载或刷新本地数据 */
private fun mockLocalData() {
iconAllDatas = IconPackParams(context = this).iconDatas
refreshAdapterResult()
}
/** 首次进入或更新数据 */
private fun onStartRefresh() =
showDialog {
title = "同步列表"
var sourceType = modulePrefs.getInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_1)
var customUrl = modulePrefs.getString(SOURCE_SYNC_WAY_CUSTOM_URL)
addView(R.layout.dia_source_from).apply {
val radio1 = findViewById<MaterialRadioButton>(R.id.dia_sf_rd1)
val radio2 = findViewById<MaterialRadioButton>(R.id.dia_sf_rd2)
val radio3 = findViewById<MaterialRadioButton>(R.id.dia_sf_rd3)
val edLin = findViewById<View>(R.id.dia_sf_text_lin)
findViewById<TextInputEditText>(R.id.dia_sf_text).apply {
if (customUrl.isNotBlank()) {
setText(customUrl)
setSelection(customUrl.length)
}
doOnTextChanged { text, _, _, _ ->
customUrl = text.toString()
modulePrefs.putString(SOURCE_SYNC_WAY_CUSTOM_URL, text.toString())
} }
} }
edLin.isVisible = sourceType == TYPE_SOURCE_SYNC_WAY_3
radio1.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_1
radio2.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_2
radio3.isChecked = sourceType == TYPE_SOURCE_SYNC_WAY_3
radio1.setOnClickListener {
radio2.isChecked = false
radio3.isChecked = false
edLin.isVisible = false
sourceType = TYPE_SOURCE_SYNC_WAY_1
modulePrefs.putInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_1)
}
radio2.setOnClickListener {
radio1.isChecked = false
radio3.isChecked = false
edLin.isVisible = false
sourceType = TYPE_SOURCE_SYNC_WAY_2
modulePrefs.putInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_2)
}
radio3.setOnClickListener {
radio1.isChecked = false
radio2.isChecked = false
edLin.isVisible = true
sourceType = TYPE_SOURCE_SYNC_WAY_3
modulePrefs.putInt(SOURCE_SYNC_WAY, TYPE_SOURCE_SYNC_WAY_3)
}
}
confirmButton {
when (sourceType) {
TYPE_SOURCE_SYNC_WAY_1 -> onRefreshing(url = "https://raw.fastgit.org/fankes/AndroidNotifyIconAdapt/main")
TYPE_SOURCE_SYNC_WAY_2 -> onRefreshing(url = "https://raw.githubusercontent.com/fankes/AndroidNotifyIconAdapt/main")
TYPE_SOURCE_SYNC_WAY_3 ->
if (customUrl.isNotBlank())
if (customUrl.startsWith("http://") || customUrl.startsWith("https://"))
onRefreshingCustom(customUrl)
else snake(msg = "同步地址不是一个合法的 URL")
else snake(msg = "同步地址不能为空")
else -> snake(msg = "同步类型错误")
}
}
cancelButton()
neutralButton(text = "自定义规则") {
showDialog {
title = "自定义规则"
var editText: TextInputEditText
addView(R.layout.dia_source_from_string).apply {
editText = findViewById<TextInputEditText>(R.id.dia_sfs_input_edit).apply {
requestFocus()
invalidate()
}
}
IconPackParams(context = this@ConfigureActivity).also { params ->
confirmButton(text = "合并") {
editText.text.toString().also { jsonString ->
when {
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> snake(msg = "不是有效的 JSON 数据")
jsonString.isNotBlank() -> {
params.save(
params.splicingJsonArray(
dataJson1 = params.storageDataJson ?: "[]",
dataJson2 = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]"
)
)
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this@ConfigureActivity)
}
else -> snake(msg = "请输入有效内容")
}
}
}
cancelButton(text = "覆盖") {
editText.text.toString().also { jsonString ->
when {
jsonString.isNotBlank() && params.isNotVaildJson(jsonString) -> snake(msg = "不是有效的 JSON 数据")
jsonString.isNotBlank() -> {
params.save(dataJson = jsonString.takeIf { params.isJsonArray(it) } ?: "[$jsonString]")
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this@ConfigureActivity)
}
else -> snake(msg = "请输入有效内容")
}
}
}
}
neutralButton(text = "取消")
}
}
}
/**
* 开始更新数据
* @param url
*/
private fun onRefreshing(url: String) = ClientRequestTool.checkingInternetConnect(context = this) {
ProgressDialog(this).apply {
setDefaultStyle(context = this@ConfigureActivity)
setCancelable(false)
setTitle("同步中")
setMessage("正在同步 OS 数据")
show()
}.also {
ClientRequestTool.wait(
context = this,
url = "$url/OS/MIUI/NotifyIconsSupportConfig.json"
) { isDone1, ctOS ->
it.setMessage("正在同步 APP 数据")
ClientRequestTool.wait(
context = this,
url = "$url/APP/NotifyIconsSupportConfig.json"
) { isDone2, ctAPP ->
it.cancel()
IconPackParams(context = this).also { params ->
if (isDone1 && isDone2) params.splicingJsonArray(ctOS, ctAPP).also {
when {
params.isHackString(it) -> snake(msg = "请求需要验证,请尝试魔法上网或关闭魔法")
params.isNotVaildJson(it) -> snake(msg = "在线规则发生问题,请稍后重试")
params.isCompareDifferent(it) -> {
params.save(it)
filterText = ""
mockLocalData()
SystemUITool.showNeedUpdateApplySnake(context = this)
}
else -> snake(msg = "列表数据已是最新")
}
} else showDialog {
title = "连接失败"
msg = "连接失败,错误如下:\n${if (!isDone1) ctOS else ctAPP}"
confirmButton(text = "解决方案") {
openBrowser(url = "https://www.baidu.com/s?wd=github%2Braw%2B%E6%97%A0%E6%B3%95%E8%AE%BF%E9%97%AE")
}
cancelButton()
}
}
}
}
}
}
/**
* 开始更新数据
* @param url
*/
private fun onRefreshingCustom(url: String) = ClientRequestTool.checkingInternetConnect(context = this) {
ProgressDialog(this).apply {
setDefaultStyle(context = this@ConfigureActivity)
setCancelable(false)
setTitle("同步中")
setMessage("正在通过自定义地址同步数据")
show()
}.also {
ClientRequestTool.wait(
context = this,
url = url
) { isDone, content ->
it.cancel()
IconPackParams(context = this).also { params ->
if (isDone)
when {
params.isHackString(content) -> snake(msg = "请求需要验证,请尝试魔法上网或关闭魔法")
params.isNotVaildJson(content) -> snake(msg = "目标地址不是有效的 JSON 数据")
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<TextView>(R.id.config_list_no_data_view).apply {
text = if (iconAllDatas.isEmpty()) "噫,竟然什么都没有~\n请点击右上角同步按钮获取云端数据" else "噫,竟然什么都没找到~"
isVisible = iconDatas.isEmpty()
}
}
/**
* 当前结果下的图标数组
* @return [Array]
*/
private val iconDatas
get() = if (filterText.isBlank()) iconAllDatas
else iconAllDatas.filter {
it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase())
} }
} }

View File

@@ -1,22 +1,24 @@
/** /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/01/24. * This file is Created by fankes on 2022/1/24.
*/ */
@file:Suppress("SetTextI18n") @file:Suppress("SetTextI18n")
@@ -25,20 +27,25 @@ package com.fankes.miui.notify.ui
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.utils.widget.ImageFilterView import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.fankes.miui.notify.BuildConfig import com.fankes.miui.notify.BuildConfig
import com.fankes.miui.notify.R import com.fankes.miui.notify.R
import com.fankes.miui.notify.hook.HookMedium 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
import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE_LOG
import com.fankes.miui.notify.hook.HookConst.ENABLE_NOTIFY_ICON_FIX
import com.fankes.miui.notify.ui.base.BaseActivity import com.fankes.miui.notify.ui.base.BaseActivity
import com.fankes.miui.notify.utils.* import com.fankes.miui.notify.utils.*
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
@@ -54,8 +61,8 @@ class MainActivity : BaseActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
/** 设置文本 */ /** 设置文本 */
findViewById<TextView>(R.id.main_text_version).text = "当前版本:$moduleVersion" findViewById<TextView>(R.id.main_text_version).text = "模块版本:$moduleVersion"
findViewById<TextView>(R.id.main_text_miui_version).text = "MIUI 版本:$miuiFullVersion" findViewById<TextView>(R.id.main_text_miui_version).text = "系统版本:$miuiFullVersion"
when { when {
/** 判断是否为 MIUI 系统 */ /** 判断是否为 MIUI 系统 */
isNotMIUI -> isNotMIUI ->
@@ -85,11 +92,7 @@ class MainActivity : BaseActivity() {
noCancelable() noCancelable()
} }
/** 判断是否 Hook */ /** 判断是否 Hook */
isHooked() -> { YukiHookModuleStatus.isActive() -> {}
findViewById<LinearLayout>(R.id.main_lin_status).setBackgroundResource(R.drawable.green_round)
findViewById<ImageFilterView>(R.id.main_img_status).setImageResource(R.mipmap.succcess)
findViewById<TextView>(R.id.main_text_status).text = "模块已激活"
}
else -> else ->
showDialog { showDialog {
title = "模块没有激活" title = "模块没有激活"
@@ -104,30 +107,44 @@ class MainActivity : BaseActivity() {
/** 初始化 View */ /** 初始化 View */
val moduleEnableSwitch = findViewById<SwitchCompat>(R.id.module_enable_switch) val moduleEnableSwitch = findViewById<SwitchCompat>(R.id.module_enable_switch)
val moduleEnableLogSwitch = findViewById<SwitchCompat>(R.id.module_enable_log_switch) val moduleEnableLogSwitch = findViewById<SwitchCompat>(R.id.module_enable_log_switch)
val colorIconHookItem = findViewById<View>(R.id.config_item_color_hook)
val notifyIconConfigItem = findViewById<View>(R.id.config_item_notify) val notifyIconConfigItem = findViewById<View>(R.id.config_item_notify)
val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch) val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch)
val colorIconHookSwitch = findViewById<SwitchCompat>(R.id.color_icon_fix_switch) val colorIconHookSwitch = findViewById<SwitchCompat>(R.id.color_icon_fix_switch)
val notifyIconHookSwitch = findViewById<SwitchCompat>(R.id.notify_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 存储的信息 */ /** 获取 Sp 存储的信息 */
notifyIconConfigItem.isVisible = getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true) colorIconHookItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
moduleEnableLogSwitch.isVisible = getBoolean(HookMedium.ENABLE_MODULE, default = true) colorIconCompatSwitch.isVisible = modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
moduleEnableSwitch.isChecked = getBoolean(HookMedium.ENABLE_MODULE, default = true) colorIconCompatText.isVisible = modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
moduleEnableLogSwitch.isChecked = getBoolean(HookMedium.ENABLE_MODULE_LOG, default = false) notifyIconConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true) &&
hideIconInLauncherSwitch.isChecked = getBoolean(HookMedium.ENABLE_HIDE_ICON) modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
colorIconHookSwitch.isChecked = getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true) moduleEnableLogSwitch.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
notifyIconHookSwitch.isChecked = getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true) notifyIconFixButton.isVisible = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)
moduleEnableSwitch.isChecked = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
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 -> moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_MODULE, b) modulePrefs.putBoolean(ENABLE_MODULE, b)
moduleEnableLogSwitch.isVisible = b moduleEnableLogSwitch.isVisible = b
colorIconHookItem.isVisible = b
notifyIconConfigItem.isVisible = b && colorIconHookSwitch.isChecked
SystemUITool.showNeedRestartSnake(context = this)
} }
moduleEnableLogSwitch.setOnCheckedChangeListener { btn, b -> moduleEnableLogSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_MODULE_LOG, b) modulePrefs.putBoolean(ENABLE_MODULE_LOG, b)
SystemUITool.showNeedRestartSnake(context = this)
} }
hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b -> hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_HIDE_ICON, b) modulePrefs.putBoolean(ENABLE_HIDE_ICON, b)
packageManager.setComponentEnabledSetting( packageManager.setComponentEnabledSetting(
ComponentName(this@MainActivity, "com.fankes.miui.notify.Home"), ComponentName(this@MainActivity, "com.fankes.miui.notify.Home"),
if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED, if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
@@ -136,96 +153,88 @@ class MainActivity : BaseActivity() {
} }
colorIconHookSwitch.setOnCheckedChangeListener { btn, b -> colorIconHookSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, b) modulePrefs.putBoolean(ENABLE_COLOR_ICON_HOOK, b)
notifyIconConfigItem.isVisible = b notifyIconConfigItem.isVisible = b
colorIconCompatSwitch.isVisible = b
colorIconCompatText.isVisible = b
SystemUITool.showNeedRestartSnake(context = this)
} }
notifyIconHookSwitch.setOnCheckedChangeListener { btn, b -> colorIconCompatSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener if (!btn.isPressed) return@setOnCheckedChangeListener
putBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, b) modulePrefs.putBoolean(ENABLE_COLOR_ICON_COMPAT, b)
} SystemUITool.showNeedRestartSnake(context = this)
/** 重启按钮点击事件 */
findViewById<View>(R.id.title_restart_icon).setOnClickListener {
showDialog {
title = "重启系统界面"
msg = "你确定要立即重启系统界面吗?\n\n" +
"部分 MIUI 系统使用了状态栏主题可能会发生主题失效的情况,这种情况请再重启一次即可。"
confirmButton { restartSystemUI() }
cancelButton()
} }
notifyIconFixSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_NOTIFY_ICON_FIX, b)
notifyIconFixButton.isVisible = b
SystemUITool.showNeedRestartSnake(context = this)
} }
/** 通知图标优化名单按钮点击事件 */ /** 通知图标优化名单按钮点击事件 */
findViewById<View>(R.id.config_notify_app_button).setOnClickListener { notifyIconFixButton.setOnClickListener { startActivity(Intent(this, ConfigureActivity::class.java)) }
startActivity(Intent(this, ConfigureActivity::class.java)) /** 重启按钮点击事件 */
} findViewById<View>(R.id.title_restart_icon).setOnClickListener { SystemUITool.restartSystemUI(context = this) }
/** 恰饭! */ /** 恰饭! */
findViewById<View>(R.id.link_with_follow_me).setOnClickListener { findViewById<View>(R.id.link_with_follow_me).setOnClickListener {
try { openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market")
startActivity(Intent().apply {
setPackage("com.coolapk.market")
action = "android.intent.action.VIEW"
data = Uri.parse("https://www.coolapk.com/u/876977")
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
} catch (e: Exception) {
Toast.makeText(this, "你可能没有安装酷安", Toast.LENGTH_SHORT).show()
}
} }
/** 项目地址点击事件 */ /** 项目地址点击事件 */
findViewById<View>(R.id.link_with_project_address).setOnClickListener { findViewById<View>(R.id.link_with_project_address).setOnClickListener {
try { openBrowser(url = "https://github.com/fankes/MIUINativeNotifyIcon")
startActivity(Intent().apply {
action = "android.intent.action.VIEW"
data = Uri.parse("https://github.com/fankes/MIUINativeNotifyIcon")
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
} catch (e: Exception) {
Toast.makeText(this, "无法启动系统默认浏览器", Toast.LENGTH_SHORT).show()
} }
} }
/** 刷新模块状态 */
private fun refreshModuleStatus() {
findViewById<LinearLayout>(R.id.main_lin_status).setBackgroundResource(
when {
YukiHookModuleStatus.isActive() && isMiuiNotifyStyle -> R.drawable.bg_yellow_round
YukiHookModuleStatus.isActive() -> R.drawable.bg_green_round
else -> R.drawable.bg_dark_round
}
)
findViewById<ImageFilterView>(R.id.main_img_status).setImageResource(
when {
YukiHookModuleStatus.isActive() && !isMiuiNotifyStyle -> R.mipmap.ic_success
else -> R.mipmap.ic_warn
}
)
findViewById<TextView>(R.id.main_text_status).text =
when {
YukiHookModuleStatus.isActive() && isMiuiNotifyStyle -> "模块已激活,但未在工作"
YukiHookModuleStatus.isActive() -> "模块已激活"
else -> "模块未激活"
}
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
/** MIUI 12 的版本特殊 - 所以给出提示 */ /** 刷新模块状态 */
if (!isWarnDialogShowing && isHooked() && miuiVersion == "12" && isMiuiNotifyStyle) refreshModuleStatus()
/** 经典样式启用后给出警告 */
if (!isWarnDialogShowing && YukiHookModuleStatus.isActive() && isMiuiNotifyStyle)
showDialog { showDialog {
isWarnDialogShowing = true isWarnDialogShowing = true
title = "经典通知栏样式已启用" title = "经典通知栏样式已启用"
msg = "在 MIUI 12 中启用了经典通知栏样式后状态栏图标将不再做原生处理,模块停止工作," + msg = "当你启用了经典通知栏样式后,为防止 MIUI 自身不规范 APP 图标被破坏,状态栏图标将不再做原生处理\n\n" +
"这取决于系统设置,你应当在 设置>通知管理>通知显示设置 中将样式设置为“原生样式”。" "若要使用原生样式,请前往 设置>通知管理>通知显示设置 中将样式设置为“原生样式”,新版本为 设置>通知与控制中心>通知显示设置"
confirmButton(text = "我知道了") { isWarnDialogShowing = false } confirmButton(text = "去设置") {
runCatching {
startActivity(Intent().apply {
component = ComponentName(
"com.miui.notification",
"miui.notification.management.activity.NotificationDisplaySettingsActivity"
)
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure {
toast(msg = "启动失败,请手动调整设置")
}
isWarnDialogShowing = false
}
cancelButton { isWarnDialogShowing = false }
noCancelable() noCancelable()
} }
} }
/**
* 判断模块是否激活
* @return [Boolean] 激活状态
*/
private fun isHooked() = HookMedium.isHooked()
/** 重启系统界面 */
private fun restartSystemUI() =
execShellCmd(cmd = "pgrep systemui").also { pid ->
if (pid.isNotBlank())
execShellCmd(cmd = "kill -9 $pid")
else Toast.makeText(this, "ROOT 权限获取失败", Toast.LENGTH_SHORT).show()
}
/**
* 获取保存的值
* @param key 名称
* @param default 默认值
* @return [Boolean] 保存的值
*/
private fun getBoolean(key: String, default: Boolean = false) = HookMedium.getBoolean(key, default)
/**
* 保存值
* @param key 名称
* @param bool 值
*/
private fun putBoolean(key: String, bool: Boolean) = HookMedium.putBoolean(key, bool)
} }

View File

@@ -1,29 +1,30 @@
/** /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/01/30. * This file is Created by fankes on 2022/1/30.
*/ */
package com.fankes.miui.notify.ui.base package com.fankes.miui.notify.ui.base
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.fankes.miui.notify.R import com.fankes.miui.notify.R
import com.fankes.miui.notify.hook.HookMedium
import com.fankes.miui.notify.utils.isNotSystemInDarkMode import com.fankes.miui.notify.utils.isNotSystemInDarkMode
import com.gyf.immersionbar.ktx.immersionBar import com.gyf.immersionbar.ktx.immersionBar
@@ -43,24 +44,4 @@ abstract class BaseActivity : AppCompatActivity() {
fitsSystemWindows(true) fitsSystemWindows(true)
} }
} }
override fun onResume() {
super.onResume()
HookMedium.setWorldReadable(this)
}
override fun onRestart() {
super.onRestart()
HookMedium.setWorldReadable(this)
}
override fun onPause() {
super.onPause()
HookMedium.setWorldReadable(this)
}
override fun onBackPressed() {
HookMedium.setWorldReadable(this)
super.onBackPressed()
}
} }

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,146 @@
/*
* 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", "DEPRECATION")
package com.fankes.miui.notify.utils
import android.app.Activity
import android.app.ProgressDialog
import android.content.Intent
import android.net.Uri
import android.provider.Settings
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 {
/**
* 检查网络连接情况
* @param context 实例
* @param it 已连接回调
*/
fun checkingInternetConnect(context: Activity, it: () -> Unit) =
ProgressDialog(context).apply {
setDefaultStyle(context)
setCancelable(false)
setTitle("准备中")
setMessage("正在检查网络连接情况")
}.apply {
wait(context, url = "https://www.baidu.com") { isDone, _ ->
cancel()
if (isDone) it() else
context.showDialog {
title = "网络不可用"
msg = "无法连接到互联网,请检查你当前的设备是否可以上网,且没有在手机管家中禁用本模块的联网权限。"
confirmButton(text = "检查设置") {
runCatching {
context.startActivity(Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", context.packageName, null)
})
}.onFailure { context.snake(msg = "启动应用信息页面失败") }
}
cancelButton()
}
}
}.show()
/**
* 发送 GET 请求内容并等待
* @param context 实例
* @param url 请求地址
* @param it 回调 - ([Boolean] 是否成功,[String] 成功的内容或失败消息)
*/
fun wait(context: Activity, url: String, it: (Boolean, String) -> Unit) = runCatching {
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) }
}
})
}.onFailure { it(false, "URL 无效") }
/**
* 自动信任 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

@@ -1,32 +1,36 @@
/** /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/7. * This file is Created by fankes on 2022/1/7.
*/ */
@file:Suppress("unused") @file:Suppress("unused", "DEPRECATION")
package com.fankes.miui.notify.utils package com.fankes.miui.notify.utils
import android.app.AlertDialog import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.graphics.Color import android.util.DisplayMetrics
import android.graphics.drawable.GradientDrawable import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import kotlin.math.round
/** /**
* 构造对话框 * 构造对话框
@@ -42,6 +46,8 @@ class DialogBuilder(private val context: Context) {
private var instance: AlertDialog.Builder? = null // 实例对象 private var instance: AlertDialog.Builder? = null // 实例对象
private var customLayoutView: View? = null // 自定义布局
init { init {
instance = AlertDialog.Builder(context, android.R.style.Theme_Material_Light_Dialog) instance = AlertDialog.Builder(context, android.R.style.Theme_Material_Light_Dialog)
} }
@@ -63,6 +69,16 @@ class DialogBuilder(private val context: Context) {
instance?.setMessage(value) instance?.setMessage(value)
} }
/**
* 设置对话框自定义布局
* @param resId 属性资源 Id
* @return [View]
*/
fun addView(resId: Int): View {
customLayoutView = LayoutInflater.from(context).inflate(resId, null)
return customLayoutView ?: error("Inflate $resId failed")
}
/** /**
* 设置对话框确定按钮 * 设置对话框确定按钮
* @param text 按钮文本内容 * @param text 按钮文本内容
@@ -89,13 +105,9 @@ class DialogBuilder(private val context: Context) {
/** 显示对话框 */ /** 显示对话框 */
internal fun show() = instance?.create()?.apply { internal fun show() = instance?.create()?.apply {
window?.setBackgroundDrawable(GradientDrawable( val dm = DisplayMetrics()
GradientDrawable.Orientation.TOP_BOTTOM, (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getMetrics(dm)
intArrayOf(Color.WHITE, Color.WHITE) customLayoutView?.let { setView(it.apply { minimumWidth = round(x = dm.widthPixels / 1.3).toInt() }) }
).apply { setDefaultStyle(context = this@DialogBuilder.context)
shape = GradientDrawable.RECTANGLE
gradientType = GradientDrawable.LINEAR_GRADIENT
cornerRadius = 15.dp(this@DialogBuilder.context)
})
}?.show() }?.show()
} }

View File

@@ -1,85 +0,0 @@
/*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com)
*
* This file is part of MIUINativeNotifyIcon.
*
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by zpp0196 on 2019/2/9.
*/
package com.fankes.miui.notify.utils;
import android.content.Context;
import com.fankes.miui.notify.BuildConfig;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@SuppressWarnings("ALL")
public class FileUtils {
private static final String FILE_PREF_NAME = BuildConfig.APPLICATION_ID + "_preferences.xml";
public static boolean copyFile(File srcFile, File targetFile) {
FileInputStream ins = null;
FileOutputStream out = null;
try {
if (targetFile.exists()) {
targetFile.delete();
}
File targetParent = targetFile.getParentFile();
if (!targetParent.exists()) {
targetParent.mkdirs();
}
targetFile.createNewFile();
ins = new FileInputStream(srcFile);
out = new FileOutputStream(targetFile);
byte[] b = new byte[1024];
int n;
while ((n = ins.read(b)) != -1) {
out.write(b, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
try {
if (ins != null) {
ins.close();
}
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
public static File getDataDir(Context context) {
return new File(context.getApplicationInfo().dataDir);
}
public static File getPrefDir(Context context) {
return new File(getDataDir(context), "shared_prefs");
}
public static File getDefaultPrefFile(Context context) {
return new File(getPrefDir(context), FILE_PREF_NAME);
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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/8.
*/
package com.fankes.miui.notify.utils
import android.content.Context
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
/**
* 系统界面工具
*/
object SystemUITool {
/**
* 重启系统界面
* @param context 实例
*/
fun restartSystemUI(context: Context) =
context.showDialog {
title = "重启系统界面"
msg = "你确定要立即重启系统界面吗?\n\n" +
"部分 MIUI 系统使用了状态栏主题可能会发生主题失效的情况,这种情况请再重启一次即可。"
confirmButton {
execShellSu(cmd = "pgrep systemui").also { pid ->
if (pid.isNotBlank())
execShellSu(cmd = "kill -9 $pid")
else toast(msg = "ROOT 权限获取失败")
}
}
cancelButton()
}
/**
* 显示需要重启系统界面的 [Snackbar]
* @param context 实例
*/
fun showNeedRestartSnake(context: Context) =
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

@@ -1,40 +1,56 @@
/** /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/7. * This file is Created by fankes on 2022/1/7.
*/ */
@file:Suppress("DEPRECATION", "PrivateApi", "unused") @file:Suppress("DEPRECATION", "PrivateApi", "unused", "ObsoleteSdkInt")
package com.fankes.miui.notify.utils package com.fankes.miui.notify.utils
import android.app.Activity
import android.app.AlertDialog
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.* import android.graphics.Bitmap
import android.graphics.Bitmap.createBitmap import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.net.Uri
import android.os.Build import android.os.Build
import android.provider.Settings import android.provider.Settings
import android.service.notification.StatusBarNotification
import android.util.Base64 import android.util.Base64
import android.widget.Toast
import com.fankes.miui.notify.application.MNNApplication.Companion.appContext import com.fankes.miui.notify.application.MNNApplication.Companion.appContext
import java.io.DataInputStream import com.google.android.material.snackbar.Snackbar
import java.io.DataOutputStream import com.highcapable.yukihookapi.hook.factory.classOf
import com.highcapable.yukihookapi.hook.factory.hasClass
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.loggerE
import com.highcapable.yukihookapi.hook.type.java.StringType
import com.topjohnwu.superuser.Shell
import java.io.ByteArrayOutputStream
/** /**
* 系统深色模式是否开启 * 系统深色模式是否开启
@@ -59,12 +75,7 @@ val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Confi
* 通知栏是否为 MIUI 样式 * 通知栏是否为 MIUI 样式
* @return [Boolean] 是否符合条件 * @return [Boolean] 是否符合条件
*/ */
val Context.isMiuiNotifyStyle val Context.isMiuiNotifyStyle get() = safeOfFalse { Settings.System.getInt(contentResolver, "status_bar_notification_style") == 0 }
get() = try {
Settings.System.getInt(contentResolver, "status_bar_notification_style") == 0
} catch (_: Throwable) {
false
}
/** /**
* 系统深色模式是否没开启 * 系统深色模式是否没开启
@@ -88,14 +99,7 @@ inline val isLowerAndroidP get() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P
* 当前设备是否是 MIUI 定制 Android 系统 * 当前设备是否是 MIUI 定制 Android 系统
* @return [Boolean] 是否符合条件 * @return [Boolean] 是否符合条件
*/ */
val isMIUI by lazy { val isMIUI by lazy { ("android.miui.R").hasClass }
try {
Class.forName("android.miui.R")
true
} catch (_: Exception) {
false
}
}
/** /**
* 当前设备是否不是 MIUI 定制 Android 系统 * 当前设备是否不是 MIUI 定制 Android 系统
@@ -147,14 +151,35 @@ val miuiVersion
* @return [String] * @return [String]
*/ */
val miuiFullVersion val miuiFullVersion
get() = if (isMIUI) (miuiVersion + " " + findPropString(key = "ro.system.build.version.incremental")) get() = if (isMIUI) findPropString(key = "ro.system.build.version.incremental").let {
else "不是 MIUI 系统" if (it.lowercase().contains("a") ||
it.lowercase().contains("b") ||
/** it.lowercase().contains("c") ||
* 获取推送通知的包名 it.lowercase().contains("d") ||
* @return [String] it.lowercase().contains("e") ||
*/ it.lowercase().contains("f") ||
val StatusBarNotification.opPkgName get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) opPkg else packageName ?: "" it.lowercase().contains("g") ||
it.lowercase().contains("h") ||
it.lowercase().contains("i") ||
it.lowercase().contains("j") ||
it.lowercase().contains("k") ||
it.lowercase().contains("l") ||
it.lowercase().contains("m") ||
it.lowercase().contains("n") ||
it.lowercase().contains("o") ||
it.lowercase().contains("p") ||
it.lowercase().contains("q") ||
it.lowercase().contains("r") ||
it.lowercase().contains("s") ||
it.lowercase().contains("t") ||
it.lowercase().contains("u") ||
it.lowercase().contains("v") ||
it.lowercase().contains("w") ||
it.lowercase().contains("x") ||
it.lowercase().contains("y") ||
it.lowercase().contains("z")
) "$it 稳定版" else "V$miuiVersion $it 开发版"
} else "不是 MIUI 系统"
/** /**
* 得到安装包信息 * 得到安装包信息
@@ -167,15 +192,12 @@ val Context.packageInfo get() = packageManager?.getPackageInfo(packageName, 0) ?
* @return [Boolean] * @return [Boolean]
*/ */
val String.isInstall val String.isInstall
get() = get() = safeOfFalse {
try {
appContext.packageManager.getPackageInfo( appContext.packageManager.getPackageInfo(
this, this,
PackageManager.GET_UNINSTALLED_PACKAGES PackageManager.GET_UNINSTALLED_PACKAGES
) )
true true
} catch (e: Exception) {
false
} }
/** /**
@@ -203,6 +225,17 @@ val Number.dp get() = (toFloat() * appContext.resources.displayMetrics.density).
*/ */
fun Number.dp(context: Context) = toFloat() * context.resources.displayMetrics.density fun Number.dp(context: Context) = toFloat() * context.resources.displayMetrics.density
/**
* Base64 加密
* @return [String]
*/
val Bitmap.base64
get() = safeOfNothing {
val baos = ByteArrayOutputStream()
compress(Bitmap.CompressFormat.PNG, 100, baos)
Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT)
}
/** /**
* Base64 加密 * Base64 加密
* @return [String] * @return [String]
@@ -228,23 +261,19 @@ val ByteArray.bitmap: Bitmap get() = BitmapFactory.decodeByteArray(this, 0, size
val String.bitmap: Bitmap get() = unbase64.bitmap val String.bitmap: Bitmap get() = unbase64.bitmap
/** /**
* 圆角图片 * 设置对话框默认风格
* @param radius 圆角度 * @param context 使用的实例
* @return [Bitmap] 圆角后的位图 - 失败会返回处理之前的位图
*/ */
fun Bitmap.round(radius: Float): Bitmap = fun AlertDialog.setDefaultStyle(context: Context) =
createBitmap(width, height, Bitmap.Config.ARGB_8888).also { out -> window?.setBackgroundDrawable(
Canvas(out).also { canvas -> GradientDrawable(
Paint().also { paint -> GradientDrawable.Orientation.TOP_BOTTOM,
paint.isAntiAlias = true intArrayOf(Color.WHITE, Color.WHITE)
canvas.drawARGB(0, 0, 0, 0) ).apply {
paint.color = Color.WHITE shape = GradientDrawable.RECTANGLE
canvas.drawRoundRect(RectF(Rect(0, 0, width, height)), radius, radius, paint) gradientType = GradientDrawable.LINEAR_GRADIENT
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN) cornerRadius = 15.dp(context)
canvas.drawBitmap(this, Rect(0, 0, width, height), Rect(0, 0, width, height), paint) })
}
}
}
/** /**
* 获取系统 Prop 值 * 获取系统 Prop 值
@@ -252,44 +281,170 @@ fun Bitmap.round(radius: Float): Bitmap =
* @param default 默认值 * @param default 默认值
* @return [String] * @return [String]
*/ */
fun findPropString(key: String, default: String = "") = fun findPropString(key: String, default: String = "") = safeOf(default) {
try { (classOf(name = "android.os.SystemProperties").method {
(Class.forName("android.os.SystemProperties").getDeclaredMethod( name = "get"
"get", param(StringType, StringType)
String::class.java, }.get().invoke(key, default)) ?: default
String::class.java }
).apply { isAccessible = true }.invoke(null, key, default)) as? String? ?: default
} catch (_: Exception) {
default
}
/** /**
* 执行命令 - su * 执行命令 - su
* @param cmd 命令 * @param cmd 命令
* @return [String] 执行结果 * @return [String] 执行结果
*/ */
fun execShellCmd(cmd: String): String { fun execShellSu(cmd: String) = safeOfNothing {
var result = "" Shell.su(cmd).exec().out.let {
var dos: DataOutputStream? = null if (it.isNotEmpty()) it[0].trim() else ""
var dis: DataInputStream? = null }
try { }
val p = Runtime.getRuntime().exec("su")
dos = DataOutputStream(p.outputStream) /**
dis = DataInputStream(p.inputStream) * 弹出 [Toast]
dos.writeBytes("$cmd\n") * @param msg 提示内容
dos.flush() */
dos.writeBytes("exit\n") fun toast(msg: String) = Toast.makeText(appContext, msg, Toast.LENGTH_SHORT).show()
dos.flush()
var line: String /**
while (dis.readLine().also { line = it } != null) result += line * 弹出 [Snackbar]
p.waitFor() * @param msg 提示内容
} catch (_: Exception) { * @param actionText 按钮文本 - 不写默认取消按钮
} finally { * @param it 按钮事件回调
try { */
dos?.close() fun Context.snake(msg: String, actionText: String = "", it: () -> Unit = {}) =
dis?.close() Snackbar.make((this as Activity).findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG)
} catch (_: Exception) { .apply {
if (actionText.isBlank()) return@apply
setActionTextColor(Color.WHITE)
setAction(actionText) { it() }
}.show()
/**
* 启动系统浏览器
* @param url 网址
* @param packageName 指定包名 - 可不填
*/
fun Context.openBrowser(url: String, packageName: String = "") =
runCatching {
startActivity(Intent().apply {
if (packageName.isNotBlank()) setPackage(packageName)
action = Intent.ACTION_VIEW
data = Uri.parse(url)
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure {
if (packageName.isNotBlank())
snake(msg = "启动 $packageName 失败")
else snake(msg = "启动系统浏览器失败")
}
/**
* 复制到剪贴板
* @param content 要复制的文本
*/
fun Context.copyToClipboard(content: String) = runCatching {
(getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).apply {
setPrimaryClip(ClipData.newPlainText(null, content))
(primaryClip?.getItemAt(0)?.text ?: "").also {
if (it != content) snake(msg = "复制失败") else snake(msg = "已复制")
}
}
}
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为空
* @return [T] 发生异常时返回设定值否则返回正常值
*/
inline fun <T> safeOfNull(it: () -> T): T? = safeOf(null, it)
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为 false
* @return [Boolean] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfFalse(it: () -> Boolean) = safeOf(default = false, it)
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为 true
* @return [Boolean] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfTrue(it: () -> Boolean) = safeOf(default = true, it)
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为 false
* @return [String] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfNothing(it: () -> String) = safeOf(default = "", it)
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为 false
* @return [Int] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfNan(it: () -> Int) = safeOf(default = 0, it)
/**
* 忽略异常返回值
* @param default 异常返回值
* @param it 正常回调值
* @return [T] 发生异常时返回设定值否则返回正常值
*/
inline fun <T> safeOf(default: T, it: () -> T): T {
return try {
it()
} catch (t: NullPointerException) {
default
} catch (t: UnsatisfiedLinkError) {
default
} catch (t: UnsupportedOperationException) {
default
} catch (t: ClassNotFoundException) {
default
} catch (t: IllegalStateException) {
default
} catch (t: NoSuchMethodError) {
default
} catch (t: NoSuchFieldError) {
default
} catch (t: Error) {
default
} catch (t: Exception) {
default
} catch (t: Throwable) {
default
}
}
/**
* 忽略异常运行
* @param msg 出错输出的消息 - 默认为空
* @param it 正常回调
*/
inline fun safeRun(msg: String = "", it: () -> Unit) {
try {
it()
} catch (e: NullPointerException) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: UnsatisfiedLinkError) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: UnsupportedOperationException) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: ClassNotFoundException) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: IllegalStateException) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: NoSuchMethodError) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: NoSuchFieldError) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: Error) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: Exception) {
if (msg.isNotBlank()) loggerE(msg = msg, e = e)
} catch (e: Throwable) {
} }
}
return result.trim()
} }

View File

@@ -1,45 +0,0 @@
/*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com)
*
* This file is part of MIUINativeNotifyIcon.
*
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This file is Created by zpp0196 on 2018/4/11.
*/
package com.fankes.miui.notify.utils
import com.fankes.miui.notify.BuildConfig
import de.robv.android.xposed.XSharedPreferences
object XPrefUtils {
private var xPrefCacheKeyValueBooleans = HashMap<String, Boolean>()
fun getBoolean(key: String, default: Boolean = false) =
xPrefCacheKeyValueBooleans[key].let {
it ?: pref.getBoolean(key, default).let { e ->
xPrefCacheKeyValueBooleans[key] = e
e
}
}
private val pref: XSharedPreferences
get() {
val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID)
preferences.makeWorldReadable()
preferences.reload()
return preferences
}
}

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,24 +1,25 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */
package com.fankes.miui.notify.utils.drawable.drawabletoolbox package com.fankes.miui.notify.utils.drawable.drawabletoolbox
class Constants { class Constants {

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,20 +1,22 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */

View File

@@ -1,24 +1,25 @@
/* /*
* Copyright (C) 2022. Fankes Studio(qzmmcn@163.com) * 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 file is part of 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>
* *
* MIUINativeNotifyIcon is free software: you can redistribute it and/or modify * This software is distributed in the hope that it will be useful,
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MIUINativeNotifyIcon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* GNU General Public License for more details. * Affero General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
* *
* This file is Created by fankes on 2022/1/8. * This file is Created by fankes on 2022/1/8.
*/ */
@file:Suppress("SameParameterValue") @file:Suppress("SameParameterValue")
package com.fankes.miui.notify.view package com.fankes.miui.notify.view

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FF9800" />
<corners android:radius="15dp" />
</shape>

View File

@@ -25,12 +25,14 @@
android:layout_height="20dp" android:layout_height="20dp"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:layout_marginEnd="25dp" android:layout_marginEnd="25dp"
android:src="@mipmap/back" android:src="@mipmap/ic_back"
android:tint="@color/colorTextGray" /> android:tint="@color/colorTextGray"
android:tooltipText="返回" />
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_weight="1" android:layout_weight="1"
android:gravity="center|start" android:gravity="center|start"
android:orientation="vertical"> android:orientation="vertical">
@@ -50,10 +52,46 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:singleLine="true"
android:text="..." android:text="适配列表正在等待装载"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/config_title_up"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginEnd="5dp"
android:src="@mipmap/ic_page_top"
android:tint="@color/colorTextGray"
android:tooltipText="滚动到顶部" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/config_title_down"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginEnd="15dp"
android:src="@mipmap/ic_page_bottom"
android:tint="@color/colorTextGray"
android:tooltipText="滚动到底部" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/config_title_filter"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginEnd="10dp"
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>
<LinearLayout <LinearLayout
@@ -62,7 +100,7 @@
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:background="@drawable/permotion_round" android:background="@drawable/bg_permotion_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="horizontal" android:orientation="horizontal"
@@ -73,7 +111,7 @@
android:layout_height="15dp" android:layout_height="15dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:alpha="0.85" android:alpha="0.85"
android:src="@mipmap/about" android:src="@mipmap/ic_about"
android:tint="@color/colorTextDark" /> android:tint="@color/colorTextDark" />
<TextView <TextView
@@ -81,24 +119,41 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:alpha="0.6" android:alpha="0.6"
android:lineSpacingExtra="5dp" android:lineSpacingExtra="5dp"
android:text="启用替换后 APP 的彩色通知图标将被替换为预设的小图标。\n启用全部替换将忽略 APP 非彩色图标,强制将全部通知图标替换为列表中预设的小图标。" android:text="启用替换后 APP 的彩色通知图标将被替换为预设的小图标。\n启用全部替换将忽略 APP 色图标,强制将全部通知图标替换为列表中预设的小图标。"
android:textColor="@color/colorTextGray" android:textColor="@color/colorTextGray"
android:textSize="11sp" android:textSize="11sp"
tools:ignore="SmallSp" /> tools:ignore="SmallSp" />
</LinearLayout> </LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@+id/config_list_no_data_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:lineSpacingExtra="6dp"
android:text="没有数据"
android:textColor="@color/colorTextDark"
android:textSize="17sp"
android:visibility="gone" />
<ListView <ListView
android:id="@+id/config_list_view" android:id="@+id/config_list_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="match_parent"
android:layout_weight="1"
android:divider="@color/trans" android:divider="@color/trans"
android:dividerHeight="15dp" android:dividerHeight="15dp"
android:fadingEdgeLength="10dp" android:fadingEdgeLength="10dp"
android:listSelector="@null" android:listSelector="@color/trans"
android:padding="15dp" android:padding="15dp"
android:requiresFadingEdge="vertical" android:requiresFadingEdge="vertical"
android:scrollbars="none" /> android:scrollbars="none" />
</FrameLayout>
<TextView <TextView
android:id="@+id/config_cbr_button" android:id="@+id/config_cbr_button"
@@ -107,7 +162,7 @@
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:layout_marginBottom="15dp" android:layout_marginBottom="15dp"
android:background="@drawable/button_round" android:background="@drawable/bg_button_round"
android:gravity="center" android:gravity="center"
android:padding="10dp" android:padding="10dp"
android:singleLine="true" android:singleLine="true"

View File

@@ -32,11 +32,13 @@
<androidx.constraintlayout.utils.widget.ImageFilterView <androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/title_restart_icon" android:id="@+id/title_restart_icon"
style="?android:attr/selectableItemBackgroundBorderless" style="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="25dp" android:layout_width="28dp"
android:layout_height="25dp" android:layout_height="28dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:src="@mipmap/restart" android:alpha="0.85"
android:tint="@color/colorTextGray" /> android:src="@mipmap/ic_restart"
android:tint="@color/colorTextGray"
android:tooltipText="重启系统界面" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
@@ -47,7 +49,7 @@
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:background="@drawable/dark_round" android:background="@drawable/bg_dark_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center"> android:gravity="center">
@@ -57,7 +59,7 @@
android:layout_height="25dp" android:layout_height="25dp"
android:layout_marginStart="25dp" android:layout_marginStart="25dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:src="@mipmap/warn" android:src="@mipmap/ic_warn"
android:tint="@color/white" /> android:tint="@color/white" />
<LinearLayout <LinearLayout
@@ -74,7 +76,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:text="模块未激活" android:text="模块状态未知"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="18sp" /> android:textSize="18sp" />
@@ -84,7 +86,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:alpha="0.8" android:alpha="0.8"
android:text="当前版本:%1" android:text="模块版本:%1"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="13sp" /> android:textSize="13sp" />
@@ -93,7 +95,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:alpha="0.8" android:alpha="0.8"
android:text="MIUI 版本:%1" android:text="系统版本:%1"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="13sp" /> android:textSize="13sp" />
</LinearLayout> </LinearLayout>
@@ -119,7 +121,7 @@
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:background="@drawable/permotion_round" android:background="@drawable/bg_permotion_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="horizontal" android:orientation="horizontal"
@@ -130,7 +132,7 @@
android:layout_height="15dp" android:layout_height="15dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:alpha="0.85" android:alpha="0.85"
android:src="@mipmap/about" android:src="@mipmap/ic_about"
android:tint="@color/colorTextDark" /> android:tint="@color/colorTextDark" />
<TextView <TextView
@@ -151,7 +153,7 @@
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:animateLayoutChanges="true" android:animateLayoutChanges="true"
android:background="@drawable/permotion_round" android:background="@drawable/bg_permotion_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
@@ -182,18 +184,19 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.6" android:alpha="0.6"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="模块关闭后功能都将彻底停止工作,以下选项都将不再生效。" android:text="模块关闭后一切功能都将彻底停止工作。"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/config_item_color_hook"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:background="@drawable/permotion_round" android:background="@drawable/bg_permotion_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
@@ -217,6 +220,26 @@
android:text="此选项默认开启,开启后将自动对 APP 通知图标进行判断,保持高版本 API 的 APP 不规范的彩色图标不被着色为白、黑色块并对图标进行圆角优化,关闭后将按照 Android API 规范对 APP 通知进行图标着色,可能会出现着色为黑白、色块情况。" android:text="此选项默认开启,开启后将自动对 APP 通知图标进行判断,保持高版本 API 的 APP 不规范的彩色图标不被着色为白、黑色块并对图标进行圆角优化,关闭后将按照 Android API 规范对 APP 通知进行图标着色,可能会出现着色为黑白、色块情况。"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> 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>
<LinearLayout <LinearLayout
@@ -226,7 +249,8 @@
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:background="@drawable/permotion_round" android:animateLayoutChanges="true"
android:background="@drawable/bg_permotion_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
@@ -248,7 +272,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:background="@drawable/button_round" android:background="@drawable/bg_button_round"
android:gravity="center" android:gravity="center"
android:padding="10dp" android:padding="10dp"
android:singleLine="true" android:singleLine="true"
@@ -264,7 +288,19 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.6" android:alpha="0.6"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="此选项默认开启,开启后将对优化名单内 APP 通知小图标进行色彩修复,使得它们的图标看起来更加美观。" android:text="首次安装请打开名单列表从云端更新数据,后期适配的内容也请手动打开名单列表重新拉取数据以检查更新,数据更新后重启系统界面即可生效。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<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="此选项默认开启,开启后将对优化名单内的 APP 通知小图标使用单色调进行修复,特别是通过 MIPUSH 推送的通知,它们始终是 APP 默认图标(彩色的 APP 图标),修复后使得它们的图标看起来更加符合 Android 原生的统一规范。"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@@ -275,7 +311,7 @@
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:background="@drawable/permotion_round" android:background="@drawable/bg_permotion_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
@@ -296,7 +332,7 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.6" android:alpha="0.6"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="隐藏模块图标后界面可能会被关闭,将不会再在桌面显示,你可以在 EdXposed、LsPosed 中找到模块设置并打开。" android:text="隐藏模块图标后界面可能会被关闭,将不会再在桌面显示,你可以在 EdXposed、LSPosed 中找到模块设置并打开。"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@@ -307,7 +343,7 @@
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:background="@drawable/permotion_round" android:background="@drawable/bg_permotion_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
@@ -324,7 +360,7 @@
android:layout_height="15dp" android:layout_height="15dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:alpha="0.85" android:alpha="0.85"
android:src="@mipmap/about" android:src="@mipmap/ic_about"
android:tint="@color/colorTextGray" /> android:tint="@color/colorTextGray" />
<TextView <TextView
@@ -343,7 +379,7 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="Q.这个模块是如何诞生的?\nA.这个模块诞生来源于 MIUI 的乱改和不规范,本来 MIUI 9 之后,官方给出了原生通知图标样式,后面由于用户反应通知栏经常出现黑白块这当然不是系统的错,而是国内 APP 极其不规范的通知图标设计,于是 MIUI 选择直接忽略这个问题把全部图标都改成了 APP 的彩色图标,使得之前拥有自有样式的原生图标也被破坏,通知中“setSmallIcon”不再有效,这个模块就是为了修复被 MIUI 开发组忽略的图标问题,并完美地给 MIUI 修复了黑白块图标的问题。" android:text="Q.这个模块是如何诞生的?\nA.这个模块诞生来源于 MIUI 的乱改和不规范,本来 MIUI 9 之后,官方给出了原生通知图标样式,后面由于用户反应通知栏经常出现黑白块。\n这当然不是系统的错,而是国内 APP 和 MIPUSH 的通知极其不规范的通知图标设计。\n但是呢接到反馈后 MIUI 开发组选择直接忽略这个问题,在 2021-5-18 的开发版开始,把全部通知图标都改成了 APP 的彩色图标,使得之前拥有自有样式的原生图标也被破坏。\n对于 Android 开发者来说,官方文档中的 “setSmallIcon” 不再适用于魔改后的 MIUI这将会严重破坏非常多的状态图标。\n当然国内的手机生态除了 MIPUSH 的营销通知就是社交软件的通知,可能大部分人都不会在意这件事情。\n但是,这个模块就是为了修复被 MIUI 开发组忽略的图标问题才诞生的,并完美地给 MIUI 修复了黑白块图标的问题。"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
@@ -353,7 +389,7 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="Q.如何使用?\nA.模块仅支持 EdXposed、LsPosed,由于涉及到修改系统应用,不支持太极(阴)框架,在 LsPosed 的作用域中,只需勾选“系统界面”即可,应用设置后需要重启系统界面。" android:text="Q.如何使用?\nA.模块仅支持 LSPosedEdXposed 也可以使用但随时停止支持,由于模块涉及到修改系统应用,不支持其它 Hook 框架,在 LSPosed 的作用域中,只需勾选“系统界面”(旧版本为“系统 UI”)即可,应用设置后需要重启系统界面。"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
@@ -363,7 +399,7 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="Q.哪些是已知问题?\nA.以下是问题描述列表:\n(1) MIUI 自己的系统应用的通知图标仍然可能出现黑白块的情况,可使用通知图标优化暂时修复其图标问题,后期只能等官方修复(基本上没什么可能性)。\n(2) 动态小图标可能会在高版本系统中闪烁,这是 MIUI 的问题,后期仍在想解决办法。\n(3) 使用 Zygisk 方式运行的 Lsposed 可能会发生通知优化图标 Hook 不生效的问题(出现黑白块),仅在最新版本的 MIUI 13 开发内测版中遇到,若出现问题请使用 Ramdisk 版本的 Lsposed。" 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:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
@@ -384,7 +420,7 @@
android:layout_marginLeft="15dp" android:layout_marginLeft="15dp"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:background="@drawable/permotion_round" android:background="@drawable/bg_permotion_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
@@ -401,7 +437,7 @@
android:layout_height="15dp" android:layout_height="15dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:alpha="0.85" android:alpha="0.85"
android:src="@mipmap/about" android:src="@mipmap/ic_about"
android:tint="@color/colorTextGray" /> android:tint="@color/colorTextGray" />
<TextView <TextView
@@ -419,7 +455,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:alpha="0.8" android:alpha="0.8"
android:lineSpacingExtra="6dp" android:lineSpacingExtra="6dp"
android:text="本软件是免费开源项目,遵循 GPL 协议,你可以点击这里前往 Github 查看源码以及获取模块更新。\n严禁以任何形式贩卖、商用本软件否则开发者有权追究其法律责任。" android:text="本软件是免费开源项目,遵循 AGPL3.0 协议,你可以点击这里前往 Github 查看源码以及获取模块更新。"
android:textColor="@color/colorTextDark" android:textColor="@color/colorTextDark"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@@ -431,7 +467,7 @@
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:background="@drawable/permotion_round" android:background="@drawable/bg_permotion_round"
android:elevation="0dp" android:elevation="0dp"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
@@ -460,7 +496,7 @@
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@mipmap/qr_pay" /> android:src="@mipmap/bg_qr_pay" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<TextView <TextView
@@ -473,6 +509,20 @@
android:textColor="@color/colorTextGray" android:textColor="@color/colorTextGray"
android:textSize="16sp" /> android:textSize="16sp" />
</LinearLayout> </LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp"
android:autoLink="web"
android:background="@drawable/bg_permotion_round"
android:lineSpacingExtra="6dp"
android:padding="10dp"
android:text="此模块使用 YukiHookAPI 构建。\n点击这里了解更多 https://github.com/fankes/YukiHookAPI"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</LinearLayout> </LinearLayout>

View File

@@ -3,8 +3,9 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/permotion_round" android:background="@drawable/bg_permotion_round"
android:baselineAligned="false" android:baselineAligned="false"
android:descendantFocusability="blocksDescendants"
android:gravity="center|start" android:gravity="center|start"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="15dp" android:padding="15dp"
@@ -28,7 +29,7 @@
android:layout_width="15dp" android:layout_width="15dp"
android:layout_height="15dp" android:layout_height="15dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:src="@mipmap/about" android:src="@mipmap/ic_about"
android:tint="@color/colorTextGray" /> android:tint="@color/colorTextGray" />
<TextView <TextView

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="15dp"
android:paddingRight="15dp"
tools:ignore="HardcodedText">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/dia_icon_filter_input_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:hint="可输入 APP 名称、包名"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="15dp"
android:paddingRight="15dp"
tools:ignore="HardcodedText">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8.5dp"
android:layout_marginRight="8.5dp"
android:layout_marginBottom="10dp"
android:lineSpacingExtra="6dp"
android:text="在线规则将不定期更新,建议定期同步列表以适配更多 APP若无法同步请自行寻找解决方法或魔法上网。"
android:textSize="14sp" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/dia_sf_rd1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="从 FastGit 获取" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/dia_sf_rd2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="从 Github Raw 获取" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/dia_sf_rd3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="从自定义地址获取" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/dia_sf_text_lin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/dia_sf_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:hint="请输入在线地址 URL"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="15dp"
android:paddingRight="15dp"
tools:ignore="HardcodedText">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/dia_sfs_input_edit"
android:layout_width="match_parent"
android:layout_height="150dp"
android:ellipsize="end"
android:gravity="center|start|top"
android:hint="请粘贴 JSON 规则到此处"
android:textSize="14sp" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

View File

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 201 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

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

View File

@@ -1,30 +1,12 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins {
buildscript { id 'com.android.application' version '7.1.2' apply false
ext.kotlin_version = "1.6.10" id 'com.android.library' version '7.1.2' apply false
repositories { id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
google()
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url "https://www.jitpack.io" }
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.4"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
} }
allprojects { ext {
repositories { appVersionName = "1.95"
google() appVersionCode = 22
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url "https://www.jitpack.io" }
mavenCentral()
}
} }
task clean(type: Delete) { task clean(type: Delete) {

View File

@@ -1,6 +1,6 @@
#Mon Jan 24 04:31:03 CST 2022 #Tue Feb 15 02:09:05 CST 2022
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@@ -1,2 +1,19 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
maven { url "https://api.xposed.info/" }
maven { url "https://www.jitpack.io" }
maven { url "https://s01.oss.sonatype.org/content/repositories/releases" }
mavenCentral()
}
}
rootProject.name = "MIUINativeNotifyIcon" rootProject.name = "MIUINativeNotifyIcon"
include ':app' include ':app'