mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-04 09:45:19 +08:00
Added Activity Proxy function
This commit is contained in:
@@ -24,6 +24,10 @@
|
||||
|
||||
[filename](public/ModuleApplication.md ':include')
|
||||
|
||||
[filename](public/ModuleAppActivity.md ':include')
|
||||
|
||||
[filename](public/ModuleAppCompatActivity.md ':include')
|
||||
|
||||
[filename](public/YukiModuleResources.md ':include')
|
||||
|
||||
[filename](public/YukiResources.md ':include')
|
||||
|
17
docs/api/public/ModuleAppActivity.md
Normal file
17
docs/api/public/ModuleAppActivity.md
Normal file
@@ -0,0 +1,17 @@
|
||||
## ModuleAppActivity [class]
|
||||
|
||||
```kotlin
|
||||
open class ModuleAppActivity : Activity()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.93` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 代理 `Activity`。
|
||||
|
||||
继承于此类的 `Activity` 可以同时在宿主与模块中启动。
|
||||
|
||||
在 (Xposed) 宿主环境需要在宿主启动时调用 `Context.registerModuleAppActivities` 进行注册。
|
33
docs/api/public/ModuleAppCompatActivity.md
Normal file
33
docs/api/public/ModuleAppCompatActivity.md
Normal file
@@ -0,0 +1,33 @@
|
||||
## ModuleAppCompatActivity [class]
|
||||
|
||||
```kotlin
|
||||
open class ModuleAppCompatActivity : AppCompatActivity()
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.93` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 代理 `AppCompatActivity`。
|
||||
|
||||
继承于此类的 `Activity` 可以同时在宿主与模块中启动。
|
||||
|
||||
在 (Xposed) 宿主环境需要在宿主启动时调用 `Context.registerModuleAppActivities` 进行注册。
|
||||
|
||||
在 (Xposed) 宿主环境需要重写 `moduleTheme` 设置 AppCompat 主题,否则会无法启动。
|
||||
|
||||
### moduleTheme [field]
|
||||
|
||||
```kotlin
|
||||
open val moduleTheme: Int
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.93` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 设置当前代理的 `Activity` 主题。
|
@@ -191,6 +191,150 @@ onAppLifecycle {
|
||||
}
|
||||
```
|
||||
|
||||
### registerModuleAppActivities [method]
|
||||
|
||||
```kotlin
|
||||
fun Context.registerModuleAppActivities(proxy: Any?)
|
||||
```
|
||||
|
||||
**变更记录**
|
||||
|
||||
`v1.0.93` `新增`
|
||||
|
||||
**功能描述**
|
||||
|
||||
> 向 Hook APP (宿主) 注册当前 Xposed 模块的 `Activity`。
|
||||
|
||||
注册成功后,你就可以直接使用 `Context.startActivity` 来启动未在宿主中注册的 `Activity`。
|
||||
|
||||
你要将需要在宿主启动的 `Activity` 继承于 `ModuleAppActivity` 或 `ModuleAppCompatActivity`。
|
||||
|
||||
为防止资源 ID 互相冲突,你需要在当前 Xposed 模块项目的 `build.gradle` 中修改资源 ID。
|
||||
|
||||
- Kotlin Gradle DSL
|
||||
|
||||
```kotlin
|
||||
androidResources.additionalParameters("--allow-reserved-package-id", "--package-id", "0x64")
|
||||
```
|
||||
|
||||
- Groovy
|
||||
|
||||
```groovy
|
||||
aaptOptions.additionalParameters '--allow-reserved-package-id', '--package-id', '0x64'
|
||||
```
|
||||
|
||||
!> 提供的示例资源 ID 值仅供参考,为了防止当前宿主存在多个 Xposed 模块,建议自定义你自己的资源 ID。
|
||||
|
||||
!> 只能在 (Xposed) 宿主环境使用此功能,其它环境下使用将不生效且会打印警告信息。
|
||||
|
||||
**功能示例**
|
||||
|
||||
在 Hook 宿主之后,我们可以直接在 Hooker 中得到的 `Context` 注册当前模块的 `Activity` 代理。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
injectMember {
|
||||
method {
|
||||
name = "onCreate"
|
||||
param(BundleClass)
|
||||
}
|
||||
afterHook {
|
||||
instance<Activity>().registerModuleAppActivities()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
你还可以直接在 `AppLifecycle` 中注册当前模块的 `Activity` 代理。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
onAppLifecycle {
|
||||
onCreate {
|
||||
registerModuleAppActivities()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果没有填写 `proxy` 参数,API 将会根据当前 `Context` 自动获取当前宿主的启动入口 `Activity` 进行代理。
|
||||
|
||||
通常情况下,它是有效的,但是以上情况在一些 APP 中会失效,例如一些 `Activity` 会在注册清单上加入启动参数,那么我们就需要使用另一种解决方案。
|
||||
|
||||
若未注册的 `Activity` 不能被正确启动,我们可以手动拿到宿主的 `AndroidManifest.xml` 进行分析,来得到一个注册过的 `Activity` 标签,获取其中的 `name`。
|
||||
|
||||
你需要选择一个当前宿主可能用不到的、不需要的 `Activity` 作为一个“傀儡”将其进行代理,通常是有效的。
|
||||
|
||||
比如我们已经找到了能够被代理的合适 `Activity`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```xml
|
||||
<activity
|
||||
android:name="com.demo.test.activity.TestActivity"
|
||||
...>
|
||||
```
|
||||
|
||||
根据其中的 `name`,我们只需要在方法中加入这个参数进行注册即可。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
registerModuleAppActivities(proxy = "com.demo.test.activity.TestActivity")
|
||||
```
|
||||
|
||||
另一种情况,如果你对宿主的类编写了一个 `stub`,那么你可以直接通过 `Class` 对象来进行注册。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
registerModuleAppActivities(TestActivity::class.java)
|
||||
```
|
||||
|
||||
注册完成后,请将你需要使用宿主启动的模块中的 `Activity` 继承于 `ModuleAppActivity` 或 `ModuleAppCompatActivity`。
|
||||
|
||||
这些 `Activity` 现在无需注册即可无缝存活于宿主中。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HostTestActivity : ModuleAppActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
// 模块资源已被自动注入,可以直接使用 xml 装载布局
|
||||
setContentView(R.layout.activity_main)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
若你需要继承于 `ModuleAppCompatActivity`,你需要手动设置 AppCompat 主题。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HostTestActivity : ModuleAppCompatActivity() {
|
||||
|
||||
// 这里的主题名称仅供参考,请填写你模块中已有的主题名称
|
||||
override val moduleTheme get() = R.style.Theme_AppCompat
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
// 模块资源已被自动注入,可以直接使用 xml 装载布局
|
||||
setContentView(R.layout.activity_main)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
以上步骤全部完成后,你就可以在 (Xposed) 宿主环境任意存在 `Context` 的地方愉快地调用 `startActivity` 了。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
val context: Context = ... // 假设这就是你的 Context
|
||||
context.startActivity(context, HostTestActivity::class.java)
|
||||
```
|
||||
|
||||
### ~~isSupportResourcesHook [field]~~ <!-- {docsify-ignore} -->
|
||||
|
||||
**变更记录**
|
||||
|
@@ -29,6 +29,7 @@
|
||||
|
||||
package com.highcapable.yukihookapi.hook.factory
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
@@ -41,6 +42,8 @@ import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base.ModuleAppActivity
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base.ModuleAppCompatActivity
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
|
||||
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
|
||||
import java.io.BufferedReader
|
||||
@@ -126,6 +129,20 @@ fun Context.injectModuleAppResources() = resources?.injectModuleAppResources()
|
||||
*/
|
||||
fun Resources.injectModuleAppResources() = AppParasitics.injectModuleAppResources(hostResources = this)
|
||||
|
||||
/**
|
||||
* 向 Hook APP (宿主) 注册当前 Xposed 模块的 [Activity]
|
||||
*
|
||||
* 注册成功后 - 你就可以直接使用 [Context.startActivity] 来启动未在宿主中注册的 [Activity]
|
||||
*
|
||||
* - 你要将需要在宿主启动的 [Activity] 继承于 [ModuleAppActivity] 或 [ModuleAppCompatActivity]
|
||||
*
|
||||
* 详情请参考 [registerModuleAppActivities](https://fankes.github.io/YukiHookAPI/#/api/document?id=registermoduleappactivities-method)
|
||||
*
|
||||
* - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息
|
||||
* @param proxy 代理的 [Activity] - 必须存在于宿主的 AndroidMainifest 清单中 - 不填使用默认 [Activity]
|
||||
*/
|
||||
fun Context.registerModuleAppActivities(proxy: Any? = null) = AppParasitics.registerModuleAppActivities(context = this, proxy)
|
||||
|
||||
/**
|
||||
* 仅判断模块是否在太极、无极中激活
|
||||
*
|
||||
|
@@ -24,11 +24,16 @@
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/8/14.
|
||||
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||
*/
|
||||
@file:Suppress("QueryPermissionsNeeded")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.ActivityManager
|
||||
import android.app.Application
|
||||
import android.app.Instrumentation
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -36,10 +41,9 @@ import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Handler
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.factory.classOf
|
||||
import com.highcapable.yukihookapi.hook.factory.current
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.factory.*
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerE
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
||||
import com.highcapable.yukihookapi.hook.param.wrapper.HookParamWrapper
|
||||
@@ -53,6 +57,10 @@ import com.highcapable.yukihookapi.hook.xposed.bridge.dummy.YukiModuleResources
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiHookHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.factory.YukiMemberHook
|
||||
import com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config.ActivityProxyConfig
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.HandlerDelegate
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.IActivityManagerProxy
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.InstrumentationDelegate
|
||||
|
||||
/**
|
||||
* 这是一个管理 APP 寄生功能的控制类
|
||||
@@ -69,6 +77,9 @@ internal object AppParasitics {
|
||||
/** [YukiHookDataChannel] 是否已经注册 */
|
||||
private var isDataChannelRegister = false
|
||||
|
||||
/** [Activity] 代理是否已经注册 */
|
||||
private var isActivityProxyRegister = false
|
||||
|
||||
/** 已被注入到宿主 [Resources] 中的当前 Xposed 模块资源 HashCode 数组 */
|
||||
private val injectedHostResourcesHashCodes = HashSet<Int>()
|
||||
|
||||
@@ -237,6 +248,61 @@ internal object AppParasitics {
|
||||
dynamicModuleAppResources?.let { moduleAppResources = it }
|
||||
}
|
||||
|
||||
/**
|
||||
* 向 Hook APP (宿主) 注册当前 Xposed 模块的 [Activity]
|
||||
* @param context 当前 [Context]
|
||||
* @param proxy 代理的 [Activity]
|
||||
*/
|
||||
internal fun registerModuleAppActivities(context: Context, proxy: Any?) {
|
||||
if (isActivityProxyRegister) return
|
||||
if (YukiHookBridge.hasXposedBridge.not()) return yLoggerW(msg = "You can only register Activity Proxy in Xposed Environment")
|
||||
runCatching {
|
||||
ActivityProxyConfig.apply {
|
||||
proxyIntentName = "${YukiHookBridge.modulePackageName}.ACTIVITY_PROXY_INTENT"
|
||||
proxyClassName = proxy?.let {
|
||||
when (it) {
|
||||
is String, is CharSequence -> it.toString()
|
||||
is Class<*> -> it.name
|
||||
else -> error("This proxy [$it] type is not allowed")
|
||||
}
|
||||
}?.takeIf { it.isNotBlank() } ?: context.packageManager?.runCatching {
|
||||
queryIntentActivities(getLaunchIntentForPackage(context.packageName)!!, 0).first().activityInfo.name
|
||||
}?.getOrNull() ?: ""
|
||||
if ((proxyClassName.hasClass(context.classLoader) && classOf(proxyClassName, context.classLoader).hasMethod {
|
||||
name = "setIntent"; param(IntentClass); superClass()
|
||||
}).not()
|
||||
) (if (proxyClassName.isBlank()) error("Cound not got launch intent for package \"${context.packageName}\"")
|
||||
else error("Could not found \"$proxyClassName\" or Class is not a type of Activity"))
|
||||
}
|
||||
/** Patched [Instrumentation] */
|
||||
ActivityThreadClass.field { name = "sCurrentActivityThread" }.ignored().get().any()?.current {
|
||||
method { name = "getInstrumentation" }
|
||||
.invoke<Instrumentation>()
|
||||
?.also { field { name = "mInstrumentation" }.set(InstrumentationDelegate.wrapper(it)) }
|
||||
HandlerClass.field { name = "mCallback" }.get(field { name = "mH" }.any()).apply {
|
||||
cast<Handler.Callback?>()?.apply {
|
||||
if (current().name != classOf<HandlerDelegate>().name) set(HandlerDelegate.wrapper(baseInstance = this))
|
||||
} ?: set(HandlerDelegate.wrapper())
|
||||
}
|
||||
}
|
||||
/** Patched [ActivityManager] */
|
||||
runCatching {
|
||||
runCatching {
|
||||
ActivityManagerNativeClass.field { name = "gDefault" }.ignored().get().any()
|
||||
}.getOrNull() ?: ActivityManagerClass.field { name = "IActivityManagerSingleton" }.ignored().get().any()
|
||||
}.getOrNull()?.also { default ->
|
||||
SingletonClass.field { name = "mInstance" }.ignored().result {
|
||||
get(default).apply { any()?.also { set(IActivityManagerProxy.wrapper(IActivityManagerClass, it)) } }
|
||||
ActivityTaskManagerClass.field { name = "IActivityTaskManagerSingleton" }.ignored().get().any().also { singleton ->
|
||||
SingletonClass.method { name = "get" }.ignored().get(singleton).call()
|
||||
get(singleton).apply { any()?.also { set(IActivityManagerProxy.wrapper(IActivityTaskManagerClass, it)) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
isActivityProxyRegister = true
|
||||
}.onFailure { yLoggerE(msg = "Activity Proxy initialization failed because got an Exception", e = it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前 Hook APP (宿主) 的生命周期回调处理类
|
||||
*/
|
||||
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/8/8.
|
||||
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.CallSuper
|
||||
import com.highcapable.yukihookapi.hook.factory.classOf
|
||||
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.ModuleClassLoader
|
||||
|
||||
/**
|
||||
* 代理 [Activity]
|
||||
*
|
||||
* 继承于此类的 [Activity] 可以同时在宿主与模块中启动
|
||||
*
|
||||
* - 在 (Xposed) 宿主环境需要在宿主启动时调用 [Context.registerModuleAppActivities] 进行注册
|
||||
*/
|
||||
open class ModuleAppActivity : Activity() {
|
||||
|
||||
override fun getClassLoader(): ClassLoader? = ModuleClassLoader.instance()
|
||||
|
||||
@CallSuper
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
savedInstanceState.getBundle("android:viewHierarchyState")?.classLoader = classOf<ModuleAppActivity>().classLoader
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/8/8.
|
||||
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.highcapable.yukihookapi.hook.factory.classOf
|
||||
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.ModuleClassLoader
|
||||
|
||||
/**
|
||||
* 代理 [AppCompatActivity]
|
||||
*
|
||||
* 继承于此类的 [Activity] 可以同时在宿主与模块中启动
|
||||
*
|
||||
* - 在 (Xposed) 宿主环境需要在宿主启动时调用 [Context.registerModuleAppActivities] 进行注册
|
||||
*
|
||||
* - 在 (Xposed) 宿主环境需要重写 [moduleTheme] 设置 AppCompat 主题 - 否则会无法启动
|
||||
*/
|
||||
open class ModuleAppCompatActivity : AppCompatActivity() {
|
||||
|
||||
/**
|
||||
* 设置当前代理的 [Activity] 主题
|
||||
* @return [Int]
|
||||
*/
|
||||
open val moduleTheme get() = -1
|
||||
|
||||
override fun getClassLoader(): ClassLoader? = ModuleClassLoader.instance()
|
||||
|
||||
@CallSuper
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
savedInstanceState.getBundle("android:viewHierarchyState")?.classLoader = classOf<ModuleAppActivity>().classLoader
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (YukiHookBridge.hasXposedBridge && moduleTheme != -1) setTheme(moduleTheme)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/8/14.
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
|
||||
/**
|
||||
* 当前代理的 [Activity] 参数配置类
|
||||
*/
|
||||
internal object ActivityProxyConfig {
|
||||
|
||||
/**
|
||||
* 用于代理的 [Intent] 名称
|
||||
*/
|
||||
internal var proxyIntentName = ""
|
||||
|
||||
/**
|
||||
* 需要代理的 [Activity] 类名
|
||||
*/
|
||||
internal var proxyClassName = ""
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/8/8.
|
||||
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Message
|
||||
import com.highcapable.yukihookapi.hook.factory.current
|
||||
import com.highcapable.yukihookapi.hook.factory.field
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.log.yLoggerE
|
||||
import com.highcapable.yukihookapi.hook.type.android.ActivityThreadClass
|
||||
import com.highcapable.yukihookapi.hook.type.android.ClientTransactionClass
|
||||
import com.highcapable.yukihookapi.hook.type.android.IBinderClass
|
||||
import com.highcapable.yukihookapi.hook.type.android.IntentClass
|
||||
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config.ActivityProxyConfig
|
||||
|
||||
/**
|
||||
* 代理当前 [Handler.Callback]
|
||||
* @param baseInstance 原始实例
|
||||
*/
|
||||
internal class HandlerDelegate private constructor(private val baseInstance: Handler.Callback?) : Handler.Callback {
|
||||
|
||||
internal companion object {
|
||||
|
||||
/** 启动 [Activity] */
|
||||
private const val LAUNCH_ACTIVITY = 100
|
||||
|
||||
/** 执行事务处理 */
|
||||
private const val EXECUTE_TRANSACTION = 159
|
||||
|
||||
/**
|
||||
* 从 [Handler.Callback] 创建 [HandlerDelegate] 实例
|
||||
* @param baseInstance [Handler.Callback] 实例 - 可空
|
||||
* @return [HandlerDelegate]
|
||||
*/
|
||||
internal fun wrapper(baseInstance: Handler.Callback? = null) = HandlerDelegate(baseInstance)
|
||||
}
|
||||
|
||||
override fun handleMessage(msg: Message): Boolean {
|
||||
when (msg.what) {
|
||||
LAUNCH_ACTIVITY -> runCatching {
|
||||
msg.obj.current().field { name = "intent" }.apply {
|
||||
cast<Intent?>()?.also { intent ->
|
||||
IntentClass.field { name = "mExtras" }.ignored().get(intent).cast<Bundle?>()
|
||||
?.classLoader = YukiHookAppHelper.currentApplication()?.classLoader
|
||||
if (intent.hasExtra(ActivityProxyConfig.proxyIntentName))
|
||||
set(intent.getParcelableExtra(ActivityProxyConfig.proxyIntentName))
|
||||
}
|
||||
}
|
||||
}.onFailure { yLoggerE(msg = "Activity Proxy got an Exception in msg.what [$LAUNCH_ACTIVITY]", e = it) }
|
||||
EXECUTE_TRANSACTION -> msg.obj?.runCatching client@{
|
||||
ClientTransactionClass.method { name = "getCallbacks" }.ignored().get(this).list<Any?>().takeIf { it.isNotEmpty() }
|
||||
?.forEach { item ->
|
||||
item?.current()?.takeIf { it.name.contains(other = "LaunchActivityItem") }?.field { name = "mIntent" }?.apply {
|
||||
cast<Intent?>()?.also { intent ->
|
||||
IntentClass.field { name = "mExtras" }.ignored().get(intent).cast<Bundle?>()
|
||||
?.classLoader = YukiHookAppHelper.currentApplication()?.classLoader
|
||||
if (intent.hasExtra(ActivityProxyConfig.proxyIntentName))
|
||||
intent.getParcelableExtra<Intent>(ActivityProxyConfig.proxyIntentName).also { subIntent ->
|
||||
if (Build.VERSION.SDK_INT >= 31)
|
||||
ActivityThreadClass.method { name = "currentActivityThread" }.ignored().get().call()
|
||||
?.current()?.method {
|
||||
name = "getLaunchingActivity"
|
||||
param(IBinderClass)
|
||||
}?.call(this@client.current().method { name = "getActivityToken" }.call())
|
||||
?.current()?.field { name = "intent" }?.set(subIntent)
|
||||
set(subIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}?.onFailure { yLoggerE(msg = "Activity Proxy got an Exception in msg.what [$EXECUTE_TRANSACTION]", e = it) }
|
||||
}
|
||||
return baseInstance?.handleMessage(msg) ?: false
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/8/8.
|
||||
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Intent
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.config.ActivityProxyConfig
|
||||
import java.lang.reflect.InvocationHandler
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
/**
|
||||
* 代理当前 [ActivityManager]
|
||||
* @param baseInstance 原始实例
|
||||
*/
|
||||
internal class IActivityManagerProxy private constructor(private val baseInstance: Any) : InvocationHandler {
|
||||
|
||||
internal companion object {
|
||||
|
||||
/**
|
||||
* 创建 [IActivityManagerProxy] 代理
|
||||
* @param clazz 代理的目标 [Class]
|
||||
* @param instance 代理的目标实例
|
||||
* @return [Any] 代理包装后的实例
|
||||
*/
|
||||
internal fun wrapper(clazz: Class<*>, instance: Any) =
|
||||
Proxy.newProxyInstance(AppParasitics.baseClassLoader, arrayOf(clazz), IActivityManagerProxy(instance))
|
||||
}
|
||||
|
||||
override fun invoke(proxy: Any?, method: Method?, args: Array<Any>?): Any? {
|
||||
if (method?.name == "startActivity") args?.indexOfFirst { it is Intent }?.also { index ->
|
||||
val argsInstance = (args[index] as? Intent) ?: return@also
|
||||
val component = argsInstance.component
|
||||
if (component != null &&
|
||||
component.packageName == YukiHookAppHelper.currentPackageName() &&
|
||||
component.className.startsWith(YukiHookBridge.modulePackageName)
|
||||
) args[index] = Intent().apply {
|
||||
setClassName(component.packageName, ActivityProxyConfig.proxyClassName)
|
||||
putExtra(ActivityProxyConfig.proxyIntentName, argsInstance)
|
||||
}
|
||||
}
|
||||
return method?.invoke(baseInstance, *(args ?: emptyArray()))
|
||||
}
|
||||
}
|
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/8/8.
|
||||
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.app.Instrumentation
|
||||
import android.app.UiAutomation
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.*
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import com.highcapable.yukihookapi.hook.factory.*
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||
|
||||
/**
|
||||
* 代理当前 [Instrumentation]
|
||||
* @param baseInstance 原始实例
|
||||
*/
|
||||
internal class InstrumentationDelegate private constructor(private val baseInstance: Instrumentation) : Instrumentation() {
|
||||
|
||||
internal companion object {
|
||||
|
||||
/**
|
||||
* 从 [Instrumentation] 创建 [InstrumentationDelegate] 实例
|
||||
* @param baseInstance [Instrumentation] 实例
|
||||
* @return [InstrumentationDelegate]
|
||||
*/
|
||||
internal fun wrapper(baseInstance: Instrumentation) = InstrumentationDelegate(baseInstance)
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入当前 [Activity] 生命周期
|
||||
* @param icicle [Bundle]
|
||||
*/
|
||||
private fun Activity.injectLifecycle(icicle: Bundle?) {
|
||||
if (icicle != null && current().name.startsWith(YukiHookBridge.modulePackageName))
|
||||
icicle.classLoader = AppParasitics.baseClassLoader
|
||||
if (current().name.startsWith(YukiHookBridge.modulePackageName)) injectModuleAppResources()
|
||||
}
|
||||
|
||||
override fun newActivity(cl: ClassLoader?, className: String?, intent: Intent?): Activity? = try {
|
||||
baseInstance.newActivity(cl, className, intent)
|
||||
} catch (e: Throwable) {
|
||||
if (className?.startsWith(YukiHookBridge.modulePackageName) == true)
|
||||
classOf(className).buildOf<Activity>() ?: throw e
|
||||
else throw e
|
||||
}
|
||||
|
||||
override fun onCreate(arguments: Bundle?) {
|
||||
baseInstance.onCreate(arguments)
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
baseInstance.start()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
baseInstance.onStart()
|
||||
}
|
||||
|
||||
override fun onException(obj: Any?, e: Throwable?) = baseInstance.onException(obj, e)
|
||||
|
||||
override fun sendStatus(resultCode: Int, results: Bundle?) {
|
||||
baseInstance.sendStatus(resultCode, results)
|
||||
}
|
||||
|
||||
override fun addResults(results: Bundle?) {
|
||||
if (Build.VERSION.SDK_INT >= 26) baseInstance.addResults(results)
|
||||
}
|
||||
|
||||
override fun finish(resultCode: Int, results: Bundle?) {
|
||||
baseInstance.finish(resultCode, results)
|
||||
}
|
||||
|
||||
override fun setAutomaticPerformanceSnapshots() {
|
||||
baseInstance.setAutomaticPerformanceSnapshots()
|
||||
}
|
||||
|
||||
override fun startPerformanceSnapshot() {
|
||||
baseInstance.startPerformanceSnapshot()
|
||||
}
|
||||
|
||||
override fun endPerformanceSnapshot() {
|
||||
baseInstance.endPerformanceSnapshot()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
baseInstance.onDestroy()
|
||||
}
|
||||
|
||||
override fun getContext(): Context? = baseInstance.context
|
||||
|
||||
override fun getComponentName(): ComponentName? = baseInstance.componentName
|
||||
|
||||
override fun getTargetContext(): Context? = baseInstance.targetContext
|
||||
|
||||
override fun getProcessName(): String? =
|
||||
if (Build.VERSION.SDK_INT >= 26) baseInstance.processName else AppParasitics.systemContext.processName
|
||||
|
||||
override fun isProfiling() = baseInstance.isProfiling
|
||||
|
||||
override fun startProfiling() {
|
||||
baseInstance.startProfiling()
|
||||
}
|
||||
|
||||
override fun stopProfiling() {
|
||||
baseInstance.stopProfiling()
|
||||
}
|
||||
|
||||
override fun setInTouchMode(inTouch: Boolean) {
|
||||
baseInstance.setInTouchMode(inTouch)
|
||||
}
|
||||
|
||||
override fun waitForIdle(recipient: Runnable?) {
|
||||
baseInstance.waitForIdle(recipient)
|
||||
}
|
||||
|
||||
override fun waitForIdleSync() {
|
||||
baseInstance.waitForIdleSync()
|
||||
}
|
||||
|
||||
override fun runOnMainSync(runner: Runnable?) {
|
||||
baseInstance.runOnMainSync(runner)
|
||||
}
|
||||
|
||||
override fun startActivitySync(intent: Intent?): Activity? = baseInstance.startActivitySync(intent)
|
||||
|
||||
override fun startActivitySync(intent: Intent, options: Bundle?): Activity =
|
||||
if (Build.VERSION.SDK_INT >= 28) baseInstance.startActivitySync(intent, options) else error("Operating system not supported")
|
||||
|
||||
override fun addMonitor(monitor: ActivityMonitor?) {
|
||||
baseInstance.addMonitor(monitor)
|
||||
}
|
||||
|
||||
override fun addMonitor(cls: String?, result: ActivityResult?, block: Boolean): ActivityMonitor? =
|
||||
baseInstance.addMonitor(cls, result, block)
|
||||
|
||||
override fun addMonitor(filter: IntentFilter?, result: ActivityResult?, block: Boolean): ActivityMonitor? =
|
||||
baseInstance.addMonitor(filter, result, block)
|
||||
|
||||
override fun checkMonitorHit(monitor: ActivityMonitor?, minHits: Int) = baseInstance.checkMonitorHit(monitor, minHits)
|
||||
|
||||
override fun waitForMonitor(monitor: ActivityMonitor?): Activity? = baseInstance.waitForMonitor(monitor)
|
||||
|
||||
override fun waitForMonitorWithTimeout(monitor: ActivityMonitor?, timeOut: Long): Activity? =
|
||||
baseInstance.waitForMonitorWithTimeout(monitor, timeOut)
|
||||
|
||||
override fun removeMonitor(monitor: ActivityMonitor?) {
|
||||
baseInstance.removeMonitor(monitor)
|
||||
}
|
||||
|
||||
override fun invokeContextMenuAction(targetActivity: Activity?, id: Int, flag: Int) =
|
||||
baseInstance.invokeContextMenuAction(targetActivity, id, flag)
|
||||
|
||||
override fun invokeMenuActionSync(targetActivity: Activity?, id: Int, flag: Int) =
|
||||
baseInstance.invokeMenuActionSync(targetActivity, id, flag)
|
||||
|
||||
override fun sendCharacterSync(keyCode: Int) {
|
||||
baseInstance.sendCharacterSync(keyCode)
|
||||
}
|
||||
|
||||
override fun sendKeyDownUpSync(key: Int) {
|
||||
baseInstance.sendKeyDownUpSync(key)
|
||||
}
|
||||
|
||||
override fun sendKeySync(event: KeyEvent?) {
|
||||
baseInstance.sendKeySync(event)
|
||||
}
|
||||
|
||||
override fun sendPointerSync(event: MotionEvent?) {
|
||||
baseInstance.sendPointerSync(event)
|
||||
}
|
||||
|
||||
override fun sendStringSync(text: String?) {
|
||||
baseInstance.sendStringSync(text)
|
||||
}
|
||||
|
||||
override fun sendTrackballEventSync(event: MotionEvent?) {
|
||||
baseInstance.sendTrackballEventSync(event)
|
||||
}
|
||||
|
||||
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application? =
|
||||
baseInstance.newApplication(cl, className, context)
|
||||
|
||||
override fun callApplicationOnCreate(app: Application?) {
|
||||
baseInstance.callApplicationOnCreate(app)
|
||||
}
|
||||
|
||||
override fun newActivity(
|
||||
clazz: Class<*>?, context: Context?,
|
||||
token: IBinder?, application: Application?,
|
||||
intent: Intent?, info: ActivityInfo?,
|
||||
title: CharSequence?, parent: Activity?,
|
||||
id: String?, lastNonConfigurationInstance: Any?
|
||||
): Activity? = baseInstance.newActivity(
|
||||
clazz, context,
|
||||
token, application,
|
||||
intent, info, title,
|
||||
parent, id, lastNonConfigurationInstance
|
||||
)
|
||||
|
||||
override fun callActivityOnCreate(activity: Activity, icicle: Bundle?, persistentState: PersistableBundle?) {
|
||||
activity.injectLifecycle(icicle)
|
||||
baseInstance.callActivityOnCreate(activity, icicle, persistentState)
|
||||
}
|
||||
|
||||
override fun callActivityOnCreate(activity: Activity, icicle: Bundle?) {
|
||||
activity.injectLifecycle(icicle)
|
||||
baseInstance.callActivityOnCreate(activity, icicle)
|
||||
}
|
||||
|
||||
override fun callActivityOnDestroy(activity: Activity?) {
|
||||
baseInstance.callActivityOnDestroy(activity)
|
||||
}
|
||||
|
||||
override fun callActivityOnRestoreInstanceState(activity: Activity, savedInstanceState: Bundle) {
|
||||
baseInstance.callActivityOnRestoreInstanceState(activity, savedInstanceState)
|
||||
}
|
||||
|
||||
override fun callActivityOnRestoreInstanceState(activity: Activity, savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
|
||||
baseInstance.callActivityOnRestoreInstanceState(activity, savedInstanceState, persistentState)
|
||||
}
|
||||
|
||||
override fun callActivityOnPostCreate(activity: Activity, savedInstanceState: Bundle?) {
|
||||
baseInstance.callActivityOnPostCreate(activity, savedInstanceState)
|
||||
}
|
||||
|
||||
override fun callActivityOnPostCreate(activity: Activity, savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
|
||||
baseInstance.callActivityOnPostCreate(activity, savedInstanceState, persistentState)
|
||||
}
|
||||
|
||||
override fun callActivityOnNewIntent(activity: Activity?, intent: Intent?) {
|
||||
baseInstance.callActivityOnNewIntent(activity, intent)
|
||||
}
|
||||
|
||||
override fun callActivityOnStart(activity: Activity?) {
|
||||
baseInstance.callActivityOnStart(activity)
|
||||
}
|
||||
|
||||
override fun callActivityOnRestart(activity: Activity?) {
|
||||
baseInstance.callActivityOnRestart(activity)
|
||||
}
|
||||
|
||||
override fun callActivityOnPause(activity: Activity?) {
|
||||
baseInstance.callActivityOnPause(activity)
|
||||
}
|
||||
|
||||
override fun callActivityOnResume(activity: Activity?) {
|
||||
baseInstance.callActivityOnResume(activity)
|
||||
}
|
||||
|
||||
override fun callActivityOnStop(activity: Activity?) {
|
||||
baseInstance.callActivityOnStop(activity)
|
||||
}
|
||||
|
||||
override fun callActivityOnUserLeaving(activity: Activity?) {
|
||||
baseInstance.callActivityOnUserLeaving(activity)
|
||||
}
|
||||
|
||||
override fun callActivityOnSaveInstanceState(activity: Activity, outState: Bundle) {
|
||||
baseInstance.callActivityOnSaveInstanceState(activity, outState)
|
||||
}
|
||||
|
||||
override fun callActivityOnSaveInstanceState(activity: Activity, outState: Bundle, outPersistentState: PersistableBundle) {
|
||||
baseInstance.callActivityOnSaveInstanceState(activity, outState, outPersistentState)
|
||||
}
|
||||
|
||||
override fun callActivityOnPictureInPictureRequested(activity: Activity) {
|
||||
if (Build.VERSION.SDK_INT >= 30) baseInstance.callActivityOnPictureInPictureRequested(activity)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
@Suppress("DEPRECATION")
|
||||
override fun startAllocCounting() {
|
||||
baseInstance.startAllocCounting()
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
@Suppress("DEPRECATION")
|
||||
override fun stopAllocCounting() {
|
||||
baseInstance.stopAllocCounting()
|
||||
}
|
||||
|
||||
override fun getAllocCounts(): Bundle? = baseInstance.allocCounts
|
||||
|
||||
override fun getBinderCounts(): Bundle? = baseInstance.binderCounts
|
||||
|
||||
override fun getUiAutomation(): UiAutomation? = baseInstance.uiAutomation
|
||||
|
||||
override fun getUiAutomation(flags: Int): UiAutomation? =
|
||||
if (Build.VERSION.SDK_INT >= 24) baseInstance.getUiAutomation(flags) else error("Operating system not supported")
|
||||
|
||||
override fun acquireLooperManager(looper: Looper?): TestLooperManager? =
|
||||
if (Build.VERSION.SDK_INT >= 26) baseInstance.acquireLooperManager(looper) else error("Operating system not supported")
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/8/8.
|
||||
* Thanks for providing https://github.com/cinit/QAuxiliary/blob/main/app/src/main/java/io/github/qauxv/lifecycle/Parasitics.java
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate
|
||||
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.helper.YukiHookAppHelper
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics
|
||||
|
||||
/**
|
||||
* 自动处理 (Xposed) 宿主环境与模块环境的 [ClassLoader]
|
||||
*/
|
||||
internal class ModuleClassLoader private constructor() : ClassLoader(AppParasitics.baseClassLoader) {
|
||||
|
||||
internal companion object {
|
||||
|
||||
/** 当前 [ModuleClassLoader] 单例 */
|
||||
private var instance: ModuleClassLoader? = null
|
||||
|
||||
/**
|
||||
* 获取 [ModuleClassLoader] 单例
|
||||
* @return [ModuleClassLoader]
|
||||
*/
|
||||
internal fun instance() = instance ?: ModuleClassLoader().apply { instance = this }
|
||||
}
|
||||
|
||||
override fun loadClass(name: String, resolve: Boolean): Class<*> {
|
||||
if (YukiHookBridge.hasXposedBridge.not()) return AppParasitics.baseClassLoader.loadClass(name)
|
||||
return YukiHookAppHelper.currentApplication()?.classLoader?.let { loader ->
|
||||
runCatching { return@let AppParasitics.baseClassLoader.loadClass(name) }
|
||||
runCatching { if (name == "androidx.lifecycle.ReportFragment") return@let loader.loadClass(name) }
|
||||
runCatching { AppParasitics.baseClassLoader.loadClass(name) }.getOrNull() ?: loader.loadClass(name)
|
||||
} ?: super.loadClass(name, resolve)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user