mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-06 02:35:40 +08:00
Merge YukiHookModuleStatus to YukiHookAPI.Status
This commit is contained in:
@@ -74,7 +74,7 @@ class HookEntry : IYukiHookXposedInit {
|
|||||||
isEnableModuleAppResourcesCache = true
|
isEnableModuleAppResourcesCache = true
|
||||||
// 是否启用 Hook Xposed 模块激活等状态功能
|
// 是否启用 Hook Xposed 模块激活等状态功能
|
||||||
// 为原生支持 Xposed 模块激活状态检测 - 此功能默认启用
|
// 为原生支持 Xposed 模块激活状态检测 - 此功能默认启用
|
||||||
// 关闭后你将不能再使用 [YukiHookModuleStatus] 中的功能
|
// 关闭后你将不能再在模块环境中使用 [YukiHookAPI.Status] 中的功能
|
||||||
// 功能启用后 - 将会在宿主启动时自动 Hook [YukiHookModuleStatus]
|
// 功能启用后 - 将会在宿主启动时自动 Hook [YukiHookModuleStatus]
|
||||||
isEnableHookModuleStatus = true
|
isEnableHookModuleStatus = true
|
||||||
// 是否启用当前 Xposed 模块与宿主交互的 [YukiHookDataChannel] 功能
|
// 是否启用当前 Xposed 模块与宿主交互的 [YukiHookDataChannel] 功能
|
||||||
@@ -156,7 +156,12 @@ class HookEntry : IYukiHookXposedInit {
|
|||||||
if (prefs.getBoolean("show_dialog_when_demo_app_opend"))
|
if (prefs.getBoolean("show_dialog_when_demo_app_opend"))
|
||||||
AlertDialog.Builder(instance())
|
AlertDialog.Builder(instance())
|
||||||
.setTitle("Hooked")
|
.setTitle("Hooked")
|
||||||
.setMessage("This App has been hooked!")
|
.setMessage(
|
||||||
|
"This App has been hooked!\n\n" +
|
||||||
|
"Hook Framework: ${YukiHookAPI.Status.executorName}\n\n" +
|
||||||
|
"Xposed API Version: ${YukiHookAPI.Status.executorVersion}\n\n" +
|
||||||
|
"Support Resources Hook: ${YukiHookAPI.Status.isSupportResourcesHook}"
|
||||||
|
)
|
||||||
.setPositiveButton("OK", null)
|
.setPositiveButton("OK", null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
@@ -37,10 +37,7 @@ import com.highcapable.yukihookapi.YukiHookAPI
|
|||||||
import com.highcapable.yukihookapi.demo_module.data.DataConst
|
import com.highcapable.yukihookapi.demo_module.data.DataConst
|
||||||
import com.highcapable.yukihookapi.demo_module.databinding.ActivityMainBinding
|
import com.highcapable.yukihookapi.demo_module.databinding.ActivityMainBinding
|
||||||
import com.highcapable.yukihookapi.hook.factory.dataChannel
|
import com.highcapable.yukihookapi.hook.factory.dataChannel
|
||||||
import com.highcapable.yukihookapi.hook.factory.isModuleActive
|
|
||||||
import com.highcapable.yukihookapi.hook.factory.isSupportResourcesHook
|
|
||||||
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
import com.highcapable.yukihookapi.hook.factory.modulePrefs
|
||||||
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@@ -53,17 +50,17 @@ class MainActivity : AppCompatActivity() {
|
|||||||
Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT).show()
|
Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
moduleDemoActiveText.text = "Module is Active:$isModuleActive"
|
moduleDemoActiveText.text = "Module is Active:${YukiHookAPI.Status.isModuleActive}"
|
||||||
moduleDemoActiveZhText.text = "Xposed 模块激活状态"
|
moduleDemoActiveZhText.text = "Xposed 模块激活状态"
|
||||||
moduleDemoFrameworkText.text = "Hook Framework:${YukiHookModuleStatus.executorName}"
|
moduleDemoFrameworkText.text = "Hook Framework:${YukiHookAPI.Status.executorName}"
|
||||||
moduleDemoFrameworkZhText.text = "当前的 Hook 框架"
|
moduleDemoFrameworkZhText.text = "当前的 Hook 框架"
|
||||||
moduleDemoApiVersionText.text = "Xposed API Version:${YukiHookModuleStatus.executorVersion}"
|
moduleDemoApiVersionText.text = "Xposed API Version:${YukiHookAPI.Status.executorVersion}"
|
||||||
moduleDemoApiVersionZhText.text = "Xposed API 版本"
|
moduleDemoApiVersionZhText.text = "Xposed API 版本"
|
||||||
moduleDemoYukiHookApiVersionText.text = "YukiHookAPI Version:${YukiHookAPI.API_VERSION_NAME}(${YukiHookAPI.API_VERSION_CODE})"
|
moduleDemoYukiHookApiVersionText.text = "YukiHookAPI Version:${YukiHookAPI.API_VERSION_NAME}(${YukiHookAPI.API_VERSION_CODE})"
|
||||||
moduleDemoYukiHookApiVersionZhText.text = "YukiHookAPI 版本"
|
moduleDemoYukiHookApiVersionZhText.text = "YukiHookAPI 版本"
|
||||||
moduleDemoNewXshareText.text = "New XShare Mode:${modulePrefs.isRunInNewXShareMode}"
|
moduleDemoNewXshareText.text = "New XShare Mode:${modulePrefs.isRunInNewXShareMode}"
|
||||||
moduleDemoNewXshareZhText.text = "New XShare 模式支持状态"
|
moduleDemoNewXshareZhText.text = "New XShare 模式支持状态"
|
||||||
moduleDemoResHookText.text = "Support Resources Hook:$isSupportResourcesHook"
|
moduleDemoResHookText.text = "Support Resources Hook:${YukiHookAPI.Status.isSupportResourcesHook}"
|
||||||
moduleDemoResHookZhText.text = "资源钩子支持状态"
|
moduleDemoResHookZhText.text = "资源钩子支持状态"
|
||||||
moduleDemoEditText.also {
|
moduleDemoEditText.also {
|
||||||
it.setText(modulePrefs.get(DataConst.TEST_KV_DATA))
|
it.setText(modulePrefs.get(DataConst.TEST_KV_DATA))
|
||||||
|
@@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
[filename](public/HookParam.md ':include')
|
[filename](public/HookParam.md ':include')
|
||||||
|
|
||||||
[filename](public/YukiHookModuleStatus.md ':include')
|
|
||||||
|
|
||||||
[filename](public/InjectYukiHookWithXposed.md ':include')
|
[filename](public/InjectYukiHookWithXposed.md ':include')
|
||||||
|
|
||||||
[filename](public/IYukiHookXposedInit.md ':include')
|
[filename](public/IYukiHookXposedInit.md ':include')
|
||||||
|
@@ -26,6 +26,8 @@ open class ModuleApplication: Application()
|
|||||||
|
|
||||||
- 在模块中使用系统隐藏 API,核心技术引用了开源项目 [FreeReflection](https://github.com/tiann/FreeReflection)
|
- 在模块中使用系统隐藏 API,核心技术引用了开源项目 [FreeReflection](https://github.com/tiann/FreeReflection)
|
||||||
|
|
||||||
|
- 在模块中使用 `YukiHookAPI.Status.isTaiChiModuleActive` 判断太极、无极激活状态
|
||||||
|
|
||||||
**功能示例**
|
**功能示例**
|
||||||
|
|
||||||
将此类继承到你的自定义 `Application` 上。
|
将此类继承到你的自定义 `Application` 上。
|
||||||
|
@@ -40,7 +40,41 @@ const val API_VERSION_CODE: Int
|
|||||||
|
|
||||||
> 获取当前 `YukiHookAPI` 的版本号。
|
> 获取当前 `YukiHookAPI` 的版本号。
|
||||||
|
|
||||||
### executorName [field]
|
### ~~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` 的状态。
|
||||||
|
|
||||||
|
#### executorName [field]
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val executorName: String
|
val executorName: String
|
||||||
@@ -48,7 +82,7 @@ val executorName: String
|
|||||||
|
|
||||||
**变更记录**
|
**变更记录**
|
||||||
|
|
||||||
`v1.0.5` `新增`
|
`v1.0.91` `新增`
|
||||||
|
|
||||||
**功能描述**
|
**功能描述**
|
||||||
|
|
||||||
@@ -56,7 +90,9 @@ val executorName: String
|
|||||||
|
|
||||||
无法获取会返回 `unknown`,`XposedBridge` 不存在会返回 `invalid`。
|
无法获取会返回 `unknown`,`XposedBridge` 不存在会返回 `invalid`。
|
||||||
|
|
||||||
### executorVersion [field]
|
!> 在模块环境中需要启用 `Configs.isEnableHookModuleStatus`。
|
||||||
|
|
||||||
|
#### executorVersion [field]
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val executorVersion: Int
|
val executorVersion: Int
|
||||||
@@ -64,7 +100,7 @@ val executorVersion: Int
|
|||||||
|
|
||||||
**变更记录**
|
**变更记录**
|
||||||
|
|
||||||
`v1.0.5` `新增`
|
`v1.0.91` `新增`
|
||||||
|
|
||||||
**功能描述**
|
**功能描述**
|
||||||
|
|
||||||
@@ -72,6 +108,82 @@ val executorVersion: Int
|
|||||||
|
|
||||||
无法获取会返回 `-1`。
|
无法获取会返回 `-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。
|
||||||
|
|
||||||
### Configs [object]
|
### Configs [object]
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
@@ -188,7 +300,7 @@ var isEnableHookModuleStatus: Boolean
|
|||||||
|
|
||||||
为原生支持 Xposed 模块激活状态检测,此功能默认启用。
|
为原生支持 Xposed 模块激活状态检测,此功能默认启用。
|
||||||
|
|
||||||
!> 关闭后你将不能再使用 `YukiHookModuleStatus` 中的功能。
|
!> 关闭后你将不能再在模块环境中使用 `YukiHookAPI.Status` 中的功能。
|
||||||
|
|
||||||
#### isEnableDataChannel [field]
|
#### isEnableDataChannel [field]
|
||||||
|
|
||||||
|
@@ -108,58 +108,52 @@ val Context.processName: String
|
|||||||
|
|
||||||
> 获取当前进程名称。
|
> 获取当前进程名称。
|
||||||
|
|
||||||
### isSupportResourcesHook [field]
|
### ~~isSupportResourcesHook [field]~~ <!-- {docsify-ignore} -->
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val Any?.isSupportResourcesHook: Boolean
|
|
||||||
```
|
|
||||||
|
|
||||||
**变更记录**
|
**变更记录**
|
||||||
|
|
||||||
`v1.0.80` `新增`
|
`v1.0.80` `新增`
|
||||||
|
|
||||||
**功能描述**
|
`v1.0.91` `移除`
|
||||||
|
|
||||||
> 判断当前 Hook Framework 是否支持资源钩子(Resources Hook)。
|
请转移到 `YukiHookAPI.Status.isSupportResourcesHook`
|
||||||
|
|
||||||
### isModuleActive [field]
|
### ~~isModuleActive [field]~~ <!-- {docsify-ignore} -->
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val Context.isModuleActive: Boolean
|
|
||||||
```
|
|
||||||
|
|
||||||
**变更记录**
|
**变更记录**
|
||||||
|
|
||||||
`v1.0.6` `新增`
|
`v1.0.6` `新增`
|
||||||
|
|
||||||
**功能描述**
|
`v1.0.91` `移除`
|
||||||
|
|
||||||
> 判断模块是否在 Xposed 或太极、无极中激活。
|
请转移到 `YukiHookAPI.Status.isModuleActive`
|
||||||
|
|
||||||
### isXposedModuleActive [field]
|
### ~~isXposedModuleActive [field]~~ <!-- {docsify-ignore} -->
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val Any?.isXposedModuleActive: Boolean
|
|
||||||
```
|
|
||||||
|
|
||||||
**变更记录**
|
**变更记录**
|
||||||
|
|
||||||
`v1.0.6` `新增`
|
`v1.0.6` `新增`
|
||||||
|
|
||||||
**功能描述**
|
`v1.0.91` `移除`
|
||||||
|
|
||||||
> 仅判断模块是否在 Xposed 中激活。
|
请转移到 `YukiHookAPI.Status.isXposedModuleActive`
|
||||||
|
|
||||||
### isTaiChiModuleActive [field]
|
### ~~isTaiChiModuleActive [field]~~ <!-- {docsify-ignore} -->
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val Context.isTaiChiModuleActive: Boolean
|
|
||||||
```
|
|
||||||
|
|
||||||
**变更记录**
|
**变更记录**
|
||||||
|
|
||||||
`v1.0` `添加`
|
`v1.0` `添加`
|
||||||
|
|
||||||
**功能描述**
|
`v1.0.91` `移除`
|
||||||
|
|
||||||
> 仅判断模块是否在太极、无极中激活。
|
请转移到 `YukiHookAPI.Status.isTaiChiModuleActive`
|
||||||
|
|
||||||
|
## ~~YukiHookModuleStatus [class]~~ <!-- {docsify-ignore} -->
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0` `添加`
|
||||||
|
|
||||||
|
`v1.0.91` `作废`
|
||||||
|
|
||||||
|
请转移到 `YukiHookAPI.Status`
|
@@ -1,55 +0,0 @@
|
|||||||
## YukiHookModuleStatus [class]
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
object YukiHookModuleStatus
|
|
||||||
```
|
|
||||||
|
|
||||||
**变更记录**
|
|
||||||
|
|
||||||
`v1.0` `添加`
|
|
||||||
|
|
||||||
**功能描述**
|
|
||||||
|
|
||||||
> 这是一个 Xposed 模块 Hook 状态类。
|
|
||||||
|
|
||||||
### executorName [field]
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val executorName: String
|
|
||||||
```
|
|
||||||
|
|
||||||
**变更记录**
|
|
||||||
|
|
||||||
`v1.0.5` `新增`
|
|
||||||
|
|
||||||
**功能描述**
|
|
||||||
|
|
||||||
> 获取当前 Hook 框架的名称。
|
|
||||||
|
|
||||||
模块未激活会返回 `unknown`,获取过程发生错误会返回 `invalid`。
|
|
||||||
|
|
||||||
### executorVersion [field]
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val executorVersion: Int
|
|
||||||
```
|
|
||||||
|
|
||||||
**变更记录**
|
|
||||||
|
|
||||||
`v1.0.5` `新增`
|
|
||||||
|
|
||||||
**功能描述**
|
|
||||||
|
|
||||||
> 获取当前 Hook 框架的版本。
|
|
||||||
|
|
||||||
模块未激活会返回 `-1`。
|
|
||||||
|
|
||||||
### ~~isActive [method]~~ <!-- {docsify-ignore} -->
|
|
||||||
|
|
||||||
**变更记录**
|
|
||||||
|
|
||||||
`v1.0` `添加`
|
|
||||||
|
|
||||||
`v1.0.6` `作废`
|
|
||||||
|
|
||||||
请使用 `isModuleActive`、`isXposedModuleActive` 或 `isTaiChiModuleActive`
|
|
@@ -454,41 +454,43 @@ override fun onHook() = encase {
|
|||||||
|
|
||||||
在 `YukiHookAPI` 中你完全不需要再这么做了,`YukiHookAPI` 已经帮你封装好了这个操作,你可以直接进行使用。
|
在 `YukiHookAPI` 中你完全不需要再这么做了,`YukiHookAPI` 已经帮你封装好了这个操作,你可以直接进行使用。
|
||||||
|
|
||||||
现在,你可以直接使用 `isXposedModuleActive` 在模块中判断自身是否被激活。
|
现在,你可以直接使用 `YukiHookAPI.Status.isXposedModuleActive` 在模块中判断自身是否被激活。
|
||||||
|
|
||||||
> 示例如下
|
> 示例如下
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
if(isXposedModuleActive) {
|
if(YukiHookAPI.Status.isXposedModuleActive) {
|
||||||
// Your code here.
|
// Your code here.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
由于一些特殊原因,在太极、无极中的模块无法使用标准方法检测激活状态。
|
由于一些特殊原因,在太极、无极中的模块无法使用标准方法检测激活状态。
|
||||||
|
|
||||||
此时你可以在 `Activity` 中使用 `isTaiChiModuleActive` 判断自身是否被激活。
|
此时你可以使用 `YukiHookAPI.Status.isTaiChiModuleActive` 判断自身是否被激活。
|
||||||
|
|
||||||
> 示例如下
|
> 示例如下
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
if(isTaiChiModuleActive) {
|
if(YukiHookAPI.Status.isTaiChiModuleActive) {
|
||||||
// Your code here.
|
// Your code here.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
若你想使用两者得兼的判断方案,`YukiHookAPI` 同样为你封装了便捷的方式。
|
若你想使用两者得兼的判断方案,`YukiHookAPI` 同样为你封装了便捷的方式。
|
||||||
|
|
||||||
此时你可以在 `Activity` 中使用 `isModuleActive` 判断自身是否在 Xposed 或太极、无极中被激活。
|
此时你可以使用 `YukiHookAPI.Status.isModuleActive` 判断自身是否在 Xposed 或太极、无极中被激活。
|
||||||
|
|
||||||
> 示例如下
|
> 示例如下
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
if(isModuleActive) {
|
if(YukiHookAPI.Status.isModuleActive) {
|
||||||
// Your code here.
|
// Your code here.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
若要了解更多可 [点击这里](api/document?id=ismoduleactive-field) 进行查看。
|
若要了解更多可 [点击这里](api/document?id=status-object) 进行查看。
|
||||||
|
|
||||||
|
!> 新版本的 API 修改了激活逻辑判断方式,现在你可以在模块与 Hook APP(宿主)中同时使用此 API。
|
||||||
|
|
||||||
!> 需要确保 `YukiHookAPI.Configs.isEnableHookModuleStatus` 是启用状态。
|
!> 需要确保 `YukiHookAPI.Configs.isEnableHookModuleStatus` 是启用状态。
|
||||||
|
|
||||||
|
@@ -38,15 +38,16 @@ import com.highcapable.yukihookapi.hook.core.finder.ConstructorFinder
|
|||||||
import com.highcapable.yukihookapi.hook.core.finder.FieldFinder
|
import com.highcapable.yukihookapi.hook.core.finder.FieldFinder
|
||||||
import com.highcapable.yukihookapi.hook.core.finder.MethodFinder
|
import com.highcapable.yukihookapi.hook.core.finder.MethodFinder
|
||||||
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||||
|
import com.highcapable.yukihookapi.hook.factory.isTaiChiModuleActive
|
||||||
import com.highcapable.yukihookapi.hook.factory.processName
|
import com.highcapable.yukihookapi.hook.factory.processName
|
||||||
import com.highcapable.yukihookapi.hook.log.*
|
import com.highcapable.yukihookapi.hook.log.*
|
||||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||||
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
|
import com.highcapable.yukihookapi.hook.param.type.HookEntryType
|
||||||
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
|
import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper
|
||||||
import com.highcapable.yukihookapi.hook.store.MemberCacheStore
|
import com.highcapable.yukihookapi.hook.store.MemberCacheStore
|
||||||
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
|
|
||||||
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
|
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
|
||||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus
|
||||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||||
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
||||||
import de.robv.android.xposed.XposedBridge
|
import de.robv.android.xposed.XposedBridge
|
||||||
@@ -79,23 +80,77 @@ object YukiHookAPI {
|
|||||||
const val API_VERSION_CODE = 31
|
const val API_VERSION_CODE = 31
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前 Hook 框架的名称
|
* 当前 [YukiHookAPI] 的状态
|
||||||
*
|
|
||||||
* 从 [XposedBridge] 获取 TAG
|
|
||||||
* @return [String] 无法获取会返回 unknown - [YukiHookBridge.hasXposedBridge] 不存在会返回 invalid
|
|
||||||
*/
|
*/
|
||||||
val executorName get() = YukiHookBridge.executorName
|
object Status {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前 Hook 框架的名称
|
||||||
|
*
|
||||||
|
* 从 [XposedBridge] 获取 TAG
|
||||||
|
*
|
||||||
|
* - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus]
|
||||||
|
* @return [String] 无法获取会返回 unknown - [YukiHookBridge.hasXposedBridge] 不存在会返回 invalid
|
||||||
|
*/
|
||||||
|
val executorName get() = if (YukiHookBridge.hasXposedBridge) YukiHookBridge.executorName else YukiHookModuleStatus.executorName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前 Hook 框架的版本
|
||||||
|
*
|
||||||
|
* 获取 [XposedBridge.getXposedVersion]
|
||||||
|
*
|
||||||
|
* - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus]
|
||||||
|
* @return [Int] 无法获取会返回 -1
|
||||||
|
*/
|
||||||
|
val executorVersion get() = if (YukiHookBridge.hasXposedBridge) YukiHookBridge.executorVersion else YukiHookModuleStatus.executorVersion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断模块是否在 Xposed 或太极、无极中激活
|
||||||
|
*
|
||||||
|
* - ❗在模块环境中你需要将 [Application] 继承于 [ModuleApplication]
|
||||||
|
*
|
||||||
|
* - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus]
|
||||||
|
*
|
||||||
|
* - ❗在 Xposed 环境中仅返回非 [isTaiChiModuleActive] 的激活状态
|
||||||
|
* @return [Boolean] 是否激活
|
||||||
|
*/
|
||||||
|
val isModuleActive get() = YukiHookBridge.hasXposedBridge || YukiHookModuleStatus.isActive() || isTaiChiModuleActive
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅判断模块是否在 Xposed 中激活
|
||||||
|
*
|
||||||
|
* - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus]
|
||||||
|
*
|
||||||
|
* - ❗在 Xposed 环境中始终返回 true
|
||||||
|
* @return [Boolean] 是否激活
|
||||||
|
*/
|
||||||
|
val isXposedModuleActive get() = YukiHookBridge.hasXposedBridge || YukiHookModuleStatus.isActive()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅判断模块是否在太极、无极中激活
|
||||||
|
*
|
||||||
|
* - ❗在模块环境中你需要将 [Application] 继承于 [ModuleApplication]
|
||||||
|
*
|
||||||
|
* - ❗在 Xposed 环境中始终返回 false
|
||||||
|
* @return [Boolean] 是否激活
|
||||||
|
*/
|
||||||
|
val isTaiChiModuleActive
|
||||||
|
get() = YukiHookBridge.hasXposedBridge.not() && (ModuleApplication.currentContext?.isTaiChiModuleActive ?: false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前 Hook Framework 是否支持资源钩子(Resources Hook)
|
||||||
|
*
|
||||||
|
* - ❗在模块环境中需要启用 [Configs.isEnableHookModuleStatus]
|
||||||
|
*
|
||||||
|
* - ❗在 Xposed 环境中可能会延迟等待事件回调后才会返回 true
|
||||||
|
* @return [Boolean] 是否支持
|
||||||
|
*/
|
||||||
|
val isSupportResourcesHook
|
||||||
|
get() = if (YukiHookBridge.hasXposedBridge) YukiHookBridge.isSupportResourcesHook else YukiHookModuleStatus.hasResourcesHook()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前 Hook 框架的版本
|
* 配置 [YukiHookAPI]
|
||||||
*
|
|
||||||
* 获取 [XposedBridge.getXposedVersion]
|
|
||||||
* @return [Int] 无法获取会返回 -1
|
|
||||||
*/
|
|
||||||
val executorVersion get() = YukiHookBridge.executorVersion
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置 YukiHookAPI
|
|
||||||
*/
|
*/
|
||||||
object Configs {
|
object Configs {
|
||||||
|
|
||||||
@@ -159,7 +214,7 @@ object YukiHookAPI {
|
|||||||
*
|
*
|
||||||
* - 为原生支持 Xposed 模块激活状态检测 - 此功能默认启用
|
* - 为原生支持 Xposed 模块激活状态检测 - 此功能默认启用
|
||||||
*
|
*
|
||||||
* ❗关闭后你将不能再使用 [YukiHookModuleStatus] 中的功能
|
* - ❗关闭后你将不能再在模块环境中使用 [YukiHookAPI.Status] 中的功能
|
||||||
*/
|
*/
|
||||||
var isEnableHookModuleStatus = true
|
var isEnableHookModuleStatus = true
|
||||||
|
|
||||||
@@ -294,7 +349,7 @@ object YukiHookAPI {
|
|||||||
private fun printSplashLog() {
|
private fun printSplashLog() {
|
||||||
if (Configs.isDebug.not() || isShowSplashLogOnceTime.not()) return
|
if (Configs.isDebug.not() || isShowSplashLogOnceTime.not()) return
|
||||||
isShowSplashLogOnceTime = false
|
isShowSplashLogOnceTime = false
|
||||||
yLoggerI(msg = "Welcome to YukiHookAPI $API_VERSION_NAME($API_VERSION_CODE)! Using $executorName API $executorVersion")
|
yLoggerI(msg = "Welcome to YukiHookAPI $API_VERSION_NAME($API_VERSION_CODE)! Using ${Status.executorName} API ${Status.executorVersion}")
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 输出找不到 [XposedBridge] 的错误日志 */
|
/** 输出找不到 [XposedBridge] 的错误日志 */
|
||||||
|
@@ -37,7 +37,6 @@ import android.os.Process
|
|||||||
import com.highcapable.yukihookapi.YukiHookAPI
|
import com.highcapable.yukihookapi.YukiHookAPI
|
||||||
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||||
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
|
|
||||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||||
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
||||||
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
|
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
|
||||||
@@ -98,33 +97,15 @@ val Context.processName
|
|||||||
}
|
}
|
||||||
}.getOrNull() ?: packageName ?: ""
|
}.getOrNull() ?: packageName ?: ""
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断当前 Hook Framework 是否支持资源钩子(Resources Hook)
|
|
||||||
* @return [Boolean] 是否支持
|
|
||||||
*/
|
|
||||||
val Any?.isSupportResourcesHook get() = YukiHookModuleStatus.hasResourcesHook()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断模块是否在 Xposed 或太极、无极中激活
|
|
||||||
* @return [Boolean] 是否激活
|
|
||||||
*/
|
|
||||||
val Context.isModuleActive get() = YukiHookModuleStatus.isActive() || isTaiChiModuleActive
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 仅判断模块是否在 Xposed 中激活
|
|
||||||
* @return [Boolean] 是否激活
|
|
||||||
*/
|
|
||||||
val Any?.isXposedModuleActive get() = YukiHookModuleStatus.isActive()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 仅判断模块是否在太极、无极中激活
|
* 仅判断模块是否在太极、无极中激活
|
||||||
*
|
*
|
||||||
* 此处的实现代码来自太极官方文档封装和改进
|
* 此处的实现代码来自太极官方文档中示例代码的封装与改进
|
||||||
*
|
*
|
||||||
* 详情请参考太极开发指南中的 [如何判断模块是否激活了?](https://taichi.cool/zh/doc/for-xposed-dev.html#%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E6%A8%A1%E5%9D%97%E6%98%AF%E5%90%A6%E6%BF%80%E6%B4%BB%E4%BA%86%EF%BC%9F)
|
* 详情请参考太极开发指南中的 [如何判断模块是否激活了?](https://taichi.cool/zh/doc/for-xposed-dev.html#%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E6%A8%A1%E5%9D%97%E6%98%AF%E5%90%A6%E6%BF%80%E6%B4%BB%E4%BA%86%EF%BC%9F)
|
||||||
* @return [Boolean] 是否激活
|
* @return [Boolean] 是否激活
|
||||||
*/
|
*/
|
||||||
val Context.isTaiChiModuleActive: Boolean
|
internal val Context.isTaiChiModuleActive: Boolean
|
||||||
get() {
|
get() {
|
||||||
var isModuleActive = false
|
var isModuleActive = false
|
||||||
runCatching {
|
runCatching {
|
||||||
|
@@ -53,6 +53,8 @@ import me.weishu.reflection.Reflection
|
|||||||
*
|
*
|
||||||
* - 在模块中使用系统隐藏 API - 核心技术引用了开源项目 [FreeReflection](https://github.com/tiann/FreeReflection)
|
* - 在模块中使用系统隐藏 API - 核心技术引用了开源项目 [FreeReflection](https://github.com/tiann/FreeReflection)
|
||||||
*
|
*
|
||||||
|
* - 在模块中使用 [YukiHookAPI.Status.isTaiChiModuleActive] 判断太极、无极激活状态
|
||||||
|
*
|
||||||
* 详情请参考 [ModuleApplication](https://fankes.github.io/YukiHookAPI/#/api/document?id=moduleapplication-class)
|
* 详情请参考 [ModuleApplication](https://fankes.github.io/YukiHookAPI/#/api/document?id=moduleapplication-class)
|
||||||
*/
|
*/
|
||||||
open class ModuleApplication : Application() {
|
open class ModuleApplication : Application() {
|
||||||
@@ -60,7 +62,7 @@ open class ModuleApplication : Application() {
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
/** 全局静态 [Application] 实例 */
|
/** 全局静态 [Application] 实例 */
|
||||||
private var currentContext: ModuleApplication? = null
|
internal var currentContext: ModuleApplication? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取全局静态 [Application] 实例
|
* 获取全局静态 [Application] 实例
|
||||||
|
@@ -51,10 +51,10 @@ import com.highcapable.yukihookapi.hook.type.android.ConfigurationClass
|
|||||||
import com.highcapable.yukihookapi.hook.type.android.ContextClass
|
import com.highcapable.yukihookapi.hook.type.android.ContextClass
|
||||||
import com.highcapable.yukihookapi.hook.type.android.InstrumentationClass
|
import com.highcapable.yukihookapi.hook.type.android.InstrumentationClass
|
||||||
import com.highcapable.yukihookapi.hook.type.java.IntType
|
import com.highcapable.yukihookapi.hook.type.java.IntType
|
||||||
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus
|
|
||||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
|
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
|
||||||
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources
|
import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiResources
|
||||||
import com.highcapable.yukihookapi.hook.xposed.bridge.inject.YukiHookBridge_Injector
|
import com.highcapable.yukihookapi.hook.xposed.bridge.inject.YukiHookBridge_Injector
|
||||||
|
import com.highcapable.yukihookapi.hook.xposed.bridge.status.YukiHookModuleStatus
|
||||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||||
import dalvik.system.PathClassLoader
|
import dalvik.system.PathClassLoader
|
||||||
import de.robv.android.xposed.*
|
import de.robv.android.xposed.*
|
||||||
@@ -94,6 +94,9 @@ object YukiHookBridge {
|
|||||||
/** 当前 [PackageParam] 方法体回调 */
|
/** 当前 [PackageParam] 方法体回调 */
|
||||||
internal var packageParamCallback: (PackageParam.() -> Unit)? = null
|
internal var packageParamCallback: (PackageParam.() -> Unit)? = null
|
||||||
|
|
||||||
|
/** 当前 Hook Framework 是否支持 Resources Hook */
|
||||||
|
internal var isSupportResourcesHook = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前 Hook APP (宿主) 的全局生命周期 [Application]
|
* 当前 Hook APP (宿主) 的全局生命周期 [Application]
|
||||||
*
|
*
|
||||||
@@ -388,6 +391,7 @@ object YukiHookBridge {
|
|||||||
}?.also {
|
}?.also {
|
||||||
YukiHookAPI.onXposedLoaded(it)
|
YukiHookAPI.onXposedLoaded(it)
|
||||||
if (it.type == HookEntryType.PACKAGE) registerToAppLifecycle(it.packageName)
|
if (it.type == HookEntryType.PACKAGE) registerToAppLifecycle(it.packageName)
|
||||||
|
if (it.type == HookEntryType.RESOURCES) isSupportResourcesHook = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -25,17 +25,11 @@
|
|||||||
*
|
*
|
||||||
* This file is Created by fankes on 2022/2/3.
|
* This file is Created by fankes on 2022/2/3.
|
||||||
*/
|
*/
|
||||||
package com.highcapable.yukihookapi.hook.xposed
|
package com.highcapable.yukihookapi.hook.xposed.bridge.status
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import com.highcapable.yukihookapi.hook.factory.isModuleActive
|
import com.highcapable.yukihookapi.YukiHookAPI
|
||||||
import com.highcapable.yukihookapi.hook.factory.isSupportResourcesHook
|
|
||||||
import com.highcapable.yukihookapi.hook.factory.isTaiChiModuleActive
|
|
||||||
import com.highcapable.yukihookapi.hook.factory.isXposedModuleActive
|
|
||||||
import com.highcapable.yukihookapi.hook.log.yLoggerD
|
import com.highcapable.yukihookapi.hook.log.yLoggerD
|
||||||
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus.executorName
|
|
||||||
import com.highcapable.yukihookapi.hook.xposed.YukiHookModuleStatus.executorVersion
|
|
||||||
import de.robv.android.xposed.XposedBridge
|
import de.robv.android.xposed.XposedBridge
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,19 +37,19 @@ import de.robv.android.xposed.XposedBridge
|
|||||||
*
|
*
|
||||||
* 我们需要监听自己的模块是否被激活 - 可使用以下方法调用
|
* 我们需要监听自己的模块是否被激活 - 可使用以下方法调用
|
||||||
*
|
*
|
||||||
* 在 [Activity] 中调用 [isModuleActive] 或 [isTaiChiModuleActive]
|
* 调用 [YukiHookAPI.Status.isModuleActive] 或 [YukiHookAPI.Status.isTaiChiModuleActive]
|
||||||
*
|
*
|
||||||
* 在任意地方调用 [isXposedModuleActive]
|
* 调用 [YukiHookAPI.Status.isXposedModuleActive]
|
||||||
*
|
*
|
||||||
* 你还可以使用以下方法获取当前 Hook 框架的详细信息
|
* 你还可以使用以下方法获取当前 Hook 框架的详细信息
|
||||||
*
|
*
|
||||||
* 调用 [executorName] 来获取当前 Hook 框架的名称
|
* 调用 [YukiHookAPI.Status.executorName] 来获取当前 Hook 框架的名称
|
||||||
*
|
*
|
||||||
* 调用 [executorVersion] 来获取当前 Hook 框架的版本
|
* 调用 [YukiHookAPI.Status.executorVersion] 来获取当前 Hook 框架的版本
|
||||||
*
|
*
|
||||||
* 详情请参考 [Xposed 模块判断自身激活状态](https://fankes.github.io/YukiHookAPI/#/guide/example?id=xposed-%e6%a8%a1%e5%9d%97%e5%88%a4%e6%96%ad%e8%87%aa%e8%ba%ab%e6%bf%80%e6%b4%bb%e7%8a%b6%e6%80%81)
|
* 详情请参考 [Xposed 模块判断自身激活状态](https://fankes.github.io/YukiHookAPI/#/guide/example?id=xposed-%e6%a8%a1%e5%9d%97%e5%88%a4%e6%96%ad%e8%87%aa%e8%ba%ab%e6%bf%80%e6%b4%bb%e7%8a%b6%e6%80%81)
|
||||||
*/
|
*/
|
||||||
object YukiHookModuleStatus {
|
internal object YukiHookModuleStatus {
|
||||||
|
|
||||||
/** 定义 Jvm 方法名 */
|
/** 定义 Jvm 方法名 */
|
||||||
internal const val IS_ACTIVE_METHOD_NAME = "__--"
|
internal const val IS_ACTIVE_METHOD_NAME = "__--"
|
||||||
@@ -73,23 +67,27 @@ object YukiHookModuleStatus {
|
|||||||
* 获取当前 Hook 框架的名称
|
* 获取当前 Hook 框架的名称
|
||||||
*
|
*
|
||||||
* 从 [XposedBridge] 获取 TAG
|
* 从 [XposedBridge] 获取 TAG
|
||||||
|
*
|
||||||
|
* 请使用 [YukiHookAPI.Status.executorName] 获取
|
||||||
* @return [String] 模块未激活会返回 unknown
|
* @return [String] 模块未激活会返回 unknown
|
||||||
*/
|
*/
|
||||||
val executorName
|
internal val executorName
|
||||||
get() = getXposedBridgeTag().replace(oldValue = "Bridge", newValue = "").replace(oldValue = "-", newValue = "").trim()
|
get() = getXposedBridgeTag().replace(oldValue = "Bridge", newValue = "").replace(oldValue = "-", newValue = "").trim()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前 Hook 框架的版本
|
* 获取当前 Hook 框架的版本
|
||||||
*
|
*
|
||||||
* 获取 [XposedBridge.getXposedVersion]
|
* 获取 [XposedBridge.getXposedVersion]
|
||||||
|
*
|
||||||
|
* 请使用 [YukiHookAPI.Status.executorVersion] 获取
|
||||||
* @return [Int] 模块未激活会返回 -1
|
* @return [Int] 模块未激活会返回 -1
|
||||||
*/
|
*/
|
||||||
val executorVersion get() = getXposedVersion()
|
internal val executorVersion get() = getXposedVersion()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 此方法经过 Hook 后返回 true 即模块已激活
|
* 此方法经过 Hook 后返回 true 即模块已激活
|
||||||
*
|
*
|
||||||
* 请使用 [isModuleActive]、[isXposedModuleActive]、[isTaiChiModuleActive] 判断模块激活状态
|
* 请使用 [YukiHookAPI.Status.isModuleActive]、[YukiHookAPI.Status.isXposedModuleActive]、[YukiHookAPI.Status.isTaiChiModuleActive] 判断模块激活状态
|
||||||
* @return [Boolean]
|
* @return [Boolean]
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
@@ -102,7 +100,7 @@ object YukiHookModuleStatus {
|
|||||||
/**
|
/**
|
||||||
* 此方法经过 Hook 后返回 true 即当前 Hook Framework 支持资源钩子(Resources Hook)
|
* 此方法经过 Hook 后返回 true 即当前 Hook Framework 支持资源钩子(Resources Hook)
|
||||||
*
|
*
|
||||||
* 请使用 [isSupportResourcesHook] 判断支持状态
|
* 请使用 [YukiHookAPI.Status.isSupportResourcesHook] 判断支持状态
|
||||||
* @return [Boolean]
|
* @return [Boolean]
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
Reference in New Issue
Block a user