Added custom xposed_init name function

This commit is contained in:
2022-04-22 02:06:10 +08:00
parent ed6cae2add
commit ac53910842
4 changed files with 102 additions and 14 deletions

View File

@@ -1,13 +1,21 @@
## InjectYukiHookWithXposed [annotation] ## InjectYukiHookWithXposed [annotation]
```kotlin ```kotlin
annotation class InjectYukiHookWithXposed(val sourcePath: String, val modulePackageName: String) annotation class InjectYukiHookWithXposed(
val sourcePath: String,
val modulePackageName: String,
val entryClassName: String
)
``` ```
**变更记录** **变更记录**
`v1.0` `添加` `v1.0` `添加`
`v1.0.80` `修改`
新增 `entryClassName` 参数
**功能描述** **功能描述**
> 标识 `YukiHookAPI` 注入 Xposed 入口的类注解。 > 标识 `YukiHookAPI` 注入 Xposed 入口的类注解。

View File

@@ -15,20 +15,24 @@
### InjectYukiHookWithXposed 注解 ### InjectYukiHookWithXposed 注解
```kotlin ```kotlin
annotation class InjectYukiHookWithXposed(val sourcePath: String, val modulePackageName: String) annotation class InjectYukiHookWithXposed(
val sourcePath: String,
val modulePackageName: String,
val entryClassName: String
)
``` ```
`@InjectYukiHookWithXposed` 注解是一个标记模块 Hook 入口的重要注解。 `@InjectYukiHookWithXposed` 注解是一个标记模块 Hook 入口的重要注解。
!> `@InjectYukiHookWithXposed` 注解的 `Class` 必须实现 `IYukiHookXposedInit` 接口。 !> `@InjectYukiHookWithXposed` 注解的 `Class` 必须实现 `IYukiHookXposedInit` 接口。
!> 在你当前项目中的所有 `Class` 标记中**只能存在一次**,若**存在多个声明自动处理程序<u>会在编译时抛出异常</u>**,你可以自定义其相关参数。 !> 在你当前项目中的所有 `Class` 标记中**只能存在一次**,若存在多个声明自动处理程序<u>会在编译时抛出异常</u>,你可以自定义其相关参数。
#### sourcePath 参数 #### sourcePath 参数
`sourcePath` 参数决定了自动处理程序自动查找并匹配你当前项目路径的重要标识,此参数的内容为相对路径匹配,默认参数为 `src/main` `sourcePath` 参数决定了自动处理程序自动查找并匹配你当前项目路径的重要标识,此参数的内容为相对路径匹配,默认参数为 `src/main`
!> 如果你的项目不在 `...app/src/main...` 或你手动使用 `sourceSets` 设置了项目路径,你就需要手动设置 `sourcePath` 参数,**否则自动处理程序将无法识别你的项目路径并<u>会在编译时抛出异常</u>** !> 如果你的项目不在 `...app/src/main...` 或你手动使用 `sourceSets` 设置了项目路径,你就需要手动设置 `sourcePath` 参数,否则自动处理程序将无法识别你的项目路径并<u>会在编译时抛出异常</u>
> 示例如下 > 示例如下
@@ -74,6 +78,64 @@ annotation class InjectYukiHookWithXposed(val sourcePath: String, val modulePack
You set the customize module package name to "com.example.demo", please check for yourself if it is correct You set the customize module package name to "com.example.demo", please check for yourself if it is correct
``` ```
#### entryClassName 参数
`entryClassName` 决定了自动处理程序如何生成 `xposed_init` 中的入口类名,默认会使用你的入口类包名插入 `_YukiHookXposedInit` 后缀进行生成。
假设这是你的入口类。
> 示例如下
```kotlin
@InjectYukiHookWithXposed
class HookEntry: IYukiHookXposedInit
```
Xposed 入口类处理如下。
> 示例如下
```kotlin
class HookEntry_YukiHookXposedInit: IXposedHookLoadPackage, ...
```
编译后的类名结构如下。
> 示例如下
```
...hook.HookEntry ← 你的入口类
...hook.HookEntry_YukiHookXposedInit ← 自动生成的 Xposed 入口类
```
我们现在定义入口类名称为 `HookXposedEntry`
> 示例如下
```kotlin
@InjectYukiHookWithXposed(entryClassName = "HookXposedEntry")
class HookEntry: IYukiHookXposedInit
```
Xposed 入口类处理如下。
> 示例如下
```kotlin
class HookXposedEntry: IXposedHookLoadPackage, ...
```
编译后的类名结构如下。
> 示例如下
```
...hook.HookEntry ← 你的入口类
...hook.HookXposedEntry ← 自动生成的 Xposed 入口类
```
!> 你定义的 `entryClassName` 不可与 `xposed_init` 中的类名相同,否则自动处理程序<u>会在编译时抛出异常</u>
### IYukiHookXposedInit 接口 ### IYukiHookXposedInit 接口
```kotlin ```kotlin

View File

@@ -37,6 +37,7 @@ import com.google.devtools.ksp.symbol.KSClassDeclaration
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.regex.Pattern
/** /**
* 这是 [YukiHookAPI] 的自动生成处理类 - 核心基于 KSP * 这是 [YukiHookAPI] 的自动生成处理类 - 核心基于 KSP
@@ -113,18 +114,21 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
* 检索需要注入的类 * 检索需要注入的类
* @param sourcePath 指定的 source 路径 * @param sourcePath 指定的 source 路径
* @param modulePackageName 模块包名 * @param modulePackageName 模块包名
* @param entryClassName 入口类名
*/ */
fun fetchKSClassDeclaration(sourcePath: String, modulePackageName: String) { fun fetchKSClassDeclaration(sourcePath: String, modulePackageName: String, entryClassName: String) {
asSequence().filterIsInstance<KSClassDeclaration>().forEach { asSequence().filterIsInstance<KSClassDeclaration>().forEach {
if (isInjectOnce) when { if (isInjectOnce) when {
it.superTypes.any { type -> type.element.toString() == "IYukiHookXposedInit" } -> { it.superTypes.any { type -> type.element.toString() == "IYukiHookXposedInit" } -> {
val ecName = entryClassName.ifBlank { "${it.simpleName.asString()}$xposedClassShortName" }
injectAssets( injectAssets(
codePath = (it.location as? FileLocation?)?.filePath ?: "", codePath = (it.location as? FileLocation?)?.filePath ?: "",
sourcePath = sourcePath, sourcePath = sourcePath,
packageName = it.packageName.asString(), packageName = it.packageName.asString(),
className = it.simpleName.asString(), className = it.simpleName.asString(),
entryClassName = ecName
) )
injectClass(it.packageName.asString(), it.simpleName.asString(), modulePackageName) injectClass(it.packageName.asString(), it.simpleName.asString(), modulePackageName, ecName)
} }
it.superTypes.any { type -> type.element.toString() == "YukiHookXposedInitProxy" } -> it.superTypes.any { type -> type.element.toString() == "YukiHookXposedInitProxy" } ->
error(msg = "\"YukiHookXposedInitProxy\" was deprecated, please replace to \"IYukiHookXposedInit\"") error(msg = "\"YukiHookXposedInitProxy\" was deprecated, please replace to \"IYukiHookXposedInit\"")
@@ -139,19 +143,26 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
it.annotations.forEach { e -> it.annotations.forEach { e ->
var sourcePath = "" var sourcePath = ""
var modulePackageName = "" var modulePackageName = ""
var entryClassName = ""
e.arguments.forEach { pease -> e.arguments.forEach { pease ->
if (pease.name?.asString() == "sourcePath") if (pease.name?.asString() == "sourcePath")
sourcePath = pease.value.toString() sourcePath = pease.value.toString()
if (pease.name?.asString() == "modulePackageName") if (pease.name?.asString() == "modulePackageName")
modulePackageName = pease.value.toString() modulePackageName = pease.value.toString()
if (pease.name?.asString() == "entryClassName")
entryClassName = pease.value.toString()
} }
if ((modulePackageName.startsWith(".") || if ((modulePackageName.startsWith(".") ||
modulePackageName.endsWith(".") || modulePackageName.endsWith(".") ||
modulePackageName.contains(".").not() || modulePackageName.contains(".").not() ||
modulePackageName.contains("..")) && modulePackageName.contains("..")) &&
modulePackageName.isNotEmpty() modulePackageName.isNotEmpty()
) error(msg = "Invalid Module Package name \"$modulePackageName\"") ) error(msg = "Invalid modulePackageName \"$modulePackageName\"")
else fetchKSClassDeclaration(sourcePath, modulePackageName) if ((Pattern.compile("[*,.:~`'\"|/\\\\?!^()\\[\\]{}%@#$&\\-+=<>]").matcher(entryClassName).find() ||
true.let { for (i in 0..9) if (entryClassName.startsWith(i.toString())) return@let true;false })
&& entryClassName.isNotEmpty()
) error(msg = "Invalid entryClassName \"$entryClassName\"")
else fetchKSClassDeclaration(sourcePath, modulePackageName, entryClassName)
} }
} }
} }
@@ -163,8 +174,9 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
* @param sourcePath 指定的 source 路径 * @param sourcePath 指定的 source 路径
* @param packageName 包名 * @param packageName 包名
* @param className 类名 * @param className 类名
* @param entryClassName 入口类名
*/ */
private fun injectAssets(codePath: String, sourcePath: String, packageName: String, className: String) = private fun injectAssets(codePath: String, sourcePath: String, packageName: String, className: String, entryClassName: String) =
environment { environment {
if (codePath.isBlank()) error(msg = "Project CodePath not available") if (codePath.isBlank()) error(msg = "Project CodePath not available")
if (sourcePath.isBlank()) error(msg = "Project SourcePath not available") if (sourcePath.isBlank()) error(msg = "Project SourcePath not available")
@@ -193,7 +205,7 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
assFile.mkdirs() assFile.mkdirs()
} }
File("${assFile.absolutePath}${separator}xposed_init") File("${assFile.absolutePath}${separator}xposed_init")
.writeText(text = "$packageName.$className$xposedClassShortName") .writeText(text = "$packageName.$entryClassName")
File("${assFile.absolutePath}${separator}yukihookapi_init") File("${assFile.absolutePath}${separator}yukihookapi_init")
.writeText(text = "$packageName.$className") .writeText(text = "$packageName.$className")
} else error(msg = "Project Source Path \"$sourcePath\" verify failed! Is this an Android Project?") } else error(msg = "Project Source Path \"$sourcePath\" verify failed! Is this an Android Project?")
@@ -205,8 +217,9 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
* @param packageName 包名 * @param packageName 包名
* @param className 类名 * @param className 类名
* @param modulePackageName 模块包名 * @param modulePackageName 模块包名
* @param entryClassName 入口类名
*/ */
private fun injectClass(packageName: String, className: String, modulePackageName: String) = private fun injectClass(packageName: String, className: String, modulePackageName: String, entryClassName: String) =
environment(ignoredError = true) { environment(ignoredError = true) {
if (modulePackageName.isNotBlank()) warn(msg = "You set the customize module package name to \"$modulePackageName\", please check for yourself if it is correct") if (modulePackageName.isNotBlank()) warn(msg = "You set the customize module package name to \"$modulePackageName\", please check for yourself if it is correct")
val realPackageName = val realPackageName =
@@ -258,7 +271,7 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
codeGenerator.createNewFile( codeGenerator.createNewFile(
dependencies = Dependencies.ALL_FILES, dependencies = Dependencies.ALL_FILES,
packageName = packageName, packageName = packageName,
fileName = "$className$xposedClassShortName" fileName = entryClassName
).apply { ).apply {
/** 插入 xposed_init 代码 */ /** 插入 xposed_init 代码 */
write( write(
@@ -279,7 +292,7 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
commentContent(name = "XposedInit") + commentContent(name = "XposedInit") +
"@Keep\n" + "@Keep\n" +
"@YukiGenerateApi\n" + "@YukiGenerateApi\n" +
"class $className$xposedClassShortName : IXposedHookLoadPackage {\n" + "class $entryClassName : IXposedHookLoadPackage {\n" +
"\n" + "\n" +
" override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" + " override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" +
" if (lpparam == null) return\n" + " if (lpparam == null) return\n" +

View File

@@ -60,6 +60,11 @@ import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
* 详情请参考 [InjectYukiHookWithXposed 注解](https://fankes.github.io/YukiHookAPI/#/config/xposed-using?id=injectyukihookwithxposed-%e6%b3%a8%e8%a7%a3) * 详情请参考 [InjectYukiHookWithXposed 注解](https://fankes.github.io/YukiHookAPI/#/config/xposed-using?id=injectyukihookwithxposed-%e6%b3%a8%e8%a7%a3)
* @param sourcePath 你的项目 source 相对路径 - 默认为 ..src/main.. * @param sourcePath 你的项目 source 相对路径 - 默认为 ..src/main..
* @param modulePackageName 模块包名 - 使用标准路径可不填会自动生成 * @param modulePackageName 模块包名 - 使用标准路径可不填会自动生成
* @param entryClassName 定义 [YukiHookAPI] 自动生成 Xposed 模块入口类的名称 - 不填默认使用 HookEntryClass_YukiHookXposedInit 进行生成
*/ */
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
annotation class InjectYukiHookWithXposed(val sourcePath: String = "src/main", val modulePackageName: String = "") annotation class InjectYukiHookWithXposed(
val sourcePath: String = "src/main",
val modulePackageName: String = "",
val entryClassName: String = ""
)