diff --git a/.idea/misc.xml b/.idea/misc.xml
index d22d7e5..0682c95 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -9,7 +9,7 @@
-
+
diff --git a/README.md b/README.md
index cfab435..3521350 100644
--- a/README.md
+++ b/README.md
@@ -103,4 +103,5 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
```
+Powered by [YukiHookAPI](https://github.com/fankes/YukiHookAPI)
版权所有 © 2019-2022 Fankes Studio(qzmmcn@163.com)
diff --git a/app/build.gradle b/app/build.gradle
index 90473bb..13ed32b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
+ id 'com.google.devtools.ksp' version '1.6.10-1.0.2'
}
android {
@@ -19,17 +20,17 @@ android {
defaultConfig {
applicationId "com.fankes.miui.notify"
- minSdk 26
- targetSdk 26
- versionCode 8
- versionName "1.36"
+ minSdk 23
+ targetSdk 31
+ versionCode rootProject.ext.appVersionCode
+ versionName rootProject.ext.appVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
- minifyEnabled true
+ minifyEnabled false
signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
@@ -43,21 +44,29 @@ android {
}
}
+/** 移除无效耗时 lint Task */
+tasks.whenTaskAdded {
+ task -> if (task.name == "lintVitalRelease") task.enabled = false
+}
+tasks.whenTaskAdded {
+ task -> if (task.name == "lintVitalAnalyzeRelease") task.enabled = false
+}
+tasks.whenTaskAdded {
+ task -> if (task.name == "lintVitalReportRelease") task.enabled = false
+}
+
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 快速实现
- implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
- // Kotlin 扩展
- implementation 'com.gyf.immersionbar:immersionbar-ktx:3.0.0'
+ compileOnly 'de.robv.android.xposed:api:82'
+ implementation 'com.highcapable.yukihookapi:api:1.0'
+ ksp 'com.highcapable.yukihookapi:ksp-xposed:1.0'
+ implementation 'com.geyifeng.immersionbar:immersionbar:3.2.0'
+ implementation 'com.geyifeng.immersionbar:immersionbar-ktx:3.2.0'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
- //noinspection GradleDynamicVersion
- testImplementation 'junit:junit:4.+'
+ testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a6b334e..e84fd70 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -13,20 +13,17 @@
android:theme="@style/Theme.MIUINativeNotifyIcon"
tools:ignore="AllowBackup">
-
-
-
+ android:value="93" />
*
- * This file is Created by zpp0196 on 2018/4/11.
+ * This file is Created by fankes on 2022/1/24.
*/
-package com.fankes.miui.notify.utils
+@file:Suppress("DEPRECATION", "SetWorldReadable")
-import com.fankes.miui.notify.BuildConfig
-import de.robv.android.xposed.XSharedPreferences
+package com.fankes.miui.notify.hook
-object XPrefUtils {
+object HookConst {
- private var xPrefCacheKeyValueBooleans = HashMap()
+ const val ENABLE_MODULE = "_enable_module"
+ const val ENABLE_MODULE_LOG = "_enable_module_log"
+ const val ENABLE_HIDE_ICON = "_hide_icon"
+ const val ENABLE_COLOR_ICON_HOOK = "_color_icon_hook"
+ const val ENABLE_NOTIFY_ICON_HOOK = "_notify_icon_hook"
- fun getBoolean(key: String, default: Boolean = false) =
- xPrefCacheKeyValueBooleans[key].let {
- it ?: pref.getBoolean(key, default).let { e ->
- xPrefCacheKeyValueBooleans[key] = e
- e
- }
- }
-
- private val pref: XSharedPreferences
- get() {
- val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID)
- preferences.makeWorldReadable()
- preferences.reload()
- return preferences
- }
+ const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
}
\ No newline at end of file
diff --git a/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt b/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt
new file mode 100644
index 0000000..0c6454f
--- /dev/null
+++ b/app/src/main/java/com/fankes/miui/notify/hook/HookEntry.kt
@@ -0,0 +1,508 @@
+/*
+ * 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 and our eula as published
+ * by ferredoxin.
+ *
+ * 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
+ *
+ *
+ * This file is Created by fankes on 2022/2/15.
+ */
+package com.fankes.miui.notify.hook
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.graphics.Outline
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.service.notification.StatusBarNotification
+import android.view.View
+import android.view.ViewOutlineProvider
+import android.widget.ImageView
+import androidx.core.graphics.drawable.toBitmap
+import com.fankes.miui.notify.hook.HookConst.ENABLE_COLOR_ICON_HOOK
+import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE
+import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE_LOG
+import com.fankes.miui.notify.hook.HookConst.ENABLE_NOTIFY_ICON_HOOK
+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.highcapable.yukihookapi.YukiHookAPI.configs
+import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
+import com.highcapable.yukihookapi.hook.bean.VariousClass
+import com.highcapable.yukihookapi.hook.factory.*
+import com.highcapable.yukihookapi.hook.log.loggerW
+import com.highcapable.yukihookapi.hook.param.PackageParam
+import com.highcapable.yukihookapi.hook.type.android.ContextClass
+import com.highcapable.yukihookapi.hook.type.android.DrawableClass
+import com.highcapable.yukihookapi.hook.type.android.ImageViewClass
+import com.highcapable.yukihookapi.hook.type.java.IntType
+import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy
+
+@InjectYukiHookWithXposed
+class HookEntry : YukiHookXposedInitProxy {
+
+ companion object {
+
+ /** MIUI 新版本存在的类 */
+ private const val SystemUIApplicationClass = "$SYSTEMUI_PACKAGE_NAME.SystemUIApplication"
+
+ /** MIUI 新版本存在的类 */
+ private const val NotificationHeaderViewWrapperInjectorClass =
+ "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapperInjector"
+
+ /** 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 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 = VariousClass(
+ "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationUtil",
+ "$SYSTEMUI_PACKAGE_NAME.miui.statusbar.notification.NotificationUtil"
+ )
+
+ /** 根据多个版本存在不同的包名相同的类 */
+ private val ExpandedNotificationClass = VariousClass(
+ "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.ExpandedNotification",
+ "$SYSTEMUI_PACKAGE_NAME.miui.statusbar.ExpandedNotification"
+ )
+ }
+
+ /**
+ * - 这个是修复彩色图标的关键核心代码判断
+ *
+ * 判断是否为灰度图标 - 反射执行系统方法
+ * @param context 实例
+ * @param drawable 要判断的图标
+ * @return [Boolean]
+ */
+ private fun PackageParam.isGrayscaleIcon(context: Context, drawable: Drawable) = safeOfFalse {
+ ContrastColorUtilClass.clazz.let {
+ it.obtainMethod(name = "isGrayscaleIcon", DrawableClass)
+ ?.invokeAny(it.obtainMethod(name = "getInstance", ContextClass)?.invokeStatic(context), drawable) ?: false
+ }
+ }
+
+ /**
+ * 获取当前通知栏的样式
+ * @return [Boolean]
+ */
+ private fun PackageParam.isShowMiuiStyle() = safeOfFalse {
+ NotificationUtilClass.clazz.obtainMethod(name = "showMiuiStyle")?.invokeStatic() ?: false
+ }
+
+ /**
+ * 是否为新版本 MIUI 方案
+ *
+ * 拥有状态栏图标颜色检查功能
+ * @return [Boolean]
+ */
+ private fun PackageParam.hasIgnoreStatusBarIconColor() = safeOfFalse {
+ NotificationUtilClass.clazz.hasMethod(name = "ignoreStatusBarIconColor", ExpandedNotificationClass.clazz)
+ }
+
+ /**
+ * 获取 [ExpandedNotificationClass] 的应用名称
+ * @param instance 通知实例
+ * @return [String]
+ */
+ private fun PackageParam.findAppName(instance: Any?) = safeOf(default = "") {
+ ExpandedNotificationClass.clazz.obtainMethod(name = "getAppName")?.invokeAny(instance) ?: ""
+ }
+
+ /**
+ * 判断通知是否来自 MIPUSH
+ * @return [Boolean]
+ */
+ private val StatusBarNotification.isXmsf get() = opPkgName == "com.xiaomi.xmsf"
+
+ /**
+ * 获取全局上下文
+ * @return [Context] or null
+ */
+ private val PackageParam.globalContext
+ get() = safeOfNull {
+ SystemUIApplicationClass.clazz.obtainMethod(name = "getContext")?.invokeStatic()
+ }
+
+ /**
+ * Hook 状态栏小图标
+ *
+ * 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
+ * @param context 实例
+ * @param expandedNf 通知实例
+ * @param iconDrawable 小图标 [Drawable]
+ * @param it 回调小图标 - ([Bitmap] 小图标)
+ */
+ private fun PackageParam.hookSmallIconOnSet(
+ context: Context,
+ expandedNf: StatusBarNotification?,
+ iconDrawable: Drawable?,
+ it: (Bitmap) -> Unit
+ ) = safeRun(msg = "GetSmallIconOnSet") {
+ if (iconDrawable == null) return@safeRun
+ /** 判断是否不是灰度图标 */
+ val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable)
+ /** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
+ expandedNf?.also { notifyInstance ->
+ /** 目标彩色通知 APP 图标 */
+ var customIcon: Bitmap? = null
+ if (prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, 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
+ }
+ }
+ }
+ when {
+ /** 如果开启了修复 APP 的彩色图标 */
+ customIcon != null && prefs.getBoolean(ENABLE_NOTIFY_ICON_HOOK, default = true) -> it(customIcon!!)
+ /** 若不是灰度图标自动处理为圆角 */
+ isNotGrayscaleIcon -> it(iconDrawable.toBitmap().round(15.dp(context)))
+ }
+ }
+ }
+
+ /**
+ * Hook 通知栏小图标
+ *
+ * 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
+ * @param context 实例
+ * @param expandedNf 通知实例
+ * @param iconImageView 通知图标实例
+ */
+ private fun PackageParam.hookNotifyIconOnSet(context: Context, expandedNf: StatusBarNotification?, iconImageView: ImageView) =
+ safeRun(msg = "AutoSetAppIconOnSet") {
+ /** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
+ expandedNf?.let { notifyInstance ->
+ /** 是否 Hook 彩色通知图标 */
+ val isHookColorIcon = prefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
+
+ /** 新版风格反色 */
+ val newStyle = if (context.isSystemInDarkMode) 0xFF2D2D2D.toInt() else Color.WHITE
+
+ /** 旧版风格反色 */
+ val oldStyle = if (context.isNotSystemInDarkMode) 0xFF707070.toInt() else Color.WHITE
+
+ /** 获取通知小图标 */
+ val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
+
+ /** 判断图标风格 */
+ val isGrayscaleIcon = isGrayscaleIcon(context, iconDrawable)
+
+ /** 自定义默认小图标 */
+ var customIcon: Bitmap? = null
+ if (isHookColorIcon) run {
+ IconPackParams.iconDatas.forEach {
+ if ((notifyInstance.opPkgName == it.packageName ||
+ findAppName(notifyInstance) == it.appName) &&
+ isAppNotifyHookOf(it)
+ ) {
+ if (!isGrayscaleIcon || isAppNotifyHookAllOf(it))
+ customIcon = it.iconBitmap
+ return@run
+ }
+ }
+ }
+ /** 如果开启了修复 APP 的彩色图标 */
+ if (customIcon != null && prefs.getBoolean(ENABLE_NOTIFY_ICON_HOOK, default = true))
+ iconImageView.apply {
+ /** 设置自定义小图标 */
+ setImageBitmap(customIcon)
+ /** 上色 */
+ setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle)
+ }
+ else {
+ /** 重新设置图标 - 防止系统更改它 */
+ iconImageView.setImageDrawable(iconDrawable)
+ /** 判断是否开启 Hook 彩色图标 */
+ if (isHookColorIcon) {
+ /** 判断如果是灰度图标就给他设置一个白色颜色遮罩 */
+ if (isGrayscaleIcon)
+ iconImageView.setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle)
+ else
+ iconImageView.apply {
+ clipToOutline = true
+ /** 设置一个圆角轮廓裁切 */
+ outlineProvider = object : ViewOutlineProvider() {
+ override fun getOutline(view: View, out: Outline) {
+ out.setRoundRect(
+ 0, 0,
+ view.width, view.height, 5.dp(context)
+ )
+ }
+ }
+ /** 清除原生的背景边距设置 */
+ if (isUpperOfAndroidS) setPadding(0, 0, 0, 0)
+ /** 清除原生的主题色背景圆圈颜色 */
+ if (isUpperOfAndroidS) background = null
+ }
+ /** 否则一律设置灰度图标 */
+ } else iconImageView.setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle)
+ }
+ }
+ }
+
+ /**
+ * 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)
+
+ /** 判断是否不是灰度图标 */
+ val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable)
+
+ /** 获取目标修复彩色图标的 APP */
+ var isTargetApp = false
+ run {
+ IconPackParams.iconDatas.forEach {
+ if ((notifyInstance.opPkgName == it.packageName ||
+ findAppName(notifyInstance) == it.appName) &&
+ isAppNotifyHookOf(it)
+ ) {
+ if (isNotGrayscaleIcon || isAppNotifyHookAllOf(it)) isTargetApp = true
+ return@run
+ }
+ }
+ }
+ /**
+ * 如果开启了修复 APP 的彩色图标
+ * 只要不是灰度就返回彩色图标
+ * 否则不对颜色进行反色处理防止一些系统图标出现异常
+ */
+ if (isTargetApp && prefs.getBoolean(ENABLE_NOTIFY_ICON_HOOK, default = true))
+ false
+ else isNotGrayscaleIcon
+ } ?: true
+ } else false
+
+ override fun onHook() = encase {
+ configs {
+ debugTag = "MIUINativeNotifyIcon"
+ isDebug = prefs.getBoolean(ENABLE_MODULE_LOG)
+ }
+ loadApp(SYSTEMUI_PACKAGE_NAME) {
+ when {
+ /** 不是 MIUI 系统停止 Hook */
+ isNotMIUI -> loggerW(msg = "Aborted Hook -> This System is not MIUI")
+ /** 系统版本低于 Android P 停止 Hook */
+ isLowerAndroidP -> loggerW(msg = "Aborted Hook -> This System is lower than Android P")
+ /** 不是支持的 MIUI 系统停止 Hook */
+ isNotSupportMiuiVersion -> loggerW(msg = "Aborted Hook -> This MIUI Version $miuiVersion not supported")
+ /** Hook 被手动关闭停止 Hook */
+ !prefs.getBoolean(ENABLE_MODULE, default = true) -> loggerW(msg = "Aborted Hook -> Hook Closed")
+ /** 开始 Hook */
+ else -> {
+ findClass(NotificationUtilClass).hook {
+ /** 强制回写系统的状态栏图标样式为原生 */
+ injectMember {
+ method {
+ name = "shouldSubstituteSmallIcon"
+ param(ExpandedNotificationClass.clazz)
+ }
+ /**
+ * 因为之前的 MIUI 版本的状态栏图标颜色会全部设置为白色的 - 找不到修复的地方就直接判断版本了
+ * 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook
+ */
+ replaceAny { if (hasIgnoreStatusBarIconColor()) false else 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
+ method {
+ name = "getSmallIcon"
+ param(ExpandedNotificationClass.clazz, IntType)
+ }.remedys {
+ method {
+ name = "getSmallIcon"
+ param(ExpandedNotificationClass.clazz)
+ }
+ method {
+ isUseLegacy = true
+ name = "getSmallIcon"
+ param(ContextClass, ExpandedNotificationClass.clazz)
+ }
+ }
+ 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) }
+ }
+ }
+ }
+ }
+ findClass(StatusBarIconViewClass).hook {
+ /** 修复通知图标为彩色 - MIPUSH 修复 */
+ injectMember {
+ method { name = "updateIconColor" }
+ afterHook {
+ /** 获取自身 */
+ val iconImageView = instance() ?: return@afterHook
+
+ /** 获取通知实例 */
+ val expandedNf = thisClass.obtainFieldAny(instance, name = "mNotification")
+
+ /**
+ * 强制设置图标 - 防止 MIPUSH 不生效
+ * 由于之前版本没有 [hasIgnoreStatusBarIconColor] 判断 - MIPUSH 的图标颜色也是白色的
+ * 所以之前的版本取消这个 Hook - 实在找不到设置图标的地方 - 状态栏图标就彩色吧
+ */
+ if (hasIgnoreStatusBarIconColor() && expandedNf?.isXmsf == true)
+ hookSmallIconOnSet(
+ context = iconImageView.context,
+ expandedNf,
+ expandedNf.notification?.smallIcon?.loadDrawable(iconImageView.context)
+ ) { icon -> iconImageView.setImageBitmap(icon) }
+
+ /**
+ * 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook
+ * 新版本不需要下面的代码设置颜色 - 同样停止 Hook
+ */
+ if (hasIgnoreStatusBarIconColor() || isShowMiuiStyle()) return@afterHook
+
+ /** 是否忽略图标颜色 */
+ val isIgnoredColor = hookIgnoreStatusBarIconColor(iconImageView.context, expandedNf)
+
+ /** 当前着色颜色 */
+ val currentColor = field {
+ name = "mCurrentSetColor"
+ type = IntType
+ }.of(instance) ?: Color.WHITE
+ /** 判断并设置颜色 */
+ if (isIgnoredColor)
+ iconImageView.colorFilter = null
+ else iconImageView.setColorFilter(currentColor)
+ }
+ }
+ }
+ if (NotificationHeaderViewWrapperInjectorClass.hasClass)
+ findClass(NotificationHeaderViewWrapperInjectorClass).hook {
+ /** 修复下拉通知图标自动设置回 APP 图标的方法 */
+ injectMember {
+ var isUseLegacy = false
+ method {
+ name = "setAppIcon"
+ param(ContextClass, ImageViewClass, ExpandedNotificationClass.clazz)
+ }.remedys {
+ method {
+ isUseLegacy = true
+ name = "setAppIcon"
+ param(ImageViewClass, ExpandedNotificationClass.clazz)
+ }
+ }
+ 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
+ )
+
+ }
+ }
+ /** 干掉下拉通知图标自动设置回 APP 图标的方法 - Android 12 */
+ if (isUpperOfAndroidS)
+ injectMember {
+ method {
+ name = "resetIconBgAndPaddings"
+ param(ImageViewClass, ExpandedNotificationClass.clazz)
+ }
+ intercept()
+ }
+ }
+ else
+ findClass(NotificationHeaderViewWrapperClass).hook {
+ /** 之前的版本解决方案 */
+ injectMember {
+ method { name = "handleHeaderViews" }
+ afterHook {
+ /** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
+ if (!hasIgnoreStatusBarIconColor() && isShowMiuiStyle()) return@afterHook
+ /** 获取小图标 */
+ val iconImageView = NotificationHeaderViewWrapperClass.clazz
+ .obtainFieldAny(any = instance, name = "mIcon") ?: return@afterHook
+ /** 从父类中得到 mRow 变量 - [ExpandableNotificationRowClass] */
+ NotificationViewWrapperClass.clazz.obtainFieldAny(instance, name = "mRow").apply {
+ /** 获取其中的得到通知方法 */
+ val expandedNf =
+ ExpandableNotificationRowClass.clazz.obtainMethod(name = "getStatusBarNotification")
+ ?.invokeAny(any = this)
+ /** 执行 Hook */
+ hookNotifyIconOnSet(iconImageView.context, expandedNf, iconImageView)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/fankes/miui/notify/hook/HookMain.kt b/app/src/main/java/com/fankes/miui/notify/hook/HookMain.kt
deleted file mode 100644
index 6131d6b..0000000
--- a/app/src/main/java/com/fankes/miui/notify/hook/HookMain.kt
+++ /dev/null
@@ -1,841 +0,0 @@
-/*
- * 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 and our eula as published
- * by ferredoxin.
- *
- * 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
- *
- *
- * This file is Created by fankes on 2022/1/24.
- */
-@file:Suppress("SameParameterValue", "DEPRECATION")
-
-package com.fankes.miui.notify.hook
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Color
-import android.graphics.Outline
-import android.graphics.drawable.Drawable
-import android.graphics.drawable.Icon
-import android.service.notification.StatusBarNotification
-import android.util.Log
-import android.view.View
-import android.view.ViewOutlineProvider
-import android.widget.ImageView
-import androidx.annotation.Keep
-import androidx.core.graphics.drawable.toBitmap
-import com.fankes.miui.notify.hook.HookMedium.SELF_PACKAGE_NAME
-import com.fankes.miui.notify.hook.HookMedium.SYSTEMUI_PACKAGE_NAME
-import com.fankes.miui.notify.params.IconPackParams
-import com.fankes.miui.notify.utils.*
-import de.robv.android.xposed.*
-import de.robv.android.xposed.callbacks.XC_LoadPackage
-
-@Keep
-class HookMain : IXposedHookLoadPackage {
-
- companion object {
-
- /** 一直存在的类 */
- private const val SystemUIApplicationClass = "$SYSTEMUI_PACKAGE_NAME.SystemUIApplication"
-
- /** MIUI 新版本存在的类 */
- private const val NotificationHeaderViewWrapperInjectorClass =
- "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.row.wrapper.NotificationHeaderViewWrapperInjector"
-
- /** 一直存在的类 */
- private const val NotificationHeaderViewWrapperClass =
- "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationHeaderViewWrapper"
-
- /** 一直存在的类 */
- private const val NotificationViewWrapperClass =
- "$SYSTEMUI_PACKAGE_NAME.statusbar.notification.NotificationViewWrapper"
-
- /** 原生存在的类 */
- private const val StatusBarIconViewClass = "$SYSTEMUI_PACKAGE_NAME.statusbar.StatusBarIconView"
-
- /** 原生存在的类 */
- private const val ContrastColorUtilClass = "com.android.internal.util.ContrastColorUtil"
-
- /** 未确定是否只有旧版本存在的类 */
- private 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 方法体 */
- private val replaceToNull = object : XC_MethodReplacement() {
- override fun replaceHookedMethod(param: MethodHookParam?): Any? {
- return null
- }
- }
-
- /** 仅作用于替换的 Hook 方法体 */
- private val replaceToTrue = object : XC_MethodReplacement() {
- override fun replaceHookedMethod(param: MethodHookParam?): Any {
- return true
- }
- }
-
- /** 仅作用于替换的 Hook 方法体 */
- @Suppress("unused")
- private val replaceToFalse = object : XC_MethodReplacement() {
- override fun replaceHookedMethod(param: MethodHookParam?): Any {
- return false
- }
- }
-
- /**
- * 忽略异常运行
- * @param error 错误信息
- * @param it 正常回调
- */
- private fun runWithoutError(error: String = "", it: () -> Unit) {
- try {
- it()
- } catch (e: Throwable) {
- logE(content = "hookFailed: $error", e)
- }
- }
-
- /**
- * Print the log
- * @param content
- * @param it 继续执行的方法
- */
- private fun logD(content: String, it: () -> Unit = {}) {
- it()
- if (!HookMedium.getBoolean(HookMedium.ENABLE_MODULE_LOG, default = false)) return
- XposedBridge.log("[MIUINativeNotifyIcon][D]>$content")
- Log.d("MIUINativeNotifyIcon", content)
- }
-
- /**
- * Print the log
- * @param content
- * @param it 继续执行的方法
- */
- private fun logW(content: String, it: () -> Unit = {}) {
- it()
- if (!HookMedium.getBoolean(HookMedium.ENABLE_MODULE_LOG, default = false)) return
- XposedBridge.log("[MIUINativeNotifyIcon][W]>$content")
- Log.d("MIUINativeNotifyIcon", content)
- }
-
- /**
- * Print the log
- * @param content
- * @param e 异常
- * @param it 继续执行的方法
- */
- private fun logE(content: String, e: Throwable? = null, it: () -> Unit = {}) {
- it()
- XposedBridge.log("[MIUINativeNotifyIcon][E]>$content")
- XposedBridge.log(e)
- Log.e("MIUINativeNotifyIcon", content, e)
- }
-
- /**
- * 目标类是否存在
- * @param name 类名
- * @return [Boolean]
- */
- private fun XC_LoadPackage.LoadPackageParam.isClassExist(name: String) = try {
- classLoader.loadClass(name)
- true
- } catch (_: Throwable) {
- false
- }
-
- /**
- * 目标方法是否存在
- * @param classPair 类数组
- * @param name 方法名
- * @param param 方法参数类型数组
- * @return [Boolean]
- */
- private fun XC_LoadPackage.LoadPackageParam.isMethodExist(
- classPair: Pair,
- name: String, vararg param: Class<*>
- ) = try {
- (try {
- classLoader.loadClass(classPair.first)
- } catch (_: Throwable) {
- try {
- classLoader.loadClass(classPair.second)
- } catch (_: Throwable) {
- null
- }
- })?.getDeclaredMethod(name, *param)
- true
- } catch (_: Throwable) {
- false
- }
-
- /**
- * 目标方法是否存在
- * @param className 类名
- * @param name 方法名
- * @param param 方法参数类型数组
- * @return [Boolean]
- */
- private fun XC_LoadPackage.LoadPackageParam.isMethodExist(className: String, name: String, vararg param: Class<*>) =
- try {
- (try {
- classLoader.loadClass(className)
- } catch (_: Throwable) {
- null
- })?.getDeclaredMethod(name, *param)
- true
- } catch (_: Throwable) {
- false
- }
-
- /**
- * 查找目标类
- * @param name 类名
- * @return [Class]
- */
- private fun XC_LoadPackage.LoadPackageParam.findClass(name: String) =
- classLoader.loadClass(name)
-
- /**
- * 查找目标类 - 两个类都没找到才会报错
- * @param pair 类名数组
- * @return [Class]
- */
- private fun XC_LoadPackage.LoadPackageParam.findClass(pair: Pair) = 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) = try {
- classLoader.loadClass(pair.first)
- pair.first
- } catch (_: Throwable) {
- try {
- classLoader.loadClass(pair.second)
- pair.second
- } catch (_: Throwable) {
- logE(content = "Cannot find Class ${pair.first} and ${pair.second}")
- error("[Throwable] Cannot find Class ${pair.first} and ${pair.second}")
- }
- }
-
- /**
- * - ⚡这个是修复彩色图标的关键核心代码判断
- *
- * 判断是否为灰度图标 - 反射执行系统方法
- * @param context 实例
- * @param icon 要判断的图标
- * @return [Boolean]
- */
- private fun XC_LoadPackage.LoadPackageParam.isGrayscaleIcon(context: Context, icon: Drawable) =
- findClass(ContrastColorUtilClass).let {
- val instance = it.getDeclaredMethod("getInstance", Context::class.java)
- .apply { isAccessible = true }.invoke(null, context)
- it.getDeclaredMethod("isGrayscaleIcon", Drawable::class.java)
- .apply { isAccessible = true }.invoke(instance, icon) as Boolean
- }
-
- /**
- * 获取当前通知栏的样式
- * @return [Boolean]
- */
- private fun XC_LoadPackage.LoadPackageParam.isShowMiuiStyle() = try {
- findClass(NotificationUtilClass).let {
- it.getDeclaredMethod("showMiuiStyle")
- .apply { isAccessible = true }.invoke(null) as Boolean
- }
- } catch (_: Throwable) {
- false
- }
-
- /**
- * 是否为新版本 MIUI 方案
- *
- * 拥有状态栏图标颜色检查功能
- * @return [Boolean]
- */
- private fun XC_LoadPackage.LoadPackageParam.hasIgnoreStatusBarIconColor() = try {
- isMethodExist(NotificationUtilClass, name = "ignoreStatusBarIconColor", findClass(ExpandedNotificationClass))
- } catch (_: Throwable) {
- false
- }
-
- /**
- * 获取 [ExpandedNotificationClass] 的应用名称
- * @param instance 通知实例
- * @return [String]
- */
- private fun XC_LoadPackage.LoadPackageParam.findAppName(instance: Any?) = try {
- findClass(ExpandedNotificationClass).getDeclaredMethod("getAppName").let {
- it.isAccessible = true
- it.invoke(instance) as? String ?: ""
- }
- } catch (e: Throwable) {
- ""
- }
-
- /**
- * 判断通知是否来自 MIPUSH
- * @return [Boolean]
- */
- private val StatusBarNotification.isXmsf get() = opPkgName == "com.xiaomi.xmsf"
-
- /**
- * 获取全局上下文
- * @return [Context] or null
- */
- private val XC_LoadPackage.LoadPackageParam.globalContext
- get() = try {
- findClass(SystemUIApplicationClass)
- .getDeclaredMethod("getContext").apply { isAccessible = true }
- .invoke(null) as? Context?
- } catch (_: Throwable) {
- null
- }
-
- /**
- * Hook 状态栏小图标
- *
- * 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
- * @param context 实例
- * @param expandedNf 通知实例
- * @param iconDrawable 小图标 [Drawable]
- * @param isLegacyWay 旧版本 Hook 方式
- * @param it 回调小图标 - ([Bitmap] 小图标)
- */
- private fun XC_LoadPackage.LoadPackageParam.hookSmallIconOnSet(
- context: Context,
- expandedNf: StatusBarNotification?,
- iconDrawable: Drawable?,
- isLegacyWay: Boolean,
- it: (Bitmap) -> Unit
- ) {
- runWithoutError(error = "GetSmallIconOnSet") {
- if (iconDrawable == null) {
- logD(content = "GetSmallIconOnSet -> icon is null")
- return@runWithoutError
- }
- /** 判断是否不是灰度图标 */
- val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable)
- /** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
- expandedNf?.also { notifyInstance ->
- /** 目标彩色通知 APP 图标 */
- var customIcon: Bitmap? = null
- if (HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true))
- run {
- IconPackParams.iconDatas.forEach {
- if ((notifyInstance.opPkgName == it.packageName ||
- findAppName(notifyInstance) == it.appName) &&
- HookMedium.isAppNotifyHookOf(it)
- ) {
- if (isNotGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it))
- customIcon = it.iconBitmap
- return@run
- }
- }
- }
- when {
- /** 如果开启了修复 APP 的彩色图标 */
- customIcon != null && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true) ->
- logD(
- content = "GetSmallIconOnSet -> " +
- "hook Custom AppIcon [pkgName] ${notifyInstance.opPkgName} " +
- "[appName] ${findAppName(notifyInstance)} " +
- "[legacyWay] $isLegacyWay"
- ) { it(customIcon!!) }
- /** 若不是灰度图标自动处理为圆角 */
- isNotGrayscaleIcon ->
- logD(
- content = "GetSmallIconOnSet -> " +
- "hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
- "[appName] ${findAppName(notifyInstance)} " +
- "[legacyWay] $isLegacyWay"
- ) {
- it(iconDrawable.toBitmap().round(15.dp(context)))
- }
- }
- } ?: logW(content = "GetSmallIconOnSet -> StatusBarNotification got null [legacyWay] $isLegacyWay")
- }
- }
-
- /**
- * Hook 通知栏小图标
- *
- * 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
- * @param context 实例
- * @param expandedNf 通知实例
- * @param iconImageView 通知图标实例
- */
- private fun XC_LoadPackage.LoadPackageParam.hookNotifyIconOnSet(
- context: Context,
- expandedNf: StatusBarNotification?,
- iconImageView: ImageView
- ) {
- runWithoutError(error = "AutoSetAppIconOnSet") {
- /** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
- expandedNf?.let { notifyInstance ->
- /** 是否 Hook 彩色通知图标 */
- val isHookColorIcon = HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true)
-
- /** 新版风格反色 */
- val newStyle = if (context.isSystemInDarkMode) 0xFF2D2D2D.toInt() else Color.WHITE
-
- /** 旧版风格反色 */
- val oldStyle = if (context.isNotSystemInDarkMode) 0xFF707070.toInt() else Color.WHITE
-
- /** 获取通知小图标 */
- val iconDrawable = notifyInstance.notification.smallIcon.loadDrawable(context)
-
- /** 判断图标风格 */
- val isGrayscaleIcon = isGrayscaleIcon(context, iconDrawable)
-
- /** 自定义默认小图标 */
- var customIcon: Bitmap? = null
- if (isHookColorIcon) run {
- IconPackParams.iconDatas.forEach {
- if ((notifyInstance.opPkgName == it.packageName ||
- findAppName(notifyInstance) == it.appName) &&
- HookMedium.isAppNotifyHookOf(it)
- ) {
- if (!isGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it))
- customIcon = it.iconBitmap
- return@run
- }
- }
- }
- /** 如果开启了修复 APP 的彩色图标 */
- if (customIcon != null && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true))
- iconImageView.apply {
- /** 设置自定义小图标 */
- setImageBitmap(customIcon)
- /** 上色 */
- setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle)
- /** 输出调试日志 */
- logD(
- content = "AutoSetAppIconOnSet -> " +
- "hook Custom AppIcon [pkgName] ${notifyInstance.opPkgName} " +
- "[appName] ${findAppName(notifyInstance)}"
- )
- }
- else {
- /** 重新设置图标 - 防止系统更改它 */
- iconImageView.setImageDrawable(iconDrawable)
- /** 判断是否开启 Hook 彩色图标 */
- if (isHookColorIcon) {
- /** 判断如果是灰度图标就给他设置一个白色颜色遮罩 */
- if (isGrayscaleIcon)
- logD(
- content = "AutoSetAppIconOnSet -> " +
- "hook Grayscale AppIcon [pkgName] ${notifyInstance.opPkgName} " +
- "[appName] ${findAppName(notifyInstance)}"
- ) { iconImageView.setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle) }
- else
- iconImageView.apply {
- clipToOutline = true
- /** 设置一个圆角轮廓裁切 */
- outlineProvider = object : ViewOutlineProvider() {
- override fun getOutline(view: View, out: Outline) {
- out.setRoundRect(
- 0, 0,
- view.width, view.height, 5.dp(context)
- )
- }
- }
- /** 清除原生的背景边距设置 */
- if (isUpperOfAndroidS) setPadding(0, 0, 0, 0)
- /** 清除原生的主题色背景圆圈颜色 */
- if (isUpperOfAndroidS) background = null
- /** 输出调试日志 */
- logD(
- content = "AutoSetAppIconOnSet -> " +
- "hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
- "[appName] ${findAppName(notifyInstance)}"
- )
- }
- /** 否则一律设置灰度图标 */
- } else
- logD(
- content = "AutoSetAppIconOnSet -> " +
- "hook NonColor AppIcon [pkgName] ${notifyInstance.opPkgName} " +
- "[appName] ${findAppName(notifyInstance)}"
- ) { iconImageView.setColorFilter(if (isUpperOfAndroidS) newStyle else oldStyle) }
- }
- } ?: logW(content = "AutoSetAppIconOnSet -> StatusBarNotification got null")
- }
- }
-
- /**
- * Hook 通知栏小图标颜色
- *
- * 区分系统版本 - 由于每个系统版本的方法不一样这里单独拿出来进行 Hook
- * @param context 实例
- * @param expandedNf 状态栏实例
- * @return [Boolean] 是否忽略通知图标颜色
- */
- private fun XC_LoadPackage.LoadPackageParam.hookIgnoreStatusBarIconColor(
- context: Context,
- expandedNf: StatusBarNotification?
- ) = if (HookMedium.getBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, default = true))
- try {
- /** 获取通知对象 - 由于 MIUI 的版本迭代不规范性可能是空的 */
- expandedNf?.let { notifyInstance ->
- /** 获取通知小图标 */
- val iconDrawable =
- notifyInstance.notification.smallIcon.loadDrawable(context)
-
- /** 判断是否不是灰度图标 */
- val isNotGrayscaleIcon = !isGrayscaleIcon(context, iconDrawable)
-
- /** 获取目标修复彩色图标的 APP */
- var isTargetApp = false
- run {
- IconPackParams.iconDatas.forEach {
- if ((notifyInstance.opPkgName == it.packageName ||
- findAppName(notifyInstance) == it.appName) &&
- HookMedium.isAppNotifyHookOf(it)
- ) {
- if (isNotGrayscaleIcon || HookMedium.isAppNotifyHookAllOf(it)) isTargetApp = true
- return@run
- }
- }
- }
- /** 如果开启了修复 APP 的彩色图标 */
- if (isTargetApp && HookMedium.getBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, default = true)) let {
- logD(
- content = "IgnoreStatusBarIconColor -> " +
- "hook Color AppIcon [pkgName] ${notifyInstance.opPkgName} " +
- "[appName] ${findAppName(notifyInstance)}"
- )
- false
- }
- else let {
- logD(
- content = "IgnoreStatusBarIconColor -> " +
- "hook Grayscale[${!isNotGrayscaleIcon}] AppIcon " +
- "[pkgName] ${notifyInstance.opPkgName} " +
- "[appName] ${findAppName(notifyInstance)}"
- )
- /** 只要不是灰度就返回彩色图标 */
- isNotGrayscaleIcon
- }
- } ?: let {
- logW(content = "IgnoreStatusBarIconColor -> StatusBarNotification got null")
- /** 否则不对颜色进行反色处理防止一些系统图标出现异常 */
- true
- }
- } catch (e: Exception) {
- logE("Failed to hook ignoreStatusBarIconColor", e)
- false
- }
- else let {
- logD(content = "IgnoreStatusBarIconColor -> hook NonColor AppIcon")
- false
- }
-
- override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
- if (lpparam == null) return
- when (lpparam.packageName) {
- /** Hook 自身 */
- SELF_PACKAGE_NAME ->
- runWithoutError(error = "HookModuleSelf") {
- XposedHelpers.findAndHookMethod(
- "$SELF_PACKAGE_NAME.hook.HookMedium",
- lpparam.classLoader,
- "isHooked",
- replaceToTrue
- )
- }
- /** Hook 系统 UI */
- SYSTEMUI_PACKAGE_NAME ->
- when {
- /** 不是 MIUI 系统停止 Hook */
- isNotMIUI ->
- logW(content = "Aborted Hook -> This System is not MIUI")
- /** 系统版本低于 Android P 停止 Hook */
- isLowerAndroidP ->
- logW(content = "Aborted Hook -> This System is lower than Android P")
- /** 不是支持的 MIUI 系统停止 Hook */
- isNotSupportMiuiVersion ->
- logW(content = "Aborted Hook -> This MIUI Version $miuiVersion not supported")
- /** Hook 被手动关闭停止 Hook */
- !HookMedium.getBoolean(HookMedium.ENABLE_MODULE, default = true) ->
- logW(content = "Aborted Hook -> Hook Closed")
- else -> {
- /** 强制回写系统的状态栏图标样式为原生 */
- runWithoutError(error = "SubstituteSmallIcon") {
- XposedHelpers.findAndHookMethod(
- lpparam.existClass(NotificationUtilClass),
- lpparam.classLoader,
- "shouldSubstituteSmallIcon",
- lpparam.findClass(ExpandedNotificationClass),
- object : XC_MethodReplacement() {
- override fun replaceHookedMethod(param: MethodHookParam) =
- /**
- * 因为之前的 MIUI 版本的状态栏图标颜色会全部设置为白色的 - 找不到修复的地方就直接判断版本了
- * 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook
- */
- if (lpparam.hasIgnoreStatusBarIconColor()) false else lpparam.isShowMiuiStyle()
- }
- )
- }
- /** 修复通知图标为彩色 */
- if (lpparam.hasIgnoreStatusBarIconColor())
- runWithoutError(error = "IgnoreStatusBarIconColor") {
- XposedHelpers.findAndHookMethod(
- lpparam.existClass(NotificationUtilClass),
- lpparam.classLoader,
- "ignoreStatusBarIconColor",
- lpparam.findClass(ExpandedNotificationClass),
- object : XC_MethodReplacement() {
- override fun replaceHookedMethod(param: MethodHookParam) =
- lpparam.hookIgnoreStatusBarIconColor(
- context = lpparam.globalContext ?: error("GlobalContext got null"),
- param.args?.get(0) as? StatusBarNotification?
- )
- }
- )
- }
- /** 修复通知图标为彩色 - MIPUSH 修复 */
- runWithoutError(error = "UpdateIconColor") {
- XposedHelpers.findAndHookMethod(
- StatusBarIconViewClass,
- lpparam.classLoader, "updateIconColor",
- object : XC_MethodHook() {
- override fun afterHookedMethod(param: MethodHookParam) =
- runWithoutError(error = "UpdateIconColorOnSet") hook@{
- /** 获取自身 */
- val iconImageView = param.thisObject as? ImageView ?: return@hook
-
- /** 获取通知实例 */
- val expandedNf =
- param.thisObject.javaClass.getDeclaredField("mNotification").apply {
- isAccessible = true
- }[param.thisObject] as? StatusBarNotification?
-
- /**
- * 强制设置图标 - 防止 MIPUSH 不生效
- * 由于之前版本没有 [hasIgnoreStatusBarIconColor] 判断 - MIPUSH 的图标颜色也是白色的
- * 所以之前的版本取消这个 Hook - 实在找不到设置图标的地方 - 状态栏图标就彩色吧
- */
- if (lpparam.hasIgnoreStatusBarIconColor() && expandedNf?.isXmsf == true)
- lpparam.hookSmallIconOnSet(
- context = iconImageView.context,
- expandedNf,
- expandedNf.notification?.smallIcon?.loadDrawable(iconImageView.context),
- 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(
- "getSmallIcon",
- lpparam.findClass(ExpandedNotificationClass),
- Int::class.java
- ).apply { isAccessible = true }
- } catch (_: Throwable) {
- try {
- isTooOld = false
- /** 旧版方法 */
- lpparam.findClass(NotificationUtilClass)
- .getDeclaredMethod("getSmallIcon", lpparam.findClass(ExpandedNotificationClass))
- .apply { isAccessible = true }
- } catch (_: Throwable) {
- isTooOld = true
- /** 超旧版方法 */
- lpparam.findClass(NotificationUtilClass)
- .getDeclaredMethod(
- "getSmallIcon",
- Context::class.java,
- lpparam.findClass(ExpandedNotificationClass)
- ).apply { isAccessible = true }
- }
- }.also {
- XposedBridge.hookMethod(it, object : XC_MethodHook() {
- override fun afterHookedMethod(param: MethodHookParam) {
- /** 对于之前没有通知图标色彩判断功能的版本判断是 MIUI 样式就停止 Hook */
- if (!lpparam.hasIgnoreStatusBarIconColor() && lpparam.isShowMiuiStyle()) return
- 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 图标的方法 */
- 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 &&
- 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!")
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/fankes/miui/notify/hook/HookMedium.kt b/app/src/main/java/com/fankes/miui/notify/hook/HookMedium.kt
deleted file mode 100644
index 8c24333..0000000
--- a/app/src/main/java/com/fankes/miui/notify/hook/HookMedium.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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 and our eula as published
- * by ferredoxin.
- *
- * 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
- *
- *
- * This file is Created by fankes on 2022/1/24.
- */
-@file:Suppress("DEPRECATION", "SetWorldReadable")
-
-package com.fankes.miui.notify.hook
-
-import android.content.Context
-import android.os.Handler
-import android.util.Log
-import android.widget.Toast
-import androidx.annotation.Keep
-import com.fankes.miui.notify.application.MNNApplication.Companion.appContext
-import com.fankes.miui.notify.application.MNNApplication.Companion.isMineStarted
-import com.fankes.miui.notify.bean.IconDataBean
-import com.fankes.miui.notify.utils.FileUtils
-import com.fankes.miui.notify.utils.XPrefUtils
-import java.io.File
-
-@Keep
-object HookMedium {
-
- const val ENABLE_MODULE = "_enable_module"
- const val ENABLE_MODULE_LOG = "_enable_module_log"
- const val ENABLE_HIDE_ICON = "_hide_icon"
- const val ENABLE_COLOR_ICON_HOOK = "_color_icon_hook"
- const val ENABLE_NOTIFY_ICON_HOOK = "_notify_icon_hook"
-
- const val SELF_PACKAGE_NAME = "com.fankes.miui.notify"
- const val SYSTEMUI_PACKAGE_NAME = "com.android.systemui"
-
- /**
- * 判断模块是否激活
- *
- * 在 [HookMain] 中 Hook 掉此方法
- * @return [Boolean] 激活状态
- */
- fun isHooked(): Boolean {
- Log.d("MIUINativeNotifyIcon", "isHooked: true")
- return false
- }
-
- /**
- * 获取此 APP 的通知图标是否被 Hook
- * @param bean 图标 bean
- */
- fun isAppNotifyHookOf(bean: IconDataBean) = getBoolean(key = bean.toEnabledName(), default = bean.isEnabled)
-
- /**
- * 设置 Hook 此 APP 的通知图标
- * @param bean 图标 bean
- * @param isHook 是否 Hook
- */
- fun putAppNotifyHookOf(bean: IconDataBean, isHook: Boolean) = putBoolean(key = bean.toEnabledName(), bool = isHook)
-
- /**
- * 获取此 APP 的通知图标是否被全部 Hook
- * @param bean 图标 bean
- */
- fun isAppNotifyHookAllOf(bean: IconDataBean) = getBoolean(key = bean.toEnabledAllName(), default = bean.isEnabledAll)
-
- /**
- * 设置全部 Hook 此 APP 的通知图标
- * @param bean 图标 bean
- * @param isHook 是否 Hook
- */
- fun putAppNotifyHookAllOf(bean: IconDataBean, isHook: Boolean) = putBoolean(key = bean.toEnabledAllName(), bool = isHook)
-
- /**
- * 获取保存的值
- * @param key 名称
- * @param default 默认值
- * @return [Boolean] 保存的值
- */
- fun getBoolean(key: String, default: Boolean = false) =
- if (isMineStarted)
- appContext.getSharedPreferences(
- appContext.packageName + "_preferences",
- Context.MODE_PRIVATE
- ).getBoolean(key, default)
- else XPrefUtils.getBoolean(key, default)
-
- /**
- * 保存值
- * @param key 名称
- * @param bool 值
- */
- fun putBoolean(key: String, bool: Boolean) {
- appContext.getSharedPreferences(
- appContext.packageName + "_preferences",
- Context.MODE_PRIVATE
- ).edit().putBoolean(key, bool).apply()
- setWorldReadable(appContext)
- /** 延迟继续设置强制允许 SP 可读可写 */
- Handler().postDelayed({ setWorldReadable(appContext) }, 500)
- Handler().postDelayed({ setWorldReadable(appContext) }, 1000)
- Handler().postDelayed({ setWorldReadable(appContext) }, 1500)
- }
-
- /**
- * 强制设置 Sp 存储为全局可读可写
- *
- * 以供模块使用
- * @param context 实例
- */
- fun setWorldReadable(context: Context) {
- try {
- if (FileUtils.getDefaultPrefFile(context).exists()) {
- for (file in arrayOf(
- FileUtils.getDataDir(context),
- FileUtils.getPrefDir(context),
- FileUtils.getDefaultPrefFile(context)
- )) {
- file.setReadable(true, false)
- file.setExecutable(true, false)
- }
- }
- } catch (_: Exception) {
- Toast.makeText(context, "无法写入模块设置,请检查权限\n如果此提示一直显示,请不要双开模块", Toast.LENGTH_SHORT).show()
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/fankes/miui/notify/hook/factory/DataFactory.kt b/app/src/main/java/com/fankes/miui/notify/hook/factory/DataFactory.kt
new file mode 100644
index 0000000..e53531a
--- /dev/null
+++ b/app/src/main/java/com/fankes/miui/notify/hook/factory/DataFactory.kt
@@ -0,0 +1,70 @@
+/*
+ * MIUINativeNotifyIcon - Fix the native notification bar icon function abandoned by the MIUI development team.
+ * Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
+ * https://github.com/fankes/MIUINativeNotifyIcon
+ *
+ * This software is non-free but opensource software: you can redistribute it
+ * and/or modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version and our eula as published
+ * by ferredoxin.
+ *
+ * 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
+ *
+ *
+ * This file is Created by fankes on 2022/2/15.
+ */
+package com.fankes.miui.notify.hook.factory
+
+import android.content.Context
+import com.fankes.miui.notify.bean.IconDataBean
+import com.highcapable.yukihookapi.hook.factory.modulePrefs
+import com.highcapable.yukihookapi.hook.param.PackageParam
+
+/**
+ * 获取此 APP 的通知图标是否被 Hook
+ * @param bean 图标 bean
+ */
+fun PackageParam.isAppNotifyHookOf(bean: IconDataBean) = prefs.getBoolean(key = bean.toEnabledName(), default = bean.isEnabled)
+
+/**
+ * 获取此 APP 的通知图标是否被 Hook
+ * @param bean 图标 bean
+ */
+fun Context.isAppNotifyHookOf(bean: IconDataBean) = modulePrefs.getBoolean(key = bean.toEnabledName(), default = bean.isEnabled)
+
+/**
+ * 设置 Hook 此 APP 的通知图标
+ * @param bean 图标 bean
+ * @param isHook 是否 Hook
+ */
+fun Context.putAppNotifyHookOf(bean: IconDataBean, isHook: Boolean) =
+ modulePrefs.putBoolean(key = bean.toEnabledName(), value = isHook)
+
+/**
+ * 获取此 APP 的通知图标是否被全部 Hook
+ * @param bean 图标 bean
+ */
+fun PackageParam.isAppNotifyHookAllOf(bean: IconDataBean) =
+ prefs.getBoolean(key = bean.toEnabledAllName(), default = bean.isEnabledAll)
+
+/**
+ * 获取此 APP 的通知图标是否被全部 Hook
+ * @param bean 图标 bean
+ */
+fun Context.isAppNotifyHookAllOf(bean: IconDataBean) =
+ modulePrefs.getBoolean(key = bean.toEnabledAllName(), default = bean.isEnabledAll)
+
+/**
+ * 设置全部 Hook 此 APP 的通知图标
+ * @param bean 图标 bean
+ * @param isHook 是否 Hook
+ */
+fun Context.putAppNotifyHookAllOf(bean: IconDataBean, isHook: Boolean) =
+ modulePrefs.putBoolean(key = bean.toEnabledAllName(), value = isHook)
\ No newline at end of file
diff --git a/app/src/main/java/com/fankes/miui/notify/ui/ConfigureActivity.kt b/app/src/main/java/com/fankes/miui/notify/ui/ConfigureActivity.kt
index 8eb55fa..3e5acf9 100644
--- a/app/src/main/java/com/fankes/miui/notify/ui/ConfigureActivity.kt
+++ b/app/src/main/java/com/fankes/miui/notify/ui/ConfigureActivity.kt
@@ -36,7 +36,10 @@ import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.utils.widget.ImageFilterView
import com.fankes.miui.notify.R
-import com.fankes.miui.notify.hook.HookMedium
+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
@@ -87,20 +90,20 @@ class ConfigureActivity : BaseActivity() {
holder.appName.text = it.appName
holder.pkgName.text = it.packageName
holder.cbrName.text = "贡献者:" + it.contributorName
- HookMedium.isAppNotifyHookOf(it).also { e ->
+ isAppNotifyHookOf(it).also { e ->
holder.switchOpen.isChecked = e
holder.switchAll.isEnabled = e
}
holder.switchOpen.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
- HookMedium.putAppNotifyHookOf(it, b)
+ putAppNotifyHookOf(it, b)
holder.switchAll.isEnabled = b
SystemUITool.showNeedRestartSnake(context = this@ConfigureActivity)
}
- holder.switchAll.isChecked = HookMedium.isAppNotifyHookAllOf(it)
+ holder.switchAll.isChecked = isAppNotifyHookAllOf(it)
holder.switchAll.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
- HookMedium.putAppNotifyHookAllOf(it, b)
+ putAppNotifyHookAllOf(it, b)
SystemUITool.showNeedRestartSnake(context = this@ConfigureActivity)
}
}
@@ -119,14 +122,14 @@ class ConfigureActivity : BaseActivity() {
}
/** 设置点击事件 */
findViewById(R.id.config_cbr_button).setOnClickListener {
- try {
+ runCatching {
startActivity(Intent().apply {
action = "android.intent.action.VIEW"
data = Uri.parse("https://github.com/fankes/MIUINativeNotifyIcon")
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
- } catch (e: Exception) {
+ }.onFailure {
Toast.makeText(this, "无法启动系统默认浏览器", Toast.LENGTH_SHORT).show()
}
}
diff --git a/app/src/main/java/com/fankes/miui/notify/ui/MainActivity.kt b/app/src/main/java/com/fankes/miui/notify/ui/MainActivity.kt
index 2e9e82c..a5b7ebd 100644
--- a/app/src/main/java/com/fankes/miui/notify/ui/MainActivity.kt
+++ b/app/src/main/java/com/fankes/miui/notify/ui/MainActivity.kt
@@ -38,9 +38,15 @@ import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.core.view.isVisible
import com.fankes.miui.notify.BuildConfig
import com.fankes.miui.notify.R
-import com.fankes.miui.notify.hook.HookMedium
+import com.fankes.miui.notify.hook.HookConst.ENABLE_COLOR_ICON_HOOK
+import com.fankes.miui.notify.hook.HookConst.ENABLE_HIDE_ICON
+import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE
+import com.fankes.miui.notify.hook.HookConst.ENABLE_MODULE_LOG
+import com.fankes.miui.notify.hook.HookConst.ENABLE_NOTIFY_ICON_HOOK
import com.fankes.miui.notify.ui.base.BaseActivity
import com.fankes.miui.notify.utils.*
+import com.highcapable.yukihookapi.hook.factory.modulePrefs
+import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
class MainActivity : BaseActivity() {
@@ -113,27 +119,27 @@ class MainActivity : BaseActivity() {
/** 设置旧版本警告 */
findViewById(R.id.config_notify_app_icon_warn).isVisible = miuiVersion == "12"
/** 获取 Sp 存储的信息 */
- 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 = 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)
+ notifyIconConfigItem.isVisible = modulePrefs.getBoolean(ENABLE_COLOR_ICON_HOOK, default = true)
+ moduleEnableLogSwitch.isVisible = modulePrefs.getBoolean(ENABLE_MODULE, 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)
+ notifyIconHookSwitch.isChecked = modulePrefs.getBoolean(ENABLE_NOTIFY_ICON_HOOK, default = true)
moduleEnableSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
- putBoolean(HookMedium.ENABLE_MODULE, b)
+ modulePrefs.putBoolean(ENABLE_MODULE, b)
moduleEnableLogSwitch.isVisible = b
SystemUITool.showNeedRestartSnake(context = this)
}
moduleEnableLogSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
- putBoolean(HookMedium.ENABLE_MODULE_LOG, b)
+ modulePrefs.putBoolean(ENABLE_MODULE_LOG, b)
SystemUITool.showNeedRestartSnake(context = this)
}
hideIconInLauncherSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
- putBoolean(HookMedium.ENABLE_HIDE_ICON, b)
+ modulePrefs.putBoolean(ENABLE_HIDE_ICON, b)
packageManager.setComponentEnabledSetting(
ComponentName(this@MainActivity, "com.fankes.miui.notify.Home"),
if (b) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
@@ -142,13 +148,13 @@ class MainActivity : BaseActivity() {
}
colorIconHookSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
- putBoolean(HookMedium.ENABLE_COLOR_ICON_HOOK, b)
+ modulePrefs.putBoolean(ENABLE_COLOR_ICON_HOOK, b)
notifyIconConfigItem.isVisible = b
SystemUITool.showNeedRestartSnake(context = this)
}
notifyIconHookSwitch.setOnCheckedChangeListener { btn, b ->
if (!btn.isPressed) return@setOnCheckedChangeListener
- putBoolean(HookMedium.ENABLE_NOTIFY_ICON_HOOK, b)
+ modulePrefs.putBoolean(ENABLE_NOTIFY_ICON_HOOK, b)
SystemUITool.showNeedRestartSnake(context = this)
}
/** 重启按钮点击事件 */
@@ -159,7 +165,7 @@ class MainActivity : BaseActivity() {
}
/** 恰饭! */
findViewById(R.id.link_with_follow_me).setOnClickListener {
- try {
+ runCatching {
startActivity(Intent().apply {
setPackage("com.coolapk.market")
action = "android.intent.action.VIEW"
@@ -167,20 +173,20 @@ class MainActivity : BaseActivity() {
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
- } catch (e: Exception) {
+ }.onFailure {
Toast.makeText(this, "你可能没有安装酷安", Toast.LENGTH_SHORT).show()
}
}
/** 项目地址点击事件 */
findViewById(R.id.link_with_project_address).setOnClickListener {
- try {
+ runCatching {
startActivity(Intent().apply {
action = "android.intent.action.VIEW"
data = Uri.parse("https://github.com/fankes/MIUINativeNotifyIcon")
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
- } catch (e: Exception) {
+ }.onFailure {
Toast.makeText(this, "无法启动系统默认浏览器", Toast.LENGTH_SHORT).show()
}
}
@@ -204,20 +210,5 @@ class MainActivity : BaseActivity() {
* 判断模块是否激活
* @return [Boolean] 激活状态
*/
- private fun isHooked() = HookMedium.isHooked()
-
- /**
- * 获取保存的值
- * @param key 名称
- * @param default 默认值
- * @return [Boolean] 保存的值
- */
- private fun getBoolean(key: String, default: Boolean = false) = HookMedium.getBoolean(key, default)
-
- /**
- * 保存值
- * @param key 名称
- * @param bool 值
- */
- private fun putBoolean(key: String, bool: Boolean) = HookMedium.putBoolean(key, bool)
+ private fun isHooked() = YukiHookModuleStatus.isActive()
}
\ No newline at end of file
diff --git a/app/src/main/java/com/fankes/miui/notify/ui/base/BaseActivity.kt b/app/src/main/java/com/fankes/miui/notify/ui/base/BaseActivity.kt
index 5122084..5e60b4c 100644
--- a/app/src/main/java/com/fankes/miui/notify/ui/base/BaseActivity.kt
+++ b/app/src/main/java/com/fankes/miui/notify/ui/base/BaseActivity.kt
@@ -25,7 +25,6 @@ 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.hook.HookMedium
import com.fankes.miui.notify.utils.isNotSystemInDarkMode
import com.gyf.immersionbar.ktx.immersionBar
@@ -45,24 +44,4 @@ abstract class BaseActivity : AppCompatActivity() {
fitsSystemWindows(true)
}
}
-
- override fun onResume() {
- super.onResume()
- HookMedium.setWorldReadable(this)
- }
-
- override fun onRestart() {
- super.onRestart()
- HookMedium.setWorldReadable(this)
- }
-
- override fun onPause() {
- super.onPause()
- HookMedium.setWorldReadable(this)
- }
-
- override fun onBackPressed() {
- HookMedium.setWorldReadable(this)
- super.onBackPressed()
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/fankes/miui/notify/utils/FileUtils.java b/app/src/main/java/com/fankes/miui/notify/utils/FileUtils.java
deleted file mode 100755
index ac91b7e..0000000
--- a/app/src/main/java/com/fankes/miui/notify/utils/FileUtils.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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 and our eula as published
- * by ferredoxin.
- *
- * 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
- *
- *
- * This file is Created by zpp0196 on 2019/2/9.
- */
-package com.fankes.miui.notify.utils;
-
-import android.content.Context;
-
-import com.fankes.miui.notify.BuildConfig;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-@SuppressWarnings("ALL")
-public class FileUtils {
-
- private static final String FILE_PREF_NAME = BuildConfig.APPLICATION_ID + "_preferences.xml";
-
- public static boolean copyFile(File srcFile, File targetFile) {
- FileInputStream ins = null;
- FileOutputStream out = null;
- try {
- if (targetFile.exists()) {
- targetFile.delete();
- }
- File targetParent = targetFile.getParentFile();
- if (!targetParent.exists()) {
- targetParent.mkdirs();
- }
- targetFile.createNewFile();
- ins = new FileInputStream(srcFile);
- out = new FileOutputStream(targetFile);
- byte[] b = new byte[1024];
- int n;
- while ((n = ins.read(b)) != -1) {
- out.write(b, 0, n);
- }
- } catch (IOException e) {
- e.printStackTrace();
- return false;
- } finally {
- try {
- if (ins != null) {
- ins.close();
- }
- if (out != null) {
- out.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return true;
- }
-
- public static File getDataDir(Context context) {
- return new File(context.getApplicationInfo().dataDir);
- }
-
- public static File getPrefDir(Context context) {
- return new File(getDataDir(context), "shared_prefs");
- }
-
- public static File getDefaultPrefFile(Context context) {
- return new File(getPrefDir(context), FILE_PREF_NAME);
- }
-}
diff --git a/app/src/main/java/com/fankes/miui/notify/utils/Utils.kt b/app/src/main/java/com/fankes/miui/notify/utils/Utils.kt
index 3c8d564..2d4009f 100644
--- a/app/src/main/java/com/fankes/miui/notify/utils/Utils.kt
+++ b/app/src/main/java/com/fankes/miui/notify/utils/Utils.kt
@@ -35,6 +35,12 @@ import android.provider.Settings
import android.service.notification.StatusBarNotification
import android.util.Base64
import com.fankes.miui.notify.application.MNNApplication.Companion.appContext
+import com.highcapable.yukihookapi.hook.factory.classOf
+import com.highcapable.yukihookapi.hook.factory.hasClass
+import com.highcapable.yukihookapi.hook.factory.invokeStatic
+import com.highcapable.yukihookapi.hook.factory.obtainMethod
+import com.highcapable.yukihookapi.hook.log.loggerE
+import com.highcapable.yukihookapi.hook.type.java.StringType
import com.topjohnwu.superuser.Shell
/**
@@ -60,12 +66,7 @@ val Context.isSystemInDarkMode get() = (resources.configuration.uiMode and Confi
* 通知栏是否为 MIUI 样式
* @return [Boolean] 是否符合条件
*/
-val Context.isMiuiNotifyStyle
- get() = try {
- Settings.System.getInt(contentResolver, "status_bar_notification_style") == 0
- } catch (_: Throwable) {
- false
- }
+val Context.isMiuiNotifyStyle get() = safeOfFalse { Settings.System.getInt(contentResolver, "status_bar_notification_style") == 0 }
/**
* 系统深色模式是否没开启
@@ -89,14 +90,7 @@ inline val isLowerAndroidP get() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P
* 当前设备是否是 MIUI 定制 Android 系统
* @return [Boolean] 是否符合条件
*/
-val isMIUI by lazy {
- try {
- Class.forName("android.miui.R")
- true
- } catch (_: Exception) {
- false
- }
-}
+val isMIUI by lazy { ("android.miui.R").hasClass }
/**
* 当前设备是否不是 MIUI 定制 Android 系统
@@ -168,16 +162,13 @@ val Context.packageInfo get() = packageManager?.getPackageInfo(packageName, 0) ?
* @return [Boolean]
*/
val String.isInstall
- get() =
- try {
- appContext.packageManager.getPackageInfo(
- this,
- PackageManager.GET_UNINSTALLED_PACKAGES
- )
- true
- } catch (e: Exception) {
- false
- }
+ get() = safeOfFalse {
+ appContext.packageManager.getPackageInfo(
+ this,
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ )
+ true
+ }
/**
* 得到版本信息
@@ -253,26 +244,116 @@ fun Bitmap.round(radius: Float): Bitmap =
* @param default 默认值
* @return [String]
*/
-fun findPropString(key: String, default: String = "") =
- try {
- (Class.forName("android.os.SystemProperties").getDeclaredMethod(
- "get",
- String::class.java,
- String::class.java
- ).apply { isAccessible = true }.invoke(null, key, default)) as? String? ?: default
- } catch (_: Exception) {
- default
- }
+fun findPropString(key: String, default: String = "") = safeOf(default) {
+ (classOf(name = "android.os.SystemProperties").obtainMethod(
+ name = "get", StringType, StringType
+ )?.invokeStatic(key, default)) ?: default
+}
/**
* 执行命令 - su
* @param cmd 命令
* @return [String] 执行结果
*/
-fun execShellSu(cmd: String) = try {
+fun execShellSu(cmd: String) = safeOfNothing {
Shell.su(cmd).exec().out.let {
if (it.isNotEmpty()) it[0].trim() else ""
}
-} catch (_: Throwable) {
- ""
+}
+
+/**
+ * 忽略异常返回值
+ * @param it 回调 - 如果异常为空
+ * @return [T] 发生异常时返回设定值否则返回正常值
+ */
+inline fun 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 safeOf(default: T, it: () -> T): T {
+ return try {
+ it()
+ } catch (t: NullPointerException) {
+ default
+ } catch (t: UnsatisfiedLinkError) {
+ default
+ } catch (t: UnsupportedOperationException) {
+ default
+ } catch (t: ClassNotFoundException) {
+ default
+ } catch (t: IllegalStateException) {
+ default
+ } catch (t: NoSuchMethodError) {
+ default
+ } catch (t: NoSuchFieldError) {
+ default
+ } catch (t: Error) {
+ default
+ } catch (t: Exception) {
+ default
+ } catch (t: Throwable) {
+ default
+ }
+}
+
+/**
+ * 忽略异常运行
+ * @param msg 出错输出的消息 - 默认为空
+ * @param it 正常回调
+ */
+inline fun safeRun(msg: String = "", it: () -> Unit) {
+ try {
+ it()
+ } catch (e: NullPointerException) {
+ if (msg.isNotBlank()) loggerE(msg = msg, e = e)
+ } catch (e: UnsatisfiedLinkError) {
+ if (msg.isNotBlank()) loggerE(msg = msg, e = e)
+ } catch (e: UnsupportedOperationException) {
+ if (msg.isNotBlank()) loggerE(msg = msg, e = e)
+ } catch (e: ClassNotFoundException) {
+ if (msg.isNotBlank()) loggerE(msg = msg, e = e)
+ } catch (e: IllegalStateException) {
+ if (msg.isNotBlank()) loggerE(msg = msg, e = e)
+ } catch (e: NoSuchMethodError) {
+ if (msg.isNotBlank()) loggerE(msg = msg, e = e)
+ } catch (e: NoSuchFieldError) {
+ if (msg.isNotBlank()) loggerE(msg = msg, e = e)
+ } catch (e: Error) {
+ if (msg.isNotBlank()) loggerE(msg = msg, e = e)
+ } catch (e: Exception) {
+ if (msg.isNotBlank()) loggerE(msg = msg, e = e)
+ } catch (e: Throwable) {
+ }
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index a40650f..f3555fc 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -489,6 +489,20 @@
android:textColor="@color/colorTextGray"
android:textSize="16sp" />
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 185268c..888932e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,30 +1,12 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-buildscript {
- ext.kotlin_version = "1.6.10"
- repositories {
- google()
- maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
- maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
- maven { url "https://www.jitpack.io" }
- mavenCentral()
- }
- dependencies {
- classpath "com.android.tools.build:gradle:7.0.4"
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
+plugins {
+ id 'com.android.application' version '7.1.1' apply false
+ id 'com.android.library' version '7.1.1' apply false
+ id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}
-allprojects {
- repositories {
- google()
- maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
- maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
- maven { url "https://www.jitpack.io" }
- mavenCentral()
- }
+ext {
+ appVersionName = "1.36"
+ appVersionCode = 8
}
task clean(type: Delete) {
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index dbe6c02..c44e0d1 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Jan 24 04:31:03 CST 2022
+#Tue Feb 15 02:09:05 CST 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/settings.gradle b/settings.gradle
index 5ebc41d..ac7af30 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,18 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ maven { url "https://api.xposed.info/" }
+ maven { url 'https://www.jitpack.io' }
+ mavenCentral()
+ }
+}
rootProject.name = "MIUINativeNotifyIcon"
include ':app'