refactor: replace de.rebv.android.xposed with Xposed compat for EzXHelper-1.x
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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() }
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user