70 Commits
1.71 ... 2.16

Author SHA1 Message Date
c27f124b25 Update version to 2.16,fix more bugs. 2022-03-18 05:51:42 +08:00
Fankesyooni
09be1ea2c2 Update README.md 2022-03-18 05:51:22 +08:00
f5f90d712a Update version to 2.16,fix more bugs. 2022-03-18 05:50:02 +08:00
7a1f139adc Update version to 2.16,fix more bugs. 2022-03-18 05:49:44 +08:00
3ed382165b Update YukiHookAPI 2022-03-18 05:47:35 +08:00
5173051e66 修复一个通知图标颜色判断问题 2022-03-18 02:33:46 +08:00
c30ee53796 增加提醒在线规则更新功能 2022-03-18 00:29:21 +08:00
ae2fcda709 Update README.md 2022-03-17 23:57:23 +08:00
1a077259d1 更正全部图标为半透明效果 2022-03-17 23:48:40 +08:00
6e95239d94 更正全部图标为半透明效果 2022-03-17 23:19:50 +08:00
c1b08c93d0 更正黑色下的图标为半透明效果 2022-03-17 23:02:31 +08:00
90dda0aa52 Update version to 2.15,fix more bugs. 2022-03-17 11:33:53 +08:00
4699eb137e 再次修复通知图标个数对旧版本 MIUI 不生效的问题 2022-03-17 11:32:44 +08:00
ef47eef989 Update version to 2.1,fix more bugs. 2022-03-17 10:13:09 +08:00
7a5df6457f 修复状态栏通知个数显示导致隐藏图标无效的 BUG 2022-03-17 10:11:26 +08:00
3d977998c1 Update version to 2.0,fix more bugs. 2022-03-17 05:15:11 +08:00
014e073af6 Merge code 2022-03-17 05:14:29 +08:00
0be6b9c910 Merge code 2022-03-17 05:14:10 +08:00
3816aab335 更新帮助文案 2022-03-17 05:08:08 +08:00
3b77a1cd82 更新帮助文案 2022-03-17 05:07:17 +08:00
5c5fae4ef6 修改项目地址为图标 2022-03-17 05:03:52 +08:00
ea665fdc30 增加解除状态栏通知图标个数限制功能以及优化界面风格 2022-03-17 04:56:36 +08:00
740a250a59 修复状态栏图标不是纯黑的问题 2022-03-17 03:35:18 +08:00
f433da040c Merge code 2022-03-13 00:43:05 +08:00
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
35 changed files with 1557 additions and 4048 deletions

View File

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

2
.idea/gradle.xml generated
View File

@@ -7,13 +7,13 @@
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="11" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>

17
.idea/misc.xml generated
View File

