refactor: migrate and update to YukiHookAPI 1.3.0

This commit is contained in:
2025-06-25 23:25:23 +08:00
parent c5ed4f720f
commit 8a775b3fdd
10 changed files with 271 additions and 222 deletions

View File

@@ -81,6 +81,8 @@ dependencies {
compileOnly(de.robv.android.xposed.api)
implementation(com.highcapable.yukihookapi.api)
ksp(com.highcapable.yukihookapi.ksp.xposed)
implementation(com.highcapable.kavaref.kavaref.core)
implementation(com.highcapable.kavaref.kavaref.extension)
implementation(com.fankes.projectpromote.project.promote)
implementation(com.github.topjohnwu.libsu.core)
implementation(com.github.duanhong169.drawabletoolbox)

View File

@@ -30,7 +30,6 @@ import android.app.WallpaperManager
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Outline
import android.graphics.drawable.Drawable
@@ -45,7 +44,9 @@ import android.widget.ImageView
import android.widget.RemoteViews
import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import androidx.core.graphics.scale
import androidx.core.view.children
import androidx.core.view.isNotEmpty
import androidx.core.view.isVisible
import androidx.core.view.setPadding
import com.fankes.miui.notify.R
@@ -76,28 +77,19 @@ import com.fankes.miui.notify.utils.tool.ActivationPromptTool
import com.fankes.miui.notify.utils.tool.BitmapCompatTool
import com.fankes.miui.notify.utils.tool.IconAdaptationTool
import com.fankes.miui.notify.utils.tool.SystemUITool
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.condition.MethodCondition
import com.highcapable.kavaref.extension.VariousClass
import com.highcapable.kavaref.extension.classOf
import com.highcapable.kavaref.extension.isSubclassOf
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.constructor
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.extends
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.hasMethod
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.param.HookParam
import com.highcapable.yukihookapi.hook.type.android.ContextClass
import com.highcapable.yukihookapi.hook.type.android.DrawableClass
import com.highcapable.yukihookapi.hook.type.android.ImageViewClass
import com.highcapable.yukihookapi.hook.type.android.NotificationClass
import com.highcapable.yukihookapi.hook.type.android.RemoteViewsClass
import com.highcapable.yukihookapi.hook.type.android.StatusBarNotificationClass
import com.highcapable.yukihookapi.hook.type.java.ArrayListClass
import com.highcapable.yukihookapi.hook.type.java.BooleanClass
import com.highcapable.yukihookapi.hook.type.java.BooleanType
import com.highcapable.yukihookapi.hook.type.java.FloatType
import com.highcapable.yukihookapi.hook.type.java.IntType
import top.defaults.drawabletoolbox.DrawableBuilder
/**
@@ -238,13 +230,18 @@ object SystemUIHooker : YukiBaseHooker() {
* @return [Context] or null
*/
private val globalContext
get() = SystemUIApplicationClass?.method { name = "getContext" }?.ignored()?.get()?.invoke<Context?>() ?: appContext
get() = SystemUIApplicationClass?.resolve()?.optional(silent = true)
?.firstMethodOrNull { name = "getContext" }
?.invoke<Context?>() ?: appContext
/**
* 是否为 MIUI 样式通知栏 - 旧版 - 新版一律返回 false
* @return [Boolean]
*/
private val isShowMiuiStyle get() = NotificationUtilClass.method { name = "showMiuiStyle" }.ignored().get().boolean()
private val isShowMiuiStyle
get() = NotificationUtilClass.resolve().optional(silent = true)
.firstMethodOrNull { name = "showMiuiStyle" }
?.invoke<Boolean>() == true
/**
* 是否没有单独的 MIUI 通知栏样式
@@ -360,14 +357,18 @@ object SystemUIHooker : YukiBaseHooker() {
*/
private fun isGrayscaleIcon(context: Context, drawable: Drawable) =
if (ConfigData.isEnableColorIconCompat.not()) safeOfFalse {
ContrastColorUtilClass.let {
it.method {
ContrastColorUtilClass.resolve()
.optional(silent = true)
.let {
it.firstMethodOrNull {
name = "isGrayscaleIcon"
param(DrawableClass)
}.get(it.method {
parameters(Drawable::class)
}?.of(
it.firstMethodOrNull {
name = "getInstance"
param(ContextClass)
}.get().invoke(context)).boolean(drawable)
parameters(Context::class)
}?.invoke(context)
)?.invokeQuietly<Boolean>(drawable) == true
}
} else BitmapCompatTool.isGrayscaleDrawable(drawable)
@@ -408,10 +409,10 @@ object SystemUIHooker : YukiBaseHooker() {
/** 刷新状态栏小图标 */
private fun refreshStatusBarIcons() = runInSafe {
StatusBarIconViewClass.field { name = "mNotification" }.also { result ->
StatusBarIconViewClass.resolve().optional(silent = true).firstFieldOrNull { name = "mNotification" }?.also { result ->
notificationIconContainer?.children?.forEach {
/** 得到通知实例 */
val nf = result.get(it).cast<StatusBarNotification>() ?: return
val nf = result.of(it).get<StatusBarNotification>() ?: return
/** 刷新状态栏图标 */
compatStatusIcon(it.context, nf, nf.notification.smallIcon.loadDrawable(it.context)).also { pair ->
pair.first.let { e -> (it as? ImageView?)?.setImageDrawable(e) }
@@ -423,14 +424,13 @@ object SystemUIHooker : YukiBaseHooker() {
/** 刷新通知小图标 */
private fun refreshNotificationIcons() = runInSafe {
val updateNotificationMethodName = "updateNotificationsOnDensityOrFontScaleChanged"
if (StatusBarNotificationPresenterClass.hasMethod { name = updateNotificationMethodName })
notificationPresenter?.current(ignored = true)?.method {
val result = notificationPresenter?.resolve()?.optional(silent = true)?.firstMethodOrNull {
name = updateNotificationMethodName
emptyParam()
}?.call()
else settingsManager?.current {
field { name = "notifStyle" }.set(-100)
method { name = "onNotifStyleChanged" }.call()
emptyParameters()
}?.invoke()
if (result == null) settingsManager?.resolve()?.optional(silent = true)?.apply {
firstFieldOrNull { name = "notifStyle" }?.set(-100)
firstMethodOrNull { name = "onNotifStyleChanged" }?.invoke()
}
}
@@ -645,7 +645,7 @@ object SystemUIHooker : YukiBaseHooker() {
*/
private fun ImageView.isGrayscaleIcon(): Boolean {
/** 获取 [StatusBarNotification] 实例 */
val notifyInstance = current().field { name = "mNotification" }.cast<StatusBarNotification>() ?: return false
val notifyInstance = resolve().optional().firstFieldOrNull { name = "mNotification" }?.get<StatusBarNotification>() ?: return false
/** 获取通知小图标 */
val iconDrawable = notifyInstance.notification?.smallIcon?.loadDrawable(context) ?: return false
@@ -669,7 +669,7 @@ object SystemUIHooker : YukiBaseHooker() {
* @param animColor 动画过渡颜色
*/
private fun updateStatusBarIconsColor(container: ViewGroup, isDarkIconMode: Boolean = this.isDarkIconMode, animColor: Int? = null) {
if (container.childCount > 0) container.children.forEach { iconView ->
if (container.isNotEmpty()) container.children.forEach { iconView ->
if (iconView !is ImageView) return@forEach
updateStatusBarIconColor(iconView, isDarkIconMode, animColor)
}
@@ -699,12 +699,15 @@ object SystemUIHooker : YukiBaseHooker() {
* @param container 当前 [NotificationIconContainerClass] 的实例
*/
private fun updateStatusBarIconsAlpha(container: ViewGroup) {
val iconStatesMap = container.current().field { name = "mIconStates" }.cast<HashMap<View, Any>>()
if (container.childCount > 0) container.children.forEach { iconView ->
val iconStatesMap = container.resolve().optional().firstFieldOrNull { name = "mIconStates" }?.get<HashMap<View, Any>>()
if (container.isNotEmpty()) container.children.forEach { iconView ->
if (iconView !is ImageView) return@forEach
val iconAlpha = if (iconView.isGrayscaleIcon()) statusBarIconAlpha else 1f
iconView.alpha = iconAlpha
iconStatesMap?.get(iconView)?.current()?.field { name { it == "alpha" || it == "mAlpha" }; superClass() }?.set(iconAlpha)
iconStatesMap?.get(iconView)?.resolve()?.optional()?.firstFieldOrNull {
name { it == "alpha" || it == "mAlpha" }
superclass()
}?.set(iconAlpha)
}
}
@@ -713,7 +716,7 @@ object SystemUIHooker : YukiBaseHooker() {
* @param instance 被 Hook 的 Method 的实例
*/
private fun hookStatusBarMaxStaticIcons(instance: Any) {
val maxStaticIconsField = NotificationIconContainerClass.field {
val maxStaticIconsResolver = NotificationIconContainerClass.resolve().optional().firstFieldOrNull {
name {
/** 旧版名称 */
val oldVersion = it == "mMaxStaticIcons"
@@ -725,22 +728,22 @@ object SystemUIHooker : YukiBaseHooker() {
val newVersion = it == "mMaxIcons"
oldVersion || oldVersion2 || newVersion
}
}.get(instance)
}?.of(instance) ?: return
if (statusBarMaxStaticIcons == -1 ||
/** 系统设置内修改,模块同步更新 */
moduleLastSetStatusBarMaxStaticIcons != maxStaticIconsField.int()) {
statusBarMaxStaticIcons = maxStaticIconsField.int()
moduleLastSetStatusBarMaxStaticIcons != maxStaticIconsResolver.get<Int>()) {
statusBarMaxStaticIcons = maxStaticIconsResolver.get<Int>() ?: return
}
if (!ConfigData.isEnableLiftedStatusIconCount) {
maxStaticIconsField.set(statusBarMaxStaticIcons)
maxStaticIconsResolver.set(statusBarMaxStaticIcons)
return
}
/** 解除状态栏通知图标个数限制 */
if (isShowNotificationIcons) {
moduleLastSetStatusBarMaxStaticIcons = ConfigData.liftedStatusIconCount.let { if (it in 0..100) it else 5 }
maxStaticIconsField.set(moduleLastSetStatusBarMaxStaticIcons)
maxStaticIconsResolver.set(moduleLastSetStatusBarMaxStaticIcons)
} else {
maxStaticIconsField.set(0)
maxStaticIconsResolver.set(0)
moduleLastSetStatusBarMaxStaticIcons = 0
}
}
@@ -754,8 +757,8 @@ object SystemUIHooker : YukiBaseHooker() {
if (isNotHasAbsoluteMiuiStyle && isShowMiuiStyle) return
/** 获取小图标 */
val iconImageView = NotificationHeaderViewWrapperClass
.field { name = "mIcon" }.get(wrapper).cast<ImageView>() ?: return
val iconImageView = NotificationHeaderViewWrapperClass.resolve().optional()
.firstFieldOrNull { name = "mIcon" }?.of(wrapper)?.get<ImageView>() ?: return
/** 获取 [ExpandableNotificationRowClass] */
val rowPair = wrapper.getRowPair()
@@ -788,16 +791,15 @@ object SystemUIHooker : YukiBaseHooker() {
* 从父类中得到 mRow 变量 - [ExpandableNotificationRowClass]
* 获取其中的得到通知方法
*/
val row = NotificationViewWrapperClass.field {
val row = NotificationViewWrapperClass.resolve().optional().firstFieldOrNull {
name = "mRow"
}.get(this).any()?.also {
isExpanded = ExpandableNotificationRowClass.method {
}?.of(this)?.get()?.also {
isExpanded = ExpandableNotificationRowClass.resolve().optional().firstMethodOrNull {
name = "isExpanded"
param(BooleanType)
returnType = BooleanType
}.get(it).boolean(false)
}
return Pair(isExpanded, row)
parameters(Boolean::class)
returnType = Boolean::class
}?.of(it)?.invoke<Boolean>(false) == true
}; return isExpanded to row
}
/**
@@ -805,15 +807,18 @@ object SystemUIHooker : YukiBaseHooker() {
* @return [StatusBarNotification] or null
*/
private fun Any?.getSbn() =
ExpandableNotificationRowClass
.field { name = "mEntry" }
.get(this)
.current(ignored = true)
?.field { name = "mSbn" }
?.cast<StatusBarNotification>()
?: ExpandableNotificationRowClass
.method { name = "getStatusBarNotification" }
.get(this).invoke<StatusBarNotification>()
ExpandableNotificationRowClass.resolve()
.optional(silent = true)
.firstFieldOrNull {
name = "mEntry"
}?.of(this)?.get()?.resolve()
?.optional(silent = true)
?.firstFieldOrNull { name = "mSbn" }
?.get<StatusBarNotification>()
?: ExpandableNotificationRowClass.resolve()
.optional(silent = true)
.firstMethodOrNull { name = "getStatusBarNotification" }
?.of(this)?.invoke<StatusBarNotification>()
/**
* 根据当前 [ImageView] 的父布局克隆一个新的 [ImageView]
@@ -916,20 +921,19 @@ object SystemUIHooker : YukiBaseHooker() {
/** 注入 MIUI 自己增加的一个工具类 */
NotificationUtilClass.apply {
/** 强制回写系统的状态栏图标样式为原生 */
method {
resolve().optional().method {
name { it == "shouldSubstituteSmallIcon" || it == "shouldSubstituteSmallIconForStatusBarNotification" }
param { it[0] extends StatusBarNotificationClass }
parameters { it.first() isSubclassOf StatusBarNotification::class }
}.hookAll().replaceToFalse()
var isUseLegacy = false
/**
* 强制修改 getCustomAppIcon 获取的图标为 smallIcon
* 部分系统没有 "getCustomAppIcon" 这个方法 - 所以直接忽略
*/
if (hasMethod { name = "getCustomAppIcon" })
method {
resolve().optional(silent = true).firstMethodOrNull {
name = "getCustomAppIcon"
param(NotificationClass, ContextClass)
}.hook().after {
parameters(Notification::class, Context::class)
}?.hook()?.after {
val nf = args().first().cast<Notification>()
val context = args(index = 1).cast<Context>()
val iconBitmap = nf?.smallIcon?.loadDrawable(context)?.toBitmap()
@@ -941,20 +945,24 @@ object SystemUIHooker : YukiBaseHooker() {
* 强制回写系统的状态栏图标样式为原生
* 部分系统没有 "getSmallIcon" 这个方法 - 所以直接忽略
*/
if (hasMethod { name = "getSmallIcon" })
method {
val getSmallIconCondition1 = MethodCondition<Any>().apply {
name = "getSmallIcon"
param { it[0] extends StatusBarNotificationClass && it[1] == IntType }
}.remedys {
method {
name = "getSmallIcon"
param(ExpandedNotificationClass)
parameters { it[0] isSubclassOf StatusBarNotification::class && it[1] == classOf<Int>() }
}
method {
val getSmallIconCondition2 = MethodCondition<Any>().apply {
name = "getSmallIcon"
param { it[0] == ContextClass && it[1] extends StatusBarNotificationClass }
}.onFind { isUseLegacy = true }
}.hook().after {
parameters(ExpandedNotificationClass)
}
val getSmallIconCondition3 = MethodCondition<Any>().apply {
name = "getSmallIcon"
parameters { it[0] == classOf<Context>() && it[1] isSubclassOf StatusBarNotification::class }
}
val getSmallIconResolver = resolve().optional(silent = true)
val getSmallIcon = getSmallIconResolver.firstMethodOrNull(getSmallIconCondition1)
?: getSmallIconResolver.firstMethodOrNull(getSmallIconCondition2)
?: getSmallIconResolver.firstMethodOrNull(getSmallIconCondition3)
?.also { isUseLegacy = true }
getSmallIcon?.hook()?.after {
(globalContext ?: args().first().cast())?.also { context ->
val expandedNf = args(if (isUseLegacy) 1 else 0).cast<StatusBarNotification?>()
/** Hook 状态栏小图标 */
@@ -967,20 +975,23 @@ object SystemUIHooker : YukiBaseHooker() {
}
}
/** 焦点通知深色模式切换点 */
FocusedNotifPromptViewClass?.apply {
method {
FocusedNotifPromptViewClass?.resolve()?.optional()?.apply {
firstMethodOrNull {
name = "onDarkChanged"
param(ArrayListClass, FloatType, IntType, IntType, IntType, BooleanType)
}.hook().after {
parameters(ArrayList::class, Float::class, Int::class, Int::class, Int::class, Boolean::class)
}?.hook()?.after {
val isDark = args(index = 1).float()
val mIcon = field { name = "mIcon" }.get(instance)
val mIcon = firstFieldOrNull { name = "mIcon" }?.of(instance)?.get()
if (ConfigData.isEnableModuleLog)
YLog.debug("FocusedNotifPromptView DEBUG $isDark ${mIcon.any()}")
mIcon.current()?.superClass()?.method { name = "setColorFilter" }?.call(if (isDark <= 0.5f) Color.WHITE else Color.BLACK)
YLog.debug("FocusedNotifPromptView DEBUG $isDark $mIcon")
mIcon?.resolve()?.optional()?.firstMethodOrNull {
name = "setColorFilter"
superclass()
}?.invoke(if (isDark <= 0.5f) Color.WHITE else Color.BLACK)
}
}
/** 去他妈的焦点通知彩色图标 */
FocusUtilsClass?.apply {
FocusUtilsClass?.resolve()?.optional()?.apply {
fun HookParam.hookTickerDarkIcon(isDark: Boolean) {
(globalContext ?: args().first().cast())?.also { context ->
val expandedNf = args().first().cast<StatusBarNotification?>()
@@ -992,37 +1003,33 @@ object SystemUIHooker : YukiBaseHooker() {
iconDrawable = small?.loadDrawable(context)
).also { pair ->
val originalBitmap = pair.first?.toBitmap()
val bitmap = originalBitmap?.let { Bitmap.createScaledBitmap(it, 50, 50, true) }
val bitmap = originalBitmap?.scale(50, 50)
result = Icon.createWithBitmap(bitmap).apply { if (pair.second) setTint(if (isDark) Color.BLACK else Color.WHITE) }
}
}
}
method {
firstMethodOrNull {
name = "getStatusBarTickerDarkIcon"
param(StatusBarNotificationClass)
}.remedys {
method {
name = "getStatusBarTickerDarkIcon"
param(ExpandedNotificationClass)
parameters {
(it.first() == classOf<StatusBarNotification>() ||
it.first() == ExpandedNotificationClass) && it.size == 1
}
}.hook().after { hookTickerDarkIcon(isDark = true) }
method {
}?.hook()?.after { hookTickerDarkIcon(isDark = true) }
firstMethodOrNull {
name = "getStatusBarTickerIcon"
param(StatusBarNotificationClass)
}.remedys {
method {
name = "getStatusBarTickerIcon"
param(ExpandedNotificationClass)
parameters {
(it.first() == classOf<StatusBarNotification>() ||
it.first() == ExpandedNotificationClass) && it.size == 1
}
}.hook().after { hookTickerDarkIcon(isDark = false) }
}?.hook()?.after { hookTickerDarkIcon(isDark = false) }
}
/** 注入状态栏通知图标实例 */
StatusBarIconViewClass.method {
StatusBarIconViewClass.resolve().optional().firstMethodOrNull {
name = "updateIconColor"
emptyParam()
}.ignored().hook().after {
emptyParameters()
}?.hook()?.after {
val iconView = instance<ImageView>()
val expandedNf = iconView.current().field { name = "mNotification" }.cast<StatusBarNotification>()
val expandedNf = iconView.resolve().optional().firstFieldOrNull { name = "mNotification" }?.get<StatusBarNotification>()
/** Hook 状态栏小图标 */
compatStatusIcon(
context = iconView.context,
@@ -1039,13 +1046,13 @@ object SystemUIHooker : YukiBaseHooker() {
* 在 Android 15 中,这个类被移除变成了 `interface`,所以判断并跳过 Hook 行为
*/
val isPlaceholder = NotificationIconAreaControllerClass?.isInterface == true
if (!isPlaceholder) NotificationIconAreaControllerClass?.apply {
if (!isPlaceholder) NotificationIconAreaControllerClass?.resolve()?.optional()?.apply {
/** Hook 深色图标模式改变 */
method {
firstMethodOrNull {
name = "onDarkChanged"
paramCount { it > 0 }
}.hook().after {
field { name = "mNotificationIcons" }.get(instance).cast<ViewGroup>()?.also {
parameterCount { it > 0 }
}?.hook()?.after {
firstFieldOrNull { name = "mNotificationIcons" }?.of(instance)?.get<ViewGroup>()?.also {
/** 重新设置通知图标容器实例 */
notificationIconContainer = it
when (args(index = 1).float()) {
@@ -1065,7 +1072,7 @@ object SystemUIHooker : YukiBaseHooker() {
method {
name { it == "updateNotificationIcons" || it.startsWith("onChanged") }
}.hookAll().after {
field { name = "mNotificationIcons" }.get(instance).cast<ViewGroup>()?.also {
firstFieldOrNull { name = "mNotificationIcons" }?.of(instance)?.get<ViewGroup>()?.also {
/** 重新设置通知图标容器实例 */
notificationIconContainer = it
updateStatusBarIconsColor(it)
@@ -1073,11 +1080,11 @@ object SystemUIHooker : YukiBaseHooker() {
delayedRun { updateStatusBarIconsColor(it) }
}
}
} else MiuiClockClass?.apply {
method {
} else MiuiClockClass?.resolve()?.optional()?.apply {
firstMethodOrNull {
name = "onDarkChanged"
paramCount { it > 4 }
}.hook().after {
parameterCount { it > 4 }
}?.hook()?.after {
notificationIconContainer?.let {
when (args(index = 1).float()) {
1.0f -> {
@@ -1094,24 +1101,22 @@ object SystemUIHooker : YukiBaseHooker() {
}
}
/** 注入状态栏通知图标实例 */
StatusBarIconViewClass.apply {
method {
StatusBarIconViewClass.resolve().optional().apply {
firstMethodOrNull {
name = "setNotification"
param(StatusBarNotificationClass)
}.remedys {
method {
name = "setNotification"
param(ExpandedNotificationClass)
parameters {
(it.first() == classOf<StatusBarNotification>() ||
it.first() == ExpandedNotificationClass) && it.size == 1
}
}.hook().after {
}?.hook()?.after {
/** 注册壁纸颜色监听 */
if (args().first().any() != null) instance<ImageView>().also { registerWallpaperColorChanged(it) }
}
/** Hook 深色图标模式改变 */
if (isPlaceholder) method {
if (isPlaceholder) firstMethodOrNull {
name = "onDarkChanged"
paramCount { it > 4 }
}.hook().after {
parameterCount { it > 4 }
}?.hook()?.after {
val self = instance<ImageView>()
when (args(index = 1).float()) {
1.0f -> {
@@ -1127,24 +1132,24 @@ object SystemUIHooker : YukiBaseHooker() {
}
}
/** 注入设置管理器实例 */
SettingsManagerClass?.constructor()?.hookAll()?.after { settingsManager = instance }
SettingsManagerClass?.resolve()?.optional()?.constructor {}?.hookAll()?.after { settingsManager = instance }
/** 注入通知控制器实例 */
StatusBarNotificationPresenterClass.constructor().hookAll().after { notificationPresenter = instance }
StatusBarNotificationPresenterClass.resolve().optional().constructor {}.hookAll().after { notificationPresenter = instance }
/** 注入状态栏通知图标容器实例 */
NotificationIconContainerClass.apply {
method {
resolve().optional().firstMethodOrNull {
name = "applyIconStates"
}.hook().after { updateStatusBarIconsAlpha(instance()) }
method {
}?.hook()?.after { updateStatusBarIconsAlpha(instance()) }
resolve().optional().firstMethodOrNull {
name = "resetViewStates"
}.hook().after {
}?.hook()?.after {
updateStatusBarIconsAlpha(instance())
/** HyperOS 系统设置修改通知图标个数触发此方法 */
hookStatusBarMaxStaticIcons(instance)
}
method {
resolve().optional().firstMethodOrNull {
name { it == "calculateIconTranslations" || it == "calculateIconXTranslations" }
}.hook().after {
}?.hook()?.after {
/** 缓存实例 */
notificationIconContainer = instance<ViewGroup>()
/** 修复部分开发版状态栏图标只能显示一个的问题 */
@@ -1154,38 +1159,38 @@ object SystemUIHooker : YukiBaseHooker() {
}
}
/** 旧版方法 (A13 MIUI) - 新版不存在 */
method {
resolve().optional(silent = true).firstMethodOrNull {
name = "updateState"
}.ignored().hook().before { hookStatusBarMaxStaticIcons(instance) }
}?.hook()?.before { hookStatusBarMaxStaticIcons(instance) }
/** 新版方法 (A14 MIUI14 / A14 HyperOS) - 旧版不存在 */
method {
resolve().optional(silent = true).firstMethodOrNull {
name = "onMeasure"
}.ignored().hook().before { hookStatusBarMaxStaticIcons(instance) }
}?.hook()?.before { hookStatusBarMaxStaticIcons(instance) }
/** 旧版方法 - 新版不存在 */
method {
resolve().optional(silent = true).firstMethodOrNull {
name = "setMaxStaticIcons"
param(IntType)
}.ignored().hook().before { isShowNotificationIcons = args().first().int() > 0 }
parameters(Int::class)
}?.hook()?.before { isShowNotificationIcons = args().first().int() > 0 }
/** 旧版方法 - 新版 (A15 HyperOS) 不存在 */
method {
resolve().optional(silent = true).firstMethodOrNull {
name = "miuiShowNotificationIcons"
param(BooleanType)
}.ignored().hook().before { isShowNotificationIcons = args().first().boolean() }
parameters(Boolean::class)
}?.hook()?.before { isShowNotificationIcons = args().first().boolean() }
}
/** 注入原生通知包装纸实例 */
NotificationHeaderViewWrapperClass.apply {
method {
NotificationHeaderViewWrapperClass.resolve().optional().apply {
firstMethodOrNull {
name { it == "resolveHeaderViews" || it == "handleHeaderViews" || it == "resolveViews" }
}.hook().after { hookNotificationViewWrapper(instance) }
method {
}?.hook()?.after { hookNotificationViewWrapper(instance) }
firstMethodOrNull {
name = "onContentUpdated"
}.hook().after { hookNotificationViewWrapper(instance) }
}?.hook()?.after { hookNotificationViewWrapper(instance) }
}
/** 修改 MIUI 风格通知栏的通知图标 */
MiuiNotificationViewWrapperClass?.apply {
constructor().hook().after {
MiuiNotificationViewWrapperClass?.resolve()?.optional()?.apply {
constructor {}.hookAll().after {
val nf = instance.getRowPair().second.getSbn() ?: return@after
field { name = "mAppIcon" }.get(instance).cast<ImageView>()?.clone {
firstFieldOrNull { name = "mAppIcon" }?.of(instance)?.get<ImageView>()?.clone {
if (ConfigData.isEnableReplaceMiuiStyleNotifyIcon || ConfigData.isEnableNotifyIconForceAppIcon)
compatNotifyIcon(context, nf, iconView = this, isUseMaterial3Style = true, isMiuiPanel = true)
else setImageDrawable(nf.miuiAppIcon?.loadDrawable(context) ?: context.appIconOf(nf.packageName))
@@ -1193,16 +1198,16 @@ object SystemUIHooker : YukiBaseHooker() {
}
}
/** 修改 MIUI 风格通知栏的通知图标 - 折叠通知 */
MiuiNotificationChildrenContainerClass?.apply {
MiuiNotificationChildrenContainerClass?.resolve()?.optional()?.apply {
/** 替换通知小图标 */
method {
firstMethodOrNull {
name = "updateAppIcon"
param(BooleanType)
}.hook().after {
field { name = "mAppIcon" }.get(instance).cast<ImageView>()?.apply {
val nf = NotificationChildrenContainerClass.field {
parameters(Boolean::class)
}?.hook()?.after {
firstFieldOrNull { name = "mAppIcon" }?.of(instance)?.get<ImageView>()?.apply {
val nf = NotificationChildrenContainerClass.resolve().optional().firstFieldOrNull {
name = "mContainingNotification"
}.get(instance).any()?.getSbn() ?: return@after
}?.of(instance)?.get()?.getSbn() ?: return@after
if (ConfigData.isEnableReplaceMiuiStyleNotifyIcon || ConfigData.isEnableNotifyIconForceAppIcon)
compatNotifyIcon(context, nf, iconView = this, isUseMaterial3Style = true, isMiuiPanel = true)
else setImageDrawable(nf.miuiAppIcon?.loadDrawable(context) ?: context.appIconOf(nf.packageName))
@@ -1210,29 +1215,28 @@ object SystemUIHooker : YukiBaseHooker() {
}
}
/** 干掉下拉通知图标自动设置回 APP 图标的方法 */
NotificationHeaderViewWrapperInjectorClass?.apply {
method {
NotificationHeaderViewWrapperInjectorClass?.resolve()?.optional(silent = true)?.apply {
firstMethodOrNull {
name = "setAppIcon"
param(ContextClass, ImageViewClass, ExpandedNotificationClass)
}.remedys {
method {
parameters(Context::class, ImageView::class, ExpandedNotificationClass)
}?.hook()?.intercept()
firstMethodOrNull {
name = "setAppIcon"
param(ImageViewClass, ExpandedNotificationClass)
}
}.ignored().hook().intercept()
method {
parameters(ImageView::class, ExpandedNotificationClass)
}?.hook()?.intercept()
firstMethodOrNull {
name = "resetIconBgAndPaddings"
param(ImageViewClass, ExpandedNotificationClass)
}.ignored().hook().intercept()
parameters(ImageView::class, ExpandedNotificationClass)
}?.hook()?.intercept()
}
NotificationContentInflaterInjectorClass?.method {
NotificationContentInflaterInjectorClass?.resolve()?.optional()?.firstMethodOrNull {
name = "handleAppIcon"
/**
* MIUI 14 ([RemoteViewsClass], [NotificationClass])
* HyperOS ([RemoteViewsClass], [NotificationClass], [ContextClass])
* HyperOS 2.0 ([RemoteViewsClass], [NotificationClass], [ContextClass], [BooleanClass])
*/
paramCount(numRange = 2..4)
parameterCount { it in 2..4 }
}?.hook()?.intercept()
/**
* 尝试修复从 MIUI 14 开始出现的一个崩溃问题
@@ -1245,14 +1249,14 @@ object SystemUIHooker : YukiBaseHooker() {
* }
* ```
*/
NotificationStatClass?.method {
NotificationStatClass?.resolve()?.optional(silent = true)?.firstMethodOrNull {
name = "isUnimportantEntry"
paramCount = 1
}?.ignored()?.hook()?.replaceAny {
args().first().current(ignored = true).method {
parameterCount = 1
}?.hook()?.replaceAny {
args().first().resolve().optional().firstMethodOrNull {
name = "getSbn"
superClass()
}.invoke<StatusBarNotification>()?.let { sbn ->
superclass()
}?.invoke<StatusBarNotification>()?.let { sbn ->
sbn.packageName == PackageName.SYSTEMUI && sbn.tag == "UNIMPORTANT"
} ?: false
}

View File

@@ -20,21 +20,20 @@
*
* This file is created by fankes on 2022/1/30.
*/
@file:Suppress("DEPRECATION")
package com.fankes.miui.notify.ui.activity.base
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat
import androidx.viewbinding.ViewBinding
import com.fankes.miui.notify.R
import com.fankes.miui.notify.utils.factory.isNotSystemInDarkMode
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
import com.highcapable.kavaref.extension.toClassOrNull
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
@@ -50,10 +49,11 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
isMainThreadRunning = true
binding = current().generic()?.argument()?.method {
val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
name = "inflate"
param(LayoutInflaterClass)
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed")
parameters(LayoutInflater::class)
}?.invoke<VB>(layoutInflater) ?: error("binding failed")
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
setContentView(binding.root)
/** 隐藏系统的标题栏 */
@@ -63,6 +63,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
isAppearanceLightStatusBars = isNotSystemInDarkMode
isAppearanceLightNavigationBars = isNotSystemInDarkMode
}
@Suppress("DEPRECATION")
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
window?.statusBarColor = it
window?.navigationBarColor = it

View File

@@ -64,6 +64,7 @@ class BaseAdapterCreater(val context: Context) {
* 绑定 [BaseAdapter] 到 [ListView]
* @param bindViews 回调 - ([VB] 每项,[Int] 下标)
*/
@Suppress("DEPRECATION")
inline fun <reified VB : ViewBinding> onBindViews(crossinline bindViews: (binding: VB, position: Int) -> Unit) {
baseAdapter = object : BaseAdapter() {
override fun getCount() = listDataCallback?.let { it() }?.size ?: 0

View File

@@ -68,6 +68,7 @@ inline fun Context.showDialog(initiate: DialogBuilder<*>.() -> Unit) = DialogBui
* @param context 实例
* @param bindingClass [ViewBinding] 的 [Class] 实例 or null
*/
@Suppress("DEPRECATION")
class DialogBuilder<VB : ViewBinding>(val context: Context, private val bindingClass: Class<*>? = null) {
/** 实例对象 */

View File

@@ -63,6 +63,8 @@ import androidx.core.app.NotificationManagerCompat
import androidx.core.content.getSystemService
import androidx.core.content.pm.PackageInfoCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.createBitmap
import androidx.core.net.toUri
import com.fankes.miui.notify.wrapper.BuildConfigWrapper
import com.google.android.material.snackbar.Snackbar
import com.highcapable.yukihookapi.hook.factory.hasClass
@@ -148,6 +150,7 @@ val isNotMiSystem get() = !isMiSystem
* 当前设备是否是 MIUI 定制 Android 系统
* @return [Boolean] 是否符合条件
*/
@Suppress("DEPRECATION")
val isMIUI by lazy { "android.miui.R".hasClass() }
/**
@@ -541,7 +544,7 @@ val String.bitmap: Bitmap get() = unbase64.bitmap
* @return [Bitmap] 圆角后的位图 - 失败会返回处理之前的位图
*/
fun Bitmap.round(radius: Float): Bitmap = safeOf(default = this) {
Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { out ->
createBitmap(width, height).also { out ->
Canvas(out).also { canvas ->
Paint().also { paint ->
paint.isAntiAlias = true
@@ -561,6 +564,7 @@ fun Bitmap.round(radius: Float): Bitmap = safeOf(default = this) {
* @param default 默认值
* @return [String]
*/
@Suppress("DEPRECATION")
fun findPropString(key: String, default: String = "") = safeOf(default) {
"android.os.SystemProperties".toClassOrNull()?.method {
name = "get"
@@ -634,7 +638,7 @@ fun Context.openBrowser(url: String, packageName: String = "") = runCatching {
startActivity(Intent().apply {
if (packageName.isNotBlank()) setPackage(packageName)
action = Intent.ACTION_VIEW
data = Uri.parse(url)
data = url.toUri()
/** 防止顶栈一样重叠在自己的 APP 中 */
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})

View File

@@ -32,6 +32,7 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.VectorDrawable
import android.util.ArrayMap
import androidx.core.graphics.createBitmap
import androidx.core.graphics.drawable.toBitmap
import com.fankes.miui.notify.utils.factory.safeOfFalse
import kotlin.math.abs
@@ -77,7 +78,7 @@ object BitmapCompatTool {
var width = bitmap.width
if (height > 64 || width > 64) {
if (tempCompactBitmap == null) {
tempCompactBitmap = Bitmap.createBitmap(64, 64, Bitmap.Config.ARGB_8888)
tempCompactBitmap = createBitmap(64, 64)
.also { tempCompactBitmapCanvas = Canvas(it) }
tempCompactBitmapPaint = Paint(Paint.FILTER_BITMAP_FLAG).apply { isFilterBitmap = true }
}
@@ -91,11 +92,12 @@ object BitmapCompatTool {
val size = height * width
ensureBufferSize(size)
tempCompactBitmap?.getPixels(tempBuffer, 0, width, 0, 0, width, height)
for (i in 0 until size)
for (i in 0 until size) {
if (isGrayscaleColor(tempBuffer[i]).not()) {
cachedBitmapGrayscales[bitmap.generationId] = false
return@let false
}
}
cachedBitmapGrayscales[bitmap.generationId] = true
true
}

View File

@@ -1457,6 +1457,35 @@
android:textColor="@color/colorTextGray"
android:textSize="11sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp"
android:background="@drawable/bg_permotion_round"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginEnd="10dp"
android:src="@mipmap/ic_kavaref" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:ellipsize="end"
android:lineSpacingExtra="6dp"
android:maxLines="2"
android:text="此模块使用 KavaRef 强力驱动。\n了解更多 https://github.com/HighCapable/KavaRef"
android:textColor="@color/colorTextGray"
android:textSize="11sp" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -22,7 +22,7 @@ repositories:
plugins:
com.android.application:
alias: android-application
version: 8.9.0
version: 8.9.3
org.jetbrains.kotlin.android:
alias: kotlin-android
version: 2.1.10
@@ -43,9 +43,14 @@ libraries:
rovo89-xposed-api
com.highcapable.yukihookapi:
api:
version: 1.2.1
version: 1.3.0
ksp-xposed:
version-ref: <this>::api
com.highcapable.kavaref:
kavaref-core:
version: 1.0.0
kavaref-extension:
version: 1.0.0
com.github.topjohnwu.libsu:
core:
version: 5.2.2
@@ -55,13 +60,13 @@ libraries:
version: 1.0.7
com.squareup.okhttp3:
okhttp:
version: 5.0.0-alpha.14
version: 5.0.0-alpha.16
androidx.core:
core-ktx:
version: 1.15.0
version: 1.16.0
androidx.appcompat:
appcompat:
version: 1.7.0
version: 1.7.1
com.google.android.material:
material:
version: 1.12.0