mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-10-19 09:39:25 +08:00
Remove old documentations
This commit is contained in:
3
docs/.vscode/settings.json
vendored
3
docs/.vscode/settings.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
# 404 - 没有找到此页面
|
||||
|
||||
请尝试重新输入你需要的页面或通过导航前往指定文档页面。
|
@@ -1,20 +0,0 @@
|
||||
<br/><br/>
|
||||
<img src="icon.png" width = "100" height = "100"/>
|
||||
<br/>
|
||||
|
||||
# Yuki Hook API
|
||||
|
||||
> 一个使用 Kotlin 重构的轻量、高效、稳定的 Xposed Hook API
|
||||
|
||||
- 致力于为 Xposed 模块开发更加便捷而打造的零学习成本框架
|
||||
|
||||
- 轻量优雅 高效调试
|
||||
|
||||
- 方便移植 快速上手
|
||||
|
||||
<font size=3 style="opacity: 0.6">`更新时间 2022-07-23 22:33`</font>
|
||||
|
||||
[GitHub](https://github.com/fankes/YukiHookAPI)
|
||||
[Get Started](#介绍)
|
||||
|
||||

|
@@ -1,21 +0,0 @@
|
||||
* 入门
|
||||
* [介绍](guide/home)
|
||||
* [基础知识](guide/knowledge)
|
||||
* [快速开始](guide/quick-start)
|
||||
* [用法示例](guide/example)
|
||||
* [特色功能](guide/special-feature)
|
||||
* [从 Xposed API 迁移](guide/move-to-new-api)
|
||||
|
||||
* 配置
|
||||
* [API 基本配置](config/api-example)
|
||||
* [API 异常处理](config/api-exception)
|
||||
* [R8 与 Proguard 混淆](config/r8-proguard)
|
||||
|
||||
* 工具
|
||||
* [YukiHookAPI 构建工具](tools/yukihookapi-projectbuilder)
|
||||
|
||||
* API 文档
|
||||
* [文档介绍](api/home)
|
||||
* [Public API](api/document)
|
||||
|
||||
* [联系我们](about/contacts)
|
@@ -1,27 +0,0 @@
|
||||
* 入门
|
||||
* [介绍](guide/home)
|
||||
* [基础知识](guide/knowledge)
|
||||
* [快速开始](guide/quick-start)
|
||||
* [用法示例](guide/example)
|
||||
* [特色功能](guide/special-feature)
|
||||
* [从 Xposed API 迁移](guide/move-to-new-api)
|
||||
|
||||
* 配置
|
||||
* [API 基本配置](config/api-example)
|
||||
* [API 异常处理](config/api-exception)
|
||||
* [作为 Xposed 模块使用的相关配置](config/xposed-using)
|
||||
* [作为 Hook API 使用的相关配置](config/api-using)
|
||||
* [R8 与 Proguard 混淆](config/r8-proguard)
|
||||
|
||||
* 工具
|
||||
* [YukiHookAPI 构建工具](tools/yukihookapi-projectbuilder)
|
||||
|
||||
* API 文档
|
||||
* [文档介绍](api/home)
|
||||
* [Public API](api/document)
|
||||
|
||||
* 关于
|
||||
* [更新日志](about/changelog)
|
||||
* [展望未来](about/future)
|
||||
* [联系我们](about/contacts)
|
||||
* [关于此文档](about/about)
|
@@ -1,35 +0,0 @@
|
||||
# 关于此文档
|
||||
|
||||
> 此文档由 [docsify](https://github.com/QingWei-Li/docsify/) 生成。
|
||||
|
||||
`docsify` 是一个动态生成文档网站的工具。不同于 `GitBook、Hexo` 的地方是它不会生成将 `.md` 转成 `.html` 文件,所有转换工作都是在运行时进行。
|
||||
|
||||
## License
|
||||
|
||||
[The MIT License (MIT)](https://github.com/fankes/YukiHookAPI/blob/master/LICENSE)
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2019-2022 HighCapable
|
||||
|
||||
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.
|
||||
```
|
||||
|
||||
版权所有 © 2019-2022 HighCapable
|
@@ -1,269 +0,0 @@
|
||||
# 更新日志
|
||||
|
||||
> 这里记录了 `YukiHookAPI` 的版本更新历史。
|
||||
|
||||
### 1.0.92 | 2022.05.31
|
||||
|
||||
- 修正了大量方法中 callback 的命名方法
|
||||
- 更换方案再次修复 `YukiHookDataChannel` 在低于 **Android 12** 的设备上不能回调当前 `Activity` 广播的问题
|
||||
- `InjectYukiHookWithXposed` 注解新增 `isUsingResourcesHook` 功能,现在你可以选择性关闭自动生成 `IXposedHookInitPackageResources` 的依赖接口了
|
||||
|
||||
### 1.0.91 | 2022.05.29
|
||||
|
||||
- 修复部分设备的定制系统在 LSPosed 环境下开机启动获取的 `ClassLoader` 错误的问题,感谢 [Luckyzyx](https://github.com/luckyzyx) 的反馈
|
||||
- 修复 `YukiHookDataChannel` 在 **ZUI** 以及低于 **Android 12** 的系统上不能回调当前 `Activity` 广播的问题
|
||||
- 整合 `YukiHookModuleStatus` 功能到 `YukiHookAPI.Status`,重写了大量方法,现在你可以在模块与宿主中双向判断模块激活等状态信息
|
||||
|
||||
### 1.0.90 | 2022.05.27
|
||||
|
||||
- 修复 `YukiHookDataChannel` 在模块设置监听回调时闪退的问题
|
||||
- 修复 `YukiHookDataChannel` 在非当前 `Activity` 情况下依然会回调的问题
|
||||
- 移除 `YukiHookDataChannel` 回调事件的默认值,没有即不回调
|
||||
- 移除 `YukiHookModulePrefs` 在 XShare 不可读的情况下打印的警告
|
||||
- 新增 `YukiHookModulePrefs` 中的 `isXSharePrefsReadable` 方法,可判断当前的 XShare 是否可用
|
||||
|
||||
### 1.0.89 | 2022.05.26
|
||||
|
||||
- 修复 `YukiHookDataChannel` 不能重复设置监听的问题,并加入在模块不同 `Activity` 中重复响应和自动跟随 `Activity` 销毁监听功能
|
||||
- 新增 `YukiHookDataChannel` 重复监听用例说明文档
|
||||
- 加入 `onAlreadyHooked` 方法,可判断当前方法是否被重复 Hook
|
||||
- 修改部分重复添加 HashMap 的逻辑,移除 `putIfAbsent` 方法,允许覆盖添加
|
||||
- 修复了几处可能的 BUG
|
||||
|
||||
### 1.0.88 | 2022.05.25
|
||||
|
||||
- 对 Xposed API 完全解耦合
|
||||
- 增加了 `type` 中的 `android` 类型
|
||||
- 将 `YukiHookModuleStatus` 从自动生成代码中分离,并加入 `isEnableHookModuleStatus` 的开关,由你决定是否启用
|
||||
- 对 API 大量类的构造方法进行了 internal 闭包处理
|
||||
- 将 `YukiHookModulePrefs` 设置为单例运行,防止重复创建浪费系统资源
|
||||
- 修复自 `1.0.80` 版本后无法嵌套 Hook 的 BUG,并优化嵌套 Hook 相关功能
|
||||
- 修改 Hooker 存储方案由 HashSet 到 HashMap,防止重复添加 Hooker 的问题
|
||||
- 修改 Hook 核心实现方法,加入查重,避免重复 Hook 多次回调 `HookParam` 方法
|
||||
- `MethodFinder` 与 `FieldFinder` 加入查找模糊方法、变量名称功能,可调用 `name { ... }` 来设置查找条件,支持正则
|
||||
- 优化并修改 `appContext` 的获取方式,降低会取到空的问题的可能性
|
||||
- 修改自动生成的代码中 `logger` 的打印 `TAG` 默认为你自定义的名称,方便进行调试
|
||||
- 优化 `YukiHookBridge` 的 `Hooker` 实现方式,提升 Hook 性能
|
||||
- `PackageParam` 增加 `onAppLifecycle` 方法,可原生监听宿主的生命周期以及实现注册系统广播功能
|
||||
- 新增 `YukiHookDataChannel` 功能,可在模块与宿主保持存活的情况下使用系统无序广播进行通讯
|
||||
- `YukiHookDataChannel` 增加 `checkingVersionEquals` 方法,可通过监听来验证模块更新后宿主并未更新版本不匹配问题
|
||||
- `demo-module` 的示例代码中新增 Java 版本的示例,仅供参考
|
||||
|
||||
### 1.0.87 | 2022.05.10
|
||||
|
||||
- 新增 `refreshModuleAppResources` 功能,以适配语言区域、字体大小、分辨率改变等情况下的 Resources 刷新
|
||||
- 新增 `isEnableModuleAppResourcesCache` 功能,可自行设置是否自动缓存当前模块的 Resources
|
||||
|
||||
### 1.0.86 | 2022.05.06
|
||||
|
||||
- 修复不支持 Resources Hook(资源钩子) 的情况下在 `initZygote` 时持续报错的问题,复现在 **ZUI**/**LSPosed CI(1.8.3-6550)**
|
||||
- 优化并对 Resources Hook 进行异常处理,只有被使用后不支持才会打印错误和警告
|
||||
|
||||
### 1.0.85 | 2022.05.04
|
||||
|
||||
- 修复无法 Hook 系统框架的严重问题,从 `1.0.80` 开始出现
|
||||
- 调试日志中新增区分 `initZygote` 装载的包名为 `android-zygote`,`packageName` 保持 `android` 不变
|
||||
|
||||
### 1.0.83 | 2022.05.04
|
||||
|
||||
- 修复 `YukiHookModuleStatus` 在 `loadSystem` 后大量报错的问题
|
||||
- 新增 `type` 中的 `android` 类型
|
||||
- 更新帮助文档的示例说明
|
||||
|
||||
### 1.0.82 | 2022.05.04
|
||||
|
||||
- 修复了一处概念混淆错误,区分 `initZygote` 与系统框架的关系,之前的注释和文档有问题,非常抱歉
|
||||
- `PackageParam` 新增 `loadSystem` 方法,不需要再写 `loadApp(name = "android")` 即可 Hook 系统框架
|
||||
|
||||
### 1.0.81 | 2022.05.04
|
||||
|
||||
- 修复使用 `by` 方法设置条件后 Hook 方法体内查找不到的方法、构造方法依然输出错误日志的问题
|
||||
- 在执行 Hook 过程中加入全局日志显示当前 Hook APP 的包名,并修复一处错误日志打印样式的问题
|
||||
|
||||
### 1.0.80 | 2022.05.01
|
||||
|
||||
- `InjectYukiHookWithXposed` 注解新增 `entryClassName` 功能,可自定义生成的 `xposed_init` 入口类名
|
||||
- ~~`YukiHookXposedInitProxy`~~ 更名为 `IYukiHookXposedInit`,原接口名称已作废,将在后续版本中直接被删除
|
||||
- 新增 `initZygote` 与 Resources Hook 功能,支持 Hook Layout
|
||||
- 新增 `onXposedEvent` 方法,可监听原生 Xposed API 的全部事件
|
||||
- 对 Hook 功能的 `lambda` 进行 `inline` 处理,避免生成过碎的匿名类,提升编译后的运行性能
|
||||
- 修复 `PrefsData` 编译后的方法体复制过大的问题
|
||||
- 增加 `XSharePreference` 可读性测试,失败后会自动打印警告日志
|
||||
- `PackageParam` 新增 `appResources`、`moduleAppResources`、`moduleAppFilePath` 功能
|
||||
- `PackageParam` 的 `loadApp` 新增不写 `name` 功能,默认筛选全部 APP
|
||||
- `PackageParam` 新增 `loadZygote` 方法,可直接 Hook 系统框架
|
||||
- `PackageParam` 新增 `resources().hook` 功能
|
||||
- 优化方法、构造方法、变量查找功能,找不到的错误日志将优先显示已设置的查询条件
|
||||
- 增加 `hasExtends` 扩展方法,可判断当前 `Class` 是否有继承关系
|
||||
- 增加 `isSupportResourcesHook` 功能,判断当前是否支持资源钩子(Resources Hook)
|
||||
- `current` 功能新增 `superClass` 方法调用父类
|
||||
- 查找方法、构造方法、变量新增 `superClass` 查询条件,可继续在父类中查找
|
||||
- `YukiHookAPI` 大量方法与 Xposed API 解耦合
|
||||
- 新增 Xposed API 的原生 Hook 优先级功能
|
||||
- 修复 `isFirstApplication` 可能判断不准确的问题
|
||||
- 屏蔽 MIUI 系统上 MiuiCatcherPatch 重复调用 Hook 入口方法的问题
|
||||
- 优化 Hook 入口调用方法,避免因为 Hook Framework 问题导致多次调用
|
||||
- 修复 Hook `ClassLoader` 导致 Hook 卡死的问题,感谢 [WankkoRee](https://github.com/WankkoRee) 的反馈
|
||||
- 提升 `XC_Callback` 接口对接后的性能
|
||||
- Java `type` 新增 `ClassLoader` 类型
|
||||
- 优化 API 帮助文档,修复可能持续缓存页面的问题
|
||||
|
||||
### 1.0.78 | 2022.04.18
|
||||
|
||||
- `YukiHookModulePrefs` 新增 `isRunInNewXShareMode` 方法,可用于判断模块当前是否处于 `New XSharePreference` 模式
|
||||
- 修复 `YukiHookModulePrefs` 在 `New XSharePreference` 模式下工作的部分问题
|
||||
- 新增 `ModulePreferenceFragment`,现在,你可以完全替换掉 `PreferenceFragmentCompat` 并开始使用新的功能
|
||||
- 适配 `PreferenceFragmentCompat` 的 Sp 数据存储解决方案,感谢 [mahoshojoHCG](https://github.com/mahoshojoHCG) 的反馈
|
||||
- 更新自动处理程序以及 `Kotlin` 依赖到最新版本
|
||||
- 修正部分文档和代码注释中的错误
|
||||
|
||||
### 1.0.77 | 2022.04.15
|
||||
|
||||
- `YukiHookModulePrefs` 新增 `clear` 方法,感谢 [WankkoRee](https://github.com/WankkoRee) 的建议
|
||||
- `YukiHookModulePrefs` 新增 `getStringSet`、`putStringSet`、`all` 方法
|
||||
- `HookParam` 的 `args` 增加 `any` 方法
|
||||
- 新增 `ModuleApplication`,可在模块中继承此类实现更多功能
|
||||
- 对接全部的 `findClass` 功能到 Xposed API,在非宿主环境继续使用原生 `ClassLoader`
|
||||
- 修复了一些可能存在的 BUG
|
||||
|
||||
### 1.0.75 | 2022.04.13
|
||||
|
||||
- 更正了自动处理程序的逻辑识别部分,感谢 [ApeaSuperz](https://github.com/ApeaSuperz) 的贡献
|
||||
- 修正一处文档注释的引用未更改的问题
|
||||
- `HookParam` 中删除了 `firstArgs` 与 `lastArgs` 方法,现在你可以使用 `args().first()` 与 `args().last()` 来取代它
|
||||
- `HookParam` 中删除了 `args()` 中的默认参数 `index = 0`,现在你可以使用 `args().first()` 或 `args(index = 0)` 来取代它
|
||||
- `HookParam` 中 `result` 功能增加了泛型匹配,现在你可以使用 `result<T>` 来匹配你的目标方法已知返回值类型了
|
||||
- 方法、构造方法查询功能新增 `emptyParam` 条件,并完善了文档相关需要注意的查询条件误区
|
||||
- 增加了 `type` 中的 `android` 类型
|
||||
|
||||
### 1.0.73 | 2022.04.10
|
||||
|
||||
- 修正几处文档的中文翻译错误,感谢 [WankkoRee](https://github.com/WankkoRee) 的贡献
|
||||
- 修复在某些情况下 `XC_LoadPackage.LoadPackageParam` 内容为空抛出异常的问题,感谢 [Luckyzyx](https://github.com/luckyzyx) 的反馈
|
||||
- 修复一些已知的 BUG,提升 Hook 稳定性
|
||||
|
||||
### 1.0.72 | 2022.04.09
|
||||
|
||||
- 更新 API 文档到新的地址
|
||||
- `PackageParam` 中加入 `appContext` 功能
|
||||
- 修复一些已知的 BUG,提升 Hook 稳定性
|
||||
|
||||
### 1.0.71 | 2022.04.04
|
||||
|
||||
- 修复 VariousClass 无法匹配时会停止 Hook 抛出异常的严重问题
|
||||
|
||||
### 1.0.70 | 2022.04.04
|
||||
|
||||
- 修复 `instanceClass` 在静态实例中调用后报错问题
|
||||
- 在 Hook 过程中加入 `isUseAppClassLoader` 功能,感谢 [WankkoRee](https://github.com/WankkoRee) 的反馈
|
||||
- 加入 `withProcess` 功能,可根据 APP 当前指定进程进行 Hook
|
||||
- 修复查找方法、构造类和变量的严重逻辑错误问题
|
||||
- 修复 Hook 目标类不存在的时候无法忽略异常输出的问题
|
||||
- 修复部分情况下 APP 启动方法装载过快导致 Hook 不能生效的问题
|
||||
- 修复 `allMethods` 未 Hook 到方法时不会抛出异常的问题,感谢 [WankkoRee](https://github.com/WankkoRee) 的反馈
|
||||
- 加入 Hook 状态监听功能,感谢 [WankkoRee](https://github.com/WankkoRee) 的建议
|
||||
- 修改 Xposed 入口注入类的方式,重新声明 API 的定义域
|
||||
- 加入混淆的方法以及变量的查找功能,可使用不同类型筛选 `index` 定位指定的方法和变量,感谢 [WankkoRee](https://github.com/WankkoRee) 提供的思路
|
||||
- 查找方法、变量时允许传入多种类型,例如 `String` 声明的类名和 `VariousClass`
|
||||
- 加入全新的 `current` 功能,可对任意的类构建一个反射方法操作空间,方便地调用和修改其中的方法和变量
|
||||
- 修复了 Hook 过程中的大量 BUG,感谢 [WankkoRee](https://github.com/WankkoRee) 对此项目所做出的贡献
|
||||
|
||||
### 1.0.69 | 2022.03.30
|
||||
|
||||
- 添加并改进一些方法功能的注释
|
||||
- 增加 Demo 中的更多示例 Hook 内容
|
||||
- 修复在一个 Hook 实例中,`allMethods` 多次使用时只有最后一个生效的问题,感谢 [WankkoRee](https://github.com/WankkoRee) 的反馈
|
||||
|
||||
### 1.0.68 | 2022.03.29
|
||||
|
||||
- 增加 Demo 中的新用例和 LSPosed 作用域
|
||||
- 增加 `Member` 查找缓存和查找缓存配置开关
|
||||
- 移除和修改 `MethodFinder`、`FieldFinder` 以及 `HookParam` 相关方法的调用
|
||||
- 增加更多 `Finder` 中的 `cast` 类型并支持 `cast` 为数组
|
||||
- 整体的性能和稳定性提升
|
||||
- 修复上一个版本可能存在的 BUG
|
||||
|
||||
### 1.0.67 | 2022.03.27
|
||||
|
||||
- 增加三个 `Finder` 中的 `modifiers` 功能,可筛选 `static`、`native`、`public`、`abstract` 等诸多描述类型
|
||||
- 增加方法和构造方法查找时可模糊方法参数类型为指定个数进行查找
|
||||
- 增加 `Member` 的 `hasModifiers` 扩展功能
|
||||
- 增加 `MethodFinder` 和 `ConstructorFinder` 中的 `give` 方法,可获得原始类型
|
||||
- 增加 `YukiHookModulePrefs` 中的 `PrefsData` 模板功能
|
||||
- 彻底对方法、构造方法及变量的查找方案进行重构
|
||||
- 优化代码注释,修复了可能产生的 BUG
|
||||
|
||||
### 1.0.66 | 2022.03.25
|
||||
|
||||
- 修复 `MethodFinder` 中的一个严重问题
|
||||
- 增加 `hookParam` 中的 `args` 调用方法
|
||||
- 修复其它可能存在的问题以及修复部分类的注释问题
|
||||
|
||||
### 1.0.65 | 2022.03.25
|
||||
|
||||
- 重新发布版本修复 Maven 仓库因为缓存问题新版本不正确的情况
|
||||
- 增加 `MethodFinder` 与 `FieldFinder` 新的返回值调用方法
|
||||
- 修复可能存在的问题,并修复太极使用过程中可能存在的问题
|
||||
- 修复自动生成 Xposed 入口类可能发生的问题
|
||||
- 增加了 `type` 中的 `android` 类型以及 `java` 类型
|
||||
|
||||
### 1.0.6 | 2022.03.20
|
||||
|
||||
- 修复 `YukiHookModulePrefs` 在使用一次 `direct` 忽略缓存后每次都忽略的 BUG
|
||||
- 增加新的 API,作废了 `isActive` 判断模块激活的传统用法
|
||||
- 修复非 Xposed 环境使用 API 时打印调试日志的问题
|
||||
- 修复查找 `Field` 时的日志输出问题和未拦截的异常问题
|
||||
- 解耦合 `ReflectionUtils` 中的 Xposed API
|
||||
- 增加 `YukiHookModuleStatus` 方法名称的混淆,以精简模块生成的体积
|
||||
- 装载模块自身 Hook 时将不再打印欢迎信息
|
||||
- 修复上一个版本仍然存在的某些 BUG
|
||||
|
||||
### 1.0.55 | 2022.03.18
|
||||
|
||||
- 修正一处注释错误
|
||||
- 临时修复一个 BUG
|
||||
- 增加了 `type` 中的大量 `android` 类型以及少量 `java` 类型
|
||||
- 修复新版与旧版 Kotlin APIs 的兼容性问题
|
||||
|
||||
### 1.0.5 | 2022.03.18
|
||||
|
||||
- 修复旧版本 LSPosed 框架情况下欢迎信息多次打印的问题
|
||||
- 添加 `onInit` 方法来配置 `YukiHookAPI`
|
||||
- 新增 `executorName` 和 `executorVersion` 来获取当前 Hook 框架的名称和版本号
|
||||
- 新增 `by` 方法来设置 Hook 的时机和条件
|
||||
- `YukiHookModulePrefs` 新增可控制的键值缓存,可在宿主运行时模块动态更新数据
|
||||
- 修复了一些可能存在的 BUG
|
||||
|
||||
### 1.0.4 | 2022.03.06
|
||||
|
||||
- 修复 LSPosed 在最新版本中启用“只有模块classloader可以使用Xposed API”选项后找不到 `XposedBridge` 的问题
|
||||
- 添加 `YukiHookAPI` 的常量版本名称和版本号
|
||||
- 新增 `hasField` 方法以及 `isAllowPrintingLogs` 配置参数
|
||||
- 新增 `isDebug` 开启的情况下 API 将自动打印欢迎信息测试模块是否生效
|
||||
|
||||
### 1.0.3 | 2022.03.02
|
||||
|
||||
- 修复一个潜在性的异常未拦截 BUG
|
||||
- 增加 `ignoredError` 功能
|
||||
- 增加了 `type` 中的 `android` 类型
|
||||
- 增加监听 `hook` 后的 `ClassNotFound` 功能
|
||||
|
||||
### 1.0.2 | 2022.02.18
|
||||
|
||||
- 修复 Windows 下无法找到项目路径的问题
|
||||
- 移除部分反射 API,合并至 `BaseFinder` 进行整合
|
||||
- 增加直接使用字符串创建 Hook 的方法
|
||||
|
||||
### 1.0.1 | 2022.02.15
|
||||
|
||||
- `RemedyPlan` 增加 `onFind` 功能
|
||||
- 整合并修改了部分反射 API 代码
|
||||
- 增加了 `type` 中的 `java` 类型
|
||||
- 修复忽略错误在控制台仍然输出的问题
|
||||
|
||||
### 1.0 | 2022.02.14
|
||||
|
||||
- 首个版本提交至 Maven
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](about/future.md)
|
@@ -1,14 +0,0 @@
|
||||
# 联系我们
|
||||
|
||||
> 如在使用中有任何问题,或有任何建设性的建议,都可以联系我们。
|
||||
|
||||
加入我们 [点击加入 Telegram 群组](https://t.me/YukiHookAPI)
|
||||
|
||||
在**酷安**找到我 [@星夜不荟](http://www.coolapk.com/u/876977)
|
||||
|
||||
## 助力维护
|
||||
|
||||
感谢您选择并使用 `YukiHookAPI`,如有代码相关的建议和请求,可在 Github 提交 Pull Request。
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](about/about.md)
|
@@ -1,38 +0,0 @@
|
||||
# 展望未来
|
||||
|
||||
> 未来是美好的,也是不确定的,让我们共同期待 `YukiHookAPI` 在未来的发展空间。
|
||||
|
||||
## 未解决的问题
|
||||
|
||||
> 这里收录了 `YukiHookAPI` 尚未解决的问题。
|
||||
|
||||
### YukiHookModulePrefs
|
||||
|
||||
目前仅限完美支持 LSPosed,其它 Xposed 框架需要降级模块 API。
|
||||
|
||||
可能完全不支持太极,太极在高版本系统上需要更低的 API 才能适配。
|
||||
|
||||
部分 Xposed 模块开发者目前选择 Hook 目标 APP 内置 Sp 存储方案解决模块设置共享问题。
|
||||
|
||||
后期 Android 系统的权限将越来越严格,`selinux` 就是目前面临的一个大问题,有待讨论和研究。
|
||||
|
||||
## 未来的计划
|
||||
|
||||
> 这里收录了 `YukiHookAPI` 可能会在后期添加的功能。
|
||||
|
||||
### 支持独立使用的 Lite 版本
|
||||
|
||||
**计划状态:待定**
|
||||
|
||||
目前 API 只支持通过自动处理程序绑定到 `xposed_init`,若您不喜欢自动处理程序,一定要自己实现模块装载入口,未来会按照需求人数推出仅有 API 功能的 Lite 版本。
|
||||
|
||||
API 已经提供了 Xposed 原生 API 监听接口,你可以 [在这里](config/xposed-using?id=原生-xposed-api-事件) 找到或查看 Demo 的实现方法。
|
||||
|
||||
### 支持更多 Hook Framework
|
||||
|
||||
作为 API 来讲,目前仅仅对接 `XposedBridge` 作为兼容层,还是有一定的局限性。
|
||||
|
||||
大部分 `inline hook` 没有 `Java` 兼容层,后期可能会考虑 `native hook` 的 `Java` 兼容层适配。
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](about/contacts.md)
|
@@ -1,84 +0,0 @@
|
||||
# Public API
|
||||
|
||||
> 以下内容为公开的 API 接口,在版本变更时将同步至最新。
|
||||
|
||||
[filename](public/YukiHookAPI.md ':include')
|
||||
|
||||
[filename](public/PackageParam.md ':include')
|
||||
|
||||
[filename](public/HookParam.md ':include')
|
||||
|
||||
[filename](public/InjectYukiHookWithXposed.md ':include')
|
||||
|
||||
[filename](public/IYukiHookXposedInit.md ':include')
|
||||
|
||||
[filename](public/YukiHookModulePrefs.md ':include')
|
||||
|
||||
[filename](public/ModulePreferenceFragment.md ':include')
|
||||
|
||||
[filename](public/PrefsData.md ':include')
|
||||
|
||||
[filename](public/YukiHookDataChannel.md ':include')
|
||||
|
||||
[filename](public/ChannelData.md ':include')
|
||||
|
||||
[filename](public/ModuleApplication.md ':include')
|
||||
|
||||
[filename](public/ModuleAppActivity.md ':include')
|
||||
|
||||
[filename](public/ModuleAppCompatActivity.md ':include')
|
||||
|
||||
[filename](public/ModuleContextThemeWrapper.md ':include')
|
||||
|
||||
[filename](public/YukiModuleResources.md ':include')
|
||||
|
||||
[filename](public/YukiResources.md ':include')
|
||||
|
||||
[filename](public/YukiResForwarder.md ':include')
|
||||
|
||||
[filename](public/YukiXposedEvent.md ':include')
|
||||
|
||||
[filename](public/ComponentTypeFactory.md ':include')
|
||||
|
||||
[filename](public/GraphicsTypeFactory.md ':include')
|
||||
|
||||
[filename](public/ViewTypeFactory.md ':include')
|
||||
|
||||
[filename](public/VariableTypeFactory.md ':include')
|
||||
|
||||
[filename](public/DefinedTypeFactory.md ':include')
|
||||
|
||||
[filename](public/LoggerFactory.md ':include')
|
||||
|
||||
[filename](public/ReflectionFactory.md ':include')
|
||||
|
||||
[filename](public/YukiHookFactory.md ':include')
|
||||
|
||||
[filename](public/YukiBaseHooker.md ':include')
|
||||
|
||||
[filename](public/YukiMemberHookCreator.md ':include')
|
||||
|
||||
[filename](public/YukiResourcesHookCreator.md ':include')
|
||||
|
||||
[filename](public/MethodFinder.md ':include')
|
||||
|
||||
[filename](public/ConstructorFinder.md ':include')
|
||||
|
||||
[filename](public/FieldFinder.md ':include')
|
||||
|
||||
[filename](public/BaseFinder-IndexTypeCondition.md ':include')
|
||||
|
||||
[filename](public/ModifierRules.md ':include')
|
||||
|
||||
[filename](public/NameRules.md ':include')
|
||||
|
||||
[filename](public/HookClass.md ':include')
|
||||
|
||||
[filename](public/VariousClass.md ':include')
|
||||
|
||||
[filename](public/CurrentClass.md ':include')
|
||||
|
||||
[filename](public/HookResources.md ':include')
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](about/changelog.md)
|
@@ -1,52 +0,0 @@
|
||||
# 文档介绍
|
||||
|
||||
> 这里的文档将同步最新 API 版本的相关用法,请保持 `YukiHookAPI` 为最新版本以使用最新版本的功能。
|
||||
|
||||
## 功能描述说明
|
||||
|
||||
> 功能描述主要介绍当前 API 的相关用法和用途。
|
||||
|
||||
## 功能示例说明
|
||||
|
||||
> 功能示例主要展示了当前 API 的基本用法示例,可供参考。
|
||||
|
||||
## 变更记录说明
|
||||
|
||||
首个版本的功能将标记为 `v$version` `添加`;
|
||||
|
||||
后期新增加的功能将标记为 `v$version` `新增`;
|
||||
|
||||
后期修改的功能将被追加为 `v$version` `修改`;
|
||||
|
||||
后期被作废的功能将标记为 `v$version` `作废` 并会标注删除线;
|
||||
|
||||
后期被删除的功能将标记为 `v$version` `移除` 并会标注删除线。
|
||||
|
||||
## 相关符号说明
|
||||
|
||||
- *kt* Kotlin Static File
|
||||
|
||||
- *annotation* 注解
|
||||
|
||||
- *interface* 接口
|
||||
|
||||
- *object* 类 (单例)
|
||||
|
||||
- *class* 类
|
||||
|
||||
- *field* 变量或 `get`、`set` 方法或只读的 `get` 方法
|
||||
|
||||
- *method* 方法
|
||||
|
||||
- *enum* Enum 常量
|
||||
|
||||
- *ext-field* 扩展的变量 (全局)
|
||||
|
||||
- *ext-method* 扩展的方法 (全局)
|
||||
|
||||
- *i-ext-field* 扩展的变量 (调用域限制)
|
||||
|
||||
- *i-ext-method* 扩展的方法 (调用域限制)
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](api/document)
|
@@ -1,101 +0,0 @@
|
||||
## BaseFinder.IndexTypeCondition *- class*
|
||||
|
||||
```kotlin
|
||||
inner class IndexTypeCondition internal constructor(private val type: IndexConfigType)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 字节码下标筛选实现类。
|
||||
|
||||
### index *- method*
|
||||
|
||||
```kotlin
|
||||
fun index(num: Int)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置下标。
|
||||
|
||||
若 `index` 小于零则为倒序,此时可以使用 `IndexTypeConditionSort.reverse` 方法实现。
|
||||
|
||||
可使用 `IndexTypeConditionSort.first` 和 `IndexTypeConditionSort.last` 设置首位和末位筛选条件。
|
||||
|
||||
### index *- method*
|
||||
|
||||
```kotlin
|
||||
fun index(): IndexTypeConditionSort
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到下标。
|
||||
|
||||
### IndexTypeConditionSort *- class*
|
||||
|
||||
```kotlin
|
||||
inner class IndexTypeConditionSort internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 字节码下标排序实现类。
|
||||
|
||||
#### first *- method*
|
||||
|
||||
```kotlin
|
||||
fun first()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置满足条件的第一个。
|
||||
|
||||
#### last *- method*
|
||||
|
||||
```kotlin
|
||||
fun last()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置满足条件的最后一个。
|
||||
|
||||
#### reverse *- method*
|
||||
|
||||
```kotlin
|
||||
fun reverse(num: Int)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置倒序下标。
|
@@ -1,64 +0,0 @@
|
||||
## ChannelData *- class*
|
||||
|
||||
```kotlin
|
||||
data class ChannelData<T>(var key: String, var value: T?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 数据通讯桥键值构造类。
|
||||
|
||||
这个类是对 `YukiHookDataChannel` 的一个扩展用法。
|
||||
|
||||
**功能示例**
|
||||
|
||||
建立一个模板类定义模块与宿主需要发送的键值数据。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
object DataConst {
|
||||
|
||||
val TEST_KV_DATA_1 = ChannelData("test_data_1", "defalut value")
|
||||
val TEST_KV_DATA_2 = ChannelData("test_data_2", 0)
|
||||
}
|
||||
```
|
||||
|
||||
键值数据定义后,你就可以方便地在模块和宿主中调用所需要发送的数据。
|
||||
|
||||
> 模块示例如下
|
||||
|
||||
```kotlin
|
||||
// 从指定包名的宿主获取
|
||||
dataChannel(packageName = "com.example.demo").wait(DataConst.TEST_KV_DATA_1) { value ->
|
||||
// Your code here.
|
||||
}
|
||||
// 发送给指定包名的宿主 - 未填写 value 时将使用模板提供的默认值
|
||||
dataChannel(packageName = "com.example.demo").put(DataConst.TEST_KV_DATA_1, value = "sending value")
|
||||
```
|
||||
|
||||
> 宿主示例如下
|
||||
|
||||
```kotlin
|
||||
// 从模块获取
|
||||
dataChannel.wait(DataConst.TEST_KV_DATA_1) { value ->
|
||||
// Your code here.
|
||||
}
|
||||
// 发送给模块 - 未填写 value 时将使用模板提供的默认值
|
||||
dataChannel.put(DataConst.TEST_KV_DATA_1, value = "sending value")
|
||||
```
|
||||
|
||||
你依然可以不使用模板定义的默认值,随时修改你的默认值。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 获取 - 此时 value 取到的默认值将会是 2 - 并不是模板提供的 0
|
||||
dataChannel.wait(DataConst.TEST_KV_DATA_2, value = 2) { value ->
|
||||
// Your code here.
|
||||
}
|
||||
```
|
@@ -1,11 +0,0 @@
|
||||
## ComponentTypeFactory *- kt*
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是一个预置 Hook 类型的常量类,主要为 `Android` 相关组件的 `Class` 内容,跟随版本更新会逐一进行增加。
|
||||
|
||||
详情可 [点击这里](https://github.com/fankes/YukiHookAPI/blob/master/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/type/android/ComponentTypeFactory.kt) 进行查看。
|
@@ -1,708 +0,0 @@
|
||||
## ConstructorFinder *- class*
|
||||
|
||||
```kotlin
|
||||
class ConstructorFinder internal constructor(override val hookInstance: YukiMemberHookCreator.MemberHookCreator?, override val classSet: Class<*>) : MemberBaseFinder
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.2` `修改`
|
||||
|
||||
合并到 `BaseFinder`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
合并到 `MemberBaseFinder`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `Constructor` 查找类。
|
||||
|
||||
可通过指定类型查找指定 `Constructor` 或一组 `Constructor`。
|
||||
|
||||
### paramCount *- field*
|
||||
|
||||
```kotlin
|
||||
var paramCount: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.67` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Constructor` 参数个数。
|
||||
|
||||
你可以不使用 `param` 指定参数类型而是仅使用此变量指定参数个数。
|
||||
|
||||
若参数个数小于零则忽略并使用 `param`。
|
||||
|
||||
### modifiers *- method*
|
||||
|
||||
```kotlin
|
||||
fun modifiers(conditions: ModifierConditions): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.67` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
合并到 `ModifierConditions`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Constructor` 标识符筛选条件。
|
||||
|
||||
可不设置筛选条件,默认模糊查找并取第一个匹配的 `Constructor`。
|
||||
|
||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||
|
||||
### emptyParam *- method*
|
||||
|
||||
```kotlin
|
||||
fun emptyParam(): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.75` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Constructor` 空参数、无参数。
|
||||
|
||||
### param *- method*
|
||||
|
||||
```kotlin
|
||||
fun param(vararg paramType: Any): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Constructor` 参数。
|
||||
|
||||
如果同时使用了 `paramCount` 则 `paramType` 的数量必须与 `paramCount` 完全匹配。
|
||||
|
||||
如果 `Constructor` 中存在一些无意义又很长的类型,你可以使用 `VagueType` 来替代它。
|
||||
|
||||
!> 无参 `Constructor` 请使用 `emptyParam` 设置查询条件。
|
||||
|
||||
!> 有参 `Constructor` 必须使用此方法设定参数或使用 `paramCount` 指定个数。
|
||||
|
||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||
|
||||
### paramCount *- method*
|
||||
|
||||
```kotlin
|
||||
fun paramCount(num: Int): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Constructor` 参数个数。
|
||||
|
||||
你可以不使用 `param` 指定参数类型而是仅使用此方法指定参数个数。
|
||||
|
||||
若参数个数小于零则忽略并使用 `param`。
|
||||
|
||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||
|
||||
### paramCount *- method*
|
||||
|
||||
```kotlin
|
||||
fun paramCount(numRange: IntRange): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Constructor` 参数个数范围。
|
||||
|
||||
你可以不使用 `param` 指定参数类型而是仅使用此方法指定参数个数范围。
|
||||
|
||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||
|
||||
### paramCount *- method*
|
||||
|
||||
```kotlin
|
||||
fun paramCount(conditions: CountConditions): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Constructor` 参数个数条件。
|
||||
|
||||
你可以不使用 `param` 指定参数类型而是仅使用此方法指定参数个数条件。
|
||||
|
||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||
|
||||
### superClass *- method*
|
||||
|
||||
```kotlin
|
||||
fun superClass(isOnlySuperClass: Boolean)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置在 `classSet` 的所有父类中查找当前 `Constructor`。
|
||||
|
||||
!> 若当前 `classSet` 的父类较多可能会耗时,API 会自动循环到父类继承是 `Any` 前的最后一个类。
|
||||
|
||||
### RemedyPlan *- class*
|
||||
|
||||
```kotlin
|
||||
inner class RemedyPlan internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `Constructor` 重查找实现类,可累计失败次数直到查找成功。
|
||||
|
||||
#### constructor *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun constructor(initiate: ConstructorConditions)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建需要重新查找的 `Constructor`。
|
||||
|
||||
你可以添加多个备选 `Constructor`,直到成功为止,若最后依然失败,将停止查找并输出错误日志。
|
||||
|
||||
#### Result *- class*
|
||||
|
||||
```kotlin
|
||||
inner class Result internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.1` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `RemedyPlan` 结果实现类。
|
||||
|
||||
##### onFind *- method*
|
||||
|
||||
```kotlin
|
||||
fun onFind(initiate: HashSet<Constructor<*>>.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.1` `新增`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
`initiate` 参数 `Constructor` 变为 `HashSet<Constructor>`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 当在 `RemedyPlan` 中找到结果时。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以方便地对重查找的 `Constructor` 实现 `onFind` 方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
constructor {
|
||||
// Your code here.
|
||||
}.onFind {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
### Process *- class*
|
||||
|
||||
```kotlin
|
||||
inner class Process internal constructor(internal val isNoSuch: Boolean, internal val throwable: Throwable?) : BaseResult
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `Constructor` 查找结果处理类,为 `hookInstance` 提供。
|
||||
|
||||
#### result *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun result(initiate: Process.() -> Unit): Process
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建监听结果事件方法体。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以使用 `lambda` 形式创建 `Result` 类。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
constructor {
|
||||
// Your code here.
|
||||
}.result {
|
||||
all()
|
||||
remedys {}
|
||||
onNoSuchConstructor {}
|
||||
}
|
||||
```
|
||||
|
||||
#### all *- method*
|
||||
|
||||
```kotlin
|
||||
fun all(): Process
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置全部查询条件匹配的多个 `Constructor` 实例结果到 `hookInstance`。
|
||||
|
||||
#### remedys *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun remedys(initiate: RemedyPlan.() -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建 `Constructor` 重查找功能。
|
||||
|
||||
**功能示例**
|
||||
|
||||
当你遇到一种 `Constructor` 可能存在不同形式的存在时,可以使用 `RemedyPlan` 重新查找它,而没有必要使用 `onNoSuchConstructor` 捕获异常二次查找 `Constructor`。
|
||||
|
||||
若第一次查找失败了,你还可以在这里继续添加此方法体直到成功为止。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
constructor {
|
||||
// Your code here.
|
||||
}.remedys {
|
||||
constructor {
|
||||
// Your code here.
|
||||
}
|
||||
constructor {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### onNoSuchConstructor *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun onNoSuchConstructor(result: (Throwable) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听找不到 `Constructor` 时。
|
||||
|
||||
只会返回第一次的错误信息,不会返回 `RemedyPlan` 的错误信息。
|
||||
|
||||
### Result *- class*
|
||||
|
||||
```kotlin
|
||||
inner class Result internal constructor(internal val isNoSuch: Boolean, internal val throwable: Throwable?) : BaseResult
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
继承到接口 `BaseResult`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `Constructor` 查找结果实现类。
|
||||
|
||||
#### result *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun result(initiate: Result.() -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建监听结果事件方法体。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以使用 `lambda` 形式创建 `Result` 类。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
constructor {
|
||||
// Your code here.
|
||||
}.result {
|
||||
get().call()
|
||||
all()
|
||||
remedys {}
|
||||
onNoSuchConstructor {}
|
||||
}
|
||||
```
|
||||
|
||||
#### get *- method*
|
||||
|
||||
```kotlin
|
||||
fun get(): Instance
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.2` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得 `Constructor` 实例处理类。
|
||||
|
||||
若有多个 `Constructor` 结果只会返回第一个。
|
||||
|
||||
!> 若你设置了 `remedys` 请使用 `wait` 回调结果方法。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以通过获得方法所在实例来执行 `Constructor` 创建新的实例对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
constructor {
|
||||
// Your code here.
|
||||
}.get().call()
|
||||
```
|
||||
|
||||
你可以 `cast` 构造方法为指定类型的实例对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
constructor {
|
||||
// Your code here.
|
||||
}.get().newInstance<TestClass>()
|
||||
```
|
||||
|
||||
!> 若构造方法含有参数则后方参数必填。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
constructor {
|
||||
// Your code here.
|
||||
}.get().newInstance<TestClass>("param1", "param2")
|
||||
```
|
||||
|
||||
#### all *- method*
|
||||
|
||||
```kotlin
|
||||
fun all(): ArrayList<Instance>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得 `Constructor` 实例处理类数组。
|
||||
|
||||
返回全部查询条件匹配的多个 `Constructor` 实例结果。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以通过此方法来获得当前条件结果中匹配的全部 `Constructor`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
constructor {
|
||||
// Your code here.
|
||||
}.all().forEach { instance ->
|
||||
instance.call(...)
|
||||
}
|
||||
```
|
||||
|
||||
#### give *- method*
|
||||
|
||||
```kotlin
|
||||
fun give(): Constructor<*>?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.67` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到 `Constructor` 本身。
|
||||
|
||||
若有多个 `Constructor` 结果只会返回第一个。
|
||||
|
||||
在查询条件找不到任何结果的时候将返回 `null`。
|
||||
|
||||
#### giveAll *- method*
|
||||
|
||||
```kotlin
|
||||
fun giveAll(): HashSet<Constructor<*>>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到 `Constructor` 本身数组。
|
||||
|
||||
返回全部查询条件匹配的多个 `Constructor` 实例。
|
||||
|
||||
在查询条件找不到任何结果的时候将返回空的 `HashSet`。
|
||||
|
||||
#### wait *- method*
|
||||
|
||||
```kotlin
|
||||
fun wait(initiate: Instance.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.2` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得 `Constructor` 实例处理类,配合 `RemedyPlan` 使用。
|
||||
|
||||
若有多个 `Constructor` 结果只会返回第一个。
|
||||
|
||||
!> 若你设置了 `remedys` 必须使用此方法才能获得结果。
|
||||
|
||||
!> 若你没有设置 `remedys` 此方法将不会被回调。
|
||||
|
||||
#### waitAll *- method*
|
||||
|
||||
```kotlin
|
||||
fun waitAll(initiate: ArrayList<Instance>.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得 `Constructor` 实例处理类数组,配合 `RemedyPlan` 使用。
|
||||
|
||||
返回全部查询条件匹配的多个 `Constructor` 实例结果。
|
||||
|
||||
!> 若你设置了 `remedys` 必须使用此方法才能获得结果。
|
||||
|
||||
!> 若你没有设置 `remedys` 此方法将不会被回调。
|
||||
|
||||
#### remedys *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun remedys(initiate: RemedyPlan.() -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建 `Constructor` 重查找功能。
|
||||
|
||||
**功能示例**
|
||||
|
||||
当你遇到一种 `Constructor` 可能存在不同形式的存在时,可以使用 `RemedyPlan` 重新查找它,而没有必要使用 `onNoSuchConstructor` 捕获异常二次查找 `Constructor`。
|
||||
|
||||
若第一次查找失败了,你还可以在这里继续添加此方法体直到成功为止。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
constructor {
|
||||
// Your code here.
|
||||
}.remedys {
|
||||
constructor {
|
||||
// Your code here.
|
||||
}
|
||||
constructor {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### onNoSuchConstructor *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun onNoSuchConstructor(result: (Throwable) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听找不到 `Constructor` 时。
|
||||
|
||||
只会返回第一次的错误信息,不会返回 `RemedyPlan` 的错误信息。
|
||||
|
||||
#### ignored *- method*
|
||||
|
||||
```kotlin
|
||||
fun ignored(): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 忽略异常并停止打印任何错误日志。
|
||||
|
||||
若 `isNotIgnoredHookingFailure` 为 `false` 则自动忽略。
|
||||
|
||||
!> 此时若要监听异常结果,你需要手动实现 `onNoSuchConstructor` 方法。
|
||||
|
||||
#### ~~ignoredError *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.3` `新增`
|
||||
|
||||
`v1.1.0` `作废`
|
||||
|
||||
请转移到新方法 `ignored()`
|
||||
|
||||
#### Instance *- class*
|
||||
|
||||
```kotlin
|
||||
inner class Instance internal constructor(private val constructor: Constructor<*>?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.2` `新增`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
新增 `constructor` 参数
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `Constructor` 实例处理类。
|
||||
|
||||
##### call *- method*
|
||||
|
||||
```kotlin
|
||||
fun call(vararg param: Any?): Any?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.2` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 执行 `Constructor` 创建目标实例,不指定目标实例类型。
|
||||
|
||||
##### newInstance *- method*
|
||||
|
||||
```kotlin
|
||||
fun <T> newInstance(vararg param: Any?): T?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.2` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 执行 `Constructor` 创建目标实例 ,指定 `T` 目标实例类型。
|
@@ -1,157 +0,0 @@
|
||||
## CurrentClass *- class*
|
||||
|
||||
```kotlin
|
||||
class CurrentClass internal constructor(internal val classSet: Class<*>, internal val instance: Any)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
调整了构造方法的参数名称
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 当前实例的类操作对象。
|
||||
|
||||
### name *- field*
|
||||
|
||||
```kotlin
|
||||
val name: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前 `classSet` 的 `Class.getName`。
|
||||
|
||||
### simpleName *- field*
|
||||
|
||||
```kotlin
|
||||
val simpleName: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前 `classSet` 的 `Class.getSimpleName`。
|
||||
|
||||
### superClass *- method*
|
||||
|
||||
```kotlin
|
||||
fun superClass(): SuperClass
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 调用父类实例。
|
||||
|
||||
### field *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun field(initiate: FieldConditions): FieldFinder.Result.Instance
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 调用当前实例中的变量。
|
||||
|
||||
### method *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun method(initiate: MethodConditions): MethodFinder.Result.Instance
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 调用当前实例中的方法。
|
||||
|
||||
### SuperClass *- class*
|
||||
|
||||
```kotlin
|
||||
inner class SuperClass internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 当前类的父类实例的类操作对象。
|
||||
|
||||
#### name *- field*
|
||||
|
||||
```kotlin
|
||||
val name: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前 `classSet` 中父类的 `Class.getName`。
|
||||
|
||||
#### simpleName *- field*
|
||||
|
||||
```kotlin
|
||||
val simpleName: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前 `classSet` 中父类的 `Class.getSimpleName`。
|
||||
|
||||
#### field *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun field(initiate: FieldConditions): FieldFinder.Result.Instance
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 调用父类实例中的变量。
|
||||
|
||||
#### method *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun method(initiate: MethodConditions): MethodFinder.Result.Instance
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 调用父类实例中的方法。
|
@@ -1,23 +0,0 @@
|
||||
## DefinedTypeFactory *- kt*
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是一个内部类型的定义常量类,主要用于反射 API 相关用法的延伸。
|
||||
|
||||
### VagueType *- field*
|
||||
|
||||
```kotlin
|
||||
val VagueType: Class<*>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到模糊类型。
|
@@ -1,871 +0,0 @@
|
||||
## FieldFinder *- class*
|
||||
|
||||
```kotlin
|
||||
class FieldFinder internal constructor(override val hookInstance: YukiMemberHookCreator.MemberHookCreator?, override val classSet: Class<*>?) : MemberBaseFinder
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.2` `修改`
|
||||
|
||||
合并到 `BaseFinder`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
合并到 `MemberBaseFinder`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `Field` 查找类。
|
||||
|
||||
可通过指定类型查找指定 `Field` 或一组 `Field`。
|
||||
|
||||
### ~~classSet *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.2` `移除`
|
||||
|
||||
### name *- field*
|
||||
|
||||
```kotlin
|
||||
var name: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.70` `修改`
|
||||
|
||||
允许不填写名称
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Field` 名称。
|
||||
|
||||
!> 若不填写名称则必须存在一个其它条件。
|
||||
|
||||
### type *- field*
|
||||
|
||||
```kotlin
|
||||
var type: Any?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Field` 类型。
|
||||
|
||||
可不填写类型。
|
||||
|
||||
### modifiers *- method*
|
||||
|
||||
```kotlin
|
||||
fun modifiers(conditions: ModifierConditions): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.67` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
合并到 `ModifierConditions`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Field` 标识符筛选条件。
|
||||
|
||||
可不设置筛选条件。
|
||||
|
||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||
|
||||
### order *- method*
|
||||
|
||||
```kotlin
|
||||
fun order(): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 顺序筛选字节码的下标。
|
||||
|
||||
### name *- method*
|
||||
|
||||
```kotlin
|
||||
fun name(value: String): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Field` 名称。
|
||||
|
||||
!> 若不填写名称则必须存在一个其它条件。
|
||||
|
||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||
|
||||
### name *- method*
|
||||
|
||||
```kotlin
|
||||
fun name(conditions: NameConditions): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
合并到 `NameConditions`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Field` 名称条件。
|
||||
|
||||
!> 若不填写名称则必须存在一个其它条件。
|
||||
|
||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||
|
||||
### type *- method*
|
||||
|
||||
```kotlin
|
||||
fun type(value: Any): IndexTypeCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 `Field` 类型。
|
||||
|
||||
!> 可不填写类型。
|
||||
|
||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||
|
||||
### superClass *- method*
|
||||
|
||||
```kotlin
|
||||
fun superClass(isOnlySuperClass: Boolean)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置在 `classSet` 的所有父类中查找当前 `Field`。
|
||||
|
||||
!> 若当前 `classSet` 的父类较多可能会耗时,API 会自动循环到父类继承是 `Any` 前的最后一个类。
|
||||
|
||||
### RemedyPlan *- class*
|
||||
|
||||
```kotlin
|
||||
inner class RemedyPlan internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `Field` 重查找实现类,可累计失败次数直到查找成功。
|
||||
|
||||
#### field *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun field(initiate: FieldConditions): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建需要重新查找的 `Field`。
|
||||
|
||||
你可以添加多个备选 `Field`,直到成功为止,若最后依然失败,将停止查找并输出错误日志。
|
||||
|
||||
#### Result *- class*
|
||||
|
||||
```kotlin
|
||||
inner class Result internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `RemedyPlan` 结果实现类。
|
||||
|
||||
##### onFind *- method*
|
||||
|
||||
```kotlin
|
||||
fun onFind(initiate: HashSet<Field>.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 当在 `RemedyPlan` 中找到结果时。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以方便地对重查找的 `Field` 实现 `onFind` 方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
field {
|
||||
// Your code here.
|
||||
}.onFind {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
### Result *- class*
|
||||
|
||||
```kotlin
|
||||
inner class Result internal constructor(internal val isNoSuch: Boolean, private val throwable: Throwable?) : BaseResult
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
继承到接口 `BaseResult`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `Field` 查找结果实现类。
|
||||
|
||||
#### result *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun result(initiate: Result.() -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建监听结果事件方法体。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以使用 `lambda` 形式创建 `Result` 类。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
field {
|
||||
// Your code here.
|
||||
}.result {
|
||||
get(instance).set("something")
|
||||
get(instance).string()
|
||||
get(instance).cast<CustomClass>()
|
||||
get().boolean()
|
||||
all(instance)
|
||||
give()
|
||||
giveAll()
|
||||
onNoSuchField {}
|
||||
}
|
||||
```
|
||||
|
||||
#### get *- method*
|
||||
|
||||
```kotlin
|
||||
fun get(instance: Any?): Instance
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得 `Field` 实例处理类。
|
||||
|
||||
若有多个 `Field` 结果只会返回第一个。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以轻松地得到 `Field` 的实例以及使用它进行设置实例。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
field {
|
||||
// Your code here.
|
||||
}.get(instance).set("something")
|
||||
```
|
||||
|
||||
如果你取到的是静态 `Field`,可以不需要设置实例。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
field {
|
||||
// Your code here.
|
||||
}.get().set("something")
|
||||
```
|
||||
|
||||
#### all *- method*
|
||||
|
||||
```kotlin
|
||||
fun all(instance: Any?): ArrayList<Instance>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得 `Field` 实例处理类数组。
|
||||
|
||||
返回全部查询条件匹配的多个 `Field` 实例结果。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以通过此方法来获得当前条件结果中匹配的全部 `Field`,其 `Field` 所在实例用法与 `get` 相同。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
field {
|
||||
// Your code here.
|
||||
}.all(instance).forEach { instance ->
|
||||
instance.self
|
||||
}
|
||||
```
|
||||
|
||||
#### give *- method*
|
||||
|
||||
```kotlin
|
||||
fun give(): Field?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到 `Field` 本身。
|
||||
|
||||
若有多个 Field 结果只会返回第一个。
|
||||
|
||||
在查询条件找不到任何结果的时候将返回 `null`。
|
||||
|
||||
#### giveAll *- method*
|
||||
|
||||
```kotlin
|
||||
fun giveAll(): HashSet<Field>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到 `Field` 本身数组。
|
||||
|
||||
返回全部查询条件匹配的多个 `Field` 实例。
|
||||
|
||||
在查询条件找不到任何结果的时候将返回空的 `HashSet`。
|
||||
|
||||
#### wait *- method*
|
||||
|
||||
```kotlin
|
||||
fun wait(instance: Any?, initiate: Instance.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得 `Field` 实例处理类,配合 `RemedyPlan` 使用。
|
||||
|
||||
若有多个 `Field` 结果只会返回第一个。
|
||||
|
||||
!> 若你设置了 `remedys` 必须使用此方法才能获得结果。
|
||||
|
||||
!> 若你没有设置 `remedys` 此方法将不会被回调。
|
||||
|
||||
#### waitAll *- method*
|
||||
|
||||
```kotlin
|
||||
fun waitAll(instance: Any?, initiate: ArrayList<Instance>.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得 `Field` 实例处理类数组,配合 `RemedyPlan` 使用。
|
||||
|
||||
返回全部查询条件匹配的多个 `Field` 实例结果。
|
||||
|
||||
!> 若你设置了 `remedys` 必须使用此方法才能获得结果。
|
||||
|
||||
!> 若你没有设置 `remedys` 此方法将不会被回调。
|
||||
|
||||
#### remedys *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun remedys(initiate: RemedyPlan.() -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建 `Field` 重查找功能。
|
||||
|
||||
**功能示例**
|
||||
|
||||
当你遇到一种 `Field` 可能存在不同形式的存在时,可以使用 `RemedyPlan` 重新查找它,而没有必要使用 `onNoSuchField` 捕获异常二次查找 `Field`。
|
||||
|
||||
若第一次查找失败了,你还可以在这里继续添加此方法体直到成功为止。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
field {
|
||||
// Your code here.
|
||||
}.remedys {
|
||||
field {
|
||||
// Your code here.
|
||||
}
|
||||
field {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### onNoSuchField *- method*
|
||||
|
||||
```kotlin
|
||||
fun onNoSuchField(result: (Throwable) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听找不到 `Field` 时。
|
||||
|
||||
#### ignored *- method*
|
||||
|
||||
```kotlin
|
||||
fun ignored(): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 忽略异常并停止打印任何错误日志。
|
||||
|
||||
若 `isNotIgnoredHookingFailure` 为 `false` 则自动忽略。
|
||||
|
||||
!> 此时若要监听异常结果,你需要手动实现 `onNoSuchField` 方法。
|
||||
|
||||
#### ~~ignoredError *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.3` `新增`
|
||||
|
||||
`v1.1.0` `作废`
|
||||
|
||||
请转移到新方法 `ignored()`
|
||||
|
||||
#### Instance *- class*
|
||||
|
||||
```kotlin
|
||||
inner class Instance internal constructor(private val instance: Any?, private val field: Field?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
新增 `field` 参数
|
||||
|
||||
不再对外公开 `self` 参数
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `Field` 实例变量处理类。
|
||||
|
||||
##### ~~self *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `移除`
|
||||
|
||||
请直接使用 `any` 方法得到 `Field` 自身的实例化对象
|
||||
|
||||
##### current *- method*
|
||||
|
||||
```kotlin
|
||||
fun current(ignored: Boolean): CurrentClass?
|
||||
```
|
||||
|
||||
```kotlin
|
||||
inline fun current(ignored: Boolean, initiate: CurrentClass.() -> Unit): Any?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前 `Field` 自身 `self` 实例的类操作对象 `CurrentClass`。
|
||||
|
||||
##### cast *- method*
|
||||
|
||||
```kotlin
|
||||
fun <T> cast(): T?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`of`~~ 为 `cast`
|
||||
|
||||
移动方法到 `Instance`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` 实例。
|
||||
|
||||
##### byte *- method*
|
||||
|
||||
```kotlin
|
||||
fun byte(): Byte?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.68` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` Byte 实例。
|
||||
|
||||
##### int *- method*
|
||||
|
||||
```kotlin
|
||||
fun int(): Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.65` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofInt`~~ 为 `int`
|
||||
|
||||
移动方法到 `Instance`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` Int 实例。
|
||||
|
||||
##### long *- method*
|
||||
|
||||
```kotlin
|
||||
fun long(): Long
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.65` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofLong`~~ 为 `long`
|
||||
|
||||
移动方法到 `Instance`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` Long 实例。
|
||||
|
||||
##### short *- method*
|
||||
|
||||
```kotlin
|
||||
fun short(): Short
|
||||
```
|
||||
**变更记录**
|
||||
|
||||
`v1.0.65` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofShort`~~ 为 `short`
|
||||
|
||||
移动方法到 `Instance`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` Short 实例。
|
||||
|
||||
##### double *- method*
|
||||
|
||||
```kotlin
|
||||
fun double(): Double
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.65` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofDouble`~~ 为 `double`
|
||||
|
||||
移动方法到 `Instance`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` Double 实例。
|
||||
|
||||
##### float *- method*
|
||||
|
||||
```kotlin
|
||||
fun float(): Float
|
||||
```
|
||||
**变更记录**
|
||||
|
||||
`v1.0.65` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofFloat`~~ 为 `float`
|
||||
|
||||
移动方法到 `Instance`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` Float 实例。
|
||||
|
||||
##### string *- method*
|
||||
|
||||
```kotlin
|
||||
fun string(): String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.65` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofString`~~ 为 `string`
|
||||
|
||||
移动方法到 `Instance`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` String 实例。
|
||||
|
||||
##### char *- method*
|
||||
|
||||
```kotlin
|
||||
fun char(): Char
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.68` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` Char 实例。
|
||||
|
||||
##### boolean *- method*
|
||||
|
||||
```kotlin
|
||||
fun boolean(): Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.65` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofBoolean`~~ 为 `boolean`
|
||||
|
||||
移动方法到 `Instance`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` Boolean 实例。
|
||||
|
||||
##### any *- method*
|
||||
|
||||
```kotlin
|
||||
fun any(): Any?
|
||||
```
|
||||
**变更记录**
|
||||
|
||||
`v1.0.65` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofAny`~~ 为 `any`
|
||||
|
||||
移动方法到 `Instance`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` Any 实例。
|
||||
|
||||
##### array *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T> array(): Array<T>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.68` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` Array 实例。
|
||||
|
||||
##### list *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T> list(): List<T>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.68` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前 `Field` List 实例。
|
||||
|
||||
##### set *- method*
|
||||
|
||||
```kotlin
|
||||
fun set(any: Any?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置当前 `Field` 实例。
|
||||
|
||||
##### setTrue *- method*
|
||||
|
||||
```kotlin
|
||||
fun setTrue()
|
||||
```
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置当前 `Field` 实例为 `true`。
|
||||
|
||||
!> 请确保实例对象类型为 `Boolean`。
|
||||
|
||||
##### setFalse *- method*
|
||||
|
||||
```kotlin
|
||||
fun setFalse()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置当前 `Field` 实例为 `false`。
|
||||
|
||||
!> 请确保实例对象类型为 `Boolean`。
|
||||
|
||||
##### setNull *- method*
|
||||
|
||||
```kotlin
|
||||
fun setNull()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置当前 `Field` 实例为 `null`。
|
@@ -1,11 +0,0 @@
|
||||
## GraphicsTypeFactory *- kt*
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是一个预置 Hook 类型的常量类,主要为 `Android` 相关 `Graphics` 的 `Class` 内容,跟随版本更新会逐一进行增加。
|
||||
|
||||
详情可 [点击这里](https://github.com/fankes/YukiHookAPI/blob/master/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/type/android/GraphicsTypeFactory.kt) 进行查看。
|
@@ -1,19 +0,0 @@
|
||||
## HookClass *- class*
|
||||
|
||||
```kotlin
|
||||
class HookClass internal constructor(internal var instance: Class<*>?, internal var name: String, internal var throwable: Throwable?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
`HookClass` 相关功能不再对外开放
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建一个当前 Hook 的 `Class` 接管类。
|
||||
|
||||
`instance` 为实例,`name` 为实例完整包名,`throwable` 为找不到实例的时候抛出的异常。
|
@@ -1,831 +0,0 @@
|
||||
## HookParam *- class*
|
||||
|
||||
```kotlin
|
||||
class HookParam internal constructor(private val creatorInstance: YukiMemberHookCreator, private var param: YukiHookCallback.Param?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
移动 `HookParamWrapper` 到 `YukiHookCallback.Param`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
修正拼写错误的 **creater** 命名到 **creator**
|
||||
|
||||
**功能描述**
|
||||
|
||||
> Hook 方法、构造方法的目标对象实现类。
|
||||
|
||||
### args *- field*
|
||||
|
||||
```kotlin
|
||||
val args: Array<Any?>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
在 `v1.0` 添加
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 对象 `member` 或 `constructor` 的参数对象数组。
|
||||
|
||||
这里的数组每项类型默认为 `Any`,你可以使用 `args` 方法来实现 `ArgsModifyer.cast` 功能。
|
||||
|
||||
### ~~firstArgs *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.75` `移除`
|
||||
|
||||
请使用 `args(index = 0)` 或 `args().first()`
|
||||
|
||||
### ~~lastArgs *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.75` `移除`
|
||||
|
||||
请使用 `args().last()`
|
||||
|
||||
### instance *- field*
|
||||
|
||||
```kotlin
|
||||
val instance: Any
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 实例的对象。
|
||||
|
||||
!> 如果你当前 Hook 的对象是一个静态,那么它将不存在实例的对象。
|
||||
|
||||
### instanceClass *- field*
|
||||
|
||||
```kotlin
|
||||
val instanceClass: Class<*>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 实例的类对象。
|
||||
|
||||
### member *- field*
|
||||
|
||||
```kotlin
|
||||
val member: Member
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 对象的 `Member`。
|
||||
|
||||
在不确定 `Member` 类型为 `Method` 或 `Constructor` 时可以使用此方法。
|
||||
|
||||
### method *- field*
|
||||
|
||||
```kotlin
|
||||
val method: Method
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 对象的方法。
|
||||
|
||||
### constructor *- field*
|
||||
|
||||
```kotlin
|
||||
val constructor: Constructor
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 对象的构造方法。
|
||||
|
||||
### result *- field*
|
||||
|
||||
```kotlin
|
||||
var result: Any?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取、设置当前 Hook 对象的 `method` 或 `constructor` 的返回值。
|
||||
|
||||
### hasThrowable *- field*
|
||||
|
||||
```kotlin
|
||||
val hasThrowable: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 判断是否存在设置过的方法调用抛出异常。
|
||||
|
||||
### throwable *- field*
|
||||
|
||||
```kotlin
|
||||
val throwable: Throwable?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取设置的方法调用抛出异常。
|
||||
|
||||
### Throwable.throwToApp *- i-ext-method*
|
||||
|
||||
```kotlin
|
||||
fun Throwable.throwToApp()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 向 Hook APP 抛出异常。
|
||||
|
||||
使用 `hasThrowable` 判断当前是否存在被抛出的异常。
|
||||
|
||||
使用 `throwable` 获取当前设置的方法调用抛出异常。
|
||||
|
||||
仅会在回调方法的 `MemberHookCreator.beforeHook` or `MemberHookCreator.afterHook` 中生效。
|
||||
|
||||
!> 设置后会同时执行 `resultNull` 方法并将异常抛出给当前 Hook APP。
|
||||
|
||||
**功能示例**
|
||||
|
||||
Hook 过程中的异常仅会作用于 (Xposed) 宿主环境,目标 Hook APP 不会受到影响。
|
||||
|
||||
若想将异常抛给 Hook APP,可以直接使用如下方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
// ...
|
||||
}
|
||||
beforeHook {
|
||||
RuntimeException("Test Exception").throwToApp()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!> 向 Hook APP 抛出异常<u>**会对其暴露被 Hook 的事实**</u>,是不安全的,容易被检测,请按实际场景合理使用。
|
||||
|
||||
### result *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T> result(): T?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.75` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 对象的 `method` 或 `constructor` 的返回值 `T`。
|
||||
|
||||
### ~~firstArgs *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.66` `新增`
|
||||
|
||||
`v1.0.75` `移除`
|
||||
|
||||
### ~~lastArgs *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.66` `新增`
|
||||
|
||||
`v1.0.75` `移除`
|
||||
|
||||
### instance *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T> instance(): T
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 实例的对象 `T`。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以通过 `instance` 方法轻松使用泛型 `cast` 为目标对象的类型。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
instance<Activity>().finish()
|
||||
```
|
||||
|
||||
### args *- method*
|
||||
|
||||
```kotlin
|
||||
fun args(): ArgsIndexCondition
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.75` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 对象的 `method` or `constructor` 的参数数组下标实例化类。
|
||||
|
||||
### args *- method*
|
||||
|
||||
```kotlin
|
||||
fun args(index: Int): ArgsModifyer
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.75` `修改`
|
||||
|
||||
默认值 `index = 0` 移动到新的使用方法 `args().first()`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 对象的 `method` 或 `constructor` 的参数实例化对象类。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以通过 `args` 方法修改当前 Hook 实例的方法、构造方法的参数内容。
|
||||
|
||||
你可以直接使用 `set` 方法设置 `param` 为你的目标实例,接受 `Any` 类型。
|
||||
|
||||
!> 请确保 `param` 类型为你的目标实例类型。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
args(index = 0).set("modify the value")
|
||||
```
|
||||
|
||||
你可以这样直接设置第一位 `param` 的值。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
args().first().set("modify the value")
|
||||
```
|
||||
|
||||
你还可以直接设置最后一位 `param` 的值。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
args().last().set("modify the value")
|
||||
```
|
||||
|
||||
你还可以使用 `setNull` 方法设置 `param` 为空。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
args(index = 1).setNull()
|
||||
```
|
||||
|
||||
你还可以使用 `setTrue` 方法设置 `param` 为 `true`。
|
||||
|
||||
!> 请确保 `param` 类型为 `Boolean`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
args(index = 1).setTrue()
|
||||
```
|
||||
|
||||
你还可以使用 `setFalse` 方法设置 `param` 为 `false`。
|
||||
|
||||
!> 请确保 `param` 类型为 `Boolean`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
args(index = 1).setFalse()
|
||||
```
|
||||
|
||||
### callOriginal *- method*
|
||||
|
||||
```kotlin
|
||||
fun callOriginal(): Any?
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun <T> callOriginal(): T?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 执行原始 `Member`。
|
||||
|
||||
调用自身未进行 Hook 的原始 `Member` 并调用原始参数执行。
|
||||
|
||||
**功能实例**
|
||||
|
||||
此方法可以 `invoke` 原始未经 Hook 的 `Member` 对象,取决于原始 `Member` 的参数。
|
||||
|
||||
调用自身原始的方法不会再经过当前 `beforeHook`、`afterHook` 以及 `replaceUnit`、`replaceAny`。
|
||||
|
||||
比如我们 Hook 的这个方法被这样调用 `test("test value")`,使用此方法会调用其中的 `"test value"` 作为参数。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
param(StringType)
|
||||
returnType = StringType
|
||||
}
|
||||
afterHook {
|
||||
// <方案1> 不使用泛型,不获取方法执行结果,调用将使用原方法传入的 args 自动传参
|
||||
callOriginal()
|
||||
// <方案2> 使用泛型,已知方法执行结果参数类型进行 cast,假设返回值为 String,失败会返回 null,调用将使用原方法传入的 args 自动传参
|
||||
val value = callOriginal<String>()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### invokeOriginal *- method*
|
||||
|
||||
```kotlin
|
||||
fun invokeOriginal(vararg args: Any?): Any?
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun <T> invokeOriginal(vararg args: Any?): T?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
不再需要使用 `member.invokeOriginal` 进行调用
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 执行原始 `Member`。
|
||||
|
||||
调用自身未进行 Hook 的原始 `Member` 并自定义 `args` 执行。
|
||||
|
||||
**功能实例**
|
||||
|
||||
此方法可以 `invoke` 原始未经 Hook 的 `Member` 对象,可自定义需要调用的参数内容。
|
||||
|
||||
调用自身原始的方法不会再经过当前 `beforeHook`、`afterHook` 以及 `replaceUnit`、`replaceAny`。
|
||||
|
||||
比如我们 Hook 的这个方法被这样调用 `test("test value")`,使用此方法可自定义其中的 `args` 作为参数。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
param(StringType)
|
||||
returnType = StringType
|
||||
}
|
||||
afterHook {
|
||||
// <方案1> 不使用泛型,不获取方法执行结果
|
||||
invokeOriginal("test value")
|
||||
// <方案2> 使用泛型,已知方法执行结果参数类型进行 cast,假设返回值为 String,失败会返回 null
|
||||
val value = invokeOriginal<String>("test value")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### resultTrue *- method*
|
||||
|
||||
```kotlin
|
||||
fun resultTrue()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置当前 Hook 对象方法的 `result` 返回值为 `true`。
|
||||
|
||||
!> 请确保 `result` 类型为 `Boolean`。
|
||||
|
||||
### resultFalse *- method*
|
||||
|
||||
```kotlin
|
||||
fun resultFalse()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置当前 Hook 对象方法的 `result` 返回值为 `false`。
|
||||
|
||||
!> 请确保 `result` 类型为 `Boolean`。
|
||||
|
||||
### resultNull *- method*
|
||||
|
||||
```kotlin
|
||||
fun resultNull()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
!> 此方法将强制设置 Hook 对象方法的 `result` 为 `null`。
|
||||
|
||||
### ArgsIndexCondition *- class*
|
||||
|
||||
```kotlin
|
||||
inner class ArgsIndexCondition internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.75` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 对方法参数的数组下标进行实例化类。
|
||||
|
||||
#### first *- method*
|
||||
|
||||
```kotlin
|
||||
fun first(): ArgsModifyer
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.75` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 对象的 `method` or `constructor` 的参数数组第一位。
|
||||
|
||||
#### last *- method*
|
||||
|
||||
```kotlin
|
||||
fun last(): ArgsModifyer
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.75` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 对象的 `method` or `constructor` 的参数数组最后一位。
|
||||
|
||||
### ArgsModifyer *- class*
|
||||
|
||||
```kotlin
|
||||
inner class ArgsModifyer internal constructor(private val index: Int)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 对方法参数的修改进行实例化类。
|
||||
|
||||
#### cast *- method*
|
||||
|
||||
```kotlin
|
||||
fun <T> cast(): T?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.66` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`of`~~ 为 `cast`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 `T`。
|
||||
|
||||
#### byte *- method*
|
||||
|
||||
```kotlin
|
||||
fun byte(): Byte?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.68` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 Byte。
|
||||
|
||||
#### int *- method*
|
||||
|
||||
```kotlin
|
||||
fun int(): Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.66` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofInt`~~ 为 `int`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 Int。
|
||||
|
||||
#### long *- method*
|
||||
|
||||
```kotlin
|
||||
fun long(): Long
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.66` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofLong`~~ 为 `long`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 Long。
|
||||
|
||||
#### short *- method*
|
||||
|
||||
```kotlin
|
||||
fun short(): Short
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.66` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofShort`~~ 为 `short`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 Short。
|
||||
|
||||
#### double *- method*
|
||||
|
||||
```kotlin
|
||||
fun double(): Double
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.66` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofDouble`~~ 为 `double`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 Double。
|
||||
|
||||
#### float *- method*
|
||||
|
||||
```kotlin
|
||||
fun float(): Float
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.66` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofFloat`~~ 为 `float`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 Float。
|
||||
|
||||
#### string *- method*
|
||||
|
||||
```kotlin
|
||||
fun string(): String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.66` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofString`~~ 为 `string`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 String。
|
||||
|
||||
#### char *- method*
|
||||
|
||||
```kotlin
|
||||
fun char(): Char
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.68` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 Char。
|
||||
|
||||
#### boolean *- method*
|
||||
|
||||
```kotlin
|
||||
fun boolean(): Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.66` `新增`
|
||||
|
||||
`v1.0.68` `修改`
|
||||
|
||||
修改 ~~`ofBoolean`~~ 为 `boolean`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 Boolean。
|
||||
|
||||
#### any *- method*
|
||||
|
||||
```kotlin
|
||||
fun any(): Any?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.77` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 Any。
|
||||
|
||||
#### array *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T> array(): Array<T>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.68` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 Array。
|
||||
|
||||
#### list *- method*
|
||||
```kotlin
|
||||
inline fun <reified T> list(): List<T>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.68` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到方法参数的实例对象 List。
|
||||
|
||||
#### set *- method*
|
||||
|
||||
```kotlin
|
||||
fun <T> set(any: T?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置方法参数的实例对象。
|
||||
|
||||
#### setNull *- method*
|
||||
|
||||
```kotlin
|
||||
fun setNull()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置方法参数的实例对象为 `null`。
|
||||
|
||||
#### setTrue *- method*
|
||||
|
||||
```kotlin
|
||||
fun setTrue()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置方法参数的实例对象为 `true`。
|
||||
|
||||
!> 请确保目标对象的类型是 `Boolean`。
|
||||
|
||||
#### setFalse *- method*
|
||||
|
||||
```kotlin
|
||||
fun setFalse()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置方法参数的实例对象为 `false`。
|
||||
|
||||
!> 请确保目标对象的类型是 `Boolean`。
|
@@ -1,13 +0,0 @@
|
||||
## HookResources *- class*
|
||||
|
||||
```kotlin
|
||||
class HookResources internal constructor(var instance: YukiResources?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建一个当前 Hook 的 `YukiResources` 接管类。
|
@@ -1,89 +0,0 @@
|
||||
## IYukiHookXposedInit *- interface*
|
||||
|
||||
```kotlin
|
||||
interface IYukiHookXposedInit
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改` `作废`
|
||||
|
||||
作废了 ~~`YukiHookXposedInitProxy`~~ 名称但保留接口
|
||||
|
||||
转移到 `IYukiHookXposedInit` 新名称
|
||||
|
||||
**功能描述**
|
||||
|
||||
> YukiHookAPI 的 Xposed 装载 API 调用接口。
|
||||
|
||||
### onInit *- method*
|
||||
|
||||
```kotlin
|
||||
fun onInit()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.5` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 配置 `YukiHookAPI.Configs` 的初始化方法。
|
||||
|
||||
!> 在这里只能进行初始化配置,不能进行 Hook 操作。
|
||||
|
||||
此方法可选,你也可以选择不对 `YukiHookAPI.Configs` 进行配置。
|
||||
|
||||
### onHook *- method*
|
||||
|
||||
```kotlin
|
||||
fun onHook()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> Xposed API 的模块装载调用入口方法。
|
||||
|
||||
### onXposedEvent *- method*
|
||||
|
||||
```kotlin
|
||||
fun onXposedEvent()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听 Xposed 原生装载事件。
|
||||
|
||||
若你的 Hook 事件中存在需要兼容的原生 Xposed 功能,可在这里实现。
|
||||
|
||||
请在这里使用 `YukiXposedEvent` 创建回调事件监听。
|
||||
|
||||
可监听的事件如下:
|
||||
|
||||
`YukiXposedEvent.onInitZygote`
|
||||
|
||||
`YukiXposedEvent.onHandleLoadPackage`
|
||||
|
||||
`YukiXposedEvent.onHandleInitPackageResources`
|
||||
|
||||
!> 此接口仅供监听和实现原生 Xposed API 的功能,请不要在这里操作 `YukiHookAPI`。
|
||||
|
||||
## ~~YukiHookXposedInitProxy *- interface*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `作废`
|
||||
|
||||
请转移到 `IYukiHookXposedInit`
|
@@ -1,30 +0,0 @@
|
||||
## InjectYukiHookWithXposed *- annotation*
|
||||
|
||||
```kotlin
|
||||
annotation class InjectYukiHookWithXposed(
|
||||
val sourcePath: String,
|
||||
val modulePackageName: String,
|
||||
val entryClassName: String,
|
||||
val isUsingResourcesHook: Boolean
|
||||
)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
新增 `entryClassName` 参数
|
||||
|
||||
`v1.0.92` `修改`
|
||||
|
||||
新增 `isUsingResourcesHook` 参数
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 标识 `YukiHookAPI` 注入 Xposed 入口的类注解。
|
||||
|
||||
**功能示例**
|
||||
|
||||
详情请参考 [InjectYukiHookWithXposed 注解](config/xposed-using?id=injectyukihookwithxposed-注解)。
|
@@ -1,171 +0,0 @@
|
||||
## LoggerFactory *- kt*
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是 `YukiHookAPI` 的日志封装类,可实现同时向 `Logcat` 和 `XposedBridge.log` 打印日志的功能。
|
||||
|
||||
### LoggerType *- class*
|
||||
|
||||
```kotlin
|
||||
enum class LoggerType
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 需要打印的日志类型。
|
||||
|
||||
决定于模块与 (Xposed) 宿主环境使用的打印方式。
|
||||
|
||||
#### LOGD *- enum*
|
||||
|
||||
```kotlin
|
||||
LOGD
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 仅使用 `android.util.Log`。
|
||||
|
||||
#### XPOSEDBRIDGE *- enum*
|
||||
|
||||
```kotlin
|
||||
XPOSEDBRIDGE
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 仅使用 `XposedBridge.log`。
|
||||
|
||||
!> 只能在 (Xposed) 宿主环境中使用,模块环境将不生效。
|
||||
|
||||
#### SCOPE *- enum*
|
||||
|
||||
```kotlin
|
||||
SCOPE
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 分区使用。
|
||||
|
||||
(Xposed) 宿主环境仅使用 `XPOSEDBRIDGE`。
|
||||
|
||||
模块环境仅使用 `LOGD`。
|
||||
|
||||
#### BOTH *- enum*
|
||||
|
||||
```kotlin
|
||||
BOTH
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 同时使用。
|
||||
|
||||
(Xposed) 宿主环境使用 `LOGD` 与 `XPOSEDBRIDGE`。
|
||||
|
||||
模块环境仅使用 `LOGD`。
|
||||
|
||||
### loggerD *- method*
|
||||
|
||||
```kotlin
|
||||
fun loggerD(tag: String, msg: String, type: LoggerType)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
新增 `type` 参数
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `D`。
|
||||
|
||||
`tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。
|
||||
|
||||
### loggerI *- method*
|
||||
|
||||
```kotlin
|
||||
fun loggerI(tag: String, msg: String, type: LoggerType)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
新增 `type` 参数
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `I`。
|
||||
|
||||
`tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。
|
||||
|
||||
### loggerW *- method*
|
||||
|
||||
```kotlin
|
||||
fun loggerW(tag: String, msg: String, type: LoggerType)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
新增 `type` 参数
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `W`。
|
||||
|
||||
`tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。
|
||||
|
||||
### loggerE *- method*
|
||||
|
||||
```kotlin
|
||||
fun loggerE(tag: String, msg: String, e: Throwable?, type: LoggerType)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
新增 `type` 参数
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 向 `Logcat` 和 `XposedBridge` 打印日志,级别 `E`,可携带 `e` 异常信息,将打印异常堆栈。
|
||||
|
||||
`tag` 的默认参数为 `YukiHookAPI.Configs.debugTag`,你可以进行自定义。
|
File diff suppressed because it is too large
Load Diff
@@ -1,23 +0,0 @@
|
||||
## ModifierRules *- class*
|
||||
|
||||
```kotlin
|
||||
class ModifierRules private constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.67` `新增`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
新增 `Class` 的描述符判断
|
||||
|
||||
作为 lambda 整体判断条件使用
|
||||
|
||||
移动到 base 包名
|
||||
|
||||
私有化构造方法
|
||||
|
||||
**功能描述**
|
||||
|
||||
!> 新的功能已转移到新文档,这里仅保留文字说明记录。
|
@@ -1,17 +0,0 @@
|
||||
## ModuleAppActivity *- class*
|
||||
|
||||
```kotlin
|
||||
open class ModuleAppActivity : Activity()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 代理 `Activity`。
|
||||
|
||||
继承于此类的 `Activity` 可以同时在宿主与模块中启动。
|
||||
|
||||
在 (Xposed) 宿主环境需要在宿主启动时调用 `Context.registerModuleAppActivities` 进行注册。
|
@@ -1,33 +0,0 @@
|
||||
## ModuleAppCompatActivity *- class*
|
||||
|
||||
```kotlin
|
||||
open class ModuleAppCompatActivity : AppCompatActivity()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 代理 `AppCompatActivity`。
|
||||
|
||||
继承于此类的 `Activity` 可以同时在宿主与模块中启动。
|
||||
|
||||
在 (Xposed) 宿主环境需要在宿主启动时调用 `Context.registerModuleAppActivities` 进行注册。
|
||||
|
||||
在 (Xposed) 宿主环境需要重写 `moduleTheme` 设置 AppCompat 主题,否则会无法启动。
|
||||
|
||||
### moduleTheme *- field*
|
||||
|
||||
```kotlin
|
||||
open val moduleTheme: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置当前代理的 `Activity` 主题。
|
@@ -1,80 +0,0 @@
|
||||
## ModuleApplication *- class*
|
||||
|
||||
```kotlin
|
||||
open class ModuleApplication: Application()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.77` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是对使用 `YukiHookAPI` Xposed 模块实现中的一个扩展功能。
|
||||
|
||||
在你的 Xposed 模块的 `Application` 中继承此类。
|
||||
|
||||
或在 `AndroidManifest.xml` 的 `application` 标签中指定此类。
|
||||
|
||||
目前可实现功能如下
|
||||
|
||||
- 全局共享模块中静态的 `appContext`
|
||||
|
||||
- 在模块与宿主中装载 `YukiHookAPI.Config` 以确保 `YukiHookAPI.Configs.debugTag` 不需要重复定义
|
||||
|
||||
- 在模块与宿主中使用 `YukiHookDataChannel` 进行通讯
|
||||
|
||||
- 在模块中使用系统隐藏 API,核心技术引用了开源项目 [FreeReflection](https://github.com/tiann/FreeReflection)
|
||||
|
||||
- 在模块中使用 `YukiHookAPI.Status.isTaiChiModuleActive` 判断太极、无极激活状态
|
||||
|
||||
**功能示例**
|
||||
|
||||
将此类继承到你的自定义 `Application` 上。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
package com.demo
|
||||
|
||||
class MyApplication: ModuleApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在 `AndroidManifest.xml` 的 `application` 标签中指定自定义的 `Application`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```xml
|
||||
<application
|
||||
android:name="com.demo.MyApplication"
|
||||
...>
|
||||
```
|
||||
|
||||
如果你不需要自定义 `Application` 可以直接将 `ModuleApplication` 设置到 `AndroidManifest.xml` 的 `application` 标签中。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```xml
|
||||
<application
|
||||
android:name="com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication"
|
||||
...>
|
||||
```
|
||||
|
||||
### appContext *- field*
|
||||
|
||||
```kotlin
|
||||
val appContext: ModuleApplication
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.77` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取全局静态 `Application` 实例。
|
@@ -1,31 +0,0 @@
|
||||
## ModuleContextThemeWrapper *- class*
|
||||
|
||||
```kotlin
|
||||
class ModuleContextThemeWrapper private constructor(baseContext: Context, theme: Int, configuration: Configuration?) : ContextThemeWrapper
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 代理 `ContextThemeWrapper`。
|
||||
|
||||
通过包装,你可以轻松在 (Xposed) 宿主环境使用来自模块的主题资源。
|
||||
|
||||
### applyConfiguration *- method*
|
||||
|
||||
```kotlin
|
||||
fun applyConfiguration(initiate: Configuration.() -> Unit): ModuleContextThemeWrapper
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置当前 `ModuleContextThemeWrapper` 的 `Configuration`。
|
||||
|
||||
设置后会自动调用 `Resources.updateConfiguration`。
|
@@ -1,81 +0,0 @@
|
||||
## ModulePreferenceFragment *- class*
|
||||
|
||||
```kotlin
|
||||
abstract class ModulePreferenceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.78` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是对使用 `YukiHookAPI` Xposed 模块实现中的一个扩展功能。
|
||||
|
||||
此类接管了 `PreferenceFragmentCompat` 并对其实现了 Sp 存储在 Xposed 模块中的全局可读可写。
|
||||
|
||||
在你使用 `PreferenceFragmentCompat` 的实例中,将继承对象换成此类。
|
||||
|
||||
然后请将重写方法由 `onCreatePreferences` 替换为 `onCreatePreferencesInModuleApp` 即可。
|
||||
|
||||
**功能示例**
|
||||
|
||||
使用 `ModulePreferenceFragment` 创建一个 `PreferenceFragmentCompat` 对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class SettingsFragment : ModulePreferenceFragment() {
|
||||
|
||||
override fun onCreatePreferencesInModuleApp(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.settings_preferences, rootKey)
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
其余用法与 `PreferenceFragmentCompat` 保持一致。
|
||||
|
||||
### onCreatePreferencesInModuleApp *- method*
|
||||
|
||||
```kotlin
|
||||
abstract fun onCreatePreferencesInModuleApp(savedInstanceState: Bundle?, rootKey: String?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.78` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 对接原始方法 `onCreatePreferences`。
|
||||
|
||||
### onSharedPreferenceChanged *- method*
|
||||
|
||||
```kotlin
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.78` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 实现了 `SharedPreferences.OnSharedPreferenceChangeListener` 的原生监听功能。
|
||||
|
||||
**功能示例**
|
||||
|
||||
!> 在使用 `onSharedPreferenceChanged` 时请注意保留 super 方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class SettingsFragment : ModulePreferenceFragment() {
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
super.onSharedPreferenceChanged(sharedPreferences, key)
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
@@ -1,23 +0,0 @@
|
||||
## NameRules *- class*
|
||||
|
||||
```kotlin
|
||||
class NameRules private constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
`NameConditions` 更名为 `NameRules`
|
||||
|
||||
作为 lambda 整体判断条件使用
|
||||
|
||||
移动到 base 包名
|
||||
|
||||
私有化构造方法
|
||||
|
||||
**功能描述**
|
||||
|
||||
!> 新的功能已转移到新文档,这里仅保留文字说明记录。
|
@@ -1,890 +0,0 @@
|
||||
## PackageParam *- class*
|
||||
|
||||
```kotlin
|
||||
open class PackageParam internal constructor(internal var wrapper: PackageParamWrapper?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 装载 Hook 的目标 APP 入口对象实现类。
|
||||
|
||||
### appClassLoader *- field*
|
||||
|
||||
```kotlin
|
||||
val appClassLoader:ClassLoader
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook APP 的 `ClassLoader`。
|
||||
|
||||
### appInfo *- field*
|
||||
|
||||
```kotlin
|
||||
val appInfo: ApplicationInfo
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook APP 的 `ApplicationInfo`。
|
||||
|
||||
### appUserId *- field*
|
||||
|
||||
```kotlin
|
||||
val appUserId: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook APP 的用户 ID。
|
||||
|
||||
机主为 `0`,应用双开 (分身) 或工作资料因系统环境不同 ID 也各不相同。
|
||||
|
||||
### appContext *- field*
|
||||
|
||||
```kotlin
|
||||
val appContext: Application?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.72` `新增`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
加入可空类型 (空安全)
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook APP 的 `Application`。
|
||||
|
||||
!> 首次装载可能是空的,请延迟一段时间再获取或通过设置 `onAppLifecycle` 监听来完成。
|
||||
|
||||
### appResources *- field*
|
||||
|
||||
```kotlin
|
||||
val appResources:Resources?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
加入可空类型 (空安全)
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook APP 的 Resources。
|
||||
|
||||
!> 你只能在 `HookResources.hook` 方法体内或 `appContext` 装载完毕时进行调用。
|
||||
|
||||
### systemContext *- field*
|
||||
|
||||
```kotlin
|
||||
val systemContext: Context
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前系统框架的 `Context`。
|
||||
|
||||
### processName *- field*
|
||||
|
||||
```kotlin
|
||||
val processName: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook APP 的进程名称。
|
||||
|
||||
### packageName *- field*
|
||||
|
||||
```kotlin
|
||||
val packageName: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook APP 的包名。
|
||||
|
||||
### isFirstApplication *- field*
|
||||
|
||||
```kotlin
|
||||
val isFirstApplication: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook APP 是否为第一个 `Application`。
|
||||
|
||||
### mainProcessName *- field*
|
||||
|
||||
```kotlin
|
||||
val mainProcessName: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook APP 的主进程名称。
|
||||
|
||||
其对应的就是 `packageName`。
|
||||
|
||||
### moduleAppFilePath *- field*
|
||||
|
||||
```kotlin
|
||||
val moduleAppFilePath: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Xposed 模块自身 APK 文件路径。
|
||||
|
||||
!> 作为 Hook API 装载时无法使用,会获取到空字符串。
|
||||
|
||||
### moduleAppResources *- field*
|
||||
|
||||
```kotlin
|
||||
val moduleAppResources: YukiModuleResources
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Xposed 模块自身 `Resources`。
|
||||
|
||||
!> 作为 Hook API 或不支持的 Hook Framework 装载时无法使用,会抛出异常。
|
||||
|
||||
### prefs *- field*
|
||||
|
||||
```kotlin
|
||||
val prefs: YukiHookModulePrefs
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前使用的存取数据对象缓存实例。
|
||||
|
||||
!> 作为 Hook API 装载时无法使用,会抛出异常。
|
||||
|
||||
### prefs *- method*
|
||||
|
||||
```kotlin
|
||||
fun prefs(name: String): YukiHookModulePrefs
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前使用的存取数据对象缓存实例。
|
||||
|
||||
你可以通过 `name` 来自定义 Sp 存储的名称。
|
||||
|
||||
!> 作为 Hook API 装载时无法使用,会抛出异常。
|
||||
|
||||
### dataChannel *- field*
|
||||
|
||||
```kotlin
|
||||
val dataChannel: YukiHookDataChannel.NameSpace
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前使用的数据通讯桥命名空间对象。
|
||||
|
||||
!> 作为 Hook API 装载时无法使用,会抛出异常。
|
||||
|
||||
### resources *- method*
|
||||
|
||||
```kotlin
|
||||
fun resources(): HookResources
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前 Hook APP 的 `YukiResources` 对象。
|
||||
|
||||
请调用 `HookResources.hook` 方法开始 Hook。
|
||||
|
||||
### refreshModuleAppResources *- method*
|
||||
|
||||
```kotlin
|
||||
fun refreshModuleAppResources()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.87` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 刷新当前 Xposed 模块自身 `Resources`。
|
||||
|
||||
### onAppLifecycle *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun onAppLifecycle(initiate: AppLifecycle.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听当前 Hook APP 生命周期装载事件。
|
||||
|
||||
!> 在 `loadZygote` 中不会被装载,仅会在 `loadSystem`、`loadApp` 中装载。
|
||||
|
||||
!> 作为 Hook API 装载时请使用原生的 `Application` 实现生命周期监听。
|
||||
|
||||
### loadApp *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun loadApp(name: String, initiate: PackageParam.() -> Unit)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun loadApp(name: String, hooker: YukiBaseHooker)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 装载并 Hook 指定包名的 APP。
|
||||
|
||||
`name` 为 APP 的包名,后方的两个参数一个可作为 `lambda` 方法体使用,一个可以直接装载子 Hooker。
|
||||
|
||||
装载并 Hook 指定、全部包名的 APP。
|
||||
|
||||
若要装载 APP Zygote 事件,请使用 `loadZygote`。
|
||||
|
||||
若要 Hook 系统框架,请使用 `loadSystem`。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以使用 `loadApp` 的 `lambda` 方法体形式或直接装载一个 Hooker。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 使用 lambda
|
||||
loadApp(name = "com.example.test") {
|
||||
// Your code here.
|
||||
}
|
||||
// 使用 Hooker
|
||||
loadApp(name = "com.example.test", CustomHooker)
|
||||
```
|
||||
|
||||
若不指定 `name` 参数,则此方法体默认会过滤当前系统中全部可被 Hook 的 APP。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 使用 lambda
|
||||
loadApp {
|
||||
// Your code here.
|
||||
}
|
||||
// 使用 Hooker
|
||||
loadApp(hooker = CustomHooker)
|
||||
```
|
||||
|
||||
### loadZygote *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun loadZygote(initiate: PackageParam.() -> Unit)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun loadZygote(hooker: YukiBaseHooker)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 装载 APP Zygote 事件。
|
||||
|
||||
方法中的两个参数一个可作为 `lambda` 方法体使用,一个可以直接装载子 Hooker。
|
||||
|
||||
### loadSystem *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun loadSystem(initiate: PackageParam.() -> Unit)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun loadSystem(hooker: YukiBaseHooker)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.82` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 装载并 Hook 系统框架。
|
||||
|
||||
方法中的两个参数一个可作为 `lambda` 方法体使用,一个可以直接装载子 Hooker。
|
||||
|
||||
### withProcess *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun withProcess(name: String, initiate: PackageParam.() -> Unit)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun withProcess(name: String, hooker: YukiBaseHooker)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 装载并 Hook APP 的指定进程。
|
||||
|
||||
`name` 为 APP 的进程名称,后方的两个参数一个可作为 `lambda` 方法体使用,一个可以直接装载子 Hooker。
|
||||
|
||||
### loadHooker *- method*
|
||||
|
||||
```kotlin
|
||||
fun loadHooker(hooker: YukiBaseHooker)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 装载 Hook 子类。
|
||||
|
||||
你可以填入 `hooker` 在 Hooker 中继续装载 Hooker。
|
||||
|
||||
### ~~String+VariousClass.clazz *- i-ext-field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `作废`
|
||||
|
||||
请转移到 `toClass(...)` 方法
|
||||
|
||||
### ~~String.hasClass *- i-ext-field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `作废`
|
||||
|
||||
请转移到 `hasClass(...)` 方法
|
||||
|
||||
### String+VariousClass.toClass *- i-ext-method*
|
||||
|
||||
```kotlin
|
||||
fun String.toClass(loader: ClassLoader?): Class<*>
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun VariousClass.toClass(loader: ClassLoader?): Class<*>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 通过字符串类名、`VariousClass` 转换为 `loader` 中的实体类。
|
||||
|
||||
默认使用当前 `appClassLoader` 装载目标 `Class`。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以轻松地将 `String` 类型的 `Class` 包名转为 `Class` 实例。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
"com.example.demo.DemoClass".toClass()
|
||||
```
|
||||
|
||||
你还可以向 `loader` 参数传入你自定义的 `ClassLoader`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
|
||||
"com.example.demo.DemoClass".toClass(customClassLoader)
|
||||
```
|
||||
|
||||
你还可以创建一个 `VariousClass`,并转换为实体类。
|
||||
|
||||
`VariousClass` 会枚举所有设置的 `Class` 并最终获得第一个存在的 `Class`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
VariousClass("com.example.demo.DemoClass1", "com.example.demo.DemoClass2").toClass()
|
||||
```
|
||||
|
||||
同样地,你还可以向 `loader` 参数传入你自定义的 `ClassLoader`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
|
||||
VariousClass("com.example.demo.DemoClass1", "com.example.demo.DemoClass2").toClass(customClassLoader)
|
||||
```
|
||||
|
||||
### String.hasClass *- i-ext-method*
|
||||
|
||||
```kotlin
|
||||
fun String.hasClass(loader: ClassLoader?): Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 通过字符串类名查找是否存在。
|
||||
|
||||
默认使用当前 `appClassLoader` 装载目标 `Class`。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以轻松的使用此方法判断字符串中的类是否存在。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
if("com.example.demo.DemoClass".hasClass()) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
你还可以自定义其中的 `loader` 参数,默认为 `appClassLoader`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
|
||||
if("com.example.demo.DemoClass".hasClass(customClassLoader)) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
### findClass *- method*
|
||||
|
||||
```kotlin
|
||||
fun findClass(name: String, loader: ClassLoader?): HookClass
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun findClass(vararg name: String, loader: ClassLoader?): VariousClass
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `修改`
|
||||
|
||||
移除了 ~~`findClass(various: VariousClass)`~~ 方法
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
新增 `loader` 参数
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 通过完整包名+名称查找需要被 Hook 的 `Class`。
|
||||
|
||||
!> 使用此方法会得到一个 `HookClass` 仅用于 Hook,若想查找 `Class` 请使用 `toClass` 功能。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以使用三种方式查找你需要 Hook 的目标 `Class`。
|
||||
|
||||
你可以直接将被查找的 `Class` 完整包名+名称填入 `name` 中。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
findClass(name = "com.example.demo.DemoClass")
|
||||
```
|
||||
|
||||
若你不确定多个版本的 `Class` 以及不同名称,你可以将多个完整包名+名称填入 `name` 中。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
findClass("com.example.demo.DemoClass1", "com.example.demo.DemoClass2", "com.example.demo.DemoClass3")
|
||||
```
|
||||
|
||||
你还可以创建一个 `VariousClass`,将 `Class` 的完整包名+名称填入 `VariousClass` 的 `name` 中并填入 `various` 参数中。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val variousClass = VariousClass("com.example.demo.DemoClass1", "com.example.demo.DemoClass2", "com.example.demo.DemoClass3")
|
||||
```
|
||||
|
||||
若你当前需要查找的 `Class` 不属于 `appClassLoader`,你可以使用 `loader` 参数指定你要装载的 `ClassLoader`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val outsideLoader: ClassLoader? = ... // 假设这就是你的 ClassLoader
|
||||
findClass(name = "com.example.demo.OutsideClass", loader = outsideLoader)
|
||||
```
|
||||
|
||||
同样地,在不确定多个版本的 `Class` 以及不同名称时,也可以使用 `loader` 参数指定你要装载的 `ClassLoader`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val outsideLoader: ClassLoader? = ... // 假设这就是你的 ClassLoader
|
||||
findClass("com.example.demo.OutsideClass1", "com.example.demo.OutsideClass2", "com.example.demo.OutsideClass3", loader = outsideLoader)
|
||||
```
|
||||
|
||||
### String+Class+VariousClass+HookClass.hook *- i-ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun String.hook(initiate: YukiMemberHookCreator.() -> Unit): YukiMemberHookCreator.Result
|
||||
```
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.hook(isForceUseAbsolute: Boolean, initiate: YukiMemberHookCreator.() -> Unit): YukiMemberHookCreator.Result
|
||||
```
|
||||
|
||||
```kotlin
|
||||
inline fun VariousClass.hook(initiate: YukiMemberHookCreator.() -> Unit): YukiMemberHookCreator.Result
|
||||
```
|
||||
|
||||
```kotlin
|
||||
inline fun HookClass.hook(initiate: YukiMemberHookCreator.() -> Unit): YukiMemberHookCreator.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `修改`
|
||||
|
||||
新增 `VariousClass` 的直接调用 `hook` 方法
|
||||
|
||||
`v1.0.2` `修改`
|
||||
|
||||
新增 `String` 的直接调用 `hook` 方法
|
||||
|
||||
`v1.0.3` `修改`
|
||||
|
||||
新增 `YukiMemberHookCreator.Result` 返回值
|
||||
|
||||
`v1.0.70` `修改`
|
||||
|
||||
新增 `isUseAppClassLoader` 参数
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
移除了 ~~`isUseAppClassLoader`~~ 参数
|
||||
|
||||
添加了 `isForceUseAbsolute` 参数到 `Class.hook` 方法
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是一切 Hook 的入口创建方法,Hook 方法、构造方法。
|
||||
|
||||
**功能示例**
|
||||
|
||||
如你所见,Hook 方法体的创建可使用 4 种方式。
|
||||
|
||||
通过字符串类名得到 `HookClass` 实例进行创建。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
"com.example.demo.DemoClass".hook {
|
||||
// Your code here.
|
||||
}
|
||||
|
||||
```
|
||||
通过 `findClass` 得到 `HookClass` 实例进行创建。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
findClass(name = "com.example.demo.DemoClass").hook {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
使用 `stub` 或直接拿到 `Class` 实例进行创建。
|
||||
|
||||
默认情况下 API 会将 `Class` 实例转换为类名并绑定到 `appClassLoader`,若失败,则会使用原始 `Class` 实例直接进行 Hook。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
Stub::class.java.hook {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
若当前 `Class` 不在 `appClassLoader` 且自动匹配无法找到该 `Class`,请启用 `isForceUseAbsolute`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
YourClass::class.java.hook(isForceUseAbsolute = true) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
使用 `VariousClass` 实例进行创建。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
VariousClass("com.example.demo.DemoClass1", "com.example.demo.DemoClass2").hook {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
或者直接使用可变字符串数组进行创建。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
findClass("com.example.demo.DemoClass1", "com.example.demo.DemoClass2").hook {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
### HookResources.hook *- i-ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun HookResources.hook(initiate: YukiResourcesHookCreator.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> Hook APP 的 Resources。
|
||||
|
||||
!> 请注意你需要确保当前 Hook Framework 支持且 `InjectYukiHookWithXposed.isUsingResourcesHook` 已启用。
|
||||
|
||||
**功能示例**
|
||||
|
||||
Resources Hook 为固定用法,获取 `resources` 对象,然后调用 `hook` 方法开始 Hook。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
resources().hook {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
!> 这是固定用法,为了防止发生问题,你不可手动实现任何 `HookResources` 实例执行 `hook` 调用。
|
||||
|
||||
将 Resources 的 Hook 设置为这样是为了与 `findClass(...).hook` 做到统一,使得调用起来逻辑不会混乱。
|
||||
|
||||
### AppLifecycle *- class*
|
||||
|
||||
```kotlin
|
||||
inner class AppLifecycle internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 当前 Hook APP 的生命周期实例处理类。
|
||||
|
||||
#### attachBaseContext *- method*
|
||||
|
||||
```kotlin
|
||||
fun attachBaseContext(result: (baseContext: Context, hasCalledSuper: Boolean) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听当前 Hook APP 装载 `Application.attachBaseContext`。
|
||||
|
||||
#### onCreate *- method*
|
||||
|
||||
```kotlin
|
||||
fun onCreate(initiate: Application.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听当前 Hook APP 装载 `Application.onCreate`。
|
||||
|
||||
#### onTerminate *- method*
|
||||
|
||||
```kotlin
|
||||
fun onTerminate(initiate: Application.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听当前 Hook APP 装载 `Application.onTerminate`。
|
||||
|
||||
#### onLowMemory *- method*
|
||||
|
||||
```kotlin
|
||||
fun onLowMemory(initiate: Application.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听当前 Hook APP 装载 `Application.onLowMemory`。
|
||||
|
||||
#### onTrimMemory *- method*
|
||||
|
||||
```kotlin
|
||||
fun onTrimMemory(result: (self: Application, level: Int) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听当前 Hook APP 装载 `Application.onTrimMemory`。
|
||||
|
||||
#### onConfigurationChanged *- method*
|
||||
|
||||
```kotlin
|
||||
fun onConfigurationChanged(result: (self: Application, config: Configuration) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听当前 Hook APP 装载 `Application.onConfigurationChanged`。
|
||||
|
||||
#### registerReceiver *- method*
|
||||
|
||||
```kotlin
|
||||
fun registerReceiver(vararg action: String, result: (context: Context, intent: Intent) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 注册系统广播监听。
|
@@ -1,59 +0,0 @@
|
||||
## PrefsData *- class*
|
||||
|
||||
```kotlin
|
||||
data class PrefsData<T>(var key: String, var value: T)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.67` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 键值对存储构造类。
|
||||
|
||||
这个类是对 `YukiHookModulePrefs` 的一个扩展用法。
|
||||
|
||||
**功能示例**
|
||||
|
||||
建立一个模板类定义模块与宿主需要使用的键值数据。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
object DataConst {
|
||||
|
||||
val TEST_KV_DATA_1 = PrefsData("test_data_1", "defalut value")
|
||||
val TEST_KV_DATA_2 = PrefsData("test_data_2", false)
|
||||
val TEST_KV_DATA_3 = PrefsData("test_data_3", 0)
|
||||
}
|
||||
```
|
||||
|
||||
键值数据定义后,你就可以方便地在模块和宿主中调用所需要的数据。
|
||||
|
||||
> 模块示例如下
|
||||
|
||||
```kotlin
|
||||
// 读取
|
||||
val data = modulePrefs.get(DataConst.TEST_KV_DATA_1)
|
||||
// 写入
|
||||
modulePrefs.put(DataConst.TEST_KV_DATA_1, "written value")
|
||||
```
|
||||
|
||||
> 宿主示例如下
|
||||
|
||||
```kotlin
|
||||
// 读取 String
|
||||
val dataString = prefs.get(DataConst.TEST_KV_DATA_1)
|
||||
// 读取 Boolean
|
||||
val dataBoolean = prefs.get(DataConst.TEST_KV_DATA_2)
|
||||
```
|
||||
|
||||
你依然可以不使用模板定义的默认值,随时修改你的默认值。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 读取 - 此时 data 取到的默认值将会是 2 - 并不是模板提供的 0
|
||||
val data = prefs.get(DataConst.TEST_KV_DATA_3, 2)
|
||||
```
|
@@ -1,649 +0,0 @@
|
||||
## ReflectionFactory *- kt*
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是自定义 `Member` 和 `Class` 相关功能的查找匹配以及 `invoke` 的封装类。
|
||||
|
||||
### MembersType *- class*
|
||||
|
||||
```kotlin
|
||||
enum class MembersType
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 定义一个 `Class` 中的 `Member` 类型
|
||||
|
||||
#### ALL *- enum*
|
||||
|
||||
```kotlin
|
||||
ALL
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 全部 `Method` 与 `Constructor`。
|
||||
|
||||
#### METHOD *- enum*
|
||||
|
||||
```kotlin
|
||||
METHOD
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 全部 `Method`。
|
||||
|
||||
#### CONSTRUCTOR *- enum*
|
||||
|
||||
```kotlin
|
||||
CONSTRUCTOR
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 全部 `Constructor`。
|
||||
|
||||
### ClassLoader.onLoadClass *- ext-method*
|
||||
|
||||
```kotlin
|
||||
fun ClassLoader.onLoadClass(result: (Class<*>) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听当前 `ClassLoader` 的 `ClassLoader.loadClass` 方法装载。
|
||||
|
||||
!> 请注意只有当前 `ClassLoader` 有主动使用 `ClassLoader.loadClass` 事件时才能被捕获。
|
||||
|
||||
!> 这是一个实验性功能,一般情况下不会用到此方法,不保证不会发生错误。
|
||||
|
||||
!> 只能在 (Xposed) 宿主环境使用此功能,其它环境下使用将不生效且会打印警告信息。
|
||||
|
||||
**功能示例**
|
||||
|
||||
针对一些使用特定 `ClassLoader` 装载 `Class` 的宿主应用,你可以使用此方法来监听 `Class` 加载情况。
|
||||
|
||||
!> 为了防止发生问题,你需要<u>**得到一个存在的 `ClassLoader` 实例**</u>来使用此功能。
|
||||
|
||||
比如我们在 `PackageParam` 中使用 `appClassLoader`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
appClassLoader.onLoadClass { clazz ->
|
||||
// 得到 clazz 即加载对象
|
||||
clazz... // 这里进行你需要的操作
|
||||
}
|
||||
```
|
||||
|
||||
或使用你得到的存在的 `ClassLoader` 实例,可以通过 Hook 获取。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
|
||||
customClassLoader?.onLoadClass { clazz ->
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
在判断到这个 `Class` 被装载成功时,开始执行你的 Hook 功能。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
|
||||
customClassLoader?.onLoadClass { clazz ->
|
||||
if(clazz.name == /** 你需要的 Class 名称 */) {
|
||||
clazz.hook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ~~hookClass *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `移除`
|
||||
|
||||
`HookClass` 相关功能不再对外开放
|
||||
|
||||
### ~~normalClass *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `移除`
|
||||
|
||||
`HookClass` 相关功能不再对外开放
|
||||
|
||||
### ~~hasClass *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `移除`
|
||||
|
||||
请直接使用 `hasClass()` 无参方法
|
||||
|
||||
### Class.hasExtends *- ext-field*
|
||||
|
||||
```kotlin
|
||||
val Class<*>.hasExtends: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 当前 `Class` 是否有继承关系,父类是 `Any` 将被认为没有继承关系。
|
||||
|
||||
### ~~classOf *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `移除`
|
||||
|
||||
请转到 `toClass(...)` 方法
|
||||
|
||||
### String.toClass *- ext-method*
|
||||
|
||||
```kotlin
|
||||
fun String.toClass(loader: ClassLoader?): Class<*>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 通过字符串类名转换为 `loader` 中的实体类。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以直接填写你要查找的目标 `Class`,必须在默认 `ClassLoader` 下存在。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
"com.example.demo.DemoClass".toClass()
|
||||
```
|
||||
|
||||
你还可以自定义 `Class` 所在的 `ClassLoader`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
|
||||
"com.example.demo.DemoClass".toClass(customClassLoader)
|
||||
```
|
||||
|
||||
### classOf *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T> classOf(loader: ClassLoader?): Class<*>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 通过 `T` 得到其 `Class` 实例并转换为实体类。
|
||||
|
||||
**功能示例**
|
||||
|
||||
我们要获取一个 `Class` 在 `Kotlin` 下不通过反射时应该这样做。
|
||||
|
||||
> 示例如下
|
||||
>
|
||||
```kotlin
|
||||
DemoClass::class.java
|
||||
```
|
||||
|
||||
现在,你可以直接 `cast` 一个实例并获取它的 `Class` 对象,必须在当前 `ClassLoader` 下存在。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
classOf<DemoClass>()
|
||||
```
|
||||
|
||||
若目标存在的 `Class` 为 `stub`,通过这种方式,你还可以自定义 `Class` 所在的 `ClassLoader`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
|
||||
classOf<DemoClass>(customClassLoader)
|
||||
```
|
||||
|
||||
### String.hasClass *- ext-method*
|
||||
|
||||
```kotlin
|
||||
fun String.hasClass(loader: ClassLoader?): Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
支持直接使用空参数方法使用默认 `ClassLoader` 进行判断
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 通过字符串类名使用指定的 `ClassLoader` 查找是否存在。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以轻松的使用此方法判断字符串中的类是否存在,效果等同于直接使用 `Class.forName`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
if("com.example.demo.DemoClass".hasClass()) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
填入方法中的 `loader` 参数可判断指定的 `ClassLoader` 中的 `Class` 是否存在。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val customClassLoader: ClassLoader? = ... // 假设这个就是你的 ClassLoader
|
||||
if("com.example.demo.DemoClass".hasClass(customClassloader)) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
### Class.hasField *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.hasField(initiate: FieldCondition): Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.4` `新增`
|
||||
|
||||
`v1.0.67` `修改`
|
||||
|
||||
合并到 `FieldFinder`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查找变量是否存在。
|
||||
|
||||
### Class.hasMethod *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.hasMethod(initiate: MethodConditions): Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `修改`
|
||||
|
||||
新增 `returnType` 参数
|
||||
|
||||
`v1.0.67` `修改`
|
||||
|
||||
合并到 `MethodFinder`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查找方法是否存在。
|
||||
|
||||
### Class.hasConstructor *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.hasConstructor(initiate: ConstructorConditions): Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.2` `新增`
|
||||
|
||||
`v1.0.67` `修改`
|
||||
|
||||
合并到 `ConstructorFinder`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查找构造方法是否存在。
|
||||
|
||||
### Member.hasModifiers *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Member.hasModifiers(conditions: ModifierConditions): Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.67` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
合并到 `ModifierConditions`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查询 `Member` 中匹配的描述符。
|
||||
|
||||
### Class.hasModifiers *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.hasModifiers(conditions: ModifierConditions): Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查询 `Class` 中匹配的描述符。
|
||||
|
||||
### ~~obtainStaticFieldAny *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `移除`
|
||||
|
||||
### ~~obtainFieldAny *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `移除`
|
||||
|
||||
### ~~modifyStaticField *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `移除`
|
||||
|
||||
### ~~modifyField *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `移除`
|
||||
|
||||
### Class.field *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.field(initiate: FieldConditions): FieldFinder.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.2` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查找并得到变量。
|
||||
|
||||
### Class.method *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.method(initiate: MethodConditions): MethodFinder.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `修改`
|
||||
|
||||
~~`obtainMethod`~~ 更名为 `method`
|
||||
|
||||
新增 `returnType` 参数
|
||||
|
||||
`v1.0.2` `修改`
|
||||
|
||||
合并到 `MethodFinder` 方法体
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查找并得到方法。
|
||||
|
||||
### Class.constructor *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.constructor(initiate: ConstructorConditions): ConstructorFinder.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `修改`
|
||||
|
||||
~~`obtainConstructor`~~ 更名为 `constructor`
|
||||
|
||||
`v1.0.2` `修改`
|
||||
|
||||
合并到 `ConstructorFinder` 方法体
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查找并得到构造方法。
|
||||
|
||||
### ~~callStatic *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `修改`
|
||||
|
||||
~~`invokeStatic`~~ 更名为 `callStatic`
|
||||
|
||||
`v1.0.2` `移除`
|
||||
|
||||
### ~~call *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.1` `修改`
|
||||
|
||||
~~`invokeAny`~~ 更名为 `call`
|
||||
|
||||
`v1.0.2` `移除`
|
||||
|
||||
### Any.current *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T : Any> T.current(ignored: Boolean): CurrentClass
|
||||
```
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T : Any> T.current(ignored: Boolean, initiate: CurrentClass.() -> Unit): T
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
新增 `ignored` 参数,可以忽略在 `CurrentClass` 中出现的异常
|
||||
|
||||
新增不使用 `current { ... }` 调用域直接使用 `current()` 得到实例的类操作对象
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前实例的类操作对象。
|
||||
|
||||
### ~~Class.buildOfAny *- ext-method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
`v1.1.0` `作废`
|
||||
|
||||
请转移到 `buildOf` 方法
|
||||
|
||||
### Class.buildOf *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.buildOf(vararg param: Any?, initiate: ConstructorConditions): Any?
|
||||
```
|
||||
|
||||
```kotlin
|
||||
inline fun <T> Class<*>.buildOf(vararg param: Any?, initiate: ConstructorConditions): T?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
加入无泛型方法 `buildOf`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 通过构造方法创建新实例,指定类型 `T` 或任意类型 `Any`。
|
||||
|
||||
### Class.allMethods *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.allMethods(result: (index: Int, method: Method) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 遍历当前类中的所有方法。
|
||||
|
||||
### Class.allConstructors *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.allConstructors(result: (index: Int, constructor: Constructor<*>) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 遍历当前类中的所有构造方法。
|
||||
|
||||
### Class.allFields *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun Class<*>.allFields(result: (index: Int, field: Field) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 遍历当前类中的所有变量。
|
@@ -1,11 +0,0 @@
|
||||
## VariableTypeFactory *- kt*
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是一个预置 Hook 类型的常量类,主要为 `Java` 相关基本变量类型的 `Class` 内容,跟随版本更新会逐一进行增加。
|
||||
|
||||
详情可 [点击这里](https://github.com/fankes/YukiHookAPI/blob/master/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/type/java/VariableTypeFactory.kt) 进行查看。
|
@@ -1,29 +0,0 @@
|
||||
## VariousClass *- class*
|
||||
|
||||
```kotlin
|
||||
class VariousClass(vararg var name: String)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是一个不确定性 `Class` 类名装载器,通过 `name` 装载 `Class` 名称数组。
|
||||
|
||||
### get *- method*
|
||||
|
||||
```kotlin
|
||||
fun get(loader: ClassLoader? = null): Class<*>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取匹配的实体类。
|
||||
|
||||
使用当前 `loader` 装载目标 `Class`。
|
@@ -1,11 +0,0 @@
|
||||
## ViewTypeFactory *- kt*
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是一个预置 Hook 类型的常量类,主要为 `Android` 相关 `Widget` 的 `Class` 内容,跟随版本更新会逐一进行增加。
|
||||
|
||||
详情可 [点击这里](https://github.com/fankes/YukiHookAPI/blob/master/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/type/android/ViewTypeFactory.kt) 进行查看。
|
@@ -1,27 +0,0 @@
|
||||
## YukiBaseHooker *- class*
|
||||
|
||||
```kotlin
|
||||
abstract class YukiBaseHooker : PackageParam()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `YukiHookAPI` 的子类 Hooker 实现。
|
||||
|
||||
### onHook *- method*
|
||||
|
||||
```kotlin
|
||||
fun onHook()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 子类 Hook 开始。
|
@@ -1,513 +0,0 @@
|
||||
## YukiHookAPI *- object*
|
||||
|
||||
```kotlin
|
||||
object YukiHookAPI
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是 `YukiHookAPI` 的 API 调用总类,Hook 相关功能的开始、Hook 相关功能的配置都在这里。
|
||||
|
||||
### API_VERSION_NAME *- field*
|
||||
|
||||
```kotlin
|
||||
const val API_VERSION_NAME: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.4` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 `YukiHookAPI` 的版本。
|
||||
|
||||
### API_VERSION_CODE *- field*
|
||||
|
||||
```kotlin
|
||||
const val API_VERSION_CODE: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.4` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 `YukiHookAPI` 的版本号。
|
||||
|
||||
### ~~executorName *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.5` `新增`
|
||||
|
||||
`v1.0.91` `移除`
|
||||
|
||||
请转移到 `Status.executorName`
|
||||
|
||||
### ~~executorVersion *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.5` `新增`
|
||||
|
||||
`v1.0.91` `移除`
|
||||
|
||||
请转移到 `Status.executorVersion`
|
||||
|
||||
### Status *- object*
|
||||
|
||||
```kotlin
|
||||
object Status
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.91` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 当前 `YukiHookAPI` 的状态。
|
||||
|
||||
#### isXposedEnvironment *- field*
|
||||
|
||||
```kotlin
|
||||
val isXposedEnvironment: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前是否为 (Xposed) 宿主环境。
|
||||
|
||||
#### executorName *- field*
|
||||
|
||||
```kotlin
|
||||
val executorName: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.91` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 框架的名称。
|
||||
|
||||
无法获取会返回 `unknown`,`XposedBridge` 不存在会返回 `invalid`。
|
||||
|
||||
!> 在模块环境中需要启用 `Configs.isEnableHookModuleStatus`。
|
||||
|
||||
#### executorVersion *- field*
|
||||
|
||||
```kotlin
|
||||
val executorVersion: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.91` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前 Hook 框架的版本。
|
||||
|
||||
无法获取会返回 `-1`。
|
||||
|
||||
!> 在模块环境中需要启用 `Configs.isEnableHookModuleStatus`。
|
||||
|
||||
#### isModuleActive *- field*
|
||||
|
||||
```kotlin
|
||||
val isModuleActive: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.91` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 判断模块是否在 Xposed 或太极、无极中激活。
|
||||
|
||||
!> 在模块环境中你需要将 `Application` 继承于 `ModuleApplication`。
|
||||
|
||||
!> 在模块环境中需要启用 `Configs.isEnableHookModuleStatus`。
|
||||
|
||||
!> 在 (Xposed) 宿主环境中仅返回非 `isTaiChiModuleActive` 的激活状态。
|
||||
|
||||
#### isXposedModuleActive *- field*
|
||||
|
||||
```kotlin
|
||||
val isXposedModuleActive: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.91` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 仅判断模块是否在 Xposed 中激活。
|
||||
|
||||
!> 在模块环境中需要启用 `Configs.isEnableHookModuleStatus`。
|
||||
|
||||
!> 在 (Xposed) 宿主环境中始终返回 true。
|
||||
|
||||
#### isTaiChiModuleActive *- field*
|
||||
|
||||
```kotlin
|
||||
val isTaiChiModuleActive: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.91` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 仅判断模块是否在太极、无极中激活。
|
||||
|
||||
!> 在模块环境中你需要将 `Application` 继承于 `ModuleApplication`。
|
||||
|
||||
!> 在 (Xposed) 宿主环境中始终返回 false。
|
||||
|
||||
#### isSupportResourcesHook *- field*
|
||||
|
||||
```kotlin
|
||||
val isSupportResourcesHook: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.91` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 判断当前 Hook Framework 是否支持资源钩子(Resources Hook)。
|
||||
|
||||
!> 在模块环境中需要启用 `Configs.isEnableHookModuleStatus`。
|
||||
|
||||
!> 在 (Xposed) 宿主环境中可能会延迟等待事件回调后才会返回 true。
|
||||
|
||||
!> 请注意你需要确保 `InjectYukiHookWithXposed.isUsingResourcesHook` 已启用,否则始终返回 false。
|
||||
|
||||
### Configs *- object*
|
||||
|
||||
```kotlin
|
||||
object Configs
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 对 API 相关功能的配置类。
|
||||
|
||||
#### debugTag *- field*
|
||||
|
||||
```kotlin
|
||||
var debugTag: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 模块在调用 `logger` 时打印的日志 `TAG` 名称。
|
||||
|
||||
你可以方便地进行自定义,并可以在 `Logcat` 和 `XposedBridge.log` 中找到它们。
|
||||
|
||||
#### isDebug *- field*
|
||||
|
||||
```kotlin
|
||||
var isDebug: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 是否启用 DEBUG 模式。
|
||||
|
||||
默认为开启状态,开启后模块将会向 `Logcat` 和 `XposedBridge.log` 打印详细的 Hook 日志,关闭后仅会打印 `E` 级别的日志。
|
||||
|
||||
#### isAllowPrintingLogs *- field*
|
||||
|
||||
```kotlin
|
||||
var isAllowPrintingLogs: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.4` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 是否启用调试日志的输出功能。
|
||||
|
||||
!> 关闭后将会停用 `YukiHookAPI` 对全部日志的输出,但是不影响当你手动调用日志方法输出日志。
|
||||
|
||||
#### isEnableModulePrefsCache *- field*
|
||||
|
||||
```kotlin
|
||||
var isEnableModulePrefsCache: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.5` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 是否启用 `YukiHookModulePrefs` 的键值缓存功能。
|
||||
|
||||
为防止内存复用过高问题,此功能默认启用。
|
||||
|
||||
你可以手动在 `YukiHookModulePrefs` 中自由开启和关闭缓存功能以及清除缓存。
|
||||
|
||||
#### isEnableModuleAppResourcesCache *- field*
|
||||
|
||||
```kotlin
|
||||
var isEnableModuleAppResourcesCache: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.87` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 是否启用当前 Xposed 模块自身 `Resources` 缓存功能。
|
||||
|
||||
为防止内存复用过高问题,此功能默认启用。
|
||||
|
||||
你可以手动调用 `PackageParam.refreshModuleAppResources` 来刷新缓存。
|
||||
|
||||
!> 关闭后每次使用 `PackageParam.moduleAppResources` 都会重新创建,可能会造成运行缓慢。
|
||||
|
||||
#### isEnableHookModuleStatus *- field*
|
||||
|
||||
```kotlin
|
||||
var isEnableHookModuleStatus: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 是否启用 Hook Xposed 模块激活等状态功能.
|
||||
|
||||
为原生支持 Xposed 模块激活状态检测,此功能默认启用。
|
||||
|
||||
!> 关闭后你将不能再在模块环境中使用 `YukiHookAPI.Status` 中的功能。
|
||||
|
||||
#### isEnableHookSharedPreferences *- field*
|
||||
|
||||
```kotlin
|
||||
var isEnableHookSharedPreferences: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 是否启用 Hook `SharedPreferences`。
|
||||
|
||||
启用后将在模块启动时强制将 `SharedPreferences` 文件权限调整为 `Context.MODE_WORLD_READABLE` (0664)。
|
||||
|
||||
!> 这是一个可选的实验性功能,此功能默认不启用。
|
||||
|
||||
仅用于修复某些系统可能会出现在启用了 `New XSharedPreferences` 后依然出现文件权限错误问题,若你能正常使用 `YukiHookModulePrefs` 就不建议启用此功能。
|
||||
|
||||
#### isEnableDataChannel *- field*
|
||||
|
||||
```kotlin
|
||||
var isEnableDataChannel: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 是否启用当前 Xposed 模块与宿主交互的 `YukiHookDataChannel` 功能。
|
||||
|
||||
请确保 Xposed 模块的 `Application` 继承于 `ModuleApplication` 才能有效。
|
||||
|
||||
此功能默认启用,关闭后将不会在功能初始化的时候装载 `YukiHookDataChannel`。
|
||||
|
||||
#### isEnableMemberCache *- field*
|
||||
|
||||
```kotlin
|
||||
var isEnableMemberCache: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.68` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 是否启用 `Member` 缓存功能。
|
||||
|
||||
为防止 `Member` 复用过高造成的系统 GC 问题,此功能默认启用。
|
||||
|
||||
启用后会缓存已经找到的 `Method`、`Constructor`、`Field`。
|
||||
|
||||
缓存的 `Member` 都将处于 `MemberCacheStore` 的全局静态实例中。
|
||||
|
||||
推荐使用 `MethodFinder`、`ConstructorFinder`、`FieldFinder` 来获取 `Member`。
|
||||
|
||||
除非缓存的 `Member` 发生了混淆的问题,例如使用 R8 混淆后的 APP 的目标 `Member`,否则建议启用。
|
||||
|
||||
### configs *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun configs(initiate: Configs.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 对 `Configs` 类实现了一个 `lambda` 方法体。
|
||||
|
||||
你可以轻松的调用它进行配置。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以在 `HookEntryClass` 的 `onInit` 方法中调用 `configs` 方法完成对 API 的功能配置,实时生效。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HookEntryClass : IYukiHookXposedInit {
|
||||
|
||||
override fun onInit() {
|
||||
YukiHookAPI.configs {
|
||||
debugTag = "YukiHookAPI"
|
||||
isDebug = BuildConfig.DEBUG
|
||||
isAllowPrintingLogs = true
|
||||
isEnableModulePrefsCache = true
|
||||
isEnableModuleAppResourcesCache = true
|
||||
isEnableHookModuleStatus = true
|
||||
isEnableDataChannel = true
|
||||
isEnableMemberCache = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHook() {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
若觉得上面的写法不美观,你还可以写得更加简洁。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HookEntryClass : IYukiHookXposedInit {
|
||||
|
||||
override fun onInit() = configs {
|
||||
debugTag = "YukiHookAPI"
|
||||
isDebug = BuildConfig.DEBUG
|
||||
isAllowPrintingLogs = true
|
||||
isEnableModulePrefsCache = true
|
||||
isEnableModuleAppResourcesCache = true
|
||||
isEnableHookModuleStatus = true
|
||||
isEnableDataChannel = true
|
||||
isEnableMemberCache = true
|
||||
}
|
||||
|
||||
override fun onHook() {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
你也可以不通过 `configs` 方法,直接进行配置。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HookEntryClass : IYukiHookXposedInit {
|
||||
|
||||
override fun onInit() {
|
||||
YukiHookAPI.Configs.debugTag = "YukiHookAPI"
|
||||
YukiHookAPI.Configs.isDebug = BuildConfig.DEBUG
|
||||
YukiHookAPI.Configs.isAllowPrintingLogs = true
|
||||
YukiHookAPI.Configs.isEnableModulePrefsCache = true
|
||||
YukiHookAPI.Configs.isEnableModuleAppResourcesCache = true
|
||||
YukiHookAPI.Configs.isEnableHookModuleStatus = true
|
||||
YukiHookAPI.Configs.isEnableDataChannel = true
|
||||
YukiHookAPI.Configs.isEnableMemberCache = true
|
||||
}
|
||||
|
||||
override fun onHook() {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### encase *- method*
|
||||
|
||||
```kotlin
|
||||
fun encase(initiate: PackageParam.() -> Unit)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun encase(vararg hooker: YukiBaseHooker)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun encase(baseContext: Context?, initiate: PackageParam.() -> Unit)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun encase(baseContext: Context?, vararg hooker: YukiBaseHooker)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 装载 Hook 入口的核心方法。
|
||||
|
||||
**功能示例**
|
||||
|
||||
详情请参考
|
||||
|
||||
- [通过 lambda 创建](config/api-example?id=通过-lambda-创建)
|
||||
|
||||
- [通过自定义 Hooker 创建](config/api-example?id=通过自定义-hooker-创建)
|
||||
|
||||
- [作为 Hook API 使用需要注意的地方](config/api-example?id=作为-hook-api-使用需要注意的地方)
|
@@ -1,141 +0,0 @@
|
||||
## YukiHookDataChannel *- class*
|
||||
|
||||
```kotlin
|
||||
class YukiHookDataChannel private constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 实现 Xposed 模块的数据通讯桥。
|
||||
|
||||
通过模块与宿主相互注册 `BroadcastReceiver` 来实现数据的交互。
|
||||
|
||||
模块需要将 `Application` 继承于 `ModuleApplication` 来实现此功能。
|
||||
|
||||
!> 模块与宿主需要保持存活状态,否则无法建立通讯。
|
||||
|
||||
### NameSpace *- class*
|
||||
|
||||
```kotlin
|
||||
inner class NameSpace internal constructor(private val context: Context?, private val packageName: String, private val isSecure: Boolean)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
`v1.0.90` `修改`
|
||||
|
||||
新增 `isSecure` 参数
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `YukiHookDataChannel` 命名空间。
|
||||
|
||||
#### with *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun with(initiate: NameSpace.() -> Unit): NameSpace
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建一个调用空间。
|
||||
|
||||
#### put *- method*
|
||||
|
||||
```kotlin
|
||||
fun <T> put(key: String, value: T)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun <T> put(data: ChannelData<T>, value: T?)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun put(vararg data: ChannelData<*>)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 发送键值数据。
|
||||
|
||||
#### put *- method*
|
||||
|
||||
```kotlin
|
||||
fun put(key: String)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 仅发送键值监听,使用默认值 `VALUE_WAIT_FOR_LISTENER` 发送键值数据。
|
||||
|
||||
#### wait *- method*
|
||||
|
||||
```kotlin
|
||||
fun <T> wait(key: String, result: (value: T) -> Unit)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun <T> wait(data: ChannelData<T>, result: (value: T) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
`v1.0.90` `修改`
|
||||
|
||||
移除默认值 `value`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取键值数据。
|
||||
|
||||
#### wait *- method*
|
||||
|
||||
```kotlin
|
||||
fun wait(key: String, callback: () -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 仅获取监听结果,不获取键值数据。
|
||||
|
||||
!> 仅限使用 `VALUE_WAIT_FOR_LISTENER` 发送的监听才能被接收。
|
||||
|
||||
#### checkingVersionEquals *- method*
|
||||
|
||||
```kotlin
|
||||
fun checkingVersionEquals(result: (Boolean) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取模块与宿主的版本是否匹配。
|
||||
|
||||
通过此方法可原生判断 Xposed 模块更新后宿主并未重新装载造成两者不匹配的情况。
|
@@ -1,225 +0,0 @@
|
||||
## YukiHookFactory *- kt*
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
合并到 `IYukiHookXposedInit`,将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 这是 `YukiHookAPI` 相关 `lambda` 方法的封装类以及部分 API 用法。
|
||||
|
||||
### IYukiHookXposedInit.configs *- ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun IYukiHookXposedInit.configs(initiate: YukiHookAPI.Configs.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.1` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
合并到 `IYukiHookXposedInit`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 在 `IYukiHookXposedInit` 中配置 `Configs`。
|
||||
|
||||
### IYukiHookXposedInit.encase *- ext-method*
|
||||
|
||||
```kotlin
|
||||
fun IYukiHookXposedInit.encase(initiate: PackageParam.() -> Unit)
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun IYukiHookXposedInit.encase(vararg hooker: YukiBaseHooker)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
合并到 `IYukiHookXposedInit`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 在 `IYukiHookXposedInit` 中调用 `YukiHookAPI`。
|
||||
|
||||
### Context.modulePrefs *- ext-field*
|
||||
|
||||
```kotlin
|
||||
val Context.modulePrefs: YukiHookModulePrefs
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取模块的存取对象。
|
||||
|
||||
### Context.modulePrefs *- ext-method*
|
||||
|
||||
```kotlin
|
||||
fun Context.modulePrefs(name: String): YukiHookModulePrefs
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取模块的存取对象,可设置 `name` 为自定义 Sp 存储名称。
|
||||
|
||||
### Context.dataChannel *- ext-method*
|
||||
|
||||
```kotlin
|
||||
fun Context.dataChannel(packageName: String): YukiHookDataChannel.NameSpace
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取模块的数据通讯桥命名空间对象。
|
||||
|
||||
!> 只能在模块环境使用此功能,其它环境下使用将不起作用。
|
||||
|
||||
### Context.processName *- ext-field*
|
||||
|
||||
```kotlin
|
||||
val Context.processName: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前进程名称。
|
||||
|
||||
### Context+Resources.injectModuleAppResources *- ext-method*
|
||||
|
||||
```kotlin
|
||||
fun Context.injectModuleAppResources()
|
||||
```
|
||||
|
||||
```kotlin
|
||||
fun Resources.injectModuleAppResources()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 向 Hook APP (宿主) `Context` 或 `Resources` 注入当前 Xposed 模块的资源。
|
||||
|
||||
注入成功后,你就可以直接使用例如 `ImageView.setImageResource` 或 `Resources.getString` 装载当前 Xposed 模块的资源 ID。
|
||||
|
||||
注入的资源作用域仅限当前 `Context` 或 `Resources`,你需要在每个用到宿主 `Context` 或 `Resources` 的地方重复调用此方法进行注入才能使用。
|
||||
|
||||
!> 只能在 (Xposed) 宿主环境使用此功能,其它环境下使用将不生效且会打印警告信息。
|
||||
|
||||
### Context.registerModuleAppActivities *- ext-method*
|
||||
|
||||
```kotlin
|
||||
fun Context.registerModuleAppActivities(proxy: Any?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 向 Hook APP (宿主) 注册当前 Xposed 模块的 `Activity`。
|
||||
|
||||
注册成功后,你就可以直接使用 `Context.startActivity` 来启动未在宿主中注册的 `Activity`。
|
||||
|
||||
使用此方法会在未注册的 `Activity` 在 Hook APP (宿主) 中启动时自动调用 `injectModuleAppResources` 注入当前 Xposed 模块的资源。
|
||||
|
||||
你要将需要在宿主启动的 `Activity` 继承于 `ModuleAppActivity` 或 `ModuleAppCompatActivity`。
|
||||
|
||||
!> 只能在 (Xposed) 宿主环境使用此功能,其它环境下使用将不生效且会打印警告信息。
|
||||
|
||||
### Context.applyModuleTheme *- ext-method*
|
||||
|
||||
```kotlin
|
||||
fun Context.applyModuleTheme(theme: Int, configuration: Configuration?): ModuleContextThemeWrapper
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 生成一个 `ContextThemeWrapper` 代理以应用当前 Xposed 模块的主题资源。
|
||||
|
||||
在 Hook APP (宿主) 中使用此方法会自动调用 `injectModuleAppResources` 注入当前 Xposed 模块的资源。
|
||||
|
||||
如果在 Hook APP (宿主) 中使用此方法发生 `ClassCastException`,请手动设置 `configuration`。
|
||||
|
||||
### ~~isSupportResourcesHook *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
`v1.0.91` `移除`
|
||||
|
||||
请转移到 `YukiHookAPI.Status.isSupportResourcesHook`
|
||||
|
||||
### ~~isModuleActive *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.6` `新增`
|
||||
|
||||
`v1.0.91` `移除`
|
||||
|
||||
请转移到 `YukiHookAPI.Status.isModuleActive`
|
||||
|
||||
### ~~isXposedModuleActive *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.6` `新增`
|
||||
|
||||
`v1.0.91` `移除`
|
||||
|
||||
请转移到 `YukiHookAPI.Status.isXposedModuleActive`
|
||||
|
||||
### ~~isTaiChiModuleActive *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.91` `移除`
|
||||
|
||||
请转移到 `YukiHookAPI.Status.isTaiChiModuleActive`
|
||||
|
||||
## ~~YukiHookModuleStatus *- class*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.91` `作废`
|
||||
|
||||
请转移到 `YukiHookAPI.Status`
|
@@ -1,427 +0,0 @@
|
||||
## YukiHookModulePrefs *- class*
|
||||
|
||||
```kotlin
|
||||
class YukiHookModulePrefs private constructor(private var context: Context?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 实现 Xposed 模块的数据存取,对接 `SharedPreferences` 和 `XSharedPreferences`。
|
||||
|
||||
在不同环境智能选择存取使用的对象。
|
||||
|
||||
!> 请注意此功能为实验性功能,仅在 LSPosed 环境测试通过,EdXposed 理论也可以使用但不再推荐。
|
||||
|
||||
使用 LSPosed 环境请在 `AndroidManifests.xml` 中将 `xposedminversion` 最低设置为 `93`。
|
||||
|
||||
详见 [New XSharedPreferences](https://github.com/LSPosed/LSPosed/wiki/New-XSharedPreferences#for-the-module)。
|
||||
|
||||
若你在按照规定配置后依然无法使用或出现文件权限错误问题,可以参考 [isEnableHookSharedPreferences](api/document?id=isenablehooksharedpreferences-field)。
|
||||
|
||||
未使用 LSPosed 环境请将你的模块 `API` 降至 `26` 以下,`YukiHookAPI` 将会尝试使用 `makeWorldReadable` 但仍有可能不成功。
|
||||
|
||||
太极请参阅 [文件权限/配置/XSharedPreference](https://taichi.cool/zh/doc/for-xposed-dev.html#文件权限-配置-xsharedpreference)。
|
||||
|
||||
!> 当你在 Xposed 模块中存取数据的时候 `context` 必须不能是空的。
|
||||
|
||||
若你正在使用 `PreferenceFragmentCompat`,请迁移到 `ModulePreferenceFragment` 以适配上述功能特性。
|
||||
|
||||
**可选配置**
|
||||
|
||||
若你不想将你的模块的 `xposedminversion` 最低设置为 `93`,你可以在 `AndroidManifest.xml` 中添加 `xposedsharedprefs` 来实现支持。
|
||||
|
||||
详见 [New XSharedPreferences](https://github.com/LSPosed/LSPosed/wiki/New-XSharedPreferences#for-the-module)。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```xml
|
||||
<meta-data
|
||||
android:name="xposedsharedprefs"
|
||||
android:value="true"/>
|
||||
```
|
||||
|
||||
### isXSharePrefsReadable *- field*
|
||||
|
||||
```kotlin
|
||||
val isXSharePrefsReadable: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.90` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取 `XSharedPreferences` 是否可读。
|
||||
|
||||
!> 只能在 (Xposed) 宿主环境中使用,模块环境中始终返回 false。
|
||||
|
||||
### isRunInNewXShareMode *- field*
|
||||
|
||||
```kotlin
|
||||
val isRunInNewXShareMode: Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.78` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取 `YukiHookModulePrefs` 是否正处于 EdXposed/LSPosed 的最高权限运行。
|
||||
|
||||
前提条件为当前 Xposed 模块已被激活。
|
||||
|
||||
!> 只能在模块环境中使用,(Xposed) 宿主环境中始终返回 false
|
||||
|
||||
### name *- method*
|
||||
|
||||
```kotlin
|
||||
fun name(name: String): YukiHookModulePrefs
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 自定义 Sp 存储名称。
|
||||
|
||||
**功能示例**
|
||||
|
||||
在 `Activity` 中的使用方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
modulePrefs("custom_name").getString("custom_key")
|
||||
```
|
||||
|
||||
在 (Xposed) 宿主环境 `PackageParam` 中的使用方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
prefs("custom_name").getString("custom_key")
|
||||
```
|
||||
|
||||
### direct *- method*
|
||||
|
||||
```kotlin
|
||||
fun direct(): YukiHookModulePrefs
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.5` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 忽略缓存直接读取键值。
|
||||
|
||||
无论是否开启 `YukiHookAPI.Configs.isEnableModulePrefsCache`。
|
||||
|
||||
仅在 `XSharedPreferences` 下生效。
|
||||
|
||||
### getString *- method*
|
||||
|
||||
```kotlin
|
||||
fun getString(key: String, value: String): String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取 `String` 键值。
|
||||
|
||||
### getStringSet *- method*
|
||||
|
||||
```kotlin
|
||||
fun getStringSet(key: String, value: Set<String>): Set<String>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.77` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取 `Set<String>` 键值。
|
||||
|
||||
### getBoolean *- method*
|
||||
|
||||
```kotlin
|
||||
fun getBoolean(key: String, value: Boolean): Boolean
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取 `Boolean` 键值。
|
||||
|
||||
### getInt *- method*
|
||||
|
||||
```kotlin
|
||||
fun getInt(key: String, value: Int): Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取 `Int` 键值。
|
||||
|
||||
### getLong *- method*
|
||||
|
||||
```kotlin
|
||||
fun getLong(key: String, value: Long): Long
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取 `Long` 键值。
|
||||
|
||||
### getFloat *- method*
|
||||
|
||||
```kotlin
|
||||
fun getFloat(key: String, value: Float): Float
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取 `Float` 键值。
|
||||
|
||||
### all *- method*
|
||||
|
||||
```kotlin
|
||||
fun all(): HashMap<String, Any?>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.77` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取全部存储的键值数据。
|
||||
|
||||
智能识别对应环境读取键值数据。
|
||||
|
||||
!> 每次调用都会获取实时的数据,不受缓存控制,请勿在高并发场景中使用。
|
||||
|
||||
### remove *- method*
|
||||
|
||||
```kotlin
|
||||
fun remove(key: String)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 移除全部包含 `key` 的存储数据。
|
||||
|
||||
!> 在 (Xposed) 宿主环境下只读,无法使用。
|
||||
|
||||
### remove *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T> remove(prefs: PrefsData<T>)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.67` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 移除 `PrefsData.key` 的存储数据。
|
||||
|
||||
!> 在 (Xposed) 宿主环境下只读,无法使用。
|
||||
|
||||
### clear *- method*
|
||||
|
||||
```kotlin
|
||||
fun clear()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.77` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 移除全部存储数据。
|
||||
|
||||
!> 在 (Xposed) 宿主环境下只读,无法使用。
|
||||
|
||||
### putString *- method*
|
||||
|
||||
```kotlin
|
||||
fun putString(key: String, value: String)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 存储 `String` 键值。
|
||||
|
||||
!> 在 (Xposed) 宿主环境下只读,无法使用。
|
||||
|
||||
### putStringSet *- method*
|
||||
|
||||
```kotlin
|
||||
fun putStringSet(key: String, value: Set<String>)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.77` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 存储 `Set<String>` 键值。
|
||||
|
||||
!> 在 (Xposed) 宿主环境下只读,无法使用。
|
||||
|
||||
### putBoolean *- method*
|
||||
|
||||
```kotlin
|
||||
fun putBoolean(key: String, value: Boolean)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 存储 `Boolean` 键值。
|
||||
|
||||
!> 在 (Xposed) 宿主环境下只读,无法使用。
|
||||
|
||||
### putInt *- method*
|
||||
|
||||
```kotlin
|
||||
fun putInt(key: String, value: Int)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 存储 `Int` 键值。
|
||||
|
||||
!> 在 (Xposed) 宿主环境下只读,无法使用。
|
||||
|
||||
### putLong *- method*
|
||||
|
||||
```kotlin
|
||||
fun putLong(key: String, value: Long)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 存储 `Long` 键值。
|
||||
|
||||
!> 在 (Xposed) 宿主环境下只读,无法使用。
|
||||
|
||||
### putFloat *- method*
|
||||
|
||||
```kotlin
|
||||
fun putFloat(key: String, value: Float)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 存储 `Float` 键值。
|
||||
|
||||
!> 在 (Xposed) 宿主环境下只读,无法使用。
|
||||
|
||||
### get *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T> get(prefs: PrefsData<T>, value: T): T
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.67` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 智能获取指定类型的键值。
|
||||
|
||||
### put *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T> put(prefs: PrefsData<T>, value: T)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.67` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 智能存储指定类型的键值。
|
||||
|
||||
!> 在 (Xposed) 宿主环境下只读,无法使用。
|
||||
|
||||
### clearCache *- method*
|
||||
|
||||
```kotlin
|
||||
fun clearCache()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.5` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 清除 `XSharedPreferences` 中缓存的键值数据。
|
||||
|
||||
无论是否开启 `YukiHookAPI.Configs.isEnableModulePrefsCache`。
|
||||
|
||||
调用此方法将清除当前存储的全部键值缓存。
|
||||
|
||||
下次将从 `XSharedPreferences` 重新读取。
|
||||
|
||||
在 (Xposed) 宿主环境中使用。
|
@@ -1,973 +0,0 @@
|
||||
## YukiMemberHookCreator *- class*
|
||||
|
||||
```kotlin
|
||||
class YukiMemberHookCreator(internal val packageParam: PackageParam, internal val hookClass: HookClass)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
对 `hookClass` 进行 inline 处理
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
修正拼写错误的 **Creater** 命名到 **Creator**
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `YukiHookAPI` 的 `Member` 核心 Hook 实现类。
|
||||
|
||||
### PRIORITY_DEFAULT *- field*
|
||||
|
||||
```kotlin
|
||||
val PRIORITY_DEFAULT: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 默认 Hook 回调优先级。
|
||||
|
||||
### PRIORITY_LOWEST *- field*
|
||||
|
||||
```kotlin
|
||||
val PRIORITY_LOWEST: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 延迟回调 Hook 方法结果。
|
||||
|
||||
### PRIORITY_HIGHEST *- field*
|
||||
|
||||
```kotlin
|
||||
val PRIORITY_HIGHEST: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 更快回调 Hook 方法结果。
|
||||
|
||||
### instanceClass *- field*
|
||||
|
||||
```kotlin
|
||||
val instanceClass: Class<*>
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.2` `修改`
|
||||
|
||||
~~`thisClass`~~ 更名为 `instanceClass`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 得到当前被 Hook 的 `Class`。
|
||||
|
||||
!> 不推荐直接使用,万一得不到 `Class` 对象则会无法处理异常导致崩溃。
|
||||
|
||||
### injectMember *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun injectMember(priority: Int, tag: String, initiate: MemberHookCreator.() -> Unit): MemberHookCreator.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
增加 `priority` Hook 优先级
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 注入要 Hook 的 `Method`、`Constructor`。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以注入任意 `Method` 与 `Constructor`,使用 `injectMember` 即可创建一个 `Hook` 对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
你还可以自定义 `tag`,方便你在调试的时候能够区分你的 `Hook` 对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember(tag = "KuriharaYuki") {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
你还可以自定义 `priority`,以控制当前 Hook 对象并列执行的优先级速度。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember(priority = PRIORITY_HIGHEST) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
### useDangerousOperation *- method*
|
||||
|
||||
```kotlin
|
||||
fun useDangerousOperation(option: String)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 允许 Hook 过程中的所有危险行为。
|
||||
|
||||
请在 `option` 中键入 `Yes do as I say!` 代表你同意允许所有危险行为。
|
||||
|
||||
你还需要在整个作用域中声明注解 `CauseProblemsApi` 以消除警告。
|
||||
|
||||
若你只需要 Hook `ClassLoader` 的 `loadClass` 方法,请参考 [ClassLoader.onLoadClass](api/document?id=classloaderonloadclass-ext-method)。
|
||||
|
||||
!> 若你不知道允许此功能会带来何种后果,请勿使用。
|
||||
|
||||
### MemberHookCreator *- class*
|
||||
|
||||
```kotlin
|
||||
inner class MemberHookCreator internal constructor(private val priority: Int, internal val tag: String)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
增加 `priority` Hook 优先级
|
||||
|
||||
`v1.0.81` `修改`
|
||||
|
||||
增加 `packageName` 当前 Hook 的 APP 包名
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
移除 `packageName`
|
||||
|
||||
修正拼写错误的 **Creater** 命名到 **Creator**
|
||||
|
||||
**功能描述**
|
||||
|
||||
> Hook 核心功能实现类,查找和处理需要 Hook 的 `Method`、`Constructor`。
|
||||
|
||||
#### ~~member *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `移除`
|
||||
|
||||
请转移到 `members`
|
||||
|
||||
#### members *- method*
|
||||
|
||||
```kotlin
|
||||
fun members(vararg member: Member?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 手动指定要 Hook 的 `Method`、`Constructor`。
|
||||
|
||||
!> 不建议使用此方法设置目标需要 Hook 的 `Member` 对象,你可以使用 `method` 或 `constructor` 方法。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以调用 `instanceClass` 来手动查询要 Hook 的 `Method`、`Constructor`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
members(instanceClass.getDeclaredMethod("test", StringType))
|
||||
beforeHook {}
|
||||
afterHook {}
|
||||
}
|
||||
```
|
||||
|
||||
同样地,你也可以传入一组 `Member` 同时进行 Hook。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
members(
|
||||
instanceClass.getDeclaredMethod("test1", StringType),
|
||||
instanceClass.getDeclaredMethod("test2", StringType),
|
||||
instanceClass.getDeclaredMethod("test3", StringType)
|
||||
)
|
||||
beforeHook {}
|
||||
afterHook {}
|
||||
}
|
||||
```
|
||||
|
||||
#### ~~allMethods *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `作废`
|
||||
|
||||
请使用 `method { name = /** name */ }.all()` 来取代它
|
||||
|
||||
#### ~~allConstructors *- method*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `作废`
|
||||
|
||||
请使用 `allMembers(MembersType.CONSTRUCTOR)` 来取代它
|
||||
|
||||
#### allMembers *- method*
|
||||
|
||||
```kotlin
|
||||
fun allMembers(type: MembersType)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查找并 Hook `hookClass` 中的全部 `Method`、`Constructor`。
|
||||
|
||||
!> 警告:无法准确处理每个 `Member` 的返回值和 `param`,建议使用 `method` or `constructor` 对每个 `Member` 单独 Hook。
|
||||
|
||||
#### method *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun method(initiate: MethodConditions): MethodFinder.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查找当前 `Class` 需要 Hook 的 `Method` 。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可参考 [MethodFinder](#methodfinder-class) 查看详细用法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
param(StringType)
|
||||
returnType = UnitType
|
||||
}
|
||||
beforeHook {}
|
||||
afterHook {}
|
||||
}
|
||||
```
|
||||
|
||||
若想 Hook 当前查询 `method { ... }` 条件的全部结果,你只需要在最后加入 `all` 即可。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
paramCount(1..5)
|
||||
}.all()
|
||||
beforeHook {}
|
||||
afterHook {}
|
||||
}
|
||||
```
|
||||
|
||||
此时 `beforeHook` 与 `afterHook` 会在每个查询到的结果中多次回调 Hook 方法体。
|
||||
|
||||
!> 若没有 `all`,默认只会 Hook 当前条件查询到的数组下标结果第一位。
|
||||
|
||||
#### constructor *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun constructor(initiate: ConstructorConditions): ConstructorFinder.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 查找当前 `Class` 需要 Hook 的 `Constructor`。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可参考 [ConstructorFinder](#constructorfinder-class) 查看详细用法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
constructor { param(StringType) }
|
||||
beforeHook {}
|
||||
afterHook {}
|
||||
}
|
||||
```
|
||||
|
||||
若想 Hook 当前查询 `constructor { ... }` 条件的全部结果,你只需要在最后加入 `all` 即可。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
constructor { paramCount(1..5) }.all()
|
||||
beforeHook {}
|
||||
afterHook {}
|
||||
}
|
||||
```
|
||||
|
||||
此时 `beforeHook` 与 `afterHook` 会在每个查询到的结果中多次回调 Hook 方法体。
|
||||
|
||||
!> 若没有 `all`,默认只会 Hook 当前条件查询到的数组下标结果第一位。
|
||||
|
||||
#### HookParam.field *- i-ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun HookParam.field(initiate: FieldConditions): FieldFinder.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 使用当前 `hookClass` 查找并得到 `Field`。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可参考 [FieldFinder](#fieldfinder-class) 查看详细用法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
param(StringType)
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
// 这里不需要再调用 instanceClass.field 进行查询
|
||||
field {
|
||||
name = "isSweet"
|
||||
type = BooleanType
|
||||
}.get(instance).setTrue()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### HookParam.method *- i-ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun HookParam.method(initiate: MethodConditions): MethodFinder.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.2` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 使用当前 `hookClass` 查找并得到 `Method` 。
|
||||
|
||||
#### HookParam.constructor *- i-ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun HookParam.constructor(initiate: ConstructorConditions): ConstructorFinder.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.2` `添加`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 使用当前 `hookClass` 查找并得到 `Constructor`。
|
||||
|
||||
#### HookParam.injectMember *- i-ext-method*
|
||||
|
||||
```kotlin
|
||||
inline fun HookParam.injectMember(priority: Int, tag: String, initiate: MemberHookCreator.() -> Unit): MemberHookCreator.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.88` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 注入要 Hook 的 `Method`、`Constructor` (嵌套 Hook)。
|
||||
|
||||
#### beforeHook *- method*
|
||||
|
||||
```kotlin
|
||||
fun beforeHook(initiate: HookParam.() -> Unit): HookCallback
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
新增 `HookCallback` 返回类型
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 在 `Member` 执行完成前 Hook。
|
||||
|
||||
#### afterHook *- method*
|
||||
|
||||
```kotlin
|
||||
fun afterHook(initiate: HookParam.() -> Unit): HookCallback
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
新增 `HookCallback` 返回类型
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 在 `Member` 执行完成后 Hook。
|
||||
|
||||
#### replaceAny *- method*
|
||||
|
||||
```kotlin
|
||||
fun replaceAny(initiate: HookParam.() -> Any?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 拦截并替换此 `Member` 内容,给出返回值。
|
||||
|
||||
#### replaceUnit *- method*
|
||||
|
||||
```kotlin
|
||||
fun replaceUnit(initiate: HookParam.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 拦截并替换此 `Member` 内容,没有返回值,可以称为 `Void`。
|
||||
|
||||
#### replaceTo *- method*
|
||||
|
||||
```kotlin
|
||||
fun replaceTo(any: Any?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 拦截并替 `Member` 返回值。
|
||||
|
||||
#### replaceToTrue *- method*
|
||||
|
||||
```kotlin
|
||||
fun replaceToTrue()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 拦截并替换 `Member` 返回值为 `true`。
|
||||
|
||||
!> 确保替换 `Member` 的返回对象为 `Boolean`。
|
||||
|
||||
#### replaceToFalse *- method*
|
||||
|
||||
```kotlin
|
||||
fun replaceToFalse()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 拦截并替换 `Member` 返回值为 `false`。
|
||||
|
||||
!> 确保替换 `Member` 的返回对象为 `Boolean`。
|
||||
|
||||
#### intercept *- method*
|
||||
|
||||
```kotlin
|
||||
fun intercept()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 拦截此 `Member` 。
|
||||
|
||||
!> 这将会禁止此 `Member` 执行并返回 `null`。
|
||||
|
||||
!> 注意:例如 `Int`、`Long`、`Boolean` 常量返回值的 `Member` 一旦被设置为 null 可能会造成 Hook APP 抛出异常。
|
||||
|
||||
#### removeSelf *- method*
|
||||
|
||||
```kotlin
|
||||
fun removeSelf(result: (Boolean) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 移除当前注入的 Hook `Method`、`Constructor` (解除 Hook)。
|
||||
|
||||
!> 你只能在 Hook 回调方法中使用此功能。
|
||||
|
||||
#### HookCallback *- class*
|
||||
|
||||
```kotlin
|
||||
inner class HookCallback internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> Hook 方法体回调实现类。
|
||||
|
||||
##### onFailureThrowToApp *- method*
|
||||
|
||||
```kotlin
|
||||
fun onFailureThrowToApp()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 当回调方法体内发生异常时将异常抛出给当前 Hook APP。
|
||||
|
||||
#### Result *- class*
|
||||
|
||||
```kotlin
|
||||
inner class Result internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听 Hook 结果实现类。
|
||||
|
||||
##### result *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun result(initiate: Result.() -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
`v1.0.5` `修改`
|
||||
|
||||
~~`failures`~~ 修改为 `result`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建监听失败事件方法体。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以使用此方法为 `Result` 类创建 `lambda` 方法体。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}.result {
|
||||
onHooked {}
|
||||
onAlreadyHooked {}
|
||||
ignoredConductFailure()
|
||||
onHookingFailure {}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
##### by *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun by(condition: () -> Boolean): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.5` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 添加执行 Hook 需要满足的条件,不满足条件将直接停止 Hook。
|
||||
|
||||
##### onHooked *- method*
|
||||
|
||||
```kotlin
|
||||
fun onHooked(result: (Member) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听 `members` Hook 成功的回调方法。
|
||||
|
||||
在首次 Hook 成功后回调。
|
||||
|
||||
在重复 Hook 时会回调 `onAlreadyHooked`。
|
||||
|
||||
##### onAlreadyHooked *- method*
|
||||
|
||||
```kotlin
|
||||
fun onAlreadyHooked(result: (Member) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.89` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听 `members` 重复 Hook 的回调方法。
|
||||
|
||||
!> 同一个 `hookClass` 中的同一个 `members` 不会被 API 重复 Hook,若由于各种原因重复 Hook 会回调此方法。
|
||||
|
||||
##### onNoSuchMemberFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun onNoSuchMemberFailure(result: (Throwable) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.5` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听 `members` 不存在发生错误的回调方法。
|
||||
|
||||
##### onConductFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun onConductFailure(result: (HookParam, Throwable) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听 Hook 进行过程中发生错误的回调方法。
|
||||
|
||||
##### onHookingFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun onHookingFailure(result: (Throwable) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听 Hook 开始时发生的错误的回调方法。
|
||||
|
||||
##### onAllFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun onAllFailure(result: (Throwable) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听全部 Hook 过程发生错误的回调方法。
|
||||
|
||||
##### ignoredNoSuchMemberFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun ignoredNoSuchMemberFailure(): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.5` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 忽略 `members` 不存在发生的错误。
|
||||
|
||||
##### ignoredConductFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun ignoredConductFailure(): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 忽略 Hook 进行过程中发生的错误。
|
||||
|
||||
##### ignoredHookingFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun ignoredHookingFailure(): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 忽略 Hook 开始时发生的错误。
|
||||
|
||||
##### ignoredAllFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun ignoredAllFailure(): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 忽略全部 Hook 过程发生的错误。
|
||||
|
||||
##### remove *- method*
|
||||
|
||||
```kotlin
|
||||
fun remove(result: (Boolean) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 移除当前注入的 Hook `Method`、`Constructor` (解除 Hook)。
|
||||
|
||||
!> 你只能在 Hook 成功后才能解除 Hook,可监听 `onHooked` 事件。
|
||||
|
||||
### Result *- class*
|
||||
|
||||
```kotlin
|
||||
inner class Result internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.3` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听全部 Hook 结果实现类。
|
||||
|
||||
#### result *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun result(initiate: Result.() -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.3` `新增`
|
||||
|
||||
`v1.0.5` `修改`
|
||||
|
||||
~~`failures`~~ 修改为 `result`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建监听事件方法体。
|
||||
|
||||
#### by *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun by(condition: () -> Boolean): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.5` `新增`
|
||||
|
||||
`v1.0.80` `修改`
|
||||
|
||||
将方法体进行 inline
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 添加执行 Hook 需要满足的条件,不满足条件将直接停止 Hook。
|
||||
|
||||
#### onPrepareHook *- method*
|
||||
|
||||
```kotlin
|
||||
fun onPrepareHook(callback: () -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.70` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听 `hookClass` 存在时准备开始 Hook 的操作。
|
||||
|
||||
#### onHookClassNotFoundFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun onHookClassNotFoundFailure(result: (Throwable) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.3` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听 `hookClass` 找不到时发生错误的回调方法。
|
||||
|
||||
#### ignoredHookClassNotFoundFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun ignoredHookClassNotFoundFailure(): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.3` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 忽略 `hookClass` 找不到时出现的错误。
|
@@ -1,29 +0,0 @@
|
||||
## YukiModuleResources *- class*
|
||||
|
||||
```kotlin
|
||||
class YukiModuleResources private constructor(private val baseInstance: XModuleResources) : Resources
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 对接 `XModuleResources` 的中间层实例。
|
||||
|
||||
### fwd *- method*
|
||||
|
||||
```kotlin
|
||||
fun fwd(resId: Int): YukiResForwarder
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 对接 `XModuleResources.fwd` 方法。
|
||||
|
||||
创建 `YukiResForwarder` 与 `XResForwarder` 实例。
|
@@ -1,51 +0,0 @@
|
||||
## YukiResForwarder *- class*
|
||||
|
||||
```kotlin
|
||||
class YukiResForwarder private constructor(private val baseInstance: XResForwarder)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 对接 `XResForwarder` 的中间层实例。
|
||||
|
||||
### ~~instance *- field*~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
`v1.1.0` `作废`
|
||||
|
||||
不再对外公开 `instance` 参数
|
||||
|
||||
### id *- field*
|
||||
|
||||
```kotlin
|
||||
val id: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前 APP 的 Resources Id。
|
||||
|
||||
### resources *- field*
|
||||
|
||||
```kotlin
|
||||
val resources: Resources
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获得当前 APP 的 Resources。
|
@@ -1,77 +0,0 @@
|
||||
## YukiResources *- class*
|
||||
|
||||
```kotlin
|
||||
class YukiResources private constructor(private val baseInstance: XResources) : Resources
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 对接 `XResources` 的中间层实例。
|
||||
|
||||
### LayoutInflatedParam *- class*
|
||||
|
||||
```kotlin
|
||||
class LayoutInflatedParam(internal val baseParam: XC_LayoutInflated.LayoutInflatedParam)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 装载 Hook APP 的目标布局 Resources 实现类。
|
||||
|
||||
#### variantName *- field*
|
||||
|
||||
```kotlin
|
||||
val variantName: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前被 Hook 的布局装载目录名称。
|
||||
|
||||
例如:`layout`、`layout-land`、`layout-sw600dp`。
|
||||
|
||||
#### currentView *- field*
|
||||
|
||||
```kotlin
|
||||
val currentView: View
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 获取当前被 Hook 的布局实例。
|
||||
|
||||
#### findViewByIdentifier *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T : View> View.findViewByIdentifier(name: String): T?
|
||||
```
|
||||
|
||||
```kotlin
|
||||
inline fun <reified T : View> findViewByIdentifier(name: String): T?
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 使用 Identifier 查找 Hook APP 指定 Id 的 `View`。
|
||||
|
||||
扩展方法可以使用 Identifier 查找 Hook APP 当前装载布局中指定 Id 的 `View`。
|
@@ -1,593 +0,0 @@
|
||||
## YukiResourcesHookCreator *- class*
|
||||
|
||||
```kotlin
|
||||
class YukiResourcesHookCreator(internal val packageParam: PackageParam, internal val hookResources: HookResources)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
修正拼写错误的 **Creater** 命名到 **Creator**
|
||||
|
||||
**功能描述**
|
||||
|
||||
> `YukiHookAPI` 的 `Resources` 核心 Hook 实现类。
|
||||
|
||||
### injectResource *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun injectResource(tag: String, initiate: ResourceHookCreator.() -> Unit): ResourceHookCreator.Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 注入要 Hook 的 Resources。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以注入任意 Resources,使用 `injectResource` 即可创建一个 `Hook` 对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
你还可以自定义 `tag`,方便你在调试的时候能够区分你的 `Hook` 对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource(tag = "KuriharaYuki") {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
### ResourcesHookCreator *- class*
|
||||
|
||||
```kotlin
|
||||
inner class ResourcesHookCreator internal constructor(private val tag: String)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
`v1.1.0` `修改`
|
||||
|
||||
移除 `packageName`
|
||||
|
||||
修正拼写错误的 **Creater** 命名到 **Creator**
|
||||
|
||||
**功能描述**
|
||||
|
||||
> Hook 核心功能实现类。
|
||||
|
||||
查找和处理需要 Hook 的 Resources。
|
||||
|
||||
#### resourceId *- field*
|
||||
|
||||
```kotlin
|
||||
var resourceId: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 直接设置需要替换的 Resources Id。
|
||||
|
||||
!> 不建议使用此方法设置目标需要 Hook 的 Resources Id,你可以使用 `conditions` 方法。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以直接设置并指定目标 Hook APP 的 Resources Id。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
resourceId = 0x7f060001.toInt()
|
||||
replaceTo(...)
|
||||
}
|
||||
```
|
||||
|
||||
#### conditions *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun conditions(initiate: ConditionFinder.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 查找条件。
|
||||
|
||||
若你设置了 `resourceId` 则此方法将不会被使用。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可参考 [ConditionFinder](#conditionfinder-class) 查看详细用法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "test_string"
|
||||
string()
|
||||
}
|
||||
replaceTo(...)
|
||||
}
|
||||
```
|
||||
|
||||
#### replaceTo *- method*
|
||||
|
||||
```kotlin
|
||||
fun replaceTo(any: Any)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 替换指定 Resources 为指定的值。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以替换找到的 Resources 为你想要的值,可以是 `String`、`Drawable` 等。
|
||||
|
||||
比如我们要替换一个找到的字符串 Resources。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "test_string"
|
||||
string()
|
||||
}
|
||||
replaceTo("replace string")
|
||||
}
|
||||
```
|
||||
|
||||
或是替换为一个 `Drawable`,你无需对目标 Resources 实现 `fwd` 方法或 `DrawableLoader`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "test_drawable"
|
||||
drawable()
|
||||
}
|
||||
replaceTo(ColorDrawable(Color.RED))
|
||||
}
|
||||
```
|
||||
|
||||
#### replaceToTrue *- method*
|
||||
|
||||
```kotlin
|
||||
fun replaceToTrue()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 替换指定 Resources 为 `true`。
|
||||
|
||||
!> 确保目标替换 Resources 的类型为 `Boolean`。
|
||||
|
||||
#### replaceToFalse *- method*
|
||||
|
||||
```kotlin
|
||||
fun replaceToFalse()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 替换指定 Resources 为 `false`。
|
||||
|
||||
!> 确保目标替换 Resources 的类型为 `Boolean`。
|
||||
|
||||
#### replaceToModuleResource *- method*
|
||||
|
||||
```kotlin
|
||||
fun replaceToModuleResource(resId: Int)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 替换为当前 Xposed 模块的 Resources。
|
||||
|
||||
你可以直接使用模块的 `R.string.xxx`、`R.mipmap.xxx`、`R.drawable.xxx` 替换 Hook APP 的 Resources。
|
||||
|
||||
**功能示例**
|
||||
|
||||
使用此方法可非常方便地使用当前模块的 Resources 去替换目标 Hook APP 的 Resources。
|
||||
|
||||
这个过程你无需对目标 Resources 实现 `fwd` 方法。
|
||||
|
||||
比如我们要替换一个字符串。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "test_string"
|
||||
string()
|
||||
}
|
||||
replaceToModuleResource(R.string.module_string)
|
||||
}
|
||||
```
|
||||
|
||||
还可以替换一些复杂的 Resources,比如 `xml` 创建的 `drawable`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "test_drawable"
|
||||
drawable()
|
||||
}
|
||||
replaceToModuleResource(R.drawable.module_drawable)
|
||||
}
|
||||
```
|
||||
|
||||
#### injectAsLayout *- method*
|
||||
|
||||
```kotlin
|
||||
fun injectAsLayout(initiate: YukiResources.LayoutInflatedParam.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 作为装载的布局注入。
|
||||
|
||||
**功能示例**
|
||||
|
||||
你可以直接注入一个布局监听并修改它的内部 `View`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "activity_main"
|
||||
layout()
|
||||
}
|
||||
injectAsLayout {
|
||||
findViewByIdentifier<View>(name = "test_view")?.isVisible = false
|
||||
findViewByIdentifier<TextView>(name = "test_text_view")?.text = "Hook this"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
你还可以通过 `currentView` 拿到 `Context`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "activity_main"
|
||||
layout()
|
||||
}
|
||||
injectAsLayout {
|
||||
Toast.makeText(currentView.context, "Hook this", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### ConditionFinder *- class*
|
||||
|
||||
```kotlin
|
||||
inner class ConditionFinder internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> Resources 查找条件实现类。
|
||||
|
||||
##### name *- field*
|
||||
|
||||
```kotlin
|
||||
var name: String
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 名称。
|
||||
|
||||
##### anim *- method*
|
||||
|
||||
```kotlin
|
||||
fun anim()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为动画。
|
||||
|
||||
##### animator *- method*
|
||||
|
||||
```kotlin
|
||||
fun animator()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为属性动画。
|
||||
|
||||
##### bool *- method*
|
||||
|
||||
```kotlin
|
||||
fun bool()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为布朗(Boolean)。
|
||||
|
||||
##### color *- method*
|
||||
|
||||
```kotlin
|
||||
fun color()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为颜色(Color)。
|
||||
|
||||
##### dimen *- method*
|
||||
|
||||
```kotlin
|
||||
fun dimen()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为尺寸(Dimention)。
|
||||
|
||||
##### drawable *- method*
|
||||
|
||||
```kotlin
|
||||
fun drawable()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为 Drawable。
|
||||
|
||||
##### integer *- method*
|
||||
|
||||
```kotlin
|
||||
fun integer()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为整型(Integer)。
|
||||
|
||||
##### layout *- method*
|
||||
|
||||
```kotlin
|
||||
fun layout()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为布局(Layout)。
|
||||
|
||||
##### plurals *- method*
|
||||
|
||||
```kotlin
|
||||
fun plurals()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为 Plurals。
|
||||
|
||||
##### string *- method*
|
||||
|
||||
```kotlin
|
||||
fun string()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为字符串(String)。
|
||||
|
||||
##### xml *- method*
|
||||
|
||||
```kotlin
|
||||
fun xml()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为 Xml。
|
||||
|
||||
##### mipmap *- method*
|
||||
|
||||
```kotlin
|
||||
fun mipmap()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为位图(Mipmap)。
|
||||
|
||||
##### array *- method*
|
||||
|
||||
```kotlin
|
||||
fun array()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.1.0` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 Resources 类型为数组(Array)。
|
||||
|
||||
#### Result *- class*
|
||||
|
||||
```kotlin
|
||||
inner class Result internal constructor()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听全部 Hook 结果实现类,可在这里处理失败事件监听。
|
||||
|
||||
##### result *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun result(initiate: Result.() -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 创建监听事件方法体。
|
||||
|
||||
##### by *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun by(condition: () -> Boolean): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 添加执行 Hook 需要满足的条件,不满足条件将直接停止 Hook。
|
||||
|
||||
#### onHookingFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun onHookingFailure(result: (Throwable) -> Unit): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 监听 Hook 过程发生错误的回调方法。
|
||||
|
||||
#### ignoredHookingFailure *- method*
|
||||
|
||||
```kotlin
|
||||
fun ignoredHookingFailure(): Result
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 忽略 Hook 过程出现的错误。
|
@@ -1,69 +0,0 @@
|
||||
## YukiXposedEvent *- object*
|
||||
|
||||
```kotlin
|
||||
object YukiXposedEvent
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `添加`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 实现对原生 Xposed API 的装载事件监听。
|
||||
|
||||
### events *- method*
|
||||
|
||||
```kotlin
|
||||
inline fun events(initiate: YukiXposedEvent.() -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 对 `YukiXposedEvent` 创建一个方法体。
|
||||
|
||||
### onInitZygote *- method*
|
||||
|
||||
```kotlin
|
||||
fun onInitZygote(result: (StartupParam) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 initZygote 事件监听。
|
||||
|
||||
### onHandleLoadPackage *- method*
|
||||
|
||||
```kotlin
|
||||
fun onHandleLoadPackage(result: (LoadPackageParam) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 handleLoadPackage 事件监听。
|
||||
|
||||
### onHandleInitPackageResources *- method*
|
||||
|
||||
```kotlin
|
||||
fun onHandleInitPackageResources(result: (InitPackageResourcesParam) -> Unit)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.80` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置 handleInitPackageResources 事件监听。
|
@@ -1,366 +0,0 @@
|
||||
# API 基本配置
|
||||
|
||||
> 这里介绍了 `YukiHookAPI` 的基本配置方法。
|
||||
|
||||
## 功能配置
|
||||
|
||||
> 无论是 [作为 Xposed 模块使用](config/xposed-using) 还是 [作为 Hook API 使用](config/api-using),你都可以在 API 装载之前或装载过程中对 `YukiHookAPI` 进行配置。
|
||||
|
||||
### configs 方法
|
||||
|
||||
```kotlin
|
||||
fun configs(initiate: Configs.() -> Unit)
|
||||
```
|
||||
|
||||
`configs` 方法对 `Configs` 类实现了一个 `lambda` 方法体,你可以轻松地调用它进行配置。
|
||||
|
||||
你可以 [点击这里](api/document?id=configs-method) 查看有关用法的说明和示例。
|
||||
|
||||
## Hooker 配置
|
||||
|
||||
> 一个 Xposed 模块或 Hook API 最重要的地方就是 Hooker 的创建与使用,`YukiHookAPI` 提供了两种使用方法。
|
||||
|
||||
### 通过 lambda 创建
|
||||
|
||||
> 这种方案是最简单的,如果你的模块功能不多,代码数量不大,不需要进行分类处理,推荐使用这种方式进行创建。
|
||||
|
||||
#### encase 方法
|
||||
|
||||
```kotlin
|
||||
fun encase(initiate: PackageParam.() -> Unit)
|
||||
```
|
||||
|
||||
`encase` 方法是 Hook 一切生命的开始,在一个模块或一个 Hook 过程中,`encase` 方法只能作用一次,用于创建 Hooker。
|
||||
|
||||
`PackageParam` 为宿主(目标 APP)的重要实例对象,通过 `PackageParam` 来实现对当前 Hook 作用对象的全部 Hook 操作。
|
||||
|
||||
你可以 [点击这里](api/document?id=packageparam-class) 了解其中的详细用法。
|
||||
|
||||
`encase` 方法可以在 `onHook` 方法中使用两种方案创建。
|
||||
|
||||
> 示例代码 1
|
||||
|
||||
```kotlin
|
||||
YukiHookAPI.encase {
|
||||
loadApp(name = "com.example.demo") {
|
||||
findClass(name = "$packageName.DemoClass").hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 示例代码 2
|
||||
|
||||
```kotlin
|
||||
encase {
|
||||
loadApp(name = "com.example.demo") {
|
||||
findClass(name = "$packageName.DemoClass").hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在 `encase` 方法中进行你的 Hook 操作。
|
||||
|
||||
### 通过自定义 Hooker 创建
|
||||
|
||||
> 这种方案更加适用于大型项目,例如需要对 Hooker 进行分类或对 Hook 的作用对象进行分类。
|
||||
|
||||
#### encase 方法
|
||||
|
||||
```kotlin
|
||||
fun encase(vararg hooker: YukiBaseHooker)
|
||||
```
|
||||
|
||||
同样为 `encase` 方法,这里的方法可变数组参数 `hooker` 为创建入口提供了一个对象,你可以将所有继承于 `YukiBaseHooker` 的 Hooker 一次性进行装载。
|
||||
|
||||
#### YukiBaseHooker 用法
|
||||
|
||||
`YukiBaseHooker` 继承于 `PackageParam`,你需要将你的子 Hooker 继承于 `YukiBaseHooker`。
|
||||
|
||||
若要了解更多可 [点击这里](api/document?id=yukibasehooker-class) 进行查看。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
object CustomHooker : YukiBaseHooker() {
|
||||
|
||||
override fun onHook() {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
子 Hooker **建议使用**单例 `object` 创建,你也可以使用 `class` 但不推荐。
|
||||
|
||||
!> 你无需再在继承于 `YukiBaseHooker` 的 `onHook` 方法中重新调用 `encase`,这是错误的,你应该直接开始编写你的 Hook 代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
object CustomHooker : YukiBaseHooker() {
|
||||
|
||||
override fun onHook() {
|
||||
loadApp(name = "com.example.demo1") {
|
||||
findClass(name = "$packageName.DemoClass").hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
loadApp(name = "com.example.demo2") {
|
||||
findClass(name = "$packageName.CustomClass").hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
作为子 Hooker 使用,你还可以在外部调用 `loadApp` 方法,然后在内部直接开始 Hook。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() = encase {
|
||||
loadApp(name = "com.example.demo", ChildCustomHooker)
|
||||
}
|
||||
}
|
||||
|
||||
object ChildCustomHooker : YukiBaseHooker() {
|
||||
|
||||
override fun onHook() {
|
||||
findClass(name = "$packageName.DemoClass").hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
你可以使用 `loadHooker` 方法在子 Hooker 中多层装载另一个 Hooker,请按照你的喜好进行即可。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
object FirstHooker : YukiBaseHooker() {
|
||||
|
||||
override fun onHook() {
|
||||
findClass(name = "$packageName.DemoClass").hook {
|
||||
// Your code here.
|
||||
}
|
||||
loadHooker(SecondHooker)
|
||||
loadHooker(ThirdHooker)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
搭建完全部 Hooker 后,你就可以在你的 `HookEntryClass` 入口类中的 `onHook` 方法中装载你的 Hooker 了。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() =
|
||||
YukiHookAPI.encase(FirstHooker, SecondHooker, ThirdHooker ...)
|
||||
}
|
||||
```
|
||||
|
||||
当然,我们同样可以对其进行简写。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() = encase(FirstHooker, SecondHooker, ThirdHooker ...)
|
||||
}
|
||||
```
|
||||
|
||||
### 扩展特性
|
||||
|
||||
如果你当前使用的 Hook Framework 支持并启用了资源钩子(Resources Hook)功能,你现在可以直接在 `encase` 中创建 Resources Hook。
|
||||
|
||||
你完全不需要与之前在使用 Xposed API 那样区分 `initZygote`、`handleLoadPackage`、`handleInitPackageResources` 方法来执行不同的功能。
|
||||
|
||||
在 `YukiHookAPI` 中,这些功能**是无缝的**。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
encase {
|
||||
loadApp(name = "com.example.demo") {
|
||||
findClass(name = "$packageName.DemoClass").hook {
|
||||
// Your code here.
|
||||
}
|
||||
// 创建一个 Resources Hook (固定用法)
|
||||
resources().hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
你还可以同时使用 `loadZygote` 方法来装载新的进程被 fork 后的第一个事件 `initZygote`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
encase {
|
||||
loadZygote {
|
||||
ActivityClass.hook {
|
||||
// Your code here.
|
||||
}
|
||||
// 在 Zygote 中创建 Resources Hook
|
||||
resources().hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
loadApp(name = "com.example.demo") {
|
||||
findClass(name = "$packageName.DemoClass").hook {
|
||||
// Your code here.
|
||||
}
|
||||
// 在 APP 中创建 Resources Hook
|
||||
resources().hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
!> 无论使用 `encase` 创建 `lambda` 方法体还是直接使用 Hooker 形式,你都不应该直接在首个 `onHook` 事件中直接装载 Hooker 或直接开始 Hook。
|
||||
|
||||
直接装载 Hooker 或直接开始 Hook 是错误的,`encase` 事件在被 Hook Framework 装载后,会经历三次回调。
|
||||
|
||||
- 装载 `initZygote` → `encase`
|
||||
|
||||
- 装载 `handleLoadPackage` → `encase`
|
||||
|
||||
- 装载 `handleInitPackageResources` → `encase`
|
||||
|
||||
在这个过程中,你需要使用 `loadApp`、`loadSystem`、`loadZygote` 来区分每一次装载代码的调用域,否则你的代码就会被<u>**多次执行造成错误**</u>。
|
||||
|
||||
下面是两个**错误**示例。
|
||||
|
||||
> 示例代码 1
|
||||
|
||||
```kotlin
|
||||
encase {
|
||||
// ❗错误的使用方法,不能直接装载 Hooker
|
||||
loadHooker(CustomHooker)
|
||||
// ❗错误的使用方法,不能直接开始 Hook
|
||||
findClass(name = "com.example.demo.DemoClass").hook {
|
||||
// ...
|
||||
}
|
||||
// ❗错误的使用方法,不能直接开始 Hook
|
||||
resources().hook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 示例代码 2
|
||||
|
||||
```kotlin
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() {
|
||||
encase(CustomHooker)
|
||||
}
|
||||
}
|
||||
|
||||
object CustomHooker : YukiBaseHooker() {
|
||||
|
||||
override fun onHook() {
|
||||
// ❗错误的使用方法,由于外层没有任何判断对象,不能直接开始 Hook
|
||||
findClass(name = "com.example.demo.DemoClass").hook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
下面是上述错误示例的**正确**示例。
|
||||
|
||||
> 示例代码 1
|
||||
|
||||
```kotlin
|
||||
encase {
|
||||
// ✅ 正确的使用方法,在 Zygote 中装载
|
||||
loadZygote(CustomHooker)
|
||||
// ✅ 正确的使用方法,在 Zygote 中装载
|
||||
loadZygote {
|
||||
// ✅ 正确的使用方法,在 Zygote 内 Hook
|
||||
resources().hook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
// ✅ 正确的使用方法,使用 APP 作用域装载
|
||||
loadApp(/** name 参数可选 */, hooker = CustomHooker)
|
||||
// ✅ 正确的使用方法,判断 APP 作用域后再装载 Hooker
|
||||
loadApp(/** name 参数可选 */) {
|
||||
loadHooker(CustomHooker)
|
||||
// ✅ 正确的使用方法,在 APP 作用域内 Hook
|
||||
findClass(name = "com.example.demo.DemoClass").hook {
|
||||
// ...
|
||||
}
|
||||
// ✅ 正确的使用方法,在 APP 作用域内 Hook
|
||||
resources().hook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 示例代码 2
|
||||
|
||||
```kotlin
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() {
|
||||
encase(CustomHooker)
|
||||
}
|
||||
}
|
||||
|
||||
object CustomHooker : YukiBaseHooker() {
|
||||
|
||||
override fun onHook() {
|
||||
// ✅ 正确的使用方法,由于外层没有任何判断对象,需要判断 APP 作用域后再进行 Hook
|
||||
loadApp(/** name 参数可选 */) {
|
||||
findClass(name = "com.example.demo.DemoClass").hook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 作为 Hook API 使用需要注意的地方
|
||||
|
||||
若你作为 Hook API 使用,那么你只需要在入口处对 `encase` 方法进行区分。
|
||||
|
||||
!> `encase` 方法对作为 Hook API 使用提供了两个完全一样的方法,但是比前两者仅多出一个参数 `baseContext`。
|
||||
|
||||
> 方法 1
|
||||
|
||||
```kotlin
|
||||
fun encase(baseContext: Context?, initiate: PackageParam.() -> Unit)
|
||||
```
|
||||
> 方法 2
|
||||
|
||||
```kotlin
|
||||
fun encase(baseContext: Context?, vararg hooker: YukiBaseHooker)
|
||||
```
|
||||
|
||||
此处的 `baseContext` 只需填入你在 `attachBaseContext` 处得到的 `Context` 即可,其它用法与上述内容完全一致。
|
||||
|
||||
!> 切勿以 Xposed 方式使用 `encase` 方法而漏掉 `baseContext` 参数,否则你的 Hook 将完全不工作。
|
||||
|
||||
!> Resources Hook 功能不支持作为 Hook API 使用。
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](config/api-exception.md)
|
File diff suppressed because it is too large
Load Diff
@@ -1,111 +0,0 @@
|
||||
# 作为 Hook API 使用的相关配置
|
||||
|
||||
> 作为 Hook API 通常为做自身 APP 热更新或功能需要以及产品测试的 Hook 操作。
|
||||
|
||||
## 依赖配置
|
||||
|
||||
你只需要集成 `com.highcapable.yukihookapi:api` 依赖即可。
|
||||
|
||||
然后请集成你目标使用的 `Hook Framework` 依赖。
|
||||
|
||||
## 入口配置
|
||||
|
||||
创建你的自定义 `Application`。
|
||||
|
||||
在 `attachBaseContext` 中添加 `YukiHookAPI.encase` 方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class MyApplication : Application() {
|
||||
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
// 装载 Hook Framework
|
||||
//
|
||||
// Your code here.
|
||||
//
|
||||
// 配置 YukiHookAPI
|
||||
YukiHookApi.configs {
|
||||
// Your code here.
|
||||
}
|
||||
// 装载 YukiHookAPI
|
||||
YukiHookAPI.encase(base) {
|
||||
// Your code here.
|
||||
}
|
||||
super.attachBaseContext(base)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这样,你就完成了 API 的相关配置。
|
||||
|
||||
你可以 [点击这里](config/api-example?id=作为-hook-api-使用需要注意的地方) 查看异同点和注意事项。
|
||||
|
||||
!> 你不能再使用 `loadApp` 进行包装,可直接开始编写你的 Hook 代码。
|
||||
|
||||
## Hook Framework
|
||||
|
||||
> 这里给出了一些较高使用率的 `Hook Framework` 如何对接 `YukiHookAPI` 的相关方式。
|
||||
|
||||
### [Pine](https://github.com/canyie/pine)
|
||||
|
||||
> **所需 Xposed API 依赖** `top.canyie.pine:xposed`
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
// 装载 Pine
|
||||
PineConfig.debug = true
|
||||
PineConfig.debuggable = BuildConfig.DEBUG
|
||||
// 装载 YukiHookAPI
|
||||
YukiHookAPI.encase(base) {
|
||||
// Your code here.
|
||||
}
|
||||
super.attachBaseContext(base)
|
||||
}
|
||||
```
|
||||
|
||||
### [SandHook](https://github.com/asLody/SandHook)
|
||||
|
||||
> **所需 Xposed API 依赖** `com.swift.sandhook:xposedcompat` 或 `com.swift.sandhook:xposedcompat_new`
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
// 装载 SandHook
|
||||
SandHookConfig.DEBUG = BuildConfig.DEBUG
|
||||
XposedCompat.cacheDir = base?.cacheDir
|
||||
XposedCompat.context = base
|
||||
XposedCompat.classLoader = javaClass.classLoader
|
||||
XposedCompat.isFirstApplication = base?.processName == base?.packageName
|
||||
// 装载 YukiHookAPI
|
||||
YukiHookAPI.encase(base) {
|
||||
// Your code here.
|
||||
}
|
||||
super.attachBaseContext(base)
|
||||
}
|
||||
```
|
||||
|
||||
### [Whale](https://github.com/asLody/whale)
|
||||
|
||||
> **所需 Xposed API 依赖** `com.wind.xposed:xposed-on-whale`
|
||||
|
||||
请参考 [xposed-hook-based-on-whale](https://github.com/WindySha/xposed-hook-based-on-whale)。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
// 装载 Whale 不需要任何配置
|
||||
// 装载 YukiHookAPI
|
||||
YukiHookAPI.encase(base) {
|
||||
// Your code here.
|
||||
}
|
||||
super.attachBaseContext(base)
|
||||
}
|
||||
```
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](config/r8-proguard.md)
|
@@ -1,22 +0,0 @@
|
||||
# R8 与 Proguard 混淆
|
||||
|
||||
> 大部分场景下 Xposed 模块可通过原生混淆压缩体积,这里介绍了混淆的配置方法。
|
||||
|
||||
## R8
|
||||
|
||||
> 如果你使用的是 `R8`,那么你无需对 `YukiHookAPI` 进行任何特殊配置。
|
||||
|
||||
## Proguard
|
||||
|
||||
> ~~如果你仍然在使用 `Proguard`,你需要做一些规则配置。~~
|
||||
|
||||
!> Proguard 规则已被弃用,请不要再使用,自从 Android Gradle Plugin 4.2 后,拥有 Android Jetpack 套件最新版本的混淆处理程序默认均为 `R8`,不再需要考虑混淆的问题。
|
||||
|
||||
若要在任何版本下启用 `R8`,请在 `gradle.properties` 文件中加入如下规则,Android Gradle Plugin 7.0 及以上版本无需任何配置。
|
||||
|
||||
```gradle
|
||||
android.enableR8=true
|
||||
```
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](tools/yukihookapi-projectbuilder.md)
|
@@ -1,255 +0,0 @@
|
||||
# 作为 Xposed 模块使用的相关配置
|
||||
|
||||
> 这里介绍了 `YukiHookAPI` 作为 Xposed 模块使用的相关配置方法。
|
||||
|
||||
## 依赖配置
|
||||
|
||||
> 作为 Xposed 模块,`YukiHookAPI` 提供了一个自动处理程序。
|
||||
|
||||
你需要在你的 `build.gradle` 中集成 `com.highcapable.yukihookapi:ksp-xposed` 依赖的最新版本。
|
||||
|
||||
## 自定义处理程序
|
||||
|
||||
> 你可以对 `YukiHookAPI` 将如何生成 `xposed_init` 入口进行相关配置。
|
||||
|
||||
### InjectYukiHookWithXposed 注解
|
||||
|
||||
```kotlin
|
||||
annotation class InjectYukiHookWithXposed(
|
||||
val sourcePath: String,
|
||||
val modulePackageName: String,
|
||||
val entryClassName: String,
|
||||
val isUsingResourcesHook: Boolean
|
||||
)
|
||||
```
|
||||
|
||||
`@InjectYukiHookWithXposed` 注解是一个标记模块 Hook 入口的重要注解。
|
||||
|
||||
!> `@InjectYukiHookWithXposed` 注解的 `Class` 必须实现 `IYukiHookXposedInit` 接口。
|
||||
|
||||
!> 在你当前项目中的所有 `Class` 标记中**只能存在一次**,若存在多个声明自动处理程序<u>**会在编译时抛出异常**</u>,你可以自定义其相关参数。
|
||||
|
||||
#### sourcePath 参数
|
||||
|
||||
`sourcePath` 参数决定了自动处理程序自动查找并匹配你当前项目路径的重要标识,此参数的内容为相对路径匹配,默认参数为 `src/main`。
|
||||
|
||||
!> 如果你的项目不在 `../src/main..` 或你手动使用 `sourceSets` 设置了项目路径,你就需要手动设置 `sourcePath` 参数,否则自动处理程序将无法识别你的项目路径并<u>**会在编译时抛出异常**</u>。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed(sourcePath = "src/custom")
|
||||
```
|
||||
|
||||
`sourcePath` 使用的文件路径分隔符写法根据 `Windows` 和 `Unix` 将自动进行识别,使用 `/` 或 `\` 均可。
|
||||
|
||||
#### modulePackageName 参数
|
||||
|
||||
`modulePackageName` 是你当前项目的 `applicationId`,也就是你的模块包名 (最终生成的应用包名),留空或不填时自动处理程序将对当前项目文件进行分析并生成。
|
||||
|
||||
!> 若你想使用模块包名自动生成,你需要确保你的项目命名空间在 `AndroidManifest.xml`、`build.gradle` 或 `build.gradle.kts` 中定义。
|
||||
|
||||
示例命名空间 `com.example.demo`,以下定义方式任选其一。
|
||||
|
||||
以下定义方式仅供参考,通常情况下**只要你的项目能够正常生成 `BuildConfig.java` 文件,就不需要做额外操作**。
|
||||
|
||||
- `AndroidManifest.xml` 示例
|
||||
|
||||
```xml
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.demo">
|
||||
```
|
||||
|
||||
- `build.gradle` 示例
|
||||
|
||||
```groovy
|
||||
android {
|
||||
namespace 'com.example.demo'
|
||||
}
|
||||
```
|
||||
|
||||
- `build.gradle.kts` 示例
|
||||
|
||||
```kotlin
|
||||
android {
|
||||
namespace = "com.example.demo"
|
||||
}
|
||||
```
|
||||
|
||||
若你的模块包名是非常规手段进行自动生成的,或你认为有必要手动定义模块包名,那么你可以直接设置 `modulePackageName` 的参数。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed(modulePackageName = "com.example.demo")
|
||||
```
|
||||
|
||||
只要你自定义了 `modulePackageName` 的参数,你就会在编译时收到警告。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```
|
||||
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: IXposedHookZygoteInit, IXposedHookLoadPackage, ...
|
||||
```
|
||||
|
||||
编译后的类名结构如下。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```
|
||||
...hook.HookEntry ← 你的入口类
|
||||
...hook.HookEntry_Impl ← 自动生成的 Impl 类
|
||||
...hook.HookEntry_YukiHookXposedInit ← 自动生成的 Xposed 入口类
|
||||
```
|
||||
|
||||
我们现在定义入口类名称为 `HookXposedEntry`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed(entryClassName = "HookXposedEntry")
|
||||
class HookEntry: IYukiHookXposedInit
|
||||
```
|
||||
|
||||
Xposed 入口类处理如下。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HookXposedEntry: IXposedHookZygoteInit, IXposedHookLoadPackage, ...
|
||||
```
|
||||
|
||||
编译后的类名结构如下。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```
|
||||
...hook.HookEntry ← 你的入口类
|
||||
...hook.HookEntry_Impl ← 自动生成的 Impl 类
|
||||
...hook.HookXposedEntry ← 自动生成的 Xposed 入口类
|
||||
```
|
||||
|
||||
!> 你定义的 `entryClassName` 不可与 `xposed_init` 中的类名相同,否则自动处理程序<u>会在编译时抛出异常</u>。
|
||||
|
||||
#### isUsingResourcesHook 参数
|
||||
|
||||
`isUsingResourcesHook` 决定了自动处理程序是否生成针对 Resources Hook 的相关代码,此功能默认是启用的。
|
||||
|
||||
启用后生成的入口类将为如下所示。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class _YukiHookXposedInit : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {
|
||||
|
||||
override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) {
|
||||
// ...
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
|
||||
// ...
|
||||
}
|
||||
|
||||
override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam?) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
若你当前的项目并不需要用到 Reources Hook,可以设置 `isUsingResourcesHook = false` 来关闭自动生成。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed(isUsingResourcesHook = false)
|
||||
```
|
||||
|
||||
关闭后生成的入口类将为如下所示。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class _YukiHookXposedInit : IXposedHookZygoteInit, IXposedHookLoadPackage {
|
||||
|
||||
override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam?) {
|
||||
// ...
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### IYukiHookXposedInit 接口
|
||||
|
||||
`IYukiHookXposedInit` 接口为你的 `HookEntryClass` 必须实现的接口,这是你的模块开始 Hook 的起点。
|
||||
|
||||
若要了解更多可 [点击这里](api/document?id=iyukihookxposedinit-interface) 进行查看。
|
||||
|
||||
当你的模块被 Xposed 装载后,`onHook` 方法将会被回调,你需要在此方法中开始使用 `YukiHookAPI`。
|
||||
|
||||
> 基本的调用流程为 `_YukiHookXposedInit` → `IYukiHookXposedInit.onXposedEvent` → `IYukiHookXposedInit.onInit` → `IYukiHookXposedInit.onHook`
|
||||
|
||||
详情请参考 [API 基本配置](config/api-example)。
|
||||
|
||||
## 原生 Xposed API 事件
|
||||
|
||||
若你当前的 Xposed 模块使用了第三方的资源,但是短时间内可能无法转移它们,此时,你可以使用 `onXposedEvent` 实现监听原生 Xposed API 的全部装载事件。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed
|
||||
class HookEntry: IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() {
|
||||
// Your code here.
|
||||
}
|
||||
|
||||
override fun onXposedEvent() {
|
||||
// 监听原生 Xposed API 的装载事件
|
||||
YukiXposedEvent.events {
|
||||
onInitZygote {
|
||||
// it 对象即 [StartupParam]
|
||||
}
|
||||
onHandleLoadPackage {
|
||||
// it 对象即 [LoadPackageParam]
|
||||
}
|
||||
onHandleInitPackageResources {
|
||||
// it 对象即 [InitPackageResourcesParam]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`onXposedEvent` 与 `onHook` 方法完全独立存在,互不影响,你可以继续在 `onHook` 方法中使用 `YukiHookAPI`。
|
||||
|
||||
若要了解更多可 [点击这里](api/document?id=onxposedevent-method) 进行查看。
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](config/api-using.md)
|
BIN
docs/favicon.ico
BIN
docs/favicon.ico
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,629 +0,0 @@
|
||||
# 用法示例
|
||||
|
||||
> 这里介绍了 `YukiHookAPI` 的基本工作方式以及列举了简单的 Hook 例子和常用功能。
|
||||
|
||||
## 结构图解
|
||||
|
||||
> 下方的结构描述了 `YukiHookAPI` 的基本工作方式和原理。
|
||||
|
||||
```
|
||||
Host Environment
|
||||
└── YukiMemberHookCreator
|
||||
└── Class
|
||||
└── MemberHookCreator
|
||||
└── Member
|
||||
├── Before
|
||||
└── After
|
||||
MemberHookCreator
|
||||
└── Member
|
||||
├── Before
|
||||
└── After
|
||||
...
|
||||
YukiResourcesHookCreator
|
||||
└── Resources
|
||||
└── ResourcesHookCreator
|
||||
└── Drawable
|
||||
└── Replace
|
||||
ResourcesHookCreator
|
||||
└── Layout
|
||||
└── Inject
|
||||
...
|
||||
```
|
||||
|
||||
> 上方的结构换做代码将可写为如下形式。
|
||||
|
||||
```kotlin
|
||||
TargetClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
// Your code here.
|
||||
}
|
||||
beforeHook {
|
||||
// Your code here.
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
// Your code here.
|
||||
}
|
||||
replaceTo(...)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Demo
|
||||
|
||||
> 你可以在下方找到 API 提供的 Demo 来学习 `YukiHookAPI` 的使用方法。
|
||||
|
||||
- 宿主 APP Demo [点击这里查看](https://github.com/fankes/YukiHookAPI/tree/master/demo-app)
|
||||
|
||||
- 模块 APP Demo [点击这里查看](https://github.com/fankes/YukiHookAPI/tree/master/demo-module)
|
||||
|
||||
同时安装宿主和模块 Demo,通过激活模块来测试宿主中被 Hook 的功能。
|
||||
|
||||
## 一个简单的 Hook 例子
|
||||
|
||||
> 这里给出了 Hook APP、Hook 系统框架与 Hook Resources 等例子,可供参考。
|
||||
|
||||
### Hook APP
|
||||
|
||||
假设,我们要 Hook `com.android.browser` 中的 `onCreate` 方法并弹出一个对话框。
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
AlertDialog.Builder(instance())
|
||||
.setTitle("Hooked")
|
||||
.setMessage("I am hook!")
|
||||
.setPositiveButton("OK", null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
至此,`onCreate` 方法将被成功 Hook 并在 `com.android.browser` 中的每个 `Activity` 启动时弹出此对话框。
|
||||
|
||||
那么,我想继续 Hook `onStart` 方法要怎么做呢?
|
||||
|
||||
在刚刚的代码中,继续插入一个 `injectMember` 方法体即可。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
AlertDialog.Builder(instance())
|
||||
.setTitle("Hooked")
|
||||
.setMessage("I am hook!")
|
||||
.setPositiveButton("OK", null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
injectMember {
|
||||
method {
|
||||
name = "onStart"
|
||||
emptyParam()
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
对于当前项目下没有的 `Class`,你可以使用 `stub` 方式或 `findClass` 方法来得到需要 Hook 的类。
|
||||
|
||||
比如,我要得到 `com.example.demo.TestClass`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
findClass(name = "com.example.demo.TestClass").hook {
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
若 `com.example.demo` 是你要 Hook 的 APP,那么写法可以更简单。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
findClass(name = "$packageName.TestClass").hook {
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
到这里有些同学可能就开始说了,在某些场景下 `findClass` 显得有些繁琐。
|
||||
|
||||
因为可能有些同学有如下需求。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
const val TestClass = "com.example.demo.TestClass"
|
||||
|
||||
TestClass.hook {
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
没关系,你还可以使用字符串类名直接创建一个 Hook。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
"$packageName.TestClass".hook {
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
更多功能请参考 [MemberHookCreator](api/document?id=memberhookcreator-class)。
|
||||
|
||||
### Hook Zygote
|
||||
|
||||
在 APP 启动时,新的进程被 fork 后的第一个事件 `initZygote`。
|
||||
|
||||
假设我们要全局 Hook 一个 APP `Activity` 的 `onCreate` 事件
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadZygote {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!> 在 `loadZygote` 中进行的功能十分有限,几乎很少的情况下需要用到 `loadZygote` 方法。
|
||||
|
||||
### Hook 系统框架
|
||||
|
||||
在 `YukiHookAPI` 中,Hook 系统框架的实现非常简单。
|
||||
|
||||
假设,你要得到 `ApplicationInfo` 与 `PackageInfo` 并对它们进行一些操作。
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadSystem {
|
||||
ApplicationInfoClass.hook {
|
||||
// Your code here.
|
||||
}
|
||||
PackageInfoClass.hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!> `loadZygote` 与 `loadSystem` 有直接性区别,`loadZygote` 会在 `initZygote` 中装载,系统框架被视为 `loadApp(name = "android")` 而存在,若要 Hook 系统框架,可直接使用 `loadSystem`。
|
||||
|
||||
### Hook Resources
|
||||
|
||||
假设,我们要 Hook `com.android.browser` 中 `string` 类型的 `app_name` 内容替换为 `123`。
|
||||
|
||||
在 `encase` 方法体中添加代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "app_name"
|
||||
string()
|
||||
}
|
||||
replaceTo("123")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
若当前 APP 使用 `app_name` 设置了标题栏文本,则它就会变成我们的 `123`。
|
||||
|
||||
你还可以使用当前 Xposed 模块的 Resources 替换 Hook APP 的 Resources。
|
||||
|
||||
假设,我们要继续 Hook `com.android.browser` 中 `mipmap` 类型的 `ic_launcher`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "ic_launcher"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
至此目标 APP 的图标将会被替换为我们设置的图标。
|
||||
|
||||
若你想替换系统框架的资源,同样也可以这样实现,只需要把 `loadApp` 换成 `loadZygote` 即可。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadZygote {
|
||||
resources().hook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
更多功能请参考 [ResourcesHookCreator](api/document?id=resourceshookcreator-class)。
|
||||
|
||||
### 解除 Hook
|
||||
|
||||
原生的 Xposed 为我们提供了一个 `XC_MethodHook.Unhook` 功能,可以从 Hook 队列中将当前 Hook 移除,`YukiHookAPI` 同样可以实现此功能。
|
||||
|
||||
第一种方法,保存当前注入对象的 `Result` 实例,在适当的时候和地方调用 `remove` 即可解除该注入对象。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// 设置一个变量保存当前实例
|
||||
val hookResult = injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
// 在适当的时候调用如下方法即可
|
||||
hookResult.remove()
|
||||
```
|
||||
|
||||
第二种方法,在 Hook 回调方法中调用 `removeSelf` 移除自身。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
returnType = UnitType
|
||||
}
|
||||
afterHook {
|
||||
// 直接调用如下方法即可
|
||||
removeSelf()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
更多功能请参考 [MemberHookCreator](api/document?id=memberhookcreator-class)。
|
||||
|
||||
## 异常处理
|
||||
|
||||
> `YukiHookAPI` 重新设计了对异常的监听,任何异常都不会在 Hook 过程中抛出,避免打断下一个 Hook 流程导致 Hook 进程“死掉”。
|
||||
|
||||
### 监听异常
|
||||
|
||||
你可以处理 Hook 方法过程发生的异常。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}.result {
|
||||
// 处理 Hook 开始时的异常
|
||||
onHookingFailure {}
|
||||
// 处理 Hook 过程中的异常
|
||||
onConductFailure { param, throwable -> }
|
||||
// 处理全部异常
|
||||
onAllFailure {}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
在 Resources Hook 时此方法同样适用。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectResource {
|
||||
// Your code here.
|
||||
}.result {
|
||||
// 处理 Hook 时的任意异常
|
||||
onHookingFailure {}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
你还可以处理 Hook 的 `Class` 不存在时发生的异常。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
TargetClass.hook {
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}
|
||||
}.onHookClassNotFoundFailure {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
你还可以处理查找方法时的异常。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
method {
|
||||
// Your code here.
|
||||
}.onNoSuchMethod {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
更多功能请参考 [MemberHookCreator.Result](api/document?id=result-class) 与 [ResourcesHookCreator.Result](api/document?id=result-class-2)。
|
||||
|
||||
这里介绍了可能发生的常见异常,若要了解更多请参考 [API 异常处理](config/api-exception.md)。
|
||||
|
||||
### 抛出异常
|
||||
|
||||
在某些情况下,你可以**手动抛出异常**来达到提醒某些功能存在问题的目的。
|
||||
|
||||
上面已经介绍过,在 `hook` 方法体内抛出的异常会被 `YukiHookAPI` 接管,避免打断下一个 Hook 流程导致 Hook 进程“死掉”。
|
||||
|
||||
以下是 `YukiHookAPI` 接管时这些异常的运作方式。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
// <情景1>
|
||||
injectMember {
|
||||
method {
|
||||
throw RuntimeException("Exception Test")
|
||||
}
|
||||
afterHook {
|
||||
// ...
|
||||
}
|
||||
}.result {
|
||||
// 能够捕获到 RuntimeException
|
||||
onHookingFailure {}
|
||||
}
|
||||
// <情景2>
|
||||
injectMember {
|
||||
method {
|
||||
// ...
|
||||
}
|
||||
afterHook {
|
||||
throw RuntimeException("Exception Test")
|
||||
}
|
||||
}.result {
|
||||
// 能够捕获到 RuntimeException
|
||||
onConductFailure { param, throwable -> }
|
||||
}
|
||||
```
|
||||
|
||||
以上情景只会在 (Xposed) 宿主环境被处理,不会对宿主自身造成任何影响。
|
||||
|
||||
若我们想将这些异常直接抛给宿主,原生的 Xposed 为我们提供了 `param.throwable` 方法,`YukiHookAPI` 同样可以实现此功能。
|
||||
|
||||
若想在 Hook 回调方法体中将一个异常直接抛给宿主,可以有如下实现方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
// ...
|
||||
}
|
||||
afterHook {
|
||||
RuntimeException("Exception Test").throwToApp()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
你也可以直接在 Hook 回调方法体中抛出异常,然后标识将异常抛给宿主。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
// ...
|
||||
}
|
||||
afterHook {
|
||||
throw RuntimeException("Exception Test")
|
||||
}.onFailureThrowToApp()
|
||||
}
|
||||
```
|
||||
|
||||
以上两种方法均可在宿主接收到异常从而使宿主进程崩溃。
|
||||
|
||||
!> 为了保证 Hook 调用域与宿主内调用域相互隔离,异常只有在 `beforeHook` 与 `afterHook` 回调方法体中才能抛给宿主。
|
||||
|
||||
更多功能请参考 [Throwable.throwToApp](api/document?id=throwablethrowtoapp-i-ext-method) 与 [HookCallback](api/document?id=hookcallback-class)。
|
||||
|
||||
## 状态监听
|
||||
|
||||
在使用 `XposedHelpers` 的同学往往会在 Hook 后打印 `UnHook` 的方法确定是否 Hook 成功。
|
||||
|
||||
在 `YukiHookAPI` 中,你可以用以下方法方便地重新实现这个功能。
|
||||
|
||||
首先我们可以监听 Hook 已经准备开始。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
YourClass.hook {
|
||||
// Your code here.
|
||||
}.onPrepareHook {
|
||||
loggerD(msg = "$instanceClass hook start")
|
||||
}
|
||||
```
|
||||
|
||||
!> 请注意 `instanceClass` 建议只在 `onPrepareHook` 中使用,万一被 Hook 的 `Class` 不存在将会抛出无法拦截的异常导致 Hook 进程“死掉”。
|
||||
|
||||
然后,我们还可以对 Hook 的方法结果进行监听是否成功。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
// Your code here.
|
||||
}.onHooked { member ->
|
||||
loggerD(msg = "$member has hooked")
|
||||
}
|
||||
```
|
||||
|
||||
## 扩展用法
|
||||
|
||||
> 你可以在 Hook 过程中使用下面的方法方便地实现各种判断和功能。
|
||||
|
||||
### 多个宿主
|
||||
|
||||
如果你的模块需要同时处理多个 APP 的 Hook 事件,你可以使用 `loadApp` 方法体来区分你要 Hook 的 APP。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
loadApp(name = "com.android.browser") {
|
||||
// Your code here.
|
||||
}
|
||||
loadApp(name = "com.android.phone") {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
更多功能请参考 [loadApp](api/document?id=loadapp-method)。
|
||||
|
||||
### 多个进程
|
||||
|
||||
如果你 Hook 的宿主 APP 有多个进程,你可以使用 `withProcess` 方法体来对它们分别进行 Hook。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
withProcess(mainProcessName) {
|
||||
// Your code here.
|
||||
}
|
||||
withProcess(name = "$packageName:tool") {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
更多功能请参考 [withProcess](api/document?id=withprocess-method)。
|
||||
|
||||
## 写法优化
|
||||
|
||||
为了使代码更加简洁,你可以删去 `YukiHookAPI` 的名称,将你的 `onHook` 入口写作 `lambda` 形式。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
override fun onHook() = encase {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
## Xposed 模块判断自身激活状态
|
||||
|
||||
通常情况下,我们会选择写一个方法,使其返回 `false`,然后 Hook 掉这个方法使其返回 `true` 来证明 Hook 已经生效。
|
||||
|
||||
在 `YukiHookAPI` 中你完全不需要再这么做了,`YukiHookAPI` 已经帮你封装好了这个操作,你可以直接进行使用。
|
||||
|
||||
现在,你可以直接使用 `YukiHookAPI.Status.isXposedModuleActive` 在模块中判断自身是否被激活。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
if(YukiHookAPI.Status.isXposedModuleActive) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
由于一些特殊原因,在太极、无极中的模块无法使用标准方法检测激活状态。
|
||||
|
||||
此时你可以使用 `YukiHookAPI.Status.isTaiChiModuleActive` 判断自身是否被激活。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
if(YukiHookAPI.Status.isTaiChiModuleActive) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
若你想使用两者得兼的判断方案,`YukiHookAPI` 同样为你封装了便捷的方式。
|
||||
|
||||
此时你可以使用 `YukiHookAPI.Status.isModuleActive` 判断自身是否在 Xposed 或太极、无极中被激活。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
if(YukiHookAPI.Status.isModuleActive) {
|
||||
// Your code here.
|
||||
}
|
||||
```
|
||||
|
||||
更多功能请参考 [YukiHookAPI.Status](api/document?id=status-object)。
|
||||
|
||||
!> 若模块激活判断中包含太极、无极中的激活状态,就必须将模块的 `Application` 继承于 `ModuleApplication` 或直接使用 `ModuleApplication`。
|
||||
|
||||
!> `1.0.91` 版本后的 API 修改了激活逻辑判断方式,现在你可以在模块与 Hook APP (宿主) 中同时使用此 API。
|
||||
|
||||
!> 需要确保 `YukiHookAPI.Configs.isEnableHookModuleStatus` 是启用状态。
|
||||
|
||||
!> 除了提供标准 API 的 Hook 框架之外,其它情况下模块可能都将无法判断自己是否被激活。
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](guide/special-feature.md)
|
@@ -1,208 +0,0 @@
|
||||
# 介绍
|
||||
|
||||
> 这是一个 Hook API 框架,本身不提供任何 Hook 功能,需要 Xposed 基础 API 的支持。
|
||||
|
||||
## 背景
|
||||
|
||||
这是一个使用 `Kotlin` 重新构建的高效 Xposed Hook API。
|
||||
|
||||
名称取自 [《ももくり》女主 栗原 雪(Yuki)](https://www.bilibili.com/bangumi/play/ss5016)。
|
||||
|
||||
前身为 [开发学习项目](https://github.com/fankes/TMore) 中使用的 Innocent Xposed API,现在重新命名并开源。
|
||||
|
||||
## 用途
|
||||
|
||||
`YukiHookAPI` 完全采用 `Kotlin` `lambda` 语法构建。
|
||||
|
||||
抛弃原始不太友好的 `XposedHelpers`,你可以使用它来轻松创建 Xposed 模块以及轻松实现自定义 Hook API。
|
||||
|
||||
## 语言要求
|
||||
|
||||
请使用 `Kotlin`,框架部分代码构成同样兼容 `Java` 但基础 Hook 场景的实现**可能完全无法使用**。
|
||||
|
||||
文档全部的 Demo 示例代码都将使用 `Kotlin` 进行描述,如果你完全不会使用 `Kotlin` 那你将有可能无法使用 `YukiHookAPI`。
|
||||
|
||||
部分 Java Demo 代码可在 [这里](https://github.com/fankes/YukiHookAPI/tree/master/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/java) 找到,但不推荐使用。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- **Xposed 模块开发**
|
||||
|
||||
自动构建程序可以帮你快速创建一个 Xposed 模块,完全省去配置入口类和 `xposed_init` 文件。
|
||||
|
||||
- **轻量优雅**
|
||||
|
||||
拥有一套强大、优雅、人性化、完全使用 `Kotlin` `lambda` 打造的 API,可以帮你快速实现字节码的查找以及方法 Hook。
|
||||
|
||||
- **高效调试**
|
||||
|
||||
拥有丰富的调试日志功能,细到每个 Hook 方法的名称、所在类以及查找耗时,可进行快速调试和排错。
|
||||
|
||||
- **方便移植**
|
||||
|
||||
原生支持 Xposed API 用法,并原生对接 Xposed API,拥有 Xposed API 的 Hook 框架都能快速对接 `YukiHookAPI`。
|
||||
|
||||
- **支持混淆**
|
||||
|
||||
使用 `YukiHookAPI` 构建的 Xposed 模块原生支持 R8 压缩优化混淆,混淆不会破坏 Hook 入口点,R8 下无需任何其它配置。
|
||||
|
||||
- **快速上手**
|
||||
|
||||
简单易用,不需要繁琐的配置,不需要十足的开发经验,搭建环境集成依赖即可立即开始使用。
|
||||
|
||||
## 灵感来源
|
||||
|
||||
以前,我们在构建 Xposed 模块的时候,首先需要在 `assets` 下创建 `xposed_init` 文件。
|
||||
|
||||
然后,将自己的入口类名手动填入文件中,使用 `XposedHelpers` 去实现我们的 Hook 逻辑。
|
||||
|
||||
自 `Kotlin` 作为 Android 主要开发语言以来,这套 API 用起来确实已经不是很优雅了。
|
||||
|
||||
有没有什么 **好用、轻量、优雅** 的解决办法呢?
|
||||
|
||||
本着这样的想法,`YukiHookAPI` 诞生了。
|
||||
|
||||
现在,我们只需要编写少量的代码,一切时间开销和花费交给自动化处理。
|
||||
|
||||
借助 `Kotlin` 优雅的 `lambda` 写法以及 `YukiHookAPI`,可以让你的 Hook 逻辑更加美观清晰。
|
||||
|
||||
> 示例如下
|
||||
|
||||
<!-- tabs:start -->
|
||||
|
||||
#### **Yuki Hook API**
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() = encase {
|
||||
loadZygote {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
}
|
||||
beforeHook {
|
||||
// Your code here.
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "sym_def_app_icon"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
loadApp(name = "com.android.browser") {
|
||||
ActivityClass.hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
}
|
||||
beforeHook {
|
||||
// Your code here.
|
||||
}
|
||||
afterHook {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
}
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "ic_launcher"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### **Xposed API**
|
||||
|
||||
```kotlin
|
||||
class HookEntry : IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {
|
||||
|
||||
private lateinit var moduleResources: XModuleResources
|
||||
|
||||
override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) {
|
||||
moduleResources = XModuleResources.createInstance(sparam.modulePath, null)
|
||||
XResources.setSystemWideReplacement("android", "mipmap", "sym_def_app_icon", moduleResources.fwd(R.mipmap.ic_launcher))
|
||||
XposedHelpers.findAndHookMethod(
|
||||
Activity::class.java.name,
|
||||
null, "onCreate",
|
||||
Bundle::class.java,
|
||||
object : XC_MethodHook() {
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
// Your code here.
|
||||
}
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam?) {
|
||||
// Your code here.
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
if (lpparam.packageName == "com.android.browser")
|
||||
XposedHelpers.findAndHookMethod(
|
||||
Activity::class.java.name,
|
||||
lpparam.classLoader, "onCreate",
|
||||
Bundle::class.java,
|
||||
object : XC_MethodHook() {
|
||||
override fun beforeHookedMethod(param: MethodHookParam?) {
|
||||
// Your code here.
|
||||
}
|
||||
|
||||
override fun afterHookedMethod(param: MethodHookParam?) {
|
||||
// Your code here.
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
|
||||
if (resparam.packageName == "com.android.browser")
|
||||
resparam.res.setReplacement("com.android.browser", "mipmap", "ic_launcher", moduleResources.fwd(R.mipmap.ic_launcher))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
是的,你没有看错,仅仅就需要这些代码,就能完全取代 Xposed API 实现同样的功能。
|
||||
|
||||
现在,借助高效强大的 `YukiHookAPI`,你就可以实现一个非常简单的 Xposed 模块。
|
||||
|
||||
## 支持的 Hook 框架
|
||||
|
||||
以下是 `YukiHookAPI` 支持的 `Hook Framework` 以及 Xposed 框架。
|
||||
|
||||
| Hook Framework | ST | Describe |
|
||||
| --------------------------------------------------------- | --- | ----------------------------------------------------------------------------------------- |
|
||||
| [LSPosed](https://github.com/LSPosed/LSPosed) | ✅ | 多场景下稳定使用 |
|
||||
| [LSPatch](https://github.com/LSPosed/LSPatch) | ⭕ | 将在此项目完善后逐渐加入 API 支持 |
|
||||
| [EdXposed](https://github.com/ElderDrivers/EdXposed) | ❎ | 已停止维护,不再推荐使用 |
|
||||
| [Pine](https://github.com/canyie/pine) | ⭕ | 可以使用 |
|
||||
| [SandHook](https://github.com/asLody/SandHook) | ⭕ | 可以使用 |
|
||||
| [Whale](https://github.com/asLody/whale) | ⭕ | 需要 [xposed-hook-based-on-whale](https://github.com/WindySha/xposed-hook-based-on-whale) |
|
||||
| [YAHFA](https://github.com/PAGalaxyLab/YAHFA) | ❗ | 需要自行实现 Xposed API |
|
||||
| [FastHook](https://github.com/turing-technician/FastHook) | ❗ | 需要自行实现 Xposed API |
|
||||
| [Epic](https://github.com/tiann/epic) | ❗ | 需要自行对接 [Dexposed](https://github.com/alibaba/dexposed) |
|
||||
| [TaiChi](https://github.com/taichi-framework/TaiChi) | ⭕ | 可以作为模块使用 |
|
||||
| [Xposed](https://github.com/rovo89/Xposed) | ❎ | 未测试,不再推荐使用 |
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](guide/knowledge.md)
|
@@ -1,88 +0,0 @@
|
||||
# 基础知识
|
||||
|
||||
> 这里收集了 Xposed 相关的介绍以及开启前需要掌握的知识要点,已经了解的同学可以略过。
|
||||
|
||||
基础知识内容<u>**并不一定完全准确**</u>,请根据自己的见解酌情阅读,若发现内容**有错误欢迎指正并帮助我们完善和改进**。
|
||||
|
||||
## 相关介绍
|
||||
|
||||
> 这里介绍了 Xposed 以及 Hook 的工作原理。
|
||||
|
||||
### Xposed 是什么
|
||||
|
||||
> Xposed 框架(Xposed Framework)是一套开源的、在 Android 高权限模式下运行的框架服务,可以在不修改 APK 文件的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。
|
||||
|
||||
上述内容复制自百度百科。
|
||||
|
||||
### Xposed 能做什么
|
||||
|
||||
> 下方的结构描述了 Xposed 的基本工作方式和原理。
|
||||
|
||||
```
|
||||
Xposed Framework
|
||||
└── App's Environment
|
||||
└── Hooker (Hooked)
|
||||
...
|
||||
App's Environment
|
||||
└── Hooker (Hooked)
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
我们可以在宿主 (APP) 运行时通过注入宿主 (APP) 来达到控制其行为的最终目的。
|
||||
|
||||
Xposed 的这种运行方式被称为**寄生**,Xposed 模块跟随宿主的生命周期,在宿主的生命周期内完成自己的生命历程。
|
||||
|
||||
我们可以通过反射的方式调用宿主的方法、变量、构造方法,以及使用 `XposedBridge` 所提供的 Hook 操作动态地在宿主 (APP) 要执行的方法前后插入自己的代码,或完全替换目标,甚至是拦截。
|
||||
|
||||
### 发展过程
|
||||
|
||||
如今的 Xposed 管理器已完全被其衍生作品替代,而 **SuperSU** 的时代也已经落幕了,现在,借助 **Magisk** 使后面的一切又成为了可能。
|
||||
|
||||
> 其发展史大致可分为 **Xposed(Dalvik)** → **Xposed(ART)** → **Xposed(Magisk)** → **EdXposed(Riru)**/**LSPosed(Riru/Zygisk)**
|
||||
|
||||
### 衍生产品
|
||||
|
||||
> 下方的结构描述了类似 Xposed 的 Hook Framework 的工作方式和原理。
|
||||
|
||||
```
|
||||
App's Environment
|
||||
└── Hook Framework
|
||||
└── Hooker (Hooked)
|
||||
...
|
||||
```
|
||||
|
||||
通过 Xposed 的运行原理,从而衍生了很多同类型框架,随着当今时代的移动设备获取 Root 权限甚至刷机越来越困难且不是刚需的时候,一些免 Root 框架也随之产生,例如**太极**。
|
||||
|
||||
这些在 ART 层面上的 Hook 框架同样也可不借助 Xposed API 完成其和 Xposed 原理一样的 Hook 流程,免 Root 的运行原理为修改 APK 并将 Hook 进程注入宿主,通过外部模块对其进行控制。
|
||||
|
||||
另外一种产品就是利用 Android 运行环境现有的功能虚拟出一个完全与当前设备系统一样的环境,并在其中运行 APP,这个就是虚拟 APP 技术 **VirtualApp**,后来衍生为 **VirtualXposed**。
|
||||
|
||||
上述提到的免 Root 框架分别为**太极/无极**、**VirtualXposed/SandVXposed**。
|
||||
|
||||
### YukiHookAPI 做了什么
|
||||
|
||||
自从 Xposed 出现到现在为止,除了开发者人人皆知的 `XposedHelpers`,依然没有一套针对 `Kotlin` 打造的语法糖以及用法封装十分完善的 API。
|
||||
|
||||
本 API 框架的诞生就是希望在 Xposed 的如今时代,能让更多有动手能力的 Xposed 模块开发者少走弯路,更容易、更简单地完成整个开发流程。
|
||||
|
||||
未来,`YukiHookAPI` 将在使用 Xposed API 的目标基础上适配更多第三方 Hook 框架,使得整个生态得到完善,并帮助更多开发者让 Xposed 模块开发变得更加简单和易懂。
|
||||
|
||||
## 让我们开始吧
|
||||
|
||||
在开始之前,你需要拥有以下基础才能更好地使用 `YukiHookAPI`。
|
||||
|
||||
- 掌握并了解 Android 开发及简单的系统运行原理
|
||||
|
||||
- 掌握并了解 Android APK 内部结构以及简单的反编译知识要领,可参考 [Jadx](https://github.com/skylot/jadx) 与 [ApkTool](https://github.com/iBotPeaches/Apktool)
|
||||
|
||||
- 掌握并熟练使用 Java 反射,了解简单的 Smali 语法,了解 DEX 文件结构,会使用逆向分析定位方法位置
|
||||
|
||||
- 掌握基础的原生 [Xposed API](https://api.xposed.info) 用法,了解 Xposed 的运行原理,可参考本文以及 [这里](https://blog.ketal.icu/2022/01/13/Xposed%E6%A8%A1%E5%9D%97%E5%BC%80%E5%8F%91%E5%85%A5%E9%97%A8%E4%BF%9D%E5%A7%86%E7%BA%A7%E6%95%99%E7%A8%8B) **(友情链接)**
|
||||
|
||||
- 掌握 Kotlin 语言,学会灵活运用 [Kotlin lambda](https://blog.ketal.icu/2022/01/01/kotlin-lambda%E5%85%A5%E9%97%A8) **(友情链接)**
|
||||
|
||||
- 掌握并了解 Kotlin 与 Java 混编、互相调用以及 Kotlin 生成的 Java 字节码
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](guide/quick-start.md)
|
@@ -1,257 +0,0 @@
|
||||
# 从 Xposed API 迁移
|
||||
|
||||
> 若你熟悉 Xposed API,你可以参考下方的相同点将自己的 API 快速迁移到 `YukiHookAPI`。
|
||||
|
||||
## 迁移 Hook 入口点
|
||||
|
||||
> 从 `XC_LoadPackage.LoadPackageParam` 迁移至 `PackageParam`。
|
||||
|
||||
`YukiHookAPI` 对 `PackageParam` 实现了 `lambda` 方法体 `this` 用法,在 `encase` 方法体内即可全局得到 `PackageParam` 对象。
|
||||
|
||||
> API 功能差异对比如下
|
||||
|
||||
<!-- tabs:start -->
|
||||
|
||||
#### **Yuki Hook API**
|
||||
|
||||
```kotlin
|
||||
override fun onHook() = encase {
|
||||
// 得到当前 Hook 的包名
|
||||
packageName
|
||||
// 得到当前 Hook 的 ApplicationInfo
|
||||
appInfo
|
||||
// 得到宿主 Application 生命周期
|
||||
appContext
|
||||
// Hook 指定的 APP
|
||||
loadApp(name = "com.demo.test") {
|
||||
// Class Hook
|
||||
findClass("com.demo.test.TestClass").hook {
|
||||
injectMember {
|
||||
method {
|
||||
name = "test"
|
||||
param(BooleanType)
|
||||
}
|
||||
afterHook {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
// Resources Hook (固定用法)
|
||||
resources().hook {
|
||||
injectResource {
|
||||
conditions {
|
||||
name = "ic_launcher"
|
||||
mipmap()
|
||||
}
|
||||
replaceToModuleResource(R.mipmap.ic_launcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### **Xposed API**
|
||||
|
||||
```kotlin
|
||||
private lateinit var moduleResources: XModuleResources
|
||||
|
||||
override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) {
|
||||
moduleResources = XModuleResources.createInstance(sparam.modulePath, null)
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
// 得到当前 Hook 的包名
|
||||
lpparam.packageName
|
||||
// 得到当前 Hook 的 ApplicationInfo
|
||||
lpparam.applicationInfo
|
||||
// 得到宿主 Application 生命周期
|
||||
AndroidAppHelper.currentApplication()
|
||||
// Class Hook
|
||||
if(lpparam.packageName == "com.demo.test")
|
||||
XposedHelpers.findAndHookMethod("com.demo.test.TestClass", lpparam.classLoader, "test", Boolean::class.java, object : XC_MethodHook() {
|
||||
override fun afterHookedMethod(param: MethodHookParam) {
|
||||
// ...
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
|
||||
// 得到当前 Hook 的包名
|
||||
resparam.packageName
|
||||
// 创建 Hook
|
||||
resparam.res.setReplacement("com.demo.test", "mipmap", "ic_launcher", moduleResources.fwd(R.mipmap.ic_launcher))
|
||||
}
|
||||
```
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
## 迁移 Hook 方法体
|
||||
|
||||
> 从 `XC_MethodHook.MethodHookParam` 迁移至 `HookParam`。
|
||||
|
||||
### Before/After Hook
|
||||
|
||||
`YukiHookAPI` 同样对 `HookParam` 实现了 `lambda` 方法体 `this` 用法,在 `beforeHook`、`afterHook` 等方法体内即可全局得到 `HookParam` 对象。
|
||||
|
||||
> API 功能差异对比如下
|
||||
|
||||
<!-- tabs:start -->
|
||||
|
||||
#### **Yuki Hook API**
|
||||
|
||||
```kotlin
|
||||
afterHook {
|
||||
// 得到当前 Hook 的实例
|
||||
instance
|
||||
// 得到当前 Hook 的 Class 实例
|
||||
instanceClass
|
||||
// 得到并 cast 当前 Hook 的实例为指定类型 T
|
||||
instance<T>()
|
||||
// 得到方法参数数组
|
||||
args
|
||||
// 得到方法参数的第一位 T
|
||||
args().first().cast<T>()
|
||||
// 得到方法参数的最后一位 T
|
||||
args().last().cast<T>()
|
||||
// 得到方法参数的任意下标 T,这里用 2 举例
|
||||
args(index = 2).cast<T>()
|
||||
// 设置方法参数的任意下标,这里用 2 举例
|
||||
args(index = 2).set(...)
|
||||
// 得到返回值
|
||||
result
|
||||
// 得到返回值并 cast 为 T
|
||||
result<T>()
|
||||
// 修改返回值内容
|
||||
result = ...
|
||||
// 删除返回值内容
|
||||
resultNull()
|
||||
// 向 Hook APP 抛出异常
|
||||
Throwable("Fatal").throwToApp()
|
||||
// 执行未经 Hook 的原始方法并使用原始方法参数调用,泛型可略
|
||||
callOriginal<Any?>()
|
||||
// 执行未经 Hook 的原始方法并自定义方法参数调用,泛型可略
|
||||
invokeOriginal<Any?>(...)
|
||||
}
|
||||
```
|
||||
|
||||
#### **Xposed API**
|
||||
|
||||
```kotlin
|
||||
override fun afterHookedMethod(param: MethodHookParam) {
|
||||
// 得到当前 Hook 的实例
|
||||
param.thisObject
|
||||
// 得到当前 Hook 的 Class 实例
|
||||
param.thisObject.javaClass
|
||||
// 得到并 cast 当前 Hook 的实例为指定类型 T
|
||||
param.thisObject as T
|
||||
// 得到方法参数数组
|
||||
param.args
|
||||
// 得到方法参数的第一位 T
|
||||
param.args[0] as T
|
||||
// 得到方法参数的最后一位 T
|
||||
param.args[param.args.lastIndex] as T
|
||||
// 得到方法参数的任意下标 T,这里用 2 举例
|
||||
param.args[2] as T
|
||||
// 设置方法参数的任意下标,这里用 2 举例
|
||||
param.args[2] = ...
|
||||
// 得到返回值
|
||||
param.result
|
||||
// 得到返回值并 cast 为 T
|
||||
param.result as T
|
||||
// 修改返回值内容
|
||||
param.result = ...
|
||||
// 删除返回值内容
|
||||
param.result = null
|
||||
// 向 Hook APP 抛出异常
|
||||
param.throwable = Throwable("Fatal")
|
||||
// 执行未经 Hook 的原始方法
|
||||
XposedBridge.invokeOriginalMethod(param.method, param.thisObject, ...)
|
||||
}
|
||||
```
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
### Replace Hook
|
||||
|
||||
`replaceHook` 方法比较特殊,`YukiHookAPI` 为它做出了多种形式以供选择。
|
||||
|
||||
> API 功能差异对比如下
|
||||
|
||||
<!-- tabs:start -->
|
||||
|
||||
#### **Yuki Hook API**
|
||||
|
||||
无返回值的方法 `void`。
|
||||
|
||||
```kotlin
|
||||
replaceUnit {
|
||||
// 直接在这里实现被替换的逻辑
|
||||
}
|
||||
```
|
||||
|
||||
有返回值的方法。
|
||||
|
||||
```kotlin
|
||||
replaceAny {
|
||||
// 在这里实现被替换的逻辑
|
||||
// ...
|
||||
// 需要返回方法对应的返回值,无需写 return,只需将参数放到最后一位
|
||||
// 假设这个方法的返回值是 Int,我们只需要保证最后一位是我们需要的返回值即可
|
||||
0
|
||||
}
|
||||
```
|
||||
|
||||
有些方法我们只需替换其返回值,则有如下实现。
|
||||
|
||||
```kotlin
|
||||
// 替换为你需要的返回值
|
||||
replaceTo(...)
|
||||
// 替换为 Boolean 类型的返回值
|
||||
replaceToTrue()
|
||||
// 拦截返回值
|
||||
intercept()
|
||||
```
|
||||
|
||||
!> 直接替换返回值的方法传入的参数是固定不变的,若想实现动态替换返回值请使用 `replaceAny` 方法体。
|
||||
|
||||
#### **Xposed API**
|
||||
|
||||
无返回值的方法 `void`。
|
||||
|
||||
```kotlin
|
||||
override fun replaceHookedMethod(param: MethodHookParam): Any? {
|
||||
// 直接在这里实现被替换的逻辑
|
||||
return null
|
||||
}
|
||||
```
|
||||
|
||||
有返回值的方法。
|
||||
|
||||
```kotlin
|
||||
override fun replaceHookedMethod(param: MethodHookParam): Int {
|
||||
// 在这里实现被替换的逻辑
|
||||
// ...
|
||||
// 假设这个方法的返回值是 Int
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
有些方法我们只需替换其返回值,则有如下实现。
|
||||
|
||||
```kotlin
|
||||
// 替换为你需要的返回值
|
||||
override fun replaceHookedMethod(param: MethodHookParam) = ...
|
||||
// 替换为 Boolean 类型的返回值
|
||||
override fun replaceHookedMethod(param: MethodHookParam) = true
|
||||
// 拦截返回值
|
||||
override fun replaceHookedMethod(param: MethodHookParam) = null
|
||||
```
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
## 迁移其它功能
|
||||
|
||||
`YukiHookAPI` 对 Xposed API 进行了完全重写,你可以参考 [API 文档](api/home) 以及 [特色功能](guide/special-feature) 来决定一些功能性的迁移和使用。
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](config/api-example.md)
|
@@ -1,187 +0,0 @@
|
||||
# 快速开始
|
||||
|
||||
> 集成 `YukiHookAPI` 到你的项目中。
|
||||
|
||||
## 环境要求
|
||||
|
||||
- Windows 7 及以上/macOS 10.14 及以上/Linux 发行版(Arch/Debian)
|
||||
|
||||
- Android Studio 2021.1 及以上
|
||||
|
||||
- IntelliJ IDEA 2021.1 及以上
|
||||
|
||||
- Kotlin 1.7.0 及以上
|
||||
|
||||
- Android Gradle Plugin 7.0 及以上
|
||||
|
||||
- Gradle 7.0 及以上
|
||||
|
||||
- Jvm 11 及以上 (Since API `1.0.80`)
|
||||
|
||||
## 自动构建项目
|
||||
|
||||
`YukiHookAPI` 提供了一个自动化构建工具,它可以帮助你快速构建一个拥有 Xposed 模块依赖的 Android 标准项目模板,使用构建好的模板即可直接开始下一步工作。
|
||||
|
||||
你可以 [点击这里](tools/yukihookapi-projectbuilder) 进行查看。
|
||||
|
||||
## 手动配置项目
|
||||
|
||||
若你不想使用自动化构建工具,你依然可以按照以下方式手动配置项目依赖。
|
||||
|
||||
### 集成依赖
|
||||
|
||||
在你的项目 `build.gradle` 中添加依赖。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```gradle
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
// ❗若你的 Plugin 版本过低,作为 Xposed 模块使用务必添加,其它情况可选
|
||||
maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
|
||||
// ❗作为 Xposed 模块使用务必添加,其它情况可选
|
||||
maven { url "https://api.xposed.info/" }
|
||||
// MavenCentral 有 2 小时缓存,若无法集成最新版本请添加此地址
|
||||
maven { url "https://s01.oss.sonatype.org/content/repositories/releases" }
|
||||
}
|
||||
```
|
||||
|
||||
在你的 app `build.gradle` 中添加 `plugin`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```gradle
|
||||
plugins {
|
||||
// ❗作为 Xposed 模块使用务必添加,其它情况可选
|
||||
id 'com.google.devtools.ksp' version '<version>'
|
||||
}
|
||||
```
|
||||
|
||||
在你的 app `build.gradle` 中添加依赖。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
// 基础依赖
|
||||
implementation 'com.highcapable.yukihookapi:api:<version>'
|
||||
// ❗作为 Xposed 模块使用务必添加,其它情况可选
|
||||
compileOnly 'de.robv.android.xposed:api:82'
|
||||
// ❗作为 Xposed 模块使用务必添加,其它情况可选
|
||||
ksp 'com.highcapable.yukihookapi:ksp-xposed:<version>'
|
||||
}
|
||||
```
|
||||
|
||||
请将 **<version>** 修改为 [这里](about/changelog) 的最新版本。 **← 请打开后再次刷新页面确保获取最新数据**
|
||||
|
||||
!> `YukiHookAPI` 的 `api` 与 `ksp-xposed` 依赖的版本必须一一对应,否则将会造成版本不匹配错误。
|
||||
|
||||
在你的 app `build.gradle` 中修改 `Kotlin` 的 Jvm 版本为 11 及以上。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```gradle
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '11'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!> 自 API `1.0.80` 版本后 Jvm 版本默认为 11,不再支持 1.8 及以下版本。
|
||||
|
||||
### 作为 Xposed 模块使用
|
||||
|
||||
在你的 `AndroidManifest.xml` 中添加基础代码。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```xml
|
||||
<!-- 设置为 Xposed 模块 -->
|
||||
<meta-data
|
||||
android:name="xposedmodule"
|
||||
android:value="true" />
|
||||
|
||||
<!-- 设置你的模块描述 -->
|
||||
<meta-data
|
||||
android:name="xposeddescription"
|
||||
android:value="填写你的 Xposed 模块描述" />
|
||||
|
||||
<!-- 最低 Xposed 版本号,若你正在使用 EdXposed/LSPosed,建议最低为 93 -->
|
||||
<meta-data
|
||||
android:name="xposedminversion"
|
||||
android:value="93" />
|
||||
|
||||
<!-- 可选:配置支持 New XSharePrefs 可无需调整 xposedminversion 为 93 -->
|
||||
<meta-data
|
||||
android:name="xposedsharedprefs"
|
||||
android:value="true"/>
|
||||
```
|
||||
|
||||
在你的项目中创建一个 Hook 入口类,继承于 `IYukiHookXposedInit` 并加入注解 `@InjectYukiHookWithXposed`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
@InjectYukiHookWithXposed
|
||||
class HookEntry : IYukiHookXposedInit {
|
||||
|
||||
override fun onHook() = YukiHookAPI.encase {
|
||||
// Your code here.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**(建议)** 你可以将你的模块 APP 的 `Application` 继承于 `ModuleApplication` 以实现完整使用体验。
|
||||
|
||||
详情请参考 [ModuleApplication](api/document?id=moduleapplication-class)。
|
||||
|
||||
然后,你就可以开始编写 Hook 代码了。
|
||||
|
||||
有关作为 Xposed 模块使用的相关配置详细内容,你可以 [点击这里](config/xposed-using) 继续阅读。
|
||||
|
||||
若你目前正在使用 Xposed API,你可以参考 [从 Xposed API 迁移](guide/move-to-new-api)。
|
||||
|
||||
### 作为 Hook API 使用
|
||||
|
||||
#### 集成方式
|
||||
|
||||
创建你的自定义 `Application`。
|
||||
|
||||
!> 无论使用任何 `Hook Framework`,你都需要加入其对接的 Xposed 依赖支持。
|
||||
|
||||
!> 若目标 `Hook Framework` 没有集成 Xposed API 你需要自行实现并对接 `XposedBridge`。
|
||||
|
||||
在 `attachBaseContext` 中添加 `YukiHookAPI.encase` 方法。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
// 装载 Hook Framework
|
||||
//
|
||||
// Your code here.
|
||||
//
|
||||
// 装载 YukiHookAPI
|
||||
YukiHookAPI.encase(base) {
|
||||
// Your code here.
|
||||
}
|
||||
super.attachBaseContext(base)
|
||||
}
|
||||
```
|
||||
|
||||
然后,你就可以开始编写 Hook 代码了,方式与作为 Xposed 模块使用基本一致。
|
||||
|
||||
有关作为 Hook API 使用的相关配置详细内容,你可以 [点击这里](config/api-using) 继续阅读。
|
||||
|
||||
#### 特别说明
|
||||
|
||||
!> 由于你使用了自定义的 Hook 框架而并非模块,~~`YukiHookModuleStatus`~~ ~~`YukiHookModulePrefs`~~ ~~`YukiHookDataChannel`~~ 以及 Resources Hook 功能将失效。
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](guide/example.md)
|
File diff suppressed because it is too large
Load Diff
BIN
docs/icon.png
BIN
docs/icon.png
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
Binary file not shown.
Before Width: | Height: | Size: 649 KiB |
@@ -1,97 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hans">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="YukiHookAPI - 轻量、高效、稳定的 Xposed Hook API" name="description">
|
||||
<title>YukiHookAPI - 轻量、高效、稳定的 Xposed Hook API</title>
|
||||
<link href="favicon.ico" rel="icon">
|
||||
<meta content="Description" name="description">
|
||||
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
||||
name="viewport">
|
||||
<meta http-equiv="pragram" content="no-cache" />
|
||||
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate" />
|
||||
<meta http-equiv="expires" content="0" />
|
||||
<link href="lib/themes/vue.css" rel="stylesheet">
|
||||
<script src="sw.js"></script>
|
||||
</head>
|
||||
<style>
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.router-link-active {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-edit-on-github"></script>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'YukiHookAPI',
|
||||
repo: 'https://github.com/fankes/YukiHookAPI',
|
||||
requestHeaders: {
|
||||
'Cache': 'no-cache',
|
||||
'Cache-Control': 'no-cache, must-revalidate',
|
||||
'Pragma': 'no-cache',
|
||||
'Expires': '0',
|
||||
'If-Modified-Since': 'Thu, 01 Jun 1970 00:00:00 GMT'
|
||||
},
|
||||
homepage: 'guide/home.md',
|
||||
coverpage: true,
|
||||
loadNavbar: true,
|
||||
loadSidebar: true,
|
||||
subMaxLevel: 4,
|
||||
auto2top: true,
|
||||
mergeNavbar: false,
|
||||
notFoundPage: '_404.md',
|
||||
search: {
|
||||
maxAge: 30000,
|
||||
paths: [
|
||||
'/',
|
||||
'/guide',
|
||||
'/config',
|
||||
'/about',
|
||||
'/api',
|
||||
'/api/public'
|
||||
],
|
||||
placeholder: '搜索文档',
|
||||
noData: '噫,什么都没找到~',
|
||||
depth: 7,
|
||||
hideOtherSidebarContent: true
|
||||
},
|
||||
tabs: {
|
||||
persist: true,
|
||||
sync: true,
|
||||
theme: 'classic',
|
||||
tabComments: true,
|
||||
tabHeadings: true
|
||||
},
|
||||
copyCode: {
|
||||
buttonText: '复制代码',
|
||||
errorText: '复制失败',
|
||||
successText: '已复制'
|
||||
},
|
||||
themeColor: '#2D2D2D',
|
||||
plugins: [
|
||||
EditOnGithubPlugin.create(
|
||||
'https://github.com/fankes/YukiHookAPI/blob/master/docs/', null, '在 Github 上编辑此页'
|
||||
)
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script src="lib/docsify.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-tabs@1"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/components/prism-groovy.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/components/prism-java.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.27.0/components/prism-kotlin.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||
<script>
|
||||
if (typeof navigator.serviceWorker !== 'undefined') navigator.serviceWorker.register('sw.js');
|
||||
</script>
|
||||
|
||||
</html>
|
10051
docs/lib/docsify.js
10051
docs/lib/docsify.js
File diff suppressed because it is too large
Load Diff
1
docs/lib/docsify.min.js
vendored
1
docs/lib/docsify.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,55 +0,0 @@
|
||||
(function () {
|
||||
/* eslint-disable no-unused-vars */
|
||||
var fixedPath = location.href.replace('/-/', '/#/');
|
||||
if (fixedPath !== location.href) {
|
||||
location.href = fixedPath;
|
||||
}
|
||||
|
||||
function install(hook, vm) {
|
||||
var dom = Docsify.dom;
|
||||
var disqus = vm.config.disqus;
|
||||
if (!disqus) {
|
||||
throw Error('$docsify.disqus is required');
|
||||
}
|
||||
|
||||
hook.init(function (_) {
|
||||
var script = dom.create('script');
|
||||
|
||||
script.async = true;
|
||||
script.src = "https://" + disqus + ".disqus.com/embed.js";
|
||||
script.setAttribute('data-timestamp', Number(new Date()));
|
||||
dom.appendTo(dom.body, script);
|
||||
});
|
||||
|
||||
hook.mounted(function (_) {
|
||||
var div = dom.create('div');
|
||||
div.id = 'disqus_thread';
|
||||
var main = dom.getNode('#main');
|
||||
div.style = "width: " + (main.clientWidth) + "px; margin: 0 auto 20px;";
|
||||
dom.appendTo(dom.find('.content'), div);
|
||||
|
||||
// eslint-disable-next-line
|
||||
window.disqus_config = function() {
|
||||
this.page.url = location.origin + '/-' + vm.route.path;
|
||||
this.page.identifier = vm.route.path;
|
||||
this.page.title = document.title;
|
||||
};
|
||||
});
|
||||
|
||||
hook.doneEach(function (_) {
|
||||
if (typeof window.DISQUS !== 'undefined') {
|
||||
window.DISQUS.reset({
|
||||
reload: true,
|
||||
config: function () {
|
||||
this.page.url = location.origin + '/-' + vm.route.path;
|
||||
this.page.identifier = vm.route.path;
|
||||
this.page.title = document.title;
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$docsify.plugins = [].concat(install, $docsify.plugins);
|
||||
|
||||
}());
|
1
docs/lib/plugins/disqus.min.js
vendored
1
docs/lib/plugins/disqus.min.js
vendored
@@ -1 +0,0 @@
|
||||
!function(){var i=location.href.replace("/-/","/#/");i!==location.href&&(location.href=i),$docsify.plugins=[].concat(function(i,o){var n=Docsify.dom,e=o.config.disqus;if(!e)throw Error("$docsify.disqus is required");i.init(function(i){var t=n.create("script");t.async=!0,t.src="https://"+e+".disqus.com/embed.js",t.setAttribute("data-timestamp",Number(new Date)),n.appendTo(n.body,t)}),i.mounted(function(i){var t=n.create("div");t.id="disqus_thread";var e=n.getNode("#main");t.style="width: "+e.clientWidth+"px; margin: 0 auto 20px;",n.appendTo(n.find(".content"),t),window.disqus_config=function(){this.page.url=location.origin+"/-"+o.route.path,this.page.identifier=o.route.path,this.page.title=document.title}}),i.doneEach(function(i){void 0!==window.DISQUS&&window.DISQUS.reset({reload:!0,config:function(){this.page.url=location.origin+"/-"+o.route.path,this.page.identifier=o.route.path,this.page.title=document.title}})})},$docsify.plugins)}();
|
File diff suppressed because it is too large
Load Diff
1
docs/lib/plugins/emoji.min.js
vendored
1
docs/lib/plugins/emoji.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,28 +0,0 @@
|
||||
(function () {
|
||||
function handleExternalScript() {
|
||||
var container = Docsify.dom.getNode('#main');
|
||||
var scripts = Docsify.dom.findAll(container, 'script');
|
||||
|
||||
for (var i = scripts.length; i--; ) {
|
||||
var script = scripts[i];
|
||||
|
||||
if (script && script.src) {
|
||||
var newScript = document.createElement('script');
|
||||
|
||||
Array.prototype.slice.call(script.attributes).forEach(function (attribute) {
|
||||
newScript[attribute.name] = attribute.value;
|
||||
});
|
||||
|
||||
script.parentNode.insertBefore(newScript, script);
|
||||
script.parentNode.removeChild(script);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var install = function (hook) {
|
||||
hook.doneEach(handleExternalScript);
|
||||
};
|
||||
|
||||
window.$docsify.plugins = [].concat(install, window.$docsify.plugins);
|
||||
|
||||
}());
|
1
docs/lib/plugins/external-script.min.js
vendored
1
docs/lib/plugins/external-script.min.js
vendored
@@ -1 +0,0 @@
|
||||
!function(){function e(){for(var o=Docsify.dom.getNode("#main"),e=Docsify.dom.findAll(o,"script"),n=e.length;n--;){var i,t=e[n];t&&t.src&&(i=document.createElement("script"),Array.prototype.slice.call(t.attributes).forEach(function(o){i[o.name]=o.value}),t.parentNode.insertBefore(i,t),t.parentNode.removeChild(t))}}window.$docsify.plugins=[].concat(function(o){o.doneEach(e)},window.$docsify.plugins)}();
|
@@ -1,505 +0,0 @@
|
||||
(function () {
|
||||
/**
|
||||
* Fork https://github.com/egoist/docute/blob/master/src/utils/yaml.js
|
||||
*/
|
||||
/* eslint-disable */
|
||||
/*
|
||||
YAML parser for Javascript
|
||||
Author: Diogo Costa
|
||||
This program is released under the MIT License as follows:
|
||||
Copyright (c) 2011 Diogo Costa (costa.h4evr@gmail.com)
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name YAML
|
||||
* @namespace
|
||||
*/
|
||||
|
||||
var errors = [],
|
||||
reference_blocks = [],
|
||||
processing_time = 0,
|
||||
regex = {
|
||||
regLevel: new RegExp('^([\\s\\-]+)'),
|
||||
invalidLine: new RegExp('^\\-\\-\\-|^\\.\\.\\.|^\\s*#.*|^\\s*$'),
|
||||
dashesString: new RegExp('^\\s*\\"([^\\"]*)\\"\\s*$'),
|
||||
quotesString: new RegExp("^\\s*\\'([^\\']*)\\'\\s*$"),
|
||||
float: new RegExp('^[+-]?[0-9]+\\.[0-9]+(e[+-]?[0-9]+(\\.[0-9]+)?)?$'),
|
||||
integer: new RegExp('^[+-]?[0-9]+$'),
|
||||
array: new RegExp('\\[\\s*(.*)\\s*\\]'),
|
||||
map: new RegExp('\\{\\s*(.*)\\s*\\}'),
|
||||
key_value: new RegExp('([a-z0-9_-][ a-z0-9_-]*):( .+)', 'i'),
|
||||
single_key_value: new RegExp('^([a-z0-9_-][ a-z0-9_-]*):( .+?)$', 'i'),
|
||||
key: new RegExp('([a-z0-9_-][ a-z0-9_-]+):( .+)?', 'i'),
|
||||
item: new RegExp('^-\\s+'),
|
||||
trim: new RegExp('^\\s+|\\s+$'),
|
||||
comment: new RegExp('([^\\\'\\"#]+([\\\'\\"][^\\\'\\"]*[\\\'\\"])*)*(#.*)?')
|
||||
};
|
||||
|
||||
/**
|
||||
* @class A block of lines of a given level.
|
||||
* @param {int} lvl The block's level.
|
||||
* @private
|
||||
*/
|
||||
function Block(lvl) {
|
||||
return {
|
||||
/* The block's parent */
|
||||
parent: null,
|
||||
/* Number of children */
|
||||
length: 0,
|
||||
/* Block's level */
|
||||
level: lvl,
|
||||
/* Lines of code to process */
|
||||
lines: [],
|
||||
/* Blocks with greater level */
|
||||
children: [],
|
||||
/* Add a block to the children collection */
|
||||
addChild: function(obj) {
|
||||
this.children.push(obj);
|
||||
obj.parent = this;
|
||||
++this.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parser(str) {
|
||||
var regLevel = regex['regLevel'];
|
||||
var invalidLine = regex['invalidLine'];
|
||||
var lines = str.split('\n');
|
||||
var m;
|
||||
var level = 0,
|
||||
curLevel = 0;
|
||||
|
||||
var blocks = [];
|
||||
|
||||
var result = new Block(-1);
|
||||
var currentBlock = new Block(0);
|
||||
result.addChild(currentBlock);
|
||||
var levels = [];
|
||||
var line = '';
|
||||
|
||||
blocks.push(currentBlock);
|
||||
levels.push(level);
|
||||
|
||||
for (var i = 0, len = lines.length; i < len; ++i) {
|
||||
line = lines[i];
|
||||
|
||||
if (line.match(invalidLine)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ((m = regLevel.exec(line))) {
|
||||
level = m[1].length;
|
||||
} else { level = 0; }
|
||||
|
||||
if (level > curLevel) {
|
||||
var oldBlock = currentBlock;
|
||||
currentBlock = new Block(level);
|
||||
oldBlock.addChild(currentBlock);
|
||||
blocks.push(currentBlock);
|
||||
levels.push(level);
|
||||
} else if (level < curLevel) {
|
||||
var added = false;
|
||||
|
||||
var k = levels.length - 1;
|
||||
for (; k >= 0; --k) {
|
||||
if (levels[k] == level) {
|
||||
currentBlock = new Block(level);
|
||||
blocks.push(currentBlock);
|
||||
levels.push(level);
|
||||
if (blocks[k].parent != null) { blocks[k].parent.addChild(currentBlock); }
|
||||
added = true;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
errors.push('Error: Invalid indentation at line ' + i + ': ' + line);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
currentBlock.lines.push(line.replace(regex['trim'], ''));
|
||||
curLevel = level;
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function processValue(val) {
|
||||
val = val.replace(regex['trim'], '');
|
||||
var m = null;
|
||||
|
||||
if (val == 'true') {
|
||||
return true
|
||||
} else if (val == 'false') {
|
||||
return false
|
||||
} else if (val == '.NaN') {
|
||||
return Number.NaN
|
||||
} else if (val == 'null') {
|
||||
return null
|
||||
} else if (val == '.inf') {
|
||||
return Number.POSITIVE_INFINITY
|
||||
} else if (val == '-.inf') {
|
||||
return Number.NEGATIVE_INFINITY
|
||||
} else if ((m = val.match(regex['dashesString']))) {
|
||||
return m[1]
|
||||
} else if ((m = val.match(regex['quotesString']))) {
|
||||
return m[1]
|
||||
} else if ((m = val.match(regex['float']))) {
|
||||
return parseFloat(m[0])
|
||||
} else if ((m = val.match(regex['integer']))) {
|
||||
return parseInt(m[0])
|
||||
} else if (!isNaN((m = Date.parse(val)))) {
|
||||
return new Date(m)
|
||||
} else if ((m = val.match(regex['single_key_value']))) {
|
||||
var res = {};
|
||||
res[m[1]] = processValue(m[2]);
|
||||
return res
|
||||
} else if ((m = val.match(regex['array']))) {
|
||||
var count = 0,
|
||||
c = ' ';
|
||||
var res = [];
|
||||
var content = '';
|
||||
var str = false;
|
||||
for (var j = 0, lenJ = m[1].length; j < lenJ; ++j) {
|
||||
c = m[1][j];
|
||||
if (c == "'" || c == '"') {
|
||||
if (str === false) {
|
||||
str = c;
|
||||
content += c;
|
||||
continue
|
||||
} else if ((c == "'" && str == "'") || (c == '"' && str == '"')) {
|
||||
str = false;
|
||||
content += c;
|
||||
continue
|
||||
}
|
||||
} else if (str === false && (c == '[' || c == '{')) {
|
||||
++count;
|
||||
} else if (str === false && (c == ']' || c == '}')) {
|
||||
--count;
|
||||
} else if (str === false && count == 0 && c == ',') {
|
||||
res.push(processValue(content));
|
||||
content = '';
|
||||
continue
|
||||
}
|
||||
|
||||
content += c;
|
||||
}
|
||||
|
||||
if (content.length > 0) { res.push(processValue(content)); }
|
||||
return res
|
||||
} else if ((m = val.match(regex['map']))) {
|
||||
var count = 0,
|
||||
c = ' ';
|
||||
var res = [];
|
||||
var content = '';
|
||||
var str = false;
|
||||
for (var j = 0, lenJ = m[1].length; j < lenJ; ++j) {
|
||||
c = m[1][j];
|
||||
if (c == "'" || c == '"') {
|
||||
if (str === false) {
|
||||
str = c;
|
||||
content += c;
|
||||
continue
|
||||
} else if ((c == "'" && str == "'") || (c == '"' && str == '"')) {
|
||||
str = false;
|
||||
content += c;
|
||||
continue
|
||||
}
|
||||
} else if (str === false && (c == '[' || c == '{')) {
|
||||
++count;
|
||||
} else if (str === false && (c == ']' || c == '}')) {
|
||||
--count;
|
||||
} else if (str === false && count == 0 && c == ',') {
|
||||
res.push(content);
|
||||
content = '';
|
||||
continue
|
||||
}
|
||||
|
||||
content += c;
|
||||
}
|
||||
|
||||
if (content.length > 0) { res.push(content); }
|
||||
|
||||
var newRes = {};
|
||||
for (var j = 0, lenJ = res.length; j < lenJ; ++j) {
|
||||
if ((m = res[j].match(regex['key_value']))) {
|
||||
newRes[m[1]] = processValue(m[2]);
|
||||
}
|
||||
}
|
||||
|
||||
return newRes
|
||||
} else { return val }
|
||||
}
|
||||
|
||||
function processFoldedBlock(block) {
|
||||
var lines = block.lines;
|
||||
var children = block.children;
|
||||
var str = lines.join(' ');
|
||||
var chunks = [str];
|
||||
for (var i = 0, len = children.length; i < len; ++i) {
|
||||
chunks.push(processFoldedBlock(children[i]));
|
||||
}
|
||||
return chunks.join('\n')
|
||||
}
|
||||
|
||||
function processLiteralBlock(block) {
|
||||
var lines = block.lines;
|
||||
var children = block.children;
|
||||
var str = lines.join('\n');
|
||||
for (var i = 0, len = children.length; i < len; ++i) {
|
||||
str += processLiteralBlock(children[i]);
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
function processBlock(blocks) {
|
||||
var m = null;
|
||||
var res = {};
|
||||
var lines = null;
|
||||
var children = null;
|
||||
var currentObj = null;
|
||||
|
||||
var level = -1;
|
||||
|
||||
var processedBlocks = [];
|
||||
|
||||
var isMap = true;
|
||||
|
||||
for (var j = 0, lenJ = blocks.length; j < lenJ; ++j) {
|
||||
if (level != -1 && level != blocks[j].level) { continue }
|
||||
|
||||
processedBlocks.push(j);
|
||||
|
||||
level = blocks[j].level;
|
||||
lines = blocks[j].lines;
|
||||
children = blocks[j].children;
|
||||
currentObj = null;
|
||||
|
||||
for (var i = 0, len = lines.length; i < len; ++i) {
|
||||
var line = lines[i];
|
||||
|
||||
if ((m = line.match(regex['key']))) {
|
||||
var key = m[1];
|
||||
|
||||
if (key[0] == '-') {
|
||||
key = key.replace(regex['item'], '');
|
||||
if (isMap) {
|
||||
isMap = false;
|
||||
if (typeof res.length === 'undefined') {
|
||||
res = [];
|
||||
}
|
||||
}
|
||||
if (currentObj != null) { res.push(currentObj); }
|
||||
currentObj = {};
|
||||
isMap = true;
|
||||
}
|
||||
|
||||
if (typeof m[2] != 'undefined') {
|
||||
var value = m[2].replace(regex['trim'], '');
|
||||
if (value[0] == '&') {
|
||||
var nb = processBlock(children);
|
||||
if (currentObj != null) { currentObj[key] = nb; }
|
||||
else { res[key] = nb; }
|
||||
reference_blocks[value.substr(1)] = nb;
|
||||
} else if (value[0] == '|') {
|
||||
if (currentObj != null)
|
||||
{ currentObj[key] = processLiteralBlock(children.shift()); }
|
||||
else { res[key] = processLiteralBlock(children.shift()); }
|
||||
} else if (value[0] == '*') {
|
||||
var v = value.substr(1);
|
||||
var no = {};
|
||||
|
||||
if (typeof reference_blocks[v] == 'undefined') {
|
||||
errors.push("Reference '" + v + "' not found!");
|
||||
} else {
|
||||
for (var k in reference_blocks[v]) {
|
||||
no[k] = reference_blocks[v][k];
|
||||
}
|
||||
|
||||
if (currentObj != null) { currentObj[key] = no; }
|
||||
else { res[key] = no; }
|
||||
}
|
||||
} else if (value[0] == '>') {
|
||||
if (currentObj != null)
|
||||
{ currentObj[key] = processFoldedBlock(children.shift()); }
|
||||
else { res[key] = processFoldedBlock(children.shift()); }
|
||||
} else {
|
||||
if (currentObj != null) { currentObj[key] = processValue(value); }
|
||||
else { res[key] = processValue(value); }
|
||||
}
|
||||
} else {
|
||||
if (currentObj != null) { currentObj[key] = processBlock(children); }
|
||||
else { res[key] = processBlock(children); }
|
||||
}
|
||||
} else if (line.match(/^-\s*$/)) {
|
||||
if (isMap) {
|
||||
isMap = false;
|
||||
if (typeof res.length === 'undefined') {
|
||||
res = [];
|
||||
}
|
||||
}
|
||||
if (currentObj != null) { res.push(currentObj); }
|
||||
currentObj = {};
|
||||
isMap = true;
|
||||
continue
|
||||
} else if ((m = line.match(/^-\s*(.*)/))) {
|
||||
if (currentObj != null) { currentObj.push(processValue(m[1])); }
|
||||
else {
|
||||
if (isMap) {
|
||||
isMap = false;
|
||||
if (typeof res.length === 'undefined') {
|
||||
res = [];
|
||||
}
|
||||
}
|
||||
res.push(processValue(m[1]));
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if (currentObj != null) {
|
||||
if (isMap) {
|
||||
isMap = false;
|
||||
if (typeof res.length === 'undefined') {
|
||||
res = [];
|
||||
}
|
||||
}
|
||||
res.push(currentObj);
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = processedBlocks.length - 1; j >= 0; --j) {
|
||||
blocks.splice.call(blocks, processedBlocks[j], 1);
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
function semanticAnalysis(blocks) {
|
||||
var res = processBlock(blocks.children);
|
||||
return res
|
||||
}
|
||||
|
||||
function preProcess(src) {
|
||||
var m;
|
||||
var lines = src.split('\n');
|
||||
|
||||
var r = regex['comment'];
|
||||
|
||||
for (var i in lines) {
|
||||
if ((m = lines[i].match(r))) {
|
||||
/* var cmt = "";
|
||||
if(typeof m[3] != "undefined")
|
||||
lines[i] = m[1];
|
||||
else if(typeof m[3] != "undefined")
|
||||
lines[i] = m[3];
|
||||
else
|
||||
lines[i] = "";
|
||||
*/
|
||||
if (typeof m[3] !== 'undefined') {
|
||||
lines[i] = m[0].substr(0, m[0].length - m[3].length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
function load(str) {
|
||||
errors = [];
|
||||
reference_blocks = [];
|
||||
processing_time = new Date().getTime();
|
||||
var pre = preProcess(str);
|
||||
var doc = parser(pre);
|
||||
var res = semanticAnalysis(doc);
|
||||
processing_time = new Date().getTime() - processing_time;
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork https://github.com/egoist/docute/blob/master/src/utils/front-matter.js
|
||||
*/
|
||||
|
||||
var optionalByteOrderMark = '\\ufeff?';
|
||||
var pattern =
|
||||
'^(' +
|
||||
optionalByteOrderMark +
|
||||
'(= yaml =|---)' +
|
||||
'$([\\s\\S]*?)' +
|
||||
'(?:\\2|\\.\\.\\.)' +
|
||||
'$' +
|
||||
'' +
|
||||
'(?:\\n)?)';
|
||||
// NOTE: If this pattern uses the 'g' flag the `regex` variable definition will
|
||||
// need to be moved down into the functions that use it.
|
||||
var regex$1 = new RegExp(pattern, 'm');
|
||||
|
||||
function extractor(string) {
|
||||
string = string || '';
|
||||
|
||||
var lines = string.split(/(\r?\n)/);
|
||||
if (lines[0] && /= yaml =|---/.test(lines[0])) {
|
||||
return parse(string)
|
||||
} else {
|
||||
return { attributes: {}, body: string }
|
||||
}
|
||||
}
|
||||
|
||||
function parse(string) {
|
||||
var match = regex$1.exec(string);
|
||||
|
||||
if (!match) {
|
||||
return {
|
||||
attributes: {},
|
||||
body: string
|
||||
}
|
||||
}
|
||||
|
||||
var yaml = match[match.length - 1].replace(/^\s+|\s+$/g, '');
|
||||
var attributes = load(yaml) || {};
|
||||
var body = string.replace(match[0], '');
|
||||
|
||||
return { attributes: attributes, body: body, frontmatter: yaml }
|
||||
}
|
||||
|
||||
var install = function (hook, vm) {
|
||||
// Used to remove front matter from embedded pages if installed.
|
||||
vm.config.frontMatter = {};
|
||||
vm.config.frontMatter.installed = true;
|
||||
vm.config.frontMatter.parseMarkdown = function (content) {
|
||||
var ref = extractor(content);
|
||||
var body = ref.body;
|
||||
return body;
|
||||
};
|
||||
|
||||
hook.beforeEach(function (content) {
|
||||
var ref = extractor(content);
|
||||
var attributes = ref.attributes;
|
||||
var body = ref.body;
|
||||
|
||||
vm.frontmatter = attributes;
|
||||
|
||||
return body;
|
||||
});
|
||||
};
|
||||
|
||||
$docsify.plugins = [].concat(install, $docsify.plugins);
|
||||
|
||||
}());
|
1
docs/lib/plugins/front-matter.min.js
vendored
1
docs/lib/plugins/front-matter.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,43 +0,0 @@
|
||||
(function () {
|
||||
/* eslint-disable no-console */
|
||||
// From https://github.com/egoist/vue-ga/blob/master/src/index.js
|
||||
function appendScript() {
|
||||
var script = document.createElement('script');
|
||||
script.async = true;
|
||||
script.src = 'https://www.google-analytics.com/analytics.js';
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
function init(id) {
|
||||
appendScript();
|
||||
window.ga =
|
||||
window.ga ||
|
||||
function () {
|
||||
(window.ga.q = window.ga.q || []).push(arguments);
|
||||
};
|
||||
|
||||
window.ga.l = Number(new Date());
|
||||
window.ga('create', id, 'auto');
|
||||
}
|
||||
|
||||
function collect() {
|
||||
if (!window.ga) {
|
||||
init($docsify.ga);
|
||||
}
|
||||
|
||||
window.ga('set', 'page', location.hash);
|
||||
window.ga('send', 'pageview');
|
||||
}
|
||||
|
||||
var install = function (hook) {
|
||||
if (!$docsify.ga) {
|
||||
console.error('[Docsify] ga is required.');
|
||||
return;
|
||||
}
|
||||
|
||||
hook.beforeEach(collect);
|
||||
};
|
||||
|
||||
$docsify.plugins = [].concat(install, $docsify.plugins);
|
||||
|
||||
}());
|
1
docs/lib/plugins/ga.min.js
vendored
1
docs/lib/plugins/ga.min.js
vendored
@@ -1 +0,0 @@
|
||||
!function(){function n(n){var o;(o=document.createElement("script")).async=!0,o.src="https://www.google-analytics.com/analytics.js",document.body.appendChild(o),window.ga=window.ga||function(){(window.ga.q=window.ga.q||[]).push(arguments)},window.ga.l=Number(new Date),window.ga("create",n,"auto")}function o(){window.ga||n($docsify.ga),window.ga("set","page",location.hash),window.ga("send","pageview")}$docsify.plugins=[].concat(function(n){$docsify.ga?n.beforeEach(o):console.error("[Docsify] ga is required.")},$docsify.plugins)}();
|
@@ -1,27 +0,0 @@
|
||||
(function () {
|
||||
/* eslint-disable no-unused-vars */
|
||||
function install(hook) {
|
||||
var dom = Docsify.dom;
|
||||
|
||||
hook.mounted(function (_) {
|
||||
var div = dom.create('div');
|
||||
div.id = 'gitalk-container';
|
||||
var main = dom.getNode('#main');
|
||||
div.style = "width: " + (main.clientWidth) + "px; margin: 0 auto 20px;";
|
||||
dom.appendTo(dom.find('.content'), div);
|
||||
});
|
||||
|
||||
hook.doneEach(function (_) {
|
||||
var el = document.getElementById('gitalk-container');
|
||||
while (el.hasChildNodes()) {
|
||||
el.removeChild(el.firstChild);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
gitalk.render('gitalk-container');
|
||||
});
|
||||
}
|
||||
|
||||
$docsify.plugins = [].concat(install, $docsify.plugins);
|
||||
|
||||
}());
|
1
docs/lib/plugins/gitalk.min.js
vendored
1
docs/lib/plugins/gitalk.min.js
vendored
@@ -1 +0,0 @@
|
||||
$docsify.plugins=[].concat(function(i){var e=Docsify.dom;i.mounted(function(i){var n=e.create("div");n.id="gitalk-container";var t=e.getNode("#main");n.style="width: "+t.clientWidth+"px; margin: 0 auto 20px;",e.appendTo(e.find(".content"),n)}),i.doneEach(function(i){for(var n=document.getElementById("gitalk-container");n.hasChildNodes();)n.removeChild(n.firstChild);gitalk.render("gitalk-container")})},$docsify.plugins);
|
@@ -1,42 +0,0 @@
|
||||
(function () {
|
||||
function appendScript(options) {
|
||||
var script = document.createElement('script');
|
||||
script.async = true;
|
||||
script.src = options.host + '/matomo.js';
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
function init(options) {
|
||||
window._paq = window._paq || [];
|
||||
window._paq.push(['trackPageView']);
|
||||
window._paq.push(['enableLinkTracking']);
|
||||
setTimeout(function () {
|
||||
appendScript(options);
|
||||
window._paq.push(['setTrackerUrl', options.host + '/matomo.php']);
|
||||
window._paq.push(['setSiteId', String(options.id)]);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function collect() {
|
||||
if (!window._paq) {
|
||||
init($docsify.matomo);
|
||||
}
|
||||
|
||||
window._paq.push(['setCustomUrl', window.location.hash.substr(1)]);
|
||||
window._paq.push(['setDocumentTitle', document.title]);
|
||||
window._paq.push(['trackPageView']);
|
||||
}
|
||||
|
||||
var install = function (hook) {
|
||||
if (!$docsify.matomo) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('[Docsify] matomo is required.');
|
||||
return;
|
||||
}
|
||||
|
||||
hook.beforeEach(collect);
|
||||
};
|
||||
|
||||
$docsify.plugins = [].concat(install, $docsify.plugins);
|
||||
|
||||
}());
|
1
docs/lib/plugins/matomo.min.js
vendored
1
docs/lib/plugins/matomo.min.js
vendored
@@ -1 +0,0 @@
|
||||
!function(){function o(n){window._paq=window._paq||[],window._paq.push(["trackPageView"]),window._paq.push(["enableLinkTracking"]),setTimeout(function(){var o,i;o=n,(i=document.createElement("script")).async=!0,i.src=o.host+"/matomo.js",document.body.appendChild(i),window._paq.push(["setTrackerUrl",n.host+"/matomo.php"]),window._paq.push(["setSiteId",String(n.id)])},0)}function i(){window._paq||o($docsify.matomo),window._paq.push(["setCustomUrl",window.location.hash.substr(1)]),window._paq.push(["setDocumentTitle",document.title]),window._paq.push(["trackPageView"])}$docsify.plugins=[].concat(function(o){$docsify.matomo?o.beforeEach(i):console.error("[Docsify] matomo is required.")},$docsify.plugins)}();
|
@@ -1,535 +0,0 @@
|
||||
(function () {
|
||||
/**
|
||||
* Converts a colon formatted string to a object with properties.
|
||||
*
|
||||
* This is process a provided string and look for any tokens in the format
|
||||
* of `:name[=value]` and then convert it to a object and return.
|
||||
* An example of this is ':include :type=code :fragment=demo' is taken and
|
||||
* then converted to:
|
||||
*
|
||||
* ```
|
||||
* {
|
||||
* include: '',
|
||||
* type: 'code',
|
||||
* fragment: 'demo'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param {string} str The string to parse.
|
||||
*
|
||||
* @return {object} The original string and parsed object, { str, config }.
|
||||
*/
|
||||
function getAndRemoveConfig(str) {
|
||||
if ( str === void 0 ) str = '';
|
||||
|
||||
var config = {};
|
||||
|
||||
if (str) {
|
||||
str = str
|
||||
.replace(/^('|")/, '')
|
||||
.replace(/('|")$/, '')
|
||||
.replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g, function (m, key, value) {
|
||||
if (key.indexOf(':') === -1) {
|
||||
config[key] = (value && value.replace(/"/g, '')) || true;
|
||||
return '';
|
||||
}
|
||||
|
||||
return m;
|
||||
})
|
||||
.trim();
|
||||
}
|
||||
|
||||
return { str: str, config: config };
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
var INDEXS = {};
|
||||
|
||||
var LOCAL_STORAGE = {
|
||||
EXPIRE_KEY: 'docsify.search.expires',
|
||||
INDEX_KEY: 'docsify.search.index',
|
||||
};
|
||||
|
||||
function resolveExpireKey(namespace) {
|
||||
return namespace
|
||||
? ((LOCAL_STORAGE.EXPIRE_KEY) + "/" + namespace)
|
||||
: LOCAL_STORAGE.EXPIRE_KEY;
|
||||
}
|
||||
|
||||
function resolveIndexKey(namespace) {
|
||||
return namespace
|
||||
? ((LOCAL_STORAGE.INDEX_KEY) + "/" + namespace)
|
||||
: LOCAL_STORAGE.INDEX_KEY;
|
||||
}
|
||||
|
||||
function escapeHtml(string) {
|
||||
var entityMap = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
};
|
||||
|
||||
return String(string).replace(/[&<>"']/g, function (s) { return entityMap[s]; });
|
||||
}
|
||||
|
||||
function getAllPaths(router) {
|
||||
var paths = [];
|
||||
|
||||
Docsify.dom
|
||||
.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])')
|
||||
.forEach(function (node) {
|
||||
var href = node.href;
|
||||
var originHref = node.getAttribute('href');
|
||||
var path = router.parse(href).path;
|
||||
|
||||
if (
|
||||
path &&
|
||||
paths.indexOf(path) === -1 &&
|
||||
!Docsify.util.isAbsolutePath(originHref)
|
||||
) {
|
||||
paths.push(path);
|
||||
}
|
||||
});
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
function getTableData(token) {
|
||||
if (!token.text && token.type === 'table') {
|
||||
token.cells.unshift(token.header);
|
||||
token.text = token.cells
|
||||
.map(function (rows) {
|
||||
return rows.join(' | ');
|
||||
})
|
||||
.join(' |\n ');
|
||||
}
|
||||
return token.text;
|
||||
}
|
||||
|
||||
function getListData(token) {
|
||||
if (!token.text && token.type === 'list') {
|
||||
token.text = token.raw;
|
||||
}
|
||||
return token.text;
|
||||
}
|
||||
|
||||
function saveData(maxAge, expireKey, indexKey) {
|
||||
localStorage.setItem(expireKey, Date.now() + maxAge);
|
||||
localStorage.setItem(indexKey, JSON.stringify(INDEXS));
|
||||
}
|
||||
|
||||
function genIndex(path, content, router, depth) {
|
||||
if ( content === void 0 ) content = '';
|
||||
|
||||
var tokens = window.marked.lexer(content);
|
||||
var slugify = window.Docsify.slugify;
|
||||
var index = {};
|
||||
var slug;
|
||||
var title = '';
|
||||
|
||||
tokens.forEach(function (token, tokenIndex) {
|
||||
if (token.type === 'heading' && token.depth <= depth) {
|
||||
var ref = getAndRemoveConfig(token.text);
|
||||
var str = ref.str;
|
||||
var config = ref.config;
|
||||
|
||||
if (config.id) {
|
||||
slug = router.toURL(path, { id: slugify(config.id) });
|
||||
} else {
|
||||
slug = router.toURL(path, { id: slugify(escapeHtml(token.text)) });
|
||||
}
|
||||
|
||||
if (str) {
|
||||
title = str
|
||||
.replace(/<!-- {docsify-ignore} -->/, '')
|
||||
.replace(/{docsify-ignore}/, '')
|
||||
.replace(/<!-- {docsify-ignore-all} -->/, '')
|
||||
.replace(/{docsify-ignore-all}/, '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
index[slug] = { slug: slug, title: title, body: '' };
|
||||
} else {
|
||||
if (tokenIndex === 0) {
|
||||
slug = router.toURL(path);
|
||||
index[slug] = {
|
||||
slug: slug,
|
||||
title: path !== '/' ? path.slice(1) : 'Home Page',
|
||||
body: token.text || '',
|
||||
};
|
||||
}
|
||||
|
||||
if (!slug) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!index[slug]) {
|
||||
index[slug] = { slug: slug, title: '', body: '' };
|
||||
} else if (index[slug].body) {
|
||||
token.text = getTableData(token);
|
||||
token.text = getListData(token);
|
||||
|
||||
index[slug].body += '\n' + (token.text || '');
|
||||
} else {
|
||||
token.text = getTableData(token);
|
||||
token.text = getListData(token);
|
||||
|
||||
index[slug].body = index[slug].body
|
||||
? index[slug].body + token.text
|
||||
: token.text;
|
||||
}
|
||||
}
|
||||
});
|
||||
slugify.clear();
|
||||
return index;
|
||||
}
|
||||
|
||||
function ignoreDiacriticalMarks(keyword) {
|
||||
if (keyword && keyword.normalize) {
|
||||
return keyword.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
||||
}
|
||||
return keyword;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} query Search query
|
||||
* @returns {Array} Array of results
|
||||
*/
|
||||
function search(query) {
|
||||
var matchingResults = [];
|
||||
var data = [];
|
||||
Object.keys(INDEXS).forEach(function (key) {
|
||||
data = data.concat(Object.keys(INDEXS[key]).map(function (page) { return INDEXS[key][page]; }));
|
||||
});
|
||||
|
||||
query = query.trim();
|
||||
var keywords = query.split(/[\s\-,\\/]+/);
|
||||
if (keywords.length !== 1) {
|
||||
keywords = [].concat(query, keywords);
|
||||
}
|
||||
|
||||
var loop = function ( i ) {
|
||||
var post = data[i];
|
||||
var matchesScore = 0;
|
||||
var resultStr = '';
|
||||
var handlePostTitle = '';
|
||||
var handlePostContent = '';
|
||||
var postTitle = post.title && post.title.trim();
|
||||
var postContent = post.body && post.body.trim();
|
||||
var postUrl = post.slug || '';
|
||||
|
||||
if (postTitle) {
|
||||
keywords.forEach(function (keyword) {
|
||||
// From https://github.com/sindresorhus/escape-string-regexp
|
||||
var regEx = new RegExp(
|
||||
escapeHtml(ignoreDiacriticalMarks(keyword)).replace(
|
||||
/[|\\{}()[\]^$+*?.]/g,
|
||||
'\\$&'
|
||||
),
|
||||
'gi'
|
||||
);
|
||||
var indexTitle = -1;
|
||||
var indexContent = -1;
|
||||
handlePostTitle = postTitle
|
||||
? escapeHtml(ignoreDiacriticalMarks(postTitle))
|
||||
: postTitle;
|
||||
handlePostContent = postContent
|
||||
? escapeHtml(ignoreDiacriticalMarks(postContent))
|
||||
: postContent;
|
||||
|
||||
indexTitle = postTitle ? handlePostTitle.search(regEx) : -1;
|
||||
indexContent = postContent ? handlePostContent.search(regEx) : -1;
|
||||
|
||||
if (indexTitle >= 0 || indexContent >= 0) {
|
||||
matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0;
|
||||
if (indexContent < 0) {
|
||||
indexContent = 0;
|
||||
}
|
||||
|
||||
var start = 0;
|
||||
var end = 0;
|
||||
|
||||
start = indexContent < 11 ? 0 : indexContent - 10;
|
||||
end = start === 0 ? 70 : indexContent + keyword.length + 60;
|
||||
|
||||
if (postContent && end > postContent.length) {
|
||||
end = postContent.length;
|
||||
}
|
||||
|
||||
var matchContent =
|
||||
'...' +
|
||||
handlePostContent
|
||||
.substring(start, end)
|
||||
.replace(
|
||||
regEx,
|
||||
function (word) { return ("<em class=\"search-keyword\">" + word + "</em>"); }
|
||||
) +
|
||||
'...';
|
||||
|
||||
resultStr += matchContent;
|
||||
}
|
||||
});
|
||||
|
||||
if (matchesScore > 0) {
|
||||
var matchingPost = {
|
||||
title: handlePostTitle,
|
||||
content: postContent ? resultStr : '',
|
||||
url: postUrl,
|
||||
score: matchesScore,
|
||||
};
|
||||
|
||||
matchingResults.push(matchingPost);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < data.length; i++) loop( i );
|
||||
|
||||
return matchingResults.sort(function (r1, r2) { return r2.score - r1.score; });
|
||||
}
|
||||
|
||||
function init(config, vm) {
|
||||
var isAuto = config.paths === 'auto';
|
||||
var paths = isAuto ? getAllPaths(vm.router) : config.paths;
|
||||
|
||||
var namespaceSuffix = '';
|
||||
|
||||
// only in auto mode
|
||||
if (paths.length && isAuto && config.pathNamespaces) {
|
||||
var path = paths[0];
|
||||
|
||||
if (Array.isArray(config.pathNamespaces)) {
|
||||
namespaceSuffix =
|
||||
config.pathNamespaces.filter(
|
||||
function (prefix) { return path.slice(0, prefix.length) === prefix; }
|
||||
)[0] || namespaceSuffix;
|
||||
} else if (config.pathNamespaces instanceof RegExp) {
|
||||
var matches = path.match(config.pathNamespaces);
|
||||
|
||||
if (matches) {
|
||||
namespaceSuffix = matches[0];
|
||||
}
|
||||
}
|
||||
var isExistHome = paths.indexOf(namespaceSuffix + '/') === -1;
|
||||
var isExistReadme = paths.indexOf(namespaceSuffix + '/README') === -1;
|
||||
if (isExistHome && isExistReadme) {
|
||||
paths.unshift(namespaceSuffix + '/');
|
||||
}
|
||||
} else if (paths.indexOf('/') === -1 && paths.indexOf('/README') === -1) {
|
||||
paths.unshift('/');
|
||||
}
|
||||
|
||||
var expireKey = resolveExpireKey(config.namespace) + namespaceSuffix;
|
||||
var indexKey = resolveIndexKey(config.namespace) + namespaceSuffix;
|
||||
|
||||
var isExpired = localStorage.getItem(expireKey) < Date.now();
|
||||
|
||||
INDEXS = JSON.parse(localStorage.getItem(indexKey));
|
||||
|
||||
if (isExpired) {
|
||||
INDEXS = {};
|
||||
} else if (!isAuto) {
|
||||
return;
|
||||
}
|
||||
|
||||
var len = paths.length;
|
||||
var count = 0;
|
||||
|
||||
paths.forEach(function (path) {
|
||||
if (INDEXS[path]) {
|
||||
return count++;
|
||||
}
|
||||
|
||||
Docsify.get(vm.router.getFile(path), false, vm.config.requestHeaders).then(
|
||||
function (result) {
|
||||
INDEXS[path] = genIndex(path, result, vm.router, config.depth);
|
||||
len === ++count && saveData(config.maxAge, expireKey, indexKey);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
var NO_DATA_TEXT = '';
|
||||
var options;
|
||||
|
||||
function style() {
|
||||
var code = "\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0.6em 7px;\n font-size: inherit;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search input::-ms-clear {\n display: none;\n height: 0;\n width: 0;\n}\n\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}";
|
||||
|
||||
Docsify.dom.style(code);
|
||||
}
|
||||
|
||||
function tpl(defaultValue) {
|
||||
if ( defaultValue === void 0 ) defaultValue = '';
|
||||
|
||||
var html = "<div class=\"input-wrap\">\n <input type=\"search\" value=\"" + defaultValue + "\" aria-label=\"Search text\" />\n <div class=\"clear-button\">\n <svg width=\"26\" height=\"24\">\n <circle cx=\"12\" cy=\"12\" r=\"11\" fill=\"#ccc\" />\n <path stroke=\"white\" stroke-width=\"2\" d=\"M8.25,8.25,15.75,15.75\" />\n <path stroke=\"white\" stroke-width=\"2\"d=\"M8.25,15.75,15.75,8.25\" />\n </svg>\n </div>\n </div>\n <div class=\"results-panel\"></div>\n </div>";
|
||||
var el = Docsify.dom.create('div', html);
|
||||
var aside = Docsify.dom.find('aside');
|
||||
|
||||
Docsify.dom.toggleClass(el, 'search');
|
||||
Docsify.dom.before(aside, el);
|
||||
}
|
||||
|
||||
function doSearch(value) {
|
||||
var $search = Docsify.dom.find('div.search');
|
||||
var $panel = Docsify.dom.find($search, '.results-panel');
|
||||
var $clearBtn = Docsify.dom.find($search, '.clear-button');
|
||||
var $sidebarNav = Docsify.dom.find('.sidebar-nav');
|
||||
var $appName = Docsify.dom.find('.app-name');
|
||||
|
||||
if (!value) {
|
||||
$panel.classList.remove('show');
|
||||
$clearBtn.classList.remove('show');
|
||||
$panel.innerHTML = '';
|
||||
|
||||
if (options.hideOtherSidebarContent) {
|
||||
$sidebarNav && $sidebarNav.classList.remove('hide');
|
||||
$appName && $appName.classList.remove('hide');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var matchs = search(value);
|
||||
|
||||
var html = '';
|
||||
matchs.forEach(function (post) {
|
||||
html += "<div class=\"matching-post\">\n<a href=\"" + (post.url) + "\">\n<h2>" + (post.title) + "</h2>\n<p>" + (post.content) + "</p>\n</a>\n</div>";
|
||||
});
|
||||
|
||||
$panel.classList.add('show');
|
||||
$clearBtn.classList.add('show');
|
||||
$panel.innerHTML = html || ("<p class=\"empty\">" + NO_DATA_TEXT + "</p>");
|
||||
if (options.hideOtherSidebarContent) {
|
||||
$sidebarNav && $sidebarNav.classList.add('hide');
|
||||
$appName && $appName.classList.add('hide');
|
||||
}
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
var $search = Docsify.dom.find('div.search');
|
||||
var $input = Docsify.dom.find($search, 'input');
|
||||
var $inputWrap = Docsify.dom.find($search, '.input-wrap');
|
||||
|
||||
var timeId;
|
||||
|
||||
/**
|
||||
Prevent to Fold sidebar.
|
||||
|
||||
When searching on the mobile end,
|
||||
the sidebar is collapsed when you click the INPUT box,
|
||||
making it impossible to search.
|
||||
*/
|
||||
Docsify.dom.on(
|
||||
$search,
|
||||
'click',
|
||||
function (e) { return ['A', 'H2', 'P', 'EM'].indexOf(e.target.tagName) === -1 &&
|
||||
e.stopPropagation(); }
|
||||
);
|
||||
Docsify.dom.on($input, 'input', function (e) {
|
||||
clearTimeout(timeId);
|
||||
timeId = setTimeout(function (_) { return doSearch(e.target.value.trim()); }, 100);
|
||||
});
|
||||
Docsify.dom.on($inputWrap, 'click', function (e) {
|
||||
// Click input outside
|
||||
if (e.target.tagName !== 'INPUT') {
|
||||
$input.value = '';
|
||||
doSearch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updatePlaceholder(text, path) {
|
||||
var $input = Docsify.dom.getNode('.search input[type="search"]');
|
||||
|
||||
if (!$input) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof text === 'string') {
|
||||
$input.placeholder = text;
|
||||
} else {
|
||||
var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];
|
||||
$input.placeholder = text[match];
|
||||
}
|
||||
}
|
||||
|
||||
function updateNoData(text, path) {
|
||||
if (typeof text === 'string') {
|
||||
NO_DATA_TEXT = text;
|
||||
} else {
|
||||
var match = Object.keys(text).filter(function (key) { return path.indexOf(key) > -1; })[0];
|
||||
NO_DATA_TEXT = text[match];
|
||||
}
|
||||
}
|
||||
|
||||
function updateOptions(opts) {
|
||||
options = opts;
|
||||
}
|
||||
|
||||
function init$1(opts, vm) {
|
||||
var keywords = vm.router.parse().query.s;
|
||||
|
||||
updateOptions(opts);
|
||||
style();
|
||||
tpl(keywords);
|
||||
bindEvents();
|
||||
keywords && setTimeout(function (_) { return doSearch(keywords); }, 500);
|
||||
}
|
||||
|
||||
function update(opts, vm) {
|
||||
updateOptions(opts);
|
||||
updatePlaceholder(opts.placeholder, vm.route.path);
|
||||
updateNoData(opts.noData, vm.route.path);
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
var CONFIG = {
|
||||
placeholder: 'Type to search',
|
||||
noData: 'No Results!',
|
||||
paths: 'auto',
|
||||
depth: 2,
|
||||
maxAge: 86400000, // 1 day
|
||||
hideOtherSidebarContent: false,
|
||||
namespace: undefined,
|
||||
pathNamespaces: undefined,
|
||||
};
|
||||
|
||||
var install = function (hook, vm) {
|
||||
var util = Docsify.util;
|
||||
var opts = vm.config.search || CONFIG;
|
||||
|
||||
if (Array.isArray(opts)) {
|
||||
CONFIG.paths = opts;
|
||||
} else if (typeof opts === 'object') {
|
||||
CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto';
|
||||
CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge;
|
||||
CONFIG.placeholder = opts.placeholder || CONFIG.placeholder;
|
||||
CONFIG.noData = opts.noData || CONFIG.noData;
|
||||
CONFIG.depth = opts.depth || CONFIG.depth;
|
||||
CONFIG.hideOtherSidebarContent =
|
||||
opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent;
|
||||
CONFIG.namespace = opts.namespace || CONFIG.namespace;
|
||||
CONFIG.pathNamespaces = opts.pathNamespaces || CONFIG.pathNamespaces;
|
||||
}
|
||||
|
||||
var isAuto = CONFIG.paths === 'auto';
|
||||
|
||||
hook.mounted(function (_) {
|
||||
init$1(CONFIG, vm);
|
||||
!isAuto && init(CONFIG, vm);
|
||||
});
|
||||
hook.doneEach(function (_) {
|
||||
update(CONFIG, vm);
|
||||
isAuto && init(CONFIG, vm);
|
||||
});
|
||||
};
|
||||
|
||||
$docsify.plugins = [].concat(install, $docsify.plugins);
|
||||
|
||||
}());
|
1
docs/lib/plugins/search.min.js
vendored
1
docs/lib/plugins/search.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,659 +0,0 @@
|
||||
(function () {
|
||||
/*! medium-zoom 1.0.6 | MIT License | https://github.com/francoischalifour/medium-zoom */
|
||||
var _extends = Object.assign || function (target) {
|
||||
var arguments$1 = arguments;
|
||||
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments$1[i];
|
||||
|
||||
for (var key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
var isSupported = function isSupported(node) {
|
||||
return node.tagName === 'IMG';
|
||||
};
|
||||
|
||||
/* eslint-disable-next-line no-prototype-builtins */
|
||||
var isNodeList = function isNodeList(selector) {
|
||||
return NodeList.prototype.isPrototypeOf(selector);
|
||||
};
|
||||
|
||||
var isNode = function isNode(selector) {
|
||||
return selector && selector.nodeType === 1;
|
||||
};
|
||||
|
||||
var isSvg = function isSvg(image) {
|
||||
var source = image.currentSrc || image.src;
|
||||
return source.substr(-4).toLowerCase() === '.svg';
|
||||
};
|
||||
|
||||
var getImagesFromSelector = function getImagesFromSelector(selector) {
|
||||
try {
|
||||
if (Array.isArray(selector)) {
|
||||
return selector.filter(isSupported);
|
||||
}
|
||||
|
||||
if (isNodeList(selector)) {
|
||||
// Do not use spread operator or Array.from() for IE support
|
||||
return [].slice.call(selector).filter(isSupported);
|
||||
}
|
||||
|
||||
if (isNode(selector)) {
|
||||
return [selector].filter(isSupported);
|
||||
}
|
||||
|
||||
if (typeof selector === 'string') {
|
||||
// Do not use spread operator or Array.from() for IE support
|
||||
return [].slice.call(document.querySelectorAll(selector)).filter(isSupported);
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (err) {
|
||||
throw new TypeError('The provided selector is invalid.\n' + 'Expects a CSS selector, a Node element, a NodeList or an array.\n' + 'See: https://github.com/francoischalifour/medium-zoom');
|
||||
}
|
||||
};
|
||||
|
||||
var createOverlay = function createOverlay(background) {
|
||||
var overlay = document.createElement('div');
|
||||
overlay.classList.add('medium-zoom-overlay');
|
||||
overlay.style.background = background;
|
||||
|
||||
return overlay;
|
||||
};
|
||||
|
||||
var cloneTarget = function cloneTarget(template) {
|
||||
var _template$getBounding = template.getBoundingClientRect(),
|
||||
top = _template$getBounding.top,
|
||||
left = _template$getBounding.left,
|
||||
width = _template$getBounding.width,
|
||||
height = _template$getBounding.height;
|
||||
|
||||
var clone = template.cloneNode();
|
||||
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
||||
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
|
||||
|
||||
clone.removeAttribute('id');
|
||||
clone.style.position = 'absolute';
|
||||
clone.style.top = top + scrollTop + 'px';
|
||||
clone.style.left = left + scrollLeft + 'px';
|
||||
clone.style.width = width + 'px';
|
||||
clone.style.height = height + 'px';
|
||||
clone.style.transform = '';
|
||||
|
||||
return clone;
|
||||
};
|
||||
|
||||
var createCustomEvent = function createCustomEvent(type, params) {
|
||||
var eventParams = _extends({
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
detail: undefined
|
||||
}, params);
|
||||
|
||||
if (typeof window.CustomEvent === 'function') {
|
||||
return new CustomEvent(type, eventParams);
|
||||
}
|
||||
|
||||
var customEvent = document.createEvent('CustomEvent');
|
||||
customEvent.initCustomEvent(type, eventParams.bubbles, eventParams.cancelable, eventParams.detail);
|
||||
|
||||
return customEvent;
|
||||
};
|
||||
|
||||
var mediumZoom = function mediumZoom(selector) {
|
||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
|
||||
/**
|
||||
* Ensure the compatibility with IE11 if no Promise polyfill are used.
|
||||
*/
|
||||
var Promise = window.Promise || function Promise(fn) {
|
||||
function noop() {}
|
||||
fn(noop, noop);
|
||||
};
|
||||
|
||||
var _handleClick = function _handleClick(event) {
|
||||
var target = event.target;
|
||||
|
||||
|
||||
if (target === overlay) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (images.indexOf(target) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggle({ target: target });
|
||||
};
|
||||
|
||||
var _handleScroll = function _handleScroll() {
|
||||
if (isAnimating || !active.original) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentScroll = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
||||
|
||||
if (Math.abs(scrollTop - currentScroll) > zoomOptions.scrollOffset) {
|
||||
setTimeout(close, 150);
|
||||
}
|
||||
};
|
||||
|
||||
var _handleKeyUp = function _handleKeyUp(event) {
|
||||
var key = event.key || event.keyCode;
|
||||
|
||||
// Close if escape key is pressed
|
||||
if (key === 'Escape' || key === 'Esc' || key === 27) {
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
var update = function update() {
|
||||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
||||
|
||||
var newOptions = options;
|
||||
|
||||
if (options.background) {
|
||||
overlay.style.background = options.background;
|
||||
}
|
||||
|
||||
if (options.container && options.container instanceof Object) {
|
||||
newOptions.container = _extends({}, zoomOptions.container, options.container);
|
||||
}
|
||||
|
||||
if (options.template) {
|
||||
var template = isNode(options.template) ? options.template : document.querySelector(options.template);
|
||||
|
||||
newOptions.template = template;
|
||||
}
|
||||
|
||||
zoomOptions = _extends({}, zoomOptions, newOptions);
|
||||
|
||||
images.forEach(function (image) {
|
||||
image.dispatchEvent(createCustomEvent('medium-zoom:update', {
|
||||
detail: { zoom: zoom }
|
||||
}));
|
||||
});
|
||||
|
||||
return zoom;
|
||||
};
|
||||
|
||||
var clone = function clone() {
|
||||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
||||
return mediumZoom(_extends({}, zoomOptions, options));
|
||||
};
|
||||
|
||||
var attach = function attach() {
|
||||
var arguments$1 = arguments;
|
||||
|
||||
for (var _len = arguments.length, selectors = Array(_len), _key = 0; _key < _len; _key++) {
|
||||
selectors[_key] = arguments$1[_key];
|
||||
}
|
||||
|
||||
var newImages = selectors.reduce(function (imagesAccumulator, currentSelector) {
|
||||
return [].concat(imagesAccumulator, getImagesFromSelector(currentSelector));
|
||||
}, []);
|
||||
|
||||
newImages.filter(function (newImage) {
|
||||
return images.indexOf(newImage) === -1;
|
||||
}).forEach(function (newImage) {
|
||||
images.push(newImage);
|
||||
newImage.classList.add('medium-zoom-image');
|
||||
});
|
||||
|
||||
eventListeners.forEach(function (_ref) {
|
||||
var type = _ref.type,
|
||||
listener = _ref.listener,
|
||||
options = _ref.options;
|
||||
|
||||
newImages.forEach(function (image) {
|
||||
image.addEventListener(type, listener, options);
|
||||
});
|
||||
});
|
||||
|
||||
return zoom;
|
||||
};
|
||||
|
||||
var detach = function detach() {
|
||||
var arguments$1 = arguments;
|
||||
|
||||
for (var _len2 = arguments.length, selectors = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||||
selectors[_key2] = arguments$1[_key2];
|
||||
}
|
||||
|
||||
if (active.zoomed) {
|
||||
close();
|
||||
}
|
||||
|
||||
var imagesToDetach = selectors.length > 0 ? selectors.reduce(function (imagesAccumulator, currentSelector) {
|
||||
return [].concat(imagesAccumulator, getImagesFromSelector(currentSelector));
|
||||
}, []) : images;
|
||||
|
||||
imagesToDetach.forEach(function (image) {
|
||||
image.classList.remove('medium-zoom-image');
|
||||
image.dispatchEvent(createCustomEvent('medium-zoom:detach', {
|
||||
detail: { zoom: zoom }
|
||||
}));
|
||||
});
|
||||
|
||||
images = images.filter(function (image) {
|
||||
return imagesToDetach.indexOf(image) === -1;
|
||||
});
|
||||
|
||||
return zoom;
|
||||
};
|
||||
|
||||
var on = function on(type, listener) {
|
||||
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
||||
|
||||
images.forEach(function (image) {
|
||||
image.addEventListener('medium-zoom:' + type, listener, options);
|
||||
});
|
||||
|
||||
eventListeners.push({ type: 'medium-zoom:' + type, listener: listener, options: options });
|
||||
|
||||
return zoom;
|
||||
};
|
||||
|
||||
var off = function off(type, listener) {
|
||||
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
||||
|
||||
images.forEach(function (image) {
|
||||
image.removeEventListener('medium-zoom:' + type, listener, options);
|
||||
});
|
||||
|
||||
eventListeners = eventListeners.filter(function (eventListener) {
|
||||
return !(eventListener.type === 'medium-zoom:' + type && eventListener.listener.toString() === listener.toString());
|
||||
});
|
||||
|
||||
return zoom;
|
||||
};
|
||||
|
||||
var open = function open() {
|
||||
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
||||
target = _ref2.target;
|
||||
|
||||
var _animate = function _animate() {
|
||||
var container = {
|
||||
width: document.documentElement.clientWidth,
|
||||
height: document.documentElement.clientHeight,
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
};
|
||||
var viewportWidth = void 0;
|
||||
var viewportHeight = void 0;
|
||||
|
||||
if (zoomOptions.container) {
|
||||
if (zoomOptions.container instanceof Object) {
|
||||
// The container is given as an object with properties like width, height, left, top
|
||||
container = _extends({}, container, zoomOptions.container);
|
||||
|
||||
// We need to adjust custom options like container.right or container.bottom
|
||||
viewportWidth = container.width - container.left - container.right - zoomOptions.margin * 2;
|
||||
viewportHeight = container.height - container.top - container.bottom - zoomOptions.margin * 2;
|
||||
} else {
|
||||
// The container is given as an element
|
||||
var zoomContainer = isNode(zoomOptions.container) ? zoomOptions.container : document.querySelector(zoomOptions.container);
|
||||
|
||||
var _zoomContainer$getBou = zoomContainer.getBoundingClientRect(),
|
||||
_width = _zoomContainer$getBou.width,
|
||||
_height = _zoomContainer$getBou.height,
|
||||
_left = _zoomContainer$getBou.left,
|
||||
_top = _zoomContainer$getBou.top;
|
||||
|
||||
container = _extends({}, container, {
|
||||
width: _width,
|
||||
height: _height,
|
||||
left: _left,
|
||||
top: _top
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
viewportWidth = viewportWidth || container.width - zoomOptions.margin * 2;
|
||||
viewportHeight = viewportHeight || container.height - zoomOptions.margin * 2;
|
||||
|
||||
var zoomTarget = active.zoomedHd || active.original;
|
||||
var naturalWidth = isSvg(zoomTarget) ? viewportWidth : zoomTarget.naturalWidth || viewportWidth;
|
||||
var naturalHeight = isSvg(zoomTarget) ? viewportHeight : zoomTarget.naturalHeight || viewportHeight;
|
||||
|
||||
var _zoomTarget$getBoundi = zoomTarget.getBoundingClientRect(),
|
||||
top = _zoomTarget$getBoundi.top,
|
||||
left = _zoomTarget$getBoundi.left,
|
||||
width = _zoomTarget$getBoundi.width,
|
||||
height = _zoomTarget$getBoundi.height;
|
||||
|
||||
var scaleX = Math.min(naturalWidth, viewportWidth) / width;
|
||||
var scaleY = Math.min(naturalHeight, viewportHeight) / height;
|
||||
var scale = Math.min(scaleX, scaleY);
|
||||
var translateX = (-left + (viewportWidth - width) / 2 + zoomOptions.margin + container.left) / scale;
|
||||
var translateY = (-top + (viewportHeight - height) / 2 + zoomOptions.margin + container.top) / scale;
|
||||
var transform = 'scale(' + scale + ') translate3d(' + translateX + 'px, ' + translateY + 'px, 0)';
|
||||
|
||||
active.zoomed.style.transform = transform;
|
||||
|
||||
if (active.zoomedHd) {
|
||||
active.zoomedHd.style.transform = transform;
|
||||
}
|
||||
};
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
if (target && images.indexOf(target) === -1) {
|
||||
resolve(zoom);
|
||||
return;
|
||||
}
|
||||
|
||||
var _handleOpenEnd = function _handleOpenEnd() {
|
||||
isAnimating = false;
|
||||
active.zoomed.removeEventListener('transitionend', _handleOpenEnd);
|
||||
active.original.dispatchEvent(createCustomEvent('medium-zoom:opened', {
|
||||
detail: { zoom: zoom }
|
||||
}));
|
||||
|
||||
resolve(zoom);
|
||||
};
|
||||
|
||||
if (active.zoomed) {
|
||||
resolve(zoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target) {
|
||||
// The zoom was triggered manually via a click
|
||||
active.original = target;
|
||||
} else if (images.length > 0) {
|
||||
var _images = images;
|
||||
active.original = _images[0];
|
||||
} else {
|
||||
resolve(zoom);
|
||||
return;
|
||||
}
|
||||
|
||||
active.original.dispatchEvent(createCustomEvent('medium-zoom:open', {
|
||||
detail: { zoom: zoom }
|
||||
}));
|
||||
|
||||
scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
||||
isAnimating = true;
|
||||
active.zoomed = cloneTarget(active.original);
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
if (zoomOptions.template) {
|
||||
var template = isNode(zoomOptions.template) ? zoomOptions.template : document.querySelector(zoomOptions.template);
|
||||
active.template = document.createElement('div');
|
||||
active.template.appendChild(template.content.cloneNode(true));
|
||||
|
||||
document.body.appendChild(active.template);
|
||||
}
|
||||
|
||||
document.body.appendChild(active.zoomed);
|
||||
|
||||
window.requestAnimationFrame(function () {
|
||||
document.body.classList.add('medium-zoom--opened');
|
||||
});
|
||||
|
||||
active.original.classList.add('medium-zoom-image--hidden');
|
||||
active.zoomed.classList.add('medium-zoom-image--opened');
|
||||
|
||||
active.zoomed.addEventListener('click', close);
|
||||
active.zoomed.addEventListener('transitionend', _handleOpenEnd);
|
||||
|
||||
if (active.original.getAttribute('data-zoom-src')) {
|
||||
active.zoomedHd = active.zoomed.cloneNode();
|
||||
|
||||
// Reset the `scrset` property or the HD image won't load.
|
||||
active.zoomedHd.removeAttribute('srcset');
|
||||
active.zoomedHd.removeAttribute('sizes');
|
||||
|
||||
active.zoomedHd.src = active.zoomed.getAttribute('data-zoom-src');
|
||||
|
||||
active.zoomedHd.onerror = function () {
|
||||
clearInterval(getZoomTargetSize);
|
||||
console.warn('Unable to reach the zoom image target ' + active.zoomedHd.src);
|
||||
active.zoomedHd = null;
|
||||
_animate();
|
||||
};
|
||||
|
||||
// We need to access the natural size of the full HD
|
||||
// target as fast as possible to compute the animation.
|
||||
var getZoomTargetSize = setInterval(function () {
|
||||
if ( active.zoomedHd.complete) {
|
||||
clearInterval(getZoomTargetSize);
|
||||
active.zoomedHd.classList.add('medium-zoom-image--opened');
|
||||
active.zoomedHd.addEventListener('click', close);
|
||||
document.body.appendChild(active.zoomedHd);
|
||||
_animate();
|
||||
}
|
||||
}, 10);
|
||||
} else if (active.original.hasAttribute('srcset')) {
|
||||
// If an image has a `srcset` attribuet, we don't know the dimensions of the
|
||||
// zoomed (HD) image (like when `data-zoom-src` is specified).
|
||||
// Therefore the approach is quite similar.
|
||||
active.zoomedHd = active.zoomed.cloneNode();
|
||||
|
||||
// Resetting the sizes attribute tells the browser to load the
|
||||
// image best fitting the current viewport size, respecting the `srcset`.
|
||||
active.zoomedHd.removeAttribute('sizes');
|
||||
|
||||
// In Firefox, the `loading` attribute needs to be set to `eager` (default
|
||||
// value) for the load event to be fired.
|
||||
active.zoomedHd.removeAttribute('loading');
|
||||
|
||||
// Wait for the load event of the hd image. This will fire if the image
|
||||
// is already cached.
|
||||
var loadEventListener = active.zoomedHd.addEventListener('load', function () {
|
||||
active.zoomedHd.removeEventListener('load', loadEventListener);
|
||||
active.zoomedHd.classList.add('medium-zoom-image--opened');
|
||||
active.zoomedHd.addEventListener('click', close);
|
||||
document.body.appendChild(active.zoomedHd);
|
||||
_animate();
|
||||
});
|
||||
} else {
|
||||
_animate();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var close = function close() {
|
||||
return new Promise(function (resolve) {
|
||||
if (isAnimating || !active.original) {
|
||||
resolve(zoom);
|
||||
return;
|
||||
}
|
||||
|
||||
var _handleCloseEnd = function _handleCloseEnd() {
|
||||
active.original.classList.remove('medium-zoom-image--hidden');
|
||||
document.body.removeChild(active.zoomed);
|
||||
if (active.zoomedHd) {
|
||||
document.body.removeChild(active.zoomedHd);
|
||||
}
|
||||
document.body.removeChild(overlay);
|
||||
active.zoomed.classList.remove('medium-zoom-image--opened');
|
||||
if (active.template) {
|
||||
document.body.removeChild(active.template);
|
||||
}
|
||||
|
||||
isAnimating = false;
|
||||
active.zoomed.removeEventListener('transitionend', _handleCloseEnd);
|
||||
|
||||
active.original.dispatchEvent(createCustomEvent('medium-zoom:closed', {
|
||||
detail: { zoom: zoom }
|
||||
}));
|
||||
|
||||
active.original = null;
|
||||
active.zoomed = null;
|
||||
active.zoomedHd = null;
|
||||
active.template = null;
|
||||
|
||||
resolve(zoom);
|
||||
};
|
||||
|
||||
isAnimating = true;
|
||||
document.body.classList.remove('medium-zoom--opened');
|
||||
active.zoomed.style.transform = '';
|
||||
|
||||
if (active.zoomedHd) {
|
||||
active.zoomedHd.style.transform = '';
|
||||
}
|
||||
|
||||
// Fade out the template so it's not too abrupt
|
||||
if (active.template) {
|
||||
active.template.style.transition = 'opacity 150ms';
|
||||
active.template.style.opacity = 0;
|
||||
}
|
||||
|
||||
active.original.dispatchEvent(createCustomEvent('medium-zoom:close', {
|
||||
detail: { zoom: zoom }
|
||||
}));
|
||||
|
||||
active.zoomed.addEventListener('transitionend', _handleCloseEnd);
|
||||
});
|
||||
};
|
||||
|
||||
var toggle = function toggle() {
|
||||
var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
||||
target = _ref3.target;
|
||||
|
||||
if (active.original) {
|
||||
return close();
|
||||
}
|
||||
|
||||
return open({ target: target });
|
||||
};
|
||||
|
||||
var getOptions = function getOptions() {
|
||||
return zoomOptions;
|
||||
};
|
||||
|
||||
var getImages = function getImages() {
|
||||
return images;
|
||||
};
|
||||
|
||||
var getZoomedImage = function getZoomedImage() {
|
||||
return active.original;
|
||||
};
|
||||
|
||||
var images = [];
|
||||
var eventListeners = [];
|
||||
var isAnimating = false;
|
||||
var scrollTop = 0;
|
||||
var zoomOptions = options;
|
||||
var active = {
|
||||
original: null,
|
||||
zoomed: null,
|
||||
zoomedHd: null,
|
||||
template: null
|
||||
|
||||
// If the selector is omitted, it's replaced by the options
|
||||
};if (Object.prototype.toString.call(selector) === '[object Object]') {
|
||||
zoomOptions = selector;
|
||||
} else if (selector || typeof selector === 'string' // to process empty string as a selector
|
||||
) {
|
||||
attach(selector);
|
||||
}
|
||||
|
||||
// Apply the default option values
|
||||
zoomOptions = _extends({
|
||||
margin: 0,
|
||||
background: '#fff',
|
||||
scrollOffset: 40,
|
||||
container: null,
|
||||
template: null
|
||||
}, zoomOptions);
|
||||
|
||||
var overlay = createOverlay(zoomOptions.background);
|
||||
|
||||
document.addEventListener('click', _handleClick);
|
||||
document.addEventListener('keyup', _handleKeyUp);
|
||||
document.addEventListener('scroll', _handleScroll);
|
||||
window.addEventListener('resize', close);
|
||||
|
||||
var zoom = {
|
||||
open: open,
|
||||
close: close,
|
||||
toggle: toggle,
|
||||
update: update,
|
||||
clone: clone,
|
||||
attach: attach,
|
||||
detach: detach,
|
||||
on: on,
|
||||
off: off,
|
||||
getOptions: getOptions,
|
||||
getImages: getImages,
|
||||
getZoomedImage: getZoomedImage
|
||||
};
|
||||
|
||||
return zoom;
|
||||
};
|
||||
|
||||
function styleInject(css, ref) {
|
||||
if ( ref === void 0 ) { ref = {}; }
|
||||
var insertAt = ref.insertAt;
|
||||
|
||||
if (!css || typeof document === 'undefined') { return; }
|
||||
|
||||
var head = document.head || document.getElementsByTagName('head')[0];
|
||||
var style = document.createElement('style');
|
||||
style.type = 'text/css';
|
||||
|
||||
if (insertAt === 'top') {
|
||||
if (head.firstChild) {
|
||||
head.insertBefore(style, head.firstChild);
|
||||
} else {
|
||||
head.appendChild(style);
|
||||
}
|
||||
} else {
|
||||
head.appendChild(style);
|
||||
}
|
||||
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = css;
|
||||
} else {
|
||||
style.appendChild(document.createTextNode(css));
|
||||
}
|
||||
}
|
||||
|
||||
var css = ".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}";
|
||||
styleInject(css);
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
var matchesSelector =
|
||||
Element.prototype.matches ||
|
||||
Element.prototype.webkitMatchesSelector ||
|
||||
Element.prototype.msMatchesSelector;
|
||||
|
||||
function install(hook) {
|
||||
var zoom;
|
||||
|
||||
hook.doneEach(function (_) {
|
||||
var elms = Array.apply(
|
||||
null,
|
||||
document.querySelectorAll(
|
||||
'.markdown-section img:not(.emoji):not([data-no-zoom])'
|
||||
)
|
||||
);
|
||||
|
||||
elms = elms.filter(function (elm) { return matchesSelector.call(elm, 'a img') === false; });
|
||||
|
||||
if (zoom) {
|
||||
zoom.detach();
|
||||
}
|
||||
|
||||
zoom = mediumZoom(elms);
|
||||
});
|
||||
}
|
||||
|
||||
$docsify.plugins = [].concat(install, $docsify.plugins);
|
||||
|
||||
}());
|
1
docs/lib/plugins/zoom-image.min.js
vendored
1
docs/lib/plugins/zoom-image.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
90
docs/sw.js
90
docs/sw.js
@@ -1,90 +0,0 @@
|
||||
/* ===========================================================
|
||||
* docsify sw.js
|
||||
* ===========================================================
|
||||
* Copyright 2016 @huxpro
|
||||
* Licensed under Apache 2.0
|
||||
* Register service worker.
|
||||
* ========================================================== */
|
||||
|
||||
const RUNTIME = 'docsify'
|
||||
const HOSTNAME_WHITELIST = [
|
||||
self.location.hostname,
|
||||
'fonts.gstatic.com',
|
||||
'fonts.googleapis.com',
|
||||
'unpkg.com',
|
||||
]
|
||||
|
||||
// The Util Function to hack URLs of intercepted requests
|
||||
const getFixedUrl = (req) => {
|
||||
var now = Date.now()
|
||||
var url = new URL(req.url)
|
||||
|
||||
// 1. fixed http URL
|
||||
// Just keep syncing with location.protocol
|
||||
// fetch(httpURL) belongs to active mixed content.
|
||||
// And fetch(httpRequest) is not supported yet.
|
||||
url.protocol = self.location.protocol
|
||||
|
||||
// 2. add query for caching-busting.
|
||||
// GitHub Pages served with Cache-Control: max-age=600
|
||||
// max-age on mutable content is error-prone, with SW life of bugs can even extend.
|
||||
// Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
|
||||
// Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190
|
||||
if (url.hostname === self.location.hostname) {
|
||||
url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
|
||||
}
|
||||
return url.href
|
||||
}
|
||||
|
||||
/**
|
||||
* @Lifecycle Activate
|
||||
* New one activated when old isn't being used.
|
||||
*
|
||||
* waitUntil(): activating ====> activated
|
||||
*/
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
|
||||
/**
|
||||
* @Functional Fetch
|
||||
* All network requests are being intercepted here.
|
||||
*
|
||||
* void respondWith(Promise<Response> r)
|
||||
*/
|
||||
self.addEventListener('fetch', (event) => {
|
||||
// Skip some of cross-origin requests, like those for Google Analytics.
|
||||
if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) {
|
||||
// Stale-while-revalidate
|
||||
// similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale
|
||||
// Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1
|
||||
const cached = caches.match(event.request)
|
||||
const fixedUrl = getFixedUrl(event.request)
|
||||
const fetched = fetch(fixedUrl, { cache: 'no-store' })
|
||||
const fetchedCopy = fetched.then((resp) => resp.clone())
|
||||
|
||||
// Call respondWith() with whatever we get first.
|
||||
// If the fetch fails ('e.g' disconnected), wait for the cache.
|
||||
// If there’s nothing in cache, wait for the fetch.
|
||||
// If neither yields a response, return offline pages.
|
||||
event.respondWith(
|
||||
Promise.race([fetched.catch((_) => cached), cached])
|
||||
.then((resp) => resp || fetched)
|
||||
.catch((_) => {
|
||||
/* eat any errors */
|
||||
}),
|
||||
)
|
||||
|
||||
// Update the cache with the version we fetched (only for ok status)
|
||||
event.waitUntil(
|
||||
Promise.all([fetchedCopy, caches.open(RUNTIME)])
|
||||
.then(
|
||||
([response, cache]) =>
|
||||
response.ok && cache.put(event.request, response),
|
||||
)
|
||||
.catch((_) => {
|
||||
/* eat any errors */
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
@@ -1,38 +0,0 @@
|
||||
# YukiHookAPI 构建工具
|
||||
|
||||
> 这是一个使用 YukiHookAPI 作为核心的 Xposed 模块自动构建工具,实现自动化搜索依赖快速搭建一个包含 Xposed 模块环境的 Android 项目模板。
|
||||
|
||||
<img src="https://raw.githubusercontent.com/fankes/YukiHookAPI/master/docs/img-src/yukihookapi-projectbuilder.png" width = "720"/>
|
||||
|
||||
## 获取项目
|
||||
|
||||
本项目完全开源免费,且会根据大家的使用情况持续维护,初代版本可能存在不完善或 BUG,欢迎反馈。
|
||||
|
||||
项目地址 https://github.com/fankes/YukiHookAPI-ProjectBuilder
|
||||
|
||||
若你想直接下载可以 [点击这里](https://github.com/fankes/YukiHookAPI-ProjectBuilder/releases) 前往 Release 发布地址。
|
||||
|
||||
## 使用方法
|
||||
|
||||
> 这里包含了基本的使用方法和功能讲解。
|
||||
|
||||
### 基本用法
|
||||
|
||||
- 软件使用过程需要当前设备已连接互联网,若项目依赖无法搜索请尝试 **科学上网** 。
|
||||
|
||||
- 启动软件后,对你期望创建的项目进行简单配置,点击右下角的 **按钮** 或菜单栏选择 **项目 > 开始创建** 即可开始任务。
|
||||
|
||||
- 等待依赖自动搜索完成后,你可以对项目依赖进行配置,建议使用最新依赖保证项目的稳定性,构建工具也仅对最新依赖进行支持。
|
||||
|
||||
- 选择你需要创建项目的目录,开始后项目会在你选择的位置中创建,并等待整个过程完成,项目就创建好了。
|
||||
|
||||
### 配置模板
|
||||
|
||||
配置模板是一个扩展功能,你可以选择菜单栏 **项目 > 配置模板 > 新建模板**,即可使用当前填写的配置创建一份模板,下次可以从 **已保存的模板** 中重新载入模板,即可还原当前配置。
|
||||
|
||||
### 多语言支持
|
||||
|
||||
软件语言跟随当前系统,你可以在菜单栏 **语言 (Language)** 一栏手动选择软件的界面语言。
|
||||
|
||||
<br/><br/>
|
||||
[浏览下一篇 ➡️](api/home.md)
|
Reference in New Issue
Block a user