mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-01 08:15:37 +08:00
refactor: merge ModuleAppActivity, ModuleAppCompatActivity to ModuleActivity and some tweaks
This commit is contained in:
@@ -146,34 +146,45 @@ Alternatively, if you write a `stub` for the Host App's class, you can register
|
||||
registerModuleAppActivities(TestActivity::class.java)
|
||||
```
|
||||
|
||||
After the registration is complete, extends the `Activity` in the Module App you need to use the Host App to start by `ModuleAppActivity` or `ModuleAppCompatActivity`.
|
||||
After registration is completed, please implement the `ModuleActivity` interface using the `Activity` module in the host-started module.
|
||||
|
||||
These `Activity` now live seamlessly in the Host App without registration.
|
||||
These `Activity` (ies) now live seamlessly in the host without registration.
|
||||
|
||||
We recommend that you create `BaseActivity` as the base class for all modules `Activity`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
class HostTestActivity : ModuleAppActivity() {
|
||||
abstract class BaseActivity : AppCompatActivity(), ModuleActivity {
|
||||
|
||||
// Set up AppCompat Theme (if currently is [AppCompatActivity])
|
||||
override val moduleTheme get() = R.style.YourAppTheme
|
||||
|
||||
override fun getClassLoader() = delegate.getClassLoader()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
delegate.onCreate(savedInstanceState)
|
||||
super.onCreate(savedInstanceState)
|
||||
// Module App's Resources have been injected automatically
|
||||
// You can directly use xml to load the layout
|
||||
setContentView(R.layout.activity_main)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
delegate.onConfigurationChanged(newConfig)
|
||||
super.onConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
delegate.onRestoreInstanceState(savedInstanceState)
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you need to extends `ModuleAppCompatActivity`, you need to set the AppCompat theme manually.
|
||||
Then inherit the `Activity` you want to implement in `BaseActivity`.
|
||||
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
class HostTestActivity : ModuleAppCompatActivity() {
|
||||
|
||||
// The theme name here is for reference only
|
||||
// Please fill in the theme name already in your Module App
|
||||
override val moduleTheme get() = R.style.Theme_AppCompat
|
||||
class HostTestActivity : BaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -200,7 +211,7 @@ If you need to specify a delegated `Activity` to use another Host App's `Activit
|
||||
> The following example
|
||||
|
||||
```kotlin
|
||||
class HostTestActivity : ModuleAppActivity() {
|
||||
class HostTestActivity : BaseActivity() {
|
||||
|
||||
// Specify an additional proxy Activity class name
|
||||
// Which must also exist in the Host App's AndroidManifest
|
||||
@@ -210,7 +221,7 @@ class HostTestActivity : ModuleAppActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
// Module App's Resources have been injected automatically
|
||||
// You can directly use xml to load the layout
|
||||
setContentView(R. layout. activity_main)
|
||||
setContentView(R.layout.activity_main)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@@ -85,4 +85,10 @@ val instance: Any
|
||||
Now, `YukiHookAPI` no longer limits duplicate Hooks to the same method, you can hook multiple times on the same method.
|
||||
|
||||
`YukiHookAPI` also deprecated the `onAlreadyHooked` method of `hook { ... }`.
|
||||
Now this method will be useless and will not be called back. If necessary, please manually handle the relevant logic of duplicate Hooks.
|
||||
Now this method will be useless and will not be called back. If necessary, please manually handle the relevant logic of duplicate Hooks.
|
||||
|
||||
## Register Module App's Activity Behavior Change
|
||||
|
||||
`YukiHookAPI` Starting with `1.3.0`, the way in which the module `Activity` behavior has changed.
|
||||
|
||||
Please read [Register Module App's Activity](../api/special-features/host-inject#register-module-app-s-activity) for more information.
|
@@ -142,32 +142,45 @@ registerModuleAppActivities(proxy = "com.demo.test.activity.TestActivity")
|
||||
registerModuleAppActivities(TestActivity::class.java)
|
||||
```
|
||||
|
||||
注册完成后,请将你需要使用宿主启动的模块中的 `Activity` 继承于 `ModuleAppActivity` 或 `ModuleAppCompatActivity`。
|
||||
注册完成后,请将你需要使用宿主启动的模块中的 `Activity` 实现 `ModuleActivity` 接口。
|
||||
|
||||
这些 `Activity` 现在无需注册即可无缝存活于宿主中。
|
||||
|
||||
我们推荐你创建 `BaseActivity` 作为所有模块 `Activity` 的基类。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HostTestActivity : ModuleAppActivity() {
|
||||
abstract class BaseActivity : AppCompatActivity(), ModuleActivity {
|
||||
|
||||
// 设置 AppCompat 主题 (如果当前是 [AppCompatActivity])
|
||||
override val moduleTheme get() = R.style.YourAppTheme
|
||||
|
||||
override fun getClassLoader() = delegate.getClassLoader()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
delegate.onCreate(savedInstanceState)
|
||||
super.onCreate(savedInstanceState)
|
||||
// 模块资源已被自动注入,可以直接使用 xml 装载布局
|
||||
setContentView(R.layout.activity_main)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
delegate.onConfigurationChanged(newConfig)
|
||||
super.onConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
delegate.onRestoreInstanceState(savedInstanceState)
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
若你需要继承于 `ModuleAppCompatActivity`,你需要手动设置 AppCompat 主题。
|
||||
然后将需要实现的 `Activity` 继承于 `BaseActivity`。
|
||||
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HostTestActivity : ModuleAppCompatActivity() {
|
||||
|
||||
// 这里的主题名称仅供参考,请填写你模块中已有的主题名称
|
||||
override val moduleTheme get() = R.style.Theme_AppCompat
|
||||
class HostTestActivity : BaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -193,7 +206,7 @@ context.startActivity(context, HostTestActivity::class.java)
|
||||
> 示例如下
|
||||
|
||||
```kotlin
|
||||
class HostTestActivity : ModuleAppActivity() {
|
||||
class HostTestActivity : BaseActivity() {
|
||||
|
||||
// 指定一个另外的代理 Activity 类名,其也必须存在于宿主的 AndroidManifest 中
|
||||
override val proxyClassName get() = "com.demo.test.activity.OtherActivity"
|
||||
|
@@ -79,4 +79,10 @@ val instance: Any
|
||||
|
||||
`YukiHookAPI` 从 `1.3.0` 版本开始弃用了重复 Hook 的限制,现在,`YukiHookAPI` 不再限制重复 Hook 同一个方法,你可以在同一个方法上多次 Hook。
|
||||
|
||||
`YukiHookAPI` 同时弃用了 `hook { ... }` 的 `onAlreadyHooked` 方法,现在此方法将无作用且不会被回调,如有需要,请手动处理重复 Hook 的相关逻辑。
|
||||
`YukiHookAPI` 同时弃用了 `hook { ... }` 的 `onAlreadyHooked` 方法,现在此方法将无作用且不会被回调,如有需要,请手动处理重复 Hook 的相关逻辑。
|
||||
|
||||
## 注册模块 Activity 行为变更
|
||||
|
||||
`YukiHookAPI` 从 `1.3.0` 版本开始,注册模块 `Activity` 行为的方式发生了变更。
|
||||
|
||||
请阅读 [注册模块 Activity](../api/special-features/host-inject#注册模块-activity) 以了解更多信息。
|
@@ -70,6 +70,13 @@ libraries:
|
||||
version: 1.0.0
|
||||
hikage-widget-material:
|
||||
version: 1.0.0
|
||||
com.highcapable.betterandroid:
|
||||
ui-component:
|
||||
version: 1.0.7
|
||||
ui-extension:
|
||||
version: 1.0.6
|
||||
system-extension:
|
||||
version: 1.0.2
|
||||
androidx.annotation:
|
||||
annotation:
|
||||
version: 1.9.1
|
||||
|
@@ -44,6 +44,8 @@ dependencies {
|
||||
implementation(org.lsposed.hiddenapibypass.hiddenapibypass)
|
||||
implementation(com.highcapable.kavaref.kavaref.core)
|
||||
implementation(com.highcapable.kavaref.kavaref.extension)
|
||||
implementation(com.highcapable.betterandroid.ui.extension)
|
||||
implementation(com.highcapable.betterandroid.system.extension)
|
||||
implementation(androidx.core.core.ktx)
|
||||
implementation(androidx.appcompat.appcompat)
|
||||
implementation(androidx.preference.preference.ktx)
|
||||
|
@@ -21,7 +21,7 @@
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.core.api.reflect
|
||||
|
||||
import android.os.Build
|
||||
import com.highcapable.betterandroid.system.extension.tool.SystemVersion
|
||||
import com.highcapable.kavaref.resolver.processor.MemberProcessor
|
||||
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
||||
import java.lang.reflect.Constructor
|
||||
@@ -49,21 +49,13 @@ class AndroidHiddenApiBypassResolver private constructor() : MemberProcessor.Res
|
||||
fun get() = self
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
|
||||
return super.getDeclaredConstructors(declaringClass)
|
||||
val constructors = HiddenApiBypass.getDeclaredMethods(declaringClass)
|
||||
.filterIsInstance<Constructor<T>>()
|
||||
.toList()
|
||||
return constructors
|
||||
}
|
||||
override fun <T : Any> getDeclaredConstructors(declaringClass: Class<T>): List<Constructor<T>> =
|
||||
SystemVersion.require(SystemVersion.P, super.getDeclaredConstructors(declaringClass)) {
|
||||
HiddenApiBypass.getDeclaredMethods(declaringClass).filterIsInstance<Constructor<T>>().toList()
|
||||
}
|
||||
|
||||
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
|
||||
return super.getDeclaredMethods(declaringClass)
|
||||
val methods = HiddenApiBypass.getDeclaredMethods(declaringClass)
|
||||
.filterIsInstance<Method>()
|
||||
.toList()
|
||||
return methods
|
||||
}
|
||||
override fun <T : Any> getDeclaredMethods(declaringClass: Class<T>): List<Method> =
|
||||
SystemVersion.require(SystemVersion.P, super.getDeclaredMethods(declaringClass)) {
|
||||
HiddenApiBypass.getDeclaredMethods(declaringClass).filterIsInstance<Method>().toList()
|
||||
}
|
||||
}
|
@@ -28,20 +28,19 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.os.Process
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StyleRes
|
||||
import androidx.core.net.toUri
|
||||
import com.highcapable.betterandroid.system.extension.tool.SystemVersion
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
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.parasitic.activity.proxy.ModuleActivity
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.context.wrapper.ModuleContextThemeWrapper
|
||||
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookPrefsBridge
|
||||
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
|
||||
@@ -163,7 +162,7 @@ fun Resources.injectModuleAppResources() = AppParasitics.injectModuleAppResource
|
||||
*
|
||||
* 使用此方法会在未注册的 [Activity] 在 Hook APP (宿主) 中启动时自动调用 [injectModuleAppResources] 注入当前 Xposed 模块的资源
|
||||
*
|
||||
* - 你要将需要在宿主启动的 [Activity] 继承于 [ModuleAppActivity] or [ModuleAppCompatActivity]
|
||||
* - 你要将需要在宿主启动的 [Activity] 实现 [ModuleActivity] 接口
|
||||
*
|
||||
* 详情请参考 [注册模块 Activity](https://highcapable.github.io/YukiHookAPI/zh-cn/api/special-features/host-inject#%E6%B3%A8%E5%86%8C%E6%A8%A1%E5%9D%97-activity)
|
||||
*
|
||||
@@ -174,7 +173,7 @@ fun Resources.injectModuleAppResources() = AppParasitics.injectModuleAppResource
|
||||
* - 最低支持 Android 7.0 (API 24)
|
||||
* @param proxy 代理的 [Activity] - 必须存在于宿主的 AndroidMainifest 清单中 - 不填使用默认 [Activity]
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
@RequiresApi(SystemVersion.N)
|
||||
fun Context.registerModuleAppActivities(proxy: Any? = null) = AppParasitics.registerModuleAppActivities(context = this, proxy)
|
||||
|
||||
/**
|
||||
|
@@ -23,7 +23,6 @@
|
||||
|
||||
package com.highcapable.yukihookapi.hook.xposed.channel
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.ActivityManager
|
||||
import android.app.Application
|
||||
@@ -32,11 +31,13 @@ import android.content.Context
|
||||
import android.content.Context.ACTIVITY_SERVICE
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.os.TransactionTooLargeException
|
||||
import com.highcapable.betterandroid.system.extension.component.BroadcastReceiver
|
||||
import com.highcapable.betterandroid.system.extension.component.registerReceiver
|
||||
import com.highcapable.betterandroid.system.extension.component.sendBroadcast
|
||||
import com.highcapable.yukihookapi.YukiHookAPI
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.log.data.YLogData
|
||||
@@ -142,26 +143,21 @@ class YukiHookDataChannel private constructor() {
|
||||
private var isAllowSendTooLargeData = false
|
||||
|
||||
/** 广播接收器 */
|
||||
private val handlerReceiver by lazy {
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent == null) return
|
||||
intent.action?.also { action ->
|
||||
runCatching {
|
||||
receiverCallbacks.takeIf { it.isNotEmpty() }?.apply {
|
||||
mutableListOf<String>().also { destroyedCallbacks ->
|
||||
forEach { (key, it) ->
|
||||
when {
|
||||
(it.first as? Activity?)?.isDestroyed == true -> destroyedCallbacks.add(key)
|
||||
isCurrentBroadcast(it.first) -> it.second(action, intent)
|
||||
}
|
||||
}
|
||||
destroyedCallbacks.takeIf { it.isNotEmpty() }?.forEach { remove(it) }
|
||||
private val handlerReceiver = BroadcastReceiver { _, intent ->
|
||||
intent.action?.also { action ->
|
||||
runCatching {
|
||||
receiverCallbacks.takeIf { it.isNotEmpty() }?.apply {
|
||||
mutableListOf<String>().also { destroyedCallbacks ->
|
||||
forEach { (key, it) ->
|
||||
when {
|
||||
(it.first as? Activity?)?.isDestroyed == true -> destroyedCallbacks.add(key)
|
||||
isCurrentBroadcast(it.first) -> it.second(action, intent)
|
||||
}
|
||||
}
|
||||
}.onFailure { YLog.innerE("Received action \"$action\" failed", it) }
|
||||
destroyedCallbacks.takeIf { it.isNotEmpty() }?.forEach { remove(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}.onFailure { YLog.innerE("Received action \"$action\" failed", it) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,18 +204,10 @@ class YukiHookDataChannel private constructor() {
|
||||
internal fun register(context: Context?, packageName: String = context?.packageName ?: "") {
|
||||
if (YukiHookAPI.Configs.isEnableDataChannel.not() || context == null) return
|
||||
receiverContext = context
|
||||
IntentFilter().apply {
|
||||
val filter = IntentFilter().apply {
|
||||
addAction(if (isXposedEnvironment) hostActionName(packageName) else moduleActionName(context))
|
||||
}.also { filter ->
|
||||
/**
|
||||
* 从 Android 14 (及预览版) 开始
|
||||
* 外部广播必须声明 [Context.RECEIVER_EXPORTED]
|
||||
*/
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
if (Build.VERSION.SDK_INT >= 33)
|
||||
context.registerReceiver(handlerReceiver, filter, Context.RECEIVER_EXPORTED)
|
||||
else context.registerReceiver(handlerReceiver, filter)
|
||||
}
|
||||
context.registerReceiver(filter, exported = true, body = handlerReceiver)
|
||||
/** 排除模块环境下模块注册自身广播 */
|
||||
if (isXposedEnvironment.not()) return
|
||||
nameSpace(context, packageName).with {
|
||||
@@ -696,13 +684,13 @@ class YukiHookDataChannel private constructor() {
|
||||
*/
|
||||
private fun pushReceiver(wrapper: ChannelDataWrapper<*>) {
|
||||
/** 发送广播 */
|
||||
(context ?: AppParasitics.currentApplication)?.sendBroadcast(Intent().apply {
|
||||
(context ?: AppParasitics.currentApplication)?.sendBroadcast {
|
||||
action = if (isXposedEnvironment) moduleActionName() else hostActionName(packageName)
|
||||
/** 由于系统框架的包名可能不唯一 - 为防止发生问题不再对系统框架的广播设置接收者包名 */
|
||||
if (packageName != AppParasitics.SYSTEM_FRAMEWORK_NAME)
|
||||
setPackage(if (isXposedEnvironment) YukiXposedModule.modulePackageName else packageName)
|
||||
putExtra(wrapper.instance.key + keyNonRepeatName, wrapper)
|
||||
}) ?: YLog.innerE("Failed to sendBroadcast like \"${wrapper.instance.key}\", because got null context in \"$packageName\"")
|
||||
} ?: YLog.innerE("Failed to sendBroadcast like \"${wrapper.instance.key}\", because got null context in \"$packageName\"")
|
||||
}
|
||||
}
|
||||
}
|
@@ -30,7 +30,6 @@ import android.app.ActivityManager
|
||||
import android.app.AndroidAppHelper
|
||||
import android.app.Application
|
||||
import android.app.Instrumentation
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
@@ -39,10 +38,11 @@ import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.UserHandle
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.highcapable.betterandroid.system.extension.component.registerReceiver
|
||||
import com.highcapable.betterandroid.system.extension.tool.SystemVersion
|
||||
import com.highcapable.kavaref.KavaRef.Companion.resolve
|
||||
import com.highcapable.kavaref.extension.classOf
|
||||
import com.highcapable.kavaref.extension.lazyClass
|
||||
@@ -363,20 +363,8 @@ internal object AppParasitics {
|
||||
* @param result 回调 - ([Context] 当前实例, [Intent] 当前对象)
|
||||
*/
|
||||
fun IntentFilter.registerReceiver(result: (Context, Intent) -> Unit) {
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (context == null || intent == null) return
|
||||
result(context, intent)
|
||||
}
|
||||
}.also { receiver ->
|
||||
/**
|
||||
* 从 Android 14 (及预览版) 开始
|
||||
* 外部广播必须声明 [Context.RECEIVER_EXPORTED]
|
||||
*/
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
if (Build.VERSION.SDK_INT >= 33)
|
||||
it.registerReceiver(receiver, this, Context.RECEIVER_EXPORTED)
|
||||
else it.registerReceiver(receiver, this)
|
||||
it.registerReceiver(filter = this, exported = true) { context, intent ->
|
||||
result(context, intent)
|
||||
}
|
||||
}
|
||||
hostApplication = it
|
||||
@@ -431,13 +419,13 @@ internal object AppParasitics {
|
||||
* @param context 当前 [Context]
|
||||
* @param proxy 代理的 [Activity]
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
@RequiresApi(SystemVersion.N)
|
||||
internal fun registerModuleAppActivities(context: Context, proxy: Any?) {
|
||||
if (isActivityProxyRegistered) return
|
||||
if (YukiXposedModule.isXposedEnvironment.not()) return YLog.innerW("You can only register Activity Proxy in Xposed Environment")
|
||||
if (context.packageName == YukiXposedModule.modulePackageName) return YLog.innerE("You cannot register Activity Proxy into yourself")
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
if (Build.VERSION.SDK_INT < 24) return YLog.innerE("Activity Proxy only support for Android 7.0 (API 24) or higher")
|
||||
if (SystemVersion.isLowTo(SystemVersion.N)) return YLog.innerE("Activity Proxy only support for Android 7.0 (API 24) or higher")
|
||||
runCatching {
|
||||
ActivityProxyConfig.apply {
|
||||
proxyIntentName = "${YukiXposedModule.modulePackageName}.ACTIVITY_PROXY_INTENT"
|
||||
|
@@ -23,45 +23,38 @@
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.CallSuper
|
||||
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
|
||||
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.reference.ModuleClassLoader
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.proxy.ModuleActivity
|
||||
|
||||
/**
|
||||
* 代理 [Activity]
|
||||
*
|
||||
* 继承于此类的 [Activity] 可以同时在宿主与模块中启动
|
||||
* - 由于超类继承者的不唯一性 - 此类已弃用 - 在之后的版本中将直接被删除
|
||||
*
|
||||
* - 在 (Xposed) 宿主环境需要在宿主启动时调用 [Context.registerModuleAppActivities] 进行注册
|
||||
* - 请现在参考并迁移到 [ModuleActivity]
|
||||
*/
|
||||
open class ModuleAppActivity : Activity() {
|
||||
@Deprecated(message = "请使用新方式来实现此功能")
|
||||
open class ModuleAppActivity : Activity(), ModuleActivity {
|
||||
|
||||
/**
|
||||
* 设置当前代理的 [Activity] 类名
|
||||
*
|
||||
* 留空则使用 [Context.registerModuleAppActivities] 时设置的类名
|
||||
*
|
||||
* - 代理的 [Activity] 类名必须存在于宿主的 AndroidMainifest 清单中
|
||||
* @return [String]
|
||||
*/
|
||||
open val proxyClassName get() = ""
|
||||
|
||||
override fun getClassLoader(): ClassLoader? = ModuleClassLoader.instance()
|
||||
override fun getClassLoader() = delegate.getClassLoader()
|
||||
|
||||
@CallSuper
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
if (YukiXposedModule.isXposedEnvironment) injectModuleAppResources()
|
||||
delegate.onConfigurationChanged(newConfig)
|
||||
super.onConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
savedInstanceState.getBundle("android:viewHierarchyState")?.classLoader = classLoader
|
||||
delegate.onRestoreInstanceState(savedInstanceState)
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
delegate.onCreate(savedInstanceState)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
}
|
@@ -22,61 +22,39 @@
|
||||
*/
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.base
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
|
||||
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.reference.ModuleClassLoader
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.proxy.ModuleActivity
|
||||
|
||||
/**
|
||||
* 代理 [AppCompatActivity]
|
||||
*
|
||||
* 继承于此类的 [Activity] 可以同时在宿主与模块中启动
|
||||
* - 由于超类继承者的不唯一性 - 此类已弃用 - 在之后的版本中将直接被删除
|
||||
*
|
||||
* - 在 (Xposed) 宿主环境需要在宿主启动时调用 [Context.registerModuleAppActivities] 进行注册
|
||||
*
|
||||
* - 在 (Xposed) 宿主环境需要重写 [moduleTheme] 设置 AppCompat 主题 - 否则会无法启动
|
||||
* - 请现在参考并迁移到 [ModuleActivity]
|
||||
*/
|
||||
open class ModuleAppCompatActivity : AppCompatActivity() {
|
||||
@Deprecated(message = "请使用新方式来实现此功能")
|
||||
open class ModuleAppCompatActivity : AppCompatActivity(), ModuleActivity {
|
||||
|
||||
/**
|
||||
* 设置当前代理的 [Activity] 类名
|
||||
*
|
||||
* 留空则使用 [Context.registerModuleAppActivities] 时设置的类名
|
||||
*
|
||||
* - 代理的 [Activity] 类名必须存在于宿主的 AndroidMainifest 清单中
|
||||
* @return [String]
|
||||
*/
|
||||
open val proxyClassName get() = ""
|
||||
|
||||
/**
|
||||
* 设置当前代理的 [Activity] 主题
|
||||
* @return [Int]
|
||||
*/
|
||||
open val moduleTheme get() = -1
|
||||
|
||||
override fun getClassLoader(): ClassLoader? = ModuleClassLoader.instance()
|
||||
override fun getClassLoader() = delegate.getClassLoader()
|
||||
|
||||
@CallSuper
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
if (YukiXposedModule.isXposedEnvironment) injectModuleAppResources()
|
||||
delegate.onConfigurationChanged(newConfig)
|
||||
super.onConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
savedInstanceState.getBundle("android:viewHierarchyState")?.classLoader = classLoader
|
||||
delegate.onRestoreInstanceState(savedInstanceState)
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (YukiXposedModule.isXposedEnvironment && moduleTheme != -1) setTheme(moduleTheme)
|
||||
delegate.onCreate(savedInstanceState)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
}
|
@@ -26,11 +26,11 @@ package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.call
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.IBinder
|
||||
import android.os.Message
|
||||
import com.highcapable.betterandroid.system.extension.tool.SystemVersion
|
||||
import com.highcapable.kavaref.KavaRef.Companion.resolve
|
||||
import com.highcapable.kavaref.extension.lazyClass
|
||||
import com.highcapable.yukihookapi.hook.core.api.reflect.AndroidHiddenApiBypassResolver
|
||||
@@ -93,7 +93,7 @@ internal object HandlerDelegateCaller {
|
||||
if (intent?.hasExtra(ActivityProxyConfig.proxyIntentName) == true) {
|
||||
@Suppress("DEPRECATION")
|
||||
val subIntent = intent.getParcelableExtra<Intent>(ActivityProxyConfig.proxyIntentName)
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
if (SystemVersion.isHighOrEqualsTo(SystemVersion.S)) {
|
||||
val currentActivityThread = ActivityThreadClass.resolve()
|
||||
.processor(AndroidHiddenApiBypassResolver.get())
|
||||
.optional(silent = true)
|
||||
|
@@ -32,9 +32,8 @@ import com.highcapable.kavaref.extension.hasClass
|
||||
import com.highcapable.kavaref.extension.isSubclassOf
|
||||
import com.highcapable.kavaref.extension.toClassOrNull
|
||||
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.parasitic.activity.config.ActivityProxyConfig
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.proxy.ModuleActivity
|
||||
import java.lang.reflect.InvocationHandler
|
||||
import java.lang.reflect.Method
|
||||
|
||||
@@ -75,10 +74,8 @@ internal object IActivityManagerProxyCaller {
|
||||
fun String.verify() = if (AppParasitics.hostApplication?.classLoader?.hasClass(this) == true) this else null
|
||||
setClassName(component.packageName, component.className.toClassOrNull()?.runCatching {
|
||||
when {
|
||||
this isSubclassOf ModuleAppActivity::class ->
|
||||
createInstanceAsTypeOrNull<ModuleAppActivity>()?.proxyClassName?.verify()
|
||||
this isSubclassOf ModuleAppCompatActivity::class ->
|
||||
createInstanceAsTypeOrNull<ModuleAppCompatActivity>()?.proxyClassName?.verify()
|
||||
this isSubclassOf ModuleActivity::class ->
|
||||
createInstanceAsTypeOrNull<ModuleActivity>()?.proxyClassName?.verify()
|
||||
else -> null
|
||||
}
|
||||
}?.getOrNull() ?: ActivityProxyConfig.proxyClassName)
|
||||
|
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Hook API and Xposed Module solution built in Kotlin.
|
||||
* Copyright (C) 2019 HighCapable
|
||||
* https://github.com/HighCapable/YukiHookAPI
|
||||
*
|
||||
* Apache License Version 2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is created by fankes on 2025/6/18.
|
||||
*/
|
||||
@file:Suppress("UNUSED_PARAMETER")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.xposed.parasitic.activity.proxy
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
|
||||
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
|
||||
import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.activity.proxy.ModuleActivity.Delegate
|
||||
import com.highcapable.yukihookapi.hook.xposed.parasitic.reference.ModuleClassLoader
|
||||
|
||||
/**
|
||||
* 模块 [Activity] 代理接口
|
||||
*
|
||||
* 实现了此接口的 [Activity] 可以同时在宿主与模块中启动
|
||||
*
|
||||
* - 在 (Xposed) 宿主环境需要在宿主启动时调用 [Context.registerModuleAppActivities] 进行注册
|
||||
*
|
||||
* - 在 (Xposed) 宿主环境需要重写 [moduleTheme] 设置 AppCompat 主题 (如果当前是 [AppCompatActivity]) - 否则会无法启动
|
||||
*
|
||||
* 请参考下方示例手动调用 [delegate] 对 [Activity] 完成必要方法的注册 - 建议在自己的 `BaseActivity` 中实现此接口并重写相关方法 -
|
||||
* 然后继承自此 `BaseActivity` 来实现模块 [Activity] 的代理
|
||||
*
|
||||
* ```kotlin
|
||||
* abstract class BaseActivity : AppCompatActivity(), ModuleActivity {
|
||||
*
|
||||
* // 设置 AppCompat 主题 (如果当前是 [AppCompatActivity])
|
||||
* override val moduleTheme get() = R.style.YourAppTheme
|
||||
*
|
||||
* override fun getClassLoader() = delegate.getClassLoader()
|
||||
*
|
||||
* override fun onCreate(savedInstanceState: Bundle?) {
|
||||
* delegate.onCreate(savedInstanceState)
|
||||
* super.onCreate(savedInstanceState)
|
||||
* }
|
||||
*
|
||||
* override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
* delegate.onConfigurationChanged(newConfig)
|
||||
* super.onConfigurationChanged(newConfig)
|
||||
* }
|
||||
*
|
||||
* override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
* delegate.onRestoreInstanceState(savedInstanceState)
|
||||
* super.onRestoreInstanceState(savedInstanceState)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @see Delegate
|
||||
*/
|
||||
interface ModuleActivity {
|
||||
|
||||
/**
|
||||
* 模块 [Activity] 代理提供者
|
||||
*/
|
||||
class Delegate internal constructor(private val self: ModuleActivity) {
|
||||
|
||||
private val selfActivity get() = self as? Activity ?: error("ModuleActivity must be implemented an Activity")
|
||||
|
||||
/**
|
||||
* @see Activity.getClassLoader
|
||||
*/
|
||||
fun getClassLoader() = ModuleClassLoader.instance()
|
||||
|
||||
/**
|
||||
* @see Activity.onCreate
|
||||
*/
|
||||
fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (YukiXposedModule.isXposedEnvironment && self.moduleTheme != -1)
|
||||
selfActivity.setTheme(self.moduleTheme)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Activity.onConfigurationChanged
|
||||
*/
|
||||
fun onConfigurationChanged(newConfig: Configuration) {
|
||||
if (YukiXposedModule.isXposedEnvironment) selfActivity.injectModuleAppResources()
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Activity.onRestoreInstanceState
|
||||
*/
|
||||
fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
savedInstanceState.getBundle("android:viewHierarchyState")?.classLoader = selfActivity.classLoader
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 [ModuleActivity] 的 [Delegate] 实例
|
||||
* @return [Delegate]
|
||||
*/
|
||||
val delegate get() = Delegate(self = this)
|
||||
|
||||
/**
|
||||
* 设置当前代理的 [Activity] 主题
|
||||
* @return [Int]
|
||||
*/
|
||||
val moduleTheme get() = -1
|
||||
|
||||
/**
|
||||
* 设置当前代理的 [Activity] 类名
|
||||
*
|
||||
* 留空则使用 [Context.registerModuleAppActivities] 时设置的类名
|
||||
*
|
||||
* - 代理的 [Activity] 类名必须存在于宿主的 AndroidMainifest 清单中
|
||||
* @return [String]
|
||||
*/
|
||||
val proxyClassName get() = ""
|
||||
}
|
Reference in New Issue
Block a user