Modify change injection process to GenerateData and integrate into the new code file

This commit is contained in:
2022-09-20 14:38:19 +08:00
parent f7f773ec6e
commit 6ea012472d
4 changed files with 302 additions and 331 deletions

View File

@@ -34,7 +34,8 @@ import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.FileLocation import com.google.devtools.ksp.symbol.FileLocation
import com.google.devtools.ksp.symbol.KSAnnotated import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.highcapable.yukihookapi_ksp_xposed.sources.CodeSourceFileTemplate import com.highcapable.yukihookapi_ksp_xposed.bean.GenerateData
import com.highcapable.yukihookapi_ksp_xposed.factory.sources
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.Node import org.w3c.dom.Node
import java.io.File import java.io.File
@@ -68,11 +69,11 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
/** /**
* 创建一个环境方法体方便调用 * 创建一个环境方法体方便调用
* @param ignoredError 是否忽略错误 - 默认否 * @param ignored 是否忽略错误 - 默认否
* @param env 方法体 * @param env 方法体
*/ */
private fun environment(ignoredError: Boolean = false, env: SymbolProcessorEnvironment.() -> Unit) { private fun environment(ignored: Boolean = false, env: SymbolProcessorEnvironment.() -> Unit) {
if (ignoredError) runCatching { environment.apply(env) } if (ignored) runCatching { environment.apply(env) }
else environment.apply(env) else environment.apply(env)
} }
@@ -86,6 +87,18 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
throw RuntimeException("[$TAG] $msg\n$helpMsg") throw RuntimeException("[$TAG] $msg\n$helpMsg")
} }
/**
* 创建代码文件 - 类型 kt
* @param fileName 文件名
* @param packageName 包名
* @param content 代码内容
*/
private fun SymbolProcessorEnvironment.createCodeFile(fileName: String, packageName: String, content: String?) =
codeGenerator.createNewFile(
dependencies = Dependencies.ALL_FILES,
packageName, fileName
).apply { content?.toByteArray()?.let { write(it) }; flush() }.close()
/** /**
* 发出警告 * 发出警告
* @param msg 错误消息 * @param msg 错误消息
@@ -98,10 +111,7 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
*/ */
private fun String.removeSpecialChars() = replace("\\s*|\t|\r|\n".toRegex(), replacement = "").replace(oldValue = "\"", newValue = "'") private fun String.removeSpecialChars() = replace("\\s*|\t|\r|\n".toRegex(), replacement = "").replace(oldValue = "\"", newValue = "'")
override fun process(resolver: Resolver) = emptyList<KSAnnotated>().let { override fun process(resolver: Resolver) = emptyList<KSAnnotated>().let { startProcess(resolver); it }
startProcess(resolver)
it
}
/** /**
* 开始作业入口 * 开始作业入口
@@ -109,40 +119,28 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
*/ */
private fun startProcess(resolver: Resolver) = environment { private fun startProcess(resolver: Resolver) = environment {
var isInjectOnce = true var isInjectOnce = true
val data = GenerateData()
resolver.getSymbolsWithAnnotation(annotationName).apply { resolver.getSymbolsWithAnnotation(annotationName).apply {
/** /**
* 检索需要注入的类 * 检索需要注入的类
* @param sourcePath 指定的 source 路径 * @param sourcePath 指定的 source 路径
* @param custMPackageName 自定义模块包名
* @param xInitClassName xposed_init 入口类名
* @param isUsingResourcesHook 是否启用 Resources Hook
*/ */
fun fetchKSClassDeclaration( fun fetchKSClassDeclaration(sourcePath: String) {
sourcePath: String,
custMPackageName: String,
xInitClassName: String,
isUsingResourcesHook: Boolean
) {
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 xInitPatchName = xInitClassName.ifBlank { "${it.simpleName.asString()}$xposedClassShortName" } val xInitPatchName = data.xInitClassName.ifBlank { "${it.simpleName.asString()}$xposedClassShortName" }
if (xInitClassName == it.simpleName.asString()) problem(msg = "Duplicate entryClassName \"$xInitClassName\"") if (data.xInitClassName == it.simpleName.asString())
generateAssetsFile( problem(msg = "Duplicate entryClassName \"${data.xInitClassName}\"")
codePath = (it.location as? FileLocation?)?.filePath ?: "", data.entryPackageName = it.packageName.asString()
sourcePath = sourcePath, data.entryClassName = it.simpleName.asString()
packageName = it.packageName.asString(), data.xInitClassName = xInitPatchName
custMPackageName = custMPackageName, generateAssetsFile(codePath = (it.location as? FileLocation?)?.filePath ?: "", sourcePath = sourcePath, data)
entryClassName = it.simpleName.asString(),
xInitClassName = xInitPatchName,
isUsingResourcesHook = isUsingResourcesHook
)
} }
it.superTypes.any { type -> type.element.toString() == "YukiHookXposedInitProxy" } -> it.superTypes.any { type -> type.element.toString() == "YukiHookXposedInitProxy" } ->
problem(msg = "\"YukiHookXposedInitProxy\" was deprecated, please replace to \"IYukiHookXposedInit\"") problem(msg = "\"YukiHookXposedInitProxy\" was deprecated, please replace to \"IYukiHookXposedInit\"")
else -> problem(msg = "HookEntryClass \"${it.simpleName.asString()}\" must be implements \"IYukiHookXposedInit\"") else -> problem(msg = "HookEntryClass \"${it.simpleName.asString()}\" must be implements \"IYukiHookXposedInit\"")
} } else problem(msg = "\"@InjectYukiHookWithXposed\" only can be use in once times")
else problem(msg = "\"@InjectYukiHookWithXposed\" only can be use in once times")
/** 仅处理第一个标记的类 - 再次处理将拦截并报错 */ /** 仅处理第一个标记的类 - 再次处理将拦截并报错 */
isInjectOnce = false isInjectOnce = false
} }
@@ -150,30 +148,27 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
forEach { forEach {
it.annotations.forEach { annotation -> it.annotations.forEach { annotation ->
var sourcePath = "" // 项目相对路径 var sourcePath = "" // 项目相对路径
var custMPackageName = "" // 自定义模块包名
var entryClassName = "" // xposed_init 入口类名
var isUsingResourcesHook = false // 是否启用 Resources Hook
annotation.arguments.forEach { args -> annotation.arguments.forEach { args ->
if (args.name?.asString() == "sourcePath") if (args.name?.asString() == "sourcePath")
sourcePath = args.value.toString().trim() sourcePath = args.value.toString().trim()
if (args.name?.asString() == "modulePackageName") if (args.name?.asString() == "modulePackageName")
custMPackageName = args.value.toString().trim() data.customMPackageName = args.value.toString().trim()
if (args.name?.asString() == "entryClassName") if (args.name?.asString() == "entryClassName")
entryClassName = args.value.toString().trim() data.entryClassName = args.value.toString().trim()
if (args.name?.asString() == "isUsingResourcesHook") if (args.name?.asString() == "isUsingResourcesHook")
isUsingResourcesHook = args.value as? Boolean ?: true data.isUsingResourcesHook = args.value as? Boolean ?: true
} }
if ((custMPackageName.startsWith(".") || if ((data.customMPackageName.startsWith(".") ||
custMPackageName.endsWith(".") || data.customMPackageName.endsWith(".") ||
custMPackageName.contains(".").not() || data.customMPackageName.contains(".").not() ||
custMPackageName.contains("..")) && data.customMPackageName.contains("..")) &&
custMPackageName.isNotEmpty() data.customMPackageName.isNotEmpty()
) problem(msg = "Invalid modulePackageName \"$custMPackageName\"") ) problem(msg = "Invalid modulePackageName \"${data.customMPackageName}\"")
if ((Pattern.compile("[*,.:~`'\"|/\\\\?!^()\\[\\]{}%@#$&\\-+=<>]").matcher(entryClassName).find() || if ((Pattern.compile("[*,.:~`'\"|/\\\\?!^()\\[\\]{}%@#$&\\-+=<>]").matcher(data.entryClassName).find() ||
true.let { for (i in 0..9) if (entryClassName.startsWith(i.toString())) return@let true;false }) true.let { for (i in 0..9) if (data.entryClassName.startsWith(i.toString())) return@let true; false })
&& entryClassName.isNotEmpty() && data.entryClassName.isNotEmpty()
) problem(msg = "Invalid entryClassName \"$entryClassName\"") ) problem(msg = "Invalid entryClassName \"${data.entryClassName}\"")
else fetchKSClassDeclaration(sourcePath, custMPackageName, entryClassName, isUsingResourcesHook) else fetchKSClassDeclaration(sourcePath)
} }
} }
} }
@@ -183,21 +178,9 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
* 自动生成 Xposed assets 入口文件 * 自动生成 Xposed assets 入口文件
* @param codePath 注解类的完整代码文件路径 * @param codePath 注解类的完整代码文件路径
* @param sourcePath 指定的 source 路径 * @param sourcePath 指定的 source 路径
* @param packageName 包名 * @param data 生成的模板数据
* @param custMPackageName 自定义模块包名
* @param entryClassName 入口类名
* @param xInitClassName xposed_init 入口类名
* @param isUsingResourcesHook 是否启用 Resources Hook
*/ */
private fun generateAssetsFile( private fun generateAssetsFile(codePath: String, sourcePath: String, data: GenerateData) = environment {
codePath: String,
sourcePath: String,
packageName: String,
custMPackageName: String,
entryClassName: String,
xInitClassName: String,
isUsingResourcesHook: Boolean
) = environment {
if (codePath.isBlank()) problem(msg = "Project CodePath not available") if (codePath.isBlank()) problem(msg = "Project CodePath not available")
if (sourcePath.isBlank()) problem(msg = "Project SourcePath not available") if (sourcePath.isBlank()) problem(msg = "Project SourcePath not available")
/** /**
@@ -214,7 +197,7 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
val projectPath = when { val projectPath = when {
codePath.contains("\\") -> sourcePath.replace("/", "\\") codePath.contains("\\") -> sourcePath.replace("/", "\\")
codePath.contains("/") -> sourcePath.replace("\\", "/") codePath.contains("/") -> sourcePath.replace("\\", "/")
else -> error("Unix File Separator unknown") else -> error("Unknown Unix File Separator")
}.let { }.let {
if (codePath.contains(it)) if (codePath.contains(it))
codePath.split(it)[0].apply { rootPath = this } + it codePath.split(it)[0].apply { rootPath = this } + it
@@ -226,78 +209,52 @@ class YukiHookXposedProcessor : SymbolProcessorProvider {
val manifestFile = File("$projectPath${separator}AndroidManifest.xml") val manifestFile = File("$projectPath${separator}AndroidManifest.xml")
if (manifestFile.exists()) { if (manifestFile.exists()) {
if (assetsFile.exists().not() || assetsFile.isDirectory.not()) assetsFile.apply { delete(); mkdirs() } if (assetsFile.exists().not() || assetsFile.isDirectory.not()) assetsFile.apply { delete(); mkdirs() }
val modulePackageName = parseModulePackageName(manifestFile, gradleFile, gradleKtsFile) data.modulePackageName = parseModulePackageName(manifestFile, gradleFile, gradleKtsFile)
if (modulePackageName.isBlank() && custMPackageName.isBlank()) if (data.modulePackageName.isBlank() && data.customMPackageName.isBlank())
problem(msg = "Cannot identify your Module App's package name, tried AndroidManifest.xml, build.gradle and build.gradle.kts") problem(msg = "Cannot identify your Module App's package name, tried AndroidManifest.xml, build.gradle and build.gradle.kts")
File("${assetsFile.absolutePath}${separator}xposed_init") File("${assetsFile.absolutePath}${separator}xposed_init")
.writeText(text = "$packageName.$xInitClassName") .writeText(text = "${data.entryPackageName}.${data.xInitClassName}")
File("${assetsFile.absolutePath}${separator}yukihookapi_init") File("${assetsFile.absolutePath}${separator}yukihookapi_init")
.writeText(text = "$packageName.$entryClassName") .writeText(text = "${data.entryPackageName}.${data.entryClassName}")
generateClassFile(packageName, modulePackageName, custMPackageName, entryClassName, xInitClassName, isUsingResourcesHook) generateClassFile(data)
} else problem(msg = "Project Source Path \"$sourcePath\" verify failed! Is this an Android Project?") } else problem(msg = "Project Source Path \"$sourcePath\" verify failed! Is this an Android Project?")
} }
/** /**
* 自动生成指定类文件 * 自动生成指定类文件
* @param packageName 包名 * @param data 生成的模板数据
* @param modulePackageName 模块包名
* @param customMPackageName 自定义模块包名
* @param entryClassName 入口类名
* @param xInitClassName xposed_init 入口类名
* @param isUsingResourcesHook 是否启用 Resources Hook
*/ */
private fun generateClassFile( private fun generateClassFile(data: GenerateData) = environment(ignored = true) {
packageName: String, if (data.customMPackageName.isNotBlank()) warn(
modulePackageName: String, msg = "You set the customize module package name to \"${data.customMPackageName}\", " +
customMPackageName: String, "please check for yourself if it is correct"
entryClassName: String, )
xInitClassName: String,
isUsingResourcesHook: Boolean
) = environment(ignoredError = true) {
if (customMPackageName.isNotBlank())
warn(msg = "You set the customize module package name to \"$customMPackageName\", please check for yourself if it is correct")
val mdAppInjectPackageName = "com.highcapable.yukihookapi.hook.xposed.application.inject" val mdAppInjectPackageName = "com.highcapable.yukihookapi.hook.xposed.application.inject"
val ykBridgeInjectPackageName = "com.highcapable.yukihookapi.hook.xposed.bridge.inject" val ykBridgeInjectPackageName = "com.highcapable.yukihookapi.hook.xposed.bridge.inject"
/** 插入 ModuleApplication_Injector 代码 */ /** 插入 ModuleApplication_Injector 代码 */
codeGenerator.createNewFile( createCodeFile(
dependencies = Dependencies.ALL_FILES, fileName = "ModuleApplication_Injector",
packageName = mdAppInjectPackageName, packageName = mdAppInjectPackageName,
fileName = "ModuleApplication_Injector" content = data.apply { injectPackageName = mdAppInjectPackageName }.sources()["ModuleApplication_Injector"]
).apply { )
write(CodeSourceFileTemplate.getModuleApplicationInjectorFileByteArray(mdAppInjectPackageName, packageName, entryClassName))
flush()
close()
}
/** 插入 YukiHookBridge_Injector 代码 */ /** 插入 YukiHookBridge_Injector 代码 */
codeGenerator.createNewFile( createCodeFile(
dependencies = Dependencies.ALL_FILES, fileName = "YukiHookBridge_Injector",
packageName = ykBridgeInjectPackageName, packageName = ykBridgeInjectPackageName,
fileName = "YukiHookBridge_Injector" content = data.apply { injectPackageName = ykBridgeInjectPackageName }.sources()["YukiHookBridge_Injector"]
).apply { )
write(CodeSourceFileTemplate.getYukiHookBridgeInjectorFileByteArray(ykBridgeInjectPackageName))
flush()
close()
}
/** 插入 xposed_init 代码 */ /** 插入 xposed_init 代码 */
codeGenerator.createNewFile( createCodeFile(
dependencies = Dependencies.ALL_FILES, fileName = data.xInitClassName,
packageName = packageName, packageName = data.entryPackageName,
fileName = xInitClassName content = data.sources()["xposed_init"]
).apply { )
write(CodeSourceFileTemplate.getXposedInitFileByteArray(packageName, entryClassName, xInitClassName, isUsingResourcesHook))
flush()
close()
}
/** 插入 xposed_init_Impl 代码 */ /** 插入 xposed_init_Impl 代码 */
codeGenerator.createNewFile( createCodeFile(
dependencies = Dependencies.ALL_FILES, fileName = "${data.entryClassName}_Impl",
packageName = packageName, packageName = data.entryPackageName,
fileName = "${entryClassName}_Impl" content = data.sources()["xposed_init_Impl"]
).apply { )
write(CodeSourceFileTemplate.getXposedInitImplFileByteArray(packageName, modulePackageName, customMPackageName, entryClassName))
flush()
close()
}
} }
/** /**

View File

@@ -0,0 +1,48 @@
/*
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
* Copyright (C) 2019-2022 HighCapable
* https://github.com/fankes/YukiHookAPI
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This file is Created by fankes on 2022/9/20.
*/
package com.highcapable.yukihookapi_ksp_xposed.bean
/**
* 生成的模板数据实例
* @param entryPackageName 入口类包名
* @param injectPackageName 注入类包名
* @param modulePackageName 模块包名 (命名空间)
* @param customMPackageName 自定义模块包名
* @param entryClassName 入口类名
* @param xInitClassName xposed_init 入口类名
* @param isUsingResourcesHook 是否启用 Resources Hook
*/
data class GenerateData(
var entryPackageName: String = "",
var injectPackageName: String = "",
var modulePackageName: String = "",
var customMPackageName: String = "",
var entryClassName: String = "",
var xInitClassName: String = "",
var isUsingResourcesHook: Boolean = true
)

View File

@@ -0,0 +1,180 @@
/*
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
* Copyright (C) 2019-2022 HighCapable
* https://github.com/fankes/YukiHookAPI
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This file is Created by fankes on 2022/9/20.
*/
package com.highcapable.yukihookapi_ksp_xposed.factory
import com.highcapable.yukihookapi_ksp_xposed.bean.GenerateData
import java.text.SimpleDateFormat
import java.util.*
/**
* 创建文件注释
* @param entryClassName 入口类名 - 空则不生成
* @param currrentClassTag 当前注入类标签
* @return [String]
*/
private fun createCommentContent(entryClassName: String = "", currrentClassTag: String) =
("/**\n" +
" * $currrentClassTag Inject Class\n" +
" *\n" +
" * Compiled from YukiHookXposedProcessor\n" +
" *\n" +
(if (entryClassName.isNotBlank()) " * HookEntryClass: [$entryClassName]\n *\n" else "") +
" * Generate Date: ${SimpleDateFormat.getDateTimeInstance().format(Date())}\n" +
" *\n" +
" * Powered by YukiHookAPI (C) HighCapable 2022\n" +
" *\n" +
" * Project Address: [YukiHookAPI](https://github.com/fankes/YukiHookAPI)\n" +
" */\n")
/**
* 获得注入文件代码内容
* @return [Map]<[String],[String]>
*/
fun GenerateData.sources() = mapOf(
"ModuleApplication_Injector" to ("@file:Suppress(\"ClassName\")\n" +
"\n" +
"package $entryPackageName\n" +
"\n" +
"import $entryPackageName.$entryClassName\n" +
"\n" +
createCommentContent(entryClassName, currrentClassTag = "ModuleApplication") +
"object ModuleApplication_Injector {\n" +
"\n" +
" @JvmStatic\n" +
" fun callApiInit() = try {\n" +
" $entryClassName().onInit()\n" +
" } catch (_: Throwable) {\n" +
" }\n" +
"}"),
"YukiHookBridge_Injector" to ("@file:Suppress(\"ClassName\")\n" +
"\n" +
"package $entryPackageName\n" +
"\n" +
createCommentContent(currrentClassTag = "YukiHookBridge") +
"object YukiHookBridge_Injector {\n" +
"\n" +
" @JvmStatic\n" +
" fun getModuleGeneratedVersion() = \"${System.currentTimeMillis()}\"\n" +
"}"),
"xposed_init" to ("@file:Suppress(\"ClassName\")\n" +
"\n" +
"package $entryPackageName\n" +
"\n" +
"import androidx.annotation.Keep\n" +
"import com.highcapable.yukihookapi.hook.xposed.bridge.event.YukiXposedEvent\n" +
"import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" +
(if (isUsingResourcesHook) "import de.robv.android.xposed.IXposedHookInitPackageResources\n" else "") +
"import de.robv.android.xposed.IXposedHookLoadPackage\n" +
"import de.robv.android.xposed.IXposedHookZygoteInit\n" +
(if (isUsingResourcesHook) "import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" else "") +
"import de.robv.android.xposed.callbacks.XC_LoadPackage\n" +
"\n" +
createCommentContent(entryClassName, currrentClassTag = "Xposed Init") +
"@Keep\n" +
"@YukiGenerateApi\n" +
"class $xInitClassName : IXposedHookZygoteInit, IXposedHookLoadPackage" +
"${if (isUsingResourcesHook) ", IXposedHookInitPackageResources" else ""} {\n" +
"\n" +
" override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) {\n" +
" ${entryClassName}_Impl.callInitZygote(sparam)\n" +
" YukiXposedEvent.EventHandler.callInitZygote(sparam)\n" +
" }\n" +
"\n" +
" override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" +
" ${entryClassName}_Impl.callHandleLoadPackage(lpparam)\n" +
" YukiXposedEvent.EventHandler.callHandleLoadPackage(lpparam)\n" +
" }\n" +
(if (isUsingResourcesHook)
("\n override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" +
" ${entryClassName}_Impl.callHandleInitPackageResources(resparam)\n" +
" YukiXposedEvent.EventHandler.callHandleInitPackageResources(resparam)\n" +
" }\n") else "") +
"}"),
"xposed_init_Impl" to ("@file:Suppress(\"ClassName\")\n" +
"\n" +
"package $entryPackageName\n" +
"\n" +
"import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" +
"import com.highcapable.yukihookapi.hook.log.loggerE\n" +
"import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge\n" +
"import de.robv.android.xposed.IXposedHookZygoteInit\n" +
"import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" +
"import de.robv.android.xposed.callbacks.XC_LoadPackage\n" +
(if (customMPackageName.isBlank()) "import $modulePackageName.BuildConfig\n" else "") +
"\n" +
createCommentContent(entryClassName, currrentClassTag = "Xposed Init Impl") +
"@YukiGenerateApi\n" +
"object ${entryClassName}_Impl {\n" +
"\n" +
" private const val modulePackageName = " +
(if (customMPackageName.isNotBlank()) "\"$customMPackageName\"" else "BuildConfig.APPLICATION_ID") + "\n" +
"\n" +
" private var isZygoteBinded = false\n" +
"\n" +
" private val hookEntry = $entryClassName()\n" +
"\n" +
" private fun callXposedLoaded(\n" +
" isZygoteLoaded: Boolean = false,\n" +
" lpparam: XC_LoadPackage.LoadPackageParam? = null,\n" +
" resparam: XC_InitPackageResources.InitPackageResourcesParam? = null\n" +
" ) {\n" +
" if (isZygoteBinded.not()) runCatching {\n" +
" hookEntry.onXposedEvent()\n" +
" hookEntry.onInit()\n" +
" if (YukiHookBridge.isXposedCallbackSetUp) {\n" +
" loggerE(msg = \"You cannot load a hooker in \\\"onInit\\\" or \\\"onXposedEvent\\\" method! Aborted\")\n" +
" return\n" +
" }\n" +
" hookEntry.onHook()\n" +
" YukiHookBridge.callXposedInitialized()\n" +
" YukiHookBridge.modulePackageName = modulePackageName\n" +
" }.onFailure { loggerE(msg = \"YukiHookAPI try to load HookEntryClass failed\", e = it) }\n" +
" YukiHookBridge.callXposedLoaded(isZygoteLoaded, lpparam, resparam)\n" +
" }\n" +
"\n" +
" @YukiGenerateApi\n" +
" fun callInitZygote(sparam: IXposedHookZygoteInit.StartupParam?) {\n" +
" if (sparam == null) return\n" +
" runCatching {\n" +
" YukiHookBridge.callXposedZygoteLoaded(sparam)\n" +
" }.onFailure { loggerE(msg = \"YukiHookAPI bind initZygote failed\", e = it) }\n" +
" callXposedLoaded(isZygoteLoaded = true)\n" +
" isZygoteBinded = true\n" +
" }\n" +
"\n" +
" @YukiGenerateApi\n" +
" fun callHandleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" +
" if (lpparam != null) callXposedLoaded(lpparam = lpparam)\n" +
" }\n" +
"\n" +
" @YukiGenerateApi\n" +
" fun callHandleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" +
" if (resparam != null) callXposedLoaded(resparam = resparam)\n" +
" }\n" +
"}")
)

View File

@@ -1,214 +0,0 @@
/*
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
* Copyright (C) 2019-2022 HighCapable
* https://github.com/fankes/YukiHookAPI
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This file is Created by fankes on 2022/4/27.
*/
package com.highcapable.yukihookapi_ksp_xposed.sources
import java.text.SimpleDateFormat
import java.util.*
/**
* 代码文件注入模板类
*/
object CodeSourceFileTemplate {
/**
* 获得文件注释
* @param entryClassName 入口类名 - 空则不生成
* @param currrentClassTag 当前注入类标签
* @return [String]
*/
private fun getCommentContent(entryClassName: String = "", currrentClassTag: String) =
("/**\n" +
" * $currrentClassTag Inject Class\n" +
" *\n" +
" * Compiled from YukiHookXposedProcessor\n" +
" *\n" +
(if (entryClassName.isNotBlank()) " * HookEntryClass: [$entryClassName]\n *\n" else "") +
" * Generate Date: ${SimpleDateFormat.getDateTimeInstance().format(Date())}\n" +
" *\n" +
" * Powered by YukiHookAPI (C) HighCapable 2022\n" +
" *\n" +
" * Project Address: [YukiHookAPI](https://github.com/fankes/YukiHookAPI)\n" +
" */\n")
/**
* 获得 ModuleApplication_Injector 注入文件
* @param packageName 包名
* @param entryPackageName 入口类包名
* @param entryClassName 入口类名
* @return [ByteArray]
*/
fun getModuleApplicationInjectorFileByteArray(packageName: String, entryPackageName: String, entryClassName: String) =
("@file:Suppress(\"ClassName\")\n" +
"\n" +
"package $packageName\n" +
"\n" +
"import $entryPackageName.$entryClassName\n" +
"\n" +
getCommentContent(entryClassName, currrentClassTag = "ModuleApplication") +
"object ModuleApplication_Injector {\n" +
"\n" +
" @JvmStatic\n" +
" fun callApiInit() = try {\n" +
" $entryClassName().onInit()\n" +
" } catch (_: Throwable) {\n" +
" }\n" +
"}").toByteArray()
/**
* 获得 YukiHookBridge_Injector 注入文件
* @param packageName 包名
* @return [ByteArray]
*/
fun getYukiHookBridgeInjectorFileByteArray(packageName: String) =
("@file:Suppress(\"ClassName\")\n" +
"\n" +
"package $packageName\n" +
"\n" +
getCommentContent(currrentClassTag = "YukiHookBridge") +
"object YukiHookBridge_Injector {\n" +
"\n" +
" @JvmStatic\n" +
" fun getModuleGeneratedVersion() = \"${System.currentTimeMillis()}\"\n" +
"}").toByteArray()
/**
* 获得 xposed_init 注入文件
* @param packageName 包名
* @param entryClassName 入口类名
* @param xInitClassName xposed_init 入口类名
* @param isUsingResourcesHook 是否启用 Resources Hook
* @return [ByteArray]
*/
fun getXposedInitFileByteArray(packageName: String, entryClassName: String, xInitClassName: String, isUsingResourcesHook: Boolean) =
("@file:Suppress(\"ClassName\")\n" +
"\n" +
"package $packageName\n" +
"\n" +
"import androidx.annotation.Keep\n" +
"import com.highcapable.yukihookapi.hook.xposed.bridge.event.YukiXposedEvent\n" +
"import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" +
(if (isUsingResourcesHook) "import de.robv.android.xposed.IXposedHookInitPackageResources\n" else "") +
"import de.robv.android.xposed.IXposedHookLoadPackage\n" +
"import de.robv.android.xposed.IXposedHookZygoteInit\n" +
(if (isUsingResourcesHook) "import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" else "") +
"import de.robv.android.xposed.callbacks.XC_LoadPackage\n" +
"\n" +
getCommentContent(entryClassName, currrentClassTag = "Xposed Init") +
"@Keep\n" +
"@YukiGenerateApi\n" +
"class $xInitClassName : IXposedHookZygoteInit, IXposedHookLoadPackage" +
"${if (isUsingResourcesHook) ", IXposedHookInitPackageResources" else ""} {\n" +
"\n" +
" override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) {\n" +
" ${entryClassName}_Impl.callInitZygote(sparam)\n" +
" YukiXposedEvent.EventHandler.callInitZygote(sparam)\n" +
" }\n" +
"\n" +
" override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" +
" ${entryClassName}_Impl.callHandleLoadPackage(lpparam)\n" +
" YukiXposedEvent.EventHandler.callHandleLoadPackage(lpparam)\n" +
" }\n" +
(if (isUsingResourcesHook)
("\n override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" +
" ${entryClassName}_Impl.callHandleInitPackageResources(resparam)\n" +
" YukiXposedEvent.EventHandler.callHandleInitPackageResources(resparam)\n" +
" }\n") else "") +
"}").toByteArray()
/**
* 获得 xposed_init_Impl 注入文件
* @param packageName 包名
* @param modulePackageName 模块包名
* @param customMPackageName 自定义模块包名
* @param entryClassName 入口类名
* @return [ByteArray]
*/
fun getXposedInitImplFileByteArray(packageName: String, modulePackageName: String, customMPackageName: String, entryClassName: String) =
("@file:Suppress(\"ClassName\")\n" +
"\n" +
"package $packageName\n" +
"\n" +
"import com.highcapable.yukihookapi.annotation.YukiGenerateApi\n" +
"import com.highcapable.yukihookapi.hook.log.loggerE\n" +
"import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge\n" +
"import de.robv.android.xposed.IXposedHookZygoteInit\n" +
"import de.robv.android.xposed.callbacks.XC_InitPackageResources\n" +
"import de.robv.android.xposed.callbacks.XC_LoadPackage\n" +
(if (customMPackageName.isBlank()) "import $modulePackageName.BuildConfig\n" else "") +
"\n" +
getCommentContent(entryClassName, currrentClassTag = "Xposed Init Impl") +
"@YukiGenerateApi\n" +
"object ${entryClassName}_Impl {\n" +
"\n" +
" private const val modulePackageName = " +
(if (customMPackageName.isNotBlank()) "\"$customMPackageName\"" else "BuildConfig.APPLICATION_ID") + "\n" +
"\n" +
" private var isZygoteBinded = false\n" +
"\n" +
" private val hookEntry = $entryClassName()\n" +
"\n" +
" private fun callXposedLoaded(\n" +
" isZygoteLoaded: Boolean = false,\n" +
" lpparam: XC_LoadPackage.LoadPackageParam? = null,\n" +
" resparam: XC_InitPackageResources.InitPackageResourcesParam? = null\n" +
" ) {\n" +
" if (isZygoteBinded.not()) runCatching {\n" +
" hookEntry.onXposedEvent()\n" +
" hookEntry.onInit()\n" +
" if (YukiHookBridge.isXposedCallbackSetUp) {\n" +
" loggerE(msg = \"You cannot load a hooker in \\\"onInit\\\" or \\\"onXposedEvent\\\" method! Aborted\")\n" +
" return\n" +
" }\n" +
" hookEntry.onHook()\n" +
" YukiHookBridge.callXposedInitialized()\n" +
" YukiHookBridge.modulePackageName = modulePackageName\n" +
" }.onFailure { loggerE(msg = \"YukiHookAPI try to load HookEntryClass failed\", e = it) }\n" +
" YukiHookBridge.callXposedLoaded(isZygoteLoaded, lpparam, resparam)\n" +
" }\n" +
"\n" +
" @YukiGenerateApi\n" +
" fun callInitZygote(sparam: IXposedHookZygoteInit.StartupParam?) {\n" +
" if (sparam == null) return\n" +
" runCatching {\n" +
" YukiHookBridge.callXposedZygoteLoaded(sparam)\n" +
" }.onFailure { loggerE(msg = \"YukiHookAPI bind initZygote failed\", e = it) }\n" +
" callXposedLoaded(isZygoteLoaded = true)\n" +
" isZygoteBinded = true\n" +
" }\n" +
"\n" +
" @YukiGenerateApi\n" +
" fun callHandleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" +
" if (lpparam != null) callXposedLoaded(lpparam = lpparam)\n" +
" }\n" +
"\n" +
" @YukiGenerateApi\n" +
" fun callHandleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {\n" +
" if (resparam != null) callXposedLoaded(resparam = resparam)\n" +
" }\n" +
"}").toByteArray()
}