├── .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 |
--------------------------------------------------------------------------------