├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── xposed_init │ ├── java │ └── cn │ │ └── houkyo │ │ └── wini │ │ ├── hooks │ │ ├── FrameworkHooks.kt │ │ ├── MainHook.kt │ │ ├── OtherHooks.kt │ │ └── blur │ │ │ ├── MiuiHome.kt │ │ │ ├── PersonalAssistant.kt │ │ │ ├── SecurityCenter.kt │ │ │ └── SystemUI.kt │ │ ├── models │ │ ├── BaseBlurBackgroundModel.kt │ │ ├── ConfigModel.kt │ │ ├── MiuiHomeModel.kt │ │ ├── NotificationBlurModel.kt │ │ ├── PersonalAssistantModel.kt │ │ ├── QuickSettingModel.kt │ │ ├── SecurityCenterModel.kt │ │ └── SystemUIModel.kt │ │ └── utils │ │ ├── ColorUtils.kt │ │ ├── HookUtils.kt │ │ └── Storage.kt │ └── res │ ├── drawable-hdpi │ └── ic_stat_name.png │ ├── drawable-mdpi │ └── ic_stat_name.png │ ├── drawable-xhdpi │ └── ic_stat_name.png │ ├── drawable-xxhdpi │ └── ic_stat_name.png │ ├── drawable-xxxhdpi │ └── ic_stat_name.png │ ├── drawable │ └── ic_launcher_background.xml │ ├── mipmap-anydpi-v26 │ └── ic_launcher.xml │ ├── mipmap-hdpi │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── values │ ├── arrays.xml │ └── strings.xml │ └── xml │ └── data_extraction_rules.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.gradle 3 | /build 4 | local.properties -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | MIT License (MIT) 3 | 4 | Copyright © 2022 Houkyo 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 7 | associated documentation files (the “Software”), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all copies or substantial 13 | portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 16 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 19 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## WINI 2 | 一个Xposed模块,为基于**Android 12**的MIUI13增加一些模糊效果。 3 | 4 | ### 注意 5 | 此开源版本与酷安发布的版本不一致,不包含参数调节界面,开发者需要自己开发调节界面。 6 | 7 | 当前模块配置JSON示例: 8 | ```json 9 | { 10 | "miuiHome": { 11 | "enableShortcutBackgroundBlur": true, 12 | "shortcutMenuBackgroundAlpha": 255 13 | }, 14 | "personalAssistant": { 15 | "background": { 16 | "backgroundColor": "#1E000000", 17 | "blurRadius": 80, 18 | "enable": true 19 | } 20 | }, 21 | "securityCenter": { 22 | "dockBackground": { 23 | "backgroundColor": "#3CFFFFFF", 24 | "blurRadius": 60, 25 | "enable": true 26 | } 27 | }, 28 | "systemUI": { 29 | "notification": { 30 | "blurBackgroundAlpha": 170, 31 | "blurRadius": 60, 32 | "cornerRadius": 48, 33 | "defaultBackgroundAlpha": 200, 34 | "enable": true 35 | }, 36 | "quickSetting": { 37 | "controlDetailBackgroundAlpha": 200, 38 | "hideMiPlayEntry": true 39 | } 40 | }, 41 | "versionCode": 0 42 | } 43 | ``` 44 | 45 | ### License 46 | 本项目基于[MIT](https://github.com/ouhoukyo/WINI/blob/main/LICENSE)协议开源 -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdk 31 8 | 9 | defaultConfig { 10 | applicationId "cn.houkyo.wini" 11 | minSdk 31 12 | //noinspection OldTargetApi 13 | targetSdk 31 14 | versionCode 11600 15 | versionName "1.1.6(11600)" 16 | ndk { 17 | abiFilters 'arm64-v8a' 18 | } 19 | } 20 | 21 | buildTypes { 22 | debug {} 23 | release { 24 | minifyEnabled false 25 | zipAlignEnabled false 26 | } 27 | } 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_11 30 | targetCompatibility JavaVersion.VERSION_11 31 | } 32 | kotlinOptions { 33 | jvmTarget = '1.8' 34 | } 35 | namespace 'cn.houkyo.wini' 36 | } 37 | 38 | dependencies { 39 | implementation 'androidx.core:core-ktx:1.7.0' 40 | // Xposed 41 | compileOnly 'de.robv.android.xposed:api:82' 42 | // Gson 43 | implementation 'com.google.code.gson:gson:2.10' 44 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 17 | 20 | 23 | 26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | cn.houkyo.wini.hooks.FrameworkHooks 2 | cn.houkyo.wini.hooks.MainHook -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/hooks/FrameworkHooks.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.hooks 2 | 3 | import android.graphics.Canvas 4 | import cn.houkyo.wini.utils.HookUtils 5 | import de.robv.android.xposed.IXposedHookZygoteInit 6 | import de.robv.android.xposed.XC_MethodHook 7 | import de.robv.android.xposed.XposedBridge 8 | 9 | class FrameworkHooks :IXposedHookZygoteInit { 10 | override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) { 11 | val classLoader = startupParam.javaClass.classLoader 12 | val BackgroundBlurDrawableClass = classLoader?.let { 13 | HookUtils.getClass( 14 | "com.android.internal.graphics.drawable.BackgroundBlurDrawable", 15 | it 16 | ) 17 | } ?: return 18 | // 为 BackgroundBlurDrawable 应当增加一个判断 19 | // 此处应该可以为AOSP提交修复补丁 20 | XposedBridge.hookAllMethods( 21 | BackgroundBlurDrawableClass, 22 | "draw", 23 | object : XC_MethodHook() { 24 | override fun beforeHookedMethod(param: MethodHookParam) { 25 | val canvas = param.args[0] as Canvas 26 | if(!canvas.isHardwareAccelerated){ 27 | HookUtils.log("BackgroundBlurDrawable canvas is not HardwareAccelerated.") 28 | param.result = null 29 | } 30 | } 31 | }) 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/hooks/MainHook.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.hooks 2 | 3 | import cn.houkyo.wini.BuildConfig 4 | import cn.houkyo.wini.hooks.blur.* 5 | import cn.houkyo.wini.models.ConfigModel 6 | import cn.houkyo.wini.utils.HookUtils 7 | import cn.houkyo.wini.utils.Storage 8 | import de.robv.android.xposed.* 9 | import de.robv.android.xposed.callbacks.XC_LoadPackage 10 | 11 | class MainHook : IXposedHookLoadPackage { 12 | 13 | override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { 14 | val otherHooks = OtherHooks(lpparam.classLoader) 15 | when (lpparam.packageName) { 16 | // 系统桌面 17 | "com.miui.home" -> { 18 | val config = getConfig() 19 | val miuiHomeHooks = MiuiHome(lpparam.classLoader, config) 20 | if (config.miuiHome.enableShortcutBackgroundBlur) { 21 | miuiHomeHooks.addBlurEffectToShortcutLayer() 22 | } 23 | 24 | // 以下针对系统桌面模糊的功能部分与 MiuiHome 模块重合,且BUG未修复,故酷安公开版本未上线 25 | // 部分功能与 MiuiHome 重合,代码没有参考MiuiHome,不需要遵守其开源协议 26 | otherHooks.deviceLevelHook() 27 | miuiHomeHooks.addBlurEffectToFolderIcon() 28 | miuiHomeHooks.addBlurEffectToAlphaIcon() 29 | miuiHomeHooks.hideBlurIconWhenEnterRecents() 30 | 31 | } 32 | // 系统界面 33 | "com.android.systemui" -> { 34 | val config = getConfig() 35 | val systemUIHooks = SystemUI(lpparam.classLoader, config) 36 | if (config.systemUI.notification.enable) { 37 | systemUIHooks.addBlurEffectToNotificationView() 38 | } 39 | if (config.systemUI.quickSetting.hideMiPlayEntry) { 40 | systemUIHooks.hideControlsPlugin() 41 | } 42 | if (config.systemUI.quickSetting.controlDetailBackgroundAlpha != 255) { 43 | systemUIHooks.setQSControlDetailBackgroundAlpha() 44 | } 45 | systemUIHooks.enableBlurForMTK() 46 | systemUIHooks.addBlurEffectToLockScreen() 47 | } 48 | // 个人助理 负一屏 49 | "com.miui.personalassistant" -> { 50 | val config = getConfig() 51 | if (config.personalAssistant.background.enable) { 52 | val personalAssistantHooks = PersonalAssistant(lpparam.classLoader, config) 53 | personalAssistantHooks.addBlurEffectToPersonalAssistant() 54 | } 55 | } 56 | // 安全中心 57 | "com.miui.securitycenter" -> { 58 | val config = getConfig() 59 | if (config.securityCenter.dockBackground.enable) { 60 | val securityCenterHooks = SecurityCenter(lpparam.classLoader, config) 61 | securityCenterHooks.addBlurEffectToDock() 62 | } 63 | } 64 | BuildConfig.APPLICATION_ID -> { 65 | getConfig(true) 66 | otherHooks.enableModule() 67 | } 68 | else -> { 69 | return 70 | } 71 | } 72 | 73 | } 74 | 75 | private fun getConfig(showLog: Boolean = false): ConfigModel { 76 | val xSharedPreferences = 77 | XSharedPreferences(BuildConfig.APPLICATION_ID, Storage.DATA_FILENAME) 78 | xSharedPreferences.makeWorldReadable() 79 | val configJsonString = xSharedPreferences.getString(Storage.CONFIG_JSON, "") 80 | if (configJsonString != null && configJsonString != "") { 81 | if (showLog) { 82 | HookUtils.log(configJsonString) 83 | } 84 | return Storage.getConfig(configJsonString) 85 | } 86 | return ConfigModel() 87 | } 88 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/hooks/OtherHooks.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.hooks; 2 | 3 | import cn.houkyo.wini.utils.HookUtils; 4 | 5 | class OtherHooks(private val classLoader: ClassLoader) { 6 | fun enableModule() { 7 | HookUtils.replaceMethodResult( 8 | "cn.houkyo.wini.utils.JavascriptBridge", 9 | classLoader, 10 | "getIsModuleActive", 11 | true 12 | ) 13 | } 14 | 15 | fun deviceLevelHook() { 16 | val DeviceLevelUtilsClassName = 17 | "com.miui.home.launcher.common.DeviceLevelUtils" 18 | val CpuLevelUtilsClassName = 19 | "com.miui.home.launcher.common.CpuLevelUtils" 20 | val UtilitiesClassName = "com.miui.home.launcher.common.Utilities" 21 | val BlurUtilsClassName = "com.miui.home.launcher.common.BlurUtils" 22 | // 高斯模糊类型 23 | HookUtils.replaceMethodResult( 24 | BlurUtilsClassName, 25 | classLoader, 26 | "getBlurType", 27 | 2 28 | ) 29 | // 打开文件夹是否开启模糊 30 | HookUtils.replaceMethodResult( 31 | BlurUtilsClassName, 32 | classLoader, 33 | "isUserBlurWhenOpenFolder", 34 | true 35 | ) 36 | // 设备等级 37 | HookUtils.replaceMethodResult( 38 | DeviceLevelUtilsClassName, 39 | classLoader, 40 | "getDeviceLevel", 41 | 2 42 | ) 43 | HookUtils.replaceMethodResult( 44 | DeviceLevelUtilsClassName, 45 | classLoader, 46 | "isUseSimpleAnim", 47 | false 48 | ) 49 | HookUtils.replaceMethodResult( 50 | CpuLevelUtilsClassName, 51 | classLoader, 52 | "getQualcommCpuLevel", 53 | 2, 54 | String::class.java 55 | ) 56 | // 下载特效 57 | HookUtils.replaceMethodResult( 58 | CpuLevelUtilsClassName, 59 | classLoader, 60 | "needMamlDownload", 61 | true 62 | ) 63 | // 平滑动画 64 | HookUtils.replaceMethodResult( 65 | UtilitiesClassName, 66 | classLoader, 67 | "isUseSmoothAnimationEffect", 68 | true 69 | ) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/hooks/blur/MiuiHome.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.hooks.blur 2 | 3 | import android.animation.Animator 4 | import android.animation.AnimatorListenerAdapter 5 | import android.animation.ValueAnimator 6 | import android.graphics.* 7 | import android.graphics.drawable.BitmapDrawable 8 | import android.graphics.drawable.Drawable 9 | import android.graphics.drawable.GradientDrawable 10 | import android.graphics.drawable.LayerDrawable 11 | import android.graphics.drawable.ShapeDrawable 12 | import android.view.View 13 | import android.view.ViewGroup 14 | import android.widget.FrameLayout 15 | import android.widget.ImageView 16 | import android.widget.TextView 17 | import androidx.core.view.ViewCompat.animate 18 | import cn.houkyo.wini.models.ConfigModel 19 | import cn.houkyo.wini.utils.HookUtils 20 | import de.robv.android.xposed.XC_MethodHook 21 | import de.robv.android.xposed.XposedBridge 22 | import de.robv.android.xposed.XposedHelpers 23 | import kotlin.collections.ArrayList 24 | import kotlin.math.sqrt 25 | 26 | 27 | class MiuiHome(private val classLoader: ClassLoader, config: ConfigModel) { 28 | 29 | val shortcutMenuBackgroundAlpha = config.miuiHome.shortcutMenuBackgroundAlpha 30 | 31 | /* 32 | 两层视图alpha计算公式:2x-x^2=y 33 | x为单层视图alpha 0完全透明 1完全不透明 34 | y为双层混合后的透明度 35 | x与y在图层透明度这种情况下永远为正值 36 | 将改公式转换为x=f(y):x=1-√(1-y) 37 | */ 38 | val singleLayerAlpha = 39 | ((1.0 - sqrt(1.0 - (shortcutMenuBackgroundAlpha / 255.0))) * 255.0).toInt() 40 | 41 | val BLUR_ICON_APP_NAME = arrayOf("锁屏", "手电筒", "数据", "飞行模式", "蓝牙", "WLAN 热点") 42 | val allBluredDrawable: MutableList = ArrayList() 43 | 44 | fun addBlurEffectToFolderIcon() { 45 | val FolderIconClass = HookUtils.getClass( 46 | "com.miui.home.launcher.FolderIcon", 47 | classLoader 48 | ) ?: return 49 | val ThumbnailContainerClass = HookUtils.getClass( 50 | "com.miui.home.launcher.ThumbnailContainer", 51 | classLoader 52 | ) ?: return 53 | val DragViewClass = HookUtils.getClass( 54 | "com.miui.home.launcher.DragView", 55 | classLoader 56 | ) ?: return 57 | val ItemIconClass = FolderIconClass.superclass 58 | val FolderClass = HookUtils.getClass( 59 | "com.miui.home.launcher.Folder", 60 | classLoader 61 | ) ?: return 62 | val ItemInfoClass = HookUtils.getClass( 63 | "com.miui.home.launcher.ItemInfo", 64 | classLoader 65 | ) ?: return 66 | 67 | // 禁止修改图标 68 | XposedBridge.hookAllMethods( 69 | FolderIconClass, 70 | "onWallpaperColorChanged", 71 | object : XC_MethodHook() { 72 | override fun beforeHookedMethod(param: MethodHookParam) { 73 | param.result = null; 74 | } 75 | }) 76 | 77 | // 禁止使用软件加速 setLayerType 78 | XposedBridge.hookAllMethods( 79 | ThumbnailContainerClass, 80 | "onFinishInflate", 81 | object : XC_MethodHook() { 82 | override fun beforeHookedMethod(param: MethodHookParam) { 83 | param.result = null; 84 | } 85 | }) 86 | 87 | XposedBridge.hookAllMethods( 88 | FolderIconClass, 89 | "onFinishInflate", 90 | object : XC_MethodHook() { 91 | override fun afterHookedMethod(param: MethodHookParam) { 92 | 93 | val mIconImageView = HookUtils.getValueByField( 94 | param.thisObject, 95 | "mIconImageView", 96 | ItemIconClass 97 | ) as ImageView 98 | 99 | val mFolderBackgroundField = 100 | FolderIconClass.getDeclaredField("mFolderBackground") 101 | mFolderBackgroundField.isAccessible = true 102 | 103 | mIconImageView.addOnAttachStateChangeListener(object : 104 | View.OnAttachStateChangeListener { 105 | 106 | override fun onViewAttachedToWindow(view: View) { 107 | val blurDrawable = 108 | HookUtils.createBlurDrawable(mIconImageView, 40, 38) ?: return 109 | allBluredDrawable.add(blurDrawable) 110 | mFolderBackgroundField.set(param.thisObject, blurDrawable) 111 | val backgroundDrawable = 112 | LayerDrawable(arrayOf(blurDrawable)) 113 | val paddingValue = 114 | HookUtils.dip2px(mIconImageView.context, 2.5f).toInt() 115 | backgroundDrawable.setLayerInsetTop(0, paddingValue) 116 | backgroundDrawable.setLayerInsetRight(0, paddingValue) 117 | backgroundDrawable.setLayerInsetBottom(0, paddingValue) 118 | backgroundDrawable.setLayerInsetLeft(0, paddingValue) 119 | mIconImageView.background = backgroundDrawable 120 | if (mIconImageView.drawable == null) { 121 | XposedHelpers.callMethod( 122 | blurDrawable, 123 | "setColor", 124 | Color.argb(60, 255, 255, 255) 125 | ) 126 | } 127 | } 128 | 129 | override fun onViewDetachedFromWindow(view: View) {} 130 | }) 131 | 132 | XposedBridge.hookAllMethods( 133 | FolderClass, 134 | "tellItemIconIsOnAnimation", 135 | object : XC_MethodHook() { 136 | override fun beforeHookedMethod(param: MethodHookParam) { 137 | val isStart = param.args[0] as Boolean 138 | val mClosing = 139 | HookUtils.getValueByField( 140 | param.thisObject, 141 | "mClosing" 142 | ) as Boolean 143 | if (isStart) { 144 | if (mClosing) { 145 | showBlurDrawable() 146 | } else { 147 | hideBlurDrawable() 148 | } 149 | } 150 | } 151 | }) 152 | } 153 | } 154 | ) 155 | 156 | // 禁止使用软件加速 setLayerType 157 | XposedBridge.hookAllMethods( 158 | DragViewClass, 159 | "shouldShowDeleteHint", 160 | object : XC_MethodHook() { 161 | override fun beforeHookedMethod(param: MethodHookParam) { 162 | param.result = true 163 | } 164 | }) 165 | 166 | // 为拖动时的图标添加背景 167 | XposedBridge.hookAllMethods( 168 | DragViewClass, 169 | "showWithAnim", 170 | object : XC_MethodHook() { 171 | override fun afterHookedMethod(param: MethodHookParam) { 172 | val dragView = param.thisObject as View 173 | dragView.setLayerType(View.LAYER_TYPE_HARDWARE, null) 174 | val mDragInfo = 175 | HookUtils.getValueByField(param.thisObject, "mDragInfo") ?: return 176 | 177 | // 只有itemType为2是文件 178 | val itemType = 179 | HookUtils.getValueByField(mDragInfo, "itemType", ItemInfoClass) as Int 180 | val mTitle = 181 | HookUtils.getValueByField(mDragInfo, "mTitle", ItemInfoClass) as String 182 | 183 | val mLauncher = HookUtils.getValueByField(param.thisObject, "mLauncher") 184 | 185 | val isFolderShowing = 186 | XposedHelpers.callMethod(mLauncher, "isFolderShowing") as Boolean 187 | 188 | val mDragSource = XposedHelpers.callMethod(param.thisObject, "getDragSource") 189 | val isFromHotSeats = mDragSource.javaClass.name.contains("HotSeats") 190 | 191 | if (!isFolderShowing && (itemType == 2 || BLUR_ICON_APP_NAME.contains(mTitle))) { 192 | val blurDrawable = HookUtils.createBlurDrawable(dragView, 38, 40) ?: return 193 | allBluredDrawable.add(blurDrawable) 194 | val backgroundDrawable = 195 | LayerDrawable(arrayOf(blurDrawable)) 196 | backgroundDrawable.setLayerInsetTop( 197 | 0, 198 | HookUtils.dip2px(dragView.context, 8f).toInt() 199 | ) 200 | backgroundDrawable.setLayerInsetRight( 201 | 0, 202 | HookUtils.dip2px(dragView.context, 18f).toInt() 203 | ) 204 | if (isFromHotSeats) { 205 | // 从底部拖动出来的时候没有文字,故缩小该区域 206 | backgroundDrawable.setLayerInsetBottom( 207 | 0, 208 | HookUtils.dip2px(dragView.context, 24f).toInt() 209 | ) 210 | XposedHelpers.callMethod( 211 | blurDrawable, 212 | "setColor", 213 | Color.argb(60, 255, 255, 255) 214 | ) 215 | } else { 216 | backgroundDrawable.setLayerInsetBottom( 217 | 0, 218 | HookUtils.dip2px(dragView.context, 48f).toInt() 219 | ) 220 | } 221 | backgroundDrawable.setLayerInsetLeft( 222 | 0, 223 | HookUtils.dip2px(dragView.context, 18f).toInt() 224 | ) 225 | dragView.background = backgroundDrawable 226 | } 227 | } 228 | }) 229 | } 230 | 231 | // 与文件夹类似 232 | fun addBlurEffectToAlphaIcon() { 233 | val ShortcutIconClass = HookUtils.getClass( 234 | "com.miui.home.launcher.ShortcutIcon", 235 | classLoader 236 | ) ?: return 237 | val ItemIconClass = HookUtils.getClass( 238 | "com.miui.home.launcher.ItemIcon", 239 | classLoader 240 | ) ?: return 241 | 242 | XposedBridge.hookAllMethods(ShortcutIconClass, "drawOutLine", object : XC_MethodHook() { 243 | override fun beforeHookedMethod(param: MethodHookParam) { 244 | param.result = null 245 | } 246 | }) 247 | 248 | XposedBridge.hookAllMethods( 249 | ItemIconClass, 250 | "setTitle", 251 | object : XC_MethodHook() { 252 | override fun afterHookedMethod(param: MethodHookParam) { 253 | val mTitle = 254 | HookUtils.getValueByField( 255 | param.thisObject, 256 | "mTitle", 257 | ItemIconClass 258 | ) as TextView 259 | val mTitleText = mTitle.text 260 | val mIconImageView = 261 | HookUtils.getValueByField( 262 | param.thisObject, 263 | "mIconImageView", 264 | ItemIconClass 265 | ) as ImageView 266 | 267 | if (BLUR_ICON_APP_NAME.contains(mTitleText) && mIconImageView.background == null && mIconImageView.isAttachedToWindow) { 268 | val blurDrawable = 269 | HookUtils.createBlurDrawable(mIconImageView, 38, 40) ?: return 270 | allBluredDrawable.add(blurDrawable) 271 | val backgroundDrawable = 272 | LayerDrawable(arrayOf(blurDrawable)) 273 | val paddingValue = HookUtils.dip2px(mIconImageView.context, 2.5f).toInt() 274 | backgroundDrawable.setLayerInsetTop(0, paddingValue) 275 | backgroundDrawable.setLayerInsetRight(0, paddingValue) 276 | backgroundDrawable.setLayerInsetBottom(0, paddingValue) 277 | backgroundDrawable.setLayerInsetLeft(0, paddingValue) 278 | mIconImageView.background = backgroundDrawable 279 | } 280 | } 281 | }) 282 | } 283 | 284 | fun addBlurEffectToShortcutLayer() { 285 | val ShortcutMenuLayerClass = HookUtils.getClass( 286 | "com.miui.home.launcher.ShortcutMenuLayer", 287 | classLoader 288 | ) ?: return 289 | val ShortcutMenuClass = HookUtils.getClass( 290 | "com.miui.home.launcher.shortcuts.ShortcutMenu", 291 | classLoader 292 | ) ?: return 293 | val BlurUtilsClass = HookUtils.getClass( 294 | "com.miui.home.launcher.common.BlurUtils", 295 | classLoader 296 | ) ?: return 297 | val ApplicationClass = HookUtils.getClass( 298 | "com.miui.home.launcher.Application", 299 | classLoader 300 | ) ?: return 301 | val UtilitiesClass = HookUtils.getClass( 302 | "com.miui.home.launcher.common.Utilities", 303 | classLoader 304 | ) ?: return 305 | val DragViewClass = HookUtils.getClass( 306 | "com.miui.home.launcher.DragView", 307 | classLoader 308 | ) ?: return 309 | 310 | var isShortcutMenuLayerBlurred = false 311 | var targetView: ViewGroup? = null 312 | var dragView: View? = null 313 | var blurBackground = true 314 | 315 | XposedBridge.hookAllMethods( 316 | ShortcutMenuLayerClass, 317 | "showShortcutMenu", 318 | object : XC_MethodHook() { 319 | override fun beforeHookedMethod(param: MethodHookParam) { 320 | val dragObject = param.args[0] 321 | val dragViewInfo = XposedHelpers.callMethod(dragObject, "getDragInfo") 322 | val iconIsInFolder = 323 | XposedHelpers.callMethod(dragViewInfo, "isInFolder") as Boolean 324 | if (iconIsInFolder) { 325 | try { 326 | val isUserBlurWhenOpenFolder = XposedHelpers.callStaticMethod( 327 | BlurUtilsClass, 328 | "isUserBlurWhenOpenFolder" 329 | ) as Boolean 330 | blurBackground = !isUserBlurWhenOpenFolder 331 | } catch (e: Throwable) { 332 | // Do Nothing. 333 | } 334 | } else { 335 | blurBackground = true 336 | } 337 | val mLauncher = XposedHelpers.callStaticMethod(ApplicationClass, "getLauncher") 338 | val systemUiController = 339 | XposedHelpers.callMethod(mLauncher, "getSystemUiController") 340 | val mWindow = HookUtils.getValueByField(systemUiController, "mWindow") 341 | val targetBlurView = XposedHelpers.callMethod(mLauncher, "getScreen") as View 342 | 343 | val renderEffectArray = arrayOfNulls(51) 344 | for (index in 0..50) { 345 | renderEffectArray[index] = RenderEffect.createBlurEffect( 346 | (index + 1).toFloat(), 347 | (index + 1).toFloat(), 348 | Shader.TileMode.MIRROR 349 | ) 350 | } 351 | 352 | val valueAnimator = ValueAnimator.ofInt(0, 50) 353 | valueAnimator.addUpdateListener { animator -> 354 | val value = animator.animatedValue as Int 355 | targetBlurView.setRenderEffect(renderEffectArray[value]) 356 | if (blurBackground) { 357 | XposedHelpers.callStaticMethod( 358 | BlurUtilsClass, 359 | "fastBlurDirectly", 360 | value / 50f, 361 | mWindow 362 | ) 363 | } 364 | } 365 | dragView = 366 | XposedHelpers.callMethod(dragObject, "getDragView") as View 367 | targetView = XposedHelpers.callMethod(dragView, "getContent") as ViewGroup 368 | valueAnimator.duration = 200 369 | valueAnimator.start() 370 | hideBlurDrawable() 371 | isShortcutMenuLayerBlurred = true 372 | } 373 | }) 374 | 375 | XposedBridge.hookAllMethods( 376 | ShortcutMenuLayerClass, 377 | "onDragStart", 378 | object : XC_MethodHook() { 379 | override fun beforeHookedMethod(param: MethodHookParam) { 380 | if (isShortcutMenuLayerBlurred) { 381 | if (targetView != null) { 382 | targetView!!.transitionAlpha = 0f 383 | } 384 | } 385 | } 386 | }) 387 | 388 | XposedBridge.hookAllMethods( 389 | ShortcutMenuLayerClass, 390 | "onDragEnd", 391 | object : XC_MethodHook() { 392 | override fun beforeHookedMethod(param: MethodHookParam) { 393 | if (isShortcutMenuLayerBlurred) { 394 | val isLocked = XposedHelpers.callStaticMethod( 395 | UtilitiesClass, 396 | "isScreenCellsLocked" 397 | ) as Boolean 398 | if (isLocked && dragView != null) { 399 | animate(dragView!!).scaleX(1f).scaleY(1f).setDuration(200).start() 400 | } 401 | } 402 | } 403 | }) 404 | 405 | XposedBridge.hookAllMethods(DragViewClass, "remove", object : XC_MethodHook() { 406 | override fun beforeHookedMethod(param: MethodHookParam) { 407 | if (isShortcutMenuLayerBlurred) { 408 | param.result = null 409 | } 410 | } 411 | }) 412 | 413 | XposedBridge.hookAllMethods( 414 | ShortcutMenuClass, 415 | "reset", 416 | object : XC_MethodHook() { 417 | override fun beforeHookedMethod(param: MethodHookParam) { 418 | if (isShortcutMenuLayerBlurred) { 419 | isShortcutMenuLayerBlurred = false 420 | if (targetView != null) { 421 | targetView!!.transitionAlpha = 1f 422 | } 423 | val mLauncher = 424 | XposedHelpers.callStaticMethod(ApplicationClass, "getLauncher") 425 | val systemUiController = 426 | XposedHelpers.callMethod(mLauncher, "getSystemUiController") 427 | val mWindow = HookUtils.getValueByField(systemUiController, "mWindow") 428 | XposedHelpers.callStaticMethod( 429 | BlurUtilsClass, 430 | "fastBlurDirectly", 431 | 0f, 432 | mWindow 433 | ) 434 | } 435 | } 436 | }) 437 | 438 | XposedBridge.hookAllMethods( 439 | ShortcutMenuLayerClass, 440 | "hideShortcutMenu", 441 | object : XC_MethodHook() { 442 | override fun beforeHookedMethod(param: MethodHookParam) { 443 | if (isShortcutMenuLayerBlurred) { 444 | val editStateChangeReason = param.args[0] 445 | val shortcutMenuLayer = param.thisObject as FrameLayout 446 | val mLauncher = 447 | XposedHelpers.callStaticMethod(ApplicationClass, "getLauncher") 448 | val systemUiController = 449 | XposedHelpers.callMethod(mLauncher, "getSystemUiController") 450 | val mWindow = HookUtils.getValueByField(systemUiController, "mWindow") 451 | 452 | val targetBlurView = 453 | XposedHelpers.callMethod(mLauncher, "getScreen") as View 454 | 455 | val valueAnimator = ValueAnimator.ofInt(50, 0) 456 | val renderEffectArray = arrayOfNulls(51) 457 | for (index in 0..50) { 458 | renderEffectArray[index] = RenderEffect.createBlurEffect( 459 | (index + 1).toFloat(), 460 | (index + 1).toFloat(), 461 | Shader.TileMode.MIRROR 462 | ) 463 | } 464 | valueAnimator.addUpdateListener { animator -> 465 | val value = animator.animatedValue as Int 466 | targetBlurView.setRenderEffect(renderEffectArray[value]) 467 | if (blurBackground) { 468 | XposedHelpers.callStaticMethod( 469 | BlurUtilsClass, 470 | "fastBlurDirectly", 471 | value / 50f, 472 | mWindow 473 | ) 474 | } 475 | } 476 | valueAnimator.addListener(object : AnimatorListenerAdapter() { 477 | override fun onAnimationEnd(animation: Animator?) { 478 | shortcutMenuLayer.background = null 479 | showBlurDrawable() 480 | targetView!!.transitionAlpha = 1f 481 | targetBlurView.setRenderEffect(null) 482 | isShortcutMenuLayerBlurred = false 483 | if (editStateChangeReason != null && editStateChangeReason.toString() != "drag_over_threshold") { 484 | XposedHelpers.callMethod(dragView, "remove") 485 | } 486 | } 487 | }) 488 | valueAnimator.duration = 200 489 | valueAnimator.start() 490 | 491 | if (editStateChangeReason != null) { 492 | HookUtils.log(editStateChangeReason) 493 | } else { 494 | isShortcutMenuLayerBlurred = false 495 | XposedHelpers.callMethod(dragView, "remove") 496 | } 497 | } 498 | } 499 | }) 500 | 501 | XposedBridge.hookAllMethods( 502 | BlurUtilsClass, 503 | "fastBlurDirectly", 504 | object : XC_MethodHook() { 505 | override fun beforeHookedMethod(param: MethodHookParam) { 506 | val blurRatio = param.args[0] as Float 507 | if (isShortcutMenuLayerBlurred && blurRatio == 0.0f) { 508 | param.result = null 509 | } 510 | } 511 | }) 512 | 513 | 514 | if (shortcutMenuBackgroundAlpha != 255) { 515 | XposedBridge.hookAllMethods( 516 | ShortcutMenuClass, 517 | "setMenuBg", 518 | object : XC_MethodHook() { 519 | override fun afterHookedMethod(param: MethodHookParam) { 520 | if (!isShortcutMenuLayerBlurred) { 521 | return 522 | } 523 | 524 | val mAppShortcutMenu = HookUtils.getValueByField( 525 | param.thisObject, 526 | "mAppShortcutMenu" 527 | ) as ViewGroup 528 | val mAppShortcutMenuBackground = 529 | mAppShortcutMenu.background as GradientDrawable 530 | mAppShortcutMenuBackground.alpha = singleLayerAlpha 531 | val mSystemShortcutMenu = HookUtils.getValueByField( 532 | param.thisObject, 533 | "mSystemShortcutMenu" 534 | ) as ViewGroup 535 | val mSystemShortcutMenuBackground = 536 | mSystemShortcutMenu.background as GradientDrawable 537 | mSystemShortcutMenuBackground.alpha = singleLayerAlpha 538 | 539 | val mWidgetShortcutMenu = HookUtils.getValueByField( 540 | param.thisObject, 541 | "mWidgetShortcutMenu" 542 | ) as ViewGroup 543 | val mWidgetShortcutMenuBackground = 544 | mWidgetShortcutMenu.background as GradientDrawable 545 | mWidgetShortcutMenuBackground.alpha = singleLayerAlpha 546 | 547 | for (index in 0..mAppShortcutMenu.childCount) { 548 | val child = mAppShortcutMenu.getChildAt(index) 549 | if (child != null && child.background != null) { 550 | if (child.background is Drawable) { 551 | val childBackground = child.background as Drawable 552 | childBackground.alpha = singleLayerAlpha 553 | } 554 | } 555 | } 556 | 557 | for (index in 0..mSystemShortcutMenu.childCount) { 558 | val child = mSystemShortcutMenu.getChildAt(index) 559 | if (child != null && child.background != null) { 560 | if (child.background is Drawable) { 561 | val childBackground = child.background as Drawable 562 | childBackground.alpha = singleLayerAlpha 563 | } 564 | } 565 | } 566 | 567 | for (index in 0..mWidgetShortcutMenu.childCount) { 568 | val child = mWidgetShortcutMenu.getChildAt(index) 569 | if (child != null && child.background != null) { 570 | if (child.background is Drawable) { 571 | val childBackground = child.background as Drawable 572 | childBackground.alpha = singleLayerAlpha 573 | } 574 | } 575 | } 576 | } 577 | }) 578 | XposedBridge.hookAllMethods( 579 | ShortcutMenuClass, 580 | "addArrow", 581 | object : XC_MethodHook() { 582 | override fun afterHookedMethod(param: MethodHookParam) { 583 | if (!isShortcutMenuLayerBlurred) { 584 | return 585 | } 586 | val mArrow = HookUtils.getValueByField( 587 | param.thisObject, 588 | "mArrow" 589 | ) as View 590 | val mArrowBackground = mArrow.background as ShapeDrawable 591 | mArrowBackground.alpha = shortcutMenuBackgroundAlpha 592 | } 593 | }) 594 | } 595 | } 596 | 597 | fun hideBlurIconWhenEnterRecents() { 598 | val BlurUtilsClass = HookUtils.getClass( 599 | "com.miui.home.launcher.common.BlurUtils", 600 | classLoader 601 | ) ?: return 602 | XposedBridge.hookAllMethods( 603 | BlurUtilsClass, 604 | "fastBlurWhenEnterRecents", 605 | object : XC_MethodHook() { 606 | override fun afterHookedMethod(param: MethodHookParam) { 607 | val launcher = param.args[0] 608 | if (launcher != null) { 609 | XposedHelpers.callMethod(launcher, "hideShortcutMenuWithoutAnim") 610 | } 611 | hideBlurDrawable() 612 | } 613 | }) 614 | XposedBridge.hookAllMethods( 615 | BlurUtilsClass, 616 | "fastBlurWhenExitRecents", 617 | object : XC_MethodHook() { 618 | override fun afterHookedMethod(param: MethodHookParam) { 619 | showBlurDrawable() 620 | } 621 | }) 622 | } 623 | 624 | fun showBlurDrawable() { 625 | allBluredDrawable.forEach { drawable -> 626 | XposedHelpers.callMethod(drawable, "setVisible", true, false) 627 | } 628 | } 629 | 630 | fun hideBlurDrawable() { 631 | allBluredDrawable.forEach { drawable -> 632 | XposedHelpers.callMethod(drawable, "setVisible", false, false) 633 | } 634 | } 635 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/hooks/blur/PersonalAssistant.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.hooks.blur 2 | 3 | import android.graphics.drawable.ColorDrawable 4 | import android.view.Window 5 | import cn.houkyo.wini.models.ConfigModel 6 | import cn.houkyo.wini.utils.ColorUtils 7 | import cn.houkyo.wini.utils.HookUtils 8 | import de.robv.android.xposed.XC_MethodHook 9 | import de.robv.android.xposed.XposedHelpers 10 | import kotlin.math.abs 11 | 12 | class PersonalAssistant(private val classLoader: ClassLoader, config: ConfigModel) { 13 | val blurRadius = config.personalAssistant.background.blurRadius 14 | val backgroundColor = ColorUtils.hexToColor(config.personalAssistant.background.backgroundColor) 15 | 16 | fun addBlurEffectToPersonalAssistant() { 17 | val AssistantOverlayWindowClass = HookUtils.getClass( 18 | "com.miui.personalassistant.core.overlay.AssistantOverlayWindow", 19 | classLoader 20 | ) ?: return 21 | 22 | var lastBlurRadius = -1 23 | 24 | XposedHelpers.findAndHookMethod( 25 | AssistantOverlayWindowClass, 26 | "a", 27 | Float::class.java, 28 | object : XC_MethodHook() { 29 | override fun afterHookedMethod(param: MethodHookParam) { 30 | val scrollX = param.args[0] as Float 31 | val window = HookUtils.getValueByField(param.thisObject, "b") ?: return 32 | if (window.javaClass.name.contains("Window")) { 33 | try { 34 | window as Window 35 | val blurRadius = (scrollX * blurRadius).toInt() 36 | if (abs(blurRadius - lastBlurRadius) > 2) { 37 | window.setBackgroundBlurRadius(blurRadius) 38 | lastBlurRadius = blurRadius 39 | } 40 | val backgroundColorDrawable = ColorDrawable(backgroundColor) 41 | backgroundColorDrawable.alpha = (scrollX * 255).toInt() 42 | window.setBackgroundDrawable(backgroundColorDrawable) 43 | } catch (e: Throwable) { 44 | // 重复报错会污染日志 45 | // HookUtils.log(e.message) 46 | } 47 | } 48 | } 49 | }) 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/hooks/blur/SecurityCenter.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.hooks.blur 2 | 3 | import android.content.Context 4 | import android.graphics.* 5 | import android.graphics.drawable.LayerDrawable 6 | import android.graphics.drawable.VectorDrawable 7 | import android.util.AttributeSet 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import android.widget.ImageView 11 | import android.widget.ListView 12 | import android.widget.TextView 13 | import cn.houkyo.wini.models.ConfigModel 14 | import cn.houkyo.wini.utils.ColorUtils 15 | import cn.houkyo.wini.utils.HookUtils 16 | import de.robv.android.xposed.XC_MethodHook 17 | import de.robv.android.xposed.XC_MethodReplacement 18 | import de.robv.android.xposed.XposedBridge 19 | import de.robv.android.xposed.XposedHelpers 20 | 21 | class SecurityCenter(private val classLoader: ClassLoader, config: ConfigModel) { 22 | val blurRadius = config.securityCenter.dockBackground.blurRadius 23 | val backgroundColor = 24 | ColorUtils.hexToColor(config.securityCenter.dockBackground.backgroundColor) 25 | val shouldInvertColor = !ColorUtils.isDarkColor(backgroundColor) 26 | 27 | private var appVersionCode = 40000727 28 | 29 | // 反色 同时保持红蓝色变化不大 30 | val invertColorRenderEffect = RenderEffect.createColorFilterEffect( 31 | ColorMatrixColorFilter( 32 | floatArrayOf( 33 | 1f, 1f, -2f, 0f, 16f, 34 | 0f, 0f, 0f, 0f, 0f, 35 | -3f, 1f, 2f, 0f, 16f, 36 | 0f, 0f, 0f, 0.85f, 0f 37 | ) 38 | ) 39 | ) 40 | 41 | // 不反转颜色的名单ID或类名 42 | // whiteList 不在列表内子元素也会反色 43 | val invertColorWhiteList = 44 | arrayOf( 45 | "lv_main", 46 | "second_view" 47 | ) 48 | 49 | // keepList 列表内元素及其子元素不会反色 50 | val keepColorList = 51 | arrayOf( 52 | "rv_information" 53 | ) 54 | 55 | fun addBlurEffectToDock() { 56 | val TurboLayoutClass = HookUtils.getClass( 57 | "com.miui.gamebooster.windowmanager.newbox.TurboLayout", 58 | classLoader 59 | ) ?: return 60 | val NewToolBoxTopViewClass = HookUtils.getClass( 61 | "com.miui.gamebooster.windowmanager.newbox.NewToolBoxTopView", 62 | classLoader 63 | ) ?: return 64 | var VideoBoxViewClass = 65 | HookUtils.getClass("com.miui.gamebooster.videobox.adapter.i", classLoader) 66 | var VideoBoxViewMethodName = "a" 67 | if (VideoBoxViewClass == null) { 68 | // v7.4.9 69 | appVersionCode = 40000749 70 | VideoBoxViewClass = HookUtils.getClass("t7.i", classLoader) ?: return 71 | VideoBoxViewMethodName = "i" 72 | } 73 | 74 | var NewboxClass: Class<*>? = null 75 | TurboLayoutClass.methods.forEach { 76 | if (it.name == "getDockLayout") { 77 | NewboxClass = it.returnType 78 | } 79 | } 80 | if (NewboxClass == null) { 81 | return 82 | } 83 | 84 | XposedBridge.hookAllConstructors(NewboxClass, object : XC_MethodHook() { 85 | override fun afterHookedMethod(param: MethodHookParam) { 86 | val view = param.thisObject as View 87 | view.addOnAttachStateChangeListener( 88 | object : 89 | View.OnAttachStateChangeListener { 90 | override fun onViewAttachedToWindow(view: View) { 91 | // 已有背景 避免重复添加 92 | 93 | if (view.background != null) { 94 | if (HookUtils.isBlurDrawable(view.background)) { 95 | return; 96 | } 97 | } 98 | 99 | view.background = 100 | HookUtils.createBlurDrawable( 101 | view, 102 | blurRadius, 103 | 40, 104 | backgroundColor 105 | ) 106 | } 107 | 108 | override fun onViewDetachedFromWindow(view: View) { 109 | view.background = null 110 | } 111 | }) 112 | } 113 | }) 114 | 115 | XposedBridge.hookAllConstructors(NewToolBoxTopViewClass, object : XC_MethodHook() { 116 | override fun afterHookedMethod(param: MethodHookParam) { 117 | val view = param.thisObject as View 118 | view.addOnAttachStateChangeListener( 119 | object : 120 | View.OnAttachStateChangeListener { 121 | override fun onViewAttachedToWindow(view: View) { 122 | val viewPaernt = view.parent as ViewGroup 123 | val gameContentLayout = viewPaernt.parent as ViewGroup 124 | if (gameContentLayout.background != null) { 125 | if (HookUtils.isBlurDrawable(gameContentLayout.background)) { 126 | return; 127 | } 128 | } 129 | 130 | gameContentLayout.background = 131 | HookUtils.createBlurDrawable( 132 | gameContentLayout, 133 | blurRadius, 134 | 40, 135 | backgroundColor 136 | ) 137 | 138 | if (shouldInvertColor) { 139 | invertViewColor(gameContentLayout) 140 | 141 | //设置 RenderEffect 后会导致文字动画出现问题,故去除动画 142 | val performanceTextView = XposedHelpers.callMethod( 143 | param.thisObject, 144 | "getPerformanceTextView" 145 | ) as View 146 | XposedHelpers.findAndHookMethod( 147 | performanceTextView.javaClass, 148 | if (appVersionCode >= 40000749) "e" else "a", 149 | Boolean::class.java, 150 | object : 151 | XC_MethodReplacement() { 152 | override fun replaceHookedMethod(param: MethodHookParam?) { 153 | param?.result = null 154 | } 155 | }) 156 | } 157 | 158 | var headBackground = 159 | HookUtils.getValueByField(param.thisObject, "j") 160 | if (headBackground == null) { 161 | headBackground = HookUtils.getValueByField(param.thisObject, "j") 162 | } else if (!headBackground.javaClass.name.contains("ImageView")) { 163 | headBackground = HookUtils.getValueByField(param.thisObject, "C") 164 | } 165 | if (headBackground == null) { 166 | return 167 | } 168 | if (headBackground.javaClass.name.contains("ImageView")) { 169 | headBackground as ImageView 170 | headBackground.visibility = View.GONE 171 | } 172 | } 173 | 174 | override fun onViewDetachedFromWindow(view: View) { 175 | val viewPaernt = view.parent as ViewGroup 176 | val gameContentLayout = viewPaernt.parent as ViewGroup 177 | gameContentLayout.background = null 178 | } 179 | }) 180 | } 181 | }) 182 | 183 | 184 | XposedHelpers.findAndHookMethod( 185 | VideoBoxViewClass, 186 | VideoBoxViewMethodName, 187 | Context::class.java, 188 | Boolean::class.java, 189 | object : XC_MethodHook() { 190 | override fun afterHookedMethod(param: MethodHookParam) { 191 | val mainContent = HookUtils.getValueByField(param.thisObject, "b") as ViewGroup 192 | mainContent.addOnAttachStateChangeListener( 193 | object : 194 | View.OnAttachStateChangeListener { 195 | override fun onViewAttachedToWindow(view: View) { 196 | if (view.background != null) { 197 | if (HookUtils.isBlurDrawable(view.background)) { 198 | return; 199 | } 200 | } 201 | 202 | view.background = 203 | HookUtils.createBlurDrawable( 204 | view, 205 | blurRadius, 206 | 40, 207 | backgroundColor 208 | ) 209 | 210 | if (shouldInvertColor) { 211 | invertViewColor(mainContent) 212 | } 213 | } 214 | 215 | override fun onViewDetachedFromWindow(view: View) { 216 | view.background = null 217 | } 218 | }) 219 | } 220 | }) 221 | 222 | if (shouldInvertColor) { 223 | val DetailSettingsLayoutClass = HookUtils.getClass( 224 | "com.miui.gamebooster.videobox.view.DetailSettingsLayout", 225 | classLoader 226 | ) ?: return 227 | val SrsLevelSeekBarProClass = HookUtils.getClass( 228 | "com.miui.gamebooster.videobox.view.SrsLevelSeekBarPro", 229 | classLoader 230 | ) ?: return 231 | var SrsLevelSeekBarInnerViewClass = HookUtils.getClass( 232 | "com.miui.gamebooster.videobox.view.c", 233 | classLoader 234 | ) 235 | if (SrsLevelSeekBarInnerViewClass == null) { 236 | SrsLevelSeekBarInnerViewClass = HookUtils.getClass( 237 | "b8.c", 238 | classLoader 239 | ) ?: return 240 | } 241 | val videoBoxWhiteList = arrayOf( 242 | "miuix.slidingwidget.widget.SlidingButton", 243 | "android.widget.ImageView", 244 | "android.widget.CompoundButton", 245 | "com.miui.common.widgets.gif.GifImageView", 246 | "com.miui.gamebooster.videobox.view.SrsLevelSeekBar", 247 | "com.miui.gamebooster.videobox.view.SrsLevelSeekBarPro", 248 | "com.miui.gamebooster.videobox.view.VideoEffectImageView", 249 | "com.miui.gamebooster.videobox.view.DisplayStyleImageView", 250 | "com.miui.gamebooster.videobox.view.c", 251 | "b8.c", 252 | "com.miui.gamebooster.videobox.view.VBIndicatorView" 253 | ) 254 | 255 | val gameBoxWhiteList = arrayOf( 256 | "audition_view", 257 | "miuix.slidingwidget.widget.SlidingButton" 258 | ) 259 | 260 | val videoBoxKeepList = arrayOf("img_wrapper2") 261 | val gameBoxKeepList = arrayOf( 262 | "rl_header", 263 | "tv_barrage_color_pick", 264 | "seekbar_text_size", 265 | "seekbar_text_speed" 266 | ) 267 | 268 | var SecondViewClass = 269 | HookUtils.getClass("com.miui.gamebooster.windowmanager.newbox.n", classLoader) 270 | var SecondViewMethodName = "b" 271 | 272 | if (appVersionCode >= 40000749) { 273 | SecondViewClass = HookUtils.getClass( 274 | "com.miui.gamebooster.windowmanager.newbox.j", 275 | classLoader 276 | ) ?: return 277 | SecondViewMethodName = "B" 278 | } 279 | val AuditionViewClass = 280 | HookUtils.getClass("com.miui.gamebooster.customview.AuditionView", classLoader) 281 | ?: return 282 | 283 | XposedBridge.hookAllMethods( 284 | DetailSettingsLayoutClass, 285 | "setFunctionType", 286 | object : XC_MethodHook() { 287 | override fun afterHookedMethod(param: MethodHookParam) { 288 | val marqueeTextView = HookUtils.getValueByField(param.thisObject, "d") 289 | if (marqueeTextView != null) { 290 | marqueeTextView as TextView 291 | marqueeTextView.setTextColor(Color.GRAY) 292 | } 293 | val listView = HookUtils.getValueByField(param.thisObject, "c") as ListView 294 | val listViewAdapterClassName = listView.adapter.javaClass.name 295 | val listViewAdapterInnerClass = 296 | HookUtils.getClass("$listViewAdapterClassName\$a", classLoader) 297 | ?: return 298 | XposedBridge.hookAllMethods( 299 | listViewAdapterInnerClass, 300 | "a", 301 | object : XC_MethodHook() { 302 | override fun afterHookedMethod(param: MethodHookParam) { 303 | val isSetupFunction = 304 | param.args[0].toString().contains("BaseModel") 305 | if (isSetupFunction) { 306 | listViewAdapterInnerClass.declaredFields.forEach { field -> 307 | val currentObject = field.get(param.thisObject) 308 | if (currentObject is ImageView) { 309 | if (getId(currentObject) == "img1" || getId( 310 | currentObject 311 | ) == "img2" 312 | ) { 313 | currentObject.setRenderEffect( 314 | RenderEffect.createColorFilterEffect( 315 | ColorMatrixColorFilter( 316 | floatArrayOf( 317 | 1f, 0f, 0f, 0f, 0f, 318 | 0f, 1f, 0f, 0f, 0f, 319 | 0f, 0f, 1f, 0f, 0f, 320 | 0.5f, 0.5f, 0.5f, 0f, 0f 321 | ) 322 | ) 323 | ) 324 | ) 325 | } 326 | } 327 | if (currentObject is View) { 328 | invertViewColor( 329 | currentObject, 330 | videoBoxWhiteList, 331 | videoBoxKeepList 332 | ) 333 | } 334 | } 335 | } 336 | } 337 | }) 338 | } 339 | }) 340 | 341 | XposedHelpers.findAndHookMethod( 342 | SrsLevelSeekBarProClass, 343 | if (appVersionCode >= 40000749) "b" else "a", Context::class.java, 344 | AttributeSet::class.java, Int::class.java, object : XC_MethodHook() { 345 | override fun afterHookedMethod(param: MethodHookParam) { 346 | val bgColorField = SrsLevelSeekBarProClass.getDeclaredField("j") 347 | bgColorField.isAccessible = true 348 | bgColorField.setInt( 349 | param.thisObject, 350 | ColorUtils.addAlphaForColor(Color.GRAY, 150) 351 | ) 352 | 353 | val selectTxtColorField = 354 | SrsLevelSeekBarProClass.getDeclaredField("l") 355 | selectTxtColorField.isAccessible = true 356 | selectTxtColorField.setInt( 357 | param.thisObject, 358 | Color.WHITE 359 | ) 360 | 361 | val normalTxtColorField = 362 | SrsLevelSeekBarProClass.getDeclaredField("l") 363 | normalTxtColorField.isAccessible = true 364 | normalTxtColorField.setInt( 365 | param.thisObject, 366 | Color.WHITE 367 | ) 368 | } 369 | } 370 | ) 371 | 372 | XposedHelpers.findAndHookMethod(SrsLevelSeekBarInnerViewClass, "a", Context::class.java, 373 | AttributeSet::class.java, Int::class.java, object : XC_MethodHook() { 374 | override fun afterHookedMethod(param: MethodHookParam) { 375 | val bgColorField = SrsLevelSeekBarInnerViewClass.getDeclaredField("h") 376 | bgColorField.isAccessible = true 377 | bgColorField.setInt( 378 | param.thisObject, 379 | ColorUtils.addAlphaForColor(Color.WHITE, 150) 380 | ) 381 | } 382 | } 383 | ) 384 | 385 | XposedHelpers.findAndHookMethod( 386 | SecondViewClass, 387 | SecondViewMethodName, 388 | View::class.java, 389 | object : XC_MethodHook() { 390 | override fun afterHookedMethod(param: MethodHookParam) { 391 | val view = param.args[0] as View 392 | invertViewColor(view, gameBoxWhiteList, gameBoxKeepList) 393 | } 394 | }) 395 | 396 | // 让图标颜色更深一点 397 | XposedHelpers.findAndHookMethod( 398 | AuditionViewClass, 399 | if (appVersionCode >= 40000749) "M" else "a", 400 | Context::class.java, 401 | object : XC_MethodHook() { 402 | override fun afterHookedMethod(param: MethodHookParam) { 403 | val view = HookUtils.getValueByField(param.thisObject, "d") as View 404 | val parentView = view.parent 405 | HookUtils.log(parentView) 406 | if (parentView is ViewGroup) { 407 | val lastChild = parentView.getChildAt(parentView.childCount - 1) 408 | if (lastChild is ImageView && lastChild.drawable is VectorDrawable) { 409 | val oldDrawable = lastChild.drawable 410 | val newDrawable = LayerDrawable( 411 | arrayOf( 412 | oldDrawable, 413 | oldDrawable, 414 | oldDrawable, 415 | oldDrawable, 416 | oldDrawable 417 | ) 418 | ) 419 | lastChild.setImageDrawable(newDrawable) 420 | } 421 | } 422 | invertViewColor(view, gameBoxWhiteList, gameBoxKeepList) 423 | } 424 | }) 425 | } 426 | } 427 | 428 | // 尽量给最外层加 RenderEffect 而不是 最内层 429 | // whiteList 不在名单内的子视图依旧反转 430 | // keepList 本身及子视图均不反转 431 | fun invertViewColor( 432 | view: View, 433 | whiteList: Array = invertColorWhiteList, 434 | keepList: Array? = keepColorList, 435 | ) { 436 | if (keepList != null) { 437 | if (keepList.contains(getId(view))) { 438 | return 439 | } 440 | if (keepList.contains(view.javaClass.name)) { 441 | return 442 | } 443 | } 444 | try { 445 | if (isChildNeedInvertColor(view, whiteList, keepList)) { 446 | view.setRenderEffect(invertColorRenderEffect) 447 | } else { 448 | if (view is ViewGroup) { 449 | for (index in 0 until view.childCount) { 450 | val childView = view.getChildAt(index) 451 | if (childView != null) { 452 | invertViewColor(childView, whiteList, keepList) 453 | } 454 | } 455 | } 456 | } 457 | } catch (e: Throwable) { 458 | HookUtils.log(e.message) 459 | } 460 | } 461 | 462 | private fun isChildNeedInvertColor( 463 | view: View, 464 | whiteList: Array, 465 | keepList: Array?, 466 | ): Boolean { 467 | val viewId = getId(view) 468 | if (whiteList.contains(viewId)) { 469 | return false 470 | } 471 | if (whiteList.contains(view.javaClass.name)) { 472 | return false 473 | } 474 | if (keepList != null) { 475 | if (keepList.contains(getId(view))) { 476 | return false 477 | } 478 | if (keepList.contains(view.javaClass.name)) { 479 | return false 480 | } 481 | } 482 | try { 483 | if (view is ViewGroup) { 484 | for (index in 0 until view.childCount) { 485 | val childView = view.getChildAt(index) 486 | if (childView != null) { 487 | if (!isChildNeedInvertColor(childView, whiteList, keepList)) { 488 | return false 489 | } 490 | } 491 | } 492 | } 493 | } catch (e: Throwable) { 494 | HookUtils.log(e.message) 495 | } 496 | return true 497 | } 498 | 499 | private fun getId(view: View): String { 500 | return if (view.id == View.NO_ID) "no-id" else view.resources.getResourceName(view.id) 501 | .replace("com.miui.securitycenter:id/", "") 502 | } 503 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/hooks/blur/SystemUI.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.hooks.blur 2 | 3 | import android.animation.ValueAnimator 4 | import android.content.Context 5 | import android.content.res.Resources 6 | import android.graphics.Color 7 | import android.graphics.drawable.Drawable 8 | import android.graphics.drawable.GradientDrawable 9 | import android.graphics.drawable.LayerDrawable 10 | import android.view.View 11 | import android.view.ViewGroup 12 | import android.widget.ImageView 13 | import cn.houkyo.wini.models.ConfigModel 14 | import cn.houkyo.wini.utils.HookUtils 15 | import de.robv.android.xposed.XC_MethodHook 16 | import de.robv.android.xposed.XC_MethodReplacement 17 | import de.robv.android.xposed.XposedBridge 18 | import de.robv.android.xposed.XposedHelpers 19 | 20 | class SystemUI(private val classLoader: ClassLoader, config: ConfigModel) { 21 | val cornerRadius = config.systemUI.notification.cornerRadius 22 | val blurRadius = config.systemUI.notification.blurRadius 23 | val blurBackgroundAlpha = config.systemUI.notification.blurBackgroundAlpha 24 | val defaultBackgroundAlpha = config.systemUI.notification.defaultBackgroundAlpha 25 | val qsControlDetailBackgroundAlpha = config.systemUI.quickSetting.controlDetailBackgroundAlpha 26 | 27 | fun addBlurEffectToNotificationView() { 28 | val MiuiExpandableNotificationRowClass = HookUtils.getClass( 29 | "com.android.systemui.statusbar.notification.row.MiuiExpandableNotificationRow", 30 | classLoader 31 | ) ?: return 32 | 33 | val NotificationBackgroundViewClass = HookUtils.getClass( 34 | "com.android.systemui.statusbar.notification.row.NotificationBackgroundView", 35 | classLoader 36 | ) ?: return 37 | 38 | val AppMiniWindowRowTouchHelperClass = HookUtils.getClass( 39 | "com.android.systemui.statusbar.notification.policy.AppMiniWindowRowTouchHelper", 40 | classLoader 41 | ) ?: return 42 | 43 | val MiuiNotificationPanelViewControllerClass = HookUtils.getClass( 44 | "com.android.systemui.statusbar.phone.MiuiNotificationPanelViewController", 45 | classLoader 46 | ) ?: return 47 | 48 | val NotificationStackScrollLayoutClass = HookUtils.getClass( 49 | "com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout", 50 | classLoader 51 | ) ?: return 52 | 53 | val LockScreenMagazineControllerClass = HookUtils.getClass( 54 | "com.android.keyguard.magazine.LockScreenMagazineController", 55 | classLoader 56 | ) ?: return 57 | 58 | val BlurRatioChangedListener = HookUtils.getClass( 59 | "com.android.systemui.statusbar.phone.MiuiNotificationPanelViewController\$mBlurRatioChangedListener\$1", 60 | classLoader 61 | ) ?: return 62 | 63 | 64 | // 每次设置背景的时候都同时改透明度 65 | XposedBridge.hookAllMethods( 66 | NotificationBackgroundViewClass, 67 | "setCustomBackground", 68 | object : XC_MethodHook() { 69 | override fun afterHookedMethod(param: MethodHookParam) { 70 | val notificationBackgroundView = param.thisObject 71 | val mDrawableAlphaField = 72 | NotificationBackgroundViewClass.getDeclaredField("mDrawableAlpha") 73 | mDrawableAlphaField.isAccessible = true 74 | val isHandsUp = 75 | XposedHelpers.callMethod(notificationBackgroundView, "headsUp") as Boolean 76 | if (isHandsUp) { 77 | mDrawableAlphaField.set(notificationBackgroundView, blurBackgroundAlpha) 78 | XposedHelpers.callMethod( 79 | notificationBackgroundView, 80 | "setDrawableAlpha", 81 | blurBackgroundAlpha 82 | ) 83 | } else { 84 | mDrawableAlphaField.set(notificationBackgroundView, defaultBackgroundAlpha) 85 | XposedHelpers.callMethod( 86 | notificationBackgroundView, 87 | "setDrawableAlpha", 88 | defaultBackgroundAlpha 89 | ) 90 | } 91 | } 92 | }) 93 | 94 | // 背景bounds改动同步到模糊 95 | XposedBridge.hookAllMethods( 96 | NotificationBackgroundViewClass, 97 | "draw", 98 | object : XC_MethodHook() { 99 | override fun afterHookedMethod(param: MethodHookParam) { 100 | val notificationBackground = param.thisObject as View 101 | val backgroundDrawable = notificationBackground.background ?: return 102 | if (HookUtils.isBlurDrawable(backgroundDrawable)) { 103 | val drawable = param.args[1] as Drawable 104 | backgroundDrawable.bounds = drawable.bounds 105 | } 106 | } 107 | }) 108 | 109 | // 进入小窗模式的时候把模糊去掉 110 | XposedBridge.hookAllMethods( 111 | AppMiniWindowRowTouchHelperClass, 112 | "onMiniWindowTrackingStart", 113 | object : XC_MethodHook() { 114 | override fun beforeHookedMethod(param: MethodHookParam) { 115 | val mPickedMiniWindowChild = 116 | HookUtils.getValueByField(param.thisObject, "mPickedMiniWindowChild") 117 | ?: return 118 | 119 | val mBackgroundNormal = 120 | HookUtils.getValueByField(mPickedMiniWindowChild, "mBackgroundNormal") 121 | ?: return 122 | mBackgroundNormal as View 123 | 124 | if (HookUtils.isBlurDrawable(mBackgroundNormal.background)) { 125 | XposedHelpers.callMethod( 126 | mBackgroundNormal.background, 127 | "setVisible", 128 | false, 129 | false 130 | ) 131 | XposedHelpers.callMethod( 132 | mBackgroundNormal, 133 | "setDrawableAlpha", 134 | defaultBackgroundAlpha + 30 135 | ) 136 | } 137 | } 138 | }) 139 | 140 | XposedBridge.hookAllMethods( 141 | AppMiniWindowRowTouchHelperClass, 142 | "onMiniWindowReset", 143 | object : XC_MethodHook() { 144 | override fun beforeHookedMethod(param: MethodHookParam) { 145 | val mPickedMiniWindowChild = 146 | HookUtils.getValueByField(param.thisObject, "mPickedMiniWindowChild") 147 | ?: return 148 | 149 | val mBackgroundNormal = 150 | HookUtils.getValueByField(mPickedMiniWindowChild, "mBackgroundNormal") 151 | ?: return 152 | mBackgroundNormal as View 153 | 154 | if (HookUtils.isBlurDrawable(mBackgroundNormal.background)) { 155 | XposedHelpers.callMethod( 156 | mBackgroundNormal.background, 157 | "setVisible", 158 | true, 159 | false 160 | ) 161 | XposedHelpers.callMethod( 162 | mBackgroundNormal, 163 | "setDrawableAlpha", 164 | blurBackgroundAlpha 165 | ) 166 | } 167 | } 168 | }) 169 | 170 | // 悬浮的时候把模糊加上 171 | XposedBridge.hookAllMethods( 172 | MiuiExpandableNotificationRowClass, 173 | "setHeadsUp", 174 | object : XC_MethodHook() { 175 | override fun beforeHookedMethod(param: MethodHookParam) { 176 | val isHeadsUp = param.args[0] as Boolean 177 | val miuiNotificationBackgroundView = param.thisObject as View 178 | val mBackgroundNormal = HookUtils.getValueByField( 179 | miuiNotificationBackgroundView, 180 | "mBackgroundNormal" 181 | ) as View 182 | if (!mBackgroundNormal.isAttachedToWindow) { 183 | return; 184 | } 185 | if (isHeadsUp) { 186 | if (mBackgroundNormal.background != null) { 187 | if (HookUtils.isBlurDrawable(mBackgroundNormal.background)) { 188 | return; 189 | } 190 | } 191 | mBackgroundNormal.background = 192 | HookUtils.createBlurDrawable( 193 | mBackgroundNormal, 194 | blurRadius, 195 | cornerRadius 196 | ) 197 | 198 | XposedHelpers.callMethod( 199 | mBackgroundNormal, 200 | "setDrawableAlpha", 201 | blurBackgroundAlpha 202 | ) 203 | } else { 204 | if (HookUtils.isBlurDrawable(mBackgroundNormal.background)) { 205 | mBackgroundNormal.background = null 206 | } 207 | try { 208 | XposedHelpers.callMethod( 209 | mBackgroundNormal, 210 | "setDrawableAlpha", 211 | defaultBackgroundAlpha 212 | ) 213 | } catch (e: Throwable) { 214 | // 215 | } 216 | } 217 | } 218 | }) 219 | 220 | // 进入不同状态,处理一下模糊 221 | XposedBridge.hookAllMethods( 222 | MiuiNotificationPanelViewControllerClass, 223 | "onStateChanged", 224 | object : XC_MethodHook() { 225 | override fun afterHookedMethod(param: MethodHookParam) { 226 | // status 1 锁屏 2 锁屏下拉 0 其他 227 | val status = param.args[0] as Int 228 | val mNotificationStackScroller = 229 | HookUtils.getValueByField( 230 | param.thisObject, 231 | "mNotificationStackScroller" 232 | ) as ViewGroup 233 | if (status == 1) { 234 | if (!isDefaultLockScreenTheme()) { 235 | return 236 | } 237 | for (i in 0..mNotificationStackScroller.childCount) { 238 | val childAt = 239 | mNotificationStackScroller.getChildAt(i) ?: continue 240 | showBlurEffectForNotificationRow(childAt) 241 | } 242 | } else { 243 | for (i in 0..mNotificationStackScroller.childCount) { 244 | val childAt = 245 | mNotificationStackScroller.getChildAt(i) ?: continue 246 | try { 247 | val isHeadsUp = 248 | XposedHelpers.callMethod(childAt, "isHeadsUpState") as Boolean 249 | val isPinned = XposedHelpers.callMethod( 250 | childAt, 251 | "isPinned" 252 | ) as Boolean 253 | if (isHeadsUp && isPinned) { 254 | showBlurEffectForNotificationRow(childAt) 255 | } else { 256 | hideBlurEffectForNotificationRow(childAt) 257 | } 258 | } catch (e: Throwable) { 259 | hideBlurEffectForNotificationRow(childAt) 260 | } 261 | } 262 | } 263 | } 264 | }) 265 | 266 | // 下拉完成处理模糊 267 | /* 268 | XposedBridge.hookAllMethods( 269 | NotificationPanelViewControllerClass, 270 | "onExpandingFinished", 271 | object : XC_MethodHook() { 272 | override fun afterHookedMethod(param: MethodHookParam) { 273 | val isOnKeyguard = 274 | XposedHelpers.callMethod(param.thisObject, "isOnKeyguard") as Boolean 275 | if (isOnKeyguard) { 276 | return 277 | } 278 | val mNotificationStackScroller = 279 | Hook.getValueByField( 280 | param.thisObject, 281 | "mNotificationStackScroller" 282 | ) ?: return 283 | mNotificationStackScroller as ViewGroup 284 | for (i in 0..mNotificationStackScroller.childCount) { 285 | val childAt = 286 | mNotificationStackScroller.getChildAt(i) ?: continue 287 | hideBlurEffectForNotificationRow(childAt) 288 | } 289 | } 290 | }) 291 | */ 292 | 293 | // 通知添加进视图的时候增加模糊 294 | XposedBridge.hookAllMethods( 295 | NotificationStackScrollLayoutClass, 296 | "onViewAddedInternal", 297 | object : XC_MethodHook() { 298 | override fun afterHookedMethod(param: MethodHookParam) { 299 | val expandableView = param.args[0] as View 300 | val mController = HookUtils.getValueByField(param.thisObject, "mController") 301 | ?: return 302 | val mPanelViewController = 303 | HookUtils.getValueByField(mController, "mPanelViewController") 304 | ?: return 305 | val isExpanding = 306 | XposedHelpers.callMethod(mPanelViewController, "isExpanding") as Boolean 307 | if (isExpanding) { 308 | return 309 | } 310 | val isOnKeyguard = 311 | XposedHelpers.callMethod(mPanelViewController, "isOnKeyguard") as Boolean 312 | if (isOnKeyguard) { 313 | if (!isDefaultLockScreenTheme()) { 314 | return 315 | } 316 | showBlurEffectForNotificationRow(expandableView) 317 | } else { 318 | // ZenModeView 没有 isHeadsUpState 方法 319 | try { 320 | val isHeadsUp = 321 | XposedHelpers.callMethod( 322 | expandableView, 323 | "isHeadsUpState" 324 | ) as Boolean 325 | if (isHeadsUp) { 326 | showBlurEffectForNotificationRow(expandableView) 327 | } 328 | } catch (e: Throwable) { 329 | return 330 | } 331 | } 332 | } 333 | }) 334 | 335 | // 锁屏状态透明度修改的时候同步修改模糊透明度 336 | XposedBridge.hookAllMethods( 337 | MiuiNotificationPanelViewControllerClass, 338 | "updateKeyguardElementAlpha", 339 | object : XC_MethodHook() { 340 | override fun afterHookedMethod(param: MethodHookParam) { 341 | if (!isDefaultLockScreenTheme()) { 342 | return 343 | } 344 | val mNotificationStackScroller = 345 | HookUtils.getValueByField( 346 | param.thisObject, 347 | "mNotificationStackScroller" 348 | ) ?: return 349 | mNotificationStackScroller as ViewGroup 350 | 351 | val keyguardContentsAlpha = 352 | XposedHelpers.callMethod( 353 | param.thisObject, 354 | "getKeyguardContentsAlpha" 355 | ) as Float 356 | val drawableAlpha = keyguardContentsAlpha * 255 357 | for (i in 0..mNotificationStackScroller.childCount) { 358 | val childAt = 359 | mNotificationStackScroller.getChildAt(i) ?: continue 360 | setBlurEffectAlphaForNotificationRow(childAt, drawableAlpha.toInt()) 361 | } 362 | } 363 | }) 364 | 365 | XposedBridge.hookAllMethods( 366 | MiuiNotificationPanelViewControllerClass, 367 | "onBouncerShowingChanged", 368 | object : XC_MethodHook() { 369 | override fun beforeHookedMethod(param: MethodHookParam) { 370 | val isBouncerShowing = param.args[0] as Boolean 371 | val mNotificationStackScroller = 372 | HookUtils.getValueByField( 373 | param.thisObject, 374 | "mNotificationStackScroller" 375 | ) ?: return 376 | mNotificationStackScroller as ViewGroup 377 | for (i in 0..mNotificationStackScroller.childCount) { 378 | val childAt = 379 | mNotificationStackScroller.getChildAt(i) ?: continue 380 | if (isBouncerShowing) { 381 | hideBlurEffectForNotificationRow(childAt) 382 | } else { 383 | showBlurEffectForNotificationRow(childAt) 384 | } 385 | } 386 | } 387 | }) 388 | 389 | // 锁屏画报 隐藏模糊 390 | XposedBridge.hookAllMethods( 391 | LockScreenMagazineControllerClass, 392 | "setViewsAlpha", 393 | object : XC_MethodHook() { 394 | override fun beforeHookedMethod(param: MethodHookParam) { 395 | if (!isDefaultLockScreenTheme()) { 396 | return 397 | } 398 | val alpha = param.args[0] as Float 399 | val drawableAlpha = alpha * 255 400 | val mNotificationStackScrollLayout = HookUtils.getValueByField( 401 | param.thisObject, 402 | "mNotificationStackScrollLayout" 403 | ) as ViewGroup 404 | for (i in 0..mNotificationStackScrollLayout.childCount) { 405 | val childAt = 406 | mNotificationStackScrollLayout.getChildAt(i) ?: continue 407 | setBlurEffectAlphaForNotificationRow(childAt, drawableAlpha.toInt()) 408 | } 409 | } 410 | }) 411 | 412 | XposedBridge.hookAllMethods( 413 | NotificationStackScrollLayoutClass, 414 | "setDozing", 415 | object : XC_MethodHook() { 416 | override fun afterHookedMethod(param: MethodHookParam) { 417 | val isDozing = param.args[0] as Boolean 418 | val mNotificationStackScrollLayout = param.thisObject as ViewGroup 419 | for (i in 0..mNotificationStackScrollLayout.childCount) { 420 | val childAt = 421 | mNotificationStackScrollLayout.getChildAt(i) ?: continue 422 | if (isDozing) { 423 | hideBlurEffectForNotificationRow(childAt) 424 | } else { 425 | showBlurEffectForNotificationRow(childAt) 426 | } 427 | } 428 | } 429 | }) 430 | 431 | /* 432 | XposedBridge.hookAllMethods( 433 | KeyguardPanelViewInjectorClass, 434 | "onKeyguardVisibilityChanged", 435 | object : XC_MethodHook() { 436 | override fun afterHookedMethod(param: MethodHookParam) { 437 | val isVisible = param.args[0] as Boolean 438 | val mPanelViewController = 439 | Hook.getValueByField(param.thisObject, "mPanelViewController") ?: return 440 | 441 | val mNotificationStackScroller = 442 | Hook.getValueByField( 443 | mPanelViewController, 444 | "mNotificationStackScroller" 445 | ) ?: return 446 | mNotificationStackScroller as ViewGroup 447 | for (i in 0..mNotificationStackScroller.childCount) { 448 | val childAt = 449 | mNotificationStackScroller.getChildAt(i) ?: continue 450 | if (isVisible) { 451 | showBlurEffectForNotificationRow(childAt) 452 | } else { 453 | try { 454 | val isHeadsUp = 455 | XposedHelpers.callMethod( 456 | childAt, 457 | "isHeadsUpState" 458 | ) as Boolean 459 | val isPinned = XposedHelpers.callMethod( 460 | childAt, 461 | "isPinned" 462 | ) as Boolean 463 | if (isHeadsUp && isPinned) { 464 | showBlurEffectForNotificationRow(childAt) 465 | } else { 466 | hideBlurEffectForNotificationRow(childAt) 467 | } 468 | } catch (e: Throwable) { 469 | hideBlurEffectForNotificationRow(childAt) 470 | } 471 | } 472 | } 473 | } 474 | }) 475 | */ 476 | 477 | XposedBridge.hookAllConstructors(MiuiNotificationPanelViewControllerClass, 478 | object : XC_MethodHook() { 479 | override fun afterHookedMethod(param: MethodHookParam) { 480 | val miuiNotificationPanelViewControllerClass = param.thisObject 481 | val mNotificationStackScroller = 482 | HookUtils.getValueByField( 483 | param.thisObject, 484 | "mNotificationStackScroller" 485 | ) ?: return 486 | mNotificationStackScroller as ViewGroup 487 | XposedBridge.hookAllMethods(BlurRatioChangedListener, 488 | "onBlurRadiusChanged", 489 | object : XC_MethodHook() { 490 | override fun afterHookedMethod(param: MethodHookParam) { 491 | val radius = param.args[0] as Int 492 | val isOnKeyguard = XposedHelpers.callMethod( 493 | miuiNotificationPanelViewControllerClass, 494 | "isOnKeyguard" 495 | ) as Boolean 496 | for (i in 0..mNotificationStackScroller.childCount) { 497 | val childAt = 498 | mNotificationStackScroller.getChildAt(i) ?: continue 499 | if (radius > 30) { 500 | hideBlurEffectForNotificationRow(childAt) 501 | } else { 502 | // 锁屏状态显示模糊 503 | if (isOnKeyguard) { 504 | showBlurEffectForNotificationRow(childAt) 505 | } 506 | } 507 | } 508 | } 509 | }) 510 | } 511 | }) 512 | } 513 | 514 | fun addBlurEffectToLockScreen() { 515 | val MiuiNotificationPanelViewControllerClass = HookUtils.getClass( 516 | "com.android.systemui.statusbar.phone.MiuiNotificationPanelViewController", 517 | classLoader 518 | ) ?: return 519 | 520 | val KeyguardBottomAreaViewClass = HookUtils.getClass( 521 | "com.android.systemui.statusbar.phone.KeyguardBottomAreaView", 522 | classLoader 523 | ) ?: return 524 | 525 | val KeyguardMoveHelperClass = HookUtils.getClass( 526 | "com.android.keyguard.KeyguardMoveHelper", 527 | classLoader 528 | ) ?: return 529 | 530 | val BaseKeyguardMoveHelperClass = HookUtils.getClass( 531 | "com.android.keyguard.BaseKeyguardMoveHelper", 532 | classLoader 533 | ) ?: return 534 | 535 | val LockScreenMagazineControllerClass = HookUtils.getClass( 536 | "com.android.keyguard.magazine.LockScreenMagazineController", 537 | classLoader 538 | ) ?: return 539 | 540 | XposedBridge.hookAllMethods( 541 | KeyguardBottomAreaViewClass, 542 | "onAttachedToWindow", 543 | object : XC_MethodHook() { 544 | override fun afterHookedMethod(param: MethodHookParam) { 545 | if (!isDefaultLockScreenTheme()) { 546 | return 547 | } 548 | val mLeftAffordanceView = HookUtils.getValueByField( 549 | param.thisObject, 550 | "mLeftAffordanceView" 551 | ) as ImageView 552 | val mRightAffordanceView = HookUtils.getValueByField( 553 | param.thisObject, 554 | "mRightAffordanceView" 555 | ) as ImageView 556 | 557 | val keyguardBottomAreaView = param.thisObject as View 558 | val leftBlurDrawable = HookUtils.createBlurDrawable( 559 | keyguardBottomAreaView, 560 | 40, 561 | 100, 562 | Color.argb(60, 255, 255, 255) 563 | ) 564 | val leftLayerDrawable = LayerDrawable(arrayOf(leftBlurDrawable)) 565 | val rightBlurDrawable = HookUtils.createBlurDrawable( 566 | keyguardBottomAreaView, 567 | 40, 568 | 100, 569 | Color.argb(60, 255, 255, 255) 570 | ) 571 | val rightLayerDrawable = LayerDrawable(arrayOf(rightBlurDrawable)) 572 | leftLayerDrawable.setLayerInset(0, 40, 40, 40, 40) 573 | rightLayerDrawable.setLayerInset(0, 40, 40, 40, 40) 574 | mLeftAffordanceView.background = leftLayerDrawable 575 | mRightAffordanceView.background = rightLayerDrawable 576 | } 577 | }) 578 | 579 | XposedBridge.hookAllMethods( 580 | MiuiNotificationPanelViewControllerClass, 581 | "setBouncerShowingFraction", 582 | object : XC_MethodHook() { 583 | override fun afterHookedMethod(param: MethodHookParam) { 584 | if (!isDefaultLockScreenTheme()) { 585 | return 586 | } 587 | val mKeyguardBouncerShowing = HookUtils.getValueByField( 588 | param.thisObject, 589 | "mKeyguardBouncerShowing" 590 | ) as Boolean 591 | val mKeyguardBottomArea = 592 | XposedHelpers.callMethod(param.thisObject, "getKeyguardBottomArea") 593 | val mLeftAffordanceView = HookUtils.getValueByField( 594 | mKeyguardBottomArea, 595 | "mLeftAffordanceView" 596 | ) as ImageView 597 | val mRightAffordanceView = HookUtils.getValueByField( 598 | mKeyguardBottomArea, 599 | "mRightAffordanceView" 600 | ) as ImageView 601 | if (mLeftAffordanceView.background is LayerDrawable) { 602 | val layerDrawable = mLeftAffordanceView.background as LayerDrawable 603 | val blurDrawable = layerDrawable.getDrawable(0) 604 | XposedHelpers.callMethod( 605 | blurDrawable, 606 | "setVisible", 607 | !mKeyguardBouncerShowing, 608 | false 609 | ) 610 | } 611 | if (mRightAffordanceView.background is LayerDrawable) { 612 | val layerDrawable = mRightAffordanceView.background as LayerDrawable 613 | val blurDrawable = layerDrawable.getDrawable(0) 614 | XposedHelpers.callMethod( 615 | blurDrawable, 616 | "setVisible", 617 | !mKeyguardBouncerShowing, 618 | false 619 | ) 620 | } 621 | } 622 | }) 623 | 624 | XposedBridge.hookAllMethods( 625 | MiuiNotificationPanelViewControllerClass, 626 | "updateKeyguardElementAlpha", 627 | object : XC_MethodHook() { 628 | override fun afterHookedMethod(param: MethodHookParam) { 629 | if (!isDefaultLockScreenTheme()) { 630 | return 631 | } 632 | val mNotificationStackScroller = 633 | HookUtils.getValueByField( 634 | param.thisObject, 635 | "mNotificationStackScroller" 636 | ) ?: return 637 | val mKeyguardBouncerShowing = HookUtils.getValueByField( 638 | param.thisObject, 639 | "mKeyguardBouncerShowing" 640 | ) ?: return 641 | mNotificationStackScroller as ViewGroup 642 | mKeyguardBouncerShowing as Boolean 643 | val keyguardContentsAlpha = 644 | XposedHelpers.callMethod( 645 | param.thisObject, 646 | "getKeyguardContentsAlpha" 647 | ) as Float 648 | val drawableAlpha = keyguardContentsAlpha * 255 649 | val mKeyguardBottomArea = 650 | XposedHelpers.callMethod(param.thisObject, "getKeyguardBottomArea") 651 | val mLeftAffordanceView = HookUtils.getValueByField( 652 | mKeyguardBottomArea, 653 | "mLeftAffordanceView" 654 | ) as ImageView 655 | val mRightAffordanceView = HookUtils.getValueByField( 656 | mKeyguardBottomArea, 657 | "mRightAffordanceView" 658 | ) as ImageView 659 | if (mLeftAffordanceView.background is LayerDrawable) { 660 | val layerDrawable = mLeftAffordanceView.background as LayerDrawable 661 | val blurDrawable = layerDrawable.getDrawable(0) 662 | XposedHelpers.callMethod( 663 | blurDrawable, 664 | "setAlpha", 665 | drawableAlpha.toInt() 666 | ) 667 | } 668 | if (mRightAffordanceView.background is LayerDrawable) { 669 | val layerDrawable = mRightAffordanceView.background as LayerDrawable 670 | val blurDrawable = layerDrawable.getDrawable(0) 671 | XposedHelpers.callMethod( 672 | blurDrawable, 673 | "setAlpha", 674 | drawableAlpha.toInt() 675 | ) 676 | } 677 | } 678 | }) 679 | 680 | XposedBridge.hookAllMethods( 681 | KeyguardMoveHelperClass, 682 | "setTranslation", 683 | object : XC_MethodHook() { 684 | override fun afterHookedMethod(param: MethodHookParam) { 685 | if (!isDefaultLockScreenTheme()) { 686 | return 687 | } 688 | val mCurrentScreen = 689 | HookUtils.getValueByField(param.thisObject, "mCurrentScreen") as Int 690 | val mLeftViewBg = 691 | HookUtils.getValueByField(param.thisObject, "mLeftViewBg") as ImageView 692 | mLeftViewBg.setImageDrawable(null) 693 | mLeftViewBg.setBackgroundColor(Color.TRANSPARENT) 694 | val transitionX = param.args[0] as Float 695 | val screenWidth = 696 | XposedHelpers.callMethod( 697 | param.thisObject, 698 | "getScreenWidth" 699 | ) as Float 700 | var alpha = (transitionX / screenWidth) * 255 701 | if (mCurrentScreen != 1) { 702 | alpha += 255 703 | } 704 | if (alpha > 255 || alpha < 0) { 705 | return 706 | } 707 | val drawableAlpha = (255 - alpha).toInt() 708 | var blurRadius = (transitionX / screenWidth) * 80 709 | var colorAlpha = (transitionX / screenWidth) * 50 710 | if (mCurrentScreen != 1) { 711 | blurRadius += 80 712 | colorAlpha += 50 713 | } 714 | val mFaceUnlockView = 715 | HookUtils.getValueByField(param.thisObject, "mFaceUnlockView") ?: return 716 | mFaceUnlockView as View 717 | if (mFaceUnlockView.parent == null || mFaceUnlockView.parent.parent == null) { 718 | return 719 | } 720 | val targetView = mFaceUnlockView.parent.parent as View 721 | if (blurRadius in 1f..81f) { 722 | if (HookUtils.isBlurDrawable(targetView.background)) { 723 | XposedHelpers.callMethod( 724 | targetView.background, 725 | "setBlurRadius", 726 | blurRadius.toInt() 727 | ) 728 | XposedHelpers.callMethod( 729 | targetView.background, 730 | "setColor", 731 | Color.argb(colorAlpha.toInt(), 0, 0, 0) 732 | ) 733 | } else { 734 | targetView.background = 735 | HookUtils.createBlurDrawable( 736 | targetView, 737 | blurRadius.toInt(), 738 | 0, 739 | Color.argb(colorAlpha.toInt(), 0, 0, 0) 740 | ) 741 | } 742 | } else { 743 | targetView.background = null 744 | } 745 | val mNotificationPanelViewController = 746 | HookUtils.getValueByField( 747 | param.thisObject, 748 | "mNotificationPanelViewController" 749 | ) 750 | ?: return 751 | val mNotificationStackScroller = 752 | HookUtils.getValueByField( 753 | mNotificationPanelViewController, 754 | "mNotificationStackScroller" 755 | ) ?: return 756 | mNotificationStackScroller as ViewGroup 757 | 758 | val mKeyguardBottomArea = 759 | XposedHelpers.callMethod( 760 | mNotificationPanelViewController, 761 | "getKeyguardBottomArea" 762 | ) 763 | val mLeftAffordanceView = HookUtils.getValueByField( 764 | mKeyguardBottomArea, 765 | "mLeftAffordanceView" 766 | ) as ImageView 767 | val mRightAffordanceView = HookUtils.getValueByField( 768 | mKeyguardBottomArea, 769 | "mRightAffordanceView" 770 | ) as ImageView 771 | if (mLeftAffordanceView.background is LayerDrawable) { 772 | val layerDrawable = mLeftAffordanceView.background as LayerDrawable 773 | layerDrawable.alpha = drawableAlpha 774 | } 775 | if (mRightAffordanceView.background is LayerDrawable) { 776 | val layerDrawable = mRightAffordanceView.background as LayerDrawable 777 | layerDrawable.alpha = drawableAlpha 778 | } 779 | } 780 | }) 781 | 782 | XposedBridge.hookAllMethods( 783 | BaseKeyguardMoveHelperClass, 784 | "doPanelViewAnimation", 785 | object : XC_MethodHook() { 786 | override fun afterHookedMethod(param: MethodHookParam) { 787 | if (!isDefaultLockScreenTheme()) { 788 | return 789 | } 790 | val mNotificationPanelViewController = 791 | HookUtils.getValueByField( 792 | param.thisObject, 793 | "mNotificationPanelViewController" 794 | ) 795 | ?: return 796 | val panelView = XposedHelpers.callMethod( 797 | mNotificationPanelViewController, 798 | "getPanelView" 799 | ) ?: return 800 | panelView as View 801 | if (panelView.parent == null) { 802 | return 803 | } 804 | val targetBlurView = panelView.parent as View 805 | val isOnKeyguard = XposedHelpers.callMethod( 806 | mNotificationPanelViewController, 807 | "isOnKeyguard" 808 | ) as Boolean 809 | if (!isOnKeyguard) { 810 | if (HookUtils.isBlurDrawable(targetBlurView.background)) { 811 | targetBlurView.background = null 812 | } 813 | return 814 | } 815 | val mKeyguardBouncerShowing = HookUtils.getValueByField( 816 | mNotificationPanelViewController, 817 | "mKeyguardBouncerShowing" 818 | ) as Boolean 819 | if (mKeyguardBouncerShowing) { 820 | return 821 | } 822 | val mTranslationPer = 823 | HookUtils.getValueByField(param.thisObject, "mTranslationPer") as Float 824 | val f2 = 1.0f - (1.0f - mTranslationPer) * (1.0f - mTranslationPer) 825 | val blurRadius = f2 * 50 826 | if (blurRadius > 0f) { 827 | if (HookUtils.isBlurDrawable(targetBlurView.background)) { 828 | XposedHelpers.callMethod( 829 | targetBlurView.background, 830 | "setBlurRadius", 831 | blurRadius.toInt() 832 | ) 833 | } else { 834 | targetBlurView.background = 835 | HookUtils.createBlurDrawable(targetBlurView, blurRadius.toInt(), 0) 836 | } 837 | } else { 838 | if (!mKeyguardBouncerShowing) { 839 | targetBlurView.background = null 840 | } 841 | } 842 | } 843 | }) 844 | 845 | XposedBridge.hookAllMethods( 846 | MiuiNotificationPanelViewControllerClass, 847 | "onBouncerShowingChanged", 848 | object : XC_MethodHook() { 849 | override fun beforeHookedMethod(param: MethodHookParam) { 850 | if (!isDefaultLockScreenTheme()) { 851 | return 852 | } 853 | val isBouncerShowing = param.args[0] as Boolean 854 | val mBouncerFractionAnimator = 855 | HookUtils.getValueByField( 856 | param.thisObject, 857 | "mBouncerFractionAnimator" 858 | ) ?: return 859 | mBouncerFractionAnimator as ValueAnimator 860 | if (isBouncerShowing) { 861 | val mKeyguardBouncerFractionField = 862 | MiuiNotificationPanelViewControllerClass.getDeclaredField("mKeyguardBouncerFraction") 863 | mKeyguardBouncerFractionField.isAccessible = true 864 | mKeyguardBouncerFractionField.set(param.thisObject, 1f) 865 | } 866 | } 867 | }) 868 | 869 | XposedBridge.hookAllMethods( 870 | LockScreenMagazineControllerClass, 871 | "setViewsAlpha", 872 | object : XC_MethodHook() { 873 | override fun beforeHookedMethod(param: MethodHookParam) { 874 | if (!isDefaultLockScreenTheme()) { 875 | return 876 | } 877 | val alpha = param.args[0] as Float 878 | val drawableAlpha = alpha * 255 879 | if (drawableAlpha < 0 || drawableAlpha > 255) { 880 | return 881 | } 882 | val mNotificationStackScrollLayout = HookUtils.getValueByField( 883 | param.thisObject, 884 | "mNotificationStackScrollLayout" 885 | ) ?: return 886 | // NotificationStackScrollLayoutController 887 | val mController = 888 | HookUtils.getValueByField(mNotificationStackScrollLayout, "mController") 889 | ?: return 890 | 891 | val mPanelViewController = 892 | HookUtils.getValueByField(mController, "mPanelViewController") ?: return 893 | 894 | val mKeyguardBottomArea = 895 | XposedHelpers.callMethod( 896 | mPanelViewController, 897 | "getKeyguardBottomArea" 898 | ) ?: return 899 | val mLeftAffordanceView = HookUtils.getValueByField( 900 | mKeyguardBottomArea, 901 | "mLeftAffordanceView" 902 | ) as ImageView 903 | val mRightAffordanceView = HookUtils.getValueByField( 904 | mKeyguardBottomArea, 905 | "mRightAffordanceView" 906 | ) as ImageView 907 | if (mLeftAffordanceView.background is LayerDrawable) { 908 | val layerDrawable = mLeftAffordanceView.background as LayerDrawable 909 | layerDrawable.alpha = drawableAlpha.toInt() 910 | } 911 | if (mRightAffordanceView.background is LayerDrawable) { 912 | val layerDrawable = mRightAffordanceView.background as LayerDrawable 913 | layerDrawable.alpha = drawableAlpha.toInt() 914 | } 915 | } 916 | }) 917 | 918 | XposedBridge.hookAllMethods( 919 | KeyguardBottomAreaViewClass, 920 | "setDozing", 921 | object : XC_MethodHook() { 922 | override fun afterHookedMethod(param: MethodHookParam) { 923 | if (!isDefaultLockScreenTheme()) { 924 | return 925 | } 926 | val isDozing = param.args[0] as Boolean 927 | val mLeftAffordanceView = HookUtils.getValueByField( 928 | param.thisObject, 929 | "mLeftAffordanceView" 930 | ) as ImageView 931 | val mRightAffordanceView = HookUtils.getValueByField( 932 | param.thisObject, 933 | "mRightAffordanceView" 934 | ) as ImageView 935 | if (mLeftAffordanceView.background == null || mRightAffordanceView.background == null) { 936 | return 937 | } 938 | val leftLayerDrawable = mLeftAffordanceView.background 939 | val rightLayerDrawable = mRightAffordanceView.background 940 | if (leftLayerDrawable is LayerDrawable && rightLayerDrawable is LayerDrawable) { 941 | val leftBlurDrawable = leftLayerDrawable.getDrawable(0) ?: return 942 | val rightBlurDrawable = rightLayerDrawable.getDrawable(0) ?: return 943 | XposedHelpers.callMethod( 944 | leftBlurDrawable, 945 | "setVisible", 946 | !isDozing, 947 | false 948 | ) 949 | XposedHelpers.callMethod( 950 | rightBlurDrawable, 951 | "setVisible", 952 | !isDozing, 953 | false 954 | ) 955 | } 956 | } 957 | }) 958 | } 959 | 960 | fun showBlurEffectForNotificationRow(notificationRow: View) { 961 | if (notificationRow.javaClass.name.contains("ZenModeView")) { 962 | val zenModeContentContainer = 963 | XposedHelpers.callMethod(notificationRow, "getContentView") ?: return 964 | zenModeContentContainer as ViewGroup 965 | val zenModeContent = 966 | zenModeContentContainer.getChildAt(0) ?: return 967 | val contentBackground = 968 | zenModeContent.background as GradientDrawable 969 | contentBackground.alpha = blurBackgroundAlpha 970 | contentBackground.invalidateSelf() 971 | if (!HookUtils.isBlurDrawable(zenModeContentContainer.background)) { 972 | zenModeContentContainer.background = 973 | HookUtils.createBlurDrawable(notificationRow, blurRadius, cornerRadius) 974 | } 975 | } else { 976 | val mBackgroundNormal = 977 | HookUtils.getValueByField(notificationRow, "mBackgroundNormal") 978 | ?: return 979 | mBackgroundNormal as View 980 | if (!HookUtils.isBlurDrawable(mBackgroundNormal.background)) { 981 | mBackgroundNormal.background = 982 | HookUtils.createBlurDrawable(mBackgroundNormal, blurRadius, cornerRadius) 983 | try { 984 | XposedHelpers.callMethod( 985 | mBackgroundNormal, 986 | "setDrawableAlpha", 987 | blurBackgroundAlpha 988 | ) 989 | } catch (e: Throwable) { 990 | // Nothing to do. 991 | } 992 | } 993 | try { 994 | val childList = 995 | XposedHelpers.callMethod(notificationRow, "getAttachedChildren") ?: return 996 | childList as List<*> 997 | if (childList.size > 0) { 998 | childList.forEach { child -> 999 | if (child != null) { 1000 | showBlurEffectForNotificationRow(child as View) 1001 | } 1002 | } 1003 | } 1004 | } catch (e: Throwable) { 1005 | // Nothing to do. 1006 | } 1007 | } 1008 | } 1009 | 1010 | fun hideBlurEffectForNotificationRow(notificationRow: View) { 1011 | if (notificationRow.javaClass.name.contains("ZenModeView")) { 1012 | val zenModeContentContainer = 1013 | XposedHelpers.callMethod(notificationRow, "getContentView") ?: return 1014 | zenModeContentContainer as ViewGroup 1015 | val zenModeContent = 1016 | zenModeContentContainer.getChildAt(0) ?: return 1017 | val contentBackground = 1018 | zenModeContent.background as GradientDrawable 1019 | contentBackground.alpha = defaultBackgroundAlpha 1020 | contentBackground.invalidateSelf() 1021 | if (HookUtils.isBlurDrawable(zenModeContentContainer.background)) { 1022 | zenModeContentContainer.background = null 1023 | } 1024 | } else { 1025 | val mBackgroundNormal = 1026 | HookUtils.getValueByField(notificationRow, "mBackgroundNormal") 1027 | ?: return 1028 | mBackgroundNormal as View 1029 | if (HookUtils.isBlurDrawable(mBackgroundNormal.background)) { 1030 | mBackgroundNormal.background = null 1031 | } 1032 | try { 1033 | XposedHelpers.callMethod( 1034 | mBackgroundNormal, 1035 | "setDrawableAlpha", 1036 | defaultBackgroundAlpha 1037 | ) 1038 | } catch (e: Throwable) { 1039 | // Nothing to do. 1040 | } 1041 | 1042 | try { 1043 | val childList = 1044 | XposedHelpers.callMethod(notificationRow, "getAttachedChildren") ?: return 1045 | childList as List<*> 1046 | if (childList.size > 0) { 1047 | childList.forEach { child -> 1048 | if (child != null) { 1049 | hideBlurEffectForNotificationRow(child as View) 1050 | } 1051 | } 1052 | } 1053 | } catch (e: Throwable) { 1054 | // Nothing to do. 1055 | } 1056 | } 1057 | } 1058 | 1059 | fun setBlurEffectAlphaForNotificationRow(notificationRow: View, alpha: Int) { 1060 | if (alpha < 0 || alpha > 255) { 1061 | return 1062 | } 1063 | if (notificationRow.javaClass.name.contains("ZenModeView")) { 1064 | val zenModeContentContainer = 1065 | XposedHelpers.callMethod(notificationRow, "getContentView") ?: return 1066 | zenModeContentContainer as ViewGroup 1067 | if (HookUtils.isBlurDrawable(zenModeContentContainer.background)) { 1068 | XposedHelpers.callMethod(zenModeContentContainer.background, "setAlpha", alpha) 1069 | } 1070 | } else { 1071 | val mBackgroundNormal = 1072 | HookUtils.getValueByField(notificationRow, "mBackgroundNormal") 1073 | ?: return 1074 | mBackgroundNormal as View 1075 | if (HookUtils.isBlurDrawable(mBackgroundNormal.background)) { 1076 | XposedHelpers.callMethod(mBackgroundNormal.background, "setAlpha", alpha) 1077 | } 1078 | try { 1079 | val childList = 1080 | XposedHelpers.callMethod(notificationRow, "getAttachedChildren") ?: return 1081 | childList as List<*> 1082 | if (childList.size > 0) { 1083 | childList.forEach { child -> 1084 | if (child != null) { 1085 | setBlurEffectAlphaForNotificationRow(child as View, alpha) 1086 | } 1087 | } 1088 | } 1089 | } catch (e: Throwable) { 1090 | // Nothing to do. 1091 | } 1092 | } 1093 | } 1094 | 1095 | fun isDefaultLockScreenTheme(): Boolean { 1096 | val MiuiKeyguardUtilsClass = HookUtils.getClass( 1097 | "com.android.keyguard.utils.MiuiKeyguardUtils", 1098 | classLoader 1099 | ) ?: return true 1100 | return XposedHelpers.callStaticMethod( 1101 | MiuiKeyguardUtilsClass, 1102 | "isDefaultLockScreenTheme" 1103 | ) as Boolean 1104 | } 1105 | 1106 | fun setQSControlDetailBackgroundAlpha() { 1107 | val QSControlDetailClass = HookUtils.getClass( 1108 | "com.android.systemui.controlcenter.phone.detail.QSControlDetail", 1109 | classLoader 1110 | ) 1111 | if(QSControlDetailClass != null){ 1112 | XposedHelpers.findAndHookMethod( 1113 | QSControlDetailClass, 1114 | "updateBackground", 1115 | object : XC_MethodHook() { 1116 | override fun afterHookedMethod(param: MethodHookParam) { 1117 | val mDetailContainer = HookUtils.getValueByField(param.thisObject,"mDetailContainer") as View 1118 | if(mDetailContainer.background != null){ 1119 | val smoothRoundDrawable = mDetailContainer.background 1120 | smoothRoundDrawable.alpha = qsControlDetailBackgroundAlpha 1121 | } 1122 | } 1123 | }) 1124 | } 1125 | val ModalQSControlDetailClass = HookUtils.getClass( 1126 | "com.android.systemui.statusbar.notification.modal.ModalQSControlDetail", 1127 | classLoader 1128 | ) 1129 | if(ModalQSControlDetailClass != null){ 1130 | XposedHelpers.findAndHookMethod( 1131 | ModalQSControlDetailClass, 1132 | "updateBackground", 1133 | object : XC_MethodHook() { 1134 | override fun afterHookedMethod(param: MethodHookParam) { 1135 | val mDetailContainer = HookUtils.getValueByField(param.thisObject,"mDetailContainer") as View 1136 | if(mDetailContainer.background != null){ 1137 | val smoothRoundDrawable = mDetailContainer.background 1138 | smoothRoundDrawable.alpha = qsControlDetailBackgroundAlpha 1139 | } 1140 | } 1141 | }) 1142 | } 1143 | 1144 | hookClassInPlugin{classLoader -> 1145 | try { 1146 | val SmoothRoundDrawableClass = XposedHelpers.callMethod( 1147 | classLoader, 1148 | "loadClass", 1149 | "miui.systemui.widget.SmoothRoundDrawable" 1150 | ) ?: return@hookClassInPlugin 1151 | XposedBridge.hookAllMethods( 1152 | SmoothRoundDrawableClass as Class<*>, 1153 | "inflate", 1154 | object : XC_MethodHook() { 1155 | override fun afterHookedMethod(param: MethodHookParam) { 1156 | try { 1157 | val currentDrawable = param.thisObject as Drawable 1158 | currentDrawable.alpha = qsControlDetailBackgroundAlpha 1159 | } catch (e: Throwable) { 1160 | // Do Nothings. 1161 | HookUtils.log(e.message) 1162 | } 1163 | } 1164 | }) 1165 | } catch (e: Throwable) { 1166 | HookUtils.log(e.message) 1167 | } 1168 | } 1169 | } 1170 | 1171 | fun hideControlsPlugin() { 1172 | val MiPlayPluginManagerClass = HookUtils.getClass( 1173 | "com.android.systemui.controlcenter.phone.controls.MiPlayPluginManager", 1174 | classLoader 1175 | ) 1176 | if(MiPlayPluginManagerClass != null){ 1177 | XposedBridge.hookAllMethods( 1178 | MiPlayPluginManagerClass, 1179 | "supportMiPlayAudio", 1180 | object : XC_MethodReplacement() { 1181 | override fun replaceHookedMethod(param: MethodHookParam) { 1182 | param.result = false 1183 | } 1184 | }) 1185 | } 1186 | 1187 | 1188 | hookClassInPlugin{classLoader -> 1189 | try { 1190 | /* 1191 | val MiLinkControllerClass = XposedHelpers.callMethod( 1192 | classLoader, 1193 | "loadClass", 1194 | "miui.systemui.util.MiLinkController" 1195 | ) ?: return@hookClassInPlugin 1196 | XposedBridge.hookAllMethods( 1197 | MiLinkControllerClass as Class<*>, 1198 | "getMiLinkPackageAvailable", 1199 | object : XC_MethodHook() { 1200 | override fun afterHookedMethod(param: MethodHookParam) { 1201 | try { 1202 | param.result = false 1203 | } catch (e: Throwable) { 1204 | // Do Nothings. 1205 | HookUtils.log(e.message) 1206 | } 1207 | } 1208 | }) 1209 | */ 1210 | 1211 | val MiPlayControllerClass = 1212 | XposedHelpers.callMethod( 1213 | classLoader, 1214 | "loadClass", 1215 | "com.android.systemui.MiPlayController" 1216 | ) ?: return@hookClassInPlugin 1217 | XposedBridge.hookAllMethods( 1218 | MiPlayControllerClass as Class<*>, 1219 | "supportMiPlayAudio", 1220 | object : XC_MethodHook() { 1221 | override fun afterHookedMethod(param: MethodHookParam) { 1222 | try { 1223 | param.result = false 1224 | } catch (e: Throwable) { 1225 | // Do Nothings. 1226 | HookUtils.log(e.message) 1227 | } 1228 | } 1229 | }) 1230 | } catch (e: Throwable) { 1231 | HookUtils.log(e.message) 1232 | } 1233 | } 1234 | } 1235 | 1236 | fun enableBlurForMTK() { 1237 | hookClassInPlugin{classLoader -> 1238 | try { 1239 | val VolumeUtilClass = XposedHelpers.callMethod( 1240 | classLoader, 1241 | "loadClass", 1242 | "com.android.systemui.miui.volume.Util" 1243 | ) ?: return@hookClassInPlugin 1244 | VolumeUtilClass as Class<*> 1245 | val allVolumeUtilMethods = VolumeUtilClass.methods 1246 | if (allVolumeUtilMethods.isEmpty()) { 1247 | return@hookClassInPlugin 1248 | } 1249 | allVolumeUtilMethods.forEach { method -> 1250 | if (method.name == "isSupportBlurS") { 1251 | XposedBridge.hookAllMethods( 1252 | VolumeUtilClass, 1253 | "isSupportBlurS", 1254 | object : XC_MethodHook() { 1255 | override fun afterHookedMethod(param: MethodHookParam) { 1256 | try { 1257 | if (param.result is Boolean) { 1258 | param.result = true 1259 | } 1260 | } catch (e: Throwable) { 1261 | // Do Nothings. 1262 | HookUtils.log(e.message) 1263 | } 1264 | } 1265 | }) 1266 | return@hookClassInPlugin 1267 | } 1268 | } 1269 | }catch (e: Throwable) { 1270 | // Do Nothings. 1271 | HookUtils.log(e.message) 1272 | } 1273 | } 1274 | } 1275 | 1276 | fun hookClassInPlugin(afterGetClassLoader: (classLoader: ClassLoader) -> Unit){ 1277 | val PluginHandlerClass = HookUtils.getClass( 1278 | "com.android.systemui.shared.plugins.PluginInstanceManager\$PluginHandler", 1279 | classLoader 1280 | ) 1281 | if (PluginHandlerClass != null) { 1282 | XposedBridge.hookAllMethods( 1283 | PluginHandlerClass, 1284 | "handleLoadPlugin", 1285 | object : XC_MethodHook() { 1286 | override fun afterHookedMethod(param: MethodHookParam) { 1287 | val componentName = param.args[0] 1288 | val className = 1289 | XposedHelpers.callMethod(componentName, "getClassName") as String 1290 | if (className != "miui.systemui.volume.VolumeDialogPlugin") { 1291 | return 1292 | } 1293 | try { 1294 | val pluginContextWrapper = 1295 | HookUtils.getValueByField(param.result ?: return, "mPluginContext") ?: return 1296 | val classLoader = XposedHelpers.callMethod( 1297 | pluginContextWrapper, 1298 | "getClassLoader" 1299 | ) as ClassLoader 1300 | afterGetClassLoader(classLoader) 1301 | } catch (e: Throwable) { 1302 | // Do Nothings. 1303 | HookUtils.log(e.message) 1304 | } 1305 | } 1306 | }) 1307 | return 1308 | } 1309 | 1310 | val PluginActionManagerClass = HookUtils.getClass( 1311 | "com.android.systemui.shared.plugins.PluginActionManager", 1312 | classLoader 1313 | ) 1314 | if (PluginActionManagerClass != null) { 1315 | XposedBridge.hookAllMethods( 1316 | PluginActionManagerClass, 1317 | "loadPluginComponent", 1318 | object : XC_MethodHook() { 1319 | override fun afterHookedMethod(param: MethodHookParam) { 1320 | val componentName = param.args[0] 1321 | val className = 1322 | XposedHelpers.callMethod(componentName, "getClassName") as String 1323 | if (className != "miui.systemui.volume.VolumeDialogPlugin") { 1324 | return 1325 | } 1326 | try { 1327 | val pluginContextWrapper = 1328 | HookUtils.getValueByField(param.result ?: return, "mPluginContext") 1329 | ?: return 1330 | val classLoader = XposedHelpers.callMethod( 1331 | pluginContextWrapper, 1332 | "getClassLoader" 1333 | ) as ClassLoader 1334 | afterGetClassLoader(classLoader) 1335 | } catch (e: Throwable) { 1336 | // Do Nothings. 1337 | HookUtils.log(e.message) 1338 | } 1339 | } 1340 | }) 1341 | return 1342 | } 1343 | } 1344 | 1345 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/models/BaseBlurBackgroundModel.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.models 2 | 3 | // 基础模糊配置 4 | data class BaseBlurBackgroundModel( 5 | var enable: Boolean = true, 6 | var blurRadius: Int = 80, 7 | var backgroundColor: String = "#FF000000" // 统一使用ARGB Hex 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/models/ConfigModel.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.models 2 | 3 | // 总配置 4 | data class ConfigModel( 5 | var versionCode: Int = 0, 6 | var systemUI: SystemUIModel = SystemUIModel(), 7 | var personalAssistant: PersonalAssistantModel = PersonalAssistantModel(), 8 | var securityCenter: SecurityCenterModel = SecurityCenterModel(), 9 | var miuiHome: MiuiHomeModel = MiuiHomeModel() 10 | ) 11 | -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/models/MiuiHomeModel.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.models 2 | 3 | // 基础模糊配置 4 | data class MiuiHomeModel( 5 | var enableShortcutBackgroundBlur: Boolean = false, 6 | var shortcutMenuBackgroundAlpha: Int = 255 7 | ) 8 | -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/models/NotificationBlurModel.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.models 2 | 3 | // 通知 4 | data class NotificationBlurModel( 5 | var enable: Boolean = true, 6 | var cornerRadius: Int = 48, 7 | var blurRadius: Int = 60, 8 | var blurBackgroundAlpha: Int = 170, 9 | var defaultBackgroundAlpha: Int = 200 10 | ) 11 | -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/models/PersonalAssistantModel.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.models 2 | 3 | // 智能助理 4 | data class PersonalAssistantModel( 5 | var background: BaseBlurBackgroundModel = BaseBlurBackgroundModel( 6 | true, 7 | 80, 8 | "#1E000000" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/models/QuickSettingModel.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.models 2 | 3 | // 快捷设置 4 | data class QuickSettingModel( 5 | var hideMiPlayEntry:Boolean = false, 6 | var controlDetailBackgroundAlpha: Int = 255, 7 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/models/SecurityCenterModel.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.models 2 | 3 | // 手机管家 4 | data class SecurityCenterModel( 5 | var dockBackground: BaseBlurBackgroundModel = BaseBlurBackgroundModel( 6 | true, 7 | 60, 8 | "#3C000000" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/models/SystemUIModel.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.models 2 | 3 | // 系统UI 4 | data class SystemUIModel( 5 | var notification: NotificationBlurModel = NotificationBlurModel(), 6 | val quickSetting: QuickSettingModel = QuickSettingModel() 7 | ) 8 | -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/utils/ColorUtils.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.utils 2 | 3 | import android.graphics.Color 4 | 5 | object ColorUtils { 6 | // color转换不可靠,加一个默认值 7 | val defaultReturnColor = Color.argb(50, 0, 0, 0) 8 | 9 | fun colorToHex(color: Int): String { 10 | var originalColor = Color.valueOf(defaultReturnColor) 11 | try { 12 | originalColor = Color.valueOf(color) 13 | } catch (e: Throwable) { 14 | // 颜色转换失败 15 | } 16 | val alpha = (originalColor.alpha() * 255).toInt() 17 | val red = (originalColor.red() * 255).toInt() 18 | val green = (originalColor.green() * 255).toInt() 19 | val blue = (originalColor.blue() * 255).toInt() 20 | val alphaHex = if (alpha <= 15) { 21 | '0' + alpha.toString() 22 | } else { 23 | alpha.toString(16) 24 | } 25 | val redHex = if (red <= 15) { 26 | '0' + red.toString() 27 | } else { 28 | red.toString(16) 29 | } 30 | val greenHex = if (green <= 15) { 31 | '0' + green.toString() 32 | } else { 33 | green.toString(16) 34 | } 35 | val blueHex = if (blue <= 15) { 36 | '0' + blue.toString() 37 | } else { 38 | blue.toString(16) 39 | } 40 | return "#$alphaHex$redHex$greenHex$blueHex".uppercase() 41 | } 42 | 43 | fun hexToColor(hexString: String): Int { 44 | try { 45 | return Color.parseColor(hexString) 46 | } catch (e: Throwable) { 47 | return defaultReturnColor 48 | } 49 | } 50 | 51 | fun isDarkColor(color: Int): Boolean { 52 | val darkness = 53 | 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255 54 | return darkness > 0.5 55 | } 56 | 57 | fun addAlphaForColor(color: Int, alpha: Int): Int { 58 | return Color.valueOf(Color.red(color) / 255f,Color.green(color)/ 255f,Color.blue(color)/ 255f,alpha/ 255f).toArgb() 59 | } 60 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/utils/HookUtils.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.utils 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.Drawable 5 | import android.view.View 6 | import de.robv.android.xposed.XC_MethodReplacement 7 | import de.robv.android.xposed.XposedBridge 8 | import de.robv.android.xposed.XposedHelpers 9 | 10 | object HookUtils { 11 | fun log(content: Any?) { 12 | XposedBridge.log("[WINI]" + content) 13 | } 14 | 15 | fun dip2px(context: Context, dpValue: Float): Float { 16 | val scale = context.resources.displayMetrics.density 17 | return dpValue * scale + 0.5f 18 | } 19 | 20 | fun getClass(className: String, classLoader: ClassLoader): Class<*>? { 21 | val result = XposedHelpers.findClassIfExists( 22 | className, 23 | classLoader 24 | ) 25 | if (result == null) { 26 | log("'" + className + "' is NOT found.") 27 | } 28 | return result 29 | } 30 | 31 | fun replaceMethodResult( 32 | className: String, 33 | classLoader: ClassLoader, 34 | methodName: String, 35 | result: Any, 36 | vararg args: Any? 37 | ) { 38 | try { 39 | XposedHelpers.findAndHookMethod( 40 | className, 41 | classLoader, 42 | methodName, 43 | *args, 44 | XC_MethodReplacement.returnConstant(result) 45 | ) 46 | } catch (e: Throwable) { 47 | log(e.message) 48 | } 49 | } 50 | 51 | fun getValueByField(target: Any, fieldName: String, clazz: Class<*>? = null): Any? { 52 | var targetClass = clazz 53 | if (targetClass == null) { 54 | targetClass = target.javaClass 55 | } 56 | return try { 57 | val field = targetClass.getDeclaredField(fieldName) 58 | field.isAccessible = true 59 | field.get(target) 60 | } catch (e: Throwable) { 61 | if (targetClass.superclass == null) { 62 | null 63 | } else { 64 | getValueByField(target, fieldName, targetClass.superclass) 65 | } 66 | } 67 | } 68 | 69 | fun createBlurDrawable( 70 | view: View, 71 | blurRadius: Int, 72 | cornerRadius: Int, 73 | color: Int? = null 74 | ): Drawable? { 75 | try { 76 | val mViewRootImpl = XposedHelpers.callMethod( 77 | view, 78 | "getViewRootImpl" 79 | ) ?: return null 80 | val blurDrawable = XposedHelpers.callMethod( 81 | mViewRootImpl, 82 | "createBackgroundBlurDrawable" 83 | ) as Drawable 84 | XposedHelpers.callMethod(blurDrawable, "setBlurRadius", blurRadius) 85 | XposedHelpers.callMethod(blurDrawable, "setCornerRadius", cornerRadius) 86 | if (color != null) { 87 | XposedHelpers.callMethod( 88 | blurDrawable, 89 | "setColor", 90 | color 91 | ) 92 | } 93 | return blurDrawable 94 | } catch (e: Throwable) { 95 | log("Create BlurDrawable Error:" + e) 96 | return null 97 | } 98 | } 99 | 100 | fun isBlurDrawable(drawable: Drawable?): Boolean { 101 | // 不够严谨,可以用 102 | if (drawable == null) { 103 | return false 104 | } 105 | val drawableClassName = drawable.javaClass.name 106 | return drawableClassName.contains("BackgroundBlurDrawable") 107 | } 108 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/houkyo/wini/utils/Storage.kt: -------------------------------------------------------------------------------- 1 | package cn.houkyo.wini.utils 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import cn.houkyo.wini.models.ConfigModel 6 | import com.google.gson.Gson 7 | 8 | object Storage { 9 | const val DATA_FILENAME = "WINIConfig" 10 | const val CONFIG_JSON = "config" 11 | 12 | @SuppressLint("WorldWriteableFiles") 13 | fun saveData(key: String, value: String, context: Context) { 14 | try { 15 | val sharedPreferences = 16 | context.getSharedPreferences(DATA_FILENAME, Context.MODE_WORLD_WRITEABLE) 17 | val editor = sharedPreferences.edit() 18 | editor.putString(key, value) 19 | editor.apply() 20 | } catch (e: Throwable) { 21 | // 也许是模块尚未加载 22 | } 23 | } 24 | 25 | @SuppressLint("WorldReadableFiles") 26 | fun getData(key: String, defaultValue: String, context: Context): String { 27 | try { 28 | val sharedPreferences = 29 | context.getSharedPreferences(DATA_FILENAME, Context.MODE_WORLD_READABLE) 30 | return sharedPreferences.getString(key, defaultValue) ?: return defaultValue 31 | } catch (e: Throwable) { 32 | // 也许是模块尚未加载 33 | } 34 | return defaultValue 35 | } 36 | 37 | fun getConfig(context: Context): ConfigModel { 38 | val configJsonString = getData(CONFIG_JSON, "{\"versionCode\":0}", context) 39 | return getConfig(configJsonString) 40 | } 41 | 42 | fun getConfig(configJsonString: String): ConfigModel { 43 | return Gson().fromJson(configJsonString, ConfigModel::class.java) 44 | } 45 | 46 | fun saveConfig(config: ConfigModel, context: Context) { 47 | val configString = Gson().toJson(config) 48 | saveData(CONFIG_JSON, configString, context) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/drawable-hdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/drawable-mdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/drawable-xhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/drawable-xxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 17 | 20 | 23 | 26 | 27 | 28 | 29 | 30 | 33 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | android 5 | cn.houkyo.wini 6 | com.android.systemui 7 | com.miui.home 8 | com.miui.personalassistant 9 | com.miui.securitycenter 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | WINI 3 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 29 | 30 | 36 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext { 4 | kotlinVersion= "1.8.0" 5 | } 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:7.4.2' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | task clean(type: Delete) { 20 | delete rootProject.buildDir 21 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ouhoukyo/WINI/9c72609606e0bf6534a0b51075a69cd4e7d10d30/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Dec 14 15:28:54 CST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | repositories { 4 | google() 5 | mavenCentral() 6 | jcenter() // Warning: this repository is going to shut down soon 7 | } 8 | } 9 | rootProject.name = "WINI" 10 | include ':app' 11 | --------------------------------------------------------------------------------