mirror of
https://github.com/fankes/MIUINativeNotifyIcon.git
synced 2025-09-06 02:35:32 +08:00
Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
8f8c260069 | |||
3461a28881 | |||
b1b99ccdfe | |||
3aa7ee2a6a | |||
1829a44eb3 | |||
b5219a00cc | |||
da81e12a60 | |||
57a14fdaf1 | |||
be18502fd8 | |||
4914ae20b2 | |||
3b338214fc | |||
3a484e4a63 | |||
199ad9fc66 | |||
51e3eb8784 | |||
e05d65e7e5 | |||
2fb557afd8 | |||
8737aaac30 | |||
993ceb5050 | |||
b1baaf4403 | |||
db9f866178 | |||
f2bccafd92 | |||
d798ca944d | |||
ca81a61aae | |||
cd4f7be354 | |||
6c961f044f | |||
485f85873c | |||
8202239f57 | |||
7e4cd7f71a | |||
f6c4448eed | |||
45dde844b7 | |||
8e987897fc | |||
60e2a5b6ea | |||
8a31373ebd | |||
963933f168 | |||
a72e89e57c | |||
c40b1bf3ee | |||
3045c90580 | |||
8e9c0127fa | |||
7da1949de3 | |||
1bbacad3d2 | |||
6e6b566ea1 | |||
bf5cd8de2b | |||
|
ec5796f37e | ||
|
51353caec2 | ||
f3ca453a9a |
@@ -21,8 +21,8 @@
|
||||
|
||||
```kotlin
|
||||
IconDataBean(
|
||||
isEnabled = true, // 是否默认启用替换
|
||||
isEnabledAll = false, // 是否默认启用完全替换
|
||||
isEnabled = true, // 是否默认启用替换彩色图标 - 关闭后将全部停止替换
|
||||
isEnabledAll = false, // 是否默认启用替换全部图标
|
||||
appName = "", // APP 名称
|
||||
packageName = "", // APP 包名
|
||||
iconBitmap = ("").bitmap, // 位图数据 Base64
|
||||
|
19
README.md
19
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
<br/><br/>
|
||||
<img src="https://github.com/fankes/MIUINativeNotifyIcon/blob/master/app/src/main/ic_launcher-playstore.png" width = "100" height = "100"/>
|
||||
<br/>
|
||||
@@ -12,12 +12,13 @@ Fix the native notification bar icon function abandoned by the MIUI development
|
||||
# 开始使用
|
||||
|
||||
点击下载最新版本
|
||||
<a href='https://github.com/fankes/MIUINativeNotifyIcon/releases'></a>
|
||||
<a href='https://github.com/fankes/MIUINativeNotifyIcon/releases'></a>
|
||||
<br/><br/>
|
||||
⚠️适配说明<br/>
|
||||
⚠️ 适配说明<br/>
|
||||
|
||||
- 此模块仅支持 Lsposed(作用域“系统界面”)、EdXposed(不推荐)、不支持太极无极(阴)
|
||||
- 目前最低支持基于 Android 9 版本的 MIUI12 或 MIUI12.5(建议)
|
||||
- 此模块仅支持 LSPosed(作用域“系统界面”)、~~EdXposed(随时停止支持)~~、不支持太极无极
|
||||
- 目前最低支持基于 Android 9 版本的 MIUI 12 或 MIUI 12.5(最低建议)
|
||||
- 请始终保持最新版本的 LSPosed,旧版本可能会出现 Hook 不生效的问题
|
||||
|
||||
# 禁止任何商业用途
|
||||
|
||||
@@ -59,7 +60,7 @@ com.android.internal.util.ContrastColorUtil
|
||||
|
||||
- 这个类中有一个方法可以拿出来判断图标的灰度效果
|
||||
|
||||
```java
|
||||
```
|
||||
ContrastColorUtil.getInstance().isGrayscaleIcon(drawable);
|
||||
```
|
||||
|
||||
@@ -68,6 +69,12 @@ ContrastColorUtil.getInstance().isGrayscaleIcon(drawable);
|
||||
- 后来一想,也是啊,被国内生态毒害的用户,怎么可能会去想到这些问题呢,最后只能是我自作多情,还对 MIUI 留有一点情怀吧。
|
||||
- ——来自一个无可奈何的 MIUI 老用户
|
||||
|
||||
# 后记
|
||||
|
||||
- 近期重新适配了 MIUI 12、12.5、13 版本,每个版本的图标设置方法都不一样,而且改的乱七八糟的,我都要无语了,只能用了很多折中方案,毕竟我也没有那么大精力每个版本去修复,实在是累了
|
||||
- 特地的把自己能有的小米手机刷成各种 MIUI 版本去为酷友做专项适配,我也是很累了,也希望你们能够多多支持,也能让 MIUI 做得更好
|
||||
- MIUI 再不重写,怕是永远会变成安卓之光。雷军,金凡!!
|
||||
|
||||
# 许可证
|
||||
|
||||
- [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
@@ -21,15 +21,15 @@ android {
|
||||
applicationId "com.fankes.miui.notify"
|
||||
minSdk 26
|
||||
targetSdk 26
|
||||
versionCode 3
|
||||
versionName "1.15"
|
||||
versionCode 7
|
||||
versionName "1.35"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
minifyEnabled true
|
||||
signingConfig signingConfigs.debug
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
@@ -45,6 +45,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
compileOnly 'de.robv.android.xposed:api:82'
|
||||
implementation "com.github.topjohnwu.libsu:core:3.1.2"
|
||||
// 基础依赖包
|
||||
implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
|
||||
// Fragment 快速实现
|
||||
|
@@ -30,8 +30,8 @@ import java.io.Serializable
|
||||
* @param packageName 包名
|
||||
* @param iconBitmap 图标位图
|
||||
* @param contributorName 贡献者昵称
|
||||
* @param isEnabled 是否默认启用替换
|
||||
* @param isEnabledAll 是否默认启用完全替换
|
||||
* @param isEnabled 是否默认启用替换彩色图标 - 关闭后将全部停止替换
|
||||
* @param isEnabledAll 是否默认启用替换全部图标
|
||||
*/
|
||||
data class IconDataBean(
|
||||
var appName: String,
|
||||
|
@@ -18,10 +18,11 @@
|
||||
*
|
||||
* This file is Created by fankes on 2022/01/24.
|
||||
*/
|
||||
@file:Suppress("SameParameterValue")
|
||||
@file:Suppress("SameParameterValue", "DEPRECATION")
|
||||
|
||||
package com.fankes.miui.notify.hook
|
||||
|
||||
import android.app.Notification
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
@@ -47,18 +48,41 @@ class HookMain : IXposedHookLoadPackage {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val NotificationUtilClass =
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationUtil"
|
||||
/** 一直存在的类 */
|
||||
private const val SystemUIApplicationClass = "$SYSTEMUI_PACKAGE_NAME.SystemUIApplication"
|
||||
|
||||
/** MIUI 新版本存在的类 */
|
||||
private const val NotificationHeaderViewWrapperInjectorClass =
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapperInjector"
|
||||
|
||||
private const val ExpandedNotificationClass =
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.ExpandedNotification"
|
||||
/** 一直存在的类 */
|
||||
private const val NotificationHeaderViewWrapperClass =
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationHeaderViewWrapper"
|
||||
|
||||
private const val SystemUIApplicationClass = "$SYSTEMUI_PACKAGE_NAME.SystemUIApplication"
|
||||
/** 一直存在的类 */
|
||||
private const val NotificationViewWrapperClass =
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationViewWrapper"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val StatusBarIconViewClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.StatusBarIconView"
|
||||
|
||||
/** 原生存在的类 */
|
||||
private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
|
||||
|
||||
/** 未确定是否只有旧版本存在的类 */
|
||||
private const val ExpandableNotificationRowClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.ExpandableNotificationRow"
|
||||
|
||||
/** 根据多个版本存在不同的包名相同的类 */
|
||||
private val NotificationUtilClass = Pair(
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationUtil",
|
||||
"$SYSTEMUI_PACKAGE_NAME.miui.statusbar.notification.NotificationUtil"
|
||||
)
|
||||
|
||||
/** 根据多个版本存在不同的包名相同的类 */
|
||||
private val ExpandedNotificationClass = Pair(
|
||||
"$SYSTEMUI_PACKAGE_NAME.statusbar.notification.ExpandedNotification",
|
||||
"$SYSTEMUI_PACKAGE_NAME.miui.statusbar.ExpandedNotification"
|
||||
)
|
||||
}
|
||||
|
||||
/** 仅作用于替换的 Hook 方法体 */
|
||||
@@ -75,13 +99,6 @@ class HookMain : IXposedHookLoadPackage {
|
||||
}
|
||||
}
|
||||
|
||||
/** 仅作用于替换的 Hook 方法体 */
|
||||
private val replaceToFalse = object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam?): Any {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略异常运行
|
||||
* @param error 错误信息
|
||||
@@ -102,7 +119,7 @@ class HookMain : IXposedHookLoadPackage {
|
||||
*/
|
||||
private fun logD(content: String, it: () -> Unit = {}) {
|
||||
it()
|
||||
if (!HookMedium.getBoolean(HookMedium.ENABLE_MODULE_LOG, default = true)) return
|
||||
if (!HookMedium.getBoolean(HookMedium.ENABLE_MODULE_LOG, default = false)) return
|
||||
XposedBridge.log("[MIUINativeNotifyIcon][D]>$content")
|
||||
Log.d("MIUINativeNotifyIcon", content)
|
||||
}
|
||||
@@ -114,7 +131,7 @@ class HookMain : IXposedHookLoadPackage {
|
||||
*/
|
||||
private fun logW(content: String, it: () -> Unit = {}) {
|
||||
it()
|
||||
if (!HookMedium.getBoolean(HookMedium.ENABLE_MODULE_LOG, default = true)) return
|
||||
if (!HookMedium.getBoolean(HookMedium.ENABLE_MODULE_LOG, default = false)) return
|
||||
XposedBridge.log("[MIUINativeNotifyIcon][W]>$content")
|
||||
Log.d("MIUINativeNotifyIcon", content)
|
||||
}
|
||||
@@ -132,6 +149,62 @@ class HookMain : IXposedHookLoadPackage {
|
||||
Log.e("MIUINativeNotifyIcon", content, e)
|
||||
}
|
||||
|
||||
/**
|
||||
* 目标类是否存在
|
||||
* @param name 类名
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.isClassExist(name: String) = try {
|
||||
classLoader.loadClass(name)
|
||||
true
|
||||
} catch (_: Throwable) {
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* 目标方法是否存在
|
||||
* @param classPair 类数组
|
||||
* @param name 方法名
|
||||
* @param param 方法参数类型数组
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.isMethodExist(
|
||||
classPair: Pair<String, String>,
|
||||
name: String, vararg param: Class<*>
|
||||
) = try {
|
||||
(try {
|
||||
classLoader.loadClass(classPair.first)
|
||||
} catch (_: Throwable) {
|
||||
try {
|
||||
classLoader.loadClass(classPair.second)
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
})?.getDeclaredMethod(name, *param)
|
||||
true
|
||||
} catch (_: Throwable) {
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* 目标方法是否存在
|
||||
* @param className 类名
|
||||
* @param name 方法名
|
||||
* @param param 方法参数类型数组
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.isMethodExist(className: String, name: String, vararg param: Class<*>) =
|
||||
try {
|
||||
(try {
|
||||
classLoader.loadClass(className)
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
})?.getDeclaredMethod(name, *param)
|
||||
true
|
||||
} catch (_: Throwable) {
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找目标类
|
||||
* @param name 类名
|
||||
@@ -141,7 +214,42 @@ class HookMain : IXposedHookLoadPackage {
|
||||
classLoader.loadClass(name)
|
||||
|
||||
/**
|
||||
* ⚠️ 这个是修复彩色图标的关键核心代码判断
|
||||
* 查找目标类 - 两个类都没找到才会报错
|
||||
* @param pair 类名数组
|
||||
* @return [Class]
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.findClass(pair: Pair<String, String>) = try {
|
||||
classLoader.loadClass(pair.first)
|
||||
} catch (_: Throwable) {
|
||||
try {
|
||||
classLoader.loadClass(pair.second)
|
||||
} catch (e: Throwable) {
|
||||
logE(content = "Cannot find Class ${pair.first} and ${pair.second}", e)
|
||||
error("[Throwable] Cannot find Class ${pair.first} and ${pair.second}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 存在目标类的类名 - 两个类都没找到会抛出异常
|
||||
* @param pair 类名数组
|
||||
* @return [String] 目标类名
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.existClass(pair: Pair<String, String>) = try {
|
||||
classLoader.loadClass(pair.first)
|
||||
pair.first
|
||||
} catch (_: Throwable) {
|
||||
try {
|
||||
classLoader.loadClass(pair.second)
|
||||
pair.second
|
||||
} catch (_: Throwable) {
|
||||
logE(content = "Cannot find Class ${pair.first} and ${pair.second}")
|
||||
error("[Throwable] Cannot find Class ${pair.first} and ${pair.second}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - 这个是修复彩色图标的关键核心代码判断
|
||||
*
|
||||
* 判断是否为灰度图标 - 反射执行系统方法
|
||||
* @param context 实例
|
||||
* @param icon 要判断的图标
|
||||
@@ -155,6 +263,31 @@ class HookMain : IXposedHookLoadPackage {
|
||||
.apply { isAccessible = true }.invoke(instance, icon) as Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前通知栏的样式
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.isShowMiuiStyle() = try {
|
||||
findClass(NotificationUtilClass).let {
|
||||
it.getDeclaredMethod("showMiuiStyle")
|
||||
.apply { isAccessible = true }.invoke(null) as Boolean
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为新版本 MIUI 方案
|
||||
*
|
||||
* 拥有状态栏图标颜色检查功能
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.hasIgnoreStatusBarIconColor() = try {
|
||||
isMethodExist(NotificationUtilClass, name = "ignoreStatusBarIconColor", findClass(ExpandedNotificationClass))
|
||||
} catch (_: Throwable) {
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 [ExpandedNotificationClass] 的应用名称
|
||||
* @param instance 通知实例
|
||||
@@ -163,46 +296,64 @@ class HookMain : IXposedHookLoadPackage {
|
||||
private fun XC_LoadPackage.LoadPackageParam.findAppName(instance: Any?) = try {
|
||||
findClass(ExpandedNotificationClass).getDeclaredMethod("getAppName").let {
|
||||
it.isAccessible = true
|
||||
it.invoke(instance) as? String ?: ""
|
||||
it.invoke(instance) as? String ?: "<empty>"
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
"unknown"
|
||||
} catch (e: Throwable) {
|
||||
"<unknown>"
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局上下文
|
||||
* @return [Context]
|
||||
* @return [Context] or null
|
||||
*/
|
||||
private val XC_LoadPackage.LoadPackageParam.globalContext
|
||||
get() = findClass(SystemUIApplicationClass)
|
||||
.getDeclaredMethod("getContext").apply { isAccessible = true }
|
||||
.invoke(null) as Context
|
||||
get() = try {
|
||||
findClass(SystemUIApplicationClass)
|
||||
.getDeclaredMethod("getContext").apply { isAccessible = true }
|
||||
.invoke(null) as? Context?
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 状态栏小图标
|
||||
*
|
||||
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
|
||||
* @param param Hook Param
|
||||
* @param context 实例
|
||||
* @param expandedNf 通知实例
|
||||
* @param iconDrawable 小图标 [Drawable]
|
||||
* @param isLegacyWay 旧版本 Hook 方式
|
||||
* @param it 回调小图标 - ([Bitmap] 小图标)
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.hookSmallIconOnSet(param: XC_MethodHook.MethodHookParam) =
|
||||
private fun XC_LoadPackage.LoadPackageParam.hookSmallIconOnSet(
|
||||
context: Context,
|
||||
expandedNf: StatusBarNotification?,
|
||||
iconDrawable: Drawable,
|
||||
isLegacyWay: Boolean,
|
||||
it: (Bitmap) -> Unit
|
||||
) {
|
||||
runWithoutError(error = "GetSmallIconOnSet") {
|
||||
/** 获取通知小图标 */
|
||||
val iconDrawable = (param.result as Icon).loadDrawable(globalContext)
|
||||
|
||||
/** 判断是否不是灰度图标 */
|
||||
val isNotGrayscaleIcon = !isGrayscaleIcon(globalContext, iconDrawable)
|
||||
val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable)
|
||||
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
|
||||
(param.args?.get(0) as? StatusBarNotification?)?.also { notifyInstance ->
|
||||
expandedNf?.also { notifyInstance ->
|
||||
/** 目标彩色通知 APP 图标 */
|
||||
var customIcon: Icon? = null
|
||||
var customIcon: Bitmap? = null
|
||||
if (HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true))
|
||||
run {
|
||||
IconPackParams.iconDatas.forEach {
|
||||
if (findAppName(notifyInstance).startsWith("Android") &&
|
||||
(notifyInstance.notification.extras.getCharSequence(Notification.EXTRA_TITLE)
|
||||
?.startsWith("Xposed 模块") == true ||
|
||||
notifyInstance.notification.extras.getCharSequence(Notification.EXTRA_TITLE)
|
||||
?.startsWith("Xposed Module") == true)
|
||||
) customIcon = IconPackParams.lsposedIcon
|
||||
else IconPackParams.iconDatas.forEach {
|
||||
if ((notifyInstance.opPkgName == it.packageName ||
|
||||
findAppName(notifyInstance) == it.appName) &&
|
||||
HookMedium.isAppNotifyHookOf(it)
|
||||
) {
|
||||
if (isNotGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it))
|
||||
customIcon = Icon.createWithBitmap(it.iconBitmap)
|
||||
customIcon = it.iconBitmap
|
||||
return@run
|
||||
}
|
||||
}
|
||||
@@ -213,47 +364,48 @@ class HookMain : IXposedHookLoadPackage {
|
||||
logD(
|
||||
content = "GetSmallIconOnSet -> " +
|
||||
"hook Custom AppIcon [pkgName] ${notifyInstance.opPkgName} " +
|
||||
"[appName] ${findAppName(notifyInstance)}"
|
||||
) { param.result = customIcon }
|
||||
"[appName] ${findAppName(notifyInstance)} " +
|
||||
"[legacyWay] $isLegacyWay"
|
||||
) { it(customIcon!!) }
|
||||
/** 若不是灰度图标自动处理为圆角 */
|
||||
isNotGrayscaleIcon ->
|
||||
logD(
|
||||
content = "GetSmallIconOnSet -> " +
|
||||
"hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
|
||||
"[appName] ${findAppName(notifyInstance)}"
|
||||
"[appName] ${findAppName(notifyInstance)} " +
|
||||
"[legacyWay] $isLegacyWay"
|
||||
) {
|
||||
param.result = Icon.createWithBitmap(
|
||||
iconDrawable.toBitmap().round(15.dp(globalContext))
|
||||
)
|
||||
it(iconDrawable.toBitmap().round(15.dp(context)))
|
||||
}
|
||||
}
|
||||
} ?: logW(content = "GetSmallIconOnSet -> StatusBarNotification got null")
|
||||
} ?: logW(content = "GetSmallIconOnSet -> StatusBarNotification got null [legacyWay] $isLegacyWay")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 通知栏小图标
|
||||
*
|
||||
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
|
||||
* @param param Hook Param
|
||||
* @param isNew 是否为新版方式
|
||||
* @param context 实例
|
||||
* @param expandedNf 通知实例
|
||||
* @param iconImageView 通知图标实例
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.hookNotifyIconOnSet(param: XC_MethodHook.MethodHookParam, isNew: Boolean) =
|
||||
private fun XC_LoadPackage.LoadPackageParam.hookNotifyIconOnSet(
|
||||
context: Context,
|
||||
expandedNf: StatusBarNotification?,
|
||||
iconImageView: ImageView
|
||||
) {
|
||||
runWithoutError(error = "AutoSetAppIconOnSet") {
|
||||
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
|
||||
(param.args?.get(if (isNew) 2 else 1) as? StatusBarNotification?)?.let { notifyInstance ->
|
||||
expandedNf?.let { notifyInstance ->
|
||||
/** 是否 Hook 彩色通知图标 */
|
||||
val isHookColorIcon = HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true)
|
||||
|
||||
/** 获取 [Context] */
|
||||
val context = if (isNew) param.args[0] as Context else globalContext
|
||||
|
||||
/** 新版风格反色 */
|
||||
val newStyle = if (context.isSystemInDarkMode) 0xFF2D2D2D.toInt() else Color.WHITE
|
||||
|
||||
/** 旧版风格反色 */
|
||||
val oldStyle = if (context.isNotSystemInDarkMode) 0xFF515151.toInt() else Color.WHITE
|
||||
|
||||
/** 获取图标框 */
|
||||
val iconImageView = param.args[if (isNew) 1 else 0] as ImageView
|
||||
val oldStyle = if (context.isNotSystemInDarkMode) 0xFF707070.toInt() else Color.WHITE
|
||||
|
||||
/** 获取通知小图标 */
|
||||
val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
|
||||
@@ -263,19 +415,24 @@ class HookMain : IXposedHookLoadPackage {
|
||||
|
||||
/** 自定义默认小图标 */
|
||||
var customIcon: Bitmap? = null
|
||||
if (isHookColorIcon)
|
||||
run {
|
||||
IconPackParams.iconDatas.forEach {
|
||||
if ((notifyInstance.opPkgName == it.packageName ||
|
||||
findAppName(notifyInstance) == it.appName) &&
|
||||
HookMedium.isAppNotifyHookOf(it)
|
||||
) {
|
||||
if (!isGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it))
|
||||
customIcon = it.iconBitmap
|
||||
return@run
|
||||
}
|
||||
if (isHookColorIcon) run {
|
||||
if (findAppName(notifyInstance).startsWith("Android") &&
|
||||
(notifyInstance.notification.extras.getCharSequence(Notification.EXTRA_TITLE)
|
||||
?.startsWith("Xposed 模块") == true ||
|
||||
notifyInstance.notification.extras.getCharSequence(Notification.EXTRA_TITLE)
|
||||
?.startsWith("Xposed Module") == true)
|
||||
) customIcon = IconPackParams.lsposedIcon
|
||||
else IconPackParams.iconDatas.forEach {
|
||||
if ((notifyInstance.opPkgName == it.packageName ||
|
||||
findAppName(notifyInstance) == it.appName) &&
|
||||
HookMedium.isAppNotifyHookOf(it)
|
||||
) {
|
||||
if (!isGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it))
|
||||
customIcon = it.iconBitmap
|
||||
return@run
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 如果开启了修复 APP 的彩色图标 */
|
||||
if (customIcon != null && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true))
|
||||
iconImageView.apply {
|
||||
@@ -309,11 +466,8 @@ class HookMain : IXposedHookLoadPackage {
|
||||
outlineProvider = object : ViewOutlineProvider() {
|
||||
override fun getOutline(view: View, out: Outline) {
|
||||
out.setRoundRect(
|
||||
0,
|
||||
0,
|
||||
view.width,
|
||||
view.height,
|
||||
5.dp(context)
|
||||
0, 0,
|
||||
view.width, view.height, 5.dp(context)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -338,6 +492,75 @@ class HookMain : IXposedHookLoadPackage {
|
||||
}
|
||||
} ?: logW(content = "AutoSetAppIconOnSet -> StatusBarNotification got null")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook 通知栏小图标颜色
|
||||
*
|
||||
* 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
|
||||
* @param context 实例
|
||||
* @param expandedNf 状态栏实例
|
||||
* @return [Boolean] 是否忽略通知图标颜色
|
||||
*/
|
||||
private fun XC_LoadPackage.LoadPackageParam.hookIgnoreStatusBarIconColor(
|
||||
context: Context,
|
||||
expandedNf: StatusBarNotification?
|
||||
) = if (HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true))
|
||||
try {
|
||||
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
|
||||
expandedNf?.let { notifyInstance ->
|
||||
/** 获取通知小图标 */
|
||||
val iconDrawable =
|
||||
notifyInstance.notification.smallIcon.loadDrawable(context)
|
||||
|
||||
/** 判断是否不是灰度图标 */
|
||||
val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable)
|
||||
|
||||
/** 获取目标修复彩色图标的 APP */
|
||||
var isTargetApp = false
|
||||
run {
|
||||
IconPackParams.iconDatas.forEach {
|
||||
if ((notifyInstance.opPkgName == it.packageName ||
|
||||
findAppName(notifyInstance) == it.appName) &&
|
||||
HookMedium.isAppNotifyHookOf(it)
|
||||
) {
|
||||
if (isNotGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it)) isTargetApp = true
|
||||
return@run
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 如果开启了修复 APP 的彩色图标 */
|
||||
if (isTargetApp && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true)) let {
|
||||
logD(
|
||||
content = "IgnoreStatusBarIconColor -> " +
|
||||
"hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
|
||||
"[appName] ${findAppName(notifyInstance)}"
|
||||
)
|
||||
false
|
||||
}
|
||||
else let {
|
||||
logD(
|
||||
content = "IgnoreStatusBarIconColor -> " +
|
||||
"hook Grayscale[${!isNotGrayscaleIcon}] AppIcon " +
|
||||
"[pkgName] ${notifyInstance.opPkgName} " +
|
||||
"[appName] ${findAppName(notifyInstance)}"
|
||||
)
|
||||
/** 只要不是灰度就返回彩色图标 */
|
||||
isNotGrayscaleIcon
|
||||
}
|
||||
} ?: let {
|
||||
logW(content = "IgnoreStatusBarIconColor -> StatusBarNotification got null")
|
||||
/** 否则不对颜色进行反色处理防止一些系统图标出现异常 */
|
||||
true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logE("Failed to hook ignoreStatusBarIconColor", e)
|
||||
false
|
||||
}
|
||||
else let {
|
||||
logD(content = "IgnoreStatusBarIconColor -> hook NonColor AppIcon")
|
||||
false
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
|
||||
if (lpparam == null) return
|
||||
@@ -361,6 +584,9 @@ class HookMain : IXposedHookLoadPackage {
|
||||
/** 系统版本低于 Android P 停止 Hook */
|
||||
isLowerAndroidP ->
|
||||
logW(content = "Aborted Hook -> This System is lower than Android P")
|
||||
/** 不是支持的 MIUI 系统停止 Hook */
|
||||
isNotSupportMiuiVersion ->
|
||||
logW(content = "Aborted Hook -> This MIUI Version $miuiVersion not supported")
|
||||
/** Hook 被手动关闭停止 Hook */
|
||||
!HookMedium.getBoolean(HookMedium.ENABLE_MODULE, default = true) ->
|
||||
logW(content = "Aborted Hook -> Hook Closed")
|
||||
@@ -368,91 +594,91 @@ class HookMain : IXposedHookLoadPackage {
|
||||
/** 强制回写系统的状态栏图标样式为原生 */
|
||||
runWithoutError(error = "SubstituteSmallIcon") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
NotificationUtilClass,
|
||||
lpparam.existClass(NotificationUtilClass),
|
||||
lpparam.classLoader,
|
||||
"shouldSubstituteSmallIcon",
|
||||
lpparam.findClass(ExpandedNotificationClass),
|
||||
replaceToFalse
|
||||
object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam) =
|
||||
/**
|
||||
* 因为之前的 MIUI 版本的状态栏图标颜色会全部设置为白色的 - 找不到修复的地方就直接判断版本了
|
||||
* 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook
|
||||
*/
|
||||
if (lpparam.hasIgnoreStatusBarIconColor()) false else lpparam.isShowMiuiStyle()
|
||||
}
|
||||
)
|
||||
}
|
||||
/** 修复通知图标为彩色 */
|
||||
runWithoutError(error = "IgnoreStatusBarIconColor") {
|
||||
if (lpparam.hasIgnoreStatusBarIconColor())
|
||||
runWithoutError(error = "IgnoreStatusBarIconColor") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
lpparam.existClass(NotificationUtilClass),
|
||||
lpparam.classLoader,
|
||||
"ignoreStatusBarIconColor",
|
||||
lpparam.findClass(ExpandedNotificationClass),
|
||||
object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam) =
|
||||
lpparam.hookIgnoreStatusBarIconColor(
|
||||
context = lpparam.globalContext ?: error("GlobalContext got null"),
|
||||
param.args?.get(0) as? StatusBarNotification?
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
/** 修复通知图标为彩色 - MIPUSH 修复 */
|
||||
runWithoutError(error = "UpdateIconColor") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
NotificationUtilClass,
|
||||
lpparam.classLoader,
|
||||
"ignoreStatusBarIconColor",
|
||||
lpparam.findClass(ExpandedNotificationClass),
|
||||
object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam) =
|
||||
if (HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true))
|
||||
try {
|
||||
/** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
|
||||
(param.args?.get(0) as? StatusBarNotification?)?.let { notifyInstance ->
|
||||
/** 获取通知小图标 */
|
||||
val iconDrawable =
|
||||
notifyInstance.notification.smallIcon.loadDrawable(lpparam.globalContext)
|
||||
StatusBarIconViewClass,
|
||||
lpparam.classLoader, "updateIconColor",
|
||||
object : XC_MethodHook() {
|
||||
override fun afterHookedMethod(param: MethodHookParam) =
|
||||
runWithoutError(error = "UpdateIconColorOnSet") hook@{
|
||||
/** 获取自身 */
|
||||
val iconImageView = param.thisObject as ImageView
|
||||
|
||||
/** 判断是否不是灰度图标 */
|
||||
val isNotGrayscaleIcon =
|
||||
!lpparam.isGrayscaleIcon(lpparam.globalContext, iconDrawable)
|
||||
/** 获取通知实例 */
|
||||
val expandedNf =
|
||||
param.thisObject.javaClass.getDeclaredField("mNotification").apply {
|
||||
isAccessible = true
|
||||
}[param.thisObject] as? StatusBarNotification?
|
||||
|
||||
/** 获取目标修复彩色图标的 APP */
|
||||
var isTargetApp = false
|
||||
run {
|
||||
IconPackParams.iconDatas.forEach {
|
||||
if ((notifyInstance.opPkgName == it.packageName ||
|
||||
lpparam.findAppName(notifyInstance) == it.appName) &&
|
||||
HookMedium.isAppNotifyHookOf(it)
|
||||
) {
|
||||
if (isNotGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it))
|
||||
isTargetApp = true
|
||||
return@run
|
||||
}
|
||||
}
|
||||
}
|
||||
/** 如果开启了修复 APP 的彩色图标 */
|
||||
if (isTargetApp &&
|
||||
HookMedium.getBoolean(
|
||||
HookMedium.ENABLE_NOTIFY_ICON_HOOK,
|
||||
default = true
|
||||
)
|
||||
) let {
|
||||
logD(
|
||||
content = "IgnoreStatusBarIconColor -> " +
|
||||
"hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
|
||||
"[appName] ${lpparam.findAppName(notifyInstance)}"
|
||||
)
|
||||
false
|
||||
}
|
||||
else let {
|
||||
logD(
|
||||
content = "IgnoreStatusBarIconColor -> " +
|
||||
"hook Grayscale[${!isNotGrayscaleIcon}] AppIcon " +
|
||||
"[pkgName] ${notifyInstance.opPkgName} " +
|
||||
"[appName] ${lpparam.findAppName(notifyInstance)}"
|
||||
)
|
||||
/** 只要不是灰度就返回彩色图标 */
|
||||
isNotGrayscaleIcon
|
||||
}
|
||||
} ?: let {
|
||||
logW(content = "IgnoreStatusBarIconColor -> StatusBarNotification got null")
|
||||
/** 否则不对颜色进行反色处理防止一些系统图标出现异常 */
|
||||
true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logE("Failed to hook ignoreStatusBarIconColor", e)
|
||||
false
|
||||
}
|
||||
else let {
|
||||
logD(content = "IgnoreStatusBarIconColor -> hook NonColor AppIcon")
|
||||
false
|
||||
/** 强制设置图标 - 防止 MIPUSH 不生效 */
|
||||
lpparam.hookSmallIconOnSet(
|
||||
context = iconImageView.context,
|
||||
expandedNf,
|
||||
iconImageView.drawable,
|
||||
isLegacyWay = true
|
||||
) { icon -> iconImageView.setImageBitmap(icon) }
|
||||
|
||||
/**
|
||||
* 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook
|
||||
* 新版本不需要下面的代码设置颜色 - 同样停止 Hook
|
||||
*/
|
||||
if (lpparam.hasIgnoreStatusBarIconColor() || lpparam.isShowMiuiStyle()) return@hook
|
||||
|
||||
/** 是否忽略图标颜色 */
|
||||
val isIgnoredColor =
|
||||
lpparam.hookIgnoreStatusBarIconColor(iconImageView.context, expandedNf)
|
||||
|
||||
/** 当前着色颜色 */
|
||||
val currentColor =
|
||||
param.thisObject.javaClass.getDeclaredField("mCurrentSetColor").apply {
|
||||
isAccessible = true
|
||||
}[param.thisObject] as? Int ?: Color.WHITE
|
||||
/** 判断并设置颜色 */
|
||||
if (isIgnoredColor)
|
||||
iconImageView.colorFilter = null
|
||||
else iconImageView.setColorFilter(currentColor)
|
||||
logD(content = "IgnoreStatusBarIconColor[UseOldWay] -> isIgnored[$isIgnoredColor]")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
/** 强制回写系统的状态栏图标样式为原生 */
|
||||
runWithoutError(error = "GetSmallIcon") {
|
||||
var isTooOld: Boolean
|
||||
try {
|
||||
isTooOld = false
|
||||
/** 新版方法 */
|
||||
lpparam.findClass(NotificationUtilClass)
|
||||
.getDeclaredMethod(
|
||||
@@ -461,62 +687,141 @@ class HookMain : IXposedHookLoadPackage {
|
||||
Int::class.java
|
||||
).apply { isAccessible = true }
|
||||
} catch (_: Throwable) {
|
||||
/** 旧版方法 */
|
||||
lpparam.findClass(NotificationUtilClass)
|
||||
.getDeclaredMethod("getSmallIcon", lpparam.findClass(ExpandedNotificationClass))
|
||||
.apply { isAccessible = true }
|
||||
try {
|
||||
isTooOld = false
|
||||
/** 旧版方法 */
|
||||
lpparam.findClass(NotificationUtilClass)
|
||||
.getDeclaredMethod("getSmallIcon", lpparam.findClass(ExpandedNotificationClass))
|
||||
.apply { isAccessible = true }
|
||||
} catch (_: Throwable) {
|
||||
isTooOld = true
|
||||
/** 超旧版方法 */
|
||||
lpparam.findClass(NotificationUtilClass)
|
||||
.getDeclaredMethod(
|
||||
"getSmallIcon",
|
||||
Context::class.java,
|
||||
lpparam.findClass(ExpandedNotificationClass)
|
||||
).apply { isAccessible = true }
|
||||
}
|
||||
}.also {
|
||||
XposedBridge.hookMethod(it, object : XC_MethodHook() {
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam) {
|
||||
lpparam.hookSmallIconOnSet(param)
|
||||
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
|
||||
if (!lpparam.hasIgnoreStatusBarIconColor() && lpparam.isShowMiuiStyle()) return
|
||||
runWithoutError(error = "GetSmallIconDoing") {
|
||||
(lpparam.globalContext ?: param.args[0] as Context).also { context ->
|
||||
lpparam.hookSmallIconOnSet(
|
||||
context = context,
|
||||
param.args?.get(if (isTooOld) 1 else 0) as? StatusBarNotification?,
|
||||
(param.result as Icon).loadDrawable(context),
|
||||
isLegacyWay = isTooOld
|
||||
) { icon -> param.result = Icon.createWithBitmap(icon) }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
/** 修复下拉通知图标自动设置回 APP 图标的方法 */
|
||||
runWithoutError(error = "AutoSetAppIcon") {
|
||||
var isNewWay = true
|
||||
try {
|
||||
/** 新版方法 */
|
||||
lpparam.findClass(NotificationHeaderViewWrapperInjectorClass)
|
||||
.getDeclaredMethod(
|
||||
"setAppIcon",
|
||||
Context::class.java,
|
||||
ImageView::class.java,
|
||||
lpparam.findClass(ExpandedNotificationClass)
|
||||
).apply { isAccessible = true }
|
||||
} catch (_: Throwable) {
|
||||
isNewWay = false
|
||||
/** 旧版方法 */
|
||||
lpparam.findClass(NotificationHeaderViewWrapperInjectorClass)
|
||||
.getDeclaredMethod(
|
||||
"setAppIcon",
|
||||
ImageView::class.java,
|
||||
lpparam.findClass(ExpandedNotificationClass)
|
||||
).apply { isAccessible = true }
|
||||
}.also {
|
||||
XposedBridge.hookMethod(it, object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam): Any? {
|
||||
lpparam.hookNotifyIconOnSet(param, isNew = isNewWay)
|
||||
return null
|
||||
}
|
||||
})
|
||||
if (lpparam.isClassExist(NotificationHeaderViewWrapperInjectorClass))
|
||||
runWithoutError(error = "AutoSetAppIcon") {
|
||||
var isNewWay = true
|
||||
try {
|
||||
/** 新版方法 */
|
||||
lpparam.findClass(NotificationHeaderViewWrapperInjectorClass)
|
||||
.getDeclaredMethod(
|
||||
"setAppIcon",
|
||||
Context::class.java,
|
||||
ImageView::class.java,
|
||||
lpparam.findClass(ExpandedNotificationClass)
|
||||
).apply { isAccessible = true }
|
||||
} catch (_: Throwable) {
|
||||
isNewWay = false
|
||||
/** 旧版方法 */
|
||||
lpparam.findClass(NotificationHeaderViewWrapperInjectorClass)
|
||||
.getDeclaredMethod(
|
||||
"setAppIcon",
|
||||
ImageView::class.java,
|
||||
lpparam.findClass(ExpandedNotificationClass)
|
||||
).apply { isAccessible = true }
|
||||
}.also {
|
||||
XposedBridge.hookMethod(it, object : XC_MethodReplacement() {
|
||||
override fun replaceHookedMethod(param: MethodHookParam): Any? {
|
||||
runWithoutError(error = "AutoSetAppIconDoing") {
|
||||
if (isNewWay)
|
||||
lpparam.hookNotifyIconOnSet(
|
||||
context = param.args?.get(0) as? Context ?: lpparam.globalContext
|
||||
?: error("GlobalContext got null"),
|
||||
param.args?.get(2) as? StatusBarNotification?,
|
||||
param.args?.get(1) as ImageView
|
||||
)
|
||||
else
|
||||
lpparam.hookNotifyIconOnSet(
|
||||
context = lpparam.globalContext ?: error("GlobalContext got null"),
|
||||
param.args?.get(1) as? StatusBarNotification?,
|
||||
param.args?.get(0) as ImageView
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
/** 之前的版本解决方案 */
|
||||
else runWithoutError(error = "AutoSetAppIconOldWay") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
NotificationHeaderViewWrapperClass,
|
||||
lpparam.classLoader, "handleHeaderViews",
|
||||
object : XC_MethodHook() {
|
||||
override fun afterHookedMethod(param: MethodHookParam) {
|
||||
runWithoutError(error = "AutoSetAppIconOldWayOnSet") hook@{
|
||||
/** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
|
||||
if (!lpparam.hasIgnoreStatusBarIconColor() && lpparam.isShowMiuiStyle()) return@hook
|
||||
/** 从父类中得到 mRow 变量 - [ExpandableNotificationRowClass] */
|
||||
lpparam.findClass(NotificationViewWrapperClass).getDeclaredField("mRow")
|
||||
.apply {
|
||||
isAccessible = true
|
||||
}[param.thisObject].apply {
|
||||
/** 获取小图标 */
|
||||
val iconImageView = lpparam.findClass(NotificationHeaderViewWrapperClass)
|
||||
.getDeclaredField("mIcon")
|
||||
.apply {
|
||||
isAccessible = true
|
||||
}[param.thisObject] as ImageView
|
||||
|
||||
/** 获取其中的得到通知方法 */
|
||||
val expandedNf =
|
||||
javaClass.getDeclaredMethod("getStatusBarNotification").apply {
|
||||
isAccessible = true
|
||||
}.invoke(this) as? StatusBarNotification?
|
||||
/** 执行 Hook */
|
||||
lpparam.hookNotifyIconOnSet(
|
||||
iconImageView.context,
|
||||
expandedNf, iconImageView
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
/** 干掉下拉通知图标自动设置回 APP 图标的方法 - Android 12 */
|
||||
if (isUpperOfAndroidS)
|
||||
runWithoutError(error = "ResetIconBgAndPaddings") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
NotificationHeaderViewWrapperInjectorClass,
|
||||
lpparam.classLoader,
|
||||
"resetIconBgAndPaddings",
|
||||
ImageView::class.java,
|
||||
lpparam.findClass(ExpandedNotificationClass),
|
||||
replaceToNull
|
||||
)
|
||||
}
|
||||
logD("hook Completed!")
|
||||
if (isUpperOfAndroidS &&
|
||||
lpparam.isMethodExist(
|
||||
NotificationHeaderViewWrapperInjectorClass,
|
||||
name = "resetIconBgAndPaddings"
|
||||
)
|
||||
) runWithoutError(error = "ResetIconBgAndPaddings") {
|
||||
XposedHelpers.findAndHookMethod(
|
||||
NotificationHeaderViewWrapperInjectorClass,
|
||||
lpparam.classLoader,
|
||||
"resetIconBgAndPaddings",
|
||||
ImageView::class.java,
|
||||
lpparam.findClass(ExpandedNotificationClass),
|
||||
replaceToNull
|
||||
)
|
||||
}
|
||||
logD(content = "hook Completed!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -48,6 +48,7 @@ object HookMedium {
|
||||
|
||||
/**
|
||||
* 判断模块是否激活
|
||||
*
|
||||
* 在 [HookMain] 中 Hook 掉此方法
|
||||
* @return [Boolean] 激活状态
|
||||
*/
|
||||
@@ -115,6 +116,7 @@ object HookMedium {
|
||||
|
||||
/**
|
||||
* 强制设置 Sp 存储为全局可读可写
|
||||
*
|
||||
* 以供模块使用
|
||||
* @param context 实例
|
||||
*/
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -46,6 +46,12 @@ class ConfigureActivity : BaseActivity() {
|
||||
setContentView(R.layout.activity_config)
|
||||
/** 返回按钮点击事件 */
|
||||
findViewById<View>(R.id.title_back_icon).setOnClickListener { onBackPressed() }
|
||||
/** 设置标题个数文本 */
|
||||
findViewById<TextView>(R.id.config_title_count_text).text = "已适配 ${IconPackParams.iconDatas.size} 个 APP 的通知图标"
|
||||
/** 设置搜索按钮点击事件 */
|
||||
findViewById<View>(R.id.config_title_search).setOnClickListener {
|
||||
Toast.makeText(this, "后期开放", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
/** 设置列表元素和 Adapter */
|
||||
findViewById<ListView>(R.id.config_list_view).apply {
|
||||
adapter = object : BaseAdapter() {
|
||||
|
@@ -48,12 +48,14 @@ class MainActivity : BaseActivity() {
|
||||
private const val moduleVersion = BuildConfig.VERSION_NAME
|
||||
}
|
||||
|
||||
private var isWarnDialogShowing = false // 警告对话框是否显示
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
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 版本:$miuiVersion"
|
||||
findViewById<TextView>(R.id.main_text_miui_version).text = "MIUI 版本:$miuiFullVersion"
|
||||
when {
|
||||
/** 判断是否为 MIUI 系统 */
|
||||
isNotMIUI ->
|
||||
@@ -64,15 +66,24 @@ class MainActivity : BaseActivity() {
|
||||
confirmButton(text = "退出") { finish() }
|
||||
noCancelable()
|
||||
}
|
||||
/** 判断最低系统版本 */
|
||||
/** 判断最低 Android 系统版本 */
|
||||
isLowerAndroidP ->
|
||||
showDialog {
|
||||
title = "系统版本过低"
|
||||
title = "Android 系统版本过低"
|
||||
msg = "此模块最低支持基于 Android 9 的 MIUI 系统,你的系统版本过低不再进行适配。\n" +
|
||||
"如有问题请联系 酷安 @星夜不荟"
|
||||
confirmButton(text = "退出") { finish() }
|
||||
noCancelable()
|
||||
}
|
||||
/** 判断最低 MIUI 版本 */
|
||||
isNotSupportMiuiVersion ->
|
||||
showDialog {
|
||||
title = "MIUI 版本过低"
|
||||
msg = "此模块最低支持 MIUI 12 系统,你的 MIUI 版本为 ${miuiVersion},不再进行适配。\n" +
|
||||
"如有问题请联系 酷安 @星夜不荟"
|
||||
confirmButton(text = "退出") { finish() }
|
||||
noCancelable()
|
||||
}
|
||||
/** 判断是否 Hook */
|
||||
isHooked() -> {
|
||||
findViewById<LinearLayout>(R.id.main_lin_status).setBackgroundResource(R.drawable.green_round)
|
||||
@@ -101,7 +112,7 @@ class MainActivity : BaseActivity() {
|
||||
notifyIconConfigItem.isVisible = getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true)
|
||||
moduleEnableLogSwitch.isVisible = getBoolean(HookMedium.ENABLE_MODULE, default = true)
|
||||
moduleEnableSwitch.isChecked = getBoolean(HookMedium.ENABLE_MODULE, default = true)
|
||||
moduleEnableLogSwitch.isChecked = getBoolean(HookMedium.ENABLE_MODULE_LOG, default = true)
|
||||
moduleEnableLogSwitch.isChecked = getBoolean(HookMedium.ENABLE_MODULE_LOG, default = false)
|
||||
hideIconInLauncherSwitch.isChecked = getBoolean(HookMedium.ENABLE_HIDE_ICON)
|
||||
colorIconHookSwitch.isChecked = getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true)
|
||||
notifyIconHookSwitch.isChecked = getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true)
|
||||
@@ -175,6 +186,20 @@ class MainActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
/** MIUI 12 的版本特殊 - 所以给出提示 */
|
||||
if (!isWarnDialogShowing && isHooked() && miuiVersion == "12" && isMiuiNotifyStyle)
|
||||
showDialog {
|
||||
isWarnDialogShowing = true
|
||||
title = "经典通知栏样式已启用"
|
||||
msg = "在 MIUI 12 中启用了经典通知栏样式后状态栏图标将不再做原生处理,模块停止工作," +
|
||||
"这取决于系统设置,你应当在 设置>通知管理>通知显示设置 中将样式设置为“原生样式”。"
|
||||
confirmButton(text = "我知道了") { isWarnDialogShowing = false }
|
||||
noCancelable()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断模块是否激活
|
||||
* @return [Boolean] 激活状态
|
||||
@@ -183,9 +208,9 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
/** 重启系统界面 */
|
||||
private fun restartSystemUI() =
|
||||
execShellCmd(cmd = "pgrep systemui").also { pid ->
|
||||
execShellSu(cmd = "pgrep systemui").also { pid ->
|
||||
if (pid.isNotBlank())
|
||||
execShellCmd(cmd = "kill -9 $pid")
|
||||
execShellSu(cmd = "kill -9 $pid")
|
||||
else Toast.makeText(this, "ROOT 权限获取失败", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
|
@@ -29,11 +29,11 @@ import android.content.res.Configuration
|
||||
import android.graphics.*
|
||||
import android.graphics.Bitmap.createBitmap
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.service.notification.StatusBarNotification
|
||||
import android.util.Base64
|
||||
import com.fankes.miui.notify.application.MNNApplication.Companion.appContext
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import com.topjohnwu.superuser.Shell
|
||||
|
||||
/**
|
||||
* 系统深色模式是否开启
|
||||
@@ -54,6 +54,17 @@ inline val isNotSystemInDarkMode get() = !isSystemInDarkMode
|
||||
*/
|
||||
val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
||||
|
||||
/**
|
||||
* 通知栏是否为 MIUI 样式
|
||||
* @return [Boolean] 是否符合条件
|
||||
*/
|
||||
val Context.isMiuiNotifyStyle
|
||||
get() = try {
|
||||
Settings.System.getInt(contentResolver, "status_bar_notification_style") == 0
|
||||
} catch (_: Throwable) {
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统深色模式是否没开启
|
||||
* @return [Boolean] 是否开启
|
||||
@@ -91,6 +102,24 @@ val isMIUI by lazy {
|
||||
*/
|
||||
inline val isNotMIUI get() = !isMIUI
|
||||
|
||||
/**
|
||||
* 是否为支持的 MIUI 版本
|
||||
* @return [Boolean]
|
||||
*/
|
||||
val isSupportMiuiVersion
|
||||
get() = when (miuiVersion) {
|
||||
"12" -> true
|
||||
"12.5" -> true
|
||||
"13" -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否不为支持的 MIUI 版本
|
||||
* @return [Boolean]
|
||||
*/
|
||||
inline val isNotSupportMiuiVersion get() = !isSupportMiuiVersion
|
||||
|
||||
/**
|
||||
* 获取 MIUI 版本
|
||||
* @return [String]
|
||||
@@ -109,8 +138,16 @@ val miuiVersion
|
||||
"V13" -> "13"
|
||||
else -> it.replace(oldValue = "V", newValue = "")
|
||||
}
|
||||
} + " " + findPropString(key = "ro.system.build.version.incremental")
|
||||
else "不是 MIUI 系统"
|
||||
}.trim()
|
||||
else "NULL"
|
||||
|
||||
/**
|
||||
* 获取 MIUI 完全版本
|
||||
* @return [String]
|
||||
*/
|
||||
val miuiFullVersion
|
||||
get() = if (isMIUI) (miuiVersion + " " + findPropString(key = "ro.system.build.version.incremental"))
|
||||
else "不是 MIUI 系统"
|
||||
|
||||
/**
|
||||
* 获取推送通知的包名
|
||||
@@ -230,28 +267,10 @@ fun findPropString(key: String, default: String = "") =
|
||||
* @param cmd 命令
|
||||
* @return [String] 执行结果
|
||||
*/
|
||||
fun execShellCmd(cmd: String): String {
|
||||
var result = ""
|
||||
var dos: DataOutputStream? = null
|
||||
var dis: DataInputStream? = null
|
||||
try {
|
||||
val p = Runtime.getRuntime().exec("su")
|
||||
dos = DataOutputStream(p.outputStream)
|
||||
dis = DataInputStream(p.inputStream)
|
||||
dos.writeBytes("$cmd\n")
|
||||
dos.flush()
|
||||
dos.writeBytes("exit\n")
|
||||
dos.flush()
|
||||
var line: String
|
||||
while (dis.readLine().also { line = it } != null) result += line
|
||||
p.waitFor()
|
||||
} catch (_: Exception) {
|
||||
} finally {
|
||||
try {
|
||||
dos?.close()
|
||||
dis?.close()
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
fun execShellSu(cmd: String) = try {
|
||||
Shell.su(cmd).exec().out.let {
|
||||
if (it.isNotEmpty()) it[0].trim() else ""
|
||||
}
|
||||
return result.trim()
|
||||
} catch (_: Throwable) {
|
||||
""
|
||||
}
|
@@ -25,7 +25,15 @@ import de.robv.android.xposed.XSharedPreferences
|
||||
|
||||
object XPrefUtils {
|
||||
|
||||
fun getBoolean(key: String, default: Boolean = false) = pref.getBoolean(key, default)
|
||||
private var xPrefCacheKeyValueBooleans = HashMap<String, Boolean>()
|
||||
|
||||
fun getBoolean(key: String, default: Boolean = false) =
|
||||
xPrefCacheKeyValueBooleans[key].let {
|
||||
it ?: pref.getBoolean(key, default).let { e ->
|
||||
xPrefCacheKeyValueBooleans[key] = e
|
||||
e
|
||||
}
|
||||
}
|
||||
|
||||
private val pref: XSharedPreferences
|
||||
get() {
|
||||
|
10
app/src/main/res/drawable/button_round.xml
Normal file
10
app/src/main/res/drawable/button_round.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="#777777">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#66DAD9D9" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
@@ -28,15 +28,40 @@
|
||||
android:src="@mipmap/back"
|
||||
android:tint="@color/colorTextGray" />
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:text="通知图标优化名单"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold" />
|
||||
android:gravity="center|start"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:singleLine="true"
|
||||
android:text="通知图标优化名单"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="19sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/config_title_count_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:text="..."
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/config_title_search"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:src="@mipmap/icon_search"
|
||||
android:tint="@color/colorTextGray" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
@@ -64,7 +89,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="5dp"
|
||||
android:text="启用替换后 APP 的彩色通知图标将被替换为预设的小图标。\n启用完全替换将忽略 APP 非彩色图标,强制将全部通知图标替换为列表中预设的小图标。"
|
||||
android:text="启用替换后 APP 的彩色通知图标将被替换为预设的小图标。\n启用全部替换将忽略 APP 非彩色图标,强制将全部通知图标替换为列表中预设的小图标。"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="11sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
@@ -296,7 +296,7 @@
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.6"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="隐藏模块图标后界面可能会被关闭,将不会再在桌面显示,你可以在 EdXposed、LsPosed 中找到模块设置并打开。"
|
||||
android:text="隐藏模块图标后界面可能会被关闭,将不会再在桌面显示,你可以在 EdXposed、LSPosed 中找到模块设置并打开。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
@@ -353,7 +353,7 @@
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="Q.如何使用?\nA.模块仅支持 EdXposed、LsPosed,由于涉及到修改系统应用,不支持太极(阴)框架,在 LsPosed 的作用域中,只需勾选“系统界面”即可,应用设置后需要重启系统界面。"
|
||||
android:text="Q.如何使用?\nA.模块仅支持 LSPosed,EdXposed 也可以使用但随时停止支持,由于模块涉及到修改系统应用,不支持其它 Hook 框架,在 LSPosed 的作用域中,只需勾选“系统界面”(旧版本为“系统 UI”)即可,应用设置后需要重启系统界面。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
@@ -363,7 +363,7 @@
|
||||
android:layout_marginBottom="10dp"
|
||||
android:alpha="0.8"
|
||||
android:lineSpacingExtra="6dp"
|
||||
android:text="Q.哪些是已知问题?\nA.MIUI 自己的系统应用的通知图标仍然可能出现黑白块的情况,可使用通知图标优化暂时修复其图标问题,后期只能等官方修复(基本上没什么可能性)。动态小图标可能会在高版本系统中闪烁,这是 MIUI 的问题,后期仍在想解决办法。"
|
||||
android:text="Q.哪些是已知问题?\nA.以下是问题描述列表:\n(1) 动态小图标可能会在高版本系统中闪烁,这是 MIUI 自身就存在的问题,后期只能等官方修复。\n(2) 请始终保持最新版本的 LSPosed,旧版本可能会出现 Hook 不生效的问题。"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
|
||||
|
@@ -80,7 +80,7 @@
|
||||
android:id="@+id/adp_app_all_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="30dp"
|
||||
android:text="完全替换 "
|
||||
android:text="全部替换 "
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="13sp" />
|
||||
</LinearLayout>
|
||||
|
Reference in New Issue
Block a user