mirror of
https://github.com/fankes/TSBattery.git
synced 2025-09-04 17:55:30 +08:00
feat: simple support dexkit test
This commit is contained in:
@@ -71,6 +71,7 @@ dependencies {
|
|||||||
implementation(com.highcapable.yukihookapi.api)
|
implementation(com.highcapable.yukihookapi.api)
|
||||||
ksp(com.highcapable.yukihookapi.ksp.xposed)
|
ksp(com.highcapable.yukihookapi.ksp.xposed)
|
||||||
implementation(com.fankes.projectpromote.project.promote)
|
implementation(com.fankes.projectpromote.project.promote)
|
||||||
|
implementation(org.luckypray.dexkit)
|
||||||
implementation(com.github.duanhong169.drawabletoolbox)
|
implementation(com.github.duanhong169.drawabletoolbox)
|
||||||
implementation(com.squareup.okhttp3.okhttp)
|
implementation(com.squareup.okhttp3.okhttp)
|
||||||
implementation(androidx.core.core.ktx)
|
implementation(androidx.core.core.ktx)
|
||||||
|
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@@ -37,6 +37,11 @@
|
|||||||
# 排除注入的 Activity
|
# 排除注入的 Activity
|
||||||
-keep class com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
|
-keep class com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
|
||||||
|
|
||||||
|
# DexKit
|
||||||
|
-keep class org.luckypray.dexkit.DexKitBridge {
|
||||||
|
native <methods>;
|
||||||
|
}
|
||||||
|
|
||||||
# 防止某些类被 R8 混淆 (可能是 BUG)
|
# 防止某些类被 R8 混淆 (可能是 BUG)
|
||||||
# FIXME: 已知问题字符串类名 (即使是常量) 也会被 R8 处理为混淆后的类名
|
# FIXME: 已知问题字符串类名 (即使是常量) 也会被 R8 处理为混淆后的类名
|
||||||
# FIXME: 所以目前只能把不允许 R8 处理的类 keep 掉,同时在当前模块中也不会被混淆就是了
|
# FIXME: 所以目前只能把不允许 R8 处理的类 keep 掉,同时在当前模块中也不会被混淆就是了
|
||||||
|
@@ -36,11 +36,11 @@ import com.fankes.tsbattery.R
|
|||||||
import com.fankes.tsbattery.const.ModuleVersion
|
import com.fankes.tsbattery.const.ModuleVersion
|
||||||
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.factory.hookSystemWakeLock
|
import com.fankes.tsbattery.hook.factory.hookSystemWakeLock
|
||||||
import com.fankes.tsbattery.hook.factory.isQQNightMode
|
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.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.yukihookapi.hook.bean.VariousClass
|
||||||
@@ -50,7 +50,6 @@ import com.highcapable.yukihookapi.hook.factory.current
|
|||||||
import com.highcapable.yukihookapi.hook.factory.field
|
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.method
|
||||||
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.BuildClass
|
import com.highcapable.yukihookapi.hook.type.android.BuildClass
|
||||||
@@ -67,6 +66,7 @@ import com.highcapable.yukihookapi.hook.type.java.ListClass
|
|||||||
import com.highcapable.yukihookapi.hook.type.java.LongType
|
import com.highcapable.yukihookapi.hook.type.java.LongType
|
||||||
import com.highcapable.yukihookapi.hook.type.java.StringClass
|
import com.highcapable.yukihookapi.hook.type.java.StringClass
|
||||||
import com.highcapable.yukihookapi.hook.type.java.UnitType
|
import com.highcapable.yukihookapi.hook.type.java.UnitType
|
||||||
|
import java.lang.reflect.Method
|
||||||
import java.lang.reflect.Proxy
|
import java.lang.reflect.Proxy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,6 +118,16 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DexKit 搜索结果数据实现类
|
||||||
|
*/
|
||||||
|
private object DexKitData {
|
||||||
|
var BaseChatPie_RemainScreenOnMethod: Method? = null
|
||||||
|
var BaseChatPie_CancelRemainScreenOnMethod: Method? = null
|
||||||
|
var SimpleItemProcessorClass: Class<*>? = null
|
||||||
|
var SimpleItemProcessorClass_OnClickMethod: Method? = null
|
||||||
|
}
|
||||||
|
|
||||||
/** 一个内部进程的名称 (与 X5 浏览器内核有关) */
|
/** 一个内部进程的名称 (与 X5 浏览器内核有关) */
|
||||||
private val privilegedProcessName = "$packageName:privileged_process"
|
private val privilegedProcessName = "$packageName:privileged_process"
|
||||||
|
|
||||||
@@ -147,6 +157,61 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
*/
|
*/
|
||||||
private fun Any.compatToActivity() = if (this is Activity) this else current().method { name = "getActivity"; superClass() }.invoke()
|
private fun Any.compatToActivity() = if (this is Activity) this else current().method { name = "getActivity"; superClass() }.invoke()
|
||||||
|
|
||||||
|
/** 使用 DexKit 进行搜索 */
|
||||||
|
private fun searchUsingDexKit() {
|
||||||
|
val classLoader = appClassLoader ?: return
|
||||||
|
DexKitHelper.create(this) {
|
||||||
|
BaseChatPieClass?.name?.also { baseChatPieClassName ->
|
||||||
|
DexKitData.BaseChatPie_RemainScreenOnMethod =
|
||||||
|
findMethod {
|
||||||
|
matcher {
|
||||||
|
declaredClass(baseChatPieClassName)
|
||||||
|
usingStrings("remainScreenOn")
|
||||||
|
paramCount = 0
|
||||||
|
returnType = UnitType.name
|
||||||
|
}
|
||||||
|
}.firstOrNull()?.getMethodInstance(classLoader)
|
||||||
|
DexKitData.BaseChatPie_CancelRemainScreenOnMethod =
|
||||||
|
findMethod {
|
||||||
|
matcher {
|
||||||
|
declaredClass(baseChatPieClassName)
|
||||||
|
usingStrings("cancelRemainScreenOn")
|
||||||
|
paramCount = 0
|
||||||
|
returnType = UnitType.name
|
||||||
|
}
|
||||||
|
}.firstOrNull()?.getMethodInstance(classLoader)
|
||||||
|
}
|
||||||
|
val kotlinFunction0 = "kotlin.jvm.functions.Function0"
|
||||||
|
findClass {
|
||||||
|
searchPackages("${PackageName.QQ}.setting.processor")
|
||||||
|
matcher {
|
||||||
|
methods {
|
||||||
|
add {
|
||||||
|
name = "<init>"
|
||||||
|
paramTypes(ContextClass.name, IntType.name, CharSequenceClass.name, IntType.name)
|
||||||
|
}
|
||||||
|
add {
|
||||||
|
paramTypes(kotlinFunction0)
|
||||||
|
returnType = UnitType.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields { count(6..Int.MAX_VALUE) }
|
||||||
|
}
|
||||||
|
}.firstOrNull()?.name?.also { className ->
|
||||||
|
DexKitData.SimpleItemProcessorClass = className.toClass()
|
||||||
|
DexKitData.SimpleItemProcessorClass_OnClickMethod =
|
||||||
|
findMethod {
|
||||||
|
matcher {
|
||||||
|
declaredClass = className
|
||||||
|
paramTypes(kotlinFunction0)
|
||||||
|
returnType = UnitType.name
|
||||||
|
usingNumbers(2)
|
||||||
|
}
|
||||||
|
}.firstOrNull()?.getMethodInstance(classLoader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 这个类 QQ 的 BaseChatPie 是控制聊天界面的
|
* 这个类 QQ 的 BaseChatPie 是控制聊天界面的
|
||||||
*
|
*
|
||||||
@@ -154,175 +219,16 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
*
|
*
|
||||||
* remainScreenOn、cancelRemainScreenOn
|
* remainScreenOn、cancelRemainScreenOn
|
||||||
*
|
*
|
||||||
* 这两个方法一个是挂起电源锁常驻亮屏
|
* 这两个方法一个是挂起电源锁常驻亮屏 - 一个是停止常驻亮屏
|
||||||
*
|
|
||||||
* 一个是停止常驻亮屏
|
|
||||||
*
|
|
||||||
* 不由分说每个版本混淆的方法名都会变
|
|
||||||
*
|
|
||||||
* 所以说每个版本重新适配 - 也可以提交分支帮我适配
|
|
||||||
*
|
|
||||||
* - ❗Hook 错了方法会造成闪退!
|
|
||||||
*/
|
*/
|
||||||
private fun hookQQBaseChatPie() {
|
private fun hookQQBaseChatPie() {
|
||||||
if (isQQ) when (hostVersionName) {
|
/**
|
||||||
"8.0.0" -> {
|
* 打印警告信息
|
||||||
hookBaseChatPie("bq")
|
* @param index 序号
|
||||||
hookBaseChatPie("aL")
|
*/
|
||||||
}
|
fun warn(index: Int) = YLog.warn("$hostVersionName [$index] not support!")
|
||||||
"8.0.5", "8.0.7" -> {
|
DexKitData.BaseChatPie_RemainScreenOnMethod?.hook()?.intercept() ?: warn(index = 0)
|
||||||
hookBaseChatPie("bw")
|
DexKitData.BaseChatPie_CancelRemainScreenOnMethod?.hook()?.intercept() ?: warn(index = 1)
|
||||||
hookBaseChatPie("aQ")
|
|
||||||
}
|
|
||||||
"8.1.0", "8.1.3" -> {
|
|
||||||
hookBaseChatPie("bE")
|
|
||||||
hookBaseChatPie("aT")
|
|
||||||
}
|
|
||||||
"8.1.5" -> {
|
|
||||||
hookBaseChatPie("bF")
|
|
||||||
hookBaseChatPie("aT")
|
|
||||||
}
|
|
||||||
"8.1.8", "8.2.0", "8.2.6" -> {
|
|
||||||
hookBaseChatPie("bC")
|
|
||||||
hookBaseChatPie("aT")
|
|
||||||
}
|
|
||||||
"8.2.7", "8.2.8", "8.2.11", "8.3.0" -> {
|
|
||||||
hookBaseChatPie("bE")
|
|
||||||
hookBaseChatPie("aV")
|
|
||||||
}
|
|
||||||
"8.3.5" -> {
|
|
||||||
hookBaseChatPie("bR")
|
|
||||||
hookBaseChatPie("aX")
|
|
||||||
}
|
|
||||||
"8.3.6" -> {
|
|
||||||
hookBaseChatPie("cp")
|
|
||||||
hookBaseChatPie("aX")
|
|
||||||
}
|
|
||||||
"8.3.9" -> {
|
|
||||||
hookBaseChatPie("cj")
|
|
||||||
hookBaseChatPie("aT")
|
|
||||||
}
|
|
||||||
"8.4.1", "8.4.5" -> {
|
|
||||||
hookBaseChatPie("ck")
|
|
||||||
hookBaseChatPie("aT")
|
|
||||||
}
|
|
||||||
"8.4.8", "8.4.10", "8.4.17", "8.4.18", "8.5.0" -> {
|
|
||||||
hookBaseChatPie("remainScreenOn")
|
|
||||||
hookBaseChatPie("cancelRemainScreenOn")
|
|
||||||
}
|
|
||||||
"8.5.5" -> {
|
|
||||||
hookBaseChatPie("bT")
|
|
||||||
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" -> {
|
|
||||||
hookBaseChatPie("ag")
|
|
||||||
hookBaseChatPie("ah")
|
|
||||||
}
|
|
||||||
"8.8.11", "8.8.12" -> {
|
|
||||||
hookBaseChatPie("bc")
|
|
||||||
hookBaseChatPie("bd")
|
|
||||||
}
|
|
||||||
"8.8.17", "8.8.20" -> {
|
|
||||||
hookBaseChatPie("bd")
|
|
||||||
hookBaseChatPie("be")
|
|
||||||
}
|
|
||||||
"8.8.23", "8.8.28" -> {
|
|
||||||
hookBaseChatPie("bf")
|
|
||||||
hookBaseChatPie("bg")
|
|
||||||
}
|
|
||||||
"8.8.33" -> {
|
|
||||||
hookBaseChatPie("bg")
|
|
||||||
hookBaseChatPie("bh")
|
|
||||||
}
|
|
||||||
"8.8.35", "8.8.38" -> {
|
|
||||||
hookBaseChatPie("bi")
|
|
||||||
hookBaseChatPie("bj")
|
|
||||||
}
|
|
||||||
"8.8.50" -> {
|
|
||||||
hookBaseChatPie("bj")
|
|
||||||
hookBaseChatPie("bk")
|
|
||||||
}
|
|
||||||
"8.8.55", "8.8.68", "8.8.80" -> {
|
|
||||||
hookBaseChatPie("bk")
|
|
||||||
hookBaseChatPie("bl")
|
|
||||||
}
|
|
||||||
"8.8.83", "8.8.85", "8.8.88", "8.8.90" -> {
|
|
||||||
hookBaseChatPie("bl")
|
|
||||||
hookBaseChatPie("bm")
|
|
||||||
}
|
|
||||||
"8.8.93", "8.8.95" -> {
|
|
||||||
hookBaseChatPie("J3")
|
|
||||||
hookBaseChatPie("S")
|
|
||||||
}
|
|
||||||
"8.8.98" -> {
|
|
||||||
hookBaseChatPie("M3")
|
|
||||||
hookBaseChatPie("S")
|
|
||||||
}
|
|
||||||
"8.9.0", "8.9.1", "8.9.2" -> {
|
|
||||||
hookBaseChatPie("N3")
|
|
||||||
hookBaseChatPie("S")
|
|
||||||
}
|
|
||||||
"8.9.3", "8.9.5" -> {
|
|
||||||
hookBaseChatPie("H3")
|
|
||||||
hookBaseChatPie("P")
|
|
||||||
}
|
|
||||||
"8.9.8", "8.9.10" -> {
|
|
||||||
hookBaseChatPie("H3")
|
|
||||||
hookBaseChatPie("N")
|
|
||||||
}
|
|
||||||
"8.9.13" -> {
|
|
||||||
hookBaseChatPie("y3")
|
|
||||||
hookBaseChatPie("H")
|
|
||||||
}
|
|
||||||
"8.9.15", "8.9.18", "8.9.19", "8.9.20" -> {
|
|
||||||
hookBaseChatPie("w3")
|
|
||||||
hookBaseChatPie("H")
|
|
||||||
}
|
|
||||||
"8.9.23", "8.9.25" -> {
|
|
||||||
hookBaseChatPie("z3")
|
|
||||||
hookBaseChatPie("H")
|
|
||||||
}
|
|
||||||
"8.9.28", "8.9.30", "8.9.33" -> {
|
|
||||||
hookBaseChatPie("A3")
|
|
||||||
hookBaseChatPie("H")
|
|
||||||
}
|
|
||||||
"8.9.35", "8.9.38", "8.9.50" -> {
|
|
||||||
hookBaseChatPie("B3")
|
|
||||||
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", "8.9.71", "8.9.73", "8.9.75", "8.9.76" -> {
|
|
||||||
hookBaseChatPie("u3")
|
|
||||||
hookBaseChatPie("J")
|
|
||||||
}
|
|
||||||
"8.9.78", "8.9.80", "8.9.83" -> {
|
|
||||||
hookBaseChatPie("v3")
|
|
||||||
hookBaseChatPie("I")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
HookEntry.isHookClientSupport = false
|
|
||||||
YLog.warn("$hostVersionName not supported!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 拦截 [BaseChatPieClass] 的目标方法体封装
|
|
||||||
* @param methodName 方法名
|
|
||||||
*/
|
|
||||||
private fun hookBaseChatPie(methodName: String) {
|
|
||||||
BaseChatPieClass?.method {
|
|
||||||
name = methodName
|
|
||||||
emptyParam()
|
|
||||||
returnType = UnitType
|
|
||||||
}?.hook()?.intercept()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hook CoreService QQ、TIM */
|
/** Hook CoreService QQ、TIM */
|
||||||
@@ -527,16 +433,7 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
private fun hookQQSettingsUi() {
|
private fun hookQQSettingsUi() {
|
||||||
if (MainSettingFragmentClass == null) return YLog.error("Could not found main setting class, hook aborted")
|
if (MainSettingFragmentClass == null) return YLog.error("Could not found main setting class, hook aborted")
|
||||||
val kotlinUnit = "kotlin.Unit"
|
val kotlinUnit = "kotlin.Unit"
|
||||||
val kotlinFunction0 = "kotlin.jvm.functions.Function0"
|
val simpleItemProcessorClass = DexKitData.SimpleItemProcessorClass ?: return YLog.error("Could not found processor class, hook aborted")
|
||||||
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 YLog.error("Could not found processor class, hook aborted")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建入口点条目
|
* 创建入口点条目
|
||||||
@@ -550,11 +447,7 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
return simpleItemProcessorClass.buildOf(context, R.id.tsbattery_qq_entry_item_id, "TSBattery", iconResId) {
|
return simpleItemProcessorClass.buildOf(context, R.id.tsbattery_qq_entry_item_id, "TSBattery", iconResId) {
|
||||||
param(ContextClass, IntType, CharSequenceClass, IntType)
|
param(ContextClass, IntType, CharSequenceClass, IntType)
|
||||||
}?.also { entryItem ->
|
}?.also { entryItem ->
|
||||||
val onClickMethod = simpleItemProcessorClass.method {
|
val onClickMethod = DexKitData.SimpleItemProcessorClass_OnClickMethod ?: error("Could not found processor method")
|
||||||
param { it[0].name == kotlinFunction0 }
|
|
||||||
paramCount = 1
|
|
||||||
returnType = UnitType
|
|
||||||
}.giveAll().firstOrNull() ?: 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()
|
||||||
@@ -613,6 +506,7 @@ object QQTIMHooker : YukiBaseHooker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onHook() {
|
override fun onHook() {
|
||||||
|
searchUsingDexKit()
|
||||||
onAppLifecycle(isOnFailureThrowToApp = false) {
|
onAppLifecycle(isOnFailureThrowToApp = false) {
|
||||||
attachBaseContext { baseContext, hasCalledSuper ->
|
attachBaseContext { baseContext, hasCalledSuper ->
|
||||||
if (hasCalledSuper.not()) baseConfiguration = baseContext.resources.configuration
|
if (hasCalledSuper.not()) baseConfiguration = baseContext.resources.configuration
|
||||||
|
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||||
|
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||||
|
* https://github.com/fankes/TSBattery
|
||||||
|
*
|
||||||
|
* This software is non-free but opensource software: you can redistribute it
|
||||||
|
* and/or modify it under the terms of the GNU Affero General Public License
|
||||||
|
* as published by the Free Software Foundation; either
|
||||||
|
* version 3 of the License, or any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* and eula along with this software. If not, see
|
||||||
|
* <https://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
* This file is created by fankes on 2023/10/8.
|
||||||
|
*/
|
||||||
|
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
|
package com.fankes.tsbattery.hook.helper
|
||||||
|
|
||||||
|
import com.highcapable.yukihookapi.hook.log.YLog
|
||||||
|
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||||
|
import org.luckypray.dexkit.DexKitBridge
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DexKit 工具类
|
||||||
|
*/
|
||||||
|
object DexKitHelper {
|
||||||
|
|
||||||
|
/** 是否已装载 */
|
||||||
|
private var isLoaded = false
|
||||||
|
|
||||||
|
/** 装载 */
|
||||||
|
fun load() {
|
||||||
|
if (isLoaded) return
|
||||||
|
runCatching {
|
||||||
|
System.loadLibrary("dexkit")
|
||||||
|
isLoaded = true
|
||||||
|
}.onFailure { YLog.error("Load DexKit failed!", it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 [DexKitBridge]
|
||||||
|
* @param param 当前实例
|
||||||
|
* @param initiate 方法体
|
||||||
|
*/
|
||||||
|
fun create(param: PackageParam, initiate: DexKitBridge.() -> Unit) {
|
||||||
|
load()
|
||||||
|
runCatching { DexKitBridge.create(param.appInfo.sourceDir)?.use { initiate(it) } }
|
||||||
|
}
|
||||||
|
}
|
@@ -50,29 +50,7 @@ import com.highcapable.yukihookapi.YukiHookAPI
|
|||||||
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val QQ_SUPPORT_VERSION = "理论支持 8.0.0+ 及以上版本。"
|
||||||
private val qqSupportVersions = arrayOf(
|
|
||||||
"8.0.0", "8.0.5", "8.0.7", "8.1.0", "8.1.3", "8.1.5", "8.1.8",
|
|
||||||
"8.2.0", "8.2.6", "8.2.7", "8.2.8", "8.2.11", "8.3.0", "8.3.5",
|
|
||||||
"8.3.6", "8.3.9", "8.4.1", "8.4.5", "8.4.8", "8.4.10", "8.4.17",
|
|
||||||
"8.4.18", "8.5.0", "8.5.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", "8.8.11", "8.8.12", "8.8.17",
|
|
||||||
"8.8.20", "8.8.23", "8.8.28", "8.8.33", "8.8.35", "8.8.38", "8.8.50",
|
|
||||||
"8.8.55", "8.8.68", "8.8.80", "8.8.83", "8.8.85", "8.8.88", "8.8.90",
|
|
||||||
"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.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.53", "8.9.55", "8.9.58", "8.9.63", "8.9.68",
|
|
||||||
"8.9.70", "8.9.71", "8.9.73", "8.9.75", "8.9.76", "8.9.78",
|
|
||||||
"8.9.80", "8.9.83"
|
|
||||||
)
|
|
||||||
private val qqSupportVersion by lazy {
|
|
||||||
if (qqSupportVersions.isNotEmpty()) {
|
|
||||||
var value = ""
|
|
||||||
qqSupportVersions.forEach { value += "$it、" }
|
|
||||||
"${value.trim().let { it.substring(0, it.lastIndex) }}\n\n其余版本请自行测试是否有效。"
|
|
||||||
} else "empty"
|
|
||||||
}
|
|
||||||
private const val TIM_SUPPORT_VERSION = "2+、3+ (并未完全测试每个版本)。"
|
private const val TIM_SUPPORT_VERSION = "2+、3+ (并未完全测试每个版本)。"
|
||||||
private const val WECHAT_SUPPORT_VERSION = "全版本仅支持基础省电,更多功能依然画饼。"
|
private const val WECHAT_SUPPORT_VERSION = "全版本仅支持基础省电,更多功能依然画饼。"
|
||||||
}
|
}
|
||||||
@@ -131,7 +109,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
binding.mainQqItem.setOnClickListener {
|
binding.mainQqItem.setOnClickListener {
|
||||||
showDialog {
|
showDialog {
|
||||||
title = "兼容的 QQ 版本"
|
title = "兼容的 QQ 版本"
|
||||||
msg = qqSupportVersion
|
msg = QQ_SUPPORT_VERSION
|
||||||
confirmButton(text = "我知道了")
|
confirmButton(text = "我知道了")
|
||||||
}
|
}
|
||||||
/** 振动提醒 */
|
/** 振动提醒 */
|
||||||
|
@@ -46,6 +46,9 @@ libraries:
|
|||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
ksp-xposed:
|
ksp-xposed:
|
||||||
version-ref: <this>::api
|
version-ref: <this>::api
|
||||||
|
org.luckypray:
|
||||||
|
dexkit:
|
||||||
|
version: 2.0.0-rc4
|
||||||
com.github.duanhong169:
|
com.github.duanhong169:
|
||||||
drawabletoolbox:
|
drawabletoolbox:
|
||||||
version: 1.0.7
|
version: 1.0.7
|
||||||
|
Reference in New Issue
Block a user