mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-06 10:45:47 +08:00
Modify move documentation and replace applyTheme to applyModuleTheme
This commit is contained in:
@@ -132,67 +132,6 @@ fun Resources.injectModuleAppResources()
|
|||||||
|
|
||||||
注入的资源作用域仅限当前 `Context` 或 `Resources`,你需要在每个用到宿主 `Context` 或 `Resources` 的地方重复调用此方法进行注入才能使用。
|
注入的资源作用域仅限当前 `Context` 或 `Resources`,你需要在每个用到宿主 `Context` 或 `Resources` 的地方重复调用此方法进行注入才能使用。
|
||||||
|
|
||||||
为防止资源 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` 注入当前模块资源。
|
|
||||||
|
|
||||||
> 示例如下
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
injectMember {
|
|
||||||
method {
|
|
||||||
name = "onCreate"
|
|
||||||
param(BundleClass)
|
|
||||||
}
|
|
||||||
afterHook {
|
|
||||||
instance<Activity>().also {
|
|
||||||
// <方案1> 通过 Context 注入模块资源
|
|
||||||
it.injectModuleAppResources()
|
|
||||||
// <方案2> 直接得到宿主 Resources 注入模块资源
|
|
||||||
it.resources.injectModuleAppResources()
|
|
||||||
// 直接使用模块资源 ID
|
|
||||||
it.getString(R.id.app_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
你还可以直接在 `AppLifecycle` 中注入当前模块资源。
|
|
||||||
|
|
||||||
> 示例如下
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
onAppLifecycle {
|
|
||||||
onCreate {
|
|
||||||
// 全局注入模块资源,但仅限于全局生命周期,类似 ImageView.setImageResource 这样的方法在 Activity 中需要单独注入
|
|
||||||
// <方案1> 通过 Context 注入模块资源
|
|
||||||
injectModuleAppResources()
|
|
||||||
// <方案2> 直接得到宿主 Resources 注入模块资源
|
|
||||||
resources.injectModuleAppResources()
|
|
||||||
// 直接使用模块资源 ID
|
|
||||||
getString(R.id.app_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Context.registerModuleAppActivities *- ext-method*
|
### Context.registerModuleAppActivities *- ext-method*
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
@@ -213,136 +152,10 @@ fun Context.registerModuleAppActivities(proxy: Any?)
|
|||||||
|
|
||||||
你要将需要在宿主启动的 `Activity` 继承于 `ModuleAppActivity` 或 `ModuleAppCompatActivity`。
|
你要将需要在宿主启动的 `Activity` 继承于 `ModuleAppActivity` 或 `ModuleAppCompatActivity`。
|
||||||
|
|
||||||
为防止资源 ID 互相冲突,你需要在当前 Xposed 模块项目的 `build.gradle` 中修改资源 ID。
|
### Context.applyModuleTheme *- ext-method*
|
||||||
|
|
||||||
- Kotlin Gradle DSL
|
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
androidResources.additionalParameters("--allow-reserved-package-id", "--package-id", "0x64")
|
fun Context.applyModuleTheme(theme: Int, configuration: Configuration?): ModuleContextThemeWrapper
|
||||||
```
|
|
||||||
|
|
||||||
- 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)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Context.applyTheme *- ext-method*
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
fun Context.applyTheme(theme: Int, configuration: Configuration?): ModuleContextThemeWrapper
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**变更记录**
|
**变更记录**
|
||||||
@@ -351,90 +164,12 @@ fun Context.applyTheme(theme: Int, configuration: Configuration?): ModuleContext
|
|||||||
|
|
||||||
**功能描述**
|
**功能描述**
|
||||||
|
|
||||||
> 生成一个 `ContextThemeWrapper` 代理以应用主题资源。
|
> 生成一个 `ContextThemeWrapper` 代理以应用当前 Xposed 模块的主题资源。
|
||||||
|
|
||||||
在 Hook APP (宿主) 中使用此方法会自动调用 `injectModuleAppResources` 注入当前 Xposed 模块的资源。
|
在 Hook APP (宿主) 中使用此方法会自动调用 `injectModuleAppResources` 注入当前 Xposed 模块的资源。
|
||||||
|
|
||||||
如果在 Hook APP (宿主) 中使用此方法发生 `ClassCastException`,请手动设置 `configuration`。
|
如果在 Hook APP (宿主) 中使用此方法发生 `ClassCastException`,请手动设置 `configuration`。
|
||||||
|
|
||||||
为防止资源 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。
|
|
||||||
|
|
||||||
**功能示例**
|
|
||||||
|
|
||||||
有时候,我们需要使用 `MaterialAlertDialogBuilder` 来美化自己在宿主中的对话框,但是拿不到 AppCompat 主题就无法创建。
|
|
||||||
|
|
||||||
- 会得到如下异常
|
|
||||||
|
|
||||||
```
|
|
||||||
The style on this component requires your app theme to be Theme.AppCompat (or a descendant).
|
|
||||||
```
|
|
||||||
|
|
||||||
这时,我们想在宿主被 Hook 的当前 `Activity` 中使用 `MaterialAlertDialogBuilder` 来创建对话框,就可以有如下方法。
|
|
||||||
|
|
||||||
> 示例如下
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
injectMember {
|
|
||||||
method {
|
|
||||||
name = "onCreate"
|
|
||||||
param(BundleClass)
|
|
||||||
}
|
|
||||||
afterHook {
|
|
||||||
// 使用 applyTheme 创建一个当前模块中的主题资源
|
|
||||||
val appCompatContext = instance<Activity>().applyTheme(R.style.Theme_AppCompat)
|
|
||||||
// 直接使用这个包装了模块主题后的 Context 创建对话框
|
|
||||||
MaterialAlertDialogBuilder(appCompatContext)
|
|
||||||
.setTitle("AppCompat 主题对话框")
|
|
||||||
.setMessage("我是一个在宿主中显示的 AppCompat 主题对话框。")
|
|
||||||
.setPositiveButton("确定", null)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
你还可以对当前 `Context` 通过 `uiMode` 设置原生的夜间模式和日间模式,至少需要 Android 10 及以上系统版本支持且当前主题包含夜间模式相关元素。
|
|
||||||
|
|
||||||
> 示例如下
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
injectMember {
|
|
||||||
method {
|
|
||||||
name = "onCreate"
|
|
||||||
param(BundleClass)
|
|
||||||
}
|
|
||||||
afterHook {
|
|
||||||
// 定义当前模块中的主题资源
|
|
||||||
var appCompatContext: ModuleContextThemeWrapper
|
|
||||||
// <方案1> 直接得到 Configuration 对象设置
|
|
||||||
appCompatContext = instance<Activity>().applyTheme(R.style.Theme_AppCompat).applyConfiguration { uiMode = Configuration.UI_MODE_NIGHT_YES }
|
|
||||||
// <方案2> 创建一个新的 Configuration 对象,但会破坏当前宿主中原有的字体缩放大小等设置,你需要手动重新传递 densityDpi 等参数
|
|
||||||
appCompatContext = instance<Activity>().applyTheme(R.style.Theme_AppCompat, Configuration().apply { uiMode = Configuration.UI_MODE_NIGHT_YES })
|
|
||||||
// 直接使用这个包装了模块主题后的 Context 创建对话框
|
|
||||||
MaterialAlertDialogBuilder(appCompatContext)
|
|
||||||
.setTitle("AppCompat 主题对话框")
|
|
||||||
.setMessage("我是一个在宿主中显示的 AppCompat 主题对话框。")
|
|
||||||
.setPositiveButton("确定", null)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
这样,我们就可以在宿主中非常简单地使用 `MaterialAlertDialogBuilder` 创建对话框了。
|
|
||||||
|
|
||||||
### ~~isSupportResourcesHook *- field*~~ <!-- {docsify-ignore} -->
|
### ~~isSupportResourcesHook *- field*~~ <!-- {docsify-ignore} -->
|
||||||
|
|
||||||
**变更记录**
|
**变更记录**
|
||||||
|
@@ -1492,5 +1492,256 @@ loadApp(name = "com.example.demo") {
|
|||||||
|
|
||||||
详情请参考 [AppLifecycle](api/document?id=applifecycle-class)。
|
详情请参考 [AppLifecycle](api/document?id=applifecycle-class)。
|
||||||
|
|
||||||
|
## 宿主资源注入扩展功能
|
||||||
|
|
||||||
|
> 这是一个将模块资源、`Activity` 组件以及 `Context` 主题注入到宿主的扩展功能。
|
||||||
|
|
||||||
|
在使用以下功能之前,为防止资源 ID 互相冲突,你需要在当前 Xposed 模块项目的 `build.gradle` 中修改资源 ID。
|
||||||
|
|
||||||
|
- Kotlin Gradle DSL
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
android {
|
||||||
|
androidResources.additionalParameters("--allow-reserved-package-id", "--package-id", "0x64")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Groovy
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
android {
|
||||||
|
aaptOptions.additionalParameters '--allow-reserved-package-id', '--package-id', '0x64'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!> 提供的示例资源 ID 值仅供参考,不可使用 `0x7f`,默认为 `0x64`,为了防止当前宿主存在多个 Xposed 模块,建议自定义你自己的资源 ID。
|
||||||
|
|
||||||
|
### 注入模块资源 (Resources)
|
||||||
|
|
||||||
|
在 Hook 宿主之后,我们可以直接在 Hooker 中得到的 `Context` 注入当前模块资源。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
injectMember {
|
||||||
|
method {
|
||||||
|
name = "onCreate"
|
||||||
|
param(BundleClass)
|
||||||
|
}
|
||||||
|
afterHook {
|
||||||
|
instance<Activity>().also {
|
||||||
|
// <方案1> 通过 Context 注入模块资源
|
||||||
|
it.injectModuleAppResources()
|
||||||
|
// <方案2> 直接得到宿主 Resources 注入模块资源
|
||||||
|
it.resources.injectModuleAppResources()
|
||||||
|
// 直接使用模块资源 ID
|
||||||
|
it.getString(R.id.app_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
你还可以直接在 `AppLifecycle` 中注入当前模块资源。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
onAppLifecycle {
|
||||||
|
onCreate {
|
||||||
|
// 全局注入模块资源,但仅限于全局生命周期,类似 ImageView.setImageResource 这样的方法在 Activity 中需要单独注入
|
||||||
|
// <方案1> 通过 Context 注入模块资源
|
||||||
|
injectModuleAppResources()
|
||||||
|
// <方案2> 直接得到宿主 Resources 注入模块资源
|
||||||
|
resources.injectModuleAppResources()
|
||||||
|
// 直接使用模块资源 ID
|
||||||
|
getString(R.id.app_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
详情请参考 [Context+Resources.injectModuleAppResources](api/document?id=contextresourcesinjectmoduleappresources-ext-method)。
|
||||||
|
|
||||||
|
### 注册模块 Activity
|
||||||
|
|
||||||
|
在 Android 系统中所有应用的 `Activity` 启动时,都需要在 `AndroidManifest.xml` 中进行注册,在 Hook 过程中,如果我们想通过宿主来直接启动模块中未注册的 `Activity` 要怎么做呢?
|
||||||
|
|
||||||
|
在 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)
|
||||||
|
```
|
||||||
|
|
||||||
|
详情请参考 [Context.registerModuleAppActivities](api/document?id=contextregistermoduleappactivities-ext-method)。
|
||||||
|
|
||||||
|
### 创建 ContextThemeWrapper 代理
|
||||||
|
|
||||||
|
有时候,我们需要使用 `MaterialAlertDialogBuilder` 来美化自己在宿主中的对话框,但是拿不到 AppCompat 主题就无法创建。
|
||||||
|
|
||||||
|
- 会得到如下异常
|
||||||
|
|
||||||
|
```
|
||||||
|
The style on this component requires your app theme to be Theme.AppCompat (or a descendant).
|
||||||
|
```
|
||||||
|
|
||||||
|
这时,我们想在宿主被 Hook 的当前 `Activity` 中使用 `MaterialAlertDialogBuilder` 来创建对话框,就可以有如下方法。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
injectMember {
|
||||||
|
method {
|
||||||
|
name = "onCreate"
|
||||||
|
param(BundleClass)
|
||||||
|
}
|
||||||
|
afterHook {
|
||||||
|
// 使用 applyModuleTheme 创建一个当前模块中的主题资源
|
||||||
|
val appCompatContext = instance<Activity>().applyModuleTheme(R.style.Theme_AppCompat)
|
||||||
|
// 直接使用这个包装了模块主题后的 Context 创建对话框
|
||||||
|
MaterialAlertDialogBuilder(appCompatContext)
|
||||||
|
.setTitle("AppCompat 主题对话框")
|
||||||
|
.setMessage("我是一个在宿主中显示的 AppCompat 主题对话框。")
|
||||||
|
.setPositiveButton("确定", null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
你还可以对当前 `Context` 通过 `uiMode` 设置原生的夜间模式和日间模式,至少需要 Android 10 及以上系统版本支持且当前主题包含夜间模式相关元素。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
injectMember {
|
||||||
|
method {
|
||||||
|
name = "onCreate"
|
||||||
|
param(BundleClass)
|
||||||
|
}
|
||||||
|
afterHook {
|
||||||
|
// 定义当前模块中的主题资源
|
||||||
|
var appCompatContext: ModuleContextThemeWrapper
|
||||||
|
// <方案1> 直接得到 Configuration 对象设置
|
||||||
|
appCompatContext = instance<Activity>().applyModuleTheme(R.style.Theme_AppCompat).applyConfiguration { uiMode = Configuration.UI_MODE_NIGHT_YES }
|
||||||
|
// <方案2> 创建一个新的 Configuration 对象,但会破坏当前宿主中原有的字体缩放大小等设置,你需要手动重新传递 densityDpi 等参数
|
||||||
|
appCompatContext = instance<Activity>().applyModuleTheme(R.style.Theme_AppCompat, Configuration().apply { uiMode = Configuration.UI_MODE_NIGHT_YES })
|
||||||
|
// 直接使用这个包装了模块主题后的 Context 创建对话框
|
||||||
|
MaterialAlertDialogBuilder(appCompatContext)
|
||||||
|
.setTitle("AppCompat 主题对话框")
|
||||||
|
.setMessage("我是一个在宿主中显示的 AppCompat 主题对话框。")
|
||||||
|
.setPositiveButton("确定", null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这样,我们就可以在宿主中非常简单地使用 `MaterialAlertDialogBuilder` 创建对话框了。
|
||||||
|
|
||||||
|
- 可能存在的问题
|
||||||
|
|
||||||
|
由于一些 APP 自身使用的 `androidx` 依赖库或自定义主题可能会对当前 `MaterialAlertDialog` 实际样式造成干扰,你可以参考 [模块 APP Demo](https://github.com/fankes/YukiHookAPI/tree/master/demo-module) 来修复这个问题。
|
||||||
|
|
||||||
|
某些 APP 在创建时可能会发生 `ClassCastException` 异常,请手动指定新的 `Configuration` 实例来进行修复。
|
||||||
|
|
||||||
|
详情请参考 [Context.applyModuleTheme](api/document?id=contextapplymoduletheme-ext-method)。
|
||||||
|
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
[浏览下一篇 ➡️](guide/move-to-new-api.md)
|
[浏览下一篇 ➡️](guide/move-to-new-api.md)
|
@@ -116,7 +116,7 @@ val Context.processName
|
|||||||
*
|
*
|
||||||
* 注入的资源作用域仅限当前 [Context] - 你需要在每个用到宿主 [Context] 的地方重复调用此方法进行注入才能使用
|
* 注入的资源作用域仅限当前 [Context] - 你需要在每个用到宿主 [Context] 的地方重复调用此方法进行注入才能使用
|
||||||
*
|
*
|
||||||
* 详情请参考 [API 文档 - Context+Resources.injectModuleAppResources](https://fankes.github.io/YukiHookAPI/#/api/document?id=contextresourcesinjectmoduleappresources-ext-method)
|
* 详情请参考 [注入模块资源 (Resources)](https://fankes.github.io/YukiHookAPI/#/guide/special-feature?id=%e6%b3%a8%e5%85%a5%e6%a8%a1%e5%9d%97%e8%b5%84%e6%ba%90-resources)
|
||||||
*
|
*
|
||||||
* - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息
|
* - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息
|
||||||
*/
|
*/
|
||||||
@@ -129,7 +129,7 @@ fun Context.injectModuleAppResources() = resources?.injectModuleAppResources()
|
|||||||
*
|
*
|
||||||
* 注入的资源作用域仅限当前 [Resources] - 你需要在每个用到宿主 [Resources] 的地方重复调用此方法进行注入才能使用
|
* 注入的资源作用域仅限当前 [Resources] - 你需要在每个用到宿主 [Resources] 的地方重复调用此方法进行注入才能使用
|
||||||
*
|
*
|
||||||
* 详情请参考 [API 文档 - Context+Resources.injectModuleAppResources](https://fankes.github.io/YukiHookAPI/#/api/document?id=contextresourcesinjectmoduleappresources-ext-method)
|
* 详情请参考 [注入模块资源 (Resources)](https://fankes.github.io/YukiHookAPI/#/guide/special-feature?id=%e6%b3%a8%e5%85%a5%e6%a8%a1%e5%9d%97%e8%b5%84%e6%ba%90-resources)
|
||||||
*
|
*
|
||||||
* - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息
|
* - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息
|
||||||
*/
|
*/
|
||||||
@@ -144,7 +144,7 @@ fun Resources.injectModuleAppResources() = AppParasitics.injectModuleAppResource
|
|||||||
*
|
*
|
||||||
* - 你要将需要在宿主启动的 [Activity] 继承于 [ModuleAppActivity] 或 [ModuleAppCompatActivity]
|
* - 你要将需要在宿主启动的 [Activity] 继承于 [ModuleAppActivity] 或 [ModuleAppCompatActivity]
|
||||||
*
|
*
|
||||||
* 详情请参考 [API 文档 - Context.registerModuleAppActivities](https://fankes.github.io/YukiHookAPI/#/api/document?id=contextregistermoduleappactivities-ext-method)
|
* 详情请参考 [注册模块 Activity](https://fankes.github.io/YukiHookAPI/#/guide/special-feature?id=%e6%b3%a8%e5%86%8c%e6%a8%a1%e5%9d%97-activity)
|
||||||
*
|
*
|
||||||
* - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息
|
* - ❗只能在 (Xposed) 宿主环境使用此功能 - 其它环境下使用将不生效且会打印警告信息
|
||||||
* @param proxy 代理的 [Activity] - 必须存在于宿主的 AndroidMainifest 清单中 - 不填使用默认 [Activity]
|
* @param proxy 代理的 [Activity] - 必须存在于宿主的 AndroidMainifest 清单中 - 不填使用默认 [Activity]
|
||||||
@@ -152,18 +152,18 @@ fun Resources.injectModuleAppResources() = AppParasitics.injectModuleAppResource
|
|||||||
fun Context.registerModuleAppActivities(proxy: Any? = null) = AppParasitics.registerModuleAppActivities(context = this, proxy)
|
fun Context.registerModuleAppActivities(proxy: Any? = null) = AppParasitics.registerModuleAppActivities(context = this, proxy)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成一个 [ContextThemeWrapper] 代理以应用主题资源
|
* 生成一个 [ContextThemeWrapper] 代理以应用当前 Xposed 模块的主题资源
|
||||||
*
|
*
|
||||||
* 在 Hook APP (宿主) 中使用此方法会自动调用 [injectModuleAppResources] 注入当前 Xposed 模块的资源
|
* 在 Hook APP (宿主) 中使用此方法会自动调用 [injectModuleAppResources] 注入当前 Xposed 模块的资源
|
||||||
*
|
*
|
||||||
* - 如果在 Hook APP (宿主) 中使用此方法发生 [ClassCastException] - 请手动设置新的 [configuration]
|
* - 如果在 Hook APP (宿主) 中使用此方法发生 [ClassCastException] - 请手动设置新的 [configuration]
|
||||||
*
|
*
|
||||||
* 详情请参考 [API 文档 - Context.applyTheme](https://fankes.github.io/YukiHookAPI/#/api/document?id=contextapplytheme-ext-method)
|
* 详情请参考 [创建 ContextThemeWrapper 代理](https://fankes.github.io/YukiHookAPI/#/guide/special-feature?id=%e5%88%9b%e5%bb%ba-contextthemewrapper-%e4%bb%a3%e7%90%86)
|
||||||
* @param theme 主题资源 ID
|
* @param theme 主题资源 ID
|
||||||
* @param configuration 使用的 [Configuration] - 默认空
|
* @param configuration 使用的 [Configuration] - 默认空
|
||||||
* @return [ModuleContextThemeWrapper]
|
* @return [ModuleContextThemeWrapper]
|
||||||
*/
|
*/
|
||||||
fun Context.applyTheme(@StyleRes theme: Int, configuration: Configuration? = null) =
|
fun Context.applyModuleTheme(@StyleRes theme: Int, configuration: Configuration? = null) =
|
||||||
ModuleContextThemeWrapper.wrapper(baseContext = this, theme, configuration)
|
ModuleContextThemeWrapper.wrapper(baseContext = this, theme, configuration)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user