@@ -6,18 +6,29 @@
<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-v24/ic_launcher_foreground.xml" value="0.44871794871794873" />
<entry key="app/src/main/res/drawable/bg_dark_round.xml" value="0.2325" />
<entry key="app/src/main/res/drawable/bg_green_round.xml" value="0.255" />
<entry key="app/src/main/res/drawable/bg_warn_round.xml" value="0.2325" />
<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/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-w936dp/dia_status_icon_cout.xml" value="0.935546875" />
<entry key="app/src/main/res/layout/activity_config.xml" value="0.42168674698795183" />
<entry key="app/src/main/res/layout/activity_login.xml" value="0.4375" />
<entry key="app/src/main/res/layout/activity_main.xml" value="0.36160137752905724" />
<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.3448275862068966" />
<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.xml" value="0.3591278324070115" />
<entry key="app/src/main/res/layout/dia_source_from_string.xml" value="0.4307692307692308" />
<entry key="app/src/main/res/layout/dia_status_icon_count.xml" value="0.45" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.44871794871794873" />
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@@ -1,44 +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
iconColor = 0, // 通知栏中显示的图标颜色 - 设置为 0 使用系统默认颜色 (不设置颜色可不写)
contributorName = "" // 贡献者昵称
)
```
- 图标大小建议保持在 50x50
- 提交时请将后方的注释删除,否则不予合并代码
## 其它要求
- 1.调试性质或大批量注释代码,禁止提交
- 2.类名和方法名仅能由开发者进行修改和提交,禁止随意修改项目名称、方法名称以及类名
- 3.禁止随意更新项目依赖以及增加新的依赖,有问题请提前提交到 issues 进行说明
- 4.禁止更新项目版本号,版本号交由开发者合并代码并发布 release 版本
- 5.代码语言要求,请统一使用 Kotlin除特殊情况外不接受其他语言的提交
- 6.以上

View File

@@ -2,7 +2,8 @@
![Eclipse Marketplace](https://img.shields.io/badge/build-passing-brightgreen)
![Eclipse Marketplace](https://img.shields.io/badge/license-AGPL3.0-blue)
![Eclipse Marketplace](https://img.shields.io/badge/version-v1.71-green)
![Eclipse Marketplace](https://img.shields.io/badge/version-v2.16-green)
[![Telegram](https://img.shields.io/static/v1?label=Telegram&message=交流讨论&color=0088cc)](https://t.me/XiaofangInternet)
<br/><br/>
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/app/src/main/ic_launcher-playstore.png" width = "100" height = "100"/>
<br/>
@@ -12,14 +13,15 @@ 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.71-green)</a>
<a href='https://github.com/fankes/MIUINativeNotifyIcon/releases'>![Eclipse Marketplace](https://img.shields.io/badge/download-v2.16-green)</a>
<br/><br/>
⚠️ 适配说明<br/>
- 此模块仅支持 LSPosed(作用域“系统界面”)、~~EdXposed(随时停止支持)~~、不支持太极无极
- 请确保你使用的是 MIUI 官方版本,任何第三方官改包发生的问题,开发者没有义务去解决和修复,请自求多福
- 目前最低支持基于 Android 9 版本的 MIUI 12 或 MIUI 12.5(最低建议)
- 建议最低从 MIUI 12.5 `2021-5-18` 开发版以后开始使用模块,之前的版本可能或多或少存在 MIUI 自身 BUG 不生效、黑白块的问题
- 请始终保持最新版本的 LSPosed旧版本可能会出现 Hook 不生效的问题
- 请始终保持最新版本的 LSPosed旧版本可能会出现 Hook 不生效的问题,若最新版本依然不生效请在作用域中长按“系统界面”(“系统 UI”)选择重新优化
# 请勿用于非法用途
@@ -28,16 +30,11 @@ Fix the native notification bar icon function abandoned by the MIUI development
[Release](https://github.com/fankes/MIUINativeNotifyIcon/releases)
及[蓝奏云](https://fankes.lanzouy.com/b030o2e8h),从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。
# 开始贡献
# 贡献通知图标优化名单
由于国内厂商 APP 的不规范彩色图标影响整体图标的美观,现在开放第三方 APP 的通知图标适配<br/>
欢迎为通知图标优化名单贡献宝贵资源!<br/>
此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方<br/>
- [CONTRIBUTING](https://github.com/fankes/MIUINativeNotifyIcon/blob/master/CONTRIBUTING.md)
# 通知测试
你可以 [点击这里下载](https://github.com/fankes/MIUINativeNotifyIcon/blob/master/tool/NotifyTester.apk) 工具测试通知图标是否生效。
- [Android 通知图标规范适配计划](https://github.com/fankes/AndroidNotifyIconAdapt)
# 历史背景

View File

@@ -63,15 +63,14 @@ tasks.whenTaskAdded {
}
dependencies {
compileOnly 'de.robv.android.xposed:api:82'
implementation 'com.highcapable.yukihookapi:api:1.0.5-fix'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.5-fix'
implementation "com.github.topjohnwu.libsu:core:3.1.2"
implementation 'androidx.annotation:annotation:1.3.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
compileOnly 'de.robv.android.xposed:api:82'
implementation 'com.highcapable.yukihookapi:api:1.0.2'
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0.2'
implementation 'com.geyifeng.immersionbar:immersionbar:3.2.0'
implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'

View File

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

View File

@@ -36,15 +36,10 @@ class MNNApplication : Application() {
/** 调用全局静态实例 */
val appContext get() = context ?: error("App is death")
/** 自身 APP 是否已启动 */
var isMineStarted = false
}
override fun onCreate() {
super.onCreate()
/** 设置状态 */
isMineStarted = true
/** 设置静态实例 */
context = this
/** 跟随系统夜间模式 */

View File

@@ -23,12 +23,12 @@
package com.fankes.miui.notify.bean
import android.graphics.Bitmap
import com.fankes.miui.notify.utils.base64
import com.fankes.miui.notify.utils.factory.base64
import java.io.Serializable
/**
* 通知栏小图标 bean
* @param appName APP 名称 - 仅限默认语言区域
* @param appName APP 名称
* @param packageName 包名
* @param iconBitmap 图标位图
* @param iconColor 通知栏中显示的图标颜色 - 设置为 0 使用系统默认颜色
@@ -47,4 +47,13 @@ data class IconDataBean(
) : Serializable {
fun toEnabledName() = ("$appName$packageName").base64 + "_enable"
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

@@ -32,6 +32,16 @@ object HookConst {
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 ENABLE_HOOK_STATUS_ICON_COUNT = "_enable_hook_status_icon_count"
const val NOTIFY_ICON_DATAS = "_notify_icon_datas"
const val HOOK_STATUS_ICON_COUNT = "_hook_status_icon_count"
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

@@ -22,6 +22,7 @@
*/
package com.fankes.miui.notify.hook
import android.app.NotificationManager
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
@@ -31,20 +32,25 @@ import android.graphics.drawable.Icon
import android.os.Build
import android.service.notification.StatusBarNotification
import android.view.View
import android.view.ViewGroup
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_HOOK_STATUS_ICON_COUNT
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.HOOK_STATUS_ICON_COUNT
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.fankes.miui.notify.utils.factory.*
import com.fankes.miui.notify.utils.tool.BitmapCompatTool
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.factory.*
@@ -54,6 +60,7 @@ 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
@@ -69,20 +76,33 @@ class HookEntry : YukiHookXposedInitProxy {
private const val NotificationHeaderViewWrapperInjectorClass =
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapperInjector"
/** MIUI 新版本存在的类 */
private const val NotificationHeaderViewWrapperClass =
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationHeaderViewWrapper"
/** MIUI 新版本存在的类 */
private const val NotificationViewWrapperClass =
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationViewWrapper"
/** 未确定是否只有旧版本存在的类 */
private const val ExpandableNotificationRowClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.ExpandableNotificationRow"
/** 原生存在的类 */
private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
/** 原生存在的类 */
private const val StatusBarIconViewClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.StatusBarIconView"
/** 原生存在的类 */
private const val NotificationIconContainerClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.phone.NotificationIconContainer"
/** 根据多个版本存在不同的包名相同的类 */
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",
@@ -96,6 +116,12 @@ class HookEntry : YukiHookXposedInitProxy {
)
}
/** 缓存的通知优化图标数组 */
private var iconDatas = ArrayList<IconDataBean>()
/** 是否显示通知图标 - 跟随 Hook 保存 */
private var isShowNotificationIcons = true
/**
* - 这个是修复彩色图标的关键核心代码判断
*
@@ -118,18 +144,20 @@ class HookEntry : YukiHookXposedInitProxy {
} else BitmapCompatTool.isGrayscaleDrawable(drawable)
/**
* 是否为版本 MIUI 方案
* 是否为版本 MIUI 方案
*
* 拥有状态栏图标颜色检查功能
* 拥有 “handleHeaderViews” 方法
* @return [Boolean]
*/
private val PackageParam.hasIgnoreStatusBarIconColor
private val PackageParam.hasHandleHeaderViews
get() = safeOfFalse {
NotificationUtilClass.clazz.hasMethod(name = "ignoreStatusBarIconColor", ExpandedNotificationClass.clazz)
NotificationHeaderViewWrapperClass.clazz.hasMethod(name = "handleHeaderViews")
}
/**
* 获取当前通知栏的样式
*
* - ❗新版本可能不存在这个方法
* @return [Boolean]
*/
private val PackageParam.isShowMiuiStyle
@@ -137,15 +165,6 @@ class HookEntry : YukiHookXposedInitProxy {
NotificationUtilClass.clazz.method { name = "showMiuiStyle" }.get().invoke() ?: false
}
/**
* 获取 [ExpandedNotificationClass] 的应用名称
* @param instance 通知实例
* @return [String]
*/
private fun PackageParam.findAppName(instance: Any?) = safeOf(default = "<unknown>") {
ExpandedNotificationClass.clazz.method { name = "getAppName" }.get(instance).invoke() ?: "<empty>"
}
/**
* 适配通知栏、状态栏图标
*
@@ -159,10 +178,51 @@ class HookEntry : YukiHookXposedInitProxy {
private fun StatusBarNotification.compatNotifyIcon(context: Context, iconDrawable: Drawable) = safeOf(iconDrawable) {
/** 给 MIPUSH 设置 APP 自己的图标 */
if (isXmsf && opPkgName.isNotBlank())
context.packageManager.getPackageInfo(opPkgName, 0).applicationInfo.loadIcon(context.packageManager)
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}]"
)
}
/**
* 获取推送通知的包名
*
@@ -203,11 +263,31 @@ class HookEntry : YukiHookXposedInitProxy {
*/
private val PackageParam.globalContext
get() = safeOfNull {
if (SystemUIApplicationClass.clazz.hasMethod(name = "getContext"))
SystemUIApplicationClass.clazz.method { name = "getContext" }.get().invoke<Context>()
else null
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 状态栏小图标
*
@@ -222,36 +302,27 @@ class HookEntry : YukiHookXposedInitProxy {
expandedNf: StatusBarNotification?,
iconDrawable: Drawable?,
it: (Bitmap) -> Unit
) = safeRun(msg = "GetSmallIconOnSet") {
if (iconDrawable == null) return@safeRun
) = runSafe(msg = "GetSmallIconOnSet") {
if (iconDrawable == null) return@runSafe
/** 如果没开启修复 APP 的彩色图标 */
if (!prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) return@safeRun
if (!prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) return@runSafe
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.also { notifyInstance ->
/** 判断是 MIUI 样式就停止 Hook */
if (context.isMiuiNotifyStyle) {
it(notifyInstance.findAppIcon(context).toBitmap())
return@runSafe
}
/** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = notifyInstance.isXmsf || !isGrayscaleIcon(context, iconDrawable)
/** 目标彩色通知 APP 图标 */
var customIcon: Bitmap? = null
if (prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true))
run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
isAppNotifyHookOf(it)
) {
if (isNotGrayscaleIcon || isAppNotifyHookAllOf(it))
customIcon = it.iconBitmap
return@run
}
}
}
val customIcon = compatCustomIcon(!isNotGrayscaleIcon, notifyInstance.opPkgName).first
/** 打印日志 */
if (prefs.getBoolean(ENABLE_MODULE_LOG))
loggerD(msg = "Icon --> [${findAppName(notifyInstance)}][${notifyInstance.opPkgName}] custom [${customIcon != null}] grayscale [${!isNotGrayscaleIcon}] xmsf [${notifyInstance.isXmsf}]")
printLogcat(tag = "StatusIcon", context, notifyInstance, isCustom = customIcon != null, !isNotGrayscaleIcon)
when {
/** 处理自定义通知图标优化 */
customIcon != null -> it(customIcon!!)
customIcon != null -> it(customIcon)
/** 若不是灰度图标自动处理为圆角 */
isNotGrayscaleIcon -> it(notifyInstance.compatNotifyIcon(context, iconDrawable).toBitmap())
}
@@ -265,154 +336,141 @@ class HookEntry : YukiHookXposedInitProxy {
* @param context 实例
* @param expandedNf 通知实例
* @param iconImageView 通知图标实例
* @param isExpanded 通知是否展开 - 可做最小化通知处理
*/
private fun PackageParam.hookNotifyIconOnSet(context: Context, expandedNf: StatusBarNotification?, iconImageView: ImageView) =
safeRun(msg = "AutoSetAppIconOnSet") {
/** 如果没开启修复 APP 的彩色图标 */
if (!prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) return@safeRun
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.let { notifyInstance ->
/** 是否开启修复 APP 的彩色图标 */
val isNotifyIconFix = prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)
private fun PackageParam.hookNotifyIconOnSet(
context: Context,
expandedNf: StatusBarNotification?,
iconImageView: ImageView,
isExpanded: Boolean
) = runSafe(msg = "AutoSetAppIconOnSet") {
/** 判断是 MIUI 样式就停止 Hook */
if (context.isMiuiNotifyStyle) return@runSafe
/** 如果没开启修复 APP 的彩色图标 */
if (!prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) return@runSafe
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.let { notifyInstance ->
/** 新版风格反色 */
val newStyle = if (context.isSystemInDarkMode) 0xFF2D2D2D.toInt() else Color.WHITE
/** 新版风格反色 */
val newStyle = if (context.isSystemInDarkMode) 0xFF2D2D2D.toInt() else Color.WHITE
/** 旧版风格反色 */
val oldStyle = if (context.isNotSystemInDarkMode) 0xFF707070.toInt() else Color.WHITE
/** 旧版风格反色 */
val oldStyle = if (context.isNotSystemInDarkMode) 0xFF707070.toInt() else Color.WHITE
/** 通知图标原始颜色 */
val iconColor = notifyInstance.notification.color
/** 通知图标原始颜色 */
val iconColor = notifyInstance.notification.color
/** 是否有通知栏图标颜色 */
val hasIconColor = iconColor != 0
/** 是否有通知栏图标颜色 */
val hasIconColor = iconColor != 0
/** 通知图标适配颜色 */
val supportColor = iconColor.let {
when {
isUpperOfAndroidS -> newStyle
it == 0 -> oldStyle
else -> it
}
/** 通知图标适配颜色 */
val supportColor = iconColor.let {
when {
isUpperOfAndroidS -> newStyle
it == 0 || !isExpanded -> oldStyle
else -> it
}
}
/** 获取通知小图标 */
val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
/** 获取通知小图标 */
val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
/** 判断图标风格 */
val isGrayscaleIcon = !notifyInstance.isXmsf && isGrayscaleIcon(context, iconDrawable)
/** 判断图标风格 */
val isGrayscaleIcon = !notifyInstance.isXmsf && isGrayscaleIcon(context, iconDrawable)
/** 自定义默认小图标 */
var customIcon: Bitmap? = null
/** 自定义默认小图标 */
var customIcon: Bitmap?
/** 自定义默认小图标颜色 */
var customIconColor = 0
if (isNotifyIconFix) run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
isAppNotifyHookOf(it)
) {
if (!isGrayscaleIcon || isAppNotifyHookAllOf(it)) {
customIcon = it.iconBitmap
customIconColor = it.iconColor
return@run
}
/** 自定义默认小图标颜色 */
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 (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
}
/** 清除原生的背景边距设置 */
if (isUpperOfAndroidS) setPadding(0, 0, 0, 0)
/** 清除原生的主题色背景圆圈颜色 */
if (isUpperOfAndroidS) background = null
}
}
}
}
/**
* Hook 通知栏小图标颜色
* 判断状态栏小图标颜色以及反射的核心方法
*
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
* @param context 实例
* @param expandedNf 状态栏实例
* @return [Boolean] 是否忽略通知图标颜色
*/
private fun PackageParam.hookIgnoreStatusBarIconColor(context: Context, expandedNf: StatusBarNotification?) =
if (prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)) safeOfFalse {
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
expandedNf?.let { notifyInstance ->
/** 获取通知小图标 */
val iconDrawable =
notifyInstance.notification.smallIcon.loadDrawable(context)
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)
/** 判断是否不是灰度图标 */
val isNotGrayscaleIcon = notifyInstance.isXmsf || !isGrayscaleIcon(context, iconDrawable)
/** 获取目标修复彩色图标的 APP */
var isTargetFixApp = false
/** 如果开启了自定义通知图标优化 */
if (prefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true))
run {
IconPackParams.iconDatas.forEach {
if ((notifyInstance.opPkgName == it.packageName ||
findAppName(notifyInstance) == it.appName) &&
isAppNotifyHookOf(it)
) {
if (isNotGrayscaleIcon || isAppNotifyHookAllOf(it)) isTargetFixApp = true
return@run
}
}
/** 获取目标修复彩色图标的 APP */
val isTargetFixApp = compatCustomIcon(!isNotGrayscaleIcon, notifyInstance.opPkgName).first != null
/**
* 只要不是灰度就返回彩色图标
* 否则不对颜色进行反色处理防止一些系统图标出现异常
*/
(if (isTargetFixApp) false else isNotGrayscaleIcon).also {
printLogcat(tag = "IconColor", context, expandedNf, isTargetFixApp, !isNotGrayscaleIcon)
}
/**
* 只要不是灰度就返回彩色图标
* 否则不对颜色进行反色处理防止一些系统图标出现异常
*/
if (isTargetFixApp) false else isNotGrayscaleIcon
} ?: true
} else false
} ?: true.also { printLogcat(tag = "IconColor", context, expandedNf = null, 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 onInit() = configs {
debugTag = "MIUINativeNotifyIcon"
isDebug = false
}
override fun onHook() = encase {
configs {
debugTag = "MIUINativeNotifyIcon"
isDebug = false
}
loadApp(SYSTEMUI_PACKAGE_NAME) {
when {
/** 不是 MIUI 系统停止 Hook */
@@ -425,6 +483,9 @@ class HookEntry : YukiHookXposedInitProxy {
!prefs.getBoolean(ENABLE_MODULE, default = true) -> loggerW(msg = "Aborted Hook -> Hook Closed")
/** 开始 Hook */
else -> {
/** 缓存图标数据 */
iconDatas = IconPackParams(param = this).iconDatas
/** 执行 Hook */
NotificationUtilClass.hook {
/** 强制回写系统的状态栏图标样式为原生 */
injectMember {
@@ -433,24 +494,11 @@ class HookEntry : YukiHookXposedInitProxy {
param(ExpandedNotificationClass.clazz)
}
/**
* 因为之前的 MIUI 版本的状态栏图标颜色会全部设置为白色的 - 找不到修复的地方就直接判断版本了
* 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook
* 为了防止 MIUI 自身的版本不同造成的各种 BUG
* 判断是 MIUI 样式就停止 Hook
*/
replaceAny { if (hasIgnoreStatusBarIconColor) false else isShowMiuiStyle }
replaceAny { globalContext?.isMiuiNotifyStyle ?: isShowMiuiStyle }
}
if (hasIgnoreStatusBarIconColor)
injectMember {
method {
name = "ignoreStatusBarIconColor"
param(ExpandedNotificationClass.clazz)
}
replaceAny {
hookIgnoreStatusBarIconColor(
context = globalContext ?: error("GlobalContext got null"),
expandedNf = args[0] as? StatusBarNotification?
)
}
}
/** 强制回写系统的状态栏图标样式为原生 */
injectMember {
var isUseLegacy = false
@@ -468,86 +516,146 @@ class HookEntry : YukiHookXposedInitProxy {
}.onFind { isUseLegacy = true }
}
afterHook {
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
if (hasIgnoreStatusBarIconColor || !isShowMiuiStyle)
(globalContext ?: args[0] 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) }
}
(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) }
}
}
}
}
if (NotificationHeaderViewWrapperInjectorClass.hasClass)
NotificationHeaderViewWrapperInjectorClass.hook {
/** 修复下拉通知图标自动设置回 APP 图标的方法 */
injectMember {
var isUseLegacy = false
StatusBarIconViewClass.hook {
/** Hook 状态栏图标的颜色 */
injectMember {
method { name = "updateIconColor" }
afterHook {
instance<ImageView>().also {
if (hasIgnoreStatusBarIconColor(it.context, field { name = "mNotification" }
.of<StatusBarNotification>(instance))) it.apply {
alpha = 1f
colorFilter = null
}
/**
* 防止图标不是纯黑的问题
* 图标在任何场景下跟随状态栏其它图标保持半透明
*/
else it.apply {
field { name = "mCurrentSetColor" }.of<Int>(instance)?.also { color ->
alpha = if (color.isWhite) 0.95f else 0.8f
setColorFilter(if (color.isWhite) color else Color.BLACK)
}
}
}
}
}
}
NotificationIconContainerClass.hook {
injectMember {
method { name = "calculateIconTranslations" }
afterHook {
/** 修复最新开发版状态栏图标只能显示一个的问题 */
instance<ViewGroup>().layoutParams.width = 9999
}
}
injectMember {
method { name = "updateState" }
beforeHook {
/** 解除状态栏通知图标个数限制 */
if (isShowNotificationIcons && prefs.getBoolean(ENABLE_HOOK_STATUS_ICON_COUNT, default = true))
field { name = "MAX_STATIC_ICONS" }
.get(instance).set(prefs.getInt(HOOK_STATUS_ICON_COUNT, default = 5)
.let { if (it in 0..100) it else 5 })
}
}
/** 旧版方法 - 新版不存在 */
injectMember {
method {
name = "setMaxStaticIcons"
param(IntType)
beforeHook { isShowNotificationIcons = firstArgs as Int > 0 }
}
}.ignoredNoSuchMemberFailure()
/** 新版方法 - 旧版不存在 */
injectMember {
method {
name = "miuiShowNotificationIcons"
param(BooleanType)
}
beforeHook { isShowNotificationIcons = firstArgs as Boolean }
}.ignoredNoSuchMemberFailure()
}.by { safeOfFalse { NotificationIconContainerClass.clazz.hasField(name = "MAX_STATIC_ICONS") } }
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(ContextClass, ImageViewClass, ExpandedNotificationClass.clazz)
}.remedys {
method {
name = "setAppIcon"
param(ImageViewClass, ExpandedNotificationClass.clazz)
}.onFind { isUseLegacy = true }
}
replaceUnit {
if (isUseLegacy)
hookNotifyIconOnSet(
context = globalContext ?: error("GlobalContext got null"),
args[1] as? StatusBarNotification?,
args[0] as ImageView
)
else
hookNotifyIconOnSet(
context = args[0] as? Context ?: globalContext ?: error("GlobalContext got null"),
args[2] as? StatusBarNotification?,
args[1] as ImageView
)
param(ImageViewClass, ExpandedNotificationClass.clazz)
}
}
/** 干掉下拉通知图标自动设置回 APP 图标的方法 - Android 12 */
if (isUpperOfAndroidS)
injectMember {
method {
name = "resetIconBgAndPaddings"
param(ImageViewClass, ExpandedNotificationClass.clazz)
}
intercept()
}
}
else
NotificationHeaderViewWrapperClass.hook {
/** 之前的版本解决方案 */
injectMember {
method { name = "handleHeaderViews" }
afterHook {
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
if (!hasIgnoreStatusBarIconColor && isShowMiuiStyle) return@afterHook
/** 获取小图标 */
val iconImageView =
NotificationHeaderViewWrapperClass.clazz
.field { name = "mIcon" }.of<ImageView>(instance) ?: return@afterHook
/**
* 从父类中得到 mRow 变量 - [ExpandableNotificationRowClass]
* 获取其中的得到通知方法
*/
val expandedNf =
ExpandableNotificationRowClass.clazz
.method { name = "getStatusBarNotification" }
.get(NotificationViewWrapperClass.clazz.field { name = "mRow" }.get(instance).self)
.invoke<StatusBarNotification>()
/** 执行 Hook */
hookNotifyIconOnSet(iconImageView.context, expandedNf, iconImageView)
}
intercept()
}.ignoredNoSuchMemberFailure()
injectMember {
method {
name = "resetIconBgAndPaddings"
param(ImageViewClass, ExpandedNotificationClass.clazz)
}
}
intercept()
}.ignoredNoSuchMemberFailure()
}.ignoredHookClassNotFoundFailure()
}
}
}

View File

@@ -24,8 +24,7 @@
package com.fankes.miui.notify.ui
import android.content.Intent
import android.net.Uri
import android.app.ProgressDialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -35,19 +34,28 @@ import android.widget.ListView
import android.widget.TextView
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.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.ui.base.BaseActivity
import com.fankes.miui.notify.utils.SystemUITool
import com.fankes.miui.notify.utils.showDialog
import com.fankes.miui.notify.utils.snake
import com.fankes.miui.notify.utils.toast
import com.fankes.miui.notify.utils.factory.*
import com.fankes.miui.notify.utils.tool.ClientRequestTool
import com.fankes.miui.notify.utils.tool.SystemUITool
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() {
@@ -60,9 +68,22 @@ class ConfigureActivity : BaseActivity() {
/** 回调滚动事件改变 */
private var onScrollEvent: ((Boolean) -> Unit)? = null
/** 全部的通知优化图标数据 */
private var iconAllDatas = ArrayList<IconDataBean>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_config)
/** 检查激活状态 */
if (!YukiHookModuleStatus.isActive()) {
showDialog {
title = "模块没有激活"
msg = "模块没有激活,你无法使用这里的功能,请先激活模块。"
confirmButton(text = "我知道了") { finish() }
noCancelable()
}
return
}
/** 返回按钮点击事件 */
findViewById<View>(R.id.title_back_icon).setOnClickListener { onBackPressed() }
/** 刷新适配器结果相关 */
@@ -94,7 +115,6 @@ class ConfigureActivity : BaseActivity() {
confirmButton {
if (editText.text.toString().isNotBlank()) {
filterText = editText.text.toString().trim()
onChanged?.invoke()
refreshAdapterResult()
} else {
toast(msg = "条件不能为空")
@@ -105,11 +125,12 @@ class ConfigureActivity : BaseActivity() {
if (filterText.isNotBlank())
neutralButton(text = "清除条件") {
filterText = ""
onChanged?.invoke()
refreshAdapterResult()
}
}
}
/** 设置同步列表按钮点击事件 */
findViewById<View>(R.id.config_title_sync).setOnClickListener { onStartRefresh() }
/** 设置列表元素和 Adapter */
findViewById<ListView>(R.id.config_list_view).apply {
adapter = object : BaseAdapter() {
@@ -174,30 +195,246 @@ class ConfigureActivity : BaseActivity() {
lateinit var switchOpen: MaterialSwitch
lateinit var switchAll: MaterialSwitch
}
}.apply { onChanged = { notifyDataSetChanged() } }
}.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 {
runCatching {
startActivity(Intent().apply {
action = "android.intent.action.VIEW"
data = Uri.parse("https://github.com/fankes/MIUINativeNotifyIcon")
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure {
toast(msg = "无法启动系统默认浏览器")
openBrowser(url = "https://github.com/fankes/AndroidNotifyIconAdapt/blob/main/CONTRIBUTING.md")
}
/** 装载数据 */
mockLocalData()
/** 更新数据 */
onStartRefresh()
}
/** 装载或刷新本地数据 */
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<View>(R.id.config_list_no_data_view).isVisible = iconDatas.isEmpty()
findViewById<TextView>(R.id.config_list_no_data_view).apply {
text = if (iconAllDatas.isEmpty()) "噫,竟然什么都没有~\n请点击右上角同步按钮获取云端数据" else "噫,竟然什么都没找到~"
isVisible = iconDatas.isEmpty()
}
}
/**
@@ -205,8 +442,8 @@ class ConfigureActivity : BaseActivity() {
* @return [Array]
*/
private val iconDatas
get() = if (filterText.isBlank()) IconPackParams.iconDatas
else IconPackParams.iconDatas.filter {
get() = if (filterText.isBlank()) iconAllDatas
else iconAllDatas.filter {
it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase())
}.toTypedArray()
}
}
}

View File

@@ -27,24 +27,29 @@ package com.fankes.miui.notify.ui
import android.content.ComponentName
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.fankes.miui.notify.BuildConfig
import com.fankes.miui.notify.R
import com.fankes.miui.notify.hook.HookConst.ENABLE_COLOR_ICON_COMPAT
import com.fankes.miui.notify.hook.HookConst.ENABLE_COLOR_ICON_HOOK
import com.fankes.miui.notify.hook.HookConst.ENABLE_HIDE_ICON
import com.fankes.miui.notify.hook.HookConst.ENABLE_HOOK_STATUS_ICON_COUNT
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.HOOK_STATUS_ICON_COUNT
import com.fankes.miui.notify.params.IconPackParams
import com.fankes.miui.notify.ui.base.BaseActivity
import com.fankes.miui.notify.utils.*
import com.fankes.miui.notify.utils.factory.*
import com.fankes.miui.notify.utils.tool.SystemUITool
import com.google.android.material.textfield.TextInputEditText
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
@@ -62,8 +67,8 @@ class MainActivity : BaseActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/** 设置文本 */
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_version).text = "模块版本:$moduleVersion"
findViewById<TextView>(R.id.main_text_miui_version).text = "系统版本:$miuiFullVersion"
when {
/** 判断是否为 MIUI 系统 */
isNotMIUI ->
@@ -94,9 +99,16 @@ class MainActivity : BaseActivity() {
}
/** 判断是否 Hook */
YukiHookModuleStatus.isActive() -> {
findViewById<LinearLayout>(R.id.main_lin_status).setBackgroundResource(R.drawable.bg_green_round)
findViewById<ImageFilterView>(R.id.main_img_status).setImageResource(R.mipmap.ic_success)
findViewById<TextView>(R.id.main_text_status).text = "模块已激活"
if (IconPackParams(context = this).iconDatas.isEmpty()
&& modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)
) showDialog {
title = "配置通知图标优化名单"
msg = "模块需要获取在线规则以更新“通知图标优化名单”,它现在是空的,这看起来是你第一次使用模块,请首先进行配置才可以使用相关功能。\n" +
"你可以随时在本页面下方找到“配置通知图标优化名单”手动前往。"
confirmButton(text = "前往") { startActivity(Intent(this@MainActivity, ConfigureActivity::class.java)) }
cancelButton()
noCancelable()
}
}
else ->
showDialog {
@@ -112,6 +124,10 @@ class MainActivity : BaseActivity() {
/** 初始化 View */
val moduleEnableSwitch = findViewById<SwitchCompat>(R.id.module_enable_switch)
val moduleEnableLogSwitch = findViewById<SwitchCompat>(R.id.module_enable_log_switch)
val statusIconCountItem = findViewById<View>(R.id.config_item_s_count_hook)
val statusIconCountChildItem = findViewById<View>(R.id.config_item_s_count_child_hook)
val statusIconCountSwitch = findViewById<SwitchCompat>(R.id.config_status_icon_count_switch)
val statusIconCountText = findViewById<TextView>(R.id.config_status_icon_count_text)
val colorIconHookItem = findViewById<View>(R.id.config_item_color_hook)
val notifyIconConfigItem = findViewById<View>(R.id.config_item_notify)
val hideIconInLauncherSwitch = findViewById<SwitchCompat>(R.id.hide_icon_in_launcher_switch)
@@ -120,25 +136,31 @@ class MainActivity : BaseActivity() {
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 存储的信息 */
var statusBarIconCount = modulePrefs.getInt(HOOK_STATUS_ICON_COUNT, default = 5)
colorIconHookItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
statusIconCountItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
colorIconCompatSwitch.isVisible = modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
colorIconCompatText.isVisible = modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
notifyIconConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true) &&
modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
moduleEnableLogSwitch.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, default = true)
notifyIconFixButton.isVisible = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_FIX, default = true)
statusIconCountSwitch.isChecked = modulePrefs.getBoolean(ENABLE_HOOK_STATUS_ICON_COUNT, default = true)
statusIconCountChildItem.isVisible = modulePrefs.getBoolean(ENABLE_HOOK_STATUS_ICON_COUNT, 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)
statusIconCountText.text = statusBarIconCount.toString()
moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_MODULE, b)
moduleEnableLogSwitch.isVisible = b
colorIconHookItem.isVisible = b
statusIconCountItem.isVisible = b
notifyIconConfigItem.isVisible = b && colorIconHookSwitch.isChecked
SystemUITool.showNeedRestartSnake(context = this)
}
@@ -156,6 +178,12 @@ class MainActivity : BaseActivity() {
PackageManager.DONT_KILL_APP
)
}
statusIconCountSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_HOOK_STATUS_ICON_COUNT, b)
statusIconCountChildItem.isVisible = b
SystemUITool.showNeedRestartSnake(context = this)
}
colorIconHookSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
modulePrefs.putBoolean(ENABLE_COLOR_ICON_HOOK, b)
@@ -177,47 +205,99 @@ class MainActivity : BaseActivity() {
}
/** 通知图标优化名单按钮点击事件 */
notifyIconFixButton.setOnClickListener { startActivity(Intent(this, ConfigureActivity::class.java)) }
/** 设置警告 */
findViewById<View>(R.id.config_warn_s_count_dis_tip).isGone = miuiVersionCode > 12.5
/** 修改状态栏通知图标个数按钮点击事件 */
findViewById<View>(R.id.config_status_icon_count_button).setOnClickListener {
showDialog {
title = "设置最多显示的图标个数"
var editText: TextInputEditText
addView(R.layout.dia_status_icon_count).apply {
editText = findViewById<TextInputEditText>(R.id.dia_status_icon_count_input_edit).apply {
requestFocus()
invalidate()
setText(statusBarIconCount.toString())
setSelection(statusBarIconCount.toString().length)
}
}
confirmButton {
when {
(runCatching { editText.text.toString().toInt() }.getOrNull() ?: -1)
!in 0..100 -> snake(msg = "请输入有效数值")
editText.text.toString().isNotBlank() -> runCatching {
statusBarIconCount = editText.text.toString().trim().toInt()
modulePrefs.putInt(HOOK_STATUS_ICON_COUNT, statusBarIconCount)
statusIconCountText.text = statusBarIconCount.toString()
SystemUITool.showNeedRestartSnake(context = this@MainActivity)
}.onFailure { snake(msg = "数值格式无效") }
else -> snake(msg = "请输入有效数值")
}
}
cancelButton()
}
}
/** 重启按钮点击事件 */
findViewById<View>(R.id.title_restart_icon).setOnClickListener { SystemUITool.restartSystemUI(context = this) }
/** 项目地址按钮点击事件 */
findViewById<View>(R.id.title_github_icon).setOnClickListener {
openBrowser(url = "https://github.com/fankes/MIUINativeNotifyIcon")
}
/** 恰饭! */
findViewById<View>(R.id.link_with_follow_me).setOnClickListener {
runCatching {
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
})
}.onFailure {
toast(msg = "你可能没有安装酷安")
}
openBrowser(url = "https://www.coolapk.com/u/876977", packageName = "com.coolapk.market")
}
/** 项目地址点击事件 */
findViewById<View>(R.id.link_with_project_address).setOnClickListener {
runCatching {
startActivity(Intent().apply {
action = "android.intent.action.VIEW"
data = Uri.parse("https://github.com/fankes/MIUINativeNotifyIcon")
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}.onFailure {
toast(msg = "无法启动系统默认浏览器")
}
/** 刷新模块状态 */
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() {
super.onResume()
/** MIUI 12 的版本特殊 - 所以给出提示 */
if (!isWarnDialogShowing && YukiHookModuleStatus.isActive() && miuiVersion == "12" && isMiuiNotifyStyle)
/** 刷新模块状态 */
refreshModuleStatus()
/** 经典样式启用后给出警告 */
if (!isWarnDialogShowing && YukiHookModuleStatus.isActive() && isMiuiNotifyStyle)
showDialog {
isWarnDialogShowing = true
title = "经典通知栏样式已启用"
msg = "在 MIUI 12 中启用了经典通知栏样式后状态栏图标将不再做原生处理,模块停止工作," +
"这取决于系统设置,你应当在 设置>通知管理>通知显示设置 中将样式设置为“原生样式”。"
confirmButton(text = "我知道了") { isWarnDialogShowing = false }
msg = "当你启用了经典通知栏样式后,为防止 MIUI 自身不规范 APP 图标被破坏,状态栏图标将不再做原生处理\n\n" +
"若要使用原生样式,请前往 设置>通知管理>通知显示设置 中将样式设置为“原生样式”,新版本为 设置>通知与控制中心>通知显示设置"
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()
}
}

View File

@@ -25,7 +25,7 @@ package com.fankes.miui.notify.ui.base
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.fankes.miui.notify.R
import com.fankes.miui.notify.utils.isNotSystemInDarkMode
import com.fankes.miui.notify.utils.factory.isNotSystemInDarkMode
import com.gyf.immersionbar.ktx.immersionBar
abstract class BaseActivity : AppCompatActivity() {

View File

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

View File

@@ -0,0 +1,83 @@
/*
* 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/3/13.
*/
@file:Suppress("unused")
package com.fankes.miui.notify.utils.factory
import com.highcapable.yukihookapi.hook.log.loggerE
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为空
* @return [T] 发生异常时返回设定值否则返回正常值
*/
inline fun <T> safeOfNull(result: () -> T): T? = safeOf(default = null, result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 false
* @return [Boolean] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfFalse(result: () -> Boolean) = safeOf(default = false, result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 true
* @return [Boolean] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfTrue(result: () -> Boolean) = safeOf(default = true, result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 false
* @return [String] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfNothing(result: () -> String) = safeOf(default = "", result)
/**
* 忽略异常返回值
* @param result 回调 - 如果异常为 false
* @return [Int] 发生异常时返回设定值否则返回正常值
*/
inline fun safeOfNan(result: () -> Int) = safeOf(default = 0, result)
/**
* 忽略异常返回值
* @param default 异常返回值
* @param result 正常回调值
* @return [T] 发生异常时返回设定值否则返回正常值
*/
inline fun <T> safeOf(default: T, result: () -> T) = try {
result()
} catch (_: Throwable) {
default
}
/**
* 忽略异常运行
* @param msg 出错输出的消息 - 默认为空
* @param block 正常回调
*/
inline fun <T> T.runSafe(msg: String = "", block: () -> Unit) {
runCatching(block).onFailure { if (msg.isNotBlank()) loggerE(msg = msg, e = it) }
}

View File

@@ -22,16 +22,22 @@
*/
@file:Suppress("DEPRECATION", "PrivateApi", "unused", "ObsoleteSdkInt")
package com.fankes.miui.notify.utils
package com.fankes.miui.notify.utils.factory
import android.app.Activity
import android.app.AlertDialog
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.net.Uri
import android.os.Build
import android.provider.Settings
import android.util.Base64
@@ -41,9 +47,10 @@ import com.google.android.material.snackbar.Snackbar
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
/**
* 系统深色模式是否开启
@@ -123,29 +130,61 @@ inline val isNotSupportMiuiVersion get() = !isSupportMiuiVersion
* @return [String]
*/
val miuiVersion
get() =
if (isMIUI)
findPropString(key = "ro.miui.ui.version.name", default = "V无法获取").let {
when (it) {
"V110" -> "11"
"V11" -> "11"
"V120" -> "12"
"V12" -> "12"
"V125" -> "12.5"
"V130" -> "13"
"V13" -> "13"
else -> it.replace(oldValue = "V", newValue = "")
}
}.trim()
else "NULL"
get() = if (isMIUI)
findPropString(key = "ro.miui.ui.version.name", default = "V无法获取").let {
when (it) {
"V110" -> "11"
"V11" -> "11"
"V120" -> "12"
"V12" -> "12"
"V125" -> "12.5"
"V130" -> "13"
"V13" -> "13"
else -> it.replace(oldValue = "V", newValue = "")
}
}.trim()
else "NULL"
/**
* 获取 MIUI 版本号
* @return [Float]
*/
val miuiVersionCode get() = safeOf(default = 0f) { miuiVersion.toFloat() }
/**
* 获取 MIUI 完全版本
* @return [String]
*/
val miuiFullVersion
get() = if (isMIUI) (miuiVersion + " " + findPropString(key = "ro.system.build.version.incremental"))
else "不是 MIUI 系统"
get() = if (isMIUI) findPropString(key = "ro.system.build.version.incremental").let {
if (it.lowercase().contains("a") ||
it.lowercase().contains("b") ||
it.lowercase().contains("c") ||
it.lowercase().contains("d") ||
it.lowercase().contains("e") ||
it.lowercase().contains("f") ||
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 系统"
/**
* 得到安装包信息
@@ -178,18 +217,35 @@ val Context.versionName get() = packageInfo.versionName ?: ""
*/
val Context.versionCode get() = packageInfo.versionCode
/**
* dp 转换为 px
* @return [Int]
*/
val Number.dp get() = (toFloat() * appContext.resources.displayMetrics.density).toInt()
/**
* dp 转换为 px
* @param context 使用的实例
* @return [Float]
*/
fun Number.dp(context: Context) = toFloat() * context.resources.displayMetrics.density
fun Number.dp(context: Context) = (toFloat() * context.resources.displayMetrics.density)
/**
* 是否为白色
* @return [Boolean]
*/
val Int.isWhite
get() = safeOfTrue {
val r = this and 0xff0000 shr 16
val g = this and 0x00ff00 shr 8
val b = this and 0x0000ff
(0.2126 * r + 0.7152 * g + 0.0722 * b) >= 128
}
/**
* Base64 加密
* @return [String]
*/
val Bitmap.base64
get() = safeOfNothing {
val baos = ByteArrayOutputStream()
compress(Bitmap.CompressFormat.PNG, 100, baos)
Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT)
}
/**
* Base64 加密
@@ -215,6 +271,21 @@ val ByteArray.bitmap: Bitmap get() = BitmapFactory.decodeByteArray(this, 0, size
*/
val String.bitmap: Bitmap get() = unbase64.bitmap
/**
* 设置对话框默认风格
* @param context 使用的实例
*/
fun AlertDialog.setDefaultStyle(context: Context) =
window?.setBackgroundDrawable(
GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
intArrayOf(Color.WHITE, Color.WHITE)
).apply {
shape = GradientDrawable.RECTANGLE
gradientType = GradientDrawable.LINEAR_GRADIENT
cornerRadius = 15.dp(context)
})
/**
* 获取系统 Prop
* @param key Key
@@ -260,98 +331,33 @@ fun Context.snake(msg: String, actionText: String = "", it: () -> Unit = {}) =
}.show()
/**
* 忽略异常返回值
* @param it 回调 - 如果异常为空
* @return [T] 发生异常时返回设定值否则返回正常值
* 启动系统浏览器
* @param url 网址
* @param packageName 指定包名 - 可不填
*/
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
}
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 msg 出错输出的消息 - 默认为空
* @param it 正常回调
* 复制到剪贴板
* @param content 要复制的文本
*/
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) {
fun Context.copyToClipboard(content: String) = runSafe {
(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 = "已复制")
}
}
}

View File

@@ -20,7 +20,7 @@
*
* This file is Created by fankes on 2022/2/19.
*/
package com.fankes.miui.notify.utils
package com.fankes.miui.notify.utils.tool
import android.graphics.*
import android.graphics.drawable.AnimationDrawable
@@ -29,6 +29,7 @@ import android.graphics.drawable.Drawable
import android.graphics.drawable.VectorDrawable
import android.util.ArrayMap
import androidx.core.graphics.drawable.toBitmap
import com.fankes.miui.notify.utils.factory.safeOfFalse
import kotlin.math.abs
/**

View File

@@ -0,0 +1,150 @@
/*
* 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.tool
import android.app.Activity
import android.app.ProgressDialog
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import com.fankes.miui.notify.utils.factory.safeOfNull
import com.fankes.miui.notify.utils.factory.setDefaultStyle
import com.fankes.miui.notify.utils.factory.showDialog
import com.fankes.miui.notify.utils.factory.snake
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

@@ -20,9 +20,13 @@
*
* This file is Created by fankes on 2022/2/8.
*/
package com.fankes.miui.notify.utils
package com.fankes.miui.notify.utils.tool
import android.content.Context
import com.fankes.miui.notify.utils.factory.execShellSu
import com.fankes.miui.notify.utils.factory.showDialog
import com.fankes.miui.notify.utils.factory.snake
import com.fankes.miui.notify.utils.factory.toast
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
@@ -58,4 +62,13 @@ object SystemUITool {
if (YukiHookModuleStatus.isActive())
context.snake(msg = "设置需要重启系统界面才能生效", actionText = "立即重启") { restartSystemUI(context) }
else context.snake(msg = "模块没有激活,更改不会生效")
/**
* 显示更新数据后需要重启系统界面的 [Snackbar]
* @param context 实例
*/
fun showNeedUpdateApplySnake(context: Context) =
if (YukiHookModuleStatus.isActive())
context.snake(msg = "数据已更新,请重启系统界面使更改生效", actionText = "立即重启") { restartSystemUI(context) }
else context.snake(msg = "模块没有激活,更改不会生效")
}

View File

@@ -29,8 +29,8 @@ import android.content.res.ColorStateList
import android.graphics.Color
import android.util.AttributeSet
import androidx.appcompat.widget.SwitchCompat
import com.fankes.miui.notify.utils.dp
import com.fankes.miui.notify.utils.drawable.drawabletoolbox.DrawableBuilder
import com.fankes.miui.notify.utils.factory.dp
class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) {
@@ -48,16 +48,16 @@ class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(cont
.rectangle()
.rounded()
.solidColor(0xFF656565.toInt())
.height(20.dp)
.cornerRadius(15.dp)
.height(20.dp(context).toInt())
.cornerRadius(15.dp(context).toInt())
.build()
thumbDrawable = DrawableBuilder()
.rectangle()
.rounded()
.solidColor(Color.WHITE)
.size(20.dp, 20.dp)
.cornerRadius(20.dp)
.strokeWidth(2.dp)
.size(20.dp(context).toInt(), 20.dp(context).toInt())
.cornerRadius(20.dp(context).toInt())
.strokeWidth(8.dp(context).toInt())
.strokeColor(Color.TRANSPARENT)
.build()
trackTintList = toColors(

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

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M192,736 L192,416C192,261.2 302,132.1 448,102.4L448,64C448,28.6 476.6,0 512,0 547.4,0 576,28.6 576,64L576,102.4C722,132.1 832,261.2 832,416L832,736 864.1,736C899.4,736 928,764.4 928,800 928,835.4 899.4,864 864.1,864L159.9,864C124.6,864 96,835.6 96,800 96,764.6 124.6,736 159.9,736L192,736ZM608,928C608,981 565,1024 512,1024 459,1024 416,981 416,928L608,928Z"/>
</vector>

View File

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

View File

@@ -29,6 +29,17 @@
android:textSize="25sp"
android:textStyle="bold" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/title_github_icon"
style="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="27dp"
android:layout_height="27dp"
android:layout_marginEnd="15dp"
android:alpha="0.85"
android:src="@mipmap/ic_github"
android:tint="@color/colorTextGray"
android:tooltipText="项目地址" />
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/title_restart_icon"
style="?android:attr/selectableItemBackgroundBorderless"
@@ -76,7 +87,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:text="模块未激活"
android:text="模块状态未知"
android:textColor="@color/white"
android:textSize="18sp" />
@@ -86,7 +97,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:alpha="0.8"
android:text="当前版本:%1"
android:text="模块版本:%1"
android:textColor="@color/white"
android:textSize="13sp" />
@@ -95,7 +106,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.8"
android:text="MIUI 版本:%1"
android:text="系统版本:%1"
android:textColor="@color/white"
android:textSize="13sp" />
</LinearLayout>
@@ -189,6 +200,117 @@
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/config_item_s_count_hook"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:layout_marginRight="15dp"
android:animateLayoutChanges="true"
android:background="@drawable/bg_permotion_round"
android:elevation="0dp"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingRight="15dp">
<com.fankes.miui.notify.view.MaterialSwitch
android:id="@+id/config_status_icon_count_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="解除状态栏通知图标个数限制"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="此选项默认开启MIUI 默认最多只能显示 3 个图标,其余图标将变成省略号,你可以在下方自定义最多显示的图标个数,修改为 0 则只会显示省略号代表图标个数,为防止发生异常,最大限制 100 个,超出的图标可能会被信号或网速指示器遮挡。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<TextView
android:id="@+id/config_warn_s_count_dis_tip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="⚠️ 仅支持 MIUI 12.5 后期开发版以及最新版本"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<LinearLayout
android:id="@+id/config_item_s_count_child_hook"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:gravity="center">
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:src="@drawable/ic_notify_icon"
app:tint="@color/colorTextGray" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:ellipsize="end"
android:gravity="center"
android:singleLine="true"
android:text="最多显示"
android:textColor="@color/colorTextGray"
android:textSize="14sp" />
<TextView
android:id="@+id/config_status_icon_count_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center"
android:maxWidth="100dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:singleLine="true"
android:text="5"
android:textColor="@color/colorTextGray"
android:textSize="14sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:gravity="center"
android:singleLine="true"
android:text="个"
android:textColor="@color/colorTextGray"
android:textSize="14sp" />
<TextView
android:id="@+id/config_status_icon_count_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_button_round"
android:ellipsize="end"
android:gravity="center"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="5dp"
android:singleLine="true"
android:text="修改个数"
android:textColor="@color/colorTextGray"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/config_item_color_hook"
android:layout_width="match_parent"
@@ -280,6 +402,18 @@
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="10dp"
android:alpha="0.6"
android:lineSpacingExtra="6dp"
android:text="首次安装请打开名单列表从云端更新数据,后期适配的内容也请手动打开名单列表重新拉取数据以检查更新,数据更新后重启系统界面即可生效。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -387,7 +521,7 @@
android:layout_marginBottom="10dp"
android:alpha="0.8"
android:lineSpacingExtra="6dp"
android:text="Q.哪些是已知问题?\nA.以下是问题描述列表:\n(1) 动态小图标可能会在高版本系统中闪烁,这是 MIUI 自身就存在的问题,后期只能等官方修复。\n(2) 请始终保持最新版本的 LSPosed旧版本可能会出现 Hook 不生效的问题。\n(3) 建议最低从 MIUI 12.5 “2021-5-18” 开发版以后开始使用,之前的版本可能或多或少存在 MIUI 自身 BUG 不生效、黑白块问题,若你的系统不存在原生图标问题不建议使用模块。"
android:text="Q.哪些是已知问题?\nA.以下是问题描述列表:\n(1) 动态小图标可能会在高版本系统中闪烁,这是 MIUI 自身就存在的问题,后期只能等官方修复。\n(2) 请始终保持最新版本的 LSPosed旧版本可能会出现 Hook 不生效的问题,若最新版本依然不生效请在作用域中长按“系统界面”(“系统 UI”)选择重新优化。\n(3) 建议最低从 MIUI 12.5 “2021-5-18” 开发版以后开始使用,之前的版本可能或多或少存在 MIUI 自身 BUG 不生效、黑白块问题,将不再进行适配。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
@@ -401,53 +535,6 @@
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/link_with_project_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:layout_marginRight="15dp"
android:background="@drawable/bg_permotion_round"
android:elevation="0dp"
android:gravity="center"
android:orientation="vertical"
android:padding="15dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:gravity="center|start">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginEnd="5dp"
android:alpha="0.85"
android:src="@mipmap/ic_about"
android:tint="@color/colorTextGray" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.85"
android:singleLine="true"
android:text="项目地址"
android:textColor="@color/colorTextGray"
android:textSize="12sp" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.8"
android:lineSpacingExtra="6dp"
android:text="本软件是免费开源项目,遵循 AGPL3.0 协议,你可以点击这里前往 Github 查看源码以及获取模块更新。"
android:textColor="@color/colorTextDark"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -5,6 +5,7 @@
android:layout_height="wrap_content"
android:background="@drawable/bg_permotion_round"
android:baselineAligned="false"
android:descendantFocusability="blocksDescendants"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="15dp"

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

@@ -0,0 +1,26 @@
<?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_status_icon_count_input_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:digits="0123456789"
android:ellipsize="end"
android:hint="请输入 0-100 之间的整数"
android:inputType="number"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

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

View File

@@ -1,12 +1,12 @@
plugins {
id 'com.android.application' version '7.1.1' apply false
id 'com.android.library' version '7.1.1' apply false
id 'com.android.application' version '7.1.2' apply false
id 'com.android.library' version '7.1.2' apply false
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}
ext {
appVersionName = "1.71"
appVersionCode = 16
appVersionName = "2.16"
appVersionCode = 26
}
task clean(type: Delete) {

Binary file not shown.