refactor: replace de.rebv.android.xposed with Xposed compat for EzXHelper-1.x

This commit is contained in:
ACh Sulfate
2024-07-22 14:04:14 +08:00
parent bdabefaa24
commit deed89ab18
16 changed files with 3906 additions and 4 deletions

View File

@@ -248,6 +248,9 @@ kotlin {
sourceSets.configureEach {
kotlin.srcDir("$buildDir/generated/ksp/$name/kotlin/")
}
sourceSets.main {
kotlin.srcDir(File(rootDir, "libs/ezxhelper/src/main/java"))
}
}
dependencies {
@@ -255,8 +258,6 @@ dependencies {
compileOnly(projects.loader.hookapi)
runtimeOnly(projects.loader.sbl)
implementation(projects.loader.startup)
// TODO: 2024-07-21 remove libs.xposed.api once refactor done
compileOnly(libs.xposed.api)
// ksp
ksp(projects.libs.ksp)
// host stub
@@ -278,7 +279,6 @@ dependencies {
implementation(libs.colorpicker)
implementation(libs.material.dialogs.core)
implementation(libs.material.dialogs.input)
implementation(libs.ezXHelper)
// festival title
implementation(libs.confetti)
implementation(libs.weatherView)

View File

@@ -34,7 +34,6 @@ material-dialogs-input = { module = "com.afollestad.material-dialogs:input", ver
xposed-api = { module = "de.robv.android.xposed:api", version = "82" }
flexbox = { module = "com.google.android.flexbox:flexbox", version = "3.0.0" }
colorpicker = { module = "com.jaredrummler:colorpicker", version = "1.1.0" }
ezXHelper = { module = "com.github.kyuubiran:EzXHelper", version = "1.0.3" }
confetti = { module = "com.github.jinatonic.confetti:confetti", version = "1.1.2" }
weatherView = { module = "com.github.MatteoBattilana:WeatherView", version = "3.0.0" }
sealedEnum-runtime = { module = "com.github.livefront.sealed-enum:runtime", version.ref = "sealedEnum" }

View File

@@ -0,0 +1,20 @@
package com.github.kyuubiran.ezxhelper.init
import android.content.Context
import android.content.res.Resources
import io.github.qauxv.util.hostInfo
object InitFields {
/**
* 宿主全局AppContext
*/
val appContext: Context
get() = hostInfo.application
/**
* 调用本库加载类函数时使用的类加载器
*/
lateinit var ezXClassLoader: ClassLoader
internal set
}

View File

@@ -0,0 +1,49 @@
package com.github.kyuubiran.ezxhelper.utils
import android.content.Context
import android.content.res.Resources
import android.os.Handler
import android.os.Looper
import android.widget.Toast
val mainHandler: Handler by lazy {
Handler(Looper.getMainLooper())
}
val runtimeProcess: Runtime by lazy {
Runtime.getRuntime()
}
/**
* 扩展函数 将函数放到主线程执行 如UI更新、显示Toast等
*/
fun Runnable.postOnMainThread() {
if (Looper.myLooper() == Looper.getMainLooper()) {
this.run()
} else {
mainHandler.post(this)
}
}
fun runOnMainThread(runnable: Runnable) {
runnable.postOnMainThread()
}
/**
* 扩展函数 显示一个Toast
* @param msg Toast显示的消息
* @param length Toast显示的时长
*/
fun Context.showToast(msg: String, length: Int = Toast.LENGTH_SHORT) = runOnMainThread {
Toast.makeText(this, msg, length).show()
}
/**
* 扩展函数 显示一个Toast
* @param msg Toast显示的消息
* @param args 格式化的参数
* @param length Toast显示的时长
*/
fun Context.showToast(msg: String, vararg args: Any?, length: Int = Toast.LENGTH_SHORT) = runOnMainThread {
Toast.makeText(this, msg.format(args), length).show()
}

View File

@@ -0,0 +1,113 @@
package com.github.kyuubiran.ezxhelper.utils
import com.github.kyuubiran.ezxhelper.init.InitFields
import io.github.qauxv.util.xpcompat.XposedHelpers
/**
* 通过模块加载类
* @param clzName 类名
* @param clzLoader 类加载器
* @return 被加载的类
* @throws IllegalArgumentException 类名为空
* @throws ClassNotFoundException 未找到类
*/
fun loadClass(clzName: String, clzLoader: ClassLoader = InitFields.ezXClassLoader): Class<*> {
if (clzName.isBlank()) throw IllegalArgumentException("Class name must not be null or empty!")
return clzLoader.loadClass(clzName)
}
/**
* 尝试加载列表中的一个类
* @param clzName 类名
* @param clzLoader 类加载器
* @return 第一个成功被加载的类
*/
fun loadClassAny(
vararg clzName: String,
clzLoader: ClassLoader = InitFields.ezXClassLoader
): Class<*> = clzName.firstNotNullOfOrNull { loadClassOrNull(it, clzLoader) }
?: throw ClassNotFoundException()
/**
* 尝试加载列表中的一个类 失败则返回null
* @param clzName 类名
* @param clzLoader 类加载器
* @return 第一个成功被加载的类或者null
*/
fun loadClassAnyOrNull(
vararg clzName: String,
clzLoader: ClassLoader = InitFields.ezXClassLoader
): Class<*>? = clzName.firstNotNullOfOrNull { loadClassOrNull(it, clzLoader) }
/**
* 尝试加载一个类 如果失败则返回null
* @param clzName 类名
* @param clzLoader 类加载器
* @return 被加载的类
*/
fun loadClassOrNull(
clzName: String,
clzLoader: ClassLoader = InitFields.ezXClassLoader
): Class<*>? {
if (clzName.isBlank()) throw IllegalArgumentException("Class name must not be null or empty!")
return XposedHelpers.findClassIfExists(clzName, clzLoader)
}
/**
* 扩展函数 加载数组中的所有类
* @param clzLoader 类加载器
* @return 类数组
*/
fun Array<String>.loadAllClasses(clzLoader: ClassLoader = InitFields.ezXClassLoader): Array<Class<*>> {
return Array(this.size) { i -> loadClass(this[i], clzLoader) }
}
fun Iterable<String>.loadAllClasses(clzLoader: ClassLoader = InitFields.ezXClassLoader): List<Class<*>> {
return this.map { loadClass(it, clzLoader) }
}
/**
* 扩展函数 尝试加载数组中的所有类
* @param clzLoader 类加载器
* @return 加载成功的类数组
*/
fun Array<String>.loadClassesIfExists(clzLoader: ClassLoader = InitFields.ezXClassLoader): Array<Class<*>> {
return this.mapNotNull { loadClassOrNull(it, clzLoader) }.toTypedArray()
}
fun Iterable<String>.loadClassesIfExists(clzLoader: ClassLoader = InitFields.ezXClassLoader): List<Class<*>> {
return this.mapNotNull { loadClassOrNull(it, clzLoader) }
}
/**
* 尝试加载数组中的一个类
* @param clzLoader 类加载器
* @return 第一个成功被加载的类
*/
@JvmName("loadClassAnyFromArray")
fun Array<String>.loadClassAny(clzLoader: ClassLoader = InitFields.ezXClassLoader): Class<*> =
this.firstNotNullOfOrNull { loadClassOrNull(it, clzLoader) } ?: throw ClassNotFoundException()
fun Iterable<String>.loadClassAny(clzLoader: ClassLoader = InitFields.ezXClassLoader): Class<*> =
this.firstNotNullOfOrNull { loadClassOrNull(it, clzLoader) } ?: throw ClassNotFoundException()
/**
* 尝试加载数组中的一个类 失败则返回null
* @param clzLoader 类加载器
* @return 第一个成功被加载的类或者null
*/
@JvmName("loadClassAnyOrFromList")
fun Array<String>.loadClassAnyOrNull(clzLoader: ClassLoader = InitFields.ezXClassLoader): Class<*>? =
this.firstNotNullOfOrNull { loadClassOrNull(it, clzLoader) }
fun Iterable<String>.loadClassAnyOrNull(clzLoader: ClassLoader = InitFields.ezXClassLoader): Class<*>? =
this.firstNotNullOfOrNull { loadClassOrNull(it, clzLoader) }
/**
* 扩展函数 判断自身是否为某个类的子类
* @param clzName 类名
* @param clzLoader 类加载器
* @return 是否为子类
*/
fun Class<*>.isChildClassOf(clzName: String, clzLoader: ClassLoader = InitFields.ezXClassLoader): Boolean =
loadClass(clzName, clzLoader).isAssignableFrom(this)

View File

@@ -0,0 +1,138 @@
package com.github.kyuubiran.ezxhelper.utils
import com.github.kyuubiran.ezxhelper.init.InitFields.ezXClassLoader
import java.io.Serializable
import java.lang.reflect.Field
import java.lang.reflect.Method
internal class DexDescriptor private constructor(sig: String, type: TYPE) :
Serializable, Cloneable {
private var name: String
private var declaringClass: String
private var signature: String
init {
when (type) {
TYPE.FIELD -> {
val retIdx: Int = sig.indexOf("->")
val typeIdx: Int = sig.indexOf(':', retIdx)
declaringClass = sig.substring(0, retIdx)
name = sig.substring(retIdx + 2, typeIdx)
signature = sig.substring(typeIdx + 1)
}
TYPE.METHOD -> {
val retIdx: Int = sig.indexOf("->")
val argsIdx: Int = sig.indexOf('(', retIdx)
declaringClass = sig.substring(0, retIdx)
name = sig.substring(retIdx + 2, argsIdx)
signature = sig.substring(argsIdx)
}
}
}
companion object {
private enum class TYPE {
METHOD, FIELD
}
fun newMethodDesc(sig: String): DexDescriptor {
return DexDescriptor(sig, TYPE.METHOD)
}
fun newFieldDesc(sig: String): DexDescriptor {
return DexDescriptor(sig, TYPE.FIELD)
}
}
override fun toString(): String {
return "$declaringClass->$name$signature"
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
return if (other == null || javaClass != other.javaClass) false else toString() == other.toString()
}
override fun hashCode(): Int {
return toString().hashCode()
}
private fun getDeclaringClassName(): String {
return declaringClass.substring(1, declaringClass.length - 1).replace('/', '.')
}
private fun getTypeSig(type: Class<*>): String {
if (type.isPrimitive) {
return when (type.name) {
Void.TYPE.name -> "V"
Integer.TYPE.name -> "I"
java.lang.Boolean.TYPE.name -> "Z"
java.lang.Byte.TYPE.name -> "B"
java.lang.Long.TYPE.name -> "L"
java.lang.Float.TYPE.name -> "F"
java.lang.Double.TYPE.name -> "D"
Character.TYPE.name -> "C"
java.lang.Short.TYPE.name -> "S"
else -> throw IllegalStateException("Type: " + type.name + " is not a primitive type")
}
}
return if (type.isArray) "[" + getTypeSig(type.componentType!!)
else "L" + type.name.replace('.', '/') + ";"
}
private fun getMethodTypeDesc(method: Method): String {
return buildString {
append("(")
method.parameterTypes.forEach {
append(getTypeSig(it))
}
append(")")
append(getTypeSig(method.returnType))
}
}
internal fun getMethod(clzLoader: ClassLoader = ezXClassLoader): Method {
try {
var clz =
loadClass(
declaringClass.substring(1, declaringClass.length - 1).replace('/', '.'),
clzLoader
)
clz.declaredMethods.forEach { m ->
if (m.name == name && getMethodTypeDesc(m) == signature) return m
}
while (clz.superclass?.also { clz = it } != null) {
clz.declaredMethods.forEach { m ->
if (m.isPrivate || m.isStatic) return@forEach
if (m.name == name && getMethodTypeDesc(m) == signature) return m
}
}
throw NoSuchMethodException("$declaringClass->$name$signature")
} catch (e: ClassNotFoundException) {
throw NoSuchMethodException("$declaringClass->$name$signature").initCause(e)
}
}
internal fun getField(clzLoader: ClassLoader = ezXClassLoader): Field {
try {
var clz =
loadClass(
declaringClass.substring(1, declaringClass.length - 1).replace('/', '.'),
clzLoader
)
clz.declaredFields.forEach { f ->
if (f.name == name && getTypeSig(f.type) == signature) return f
}
while (clz.superclass?.also { clz = it } != null) {
clz.declaredFields.forEach { f ->
if (f.isPrivate || f.isStatic) return@forEach
if (f.name == name && getTypeSig(f.type) == signature) return f
}
}
throw NoSuchFieldException("$declaringClass->$name$signature")
} catch (e: ClassNotFoundException) {
throw NoSuchFieldException("$declaringClass->$name$signature").initCause(e)
}
}
}

View File

@@ -0,0 +1,109 @@
@file:Suppress("NOTHING_TO_INLINE")
package com.github.kyuubiran.ezxhelper.utils
/*
import com.github.kyuubiran.ezxhelper.init.InitFields.ezXClassLoader
inline fun String.loadClass(cl: ClassLoader = ezXClassLoader) = loadClass(this, cl)
inline fun String.loadClassOrNull(cl: ClassLoader = ezXClassLoader) = loadClassOrNull(this, cl)
inline fun String.getDeclaredMethods(cl: ClassLoader = ezXClassLoader) =
getDeclaredMethods(this, cl)
inline fun String.findMethod(
cl: ClassLoader = ezXClassLoader,
findSuper: Boolean = false,
noinline condition: MethodCondition
) = findMethod(this, cl, findSuper, condition)
inline fun String.findMethodOrNull(
cl: ClassLoader = ezXClassLoader,
findSuper: Boolean = false,
noinline condition: MethodCondition
) = findMethodOrNull(this, cl, findSuper, condition)
inline fun String.findAllMethods(
cl: ClassLoader = ezXClassLoader,
findSuper: Boolean = false,
noinline condition: MethodCondition
) = findAllMethods(this, cl, findSuper, condition)
inline fun String.findField(
cl: ClassLoader = ezXClassLoader,
findSuper: Boolean = false,
noinline condition: FieldCondition
) = findField(this, cl, findSuper, condition)
inline fun String.findFieldOrNull(
cl: ClassLoader = ezXClassLoader,
findSuper: Boolean = false,
noinline condition: FieldCondition
) = findFieldOrNull(this, cl, findSuper, condition)
inline fun String.findAllFields(
cl: ClassLoader = ezXClassLoader,
findSuper: Boolean = false,
noinline condition: FieldCondition
) = findAllFields(this, cl, findSuper, condition)
inline fun String.findConstructor(
cl: ClassLoader = ezXClassLoader,
noinline condition: ConstructorCondition
) = findConstructor(this, cl, condition)
inline fun String.findConstructorOrNull(
cl: ClassLoader = ezXClassLoader,
noinline condition: ConstructorCondition
) = findConstructorOrNull(this, cl, condition)
inline fun String.findAllConstructors(
cl: ClassLoader = ezXClassLoader,
noinline condition: ConstructorCondition
) = findAllConstructors(this, cl, condition)
inline fun String.getMethodByDesc(cl: ClassLoader = ezXClassLoader) = getMethodByDesc(this, cl)
inline fun String.getFieldByDesc(cl: ClassLoader = ezXClassLoader) = getFieldByDesc(this, cl)
*/
inline fun Class<*>.findMethod(
findSuper: Boolean = false,
noinline condition: MethodCondition
) = findMethod(this, findSuper, condition)
inline fun Class<*>.findMethodOrNull(
findSuper: Boolean = false,
noinline condition: MethodCondition
) = findMethodOrNull(this, findSuper, condition)
inline fun Class<*>.findAllMethods(
findSuper: Boolean = false,
noinline condition: MethodCondition
) = findAllMethods(this, findSuper, condition)
inline fun Class<*>.findField(
findSuper: Boolean = false,
noinline condition: FieldCondition
) = findField(this, findSuper, condition)
inline fun Class<*>.findFieldOrNull(
findSuper: Boolean = false,
noinline condition: FieldCondition
) = findFieldOrNull(this, findSuper, condition)
inline fun Class<*>.findAllFields(
findSuper: Boolean = false,
noinline condition: FieldCondition
) = findAllFields(this, findSuper, condition)
inline fun Class<*>.findConstructor(
noinline condition: ConstructorCondition
) = findConstructor(this, condition)
inline fun Class<*>.findConstructorOrNull(
noinline condition: ConstructorCondition
) = findConstructorOrNull(this, condition)
inline fun Class<*>.findAllConstructors(
noinline condition: ConstructorCondition
) = findAllConstructors(this, condition)

View File

@@ -0,0 +1,762 @@
package com.github.kyuubiran.ezxhelper.utils
import com.github.kyuubiran.ezxhelper.init.InitFields
import java.lang.reflect.Field
import java.lang.reflect.Method
typealias FieldCondition = Field.() -> Boolean
/**
* 通过条件查找类中的属性
* @param clz 类
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的属性
* @throws NoSuchFieldException
*/
fun findField(
clz: Class<*>,
findSuper: Boolean = false,
condition: FieldCondition
): Field {
return findFieldOrNull(clz, findSuper, condition) ?: throw NoSuchFieldException()
}
/**
* 通过条件查找类中的属性
* @param clz 类
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的属性 未找到时返回null
*/
fun findFieldOrNull(
clz: Class<*>,
findSuper: Boolean = false,
condition: FieldCondition
): Field? {
var c = clz
c.declaredFields.firstOrNull { it.condition() }?.let {
it.isAccessible = true;return it
}
if (findSuper) {
while (c.superclass?.also { c = it } != null) {
c.declaredFields.firstOrNull { it.condition() }
?.let { it.isAccessible = true;return it }
}
}
return null
}
/**
* 通过条件查找类中的属性
* @param clzName 类名
* @param classLoader 类加载器
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的属性
* @throws NoSuchFieldException 未找到属性
*/
fun findField(
clzName: String,
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false,
condition: FieldCondition
): Field {
return findField(loadClass(clzName, classLoader), findSuper, condition)
}
/**
* 通过条件查找类中的属性
* @param clzName 类名
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的属性 未找到时返回null
*/
fun findFieldOrNull(
clzName: String,
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false,
condition: FieldCondition
): Field? {
return findFieldOrNull(loadClass(clzName, classLoader), findSuper, condition)
}
/**
* 扩展函数 通过条件查找属性
* @param condition 条件
* @return 符合条件的属性
* @throws NoSuchFieldException 未找到属性
*/
fun Array<Field>.findField(condition: FieldCondition): Field {
return this.firstOrNull { it.condition() }?.apply { isAccessible = true }
?: throw NoSuchFieldException()
}
fun Iterable<Field>.findField(condition: FieldCondition): Field {
return this.firstOrNull { it.condition() }?.apply { isAccessible = true }
?: throw NoSuchFieldException()
}
/**
* 扩展函数 通过条件查找属性
* @param condition 条件
* @return 符合条件的属性 未找到时返回null
*/
fun Array<Field>.findFieldOrNull(condition: FieldCondition): Field? =
this.firstOrNull { it.condition() }?.apply { isAccessible = true }
fun Iterable<Field>.findFieldOrNull(condition: FieldCondition): Field? =
this.firstOrNull { it.condition() }?.apply { isAccessible = true }
/**
* 扩展函数 通过遍历属性数组 返回符合条件的属性数组
* @param condition 条件
* @return 符合条件的属性数组
*/
fun Array<Field>.findAllFields(condition: FieldCondition): Array<Field> =
this.filter { it.condition() }.onEach { it.isAccessible = true }.toTypedArray()
fun Iterable<Field>.findAllFields(condition: FieldCondition): List<Field> =
this.filter { it.condition() }.map { it.isAccessible = true;it }
/**
* 通过条件获取属性数组
* @param clz 类
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的属性数组
*/
fun findAllFields(
clz: Class<*>,
findSuper: Boolean = false,
condition: FieldCondition
): List<Field> {
var c = clz
val arr = ArrayList<Field>()
arr.addAll(c.declaredFields.findAllFields(condition))
if (findSuper) {
while (c.superclass?.also { c = it } != null) {
arr.addAll(c.declaredFields.findAllFields(condition))
}
}
return arr
}
/**
* 通过条件获取属性数组
* @param clzName 类名
* @param classLoader 类加载器
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的属性数组
*/
fun findAllFields(
clzName: String,
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false,
condition: FieldCondition
): List<Field> = findAllFields(loadClass(clzName, classLoader), findSuper, condition)
/**
* 扩展函数 通过类或者对象获取单个属性
* @param fieldName 属性名
* @param isStatic 是否静态类型
* @param fieldType 属性类型
* @return 符合条件的属性
* @throws IllegalArgumentException 属性名为空
* @throws NoSuchFieldException 未找到属性
*/
fun Any.field(
fieldName: String,
isStatic: Boolean = false,
fieldType: Class<*>? = null
): Field {
if (fieldName.isBlank()) throw IllegalArgumentException("Field name must not be empty!")
var c: Class<*> = if (this is Class<*>) this else this.javaClass
do {
c.declaredFields
.filter { isStatic == it.isStatic }
.firstOrNull { (fieldType == null || it.type == fieldType) && (it.name == fieldName) }
?.let { it.isAccessible = true;return it }
} while (c.superclass?.also { c = it } != null)
throw NoSuchFieldException("Name: $fieldName,Static: $isStatic, Type: ${if (fieldType == null) "ignore" else fieldType.name}")
}
/**
* 扩展函数 通过类型获取属性
* @param type 类型
* @param isStatic 是否静态
* @return 符合条件的属性
* @throws NoSuchFieldException 未找到属性
*/
fun Any.getFieldByType(type: Class<*>, isStatic: Boolean = false): Field {
var c: Class<*> = if (this is Class<*>) this else this.javaClass
do {
c.declaredFields
.filter { isStatic == it.isStatic }
.firstOrNull { it.type == type }
?.let { it.isAccessible = true;return it }
} while (c.superclass?.also { c = it } != null)
throw NoSuchFieldException()
}
fun Any.getStaticFieldByType(type: Class<*>): Field = this.getFieldByType(type, true)
/**
* 扩展函数 通过类获取静态属性
* @param fieldName 属性名称
* @param type 属性类型
* @return 符合条件的属性
* @throws IllegalArgumentException 属性名为空
* @throws NoSuchFieldException 未找到属性
*/
fun Class<*>.staticField(fieldName: String, type: Class<*>? = null): Field {
if (fieldName.isBlank()) throw IllegalArgumentException("Field name must not be empty!")
return this.field(fieldName, true, type)
}
/**
* 扩展函数 获取静态对象 并转换为T?类型
* @return 成功时返回获取到的对象 失败时返回null
*/
@Suppress("UNCHECKED_CAST")
fun <T> Field.getStaticAs(): T? = this.run {
isAccessible = true
get(null) as T?
}
/**
* 扩展函数 获取非空对象
* @param obj 对象
* @return 成功时返回获取到的对象 失败时抛出异常
*/
fun Field.getNonNull(obj: Any?): Any = this.run {
isAccessible = true
get(obj)!!
}
/**
* 扩展函数 获取非空对象 并转换为T类型
* @param obj 对象
* @return 成功时返回获取到的对象 失败时抛出异常
*/
@Suppress("UNCHECKED_CAST")
fun <T> Field.getNonNullAs(obj: Any?): T = this.run {
isAccessible = true
get(obj)!! as T
}
/**
* 扩展函数 获取静态非空对象
* @return 成功时返回获取到的对象 失败时抛出异常
*/
fun Field.getStaticNonNull(): Any = this.run {
isAccessible = true
get(null)!!
}
/**
* 扩展函数 获取静态非空对象 并转换为T类型
* @return 成功时返回获取到的对象 失败时抛出异常
*/
@Suppress("UNCHECKED_CAST")
fun <T> Field.getStaticNonNullAs(): T = this.run {
isAccessible = true
get(null)!! as T
}
/**
* 扩展函数 获取对象 并转换为T?类型
* @param obj 对象
* @return 成功时返回获取到的对象 失败时返回null
*/
@Suppress("UNCHECKED_CAST")
fun <T> Field.getAs(obj: Any?): T? = this.run {
isAccessible = true
get(obj) as T?
}
/**
* 扩展函数 获取静态对象
* @return 成功时返回获取到的对象 失败时返回null
*/
fun Field.getStatic(): Any? = this.run {
isAccessible = true
get(null)
}
/**
* 深拷贝一个对象
* @param srcObj 源对象
* @param newObj 新对象
* @return 成功返回拷贝后的对象 失败返回null
*/
fun <T> fieldCpy(srcObj: T, newObj: T): T? = tryOrLogNull {
var clz: Class<*> = srcObj!!::class.java
var fields: Array<Field>
while (Object::class.java != clz) {
fields = clz.declaredFields
for (f in fields) {
f.isAccessible = true
f.set(newObj, f.get(srcObj))
}
clz = clz.superclass
}
newObj
}
typealias ObjectCondition = Any?.() -> Boolean
/**
* 强烈不推荐!!非常慢!!
*
* 扩展函数 遍历对象中的属性并返回符合条件的对象
* @param condition 条件
* @return 成功时返回找到的对象 失败时返回null
*/
fun Any.findObject(condition: ObjectCondition): Any? =
this.javaClass.declaredFields.firstNotNullOfOrNull {
it.isAccessible = true
it.get(this)?.let { o -> o.condition() } ?: false
}
/**
* 强烈不推荐!!非常慢!!
*
* 扩展函数 遍历对象中的属性并返回符合条件的对象
* @param fieldCond 属性条件
* @param objCond 对象条件
* @return 成功时返回找到的对象 失败时返回null
*/
fun Any.findObject(
fieldCond: FieldCondition,
objCond: ObjectCondition
): Any? = this.javaClass.declaredFields.firstNotNullOfOrNull f@{
if (!it.fieldCond()) return@f false
it.isAccessible = true
it.get(this)?.let { o -> o.objCond() } ?: false
}
/**
* 强烈不推荐!!非常慢!!
*
* 扩展函数 遍历类中的静态属性并返回符合条件的静态对象
* @param condition 条件
* @return 成功时返回找到的静态对象 失败时返回null
*/
fun Class<*>.findStaticObject(condition: ObjectCondition): Any? =
this.declaredFields.firstNotNullOfOrNull {
it.isAccessible = true
it.get(null)?.let(condition) ?: false
}
/**
* 强烈不推荐!!非常慢!!
*
* 扩展函数 遍历类中的静态属性并返回符合条件的静态对象
* @param fieldCond 属性条件
* @param objCond 对象条件
* @return 成功时返回找到的静态对象 失败时返回null
*/
fun Any.findStaticObject(
fieldCond: FieldCondition,
objCond: ObjectCondition
): Any? = this.javaClass.declaredFields.firstNotNullOfOrNull f@{
if (!it.fieldCond()) return@f false
it.isAccessible = true
it.get(null)?.let(objCond) ?: false
}
/**
* 扩展函数 获取实例化对象中的对象
* @param objName 对象名称
* @param type 类型
* @return 成功时返回获取到的对象 失败时返回null
* @throws IllegalArgumentException 目标对象名为空
*/
fun Any.getObjectOrNull(objName: String, type: Class<*>? = null): Any? {
if (objName.isBlank()) throw java.lang.IllegalArgumentException("Object name must not be empty!")
return tryOrLogNull { this.field(objName, fieldType = type).get(this) }
}
/**
* 扩展函数 获取实例化对象中的对象
* @param objName 对象名称
* @param type 类型
* @param T 转换的类型
* @return 成功时返回获取到的对象 失败时返回null
* @throws IllegalArgumentException 目标对象名为空
*/
@Suppress("UNCHECKED_CAST")
fun <T> Any.getObjectOrNullAs(objName: String, type: Class<*>? = null): T? {
return this.getObjectOrNull(objName, type) as T?
}
/**
* 扩展函数 获取实例化对象中的对象
* @param objName 对象名称
* @param type 类型
* @return 成功时返回获取到的对象 失败时抛出异常
* @throws IllegalArgumentException 目标对象名为空
*/
fun Any.getObject(objName: String, type: Class<*>? = null): Any {
if (objName.isBlank()) throw IllegalArgumentException("Object name must not be empty!")
return this.javaClass.field(objName, false, type).get(this)!!
}
/**
* 扩展函数 获取实例化对象中的对象 并转化为类型T
*
* @param objName 对象名称
* @param type 类型
* @return 成功时返回获取到的对象 失败时抛出异常
* @throws IllegalArgumentException 目标对象名为空
*/
@Suppress("UNCHECKED_CAST")
fun <T> Any.getObjectAs(objName: String, type: Class<*>? = null): T =
this.getObject(objName, type) as T
@Suppress("UNCHECKED_CAST")
fun <T> Any.getObjectAs(field: Field): T = field.get(this) as T
/**
* 扩展函数 获取实例化对象中的对象
* @param field 属性
* @return 成功时返回获取到的对象 失败时返回null
*/
fun Any.getObjectOrNull(field: Field): Any? = tryOrLogNull { field.let { it.isAccessible;it.get(this) } }
/**
* 扩展函数 获取实例化对象中的对象 并且转换为T?类型
*
* 注意: 请勿对Class使用此函数
* @param field 属性
* @param T 转换的类型
* @return 成功时返回获取到的对象 失败时返回null
*/
@Suppress("UNCHECKED_CAST")
fun <T> Any.getObjectOrNullAs(field: Field): T? = this.getObjectOrNull(field) as T?
/**
* 扩展函数 通过类型 获取实例化对象中的对象
*
* 不推荐使用 此函数只会返回第一次匹配到的对象
* @param type 类型
* @return 成功时返回获取到的对象 失败时返回null
*/
fun Any.getObjectOrNullByType(type: Class<*>): Any? = tryOrLogNull {
this.getFieldByType(type).get(this)
}
/**
* 扩展函数 通过类型 获取实例化对象中的对象 并转换为T?类型
*
* 不推荐使用 此函数只会返回第一次匹配到的对象
*
* 注意: 请勿对Class使用此函数
* @param type 类型
* @return 成功时返回获取到的对象 失败时返回null
*/
@Suppress("UNCHECKED_CAST")
fun <T> Any.getObjectOrNullByTypeAs(type: Class<*>): T? = this.getObjectOrNullByType(type) as T?
/**
* 扩展函数 通过类型 获取实例化对象中的对象
*
* 不推荐使用 此函数只会返回第一次匹配到的对象
*
* 注意: 请勿对Class使用此函数
* @param type 类型
* @return 成功时返回获取到的对象 失败时抛出异常
*/
fun Any.getObjectByType(type: Class<*>): Any = this.getFieldByType(type).get(this)!!
/**
* 扩展函数 通过类型 获取实例化对象中的对象 并转换为T类型
*
* 不推荐使用 此函数只会返回第一次匹配到的对象
*
* 注意: 请勿对Class使用此函数
* @param type 类型
* @return 成功时返回获取到的对象 失败时抛出异常
*/
@Suppress("UNCHECKED_CAST")
fun <T> Any.getObjectByTypeAs(type: Class<*>): T = this.getObjectByType(type) as T
/**
* 扩展函数 获取类中的静态对象
* @param objName 需要获取的对象名
* @param type 类型
* @return 成功时返回获取到的对象 失败时返回null
* @throws IllegalArgumentException 当名字为空时
*/
fun Class<*>.getStaticObjectOrNull(
objName: String,
type: Class<*>? = null
): Any? = tryOrLogNull {
if (objName.isBlank()) throw IllegalArgumentException("Object name must not be empty!")
tryOrNull { this.staticField(objName, type) }?.get(null)
}
/**
* 扩展函数 获取类中的静态对象 并且转换为T?类型
* @param objName 需要获取的对象名
* @param type 类型
* @param T 转换的类型
* @return 成功时返回获取到的对象 失败时返回null
* @throws IllegalArgumentException 当名字为空时
*/
@Suppress("UNCHECKED_CAST")
fun <T> Class<*>.getStaticObjectOrNullAs(
objName: String,
type: Class<*>? = null
): T? = this.getStaticObjectOrNull(objName, type) as T?
/**
* 扩展函数 获取类中的静态对象
* @param objName 需要获取的对象名
* @param type 类型
* @return 成功时返回获取到的对象 失败时抛出异常
* @throws IllegalArgumentException 当名字为空时
* @throws NoSuchFieldException 未找到属性
*/
fun Class<*>.getStaticObject(
objName: String,
type: Class<*>? = null
): Any {
if (objName.isBlank()) throw IllegalArgumentException("Object name must not be empty!")
return this.staticField(objName, type).get(this)!!
}
/**
* 扩展函数 获取类中的静态对象 并转换为T类型
* @param objName 需要获取的对象名
* @param type 类型
* @return 成功时返回获取到的对象 失败时抛出异常
* @throws IllegalArgumentException 当名字为空时
*/
@Suppress("UNCHECKED_CAST")
fun <T> Class<*>.getStaticObjectAs(
objName: String,
type: Class<*>? = null
): T = this.getStaticObject(objName, type) as T
/**
* 获取Field中的对象
* @param field 属性
* @return 返回获取到的对象(Nullable)
*/
fun getStaticObjectOrNull(field: Field): Any? = field.run {
isAccessible = true
get(null)
}
/**
* 获取Field中的对象 并转换为T?类型
* @param field 属性
* @return 返回获取到的对象(Nullable)
*/
@Suppress("UNCHECKED_CAST")
fun <T> getStaticObjectOrNullAs(field: Field): T? = getStaticObjectOrNull(field) as T?
/**
* 获取Field中的对象
* @param field 属性
* @return 成功时返回获取到的对象 失败时抛出异常
*/
fun getStaticObject(field: Field): Any = field.run {
isAccessible = true
get(null)!!
}
/**
* 获取Field中的对象 并转换为T类型
* @param field 属性
* @return 成功时返回获取到的对象 失败时抛出异常
*/
@Suppress("UNCHECKED_CAST")
fun <T> getStaticObjectAs(field: Field): T = getStaticObject(field) as T
/**
* 扩展函数 通过类型 获取类中的静态对象
*
* 不推荐使用 此函数只会返回第一次匹配到的对象
* @param type 类型
* @return 成功时返回获取到的对象 失败时抛出异常
*/
fun Class<*>.getStaticObjectByType(type: Class<*>): Any = this.getStaticFieldByType(type).get(null)!!
/**
* 扩展函数 通过类型 获取类中的静态对象 并转换为T类型
*
* 不推荐使用 此函数只会返回第一次匹配到的对象
* @param type 类型
* @return 成功时返回获取到的对象 失败时抛出异常
*/
@Suppress("UNCHECKED_CAST")
fun <T> Class<*>.getStaticObjectByTypeAs(type: Class<*>): T = this.getStaticFieldByType(type).get(null) as T
/**
* 扩展函数 通过类型 获取类中的静态对象
*
* 不推荐使用 此函数只会返回第一次匹配到的对象
* @param type 类型
* @return 成功时返回获取到的对象 失败时返回null
*/
fun Class<*>.getStaticObjectOrNullByType(type: Class<*>): Any? = tryOrLogNull {
this.getStaticFieldByType(type).get(null)
}
/**
* 扩展函数 通过类型 获取类中的静态对象 并转换为T类型
*
* 不推荐使用 此函数只会返回第一次匹配到的对象
* @param type 类型
* @return 成功时返回获取到的对象 失败时返回null
*/
@Suppress("UNCHECKED_CAST")
fun <T> Class<*>.getStaticObjectOrNullByTypeAs(type: Class<*>): T? = this.getStaticFieldByType(type) as T?
/**
* 扩展函数 设置对象中对象的值
*
* @param objName 需要设置的对象名称
* @param value 值
* @param fieldType 对象类型
* @throws IllegalArgumentException 对象名为空
*/
fun Any.putObject(objName: String, value: Any?, fieldType: Class<*>? = null) {
if (objName.isBlank()) throw IllegalArgumentException("Object name must not be empty!")
tryOrLog { this.field(objName, false, fieldType).set(this, value) }
}
/**
* 扩展函数 设置对象中对象的值
* @param field 属性
* @param value 值
*/
fun Any.putObject(field: Field, value: Any?) = tryOrLog {
field.let {
it.isAccessible = true
it.set(this, value)
}
}
/**
* 扩展函数 通过类型设置值
*
* 不推荐使用 只会设置第一个类型符合的对象的值
* @param value 值
* @param type 类型
*/
fun Any.putObjectByType(value: Any?, type: Class<*>) = tryOrLog {
this.getFieldByType(type).set(this, value)
}
/**
* 扩展函数 通过类型设置类中的静态对象的值
*
* 不推荐使用 只会设置第一个类型符合的对象的值
* @param value 值
* @param type 类型
*/
fun Class<*>.putStaticObjectByType(value: Any?, type: Class<*>) = tryOrLog {
this.getStaticFieldByType(type).set(null, value)
}
/**
* 扩展函数 设置类中静态对象值
* @param objName 需要设置的对象名称
* @param value 值
* @param fieldType 对象类型
* @throws IllegalArgumentException 对象名为空
*/
fun Class<*>.putStaticObject(objName: String, value: Any?, fieldType: Class<*>? = null) = tryOrLog {
if (objName.isBlank()) throw IllegalArgumentException("Object name must not be empty!")
try {
this.staticField(objName, fieldType)
} catch (e: NoSuchFieldException) {
return
}.set(null, value)
}
/**
* 扩展函数 查找符合条件的属性并获取对象
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的属性对象
* @throws NoSuchFieldException 未找到符合的属性
*/
fun Any.findFieldObject(findSuper: Boolean = false, condition: FieldCondition): Any =
this.javaClass.findField(findSuper, condition).get(this)!!
/**
* 扩展函数 查找符合条件的属性并获取对象 并转化为T类型
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的属性对象
* @throws NoSuchFieldException 未找到符合的属性
*/
@Suppress("UNCHECKED_CAST")
fun <T> Any.findFieldObjectAs(findSuper: Boolean = false, condition: FieldCondition): T =
this.javaClass.findField(findSuper, condition).get(this) as T
/**
* 扩展函数 查找符合条件的属性并获取对象
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的属性对象 未找到时返回null
*/
fun Any.findFieldObjectOrNull(findSuper: Boolean = false, condition: FieldCondition): Any? =
this.javaClass.findFieldOrNull(findSuper, condition)?.get(this)
/**
* 扩展函数 查找符合条件的属性并获取对象 并转化为T?类型
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的属性对象 未找到时返回null
*/
@Suppress("UNCHECKED_CAST")
fun <T> Any.findFieldObjectOrNullAs(findSuper: Boolean = false, condition: FieldCondition): T? =
this.javaClass.findFieldOrNull(findSuper, condition)?.get(this) as T?
/**
* 通过Descriptor获取属性
* @param desc Descriptor
* @param clzLoader 类加载器
* @return 找到的属性
* @throws NoSuchFieldException 未找到属性
*/
fun getFieldByDesc(desc: String, clzLoader: ClassLoader = InitFields.ezXClassLoader): Field =
DexDescriptor.newFieldDesc(desc).getField(clzLoader).apply { isAccessible = true }
/**
* 扩展函数 通过Descriptor获取属性
* @param desc Descriptor
* @return 找到的属性
* @throws NoSuchFieldException 未找到属性
*/
fun ClassLoader.getFieldByDesc(desc: String): Field = getFieldByDesc(desc, this)
/**
* 通过Descriptor获取属性
* @param desc Descriptor
* @param clzLoader 类加载器
* @return 找到的属性 未找到则返回null
*/
fun getFieldByDescOrNull(
desc: String,
clzLoader: ClassLoader = InitFields.ezXClassLoader
): Field? = runCatching { getFieldByDesc(desc, clzLoader) }.getOrNull()
/**
* 扩展函数 通过Descriptor获取属性
* @param desc Descriptor
* @return 找到的属性 未找到则返回null
*/
fun ClassLoader.getFieldByDescOrNull(desc: String): Field? = getFieldByDescOrNull(desc, this)

View File

@@ -0,0 +1,693 @@
package com.github.kyuubiran.ezxhelper.utils
import com.github.kyuubiran.ezxhelper.init.InitFields
import io.github.qauxv.loader.hookapi.IHookBridge
import io.github.qauxv.util.xpcompat.XC_MethodHook
import io.github.qauxv.util.xpcompat.XC_MethodHook.Unhook
import io.github.qauxv.util.xpcompat.XC_MethodReplacement
import io.github.qauxv.util.xpcompat.XposedBridge
import java.lang.reflect.Constructor
import java.lang.reflect.Method
typealias Hooker = (param: XC_MethodHook.MethodHookParam) -> Unit
typealias ReplaceHooker = (param: XC_MethodHook.MethodHookParam) -> Any?
/**
* 扩展函数 hook方法/构造
* @param hookCallback [XC_MethodHook]
* @return unhook [XC_MethodHook.Unhook]
*/
fun Method.hookMethod(hookCallback: XC_MethodHook): XC_MethodHook.Unhook {
return XposedBridge.hookMethod(this, hookCallback)
}
fun Constructor<*>.hookMethod(hookCallback: XC_MethodHook): XC_MethodHook.Unhook {
return XposedBridge.hookMethod(this, hookCallback)
}
inline fun hooker(crossinline hookCallback: Hooker): Hooker = object : Hooker {
override fun invoke(param: XC_MethodHook.MethodHookParam) {
hookCallback(param)
}
}
inline fun replaceHooker(crossinline hookCallback: ReplaceHooker):
ReplaceHooker = object : ReplaceHooker {
override fun invoke(param: XC_MethodHook.MethodHookParam): Any? {
return hookCallback(param)
}
}
/**
* 扩展函数 hook方法执行前
* @param priority 优先级 默认50
* @param hook [Hooker] hook具体实现
* @return unhook [XC_MethodHook.Unhook]
*/
fun Method.hookBefore(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hook: Hooker
): XC_MethodHook.Unhook {
return this.hookMethod(object : XC_MethodHook(priority) {
override fun beforeHookedMethod(param: MethodHookParam) = try {
hook(param)
} catch (thr: Throwable) {
Log.ex(thr)
}
})
}
fun Method.hookBefore(hooker: Hooker) = this.hookBefore(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 hook多个方法执行前
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun Array<Method>.hookBefore(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): Array<XC_MethodHook.Unhook> {
return this.map { it.hookBefore(priority, hooker) }.toTypedArray()
}
fun Array<Method>.hookBefore(hooker: Hooker) = this.hookBefore(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
fun Iterable<Method>.hookBefore(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): List<XC_MethodHook.Unhook> {
return this.map { it.hookBefore(priority, hooker) }
}
fun Iterable<Method>.hookBefore(hooker: Hooker) = this.hookBefore(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 hook构造执行前
* @param priority 优先级 默认50
* @param hook [Hooker] hook具体实现
* @return unhook [XC_MethodHook.Unhook]
*/
fun Constructor<*>.hookBefore(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hook: Hooker
): XC_MethodHook.Unhook {
return this.hookMethod(object : XC_MethodHook(priority) {
override fun beforeHookedMethod(param: MethodHookParam) = try {
hook(param)
} catch (thr: Throwable) {
Log.ex(thr)
}
})
}
fun Constructor<*>.hookBefore(hooker: Hooker) = this.hookBefore(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 hook多个构造执行前
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun Array<Constructor<*>>.hookBefore(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): Array<XC_MethodHook.Unhook> {
return this.map { it.hookBefore(priority, hooker) }.toTypedArray()
}
fun Array<Constructor<*>>.hookBefore(hooker: Hooker) = this.hookBefore(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
@JvmName("hookConstructorBefore")
fun Iterable<Constructor<*>>.hookBefore(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): List<XC_MethodHook.Unhook> {
return this.map { it.hookBefore(priority, hooker) }
}
@JvmName("hookConstructorBefore")
fun Iterable<Constructor<*>>.hookBefore(hooker: Hooker) = this.hookBefore(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 hook方法执行后
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhook [XC_MethodHook.Unhook]
*/
fun Method.hookAfter(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): XC_MethodHook.Unhook {
return this.hookMethod(object : XC_MethodHook(priority) {
override fun afterHookedMethod(param: MethodHookParam) = try {
hooker(param)
} catch (thr: Throwable) {
Log.ex(thr)
}
})
}
fun Method.hookAfter(hooker: Hooker) = this.hookAfter(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 hook多个方法执行后
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun Array<Method>.hookAfter(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): Array<XC_MethodHook.Unhook> {
return this.map { it.hookAfter(priority, hooker) }.toTypedArray()
}
fun Array<Method>.hookAfter(hooker: Hooker) = this.hookAfter(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
fun Iterable<Method>.hookAfter(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): List<XC_MethodHook.Unhook> {
return this.map { it.hookAfter(priority, hooker) }
}
fun Iterable<Method>.hookAfter(hooker: Hooker) = this.hookAfter(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 hook构造执行后
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhook [XC_MethodHook.Unhook]
*/
fun Constructor<*>.hookAfter(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): XC_MethodHook.Unhook {
return this.hookMethod(object : XC_MethodHook(priority) {
override fun afterHookedMethod(param: MethodHookParam) = try {
hooker(param)
} catch (thr: Throwable) {
Log.ex(thr)
}
})
}
fun Constructor<*>.hookAfter(hooker: Hooker) = this.hookAfter(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 hook多个构造执行后
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun Array<Constructor<*>>.hookAfter(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): Array<XC_MethodHook.Unhook> {
return this.map { it.hookAfter(priority, hooker) }.toTypedArray()
}
fun Array<Constructor<*>>.hookAfter(hooker: Hooker) = this.hookAfter(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
@JvmName("hookConstructorAfter")
fun Iterable<Constructor<*>>.hookAfter(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): List<XC_MethodHook.Unhook> {
return this.map { it.hookAfter(priority, hooker) }
}
@JvmName("hookConstructorAfter")
fun Iterable<Constructor<*>>.hookAfter(hooker: Hooker) = this.hookAfter(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 替换方法
* @param priority 优先级 默认50
* @param hook [Hooker] hook具体实现
* @return unhook [XC_MethodHook.Unhook]
*/
fun Method.hookReplace(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hook: ReplaceHooker
): XC_MethodHook.Unhook {
return this.hookMethod(object : XC_MethodReplacement(priority) {
override fun replaceHookedMethod(param: MethodHookParam): Any? = try {
hook(param)
} catch (thr: Throwable) {
Log.ex(thr)
}
})
}
fun Method.hookReplace(hooker: ReplaceHooker) = this.hookReplace(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 替换多个方法
*
* 注意: 会忽略hooker的返回值
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun Array<Method>.hookReplace(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: ReplaceHooker
): Array<XC_MethodHook.Unhook> {
return this.map { it.hookReplace(priority, hooker) }.toTypedArray()
}
fun Array<Method>.hookReplace(hooker: ReplaceHooker) = this.hookReplace(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
fun Iterable<Method>.hookReplace(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: ReplaceHooker
): List<XC_MethodHook.Unhook> {
return this.map { it.hookReplace(priority, hooker) }
}
fun Iterable<Method>.hookReplace(hooker: ReplaceHooker) = this.hookReplace(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 替换构造
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhook [XC_MethodHook.Unhook]
*/
fun Constructor<*>.hookReplace(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: ReplaceHooker
): XC_MethodHook.Unhook {
return this.hookMethod(object : XC_MethodReplacement(priority) {
override fun replaceHookedMethod(param: MethodHookParam): Any? = try {
hooker(param)
} catch (thr: Throwable) {
Log.ex(thr)
}
})
}
fun Constructor<*>.hookReplace(hooker: ReplaceHooker) = this.hookReplace(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 替换多个构造
*
* 注意: 会忽略hooker的返回值
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun Array<Constructor<*>>.hookReplace(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: ReplaceHooker
): Array<XC_MethodHook.Unhook> {
return this.map { it.hookReplace(priority, hooker) }.toTypedArray()
}
fun Array<Constructor<*>>.hookReplace(hooker: ReplaceHooker) = this.hookReplace(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
@JvmName("hookConstructorReplace")
fun Iterable<Constructor<*>>.hookReplace(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: ReplaceHooker
): List<XC_MethodHook.Unhook> {
return this.map { it.hookReplace(priority, hooker) }
}
@JvmName("hookConstructorReplace")
fun Iterable<Constructor<*>>.hookReplace(hooker: ReplaceHooker) = this.hookReplace(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 hook类的所有构造前
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun Class<*>.hookAllConstructorBefore(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): Array<XC_MethodHook.Unhook> {
return this.declaredConstructors.hookBefore(priority, hooker)
}
fun Class<*>.hookAllConstructorBefore(hooker: Hooker) = this.hookAllConstructorBefore(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 hook类的所有构造后
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun Class<*>.hookAllConstructorAfter(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): Array<XC_MethodHook.Unhook> {
return this.declaredConstructors.hookAfter(priority, hooker)
}
fun Class<*>.hookAllConstructorAfter(hooker: Hooker) = this.hookAllConstructorAfter(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* 扩展函数 替换类的所有构造
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun Class<*>.hookAllConstructorReplace(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: ReplaceHooker
): Array<XC_MethodHook.Unhook> {
return this.declaredConstructors.hookReplace(priority, hooker)
}
fun Class<*>.hookAllConstructorReplace(hooker: ReplaceHooker) = this.hookAllConstructorReplace(
IHookBridge.PRIORITY_DEFAULT,
hooker
)
/**
* hook类的所有构造前
* @param clzName 类名
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun hookAllConstructorBefore(
clzName: String,
clzLoader: ClassLoader = InitFields.ezXClassLoader,
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): Array<XC_MethodHook.Unhook> {
return loadClass(clzName, clzLoader).declaredConstructors.hookBefore(priority, hooker)
}
/**
* hook类的所有构造后
* @param clzName 类名
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun hookAllConstructorAfter(
clzName: String,
clzLoader: ClassLoader = InitFields.ezXClassLoader,
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): Array<XC_MethodHook.Unhook> {
return loadClass(clzName, clzLoader).declaredConstructors.hookAfter(priority, hooker)
}
/**
* 替换类的所有构造
* @param clzName 类名
* @param priority 优先级 默认50
* @param hooker [Hooker] hook具体实现
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun hookAllConstructorReplace(
clzName: String,
clzLoader: ClassLoader = InitFields.ezXClassLoader,
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: Hooker
): Array<XC_MethodHook.Unhook> {
return loadClass(clzName, clzLoader).declaredConstructors.hookReplace(priority, hooker)
}
/**
* 扩展函数 hook方法 使其直接返回一个值
* @param priority 优先级 默认50
* @param obj 要返回的值
* @return unhook [XC_MethodHook.Unhook]
*/
fun Method.hookReturnConstant(priority: Int = IHookBridge.PRIORITY_DEFAULT, obj: Any?): Unhook =
XposedBridge.hookMethod(this, XC_MethodReplacement.returnConstant(priority, obj))
fun Method.hookReturnConstant(obj: Any?) = this.hookReturnConstant(IHookBridge.PRIORITY_DEFAULT, obj)
/**
* 扩展函数 hook方法数组中的所有方法 使其直接返回一个值
* @param priority 优先级 默认50
* @param obj 要返回的值
* @return unhooks Array<[XC_MethodHook.Unhook]>
*/
fun Array<Method>.hookReturnConstant(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
obj: Any?
): Array<Unhook> =
this.map { XposedBridge.hookMethod(it, XC_MethodReplacement.returnConstant(priority, obj)) }
.toTypedArray()
fun Array<Method>.hookReturnConstant(obj: Any?) =
this.hookReturnConstant(IHookBridge.PRIORITY_DEFAULT, obj)
fun List<Method>.hookReturnConstant(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
obj: Any?
): List<Unhook> =
this.map { XposedBridge.hookMethod(it, XC_MethodReplacement.returnConstant(priority, obj)) }
fun List<Method>.hookReturnConstant(obj: Any?) =
this.hookReturnConstant(IHookBridge.PRIORITY_DEFAULT, obj)
/**
* 扩展函数 hook构造 使其直接返回一个值
* @param priority 优先级 默认50
* @param obj 要返回的值
* @return unhook [XC_MethodHook.Unhook]
*/
fun Constructor<*>.hookReturnConstant(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
obj: Any?
): Unhook =
XposedBridge.hookMethod(this, XC_MethodReplacement.returnConstant(priority, obj))
fun Constructor<*>.hookReturnConstant(obj: Any?) =
this.hookReturnConstant(IHookBridge.PRIORITY_DEFAULT, obj)
fun Array<Constructor<*>>.hookReturnConstant(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
obj: Any?
): Array<Unhook> =
this.map { XposedBridge.hookMethod(it, XC_MethodReplacement.returnConstant(priority, obj)) }
.toTypedArray()
fun Array<Constructor<*>>.hookReturnConstant(obj: Any?) =
this.hookReturnConstant(IHookBridge.PRIORITY_DEFAULT, obj)
@JvmName("hookConstructorReturnConstant")
fun List<Constructor<*>>.hookReturnConstant(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
obj: Any?
): List<Unhook> =
this.map { XposedBridge.hookMethod(it, XC_MethodReplacement.returnConstant(priority, obj)) }
@JvmName("hookConstructorReturnConstant")
fun List<Constructor<*>>.hookReturnConstant(obj: Any?) =
this.hookReturnConstant(IHookBridge.PRIORITY_DEFAULT, obj)
/**
* Hook工厂类
*/
class XposedHookFactory(priority: Int = IHookBridge.PRIORITY_DEFAULT) : XC_MethodHook(priority) {
private var beforeMethod: Hooker? = null
private var afterMethod: Hooker? = null
override fun beforeHookedMethod(param: MethodHookParam) {
beforeMethod?.invoke(param)
}
override fun afterHookedMethod(param: MethodHookParam) {
afterMethod?.invoke(param)
}
/**
* hook方法执行前
*/
fun before(before: Hooker) {
this.beforeMethod = before
}
/**
* hook方法执行后
*/
fun after(after: Hooker) {
this.afterMethod = after
}
}
/**
* 扩展函数 hook方法
* 直接以
*
* before { }
*
* after { }
*
* 的形式进行hook 两者均为可选
* @param priority 优先级 默认50
* @param hook 传递的XposedHookFactory
* @return Unhook
* @see XposedHookFactory
*/
fun Method.hookMethod(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hook: XposedHookFactory.() -> Unit
): XC_MethodHook.Unhook {
val factory = XposedHookFactory(priority)
hook(factory)
return this.hookMethod(factory)
}
/**
* 扩展函数 hook构造
* 直接以
*
* before { }
*
* after { }
*
* 的形式进行hook 两者均为可选
* @param priority 优先级 默认50
* @param hook 传递的XposedHookFactory
* @return Unhook
* @see XposedHookFactory
*/
fun Constructor<*>.hookMethod(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hook: XposedHookFactory.() -> Unit
): XC_MethodHook.Unhook {
val factory = XposedHookFactory(priority)
hook(factory)
return this.hookMethod(factory)
}
/**
* 扩展函数 hook多个方法
* 直接以
*
* before { }
*
* after { }
*
* 的形式进行hook 两者均为可选
* @param priority 优先级 默认50
* @param hook 传递的XposedHookFactory
* @return Array<Unhook>
* @see XposedHookFactory
*/
fun Array<Method>.hookMethod(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hook: XposedHookFactory.() -> Unit
): Array<XC_MethodHook.Unhook> {
return this.map { it.hookMethod(priority, hook) }.toTypedArray()
}
fun Iterable<Method>.hookMethod(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hook: XposedHookFactory.() -> Unit
): List<XC_MethodHook.Unhook> {
return this.map { it.hookMethod(priority, hook) }
}
/**
* 扩展函数 hook多个构造
* 直接以
*
* before { }
*
* after { }
*
* 的形式进行hook 两者均为可选
* @param priority 优先级 默认50
* @param hooker 传递的XposedHookFactory
* @return Array<Unhook>
* @see XposedHookFactory
*/
fun Array<Constructor<*>>.hookMethod(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: XposedHookFactory.() -> Unit
): Array<XC_MethodHook.Unhook> {
return this.map { it.hookMethod(priority, hooker) }.toTypedArray()
}
@JvmName("hookConstructor")
fun Iterable<Constructor<*>>.hookMethod(
priority: Int = IHookBridge.PRIORITY_DEFAULT,
hooker: XposedHookFactory.() -> Unit
): List<XC_MethodHook.Unhook> {
return this.map { it.hookMethod(priority, hooker) }
}
/**
* 执行数组中所有的unhook
*/
fun Array<XC_MethodHook.Unhook>.unhookAll() {
this.forEach { it.unhook() }
}
fun Iterable<XC_MethodHook.Unhook>.unhookAll() {
this.forEach { it.unhook() }
}

View File

@@ -0,0 +1,219 @@
package com.github.kyuubiran.ezxhelper.utils
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
/**
* 扩展属性 判断JSONArray是否为空
*/
val JSONArray.isEmpty: Boolean
inline get() = this.length() == 0
val JSONArray.isNotEmpty: Boolean
inline get() = this.length() != 0
/**
* 扩展属性 获取长度范围 用于for循环
*/
val JSONArray.indices: IntRange
inline get() = 0 until this.length()
/**
* 扩展函数 遍历JSONArray
* @param action 执行操作
*/
inline fun JSONArray.forEach(action: (Any) -> Unit) {
for (i in this.indices) action(this.get(i))
}
/**
* 扩展函数 遍历JSONArray 并包含索引
* @param action 执行操作
*/
inline fun JSONArray.forEachIndexed(action: (Int, Any) -> Unit) {
for (i in this.indices) action(i, this.get(i))
}
/**
* 扩展函数 遍历JSONArray 并返回同一个JSONArray
* @param action 执行操作
*/
inline fun JSONArray.onEach(action: (Any) -> Unit): JSONArray {
for (i in this.indices) action(this.get(i))
return this
}
/**
* 扩展函数 遍历JSONArray 包含索引 并返回同一个JSONArray
* @param action 执行操作
*/
inline fun JSONArray.onEachIndexed(action: (Int, Any) -> Unit): JSONArray {
for (i in this.indices) action(i, this.get(i))
return this
}
/**
* 扩展函数 对JSONArray进行过滤 并返回新的JSONArray
* @param predicate 过滤条件
*/
inline fun JSONArray.filter(predicate: (Any) -> Boolean): JSONArray {
val result = JSONArray()
for (i in this.indices) if (predicate(this.get(i))) result.put(this.get(i))
return result
}
/**
* 扩展函数 对JSONArray进行转换 并返回新的JSONArray
* @param transform 转换函数
*/
inline fun JSONArray.map(transform: (Any) -> Any): JSONArray {
val result = JSONArray()
for (i in this.indices) result.put(transform(this.get(i)))
return result
}
/**
* 扩展函数 对JSONArray进行转换 并返List
* @param transform 转换函数
*/
inline fun <T> JSONArray.mapToList(transform: (Any) -> T): List<T> {
val result = ArrayList<T>(this.length())
for (i in this.indices) result.add(transform(this.get(i)))
return result
}
/**
* 扩展函数 获取JSONObject中的 Long
* @param key 键值
* @param defValue 缺省值
* @return 获取成功时返回获取到的值 否则返回缺省值
*/
fun JSONObject.getLongOrDefault(key: String, defValue: Long = 0L): Long = try {
this.getLong(key)
} catch (e: JSONException) {
defValue
}
/**
* 扩展函数 获取JSONObject中的 Long
* @param key 键值
* @param defValue 缺省值
* @return 获取成功时返回获取到的值 否则返回null
*/
fun JSONObject.getLongOrNull(key: String): Long? = try {
this.getLong(key)
} catch (e: JSONException) {
null
}
/**
* 扩展函数 获取JSONObject中的 Int
* @param key 键值
* @param defValue 缺省值
* @return 获取成功时返回获取到的值 否则返回缺省值
*/
fun JSONObject.getIntOrDefault(key: String, defValue: Int = 0): Int = try {
this.getInt(key)
} catch (e: JSONException) {
defValue
}
/**
* 扩展函数 获取JSONObject中的 Int
* @param key 键值
* @return 获取成功时返回获取到的值 否则返回null
*/
fun JSONObject.getIntOrNull(key: String): Int? = try {
this.getInt(key)
} catch (e: JSONException) {
null
}
/**
* 扩展函数 获取JSONObject中的 Boolean
* @param key 键值
* @param defValue 缺省值
* @return 获取成功时返回获取到的值 否则返回缺省值
*/
fun JSONObject.getBooleanOrDefault(key: String, defValue: Boolean = false): Boolean = try {
this.getBoolean(key)
} catch (e: JSONException) {
defValue
}
/**
* 扩展函数 获取JSONObject中的 Boolean
* @param key 键值
* @return 获取成功时返回获取到的值 否则返回null
*/
fun JSONObject.getBooleanOrNull(key: String): Boolean? = try {
this.getBoolean(key)
} catch (e: JSONException) {
null
}
/**
* 扩展函数 获取JSONObject中的 String
* @param key 键值
* @param defValue 缺省值
* @return 获取成功时返回获取到的值 否则返回缺省值
*/
fun JSONObject.getStringOrDefault(key: String, defValue: String = ""): String = try {
this.getString(key)
} catch (e: JSONException) {
defValue
}
/**
* 扩展函数 获取JSONObject中的 String
* @param key 键值
* @return 获取成功时返回获取到的值 否则null
*/
fun JSONObject.getStringOrNull(key: String): String? = try {
this.getString(key)
} catch (e: JSONException) {
null
}
/**
* 扩展函数 获取JSONObject中的 Object
* @param key 键值
* @return 获取成功时返回获取到的值 否则返回null
*/
fun JSONObject.getObjectOrNull(key: String): Any? = try {
this.get(key)
} catch (e: JSONException) {
null
}
/**
* 扩展函数 获取JSONObject中的 JSONArray
* @param key 键值
* @return 获取成功时返回JSONArray 否则返回空JSONArray
*/
fun JSONObject.getJSONArrayOrEmpty(key: String): JSONArray = try {
this.getJSONArray(key)
} catch (e: JSONException) {
JSONArray()
}
/**
* 扩展函数 获取JSONObject中的 JSONArray
* @param key 键值
* @return 获取成功时返回JSONArray 否则返回null
*/
fun JSONObject.getJSONArrayOrNull(key: String): JSONArray? = try {
this.getJSONArray(key)
} catch (e: JSONException) {
null
}
/**
* 构建一个JSONObject
*/
inline fun buildJSONObject(builder: JSONObject.() -> Unit): JSONObject = JSONObject().apply(builder)
/**
* 构建一个JSONArray
*/
inline fun buildJSONArray(builder: JSONArray.() -> Unit): JSONArray = JSONArray().apply(builder)

View File

@@ -0,0 +1,428 @@
package com.github.kyuubiran.ezxhelper.utils
import android.widget.Toast
import com.github.kyuubiran.ezxhelper.init.InitFields.appContext
import io.github.qauxv.util.xpcompat.XposedBridge
open class Logger {
/**
* 日志等级 低于等级的日志不会被打印出来
* 可以配合BuildConfig.DEBUG / RELEASE来使用
*/
var logLevel: Int = VERBOSE
/**
* 日志Tag
*/
var logTag: String = "EZXHelper"
/**
* 是否输出日志到 Xposed
*/
var logXp: Boolean = true
internal set
/**
* Toast Tag
*/
var toastTag: String? = null
companion object LogLevel {
const val VERBOSE = 0
const val DEBUG = 1
const val INFO = 2
const val WARN = 3
const val ERROR = 4
}
/**
* 打印日志 等级: Info
* @param msg 消息
* @param thr 异常
*/
open fun i(msg: String, thr: Throwable? = null) {
if (logLevel > INFO) return
android.util.Log.i(logTag, msg, thr)
}
/**
* 打印日志 等级: Debug
* @param msg 消息
* @param thr 异常
*/
open fun d(msg: String, thr: Throwable? = null) {
if (logLevel > DEBUG) return
android.util.Log.d(logTag, msg, thr)
}
/**
* 打印日志 等级: Warn
* @param msg 消息
* @param thr 异常
*/
open fun w(msg: String, thr: Throwable? = null) {
if (logLevel > WARN) return
android.util.Log.w(logTag, msg, thr)
}
/**
* 打印日志 等级: Error
* @param msg 消息
* @param thr 异常
*/
open fun e(msg: String, thr: Throwable? = null) {
if (logLevel > ERROR) return
android.util.Log.e(logTag, msg, thr)
}
/**
* 打印日志到Xposed
* @param level 等级
* @param msg 消息
* @param thr 异常
*/
open fun px(levelInt: Int, level: String, msg: String, thr: Throwable?) {
if (logLevel > levelInt) return
if (logXp) XposedBridge.log("[$level/$logTag] $msg: ${thr?.stackTraceToString()}")
}
/**
* 打印日志 等级: Info
* @param thr 异常
* @param msg 消息
*/
fun i(thr: Throwable, msg: String = "") {
i(msg, thr)
}
/**
* 打印日志 等级: Debug
* @param thr 异常
* @param msg 消息
*/
fun d(thr: Throwable, msg: String = "") {
d(msg, thr)
}
/**
* 打印日志 等级: Warn
* @param thr 异常
* @param msg 消息
*/
fun w(thr: Throwable, msg: String = "") {
w(msg, thr)
}
/**
* 打印日志 等级: Error
* @param thr 异常
* @param msg 消息
*/
fun e(thr: Throwable, msg: String = "") {
e(msg, thr)
}
/**
* 打印日志到Xposed 等级: Info
* @param msg 消息
* @param thr 异常
*/
fun ix(msg: String, thr: Throwable? = null) {
i(msg, thr)
px(INFO, "I", msg, thr)
}
/**
* 打印日志到Xposed 等级: Info
* @param thr 异常
* @param msg 消息
*/
fun ix(thr: Throwable, msg: String = "") {
ix(msg, thr)
}
/**
* 打印日志到Xposed 等级: Warn
* @param msg 消息
* @param thr 异常
*/
fun wx(msg: String, thr: Throwable? = null) {
w(msg, thr)
px(WARN, "W", msg, thr)
}
/**
* 打印日志到Xposed 等级: Warn
* @param thr 异常
* @param msg 消息
*/
fun wx(thr: Throwable, msg: String = "") {
wx(msg, thr)
}
/**
* 打印日志到Xposed 等级: Debug
* @param msg 消息
* @param thr 异常
*/
fun dx(msg: String, thr: Throwable? = null) {
d(msg, thr)
px(DEBUG, "D", msg, thr)
}
/**
* 打印日志到Xposed 等级: Debug
* @param thr 异常
* @param msg 消息
*/
fun dx(thr: Throwable, msg: String = "") {
dx(msg, thr)
}
/**
* 打印日志到Xposed 等级: Error
* @param msg 消息
* @param thr 异常
*/
fun ex(msg: String, thr: Throwable? = null) {
e(msg, thr)
px(ERROR, "E", msg, thr)
}
/**
* 打印日志到Xposed 等级: Error
* @param thr 异常
* @param msg 消息
*/
fun ex(thr: Throwable, msg: String = "") {
ex(msg, thr)
}
}
object Log {
private val defaultLogger = Logger()
private var logger: Logger? = null
var currentLogger: Logger
get() = logger ?: defaultLogger
set(value) {
logger = value
}
/**
* 如果显示Toast时上一个Toast还没消失设置是否取消上一个Toast并显示本次Toast
*/
var cancelLastToast: Boolean = false
private var toast: Toast? = null
fun i(msg: String, thr: Throwable? = null) {
currentLogger.i(msg, thr)
}
fun d(msg: String, thr: Throwable? = null) {
currentLogger.d(msg, thr)
}
fun w(msg: String, thr: Throwable? = null) {
currentLogger.w(msg, thr)
}
fun e(msg: String, thr: Throwable? = null) {
currentLogger.e(msg, thr)
}
fun ix(msg: String, thr: Throwable? = null) {
currentLogger.ix(msg, thr)
}
fun wx(msg: String, thr: Throwable? = null) {
currentLogger.wx(msg, thr)
}
fun dx(msg: String, thr: Throwable? = null) {
currentLogger.dx(msg, thr)
}
fun ex(msg: String, thr: Throwable? = null) {
currentLogger.ex(msg, thr)
}
fun i(thr: Throwable, msg: String = "") {
currentLogger.i(thr, msg)
}
fun d(thr: Throwable, msg: String = "") {
currentLogger.d(thr, msg)
}
fun w(thr: Throwable, msg: String = "") {
currentLogger.w(thr, msg)
}
fun e(thr: Throwable, msg: String = "") {
currentLogger.e(thr, msg)
}
fun ix(thr: Throwable, msg: String = "") {
currentLogger.ix(thr, msg)
}
fun wx(thr: Throwable, msg: String = "") {
currentLogger.wx(thr, msg)
}
fun dx(thr: Throwable, msg: String = "") {
currentLogger.dx(thr, msg)
}
fun ex(thr: Throwable, msg: String = "") {
currentLogger.ex(thr, msg)
}
/**
* 显示一个Toast
*
* 需要先初始化appContext才能使用
*
* 如果不设置TOAST_TAG
* 则不显示前缀
* @see setToastTag
*/
fun toast(msg: String, duration: Int = Toast.LENGTH_SHORT) = runOnMainThread {
if (cancelLastToast) toast?.cancel()
toast = null
toast = Toast.makeText(appContext, null, duration).apply {
setText(if (currentLogger.toastTag != null) "${currentLogger.toastTag}: $msg" else msg)
show()
}
}
fun toast(msg: String, vararg formats: String, duration: Int = Toast.LENGTH_SHORT) =
toast(msg.format(*formats), duration)
/**
* 扩展函数 配合runCatching使用
* 如果抛出异常 则调用 Log.i 记录
* @param msg 消息
* @param then 发生异常时执行的函数
* @see runCatching
* @see i
*/
inline fun <R> Result<R>.logiIfThrow(msg: String = "", then: ((Throwable) -> Unit) = {}) =
this.exceptionOrNull()?.let {
currentLogger.i(it, msg)
then(it)
}
/**
* 扩展函数 配合runCatching使用
* 如果抛出异常 则调用 Log.ix 记录
* @param msg 消息
* @param then 发生异常时执行的函数
* @see runCatching
* @see ix
*/
inline fun <R> Result<R>.logixIfThrow(msg: String = "", then: ((Throwable) -> Unit) = {}) =
this.exceptionOrNull()?.let {
currentLogger.i(it, msg)
then(it)
}
/**
* 扩展函数 配合 runCatching 使用
* 如果抛出异常 则调用 Log.d 记录
* @param msg 消息
* @param then 发生异常时执行的函数
* @see runCatching
* @see d
*/
inline fun <R> Result<R>.logdIfThrow(msg: String = "", then: (Throwable) -> Unit = {}) =
this.exceptionOrNull()?.let {
currentLogger.d(it, msg)
then(it)
}
/**
* 扩展函数 配合 runCatching 使用
* 如果抛出异常 则调用 Log.dx 记录
* @param msg 消息
* @param then 发生异常时执行的函数
* @see runCatching
* @see dx
*/
inline fun <R> Result<R>.logdxIfThrow(msg: String = "", then: (Throwable) -> Unit = {}) =
this.exceptionOrNull()?.let {
currentLogger.dx(it, msg)
then(it)
}
/**
* 扩展函数 配合 runCatching 使用
* 如果抛出异常 则调用 Log.w 记录
* @param msg 消息
* @param then 发生异常时执行的函数
* @see runCatching
* @see w
*/
inline fun <R> Result<R>.logwIfThrow(msg: String = "", then: (Throwable) -> Unit = {}) =
this.exceptionOrNull()?.let {
currentLogger.w(it, msg)
then(it)
}
/**
* 扩展函数 配合 runCatching 使用
* 如果抛出异常 则调用 Log.w 记录
* @param msg 消息
* @param then 发生异常时执行的函数
* @see runCatching
* @see wx
*/
inline fun <R> Result<R>.logwxIfThrow(msg: String = "", then: (Throwable) -> Unit = {}) =
this.exceptionOrNull()?.let {
currentLogger.wx(it, msg)
then(it)
}
/**
* 扩展函数 配合 runCatching 使用
* 如果抛出异常 则调用 Log.e 记录
* @param msg 消息
* @param then 发生异常时执行的函数
* @see runCatching
* @see e
*/
inline fun <R> Result<R>.logeIfThrow(msg: String = "", then: (Throwable) -> Unit = {}) =
this.exceptionOrNull()?.let {
currentLogger.e(it, msg)
then(it)
}
/**
* 扩展函数 配合 runCatching 使用
* 如果抛出异常 则调用 Log.ex 记录
* @param msg 消息
* @param then 发生异常时执行的函数
* @see runCatching
* @see ex
*/
inline fun <R> Result<R>.logexIfThrow(msg: String = "", then: (Throwable) -> Unit = {}) =
this.exceptionOrNull()?.let {
currentLogger.ex(it, msg)
then(it)
}
}

View File

@@ -0,0 +1,144 @@
package com.github.kyuubiran.ezxhelper.utils
import java.lang.reflect.Constructor
import java.lang.reflect.Member
import java.lang.reflect.Method
import java.lang.reflect.Modifier
/**
* 扩展属性 判断是否为Static
*/
val Member.isStatic: Boolean
inline get() = Modifier.isStatic(this.modifiers)
val Member.isNotStatic: Boolean
inline get() = !this.isStatic
val Class<*>.isStatic: Boolean
inline get() = Modifier.isStatic(this.modifiers)
val Class<*>.isNotStatic: Boolean
inline get() = !this.isStatic
/**
* 扩展属性 判断是否为Public
*/
val Member.isPublic: Boolean
inline get() = Modifier.isPublic(this.modifiers)
val Member.isNotPublic: Boolean
inline get() = !this.isPublic
val Class<*>.isPublic: Boolean
inline get() = Modifier.isPublic(this.modifiers)
val Class<*>.isNotPublic: Boolean
inline get() = !this.isPublic
/**
* 扩展属性 判断是否为Protected
*/
val Member.isProtected: Boolean
inline get() = Modifier.isProtected(this.modifiers)
val Member.isNotProtected: Boolean
inline get() = !this.isProtected
val Class<*>.isProtected: Boolean
inline get() = Modifier.isProtected(this.modifiers)
val Class<*>.isNotProtected: Boolean
inline get() = !this.isProtected
/**
* 扩展属性 判断是否为Private
*/
val Member.isPrivate: Boolean
inline get() = Modifier.isPrivate(this.modifiers)
val Member.isNotPrivate: Boolean
inline get() = !this.isPrivate
val Class<*>.isPrivate: Boolean
inline get() = Modifier.isPrivate(this.modifiers)
val Class<*>.isNotPrivate: Boolean
inline get() = !this.isPrivate
/**
* 扩展属性 判断是否为Final
*/
val Member.isFinal: Boolean
inline get() = Modifier.isFinal(this.modifiers)
val Member.isNotFinal: Boolean
inline get() = !this.isFinal
val Class<*>.isFinal: Boolean
inline get() = Modifier.isFinal(this.modifiers)
val Class<*>.isNotFinal: Boolean
inline get() = !this.isFinal
/**
* 扩展属性 判断是否为Native
*/
val Member.isNative: Boolean
inline get() = Modifier.isNative(this.modifiers)
val Member.isNotNative: Boolean
inline get() = !this.isNative
/**
* 扩展属性 判断是否为Synchronized
*/
val Member.isSynchronized: Boolean
inline get() = Modifier.isSynchronized(this.modifiers)
val Member.isNotSynchronized: Boolean
inline get() = !this.isSynchronized
/**
* 扩展属性 判断是否为Abstract
*/
val Member.isAbstract: Boolean
inline get() = Modifier.isAbstract(this.modifiers)
val Member.isNotAbstract: Boolean
inline get() = !this.isAbstract
val Class<*>.isAbstract: Boolean
inline get() = Modifier.isAbstract(this.modifiers)
val Class<*>.isNotAbstract: Boolean
inline get() = !this.isAbstract
/**
* 扩展属性 判断是否为Transient
*/
val Member.isTransient: Boolean
inline get() = Modifier.isTransient(this.modifiers)
val Member.isNotTransient: Boolean
inline get() = !this.isTransient
/**
* 扩展属性 判断是否为Volatile
*/
val Member.isVolatile: Boolean
inline get() = Modifier.isVolatile(this.modifiers)
val Member.isNotVolatile: Boolean
inline get() = !this.isVolatile
/**
* 扩展属性 获取方法的参数数量
*/
val Method.paramCount: Int
inline get() = this.parameterTypes.size
/**
* 扩展属性 获取构造方法的参数数量
*/
val Constructor<*>.paramCount: Int
inline get() = this.parameterTypes.size
/**
* 扩展属性 判断方法的参数是否为空
*/
val Method.emptyParam: Boolean
inline get() = this.paramCount == 0
val Method.notEmptyParam: Boolean
inline get() = this.paramCount != 0
/**
* 扩展属性 判断构造方法的参数是否为空
*/
val Constructor<*>.emptyParam: Boolean
inline get() = this.paramCount == 0
val Constructor<*>.notEmptyParam: Boolean
inline get() = this.paramCount != 0

View File

@@ -0,0 +1,780 @@
package com.github.kyuubiran.ezxhelper.utils
import com.github.kyuubiran.ezxhelper.init.InitFields
import io.github.qauxv.util.xpcompat.XposedHelpers
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Method
@JvmInline
value class Args(val args: Array<out Any?>)
@JvmInline
value class ArgTypes(val argTypes: Array<out Class<*>>)
@Suppress("NOTHING_TO_INLINE")
inline fun args(vararg args: Any?) = Args(args)
@Suppress("NOTHING_TO_INLINE")
inline fun argTypes(vararg argTypes: Class<*>) = ArgTypes(argTypes)
/**
* 扩展函数 通过类或者对象获取单个方法
* @param methodName 方法名
* @param isStatic 是否为静态方法
* @param returnType 方法返回值 填入null为无视返回值
* @param argTypes 方法参数类型
* @return 符合条件的方法
* @throws IllegalArgumentException 方法名为空
* @throws NoSuchMethodException 未找到方法
*/
fun Any.method(
methodName: String,
returnType: Class<*>? = null,
isStatic: Boolean = false,
argTypes: ArgTypes = argTypes()
): Method {
if (methodName.isBlank()) throw IllegalArgumentException("Method name must not be empty!")
var c = if (this is Class<*>) this else this.javaClass
do {
c.declaredMethods.toList().asSequence()
.filter { it.name == methodName }
.filter { it.parameterTypes.size == argTypes.argTypes.size }
.apply { if (returnType != null) filter { returnType == it.returnType } }
.filter { it.parameterTypes.sameAs(*argTypes.argTypes) }
.filter { it.isStatic == isStatic }
.firstOrNull()?.let { it.isAccessible = true; return it }
} while (c.superclass?.also { c = it } != null)
throw NoSuchMethodException("Name:$methodName, Static: $isStatic, ArgTypes:${argTypes.argTypes.joinToString(",")}")
}
/**
* 扩展函数 通过类获取单个静态方法
* @param methodName 方法名
* @param returnType 方法返回值 填入null为无视返回值
* @param argTypes 方法参数类型
* @throws IllegalArgumentException 方法名为空
*/
fun Class<*>.staticMethod(
methodName: String,
returnType: Class<*>? = null,
argTypes: ArgTypes = argTypes()
): Method {
if (methodName.isBlank()) throw IllegalArgumentException("Method name must not be empty!")
return this.method(methodName, returnType, true, argTypes = argTypes)
}
typealias MethodCondition = Method.() -> Boolean
/**
* 通过条件查找类中的方法
* @param clz 类
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的方法
* @throws NoSuchMethodException
*/
fun findMethod(
clz: Class<*>,
findSuper: Boolean = false,
condition: MethodCondition
): Method {
return findMethodOrNull(clz, findSuper, condition) ?: throw NoSuchMethodException()
}
/**
* 通过条件查找类中的方法
* @param clz 类
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的方法 未找到时返回null
*/
fun findMethodOrNull(
clz: Class<*>,
findSuper: Boolean = false,
condition: MethodCondition
): Method? {
var c = clz
c.declaredMethods.firstOrNull { it.condition() }
?.let { it.isAccessible = true;return it }
if (findSuper) {
while (c.superclass?.also { c = it } != null) {
c.declaredMethods
.firstOrNull { it.condition() }
?.let { it.isAccessible = true;return it }
}
}
return null
}
/**
* 通过条件查找方法
* @param clzName 类名
* @param classLoader 类加载器
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的方法
* @throws NoSuchMethodException 未找到方法
*/
fun findMethod(
clzName: String,
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false,
condition: MethodCondition
): Method {
return findMethod(loadClass(clzName, classLoader), findSuper, condition)
}
/**
* 通过条件查找类中的方法
* @param clzName 类名
* @param classLoader 类加载器
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的方法 未找到时返回null
*/
fun findMethodOrNull(
clzName: String,
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false,
condition: MethodCondition
): Method? {
return findMethodOrNull(loadClass(clzName, classLoader), findSuper, condition)
}
/**
* 扩展函数 通过条件查找方法
* @param condition 方法的条件
* @return 符合条件的方法
* @throws NoSuchMethodException 未找到方法
*/
fun Array<Method>.findMethod(condition: MethodCondition): Method {
return this.firstOrNull { it.condition() }?.apply { isAccessible = true }
?: throw NoSuchMethodException()
}
fun Iterable<Method>.findMethod(condition: MethodCondition): Method {
return this.firstOrNull { it.condition() }?.apply { isAccessible = true }
?: throw NoSuchMethodException()
}
/**
* 扩展函数 通过条件查找方法
* @param condition 方法的条件
* @return 符合条件的方法 未找到时返回null
*/
fun Array<Method>.findMethodOrNull(condition: MethodCondition): Method? {
return this.firstOrNull { it.condition() }?.apply { isAccessible = true }
}
fun Iterable<Method>.findMethodOrNull(condition: MethodCondition): Method? {
return this.firstOrNull { it.condition() }?.apply { isAccessible = true }
}
/**
* 扩展函数 通过条件查找方法 每个类只搜索一个方法
* @param findSuper 是否查找父类
* @param condition 方法条件
* @return 方法数组
*/
fun Array<Class<*>>.findMethods(
findSuper: Boolean = false,
condition: MethodCondition
): Array<Method> = mapNotNull { it.findMethodOrNull(findSuper, condition) }.toTypedArray()
fun Iterable<Class<*>>.findMethods(
findSuper: Boolean = false,
condition: MethodCondition
): List<Method> = mapNotNull { it.findMethodOrNull(findSuper, condition) }
/**
* 扩展函数 加载数组中的类并且通过条件查找方法 每个类只搜索一个方法
* @param classLoader 类加载器
* @param findSuper 是否查找父类
* @param condition 方法条件
* @return 方法数组
*/
fun Array<String>.loadAndFindMethods(
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false,
condition: MethodCondition
): Array<Method> {
return this.loadAllClasses(classLoader).findMethods(findSuper, condition)
}
fun Iterable<String>.loadAndFindMethods(
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false,
condition: MethodCondition
): List<Method> {
return this.loadAllClasses(classLoader).findMethods(findSuper, condition)
}
// Method condition pair
infix fun String.mcp(condition: MethodCondition) = this to condition
infix fun Class<*>.mcp(condition: MethodCondition) = this to condition
/**
* 扩展函数 通过条件查找数组中对应的方法 每个类只搜索一个方法
* @param findSuper 是否查找父类
* @return 方法数组
*/
fun Array<Pair<Class<*>, MethodCondition>>.findMethods(
findSuper: Boolean = false
): Array<Method> {
return this.map { (k, v) -> findMethod(k, findSuper, v) }.toTypedArray()
}
fun Iterable<Pair<Class<*>, MethodCondition>>.findMethods(
findSuper: Boolean = false
): List<Method> {
return this.map { (k, v) -> findMethod(k, findSuper, v) }
}
/**
* 扩展函数 加载数组中的类并且通过条件查找方法 每个类只搜索一个方法
* @param classLoader 类加载器
* @param findSuper 是否查找父类
* @return 方法数组
*/
fun Array<Pair<String, MethodCondition>>.loadAndFindMethods(
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false
): Array<Method> {
return this.map { (k, v) -> findMethod(loadClass(k, classLoader), findSuper, v) }.toTypedArray()
}
fun Iterable<Pair<String, MethodCondition>>.loadAndFindMethods(
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false
): List<Method> {
return this.map { (k, v) -> findMethod(loadClass(k, classLoader), findSuper, v) }
}
/**
* 扩展函数 通过条件搜索所有方法
* @param findSuper 是否查找父类
* @param condition 方法条件
* @return 方法数组
*/
fun Array<Class<*>>.findAllMethods(
findSuper: Boolean = false,
condition: MethodCondition
): Array<Method> {
return this.flatMap { c -> findAllMethods(c, findSuper, condition) }.toTypedArray()
}
fun Iterable<Class<*>>.findAllMethods(
findSuper: Boolean = false,
condition: MethodCondition
): List<Method> {
return this.flatMap { c -> findAllMethods(c, findSuper, condition) }
}
/**
* 扩展函数 加载数组中的类并且通过条件查找方法
* @param findSuper 是否查找父类
* @return 方法数组
*/
fun Array<Pair<Class<*>, MethodCondition>>.findAllMethods(
findSuper: Boolean = false
): Array<Method> {
return arrayListOf<Method>()
.apply { this@findAllMethods.forEach { (k, v) -> addAll(findAllMethods(k, findSuper, v)) } }
.toTypedArray()
}
fun Iterable<Pair<Class<*>, MethodCondition>>.findAllMethods(
findSuper: Boolean = false
): List<Method> {
return arrayListOf<Method>()
.apply { this@findAllMethods.forEach { (k, v) -> addAll(findAllMethods(k, findSuper, v)) } }
}
/**
* 扩展函数 加载数组中的类并且通过条件查找方法
* @param classLoader 类加载器
* @param findSuper 是否查找父类
* @return 方法数组
*/
fun Array<Pair<String, MethodCondition>>.loadAndFindAllMethods(
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false
): Array<Method> {
return this.map { (k, v) -> loadClass(k, classLoader) to v }.toTypedArray()
.findAllMethods(findSuper)
}
fun Iterable<Pair<String, MethodCondition>>.loadAndFindAllMethods(
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false
): List<Method> {
return this.map { (k, v) -> loadClass(k, classLoader) to v }.findAllMethods(findSuper)
}
/**
* 扩展函数 加载数组中的类并且通过条件查找所有方法
* @param classLoader 类加载器
* @param findSuper 是否查找父类
* @param condition 方法条件
* @return 方法数组
*/
fun Array<String>.loadAndFindAllMethods(
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false,
condition: MethodCondition
): Array<Method> {
return this.loadAllClasses(classLoader).findAllMethods(findSuper, condition)
}
fun Iterable<String>.loadAndFindAllMethods(
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false,
condition: MethodCondition
): List<Method> {
return this.loadAllClasses(classLoader).findAllMethods(findSuper, condition)
}
typealias ConstructorCondition = Constructor<*>.() -> Boolean
/**
* 扩展函数 通过条件查找构造方法
* @param condition 构造方法的条件
* @return 符合条件的构造方法
* @throws NoSuchMethodException 未找到构造方法
*/
fun Array<Constructor<*>>.findConstructor(condition: ConstructorCondition): Constructor<*> {
return this.firstOrNull { it.condition() }?.apply { isAccessible = true }
?: throw NoSuchMethodException()
}
fun Iterable<Constructor<*>>.findConstructor(condition: ConstructorCondition): Constructor<*> {
return this.firstOrNull { it.condition() }?.apply { isAccessible = true }
?: throw NoSuchMethodException()
}
/**
* 扩展函数 通过条件查找构造方法
* @param condition 构造方法的条件
* @return 符合条件的构造方法 未找到时返回null
*/
fun Array<Constructor<*>>.findConstructorOrNull(condition: ConstructorCondition): Constructor<*>? {
return this.firstOrNull { it.condition() }?.apply { isAccessible = true }
}
fun Iterable<Constructor<*>>.findConstructorOrNull(condition: ConstructorCondition): Constructor<*>? {
return this.firstOrNull { it.condition() }?.apply { isAccessible = true }
}
/**
* 通过条件查找构造方法
* @param clz 类
* @param condition 条件
* @return 符合条件的构造方法
* @throws NoSuchMethodException 未找到构造方法
*/
fun findConstructor(
clz: Class<*>,
condition: ConstructorCondition
): Constructor<*> {
return clz.declaredConstructors.findConstructor(condition)
}
/**
* 通过条件查找构造方法
* @param clz 类
* @param condition 条件
* @return 符合条件的构造方法 未找到时返回null
*/
fun findConstructorOrNull(
clz: Class<*>,
condition: ConstructorCondition
): Constructor<*>? {
return clz.declaredConstructors.firstOrNull { it.condition() }?.apply { isAccessible = true }
}
/**
* 通过条件查找构造方法
* @param clzName 类名
* @param classLoader 类加载器
* @param condition 条件
* @return 符合条件的构造方法
* @throws NoSuchMethodException 未找到构造方法
*/
fun findConstructor(
clzName: String,
classLoader: ClassLoader = InitFields.ezXClassLoader,
condition: ConstructorCondition
): Constructor<*> {
return loadClass(clzName, classLoader).declaredConstructors.findConstructor(condition)
}
/**
* 通过条件查找构造方法
* @param clzName 类名
* @param classLoader 类加载器
* @param condition 条件
* @return 符合条件的构造方法 未找到时返回null
*/
fun findConstructorOrNull(
clzName: String,
classLoader: ClassLoader = InitFields.ezXClassLoader,
condition: ConstructorCondition
): Constructor<*>? {
return loadClass(clzName, classLoader).declaredConstructors.findConstructorOrNull(condition)
}
/**
* 查找所有符合条件的构造方法
* @param clzName 类名
* @param classLoader 类加载器
* @param condition 条件
* @return 所有符合条件的构造方法
*/
fun findAllConstructors(
clzName: String,
classLoader: ClassLoader = InitFields.ezXClassLoader,
condition: ConstructorCondition
): List<Constructor<*>> {
return loadClass(clzName, classLoader).declaredConstructors.filter(condition)
}
/**
* 查找所有符合条件的构造方法
* @param clz 类
* @param condition 条件
* @return 所有符合条件的构造方法
*/
fun findAllConstructors(
clz: Class<*>,
condition: ConstructorCondition
): List<Constructor<*>> {
return clz.declaredConstructors.filter(condition)
}
/**
* 扩展函数 通过遍历方法数组 返回符合条件的方法数组
* @param condition 条件
* @return 符合条件的方法数组
*/
fun Array<Method>.findAllMethods(condition: MethodCondition): Array<Method> {
return this.filter { it.condition() }.onEach { it.isAccessible = true }.toTypedArray()
}
fun Iterable<Method>.findAllMethods(condition: MethodCondition): List<Method> {
return this.filter { it.condition() }.onEach { it.isAccessible = true }.toList()
}
/**
* 通过条件获取方法数组
* @param clz 类
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的方法数组
*/
fun findAllMethods(
clz: Class<*>,
findSuper: Boolean = false,
condition: MethodCondition
): List<Method> {
var c = clz
val arr = ArrayList<Method>()
arr.addAll(c.declaredMethods.findAllMethods(condition))
if (findSuper) {
while (c.superclass?.also { c = it } != null) {
arr.addAll(c.declaredMethods.findAllMethods(condition))
}
}
return arr
}
/**
* 通过条件获取方法数组
* @param clzName 类名
* @param classLoader 类加载器
* @param findSuper 是否查找父类
* @param condition 条件
* @return 符合条件的方法数组
*/
fun findAllMethods(
clzName: String,
classLoader: ClassLoader = InitFields.ezXClassLoader,
findSuper: Boolean = false,
condition: MethodCondition
): List<Method> {
return findAllMethods(loadClass(clzName, classLoader), findSuper, condition)
}
/**
* 扩展函数 调用对象中符合条件的方法
* @param args 参数
* @param condition 条件
* @return 方法的返回值
* @throws NoSuchMethodException 未找到方法
*/
fun Any.invokeMethod(vararg args: Any?, condition: MethodCondition): Any? {
this::class.java.declaredMethods.firstOrNull { it.condition() }
?.let { it.isAccessible = true;return it(this, *args) }
throw NoSuchMethodException()
}
/**
* 扩展函数 调用类中符合条件的静态方法
* @param args 参数表
* @param condition 条件
* @return 方法的返回值
* @throws NoSuchMethodException 未找到方法
*/
fun Class<*>.invokeStaticMethod(
vararg args: Any?,
condition: MethodCondition
): Any? {
this::class.java.declaredMethods.firstOrNull { it.isStatic && it.condition() }
?.let { it.isAccessible = true;return it(this, *args) }
throw NoSuchMethodException()
}
/**
* 扩展函数 调用对象的方法
*
* @param methodName 方法名
* @param args 形参表 可空
* @param argTypes 形参类型 可空
* @param returnType 返回值类型 为null时无视返回值类型
* @return 函数调用后的返回值
* @throws IllegalArgumentException 当方法名为空时
* @throws IllegalArgumentException 当args的长度与argTypes的长度不符时
* @throws IllegalArgumentException 当对象是一个Class时
*/
fun Any.invokeMethod(
methodName: String,
args: Args = args(),
argTypes: ArgTypes = argTypes(),
returnType: Class<*>? = null
): Any? {
if (methodName.isBlank()) throw IllegalArgumentException("Object name must not be empty!")
if (args.args.size != argTypes.argTypes.size) throw IllegalArgumentException("Method args size must equals argTypes size!")
return if (args.args.isEmpty()) {
try {
this.method(methodName, returnType, false)
} catch (e: NoSuchMethodException) {
return null
}.invoke(this)
} else {
try {
this.method(methodName, returnType, false, argTypes = argTypes)
} catch (e: NoSuchMethodException) {
return null
}.invoke(this, *args.args)
}
}
/**
* 扩展函数 调用对象的方法 并且将返回值转换为T?类型
*
* 注意: 请勿对类使用此函数
* @param methodName 方法名
* @param args 形参表 可空
* @param argTypes 形参类型 可空
* @param returnType 返回值类型 为null时无视返回值类型
* @param T 转换的类型
* @return 函数调用后的返回值
* @throws IllegalArgumentException 当方法名为空时
* @throws IllegalArgumentException 当args的长度与argTypes的长度不符时
* @throws IllegalArgumentException 当对象是一个Class时
*/
@Suppress("UNCHECKED_CAST")
fun <T> Any.invokeMethodAs(
methodName: String,
args: Args = args(),
argTypes: ArgTypes = argTypes(),
returnType: Class<*>? = null
): T? = this.invokeMethod(methodName, args, argTypes, returnType) as T?
/**
* 扩展函数 调用对象与形参表最佳匹配的方法
* @param methodName 方法名
* @param args 形参
* @return 函数调用时的返回值
*/
fun Any.invokeMethodAuto(
methodName: String,
vararg args: Any?
): Any? {
return XposedHelpers.callMethod(this, methodName, *args)
}
/**
* 扩展函数 调用对象与形参表最佳匹配的方法 并将返回值转换为T?类型
* @param methodName 方法名
* @param args 形参
* @return 函数调用时的返回值
*/
@Suppress("UNCHECKED_CAST")
fun <T> Any.invokeMethodAutoAs(
methodName: String,
vararg args: Any?
): T? = XposedHelpers.callMethod(this, methodName, *args) as T?
/**
* 扩展函数 调用类的静态方法
* @param methodName 方法名
* @param args 形参表 可空
* @param argTypes 形参类型 可空
* @param returnType 返回值类型 为null时无视返回值类型
* @return 函数调用后的返回值
* @throws IllegalArgumentException 当args的长度与argTypes的长度不符时
*/
fun Class<*>.invokeStaticMethod(
methodName: String,
args: Args = args(),
argTypes: ArgTypes = argTypes(),
returnType: Class<*>? = null
): Any? {
if (args.args.size != argTypes.argTypes.size) throw IllegalArgumentException("Method args size must equals argTypes size!")
return if (args.args.isEmpty()) {
try {
this.method(methodName, returnType, true)
} catch (e: NoSuchMethodException) {
return null
}.invoke(this)
} else {
try {
this.method(methodName, returnType, true, argTypes = argTypes)
} catch (e: NoSuchMethodException) {
return null
}.invoke(this, *args.args)
}
}
/**
* 扩展函数 调用类的静态方法 并且将返回值转换为T?类型
* @param methodName 方法名
* @param args 形参表 可空
* @param argTypes 形参类型 可空
* @param returnType 返回值类型 为null时无视返回值类型
* @return 函数调用后的返回值
* @throws IllegalArgumentException 当args的长度与argTypes的长度不符时
*/
@Suppress("UNCHECKED_CAST")
fun <T> Class<*>.invokeStaticMethodAs(
methodName: String,
args: Args = args(),
argTypes: ArgTypes = argTypes(),
returnType: Class<*>? = null
): T? = this.invokeStaticMethod(methodName, args, argTypes, returnType) as T?
/**
* 扩展函数 调用类中与形参表最佳匹配的静态方法
* @param methodName 方法名
* @param args 形参
* @return 函数调用时的返回值
*/
fun Class<*>.invokeStaticMethodAuto(
methodName: String,
vararg args: Any?
): Any? = XposedHelpers.callStaticMethod(this, methodName, *args)
/**
* 扩展函数 调用类中与形参表最佳匹配的静态方法 并将返回值转换为T?类型
* @param methodName 方法名
* @param args 形参
* @return 函数调用时的返回值
*/
@Suppress("UNCHECKED_CAST")
fun <T> Class<*>.invokeStaticMethodAutoAs(
methodName: String,
vararg args: Any?
): T? = XposedHelpers.callStaticMethod(this, methodName, *args) as T?
/**
* 扩展函数 创建新的实例化对象
* @param args 构造函数的形参表
* @param argTypes 构造函数的形参类型
* @return 成功时返回实例化的对象 失败时返回null
* @throws IllegalArgumentException 当args的长度与argTypes的长度不符时
*/
fun Class<*>.newInstance(
args: Args = args(),
argTypes: ArgTypes = argTypes()
): Any? {
if (args.args.size != argTypes.argTypes.size) throw IllegalArgumentException("Method args size must equals argTypes size!")
return tryOrLogNull {
val constructor: Constructor<*> =
if (argTypes.argTypes.isNotEmpty())
this.getDeclaredConstructor(*argTypes.argTypes)
else
this.getDeclaredConstructor()
constructor.isAccessible = true
if (args.args.isEmpty())
constructor.newInstance()
else
constructor.newInstance(*args.args)
}
}
/**
* 扩展函数 创建新的实例化对象 并将对象转换为T?类型
* @param args 构造函数的形参表
* @param argTypes 构造函数的形参类型
* @return 成功时返回实例化的对象 失败时返回null
* @throws IllegalArgumentException 当args的长度与argTypes的长度不符时
*/
@Suppress("UNCHECKED_CAST")
fun <T> Class<*>.newInstanceAs(
args: Args = args(),
argTypes: ArgTypes = argTypes()
): T? = this.newInstance(args, argTypes) as T?
/**
* 扩展函数 调用方法 并将返回值转换为T?类型
* @param obj 被调用对象
* @param args 形参表
*/
@Suppress("UNCHECKED_CAST")
fun <T> Method.invokeAs(obj: Any?, vararg args: Any?): T? = this.run {
isAccessible = true
invoke(obj, *args) as T?
}
/**
* 通过Descriptor获取方法
* @param desc Descriptor
* @param clzLoader 类加载器
* @return 找到的方法
* @throws NoSuchMethodException 未找到方法
*/
fun getMethodByDesc(
desc: String,
clzLoader: ClassLoader = InitFields.ezXClassLoader
): Method = DexDescriptor.newMethodDesc(desc).getMethod(clzLoader).apply { isAccessible = true }
/**
* 通过Descriptor获取方法
* @param desc Descriptor
* @param clzLoader 类加载器
* @return 找到的方法 未找到则返回null
*/
fun getMethodByDescOrNull(
desc: String,
clzLoader: ClassLoader = InitFields.ezXClassLoader
): Method? = runCatching { getMethodByDesc(desc, clzLoader) }.getOrNull()
/**
* 扩展函数 通过Descriptor获取方法
* @param desc Descriptor
* @return 找到的方法
* @throws NoSuchMethodException 未找到方法
*/
fun ClassLoader.getMethodByDesc(desc: String): Method = getMethodByDesc(desc, this)
/**
* 扩展函数 通过Descriptor获取方法
* @param desc Descriptor
* @return 找到的方法 未找到则返回null
*/
fun ClassLoader.getMethodByDescOrNull(desc: String): Method? = getMethodByDescOrNull(desc, this)

View File

@@ -0,0 +1,102 @@
package com.github.kyuubiran.ezxhelper.utils
/**
* 监听一个对象,当值发生变化时调用 onValueChanged 中所有回调
*/
class Observe<T>(init: T, onValueChanged: ((T) -> Unit)? = null) {
private var _value: T = init
var value: T
get() = _value
set(newValue) = synchronized(this) {
if (_value == newValue) return@synchronized
_value = newValue
if (onValueChanged.unsafeInvoke)
onValueChanged.unsafeInvoke(newValue)
else
onValueChanged.invoke(newValue)
}
val onValueChanged = ValueChangedEvent<T>()
init {
if (onValueChanged != null) this.onValueChanged += onValueChanged
}
class ValueChangedEvent<T> {
private val _listeners = mutableSetOf<(T) -> Unit>()
var unsafeInvoke = false
var onThrow: ((thr: Throwable) -> Unit)? = null
fun add(listener: (T) -> Unit) {
_listeners.add(listener)
}
fun remove(listener: (T) -> Unit) {
_listeners.remove(listener)
}
fun addAll(listeners: Collection<(T) -> Unit>) {
_listeners.addAll(listeners)
}
fun removeAll(listeners: Collection<(T) -> Unit>) {
_listeners.removeAll(listeners.toSet())
}
fun addAll(listeners: Array<(T) -> Unit>) {
_listeners.addAll(listeners)
}
fun removeAll(listeners: Array<(T) -> Unit>) {
_listeners.removeAll(listeners.toSet())
}
fun clear() {
_listeners.clear()
}
operator fun plusAssign(listener: (T) -> Unit) {
add(listener)
}
operator fun minusAssign(listener: (T) -> Unit) {
remove(listener)
}
operator fun plusAssign(listeners: Collection<(T) -> Unit>) {
addAll(listeners)
}
operator fun minusAssign(listeners: Collection<(T) -> Unit>) {
removeAll(listeners)
}
operator fun plusAssign(listeners: Array<(T) -> Unit>) {
addAll(listeners)
}
operator fun minusAssign(listeners: Array<(T) -> Unit>) {
removeAll(listeners)
}
fun unsafeInvoke(value: T) {
_listeners.forEach { it(value) }
}
operator fun invoke(value: T) {
_listeners.retainIf {
try {
it(value)
true
} catch (e: Throwable) {
onThrow?.invoke(e)
Log.e("Event invoke failed", e)
false
}
}
}
}
}

View File

@@ -0,0 +1,224 @@
package com.github.kyuubiran.ezxhelper.utils
import android.app.Activity
import dalvik.system.BaseDexClassLoader
import java.util.*
import kotlin.system.exitProcess
/**
* 尝试执行一块代码如果成功返true失败则返回false
* @param block 执行的代码块
* @return 成功为true失败为false
*/
inline fun tryOrFalse(block: () -> Unit): Boolean = try {
block()
true
} catch (thr: Throwable) {
false
}
/**
* 尝试执行一块代码,如果失败则记录日志
* @param block 执行的代码块
*/
inline fun tryOrLog(block: () -> Unit) = try {
block()
} catch (thr: Throwable) {
Log.e(thr)
}
/**
* 尝试执行一块代码如果成功返true失败则返回false并且记录日志
* @param block 执行的代码块
* @return 成功为true失败为false
*/
inline fun tryOrLogFalse(block: () -> Unit): Boolean = try {
block()
true
} catch (thr: Throwable) {
Log.e(thr)
false
}
/**
* 尝试执行一块代码如果成功返回代码块执行的结果失败则返回null
* @param block 执行的代码块
* @return 成功返回代码块执行的返回值失败返回null
*/
inline fun <T> tryOrNull(block: () -> T?): T? = try {
block()
} catch (thr: Throwable) {
null
}
/**
* 尝试执行一块代码如果成功返回代码块执行的结果失败则返回null并且记录日志
* @param block 执行的代码块
* @return 成功返回代码块执行的返回值失败返回null
*/
inline fun <T> tryOrLogNull(block: () -> T?): T? = try {
block()
} catch (thr: Throwable) {
Log.e(thr)
null
}
/**
* 扩展函数 保留可变列表中符合条件的元素
* @param predicate 条件
*/
inline fun <E> MutableList<E>.retainIf(predicate: ((E) -> Boolean)) {
this.filter { elem -> predicate(elem) }.forEach { this.remove(it) }
}
/**
* 扩展函数 保留可变列表中符合条件的元素 并返回可变列表
* @param predicate 条件
* @return 保留符合条件的元素之后的可变列表
*/
inline fun <E> MutableList<E>.applyRetainIf(predicate: (E) -> Boolean): MutableList<E> {
this.retainIf(predicate)
return this
}
/**
* 扩展函数 保留可变集合中符合条件的元素
* @param predicate 条件
*/
inline fun <E> MutableSet<E>.retainIf(predicate: (E) -> Boolean) {
this.filter { elem -> predicate(elem) }.forEach { this.remove(it) }
}
/**
* 扩展函数 保留可变集合中符合条件的元素 并返回可变集合
* @param predicate 条件
* @return 保留符合条件的元素之后的可变集合
*/
inline fun <E> MutableSet<E>.applyRetainIf(predicate: (E) -> Boolean): MutableSet<E> {
this.retainIf(predicate)
return this
}
/**
* 扩展函数 保留可变字典中符合条件的元素
* @param predicate 条件
*/
inline fun <K, V> MutableMap<K, V>.retainIf(predicate: (K, V) -> Boolean) {
this.filter { (key, value) -> predicate(key, value) }.forEach { this.remove(it.key) }
}
/**
* 扩展函数 保留可变字典中符合条件的元素 并返回可变字典
* @param predicate 条件
* @return 保留符合条件的元素之后的可变字典
*/
inline fun <K, V> MutableMap<K, V>.applyRetainIf(predicate: (K, V) -> Boolean): MutableMap<K, V> {
this.retainIf(predicate)
return this
}
/**
* 扩展函数 移除可变字典中符合条件的元素
* @param predicate 条件
*/
inline fun <K, V> MutableMap<K, V>.removeIf(predicate: (K, V) -> Boolean) {
this.filter { (key, value) -> predicate(key, value) }.forEach { this.remove(it.key) }
}
/**
* 扩展函数 移除可变字典中符合条件的元素 并返回可变字典
* @param predicate 条件
* @return 移除符合条件的元素之后的可变字典
*/
inline fun <K, V> MutableMap<K, V>.applyRemoveIf(
predicate: (K, V) -> Boolean
): MutableMap<K, V> {
this.removeIf(predicate)
return this
}
/**
* 取自 哔哩漫游
* 查找DexClassLoader
* @see `https://github.com/yujincheng08/BiliRoaming`
*/
inline fun ClassLoader.findDexClassLoader(crossinline delegator: (BaseDexClassLoader) -> BaseDexClassLoader = { x -> x }): BaseDexClassLoader? {
var classLoader = this
while (classLoader !is BaseDexClassLoader) {
if (classLoader.parent != null) classLoader = classLoader.parent
else return null
}
return delegator(classLoader)
}
/**
* 取自 哔哩漫游
* 获取所有类名
* @see `https://github.com/yujincheng08/BiliRoaming`
*/
fun ClassLoader.getAllClassesList(delegator: (BaseDexClassLoader) -> BaseDexClassLoader = { loader -> loader }): List<String> =
findDexClassLoader(delegator)?.getObjectOrNull("pathList")
?.getObjectOrNullAs<Array<Any>>("dexElements")
?.flatMap {
it.getObjectOrNull("dexFile")?.invokeMethodAutoAs<Enumeration<String>>("entries")?.toList()
.orEmpty()
}.orEmpty()
/**
* 重新启动宿主App
*/
fun restartHostApp(activity: Activity) {
val pm = activity.packageManager
val intent = pm.getLaunchIntentForPackage(activity.packageName)
activity.finishAffinity()
activity.startActivity(intent)
exitProcess(0)
}
/**
* 扩展函数 判断类是否相同(用于判断参数)
*
* eg: fun foo(a: Boolean, b: Int) { }
* foo.parameterTypes.sameAs(*array)
* foo.parameterTypes.sameAs(Boolean::class.java, Int::class.java)
* foo.parameterTypes.sameAs("boolean", "int")
* foo.parameterTypes.sameAs(Boolean::class.java, "int")
*
* @param other 其他类(支持String或者Class<*>)
* @return 是否相等
*/
fun Array<Class<*>>.sameAs(vararg other: Any): Boolean {
if (this.size != other.size) return false
for (i in this.indices) {
when (val otherClazz = other[i]) {
is Class<*> -> {
if (this[i] != otherClazz) return false
}
is String -> {
if (this[i].name != otherClazz) return false
}
else -> {
throw IllegalArgumentException("Only support Class<*> or String")
}
}
}
return true
}
fun List<Class<*>>.sameAs(vararg other: Any): Boolean {
if (this.size != other.size) return false
for (i in this.indices) {
when (val otherClazz = other[i]) {
is Class<*> -> {
if (this[i] != otherClazz) return false
}
is String -> {
if (this[i].name != otherClazz) return false
}
else -> {
throw IllegalArgumentException("Only support Class<*> or String")
}
}
}
return true
}

View File

@@ -0,0 +1,122 @@
package com.github.kyuubiran.ezxhelper.utils
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.view.View
import android.view.ViewGroup
import com.github.kyuubiran.ezxhelper.init.InitFields
/**
* 扩展函数 将View布局的高度和宽度设置为0
*/
fun View.setViewZeroSize() {
this.layoutParams.height = 0
this.layoutParams.width = 0
}
/**
* 扩展属性 获取长度范围 用于for循环
*/
inline val ViewGroup.indices: IntRange
get() = 0 until childCount
/**
* 扩展函数 遍历ViewGroup
*/
inline fun ViewGroup.forEach(action: (view: View) -> Unit) {
for (index in this.indices) {
action(getChildAt(index))
}
}
/**
* 扩展函数 带index遍历ViewGroup
*/
inline fun ViewGroup.forEachIndexed(action: (index: Int, view: View) -> Unit) {
for (index in this.indices) {
action(index, getChildAt(index))
}
}
/**
* 扩展函数 判断ViewGroup是否为空
* @return 是否为空
*/
fun ViewGroup.isEmpty(): Boolean = this.childCount == 0
/**
* 扩展函数 判断ViewGroup是否不为空
* @return 是否不为空
*/
fun ViewGroup.isNotEmpty(): Boolean = this.childCount != 0
/**
* 扩展函数 遍历ViewGroup 根据条件查找View
* @param condition 条件
* @return 成功时返回符合条件的view 失败时返回null
*/
fun ViewGroup.findViewByCondition(condition: (view: View) -> Boolean): View? {
this.forEach {
if (condition(it)) return it
else if (it is ViewGroup) {
val v = it.findViewByCondition(condition)
if (v != null) return v
}
}
return null
}
/**
* 扩展函数 遍历ViewGroup 根据条件查找所有符合条件的View
* @param condition 条件
* @return 符合条件的ViewList
*/
fun ViewGroup.findAllViewsByCondition(condition: (view: View) -> Boolean): List<View> {
val list = mutableListOf<View>()
this.forEach {
if (condition(it)) list.add(it)
else if (it is ViewGroup) {
val v = it.findAllViewsByCondition(condition)
if (v.isNotEmpty()) list.addAll(v)
}
}
return list
}
/**
* 扩展函数 遍历ViewGroup 根据条件查找View 并将View转换为T?类型
* @param condition 条件
* @return 成功时返回符合条件的view 失败时返回null
*/
@Suppress("UNCHECKED_CAST")
fun <T : View> ViewGroup.findViewByConditionAs(condition: (view: View) -> Boolean): T? {
return this.findViewByCondition(condition) as T?
}
/**
* 通过名字获取R.id中的组件id
* @param name 名字
* @return id ID 若无法找到则返回0
*/
@SuppressLint("DiscouragedApi")
fun getIdByName(name: String, ctx: Context = InitFields.appContext): Int {
return ctx.resources.getIdentifier(name, "id", ctx.packageName)
}
/**
* 通过名字查找View
* @param name 名字
* @return View 若无法找到则返回null
*/
fun View.findViewByIdName(name: String): View? {
val id = getIdByName(name, this.context)
if (id == 0) return null
return this.findViewById(id)
}
fun Activity.findViewByIdName(name: String): View? {
val id = getIdByName(name, this)
if (id == 0) return null
return this.findViewById(id)
}