refactor: migrate and update to YukiHookAPI 1.3.0

This commit is contained in:
2025-06-25 23:20:11 +08:00
parent 276820fdc5
commit 147e4c117e
9 changed files with 315 additions and 227 deletions

View File

@@ -82,6 +82,8 @@ dependencies {
implementation(com.highcapable.yukihookapi.api) implementation(com.highcapable.yukihookapi.api)
ksp(com.highcapable.yukihookapi.ksp.xposed) ksp(com.highcapable.yukihookapi.ksp.xposed)
ksp(com.highcapable.hikage.hikage.compiler) ksp(com.highcapable.hikage.hikage.compiler)
implementation(com.highcapable.kavaref.kavaref.core)
implementation(com.highcapable.kavaref.kavaref.extension)
implementation(com.highcapable.hikage.hikage.core) implementation(com.highcapable.hikage.hikage.core)
implementation(com.highcapable.hikage.hikage.extension) implementation(com.highcapable.hikage.hikage.extension)
implementation(com.highcapable.hikage.hikage.widget.androidx) implementation(com.highcapable.hikage.hikage.widget.androidx)

View File

@@ -26,8 +26,11 @@ package com.fankes.tsbattery.hook.entity
import android.app.Activity import android.app.Activity
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.os.Bundle
import android.os.Message
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
@@ -44,29 +47,16 @@ import com.fankes.tsbattery.hook.factory.startModuleSettings
import com.fankes.tsbattery.hook.helper.DexKitHelper import com.fankes.tsbattery.hook.helper.DexKitHelper
import com.fankes.tsbattery.utils.factory.appVersionName import com.fankes.tsbattery.utils.factory.appVersionName
import com.fankes.tsbattery.utils.factory.dp import com.fankes.tsbattery.utils.factory.dp
import com.highcapable.yukihookapi.hook.bean.VariousClass import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.extension.ArrayClass
import com.highcapable.kavaref.extension.VariousClass
import com.highcapable.kavaref.extension.classOf
import com.highcapable.kavaref.extension.createInstanceAsTypeOrNull
import com.highcapable.kavaref.extension.createInstanceOrNull
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.buildOf
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
import com.highcapable.yukihookapi.hook.log.YLog import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.android.BuildClass
import com.highcapable.yukihookapi.hook.type.android.BundleClass
import com.highcapable.yukihookapi.hook.type.android.ContextClass
import com.highcapable.yukihookapi.hook.type.android.IntentClass
import com.highcapable.yukihookapi.hook.type.android.MessageClass
import com.highcapable.yukihookapi.hook.type.java.AnyArrayClass
import com.highcapable.yukihookapi.hook.type.java.AnyClass
import com.highcapable.yukihookapi.hook.type.java.BooleanType
import com.highcapable.yukihookapi.hook.type.java.CharSequenceClass
import com.highcapable.yukihookapi.hook.type.java.IntType
import com.highcapable.yukihookapi.hook.type.java.ListClass
import com.highcapable.yukihookapi.hook.type.java.LongType
import com.highcapable.yukihookapi.hook.type.java.StringClass
import com.highcapable.yukihookapi.hook.type.java.UnitType
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.Proxy import java.lang.reflect.Proxy
@@ -159,7 +149,9 @@ object QQTIMHooker : YukiBaseHooker() {
* 通过 [Activity] or [Fragment] 实例得到上下文 * 通过 [Activity] or [Fragment] 实例得到上下文
* @return [Activity] or null * @return [Activity] or null
*/ */
private fun Any.compatToActivity() = if (this is Activity) this else current().method { name = "getActivity"; superClass() }.invoke() private fun Any.compatToActivity() = if (this !is Activity)
resolve().optional().firstMethodOrNull { name = "getActivity"; superclass() }?.invoke()
else this
/** 使用 DexKit 进行搜索 */ /** 使用 DexKit 进行搜索 */
private fun searchUsingDexKit() { private fun searchUsingDexKit() {
@@ -172,7 +164,7 @@ object QQTIMHooker : YukiBaseHooker() {
declaredClass(baseChatPieClassName) declaredClass(baseChatPieClassName)
usingStrings("remainScreenOn") usingStrings("remainScreenOn")
paramCount = 0 paramCount = 0
returnType = UnitType.name returnType = Void.TYPE.name
} }
}.singleOrNull()?.getMethodInstance(classLoader) }.singleOrNull()?.getMethodInstance(classLoader)
DexKitData.BaseChatPie_CancelRemainScreenOnMethod = DexKitData.BaseChatPie_CancelRemainScreenOnMethod =
@@ -181,7 +173,7 @@ object QQTIMHooker : YukiBaseHooker() {
declaredClass(baseChatPieClassName) declaredClass(baseChatPieClassName)
usingStrings("cancelRemainScreenOn") usingStrings("cancelRemainScreenOn")
paramCount = 0 paramCount = 0
returnType = UnitType.name returnType = Void.TYPE.name
} }
}.singleOrNull()?.getMethodInstance(classLoader) }.singleOrNull()?.getMethodInstance(classLoader)
} }
@@ -192,11 +184,11 @@ object QQTIMHooker : YukiBaseHooker() {
methods { methods {
add { add {
name = "<init>" name = "<init>"
paramTypes(ContextClass.name, IntType.name, CharSequenceClass.name, IntType.name) paramTypes(classOf<Context>().name, classOf<Int>().name, classOf<CharSequence>().name, classOf<Int>().name)
} }
add { add {
paramTypes(kotlinFunction0) paramTypes(kotlinFunction0)
returnType = UnitType.name returnType = Void.TYPE.name
} }
} }
fields { count(6..Int.MAX_VALUE) } fields { count(6..Int.MAX_VALUE) }
@@ -208,7 +200,7 @@ object QQTIMHooker : YukiBaseHooker() {
matcher { matcher {
declaredClass = className declaredClass = className
paramTypes(kotlinFunction0) paramTypes(kotlinFunction0)
returnType = UnitType.name returnType = Void.TYPE.name
usingNumbers(2) usingNumbers(2)
} }
}.singleOrNull()?.getMethodInstance(classLoader) }.singleOrNull()?.getMethodInstance(classLoader)
@@ -246,23 +238,23 @@ object QQTIMHooker : YukiBaseHooker() {
/** Hook CoreService QQ、TIM */ /** Hook CoreService QQ、TIM */
private fun hookCoreService() { private fun hookCoreService() {
CoreServiceClass?.apply { CoreServiceClass?.resolve()?.optional()?.apply {
if (isQQ) { if (isQQ) {
method { firstMethodOrNull {
name = "startTempService" name = "startTempService"
}.ignored().hook().intercept() }?.hook()?.intercept()
method { firstMethodOrNull {
name = "startCoreService" name = "startCoreService"
param(BooleanType) parameters(Boolean::class)
}.ignored().hook().intercept() }?.hook()?.intercept()
method { firstMethodOrNull {
name = "onStartCommand" name = "onStartCommand"
param(IntentClass, IntType, IntType) parameters(Intent::class, Int::class, Int::class)
}.ignored().hook().replaceTo(any = 2) }?.hook()?.replaceTo(any = 2)
} }
method { firstMethodOrNull {
name = "onCreate" name = "onCreate"
}.ignored().hook().after { }?.hook()?.after {
if (ConfigData.isEnableKillQQTimCoreService) if (ConfigData.isEnableKillQQTimCoreService)
instance<Service>().apply { instance<Service>().apply {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
@@ -271,10 +263,10 @@ object QQTIMHooker : YukiBaseHooker() {
} }
} }
} }
CoreService_KernelServiceClass?.apply { CoreService_KernelServiceClass?.resolve()?.optional()?.apply {
method { firstMethodOrNull {
name = "onCreate" name = "onCreate"
}.ignored().hook().after { }?.hook()?.after {
if (ConfigData.isEnableKillQQTimCoreServiceChild) if (ConfigData.isEnableKillQQTimCoreServiceChild)
instance<Service>().apply { instance<Service>().apply {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
@@ -282,10 +274,10 @@ object QQTIMHooker : YukiBaseHooker() {
YLog.debug("Shutdown CoreService\$KernelService OK!") YLog.debug("Shutdown CoreService\$KernelService OK!")
} }
} }
method { firstMethodOrNull {
name = "onStartCommand" name = "onStartCommand"
param(IntentClass, IntType, IntType) parameters(Intent::class, Int::class, Int::class)
}.ignored().hook().replaceTo(any = 2) }?.hook()?.replaceTo(any = 2)
} }
} }
@@ -298,40 +290,46 @@ object QQTIMHooker : YukiBaseHooker() {
* 旧版本理论上没有这个类 * 旧版本理论上没有这个类
*/ */
"${PackageName.QQ}.msf.service.y".toClassOrNull() "${PackageName.QQ}.msf.service.y".toClassOrNull()
?.method { ?.resolve()
?.optional(silent = true)
?.firstMethodOrNull {
name = "a" name = "a"
param(StringClass, LongType) parameters(String::class, Long::class)
returnType = UnitType returnType = Void.TYPE
}?.ignored()?.hook()?.intercept() }?.hook()?.intercept()
/** /**
* 干掉自动上传服务的电源锁 * 干掉自动上传服务的电源锁
* 每个版本的差异暂未做排查 * 每个版本的差异暂未做排查
*/ */
"com.tencent.upload.impl.UploadServiceImpl".toClassOrNull() "com.tencent.upload.impl.UploadServiceImpl".toClassOrNull()
?.method { ?.resolve()
?.optional(silent = true)
?.firstMethodOrNull {
name = "acquireWakeLockIfNot" name = "acquireWakeLockIfNot"
}?.ignored()?.hook()?.intercept() }?.hook()?.intercept()
/** /**
* Hook 掉一个一像素保活 Activity 真的我怎么都想不到讯哥的程序员做出这种事情 * Hook 掉一个一像素保活 Activity 真的我怎么都想不到讯哥的程序员做出这种事情
* 这个东西经过测试会在锁屏的时候吊起来,解锁的时候自动 finish(),无限耍流氓耗电 * 这个东西经过测试会在锁屏的时候吊起来,解锁的时候自动 finish(),无限耍流氓耗电
* 2022/1/25 后期查证:锁屏界面消息快速回复窗口的解锁后拉起保活界面,也是毒瘤 * 2022/1/25 后期查证:锁屏界面消息快速回复窗口的解锁后拉起保活界面,也是毒瘤
*/ */
"${PackageName.QQ}.activity.QQLSUnlockActivity".toClassOrNull() "${PackageName.QQ}.activity.QQLSUnlockActivity".toClassOrNull()
?.method { ?.resolve()
?.optional(silent = true)
?.firstMethodOrNull {
name = "onCreate" name = "onCreate"
param(BundleClass) parameters(Bundle::class)
}?.ignored()?.hook { }?.hook {
var origDevice = "" var origDevice = ""
before { before {
/** 由于在 onCreate 里有一行判断只要型号是 xiaomi 的设备就开电源锁,所以说这里临时替换成菊花厂 */ /** 由于在 onCreate 里有一行判断只要型号是 xiaomi 的设备就开电源锁,所以说这里临时替换成菊花厂 */
origDevice = Build.MANUFACTURER origDevice = Build.MANUFACTURER
if (Build.MANUFACTURER.lowercase() == "xiaomi") if (Build.MANUFACTURER.lowercase() == "xiaomi")
BuildClass.field { name = "MANUFACTURER" }.get().set("HUAWEI") Build::class.resolve().firstField { name = "MANUFACTURER" }.set("HUAWEI")
} }
after { after {
instance<Activity>().finish() instance<Activity>().finish()
/** 这里再把型号替换回去 - 不影响应用变量等 Xposed 模块修改的型号 */ /** 这里再把型号替换回去 - 不影响应用变量等 Xposed 模块修改的型号 */
BuildClass.field { name = "MANUFACTURER" }.get().set(origDevice) Build::class.resolve().firstField { name = "MANUFACTURER" }.set(origDevice)
} }
} }
/** /**
@@ -341,9 +339,11 @@ object QQTIMHooker : YukiBaseHooker() {
* 2022/1/25 后期查证:锁屏界面消息快速回复窗口 * 2022/1/25 后期查证:锁屏界面消息快速回复窗口
*/ */
VariousClass("${PackageName.QQ}.activity.QQLSActivity\$14", "ktq").toClassOrNull() VariousClass("${PackageName.QQ}.activity.QQLSActivity\$14", "ktq").toClassOrNull()
?.method { ?.resolve()
?.optional(silent = true)
?.firstMethodOrNull {
name = "run" name = "run"
}?.ignored()?.hook()?.intercept() }?.hook()?.intercept()
/** /**
* 这个是毒瘤核心类 * 这个是毒瘤核心类
* WakeLockMonitor * WakeLockMonitor
@@ -354,92 +354,98 @@ object QQTIMHooker : YukiBaseHooker() {
* 👮🏻 经过排查 Play 版本没这个类...... Emmmm 不想说啥了 * 👮🏻 经过排查 Play 版本没这个类...... Emmmm 不想说啥了
* ✅ 备注8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类 * ✅ 备注8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类
*/ */
"com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor".toClassOrNull()?.apply { "com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor".toClassOrNull()
method { ?.resolve()
name = "onHook" ?.optional(silent = true)
param(StringClass, AnyClass, AnyArrayClass, AnyClass) ?.apply {
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "onHook"
name = "doReport" parameters(String::class, Any::class, ArrayClass(Any::class), Any::class)
param("com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor\$WakeLockEntity", IntType) }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "doReport"
name = "afterHookedMethod" parameters("com.tencent.qapmsdk.qqbattery.monitor.WakeLockMonitor\$WakeLockEntity", Int::class)
param("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam") }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "afterHookedMethod"
name = "beforeHookedMethod" parameters("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam")
param("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam") }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "beforeHookedMethod"
name = "onAppBackground" parameters("com.tencent.qapmsdk.qqbattery.monitor.MethodHookParam")
}.ignored().hook().intercept() }?.hook()?.intercept()
method { firstMethodOrNull {
name = "onOtherProcReport" name = "onAppBackground"
param(BundleClass) }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "onOtherProcReport"
name = "onProcessRun30Min" parameters(Bundle::class)
}.ignored().hook().intercept() }?.hook()?.intercept()
method { firstMethodOrNull {
name = "onProcessBG5Min" name = "onProcessRun30Min"
}.ignored().hook().intercept() }?.hook()?.intercept()
method { firstMethodOrNull {
name = "writeReport" name = "onProcessBG5Min"
param(BooleanType) }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
} name = "writeReport"
parameters(Boolean::class)
}?.hook()?.intercept()
}
/** /**
* 这个是毒瘤核心操作类 * 这个是毒瘤核心操作类
* 功能同上、全部拦截 * 功能同上、全部拦截
* 👮🏻 经过排查 Play 版本也没这个类...... Emmmm 不想说啥了 * 👮🏻 经过排查 Play 版本也没这个类...... Emmmm 不想说啥了
* ✅ 备注8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类 * ✅ 备注8.9.x 版本已经基本移除了这个功能,没有再发现存在这个类
*/ */
"com.tencent.qapmsdk.qqbattery.QQBatteryMonitor".toClassOrNull()?.apply { "com.tencent.qapmsdk.qqbattery.QQBatteryMonitor".toClassOrNull()
method { ?.resolve()
name = "start" ?.optional(silent = true)
}.ignored().hook().intercept() ?.apply {
method { firstMethodOrNull {
name = "stop" name = "start"
}.ignored().hook().intercept() }?.hook()?.intercept()
method { firstMethodOrNull {
name = "handleMessage" name = "stop"
param(MessageClass) }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "handleMessage"
name = "startMonitorInner" parameters(Message::class)
}.ignored().hook().intercept() }?.hook()?.intercept()
method { firstMethodOrNull {
name = "onAppBackground" name = "startMonitorInner"
}.ignored().hook().intercept() }?.hook()?.intercept()
method { firstMethodOrNull {
name = "onAppForeground" name = "onAppBackground"
}.ignored().hook().intercept() }?.hook()?.intercept()
method { firstMethodOrNull {
name = "setLogWhite" name = "onAppForeground"
paramCount = 2 }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "setLogWhite"
name = "setCmdWhite" parameterCount = 2
paramCount = 2 }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "setCmdWhite"
name = "onWriteLog" parameterCount = 2
param(StringClass, StringClass) }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "onWriteLog"
name = "onCmdRequest" parameters(String::class, String::class)
param(StringClass) }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "onCmdRequest"
name = "addData" parameters(String::class)
paramCount = 4 }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
method { name = "addData"
name = "onGpsScan" parameterCount = 4
paramCount = 2 }?.hook()?.intercept()
}.ignored().hook().intercept() firstMethodOrNull {
} name = "onGpsScan"
parameterCount = 2
}?.hook()?.intercept()
}
} }
/** Hook QQ 的设置界面添加模块设置入口 (新版) */ /** Hook QQ 的设置界面添加模块设置入口 (新版) */
@@ -457,27 +463,30 @@ object QQTIMHooker : YukiBaseHooker() {
/** 为了使用图标资源 ID - 这里需要重新注入模块资源防止不生效 */ /** 为了使用图标资源 ID - 这里需要重新注入模块资源防止不生效 */
context.injectModuleAppResources() context.injectModuleAppResources()
val iconResId = if (context.isQQNightMode()) R.mipmap.ic_tsbattery_entry_night else R.mipmap.ic_tsbattery_entry_day val iconResId = if (context.isQQNightMode()) R.mipmap.ic_tsbattery_entry_night else R.mipmap.ic_tsbattery_entry_day
return simpleItemProcessorClass.buildOf(context, R.id.tsbattery_qq_entry_item_id, "TSBattery", iconResId) { return simpleItemProcessorClass.createInstanceOrNull(context, R.id.tsbattery_qq_entry_item_id, "TSBattery", iconResId)?.also { item ->
param(ContextClass, IntType, CharSequenceClass, IntType)
}?.also { entryItem ->
val onClickMethod = DexKitData.SimpleItemProcessorClass_OnClickMethod ?: error("Could not found processor method") val onClickMethod = DexKitData.SimpleItemProcessorClass_OnClickMethod ?: error("Could not found processor method")
val proxyOnClick = Proxy.newProxyInstance(appClassLoader, arrayOf(onClickMethod.parameterTypes[0])) { any, method, args -> val proxyOnClick = Proxy.newProxyInstance(appClassLoader, arrayOf(onClickMethod.parameterTypes[0])) { any, method, args ->
if (method.name == "invoke") { if (method.name == "invoke") {
context.startModuleSettings() context.startModuleSettings()
kotlinUnit.toClass().field { name = "INSTANCE" }.get().any() kotlinUnit.toClass().resolve().firstField { name = "INSTANCE" }.get()
} else method.invoke(any, args) } else method.invoke(any, args)
}; onClickMethod.invoke(entryItem, proxyOnClick) }; onClickMethod.invoke(item, proxyOnClick)
} ?: error("Could not create TSBattery entry item") } ?: error("Could not create TSBattery entry item")
} }
MainSettingConfigProviderClass?.method { MainSettingConfigProviderClass?.resolve()?.optional()?.firstMethodOrNull {
param(ContextClass) parameters(Context::class)
returnType = ListClass returnType = List::class
}?.hook()?.after { }?.hook()?.after {
val context = args().first().cast<Context>() ?: return@after val context = args().first().cast<Context>() ?: return@after
val processor = result<MutableList<Any?>>() ?: return@after val processor = result<MutableList<Any?>>() ?: return@after
processor.add(1, processor[0]?.javaClass?.buildOf(arrayListOf<Any>().apply { add(createTSEntryItem(context)) }, "", "") { processor.add(
param(ListClass, CharSequenceClass, CharSequenceClass) 1,
}) processor[0]?.javaClass?.createInstanceOrNull(
arrayListOf<Any>().apply {
add(createTSEntryItem(context))
}.toList(), "", ""
)
)
} }
} }
@@ -487,35 +496,38 @@ object QQTIMHooker : YukiBaseHooker() {
*/ */
private fun hookQQSettingsUiLegacy(instance: Any?) { private fun hookQQSettingsUiLegacy(instance: Any?) {
/** 当前的顶级 Item 实例 */ /** 当前的顶级 Item 实例 */
val formItemRefRoot = instance?.current()?.field { val formItemRefRoot = instance?.resolve()?.optional()?.lastFieldOrNull {
type { it == FormSimpleItemClass || it == FormCommonSingleLineItemClass }.index(num = 1) type { it == FormSimpleItemClass || it == FormCommonSingleLineItemClass }
}?.cast<View?>() }?.get<View>()
/** 创建一个新的 Item */ /** 创建一个新的 Item */
FormSimpleItemClass?.buildOf<View>(instance?.compatToActivity()) { param(ContextClass) }?.current { val item = FormSimpleItemClass?.createInstanceAsTypeOrNull<View>(instance?.compatToActivity())
method { item?.resolve()?.optional()?.apply {
firstMethodOrNull {
name = "setLeftText" name = "setLeftText"
param(CharSequenceClass) parameters(CharSequence::class)
}.call("TSBattery") }?.invoke("TSBattery")
method { firstMethodOrNull {
name = "setRightText" name = "setRightText"
param(CharSequenceClass) parameters(CharSequence::class)
}.call(ModuleVersion.toString()) }?.invoke(ModuleVersion.toString())
method { firstMethodOrNull {
name = "setBgType" name = "setBgType"
param(IntType) parameters(Int::class)
}.call(if (isQQ) 0 else 2) }?.invoke(if (isQQ) 0 else 2)
}?.apply { setOnClickListener { context.startModuleSettings() } }?.also { item ->
var listGroup = formItemRefRoot?.parent as? ViewGroup?
val lparam = (if (listGroup?.childCount == 1) {
listGroup = listGroup.parent as? ViewGroup
(formItemRefRoot?.parent as? View?)?.layoutParams
} else formItemRefRoot?.layoutParams)
?: ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
/** 设置圆角和间距 */
if (isQQ) (lparam as? ViewGroup.MarginLayoutParams?)?.setMargins(0, 15.dp(item.context), 0, 0)
/** 将 Item 添加到设置界面 */
listGroup?.also { if (isQQ) it.addView(item, lparam) else it.addView(item, 0, lparam) }
} }
item ?: return
item.setOnClickListener { it.context.startModuleSettings() }
var listGroup = formItemRefRoot?.parent as? ViewGroup?
val lparam = (if (listGroup?.childCount == 1) {
listGroup = listGroup.parent as? ViewGroup
(formItemRefRoot?.parent as? View?)?.layoutParams
} else formItemRefRoot?.layoutParams)
?: ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
/** 设置圆角和间距 */
if (isQQ) (lparam as? ViewGroup.MarginLayoutParams?)?.setMargins(0, 15.dp(item.context), 0, 0)
/** 将 Item 添加到设置界面 */
listGroup?.also { if (isQQ) it.addView(item, lparam) else it.addView(item, 0, lparam) }
} }
override fun onHook() { override fun onHook() {
@@ -544,22 +556,22 @@ object QQTIMHooker : YukiBaseHooker() {
/** 仅注入主进程 */ /** 仅注入主进程 */
withProcess(mainProcessName) { withProcess(mainProcessName) {
/** Hook 跳转事件 */ /** Hook 跳转事件 */
JumpActivityClass?.method { JumpActivityClass?.resolve()?.optional()?.firstMethodOrNull {
name = "doOnCreate" name = "doOnCreate"
param(BundleClass) parameters(Bundle::class)
}?.hook()?.after { instance<Activity>().jumpToModuleSettings() } }?.hook()?.after { instance<Activity>().jumpToModuleSettings() }
/** Hook 设置界面入口点 */ /** Hook 设置界面入口点 */
if (isQQNTVersion) hookQQSettingsUi() if (isQQNTVersion) hookQQSettingsUi()
else { else {
/** 将条目注入设置界面 (Activity) */ /** 将条目注入设置界面 (Activity) */
QQSettingSettingActivityClass?.method { QQSettingSettingActivityClass?.resolve()?.optional()?.firstMethodOrNull {
name = "doOnCreate" name = "doOnCreate"
param(BundleClass) parameters(Bundle::class)
}?.hook()?.after { hookQQSettingsUiLegacy(instance) } }?.hook()?.after { hookQQSettingsUiLegacy(instance) }
/** 将条目注入设置界面 (Fragment) */ /** 将条目注入设置界面 (Fragment) */
QQSettingSettingFragmentClass?.method { QQSettingSettingFragmentClass?.resolve()?.optional()?.firstMethodOrNull {
name = "doOnCreateView" name = "doOnCreateView"
paramCount = 3 parameterCount = 3
}?.hook()?.after { hookQQSettingsUiLegacy(instance) } }?.hook()?.after { hookQQSettingsUiLegacy(instance) }
} }
} }

View File

@@ -42,14 +42,12 @@ import com.fankes.tsbattery.utils.factory.absoluteStatusBarHeight
import com.fankes.tsbattery.utils.factory.appVersionCode import com.fankes.tsbattery.utils.factory.appVersionCode
import com.fankes.tsbattery.utils.factory.appVersionName import com.fankes.tsbattery.utils.factory.appVersionName
import com.fankes.tsbattery.utils.factory.dp import com.fankes.tsbattery.utils.factory.dp
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
import com.highcapable.yukihookapi.hook.factory.current
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.factory.processName import com.highcapable.yukihookapi.hook.factory.processName
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
import com.highcapable.yukihookapi.hook.log.YLog import com.highcapable.yukihookapi.hook.log.YLog
import com.highcapable.yukihookapi.hook.type.android.ViewClass
/** /**
* Hook 微信 * Hook 微信
@@ -114,24 +112,24 @@ object WeChatHooker : YukiBaseHooker() {
/** 仅注入主进程 */ /** 仅注入主进程 */
withProcess(mainProcessName) { withProcess(mainProcessName) {
/** Hook 跳转事件 */ /** Hook 跳转事件 */
LauncherUIClass?.method { LauncherUIClass?.resolve()?.optional()?.firstMethodOrNull {
name = "onResume" name = "onResume"
emptyParam() emptyParameters()
}?.hook()?.after { instance<Activity>().jumpToModuleSettings(isFinish = false) } }?.hook()?.after { instance<Activity>().jumpToModuleSettings(isFinish = false) }
/** 向设置界面右上角添加按钮 */ /** 向设置界面右上角添加按钮 */
SettingsUIClass?.method { SettingsUIClass?.resolve()?.optional()?.firstMethodOrNull {
name = "onResume" name = "onResume"
emptyParam() emptyParameters()
}?.hook()?.after { }?.hook()?.after {
SettingsUIClass?.method { SettingsUIClass?.resolve()?.optional()?.firstMethodOrNull {
name = "get_fragment" name = "get_fragment"
emptyParam() emptyParameters()
superClass(isOnlySuperClass = true) superclass()
}?.get(instance)?.call()?.current()?.method { }?.of(instance)?.invoke()?.resolve()?.optional()?.firstMethodOrNull {
name = "getView" name = "getView"
emptyParam() emptyParameters()
returnType = ViewClass returnType = View::class
superClass(isOnlySuperClass = true) superclass()
}?.invoke<ViewGroup?>()?.also { }?.invoke<ViewGroup?>()?.also {
it.context?.injectModuleAppResources() it.context?.injectModuleAppResources()
runCatching { it.getChildAt(0) as? ViewGroup? }.getOrNull()?.also { rootView -> runCatching { it.getChildAt(0) as? ViewGroup? }.getOrNull()?.also { rootView ->

View File

@@ -24,18 +24,16 @@ package com.fankes.tsbattery.hook.factory
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.PowerManager
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import com.fankes.tsbattery.const.JumpEvent import com.fankes.tsbattery.const.JumpEvent
import com.fankes.tsbattery.const.PackageName import com.fankes.tsbattery.const.PackageName
import com.fankes.tsbattery.hook.entity.QQTIMHooker.toClass import com.fankes.tsbattery.hook.entity.QQTIMHooker.toClass
import com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity import com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.kavaref.extension.VariousClass
import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.hook.bean.VariousClass
import com.highcapable.yukihookapi.hook.factory.field
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.type.android.PowerManager_WakeLockClass
import com.highcapable.yukihookapi.hook.type.java.IntType
import kotlin.system.exitProcess import kotlin.system.exitProcess
/** QQ、TIM 存在的类 */ /** QQ、TIM 存在的类 */
@@ -48,14 +46,27 @@ private val ThemeUtilClass = VariousClass("${PackageName.QQ}.theme.ThemeUtil", "
* QQ、TIM 主题是否为夜间模式 * QQ、TIM 主题是否为夜间模式
* @return [Boolean] * @return [Boolean]
*/ */
fun Context.isQQNightMode() = runCatching { fun Context.isQQNightMode(): Boolean {
ThemeUtilClass.get(classLoader).method { val sMobileQQ = MobileQQClass.toClass(classLoader).resolve()
name = "getUserCurrentThemeId" .optional()
paramCount = 1 .firstFieldOrNull {
}.get().string(MobileQQClass.toClass(classLoader) name = "sMobileQQ"
.field { name = "sMobileQQ" }.ignored().get().current(ignored = true)?.field { name = "mAppRuntime" }?.any() superclass()
).let { it.endsWith("1103") || it.endsWith("2920") } }?.get()
}.getOrNull() ?: false val mAppRuntime = sMobileQQ?.resolve()
?.optional()
?.firstFieldOrNull {
name = "mAppRuntime"
superclass()
}?.get()
val currentThemeId = ThemeUtilClass.load(classLoader).resolve()
.optional()
.firstMethodOrNull {
name = "getUserCurrentThemeId"
parameterCount = 1
}?.invokeQuietly<String>(mAppRuntime)
return currentThemeId?.let { it.endsWith("1103") || it.endsWith("2920") } == true
}
/** 启动模块设置 [Activity] */ /** 启动模块设置 [Activity] */
fun Context.startModuleSettings() { fun Context.startModuleSettings() {
@@ -82,14 +93,14 @@ fun Activity.jumpToModuleSettings(isFinish: Boolean = true) {
/** Hook 系统电源锁 */ /** Hook 系统电源锁 */
fun PackageParam.hookSystemWakeLock() { fun PackageParam.hookSystemWakeLock() {
PowerManager_WakeLockClass.apply { PowerManager.WakeLock::class.resolve().apply {
method { firstMethod {
name = "acquireLocked" name = "acquireLocked"
emptyParam() emptyParameters()
}.hook().intercept() }.hook().intercept()
method { firstMethod {
name = "release" name = "release"
param(IntType) parameters(Int::class)
}.hook().intercept() }.hook().intercept()
} }
} }

View File

@@ -19,23 +19,24 @@
* *
* This file is created by fankes on 2022/1/30. * This file is created by fankes on 2022/1/30.
*/ */
@file:Suppress("DEPRECATION")
package com.fankes.tsbattery.ui.activity.base package com.fankes.tsbattery.ui.activity.base
import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.fankes.tsbattery.R import com.fankes.tsbattery.R
import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode
import com.highcapable.yukihookapi.hook.factory.current import com.highcapable.kavaref.KavaRef.Companion.resolve
import com.highcapable.yukihookapi.hook.factory.method import com.highcapable.kavaref.extension.genericSuperclassTypeArguments
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass import com.highcapable.kavaref.extension.toClassOrNull
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base.ModuleAppCompatActivity import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.proxy.ModuleActivity
abstract class BaseActivity<VB : ViewBinding> : ModuleAppCompatActivity() { abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity(), ModuleActivity {
override val moduleTheme get() = R.style.Theme_TSBattery override val moduleTheme get() = R.style.Theme_TSBattery
@@ -43,11 +44,13 @@ abstract class BaseActivity<VB : ViewBinding> : ModuleAppCompatActivity() {
lateinit var binding: VB lateinit var binding: VB
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
delegate.onCreate(savedInstanceState)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = current().generic()?.argument()?.method { val bindingClass = javaClass.genericSuperclassTypeArguments().firstOrNull()?.toClassOrNull()
binding = bindingClass?.resolve()?.optional()?.firstMethodOrNull {
name = "inflate" name = "inflate"
param(LayoutInflaterClass) parameters(LayoutInflater::class)
}?.get()?.invoke<VB>(layoutInflater) ?: error("binding failed") }?.invoke<VB>(layoutInflater) ?: error("binding failed")
if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true
setContentView(binding.root) setContentView(binding.root)
/** 隐藏系统的标题栏 */ /** 隐藏系统的标题栏 */
@@ -57,6 +60,7 @@ abstract class BaseActivity<VB : ViewBinding> : ModuleAppCompatActivity() {
isAppearanceLightStatusBars = isNotSystemInDarkMode isAppearanceLightStatusBars = isNotSystemInDarkMode
isAppearanceLightNavigationBars = isNotSystemInDarkMode isAppearanceLightNavigationBars = isNotSystemInDarkMode
} }
@Suppress("DEPRECATION")
ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also { ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also {
window?.statusBarColor = it window?.statusBarColor = it
window?.navigationBarColor = it window?.navigationBarColor = it
@@ -68,4 +72,16 @@ abstract class BaseActivity<VB : ViewBinding> : ModuleAppCompatActivity() {
/** 回调 [onCreate] 方法 */ /** 回调 [onCreate] 方法 */
abstract fun onCreate() abstract fun onCreate()
override fun getClassLoader() = delegate.getClassLoader()
override fun onConfigurationChanged(newConfig: Configuration) {
delegate.onConfigurationChanged(newConfig)
super.onConfigurationChanged(newConfig)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
delegate.onRestoreInstanceState(savedInstanceState)
super.onRestoreInstanceState(savedInstanceState)
}
} }

View File

@@ -23,23 +23,26 @@
package com.fankes.tsbattery.ui.activity.base package com.fankes.tsbattery.ui.activity.base
import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import com.fankes.tsbattery.R import com.fankes.tsbattery.R
import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode import com.fankes.tsbattery.utils.factory.isNotSystemInDarkMode
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base.ModuleAppCompatActivity import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.proxy.ModuleActivity
import android.R as Android_R import android.R as Android_R
abstract class BaseActivity2 : ModuleAppCompatActivity() { abstract class BaseActivity2 : AppCompatActivity(), ModuleActivity {
override val moduleTheme get() = R.style.Theme_TSBattery override val moduleTheme get() = R.style.Theme_TSBattery
@CallSuper @CallSuper
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
delegate.onCreate(savedInstanceState)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
/** 隐藏系统的标题栏 */ /** 隐藏系统的标题栏 */
supportActionBar?.hide() supportActionBar?.hide()
@@ -60,4 +63,16 @@ abstract class BaseActivity2 : ModuleAppCompatActivity() {
/** 回调 [onCreate] 方法 */ /** 回调 [onCreate] 方法 */
abstract fun onCreate() abstract fun onCreate()
override fun getClassLoader() = delegate.getClassLoader()
override fun onConfigurationChanged(newConfig: Configuration) {
delegate.onConfigurationChanged(newConfig)
super.onConfigurationChanged(newConfig)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
delegate.onRestoreInstanceState(savedInstanceState)
super.onRestoreInstanceState(savedInstanceState)
}
} }

View File

@@ -635,6 +635,35 @@
android:textColor="@color/colorTextGray" android:textColor="@color/colorTextGray"
android:textSize="11sp" /> android:textSize="11sp" />
</LinearLayout> </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> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</LinearLayout> </LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -22,7 +22,7 @@ repositories:
plugins: plugins:
com.android.application: com.android.application:
alias: android-application alias: android-application
version: 8.9.0 version: 8.9.3
org.jetbrains.kotlin.android: org.jetbrains.kotlin.android:
alias: kotlin-android alias: kotlin-android
version: 2.1.10 version: 2.1.10
@@ -43,12 +43,17 @@ libraries:
rovo89-xposed-api rovo89-xposed-api
com.highcapable.yukihookapi: com.highcapable.yukihookapi:
api: api:
version: 1.2.1 version: 1.3.0
ksp-xposed: ksp-xposed:
version-ref: <this>::api version-ref: <this>::api
com.highcapable.kavaref:
kavaref-core:
version: 1.0.0
kavaref-extension:
version: 1.0.0
com.highcapable.hikage: com.highcapable.hikage:
hikage-core: hikage-core:
version: 1.0.0 version: 1.0.1
hikage-compiler: hikage-compiler:
version: 1.0.0 version: 1.0.0
hikage-extension: hikage-extension:
@@ -59,19 +64,19 @@ libraries:
version: 1.0.0 version: 1.0.0
org.luckypray: org.luckypray:
dexkit: dexkit:
version: 2.0.3 version: 2.0.6
com.github.duanhong169: com.github.duanhong169:
drawabletoolbox: drawabletoolbox:
version: 1.0.7 version: 1.0.7
com.squareup.okhttp3: com.squareup.okhttp3:
okhttp: okhttp:
version: 5.0.0-alpha.14 version: 5.0.0-alpha.16
androidx.core: androidx.core:
core-ktx: core-ktx:
version: 1.15.0 version: 1.16.0
androidx.appcompat: androidx.appcompat:
appcompat: appcompat:
version: 1.7.0 version: 1.7.1
com.google.android.material: com.google.android.material:
material: material:
version: 1.12.0 version: 1.12.0