mirror of
https://github.com/fankes/TSBattery.git
synced 2025-09-06 18:55:45 +08:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
6b0fe1014e
|
|||
bdf52ba463
|
|||
c2b95b9133
|
|||
2f64fb9ea9
|
|||
f48277f434
|
|||
047f746afb
|
|||
ac885baa64
|
|||
4332881dad
|
|||
49c0655412
|
|||
7eeda27937
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[](https://github.com/fankes/TSBattery)
|
[](https://github.com/fankes/TSBattery)
|
||||||
[](https://github.com/fankes/TSBattery/blob/master/LICENSE)
|
[](https://github.com/fankes/TSBattery/blob/master/LICENSE)
|
||||||
[](https://github.com/fankes/TSBattery/releases)
|
[](https://github.com/fankes/TSBattery/releases)
|
||||||
[](https://github.com/fankes/TSBattery/releases)
|
[](https://github.com/fankes/TSBattery/releases)
|
||||||
[](https://github.com/Xposed-Modules-Repo/com.fankes.tsbattery/releases)
|
[](https://github.com/Xposed-Modules-Repo/com.fankes.tsbattery/releases)
|
||||||
[](https://t.me/XiaofangInternet)
|
[](https://t.me/XiaofangInternet)
|
||||||
|
@@ -72,13 +72,13 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly 'de.robv.android.xposed:api:82'
|
compileOnly 'de.robv.android.xposed:api:82'
|
||||||
implementation 'com.highcapable.yukihookapi:api:1.1.9'
|
implementation 'com.highcapable.yukihookapi:api:1.1.11'
|
||||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.1.9'
|
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.1.11'
|
||||||
implementation 'com.github.duanhong169:drawabletoolbox:1.0.7'
|
implementation 'com.github.duanhong169:drawabletoolbox:1.0.7'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.7'
|
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.7'
|
||||||
implementation 'androidx.core:core-ktx:1.10.0'
|
implementation 'androidx.core:core-ktx:1.10.1'
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
implementation 'com.google.android.material:material:1.8.0'
|
implementation 'com.google.android.material:material:1.9.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
|
6
app/proguard-rules.pro
vendored
6
app/proguard-rules.pro
vendored
@@ -37,6 +37,12 @@
|
|||||||
# 排除注入的 Activity
|
# 排除注入的 Activity
|
||||||
-keep class com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
|
-keep class com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
|
||||||
|
|
||||||
|
# 防止某些类被 R8 混淆 (可能是 BUG)
|
||||||
|
# FIXME: 已知问题字符串类名 (即使是常量) 也会被 R8 处理为混淆后的类名
|
||||||
|
# FIXME: 所以目前只能把不允许 R8 处理的类 keep 掉,同时在当前模块中也不会被混淆就是了
|
||||||
|
-keep class kotlin.Unit
|
||||||
|
-keep class kotlin.jvm.functions.Function0
|
||||||
|
|
||||||
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
||||||
public static *** throwUninitializedProperty(...);
|
public static *** throwUninitializedProperty(...);
|
||||||
public static *** throwUninitializedPropertyAccessException(...);
|
public static *** throwUninitializedPropertyAccessException(...);
|
||||||
|
@@ -23,6 +23,7 @@ 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.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@@ -30,10 +31,12 @@ import android.view.ViewGroup
|
|||||||
import androidx.core.app.ServiceCompat
|
import androidx.core.app.ServiceCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.fankes.tsbattery.BuildConfig
|
import com.fankes.tsbattery.BuildConfig
|
||||||
|
import com.fankes.tsbattery.R
|
||||||
import com.fankes.tsbattery.const.PackageName
|
import com.fankes.tsbattery.const.PackageName
|
||||||
import com.fankes.tsbattery.data.ConfigData
|
import com.fankes.tsbattery.data.ConfigData
|
||||||
import com.fankes.tsbattery.hook.HookEntry
|
import com.fankes.tsbattery.hook.HookEntry
|
||||||
import com.fankes.tsbattery.hook.factory.hookSystemWakeLock
|
import com.fankes.tsbattery.hook.factory.hookSystemWakeLock
|
||||||
|
import com.fankes.tsbattery.hook.factory.isQQNightMode
|
||||||
import com.fankes.tsbattery.hook.factory.jumpToModuleSettings
|
import com.fankes.tsbattery.hook.factory.jumpToModuleSettings
|
||||||
import com.fankes.tsbattery.hook.factory.startModuleSettings
|
import com.fankes.tsbattery.hook.factory.startModuleSettings
|
||||||
import com.fankes.tsbattery.utils.factory.appVersionName
|
import com.fankes.tsbattery.utils.factory.appVersionName
|
||||||
@@ -47,6 +50,7 @@ import com.highcapable.yukihookapi.hook.log.loggerI
|
|||||||
import com.highcapable.yukihookapi.hook.log.loggerW
|
import com.highcapable.yukihookapi.hook.log.loggerW
|
||||||
import com.highcapable.yukihookapi.hook.type.android.*
|
import com.highcapable.yukihookapi.hook.type.android.*
|
||||||
import com.highcapable.yukihookapi.hook.type.java.*
|
import com.highcapable.yukihookapi.hook.type.java.*
|
||||||
|
import java.lang.reflect.Proxy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook QQ、TIM
|
* Hook QQ、TIM
|
||||||
@@ -56,15 +60,24 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
/** QQ、TIM 存在的类 */
|
/** QQ、TIM 存在的类 */
|
||||||
const val JumpActivityClass = "${PackageName.QQ}.activity.JumpActivity"
|
const val JumpActivityClass = "${PackageName.QQ}.activity.JumpActivity"
|
||||||
|
|
||||||
/** QQ、TIM 存在的类 */
|
/** QQ、TIM 存在的类 (NT 版本不再存在) */
|
||||||
private const val QQSettingSettingActivityClass = "${PackageName.QQ}.activity.QQSettingSettingActivity"
|
private const val QQSettingSettingActivityClass = "${PackageName.QQ}.activity.QQSettingSettingActivity"
|
||||||
|
|
||||||
/** QQ 新版存在的类 (Pad 模式) */
|
/** QQ 新版存在的类 (Pad 模式 - NT 版本不再存在) */
|
||||||
private const val QQSettingSettingFragmentClass = "${PackageName.QQ}.fragment.QQSettingSettingFragment"
|
private const val QQSettingSettingFragmentClass = "${PackageName.QQ}.fragment.QQSettingSettingFragment"
|
||||||
|
|
||||||
/** QQ、TIM 存在的类 */
|
/** QQ、TIM 存在的类 (NT 版本不再存在) */
|
||||||
private const val AboutActivityClass = "${PackageName.QQ}.activity.AboutActivity"
|
private const val AboutActivityClass = "${PackageName.QQ}.activity.AboutActivity"
|
||||||
|
|
||||||
|
/** QQ 新版本存在的类 */
|
||||||
|
private const val GeneralSettingActivityClass = "${PackageName.QQ}.activity.GeneralSettingActivity"
|
||||||
|
|
||||||
|
/** QQ 新版本 (NT) 存在的类 */
|
||||||
|
private const val MainSettingFragmentClass = "${PackageName.QQ}.setting.main.MainSettingFragment"
|
||||||
|
|
||||||
|
/** QQ 新版本 (NT) 存在的类 */
|
||||||
|
private const val MainSettingConfigProviderClass = "${PackageName.QQ}.setting.main.MainSettingConfigProvider"
|
||||||
|
|
||||||
/** QQ、TIM 新版本存在的类 */
|
/** QQ、TIM 新版本存在的类 */
|
||||||
private const val FormSimpleItemClass = "${PackageName.QQ}.widget.FormSimpleItem"
|
private const val FormSimpleItemClass = "${PackageName.QQ}.widget.FormSimpleItem"
|
||||||
|
|
||||||
@@ -78,8 +91,7 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
private const val CoreService_KernelServiceClass = "${PackageName.QQ}.app.CoreService\$KernelService"
|
private const val CoreService_KernelServiceClass = "${PackageName.QQ}.app.CoreService\$KernelService"
|
||||||
|
|
||||||
/** 根据多个版本存的不同的类 */
|
/** 根据多个版本存的不同的类 */
|
||||||
private val BaseChatPieClass =
|
private val BaseChatPieClass = VariousClass("${PackageName.QQ}.activity.aio.core.BaseChatPie", "${PackageName.QQ}.activity.BaseChatPie")
|
||||||
VariousClass("${PackageName.QQ}.activity.aio.core.BaseChatPie", "${PackageName.QQ}.activity.BaseChatPie")
|
|
||||||
|
|
||||||
/** 一个内部进程的名称 (与 X5 浏览器内核有关) */
|
/** 一个内部进程的名称 (与 X5 浏览器内核有关) */
|
||||||
private val privilegedProcessName = "$packageName:privileged_process"
|
private val privilegedProcessName = "$packageName:privileged_process"
|
||||||
@@ -93,6 +105,14 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
*/
|
*/
|
||||||
private val isQQ get() = packageName == PackageName.QQ
|
private val isQQ get() = packageName == PackageName.QQ
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前是否为 QQ 的 NT 版本
|
||||||
|
*
|
||||||
|
* 在 QQ NT 中 [AboutActivityClass] 已被移除 - 以此作为判断条件
|
||||||
|
* @return [Boolean]
|
||||||
|
*/
|
||||||
|
private val isQQNTVersion get() = isQQ && AboutActivityClass.hasClass().not()
|
||||||
|
|
||||||
/** 当前宿主的版本 */
|
/** 当前宿主的版本 */
|
||||||
private var hostVersionName = "<unknown>"
|
private var hostVersionName = "<unknown>"
|
||||||
|
|
||||||
@@ -122,128 +142,140 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
private fun hookQQBaseChatPie() {
|
private fun hookQQBaseChatPie() {
|
||||||
if (isQQ) when (hostVersionName) {
|
if (isQQ) when (hostVersionName) {
|
||||||
"8.0.0" -> {
|
"8.0.0" -> {
|
||||||
hookBaseChatPie(methodName = "bq")
|
hookBaseChatPie("bq")
|
||||||
hookBaseChatPie(methodName = "aL")
|
hookBaseChatPie("aL")
|
||||||
}
|
}
|
||||||
"8.0.5", "8.0.7" -> {
|
"8.0.5", "8.0.7" -> {
|
||||||
hookBaseChatPie(methodName = "bw")
|
hookBaseChatPie("bw")
|
||||||
hookBaseChatPie(methodName = "aQ")
|
hookBaseChatPie("aQ")
|
||||||
}
|
}
|
||||||
"8.1.0", "8.1.3" -> {
|
"8.1.0", "8.1.3" -> {
|
||||||
hookBaseChatPie(methodName = "bE")
|
hookBaseChatPie("bE")
|
||||||
hookBaseChatPie(methodName = "aT")
|
hookBaseChatPie("aT")
|
||||||
}
|
}
|
||||||
"8.1.5" -> {
|
"8.1.5" -> {
|
||||||
hookBaseChatPie(methodName = "bF")
|
hookBaseChatPie("bF")
|
||||||
hookBaseChatPie(methodName = "aT")
|
hookBaseChatPie("aT")
|
||||||
}
|
}
|
||||||
"8.1.8", "8.2.0", "8.2.6" -> {
|
"8.1.8", "8.2.0", "8.2.6" -> {
|
||||||
hookBaseChatPie(methodName = "bC")
|
hookBaseChatPie("bC")
|
||||||
hookBaseChatPie(methodName = "aT")
|
hookBaseChatPie("aT")
|
||||||
}
|
}
|
||||||
"8.2.7", "8.2.8", "8.2.11", "8.3.0" -> {
|
"8.2.7", "8.2.8", "8.2.11", "8.3.0" -> {
|
||||||
hookBaseChatPie(methodName = "bE")
|
hookBaseChatPie("bE")
|
||||||
hookBaseChatPie(methodName = "aV")
|
hookBaseChatPie("aV")
|
||||||
}
|
}
|
||||||
"8.3.5" -> {
|
"8.3.5" -> {
|
||||||
hookBaseChatPie(methodName = "bR")
|
hookBaseChatPie("bR")
|
||||||
hookBaseChatPie(methodName = "aX")
|
hookBaseChatPie("aX")
|
||||||
}
|
}
|
||||||
"8.3.6" -> {
|
"8.3.6" -> {
|
||||||
hookBaseChatPie(methodName = "cp")
|
hookBaseChatPie("cp")
|
||||||
hookBaseChatPie(methodName = "aX")
|
hookBaseChatPie("aX")
|
||||||
}
|
}
|
||||||
"8.3.9" -> {
|
"8.3.9" -> {
|
||||||
hookBaseChatPie(methodName = "cj")
|
hookBaseChatPie("cj")
|
||||||
hookBaseChatPie(methodName = "aT")
|
hookBaseChatPie("aT")
|
||||||
}
|
}
|
||||||
"8.4.1", "8.4.5" -> {
|
"8.4.1", "8.4.5" -> {
|
||||||
hookBaseChatPie(methodName = "ck")
|
hookBaseChatPie("ck")
|
||||||
hookBaseChatPie(methodName = "aT")
|
hookBaseChatPie("aT")
|
||||||
}
|
}
|
||||||
"8.4.8", "8.4.10", "8.4.17", "8.4.18", "8.5.0" -> {
|
"8.4.8", "8.4.10", "8.4.17", "8.4.18", "8.5.0" -> {
|
||||||
hookBaseChatPie(methodName = "remainScreenOn")
|
hookBaseChatPie("remainScreenOn")
|
||||||
hookBaseChatPie(methodName = "cancelRemainScreenOn")
|
hookBaseChatPie("cancelRemainScreenOn")
|
||||||
}
|
}
|
||||||
"8.5.5" -> {
|
"8.5.5" -> {
|
||||||
hookBaseChatPie(methodName = "bT")
|
hookBaseChatPie("bT")
|
||||||
hookBaseChatPie(methodName = "aN")
|
hookBaseChatPie("aN")
|
||||||
}
|
}
|
||||||
"8.6.0", "8.6.5", "8.7.0", "8.7.5", "8.7.8", "8.8.0", "8.8.3", "8.8.5" -> {
|
"8.6.0", "8.6.5", "8.7.0", "8.7.5", "8.7.8", "8.8.0", "8.8.3", "8.8.5" -> {
|
||||||
hookBaseChatPie(methodName = "ag")
|
hookBaseChatPie("ag")
|
||||||
hookBaseChatPie(methodName = "ah")
|
hookBaseChatPie("ah")
|
||||||
}
|
}
|
||||||
"8.8.11", "8.8.12" -> {
|
"8.8.11", "8.8.12" -> {
|
||||||
hookBaseChatPie(methodName = "bc")
|
hookBaseChatPie("bc")
|
||||||
hookBaseChatPie(methodName = "bd")
|
hookBaseChatPie("bd")
|
||||||
}
|
}
|
||||||
"8.8.17", "8.8.20" -> {
|
"8.8.17", "8.8.20" -> {
|
||||||
hookBaseChatPie(methodName = "bd")
|
hookBaseChatPie("bd")
|
||||||
hookBaseChatPie(methodName = "be")
|
hookBaseChatPie("be")
|
||||||
}
|
}
|
||||||
"8.8.23", "8.8.28" -> {
|
"8.8.23", "8.8.28" -> {
|
||||||
hookBaseChatPie(methodName = "bf")
|
hookBaseChatPie("bf")
|
||||||
hookBaseChatPie(methodName = "bg")
|
hookBaseChatPie("bg")
|
||||||
}
|
}
|
||||||
"8.8.33" -> {
|
"8.8.33" -> {
|
||||||
hookBaseChatPie(methodName = "bg")
|
hookBaseChatPie("bg")
|
||||||
hookBaseChatPie(methodName = "bh")
|
hookBaseChatPie("bh")
|
||||||
}
|
}
|
||||||
"8.8.35", "8.8.38" -> {
|
"8.8.35", "8.8.38" -> {
|
||||||
hookBaseChatPie(methodName = "bi")
|
hookBaseChatPie("bi")
|
||||||
hookBaseChatPie(methodName = "bj")
|
hookBaseChatPie("bj")
|
||||||
}
|
}
|
||||||
"8.8.50" -> {
|
"8.8.50" -> {
|
||||||
hookBaseChatPie(methodName = "bj")
|
hookBaseChatPie("bj")
|
||||||
hookBaseChatPie(methodName = "bk")
|
hookBaseChatPie("bk")
|
||||||
}
|
}
|
||||||
"8.8.55", "8.8.68", "8.8.80" -> {
|
"8.8.55", "8.8.68", "8.8.80" -> {
|
||||||
hookBaseChatPie(methodName = "bk")
|
hookBaseChatPie("bk")
|
||||||
hookBaseChatPie(methodName = "bl")
|
hookBaseChatPie("bl")
|
||||||
}
|
}
|
||||||
"8.8.83", "8.8.85", "8.8.88", "8.8.90" -> {
|
"8.8.83", "8.8.85", "8.8.88", "8.8.90" -> {
|
||||||
hookBaseChatPie(methodName = "bl")
|
hookBaseChatPie("bl")
|
||||||
hookBaseChatPie(methodName = "bm")
|
hookBaseChatPie("bm")
|
||||||
}
|
}
|
||||||
"8.8.93", "8.8.95" -> {
|
"8.8.93", "8.8.95" -> {
|
||||||
hookBaseChatPie(methodName = "J3")
|
hookBaseChatPie("J3")
|
||||||
hookBaseChatPie(methodName = "S")
|
hookBaseChatPie("S")
|
||||||
}
|
}
|
||||||
"8.8.98" -> {
|
"8.8.98" -> {
|
||||||
hookBaseChatPie(methodName = "M3")
|
hookBaseChatPie("M3")
|
||||||
hookBaseChatPie(methodName = "S")
|
hookBaseChatPie("S")
|
||||||
}
|
}
|
||||||
"8.9.0", "8.9.1", "8.9.2" -> {
|
"8.9.0", "8.9.1", "8.9.2" -> {
|
||||||
hookBaseChatPie(methodName = "N3")
|
hookBaseChatPie("N3")
|
||||||
hookBaseChatPie(methodName = "S")
|
hookBaseChatPie("S")
|
||||||
}
|
}
|
||||||
"8.9.3", "8.9.5" -> {
|
"8.9.3", "8.9.5" -> {
|
||||||
hookBaseChatPie(methodName = "H3")
|
hookBaseChatPie("H3")
|
||||||
hookBaseChatPie(methodName = "P")
|
hookBaseChatPie("P")
|
||||||
}
|
}
|
||||||
"8.9.8", "8.9.10" -> {
|
"8.9.8", "8.9.10" -> {
|
||||||
hookBaseChatPie(methodName = "H3")
|
hookBaseChatPie("H3")
|
||||||
hookBaseChatPie(methodName = "N")
|
hookBaseChatPie("N")
|
||||||
}
|
}
|
||||||
"8.9.13" -> {
|
"8.9.13" -> {
|
||||||
hookBaseChatPie(methodName = "y3")
|
hookBaseChatPie("y3")
|
||||||
hookBaseChatPie(methodName = "H")
|
hookBaseChatPie("H")
|
||||||
}
|
}
|
||||||
"8.9.15", "8.9.18", "8.9.19", "8.9.20" -> {
|
"8.9.15", "8.9.18", "8.9.19", "8.9.20" -> {
|
||||||
hookBaseChatPie(methodName = "w3")
|
hookBaseChatPie("w3")
|
||||||
hookBaseChatPie(methodName = "H")
|
hookBaseChatPie("H")
|
||||||
}
|
}
|
||||||
"8.9.23", "8.9.25" -> {
|
"8.9.23", "8.9.25" -> {
|
||||||
hookBaseChatPie(methodName = "z3")
|
hookBaseChatPie("z3")
|
||||||
hookBaseChatPie(methodName = "H")
|
hookBaseChatPie("H")
|
||||||
}
|
}
|
||||||
"8.9.28", "8.9.30", "8.9.33" -> {
|
"8.9.28", "8.9.30", "8.9.33" -> {
|
||||||
hookBaseChatPie(methodName = "A3")
|
hookBaseChatPie("A3")
|
||||||
hookBaseChatPie(methodName = "H")
|
hookBaseChatPie("H")
|
||||||
}
|
}
|
||||||
"8.9.35", "8.9.38", "8.9.50" -> {
|
"8.9.35", "8.9.38", "8.9.50" -> {
|
||||||
hookBaseChatPie(methodName = "B3")
|
hookBaseChatPie("B3")
|
||||||
hookBaseChatPie(methodName = "H")
|
hookBaseChatPie("H")
|
||||||
|
}
|
||||||
|
"8.9.53", "8.9.55", "8.9.58" -> {
|
||||||
|
hookBaseChatPie("C3")
|
||||||
|
hookBaseChatPie("H")
|
||||||
|
}
|
||||||
|
"8.9.63", "8.9.68" -> {
|
||||||
|
hookBaseChatPie("t3")
|
||||||
|
hookBaseChatPie("J")
|
||||||
|
}
|
||||||
|
"8.9.70" -> {
|
||||||
|
hookBaseChatPie("u3")
|
||||||
|
hookBaseChatPie("J")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
HookEntry.isHookClientSupport = false
|
HookEntry.isHookClientSupport = false
|
||||||
@@ -536,11 +568,68 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
}.ignoredHookClassNotFoundFailure()
|
}.ignoredHookClassNotFoundFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Hook QQ 的设置界面添加模块设置入口 (新版) */
|
||||||
|
private fun hookQQSettingsUi() {
|
||||||
|
if (MainSettingFragmentClass.hasClass().not()) return loggerE(msg = "Could not found main setting class, hook aborted")
|
||||||
|
val kotlinUnit = "kotlin.Unit"
|
||||||
|
val kotlinFunction0 = "kotlin.jvm.functions.Function0"
|
||||||
|
val simpleItemProcessorClass = searchClass {
|
||||||
|
from("${PackageName.QQ}.setting.processor").absolute()
|
||||||
|
constructor { param(ContextClass, IntType, CharSequenceClass, IntType) }
|
||||||
|
method {
|
||||||
|
param(kotlinFunction0)
|
||||||
|
returnType = UnitType
|
||||||
|
}
|
||||||
|
field().count { it >= 6 }
|
||||||
|
}.get() ?: return loggerE(msg = "Could not found processor class, hook aborted")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建入口点条目
|
||||||
|
* @param context 当前实例
|
||||||
|
* @return [Any]
|
||||||
|
*/
|
||||||
|
fun createTSEntryItem(context: Context): Any {
|
||||||
|
/** 为了使用图标资源 ID - 这里需要重新注入模块资源防止不生效 */
|
||||||
|
context.injectModuleAppResources()
|
||||||
|
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) {
|
||||||
|
param(ContextClass, IntType, CharSequenceClass, IntType)
|
||||||
|
}?.also { entryItem ->
|
||||||
|
val onClickMethod = simpleItemProcessorClass.method {
|
||||||
|
param { it[0].name == kotlinFunction0 }
|
||||||
|
paramCount = 1
|
||||||
|
returnType = UnitType
|
||||||
|
}.giveAll().lastOrNull() ?: error("Could not found processor method")
|
||||||
|
val proxyOnClick = Proxy.newProxyInstance(appClassLoader, arrayOf(onClickMethod.parameterTypes[0])) { any, method, args ->
|
||||||
|
if (method.name == "invoke") {
|
||||||
|
context.startModuleSettings()
|
||||||
|
kotlinUnit.toClass().field { name = "INSTANCE" }.get().any()
|
||||||
|
} else method.invoke(any, args)
|
||||||
|
}; onClickMethod.invoke(entryItem, proxyOnClick)
|
||||||
|
} ?: error("Could not create TSBattery entry item")
|
||||||
|
}
|
||||||
|
MainSettingConfigProviderClass.hook {
|
||||||
|
injectMember {
|
||||||
|
method {
|
||||||
|
param(ContextClass)
|
||||||
|
returnType = ListClass
|
||||||
|
}
|
||||||
|
afterHook {
|
||||||
|
val context = args().first().cast<Context>() ?: return@afterHook
|
||||||
|
val processor = result<MutableList<Any?>>() ?: return@afterHook
|
||||||
|
processor.add(1, processor[0]?.javaClass?.buildOf(arrayListOf<Any>().apply { add(createTSEntryItem(context)) }, "", "") {
|
||||||
|
param(ListClass, CharSequenceClass, CharSequenceClass)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook QQ 的设置界面添加模块设置入口
|
* Hook QQ 的设置界面添加模块设置入口 (旧版)
|
||||||
* @param instance 当前设置界面实例
|
* @param instance 当前设置界面实例
|
||||||
*/
|
*/
|
||||||
private fun hookQQSettingsUI(instance: Any?) {
|
private fun hookQQSettingsUiLegacy(instance: Any?) {
|
||||||
/** 当前的顶级 Item 实例 */
|
/** 当前的顶级 Item 实例 */
|
||||||
val formItemRefRoot = instance?.current()?.field {
|
val formItemRefRoot = instance?.current()?.field {
|
||||||
type { it.name == FormSimpleItemClass || it.name == FormCommonSingleLineItemClass }.index(num = 1)
|
type { it.name == FormSimpleItemClass || it.name == FormCommonSingleLineItemClass }.index(num = 1)
|
||||||
@@ -583,7 +672,9 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
/** 不注入此进程防止部分系统发生 X5 浏览器内核崩溃问题 */
|
/** 不注入此进程防止部分系统发生 X5 浏览器内核崩溃问题 */
|
||||||
if (processName.startsWith(privilegedProcessName)) return@onCreate
|
if (processName.startsWith(privilegedProcessName)) return@onCreate
|
||||||
ConfigData.init(context = this)
|
ConfigData.init(context = this)
|
||||||
registerModuleAppActivities(AboutActivityClass)
|
if (isQQNTVersion)
|
||||||
|
registerModuleAppActivities(GeneralSettingActivityClass)
|
||||||
|
else registerModuleAppActivities(AboutActivityClass)
|
||||||
if (ConfigData.isDisableAllHook) return@onCreate
|
if (ConfigData.isDisableAllHook) return@onCreate
|
||||||
hookSystemWakeLock()
|
hookSystemWakeLock()
|
||||||
hookQQBaseChatPie()
|
hookQQBaseChatPie()
|
||||||
@@ -604,26 +695,30 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
afterHook { instance<Activity>().jumpToModuleSettings() }
|
afterHook { instance<Activity>().jumpToModuleSettings() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** 将条目注入设置界面 (Activity) */
|
/** Hook 设置界面入口点 */
|
||||||
QQSettingSettingActivityClass.hook {
|
if (isQQNTVersion) hookQQSettingsUi()
|
||||||
injectMember {
|
else {
|
||||||
method {
|
/** 将条目注入设置界面 (Activity) */
|
||||||
name = "doOnCreate"
|
QQSettingSettingActivityClass.hook {
|
||||||
param(BundleClass)
|
injectMember {
|
||||||
|
method {
|
||||||
|
name = "doOnCreate"
|
||||||
|
param(BundleClass)
|
||||||
|
}
|
||||||
|
afterHook { hookQQSettingsUiLegacy(instance) }
|
||||||
}
|
}
|
||||||
afterHook { hookQQSettingsUI(instance) }
|
|
||||||
}
|
}
|
||||||
|
/** 将条目注入设置界面 (Fragment) */
|
||||||
|
QQSettingSettingFragmentClass.hook {
|
||||||
|
injectMember {
|
||||||
|
method {
|
||||||
|
name = "doOnCreateView"
|
||||||
|
paramCount = 3
|
||||||
|
}
|
||||||
|
afterHook { hookQQSettingsUiLegacy(instance) }
|
||||||
|
}
|
||||||
|
}.ignoredHookClassNotFoundFailure()
|
||||||
}
|
}
|
||||||
/** 将条目注入设置界面 (Fragment) */
|
|
||||||
QQSettingSettingFragmentClass.hook {
|
|
||||||
injectMember {
|
|
||||||
method {
|
|
||||||
name = "doOnCreateView"
|
|
||||||
paramCount = 3
|
|
||||||
}
|
|
||||||
afterHook { hookQQSettingsUI(instance) }
|
|
||||||
}
|
|
||||||
}.ignoredHookClassNotFoundFailure()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -47,7 +47,7 @@ private val ThemeUtilClass = VariousClass("${PackageName.QQ}.theme.ThemeUtil", "
|
|||||||
* QQ、TIM 主题是否为夜间模式
|
* QQ、TIM 主题是否为夜间模式
|
||||||
* @return [Boolean]
|
* @return [Boolean]
|
||||||
*/
|
*/
|
||||||
private fun Context.isQQNightMode() = runCatching {
|
fun Context.isQQNightMode() = runCatching {
|
||||||
ThemeUtilClass.get(classLoader).method {
|
ThemeUtilClass.get(classLoader).method {
|
||||||
name = "getUserCurrentThemeId"
|
name = "getUserCurrentThemeId"
|
||||||
paramCount = 1
|
paramCount = 1
|
||||||
|
@@ -55,7 +55,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
"8.8.93", "8.8.95", "8.8.98", "8.9.0", "8.9.1", "8.9.2", "8.9.3",
|
"8.8.93", "8.8.95", "8.8.98", "8.9.0", "8.9.1", "8.9.2", "8.9.3",
|
||||||
"8.9.5", "8.9.8", "8.9.10", "8.9.13", "8.9.15", "8.9.18", "8.9.19",
|
"8.9.5", "8.9.8", "8.9.10", "8.9.13", "8.9.15", "8.9.18", "8.9.19",
|
||||||
"8.9.20", "8.9.23", "8.9.25", "8.9.28", "8.9.30", "8.9.33",
|
"8.9.20", "8.9.23", "8.9.25", "8.9.28", "8.9.30", "8.9.33",
|
||||||
"8.9.35", "8.9.38", "8.9.50"
|
"8.9.35", "8.9.38", "8.9.50", "8.9.53", "8.9.55", "8.9.58",
|
||||||
|
"8.9.63", "8.9.68", "8.9.70"
|
||||||
)
|
)
|
||||||
private val qqSupportVersion by lazy {
|
private val qqSupportVersion by lazy {
|
||||||
if (qqSupportVersions.isNotEmpty()) {
|
if (qqSupportVersions.isNotEmpty()) {
|
||||||
|
BIN
app/src/main/res/mipmap-xxhdpi/ic_tsbattery_entry_day.png
Normal file
BIN
app/src/main/res/mipmap-xxhdpi/ic_tsbattery_entry_day.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_tsbattery_entry_night.png
Normal file
BIN
app/src/main/res/mipmap-xxhdpi/ic_tsbattery_entry_night.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
4
app/src/main/res/values/ids.xml
Normal file
4
app/src/main/res/values/ids.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<item name="tsbattery_qq_entry_item_id" type="id" />
|
||||||
|
</resources>
|
@@ -12,8 +12,8 @@ ext {
|
|||||||
targetSdk : 33
|
targetSdk : 33
|
||||||
]
|
]
|
||||||
app = [
|
app = [
|
||||||
versionName : '4.25',
|
versionName : '4.3',
|
||||||
versionCode : 28,
|
versionCode : 29,
|
||||||
signingConfigs: [
|
signingConfigs: [
|
||||||
secretConfigsDirPath : "${projectDir.getAbsolutePath()}/.secret",
|
secretConfigsDirPath : "${projectDir.getAbsolutePath()}/.secret",
|
||||||
secretConfigsFileName: "key_store_secret.json"
|
secretConfigsFileName: "key_store_secret.json"
|
||||||
|
Reference in New Issue
Block a user