# Host Resource Injection Extension

> This is an extension that injects Module App's Resources, `Activity` components, and `Context` topics into the Host App.

Before using the following functions, in order to prevent Resource Id from conflicting with each other, you need to modify the Resource Id in the `build.gradle` of the current Xposed Module project.

- 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'
}
```

::: warning

The sample Resource Id value provided is for reference only, **0x7f** cannot be used, the default is **0x64**.

In order to prevent the existence of multiple Xposed Modules in the current Host App, it is recommended to customize your own Resource Id.

:::

## Inject Module App's Resources

After the Host App is hooked, we can directly inject the `Context` obtained in the Hooker into the current Module App's Resources.

> The following example

```kotlin
injectMember {
    method {
        name = "onCreate"
        param(BundleClass)
    }
    afterHook {
        instance<Activity>().also {
            // <Scenario 1> Inject Module App's Resources through Context
            it.injectModuleAppResources()
            // <Scenario 2> Get the Host App's Resources directly and inject the Module App's Resources
            it.resources.injectModuleAppResources()
            // Use the Module App's Resource Id directly
            it.getString(R.id.app_name)
        }
    }
}
```

You can also inject current Module App's Resources directly in `AppLifecycle`.

> The following example

```kotlin
onAppLifecycle {
    onCreate {
        // Globally inject Module App's Resources, but only in the global lifecycle
        // Methods like ImageView.setImageResource need to be injected separately in Activity
        // <Scenario 1> Inject Module App's Resources through Context
        injectModuleAppResources()
        // <Scenario 2> Get the Host App's Resources directly and inject the Module App's Resources
        resources.injectModuleAppResources()
        // Use the Module App's Resource Id directly
        getString(R.id.app_name)
    }
}
```

::: tip

For more functions, please refer to the [Context+Resources.injectModuleAppResources](../public/com/highcapable/yukihookapi/hook/factory/YukiHookFactory#context-resources-injectmoduleappresources-ext-method) method.

:::

## Register Module App's Activity

When the `Activity` of all applications in the Android system starts, it needs to be registered in `AndroidManifest.xml`.

During the Hook process, if we want to directly start the unregistered `Activity` in the Module App through the Host App, what should we do?

After the Host App is hooked, we can directly register the `Activity` proxy of the current Module App in the `Context` obtained in the Hooker.

> The following example

```kotlin
injectMember {
    method {
        name = "onCreate"
        param(BundleClass)
    }
    afterHook {
        instance<Activity>().registerModuleAppActivities()
    }
}
```

You can also register the current Module App's `Activity` proxy directly in `AppLifecycle`.

> The following example

```kotlin
onAppLifecycle {
    onCreate {
        registerModuleAppActivities()
    }
}
```

If the `proxy` parameter is not filled in, the API will automatically obtain the current Host App's launching entry `Activity` for proxying according to the current `Context`.

Usually, it works, but the above situation will fail in some apps, for example, some `Activity` will add launching parameters to the registration list, so we need to use another solution.

If the unregistered `Activity` cannot be launched correctly, we can manually get the Host App's `AndroidManifest.xml` for analysis to get a registered `Activity` tag and get the `name`.

You need to choose an unneeded `Activity` that may not be used by the current Host App as a "puppet" to proxy it, which usually works.

For example, we have found a suitable `Activity` that can be proxied.

> The following example

```xml
<activity
    android:name="com.demo.test.activity.TestActivity"
    ...>
```

According to the `name`, we only need to add this parameter to the method for registration.

> The following example

```kotlin
registerModuleAppActivities(proxy = "com.demo.test.activity.TestActivity")
```

Alternatively, if you write a `stub` for the Host App's class, you can register it directly through the `Class` object.

> The following example

```kotlin
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`.

These `Activity` now live seamlessly in the Host App without registration.

> The following example

```kotlin
class HostTestActivity : ModuleAppActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        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)
    }
}
```

If you need to extends `ModuleAppCompatActivity`, you need to set the AppCompat theme manually.

> 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

    override fun onCreate(savedInstanceState: Bundle?) {
        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)
    }
}
```

After all the above steps are completed, you can happily call `startActivity` anywhere in the (Xposed) Host environment where a `Context` exists.

> The following example

```kotlin
val context: Context = ... // Assume this is your Context
context.startActivity(context, HostTestActivity::class.java)
```

::: tip

For more functions, please refer to the [Context.registerModuleAppActivities](../public/com/highcapable/yukihookapi/hook/factory/YukiHookFactory#context-registermoduleappactivities-ext-method) method.

:::

## Create ContextThemeWrapper Proxy

Sometimes, we need to use `MaterialAlertDialogBuilder` to beautify our own dialogs in the Host App, but we can't create them without the AppCompat theme.

- Will got the following exception

```:no-line-numbers
The style on this component requires your app theme to be Theme.AppCompat (or a descendant).
```

At this time, we want to use `MaterialAlertDialogBuilder` to create a dialog in the current `Activity` of the Host App being hooked, you can have the following methods.

> The following example

```kotlin
injectMember {
    method {
        name = "onCreate"
        param(BundleClass)
    }
    afterHook {
        // Use applyModuleTheme to create a theme resource in the current Module App
        val appCompatContext = instance<Activity>().applyModuleTheme(R.style.Theme_AppCompat)
        // Directly use this Context that wraps the Module App's theme to create a dialog
        MaterialAlertDialogBuilder(appCompatContext)
            .setTitle("AppCompat Theme Dialog")
            .setMessage("I am an AppCompat theme dialog displayed in the Host App.")
            .setPositiveButton("OK", null)
            .show()
    }
}
```

You can also set the system (native) night mode and day mode on the current `Context` through `uiMode`.

Which requires at least Android 10 and above system version support and the current theme contains night mode related elements.

> The following example

```kotlin
injectMember {
    method {
        name = "onCreate"
        param(BundleClass)
    }
    afterHook {
        // Define the theme resource in the current Module App
        var appCompatContext: ModuleContextThemeWrapper
        // <Scenario 1> Get the Configuration object directly to set
        appCompatContext = instance<Activity>()
            .applyModuleTheme(R.style.Theme_AppCompat)
            .applyConfiguration { uiMode = Configuration.UI_MODE_NIGHT_YES }
        // <Scenario 2> Create a new Configuration object
        // This solution will destroy the original font scaling and other settings in the current Host App
        // You need to manually re-pass parameters such as densityDpi
        appCompatContext = instance<Activity>().applyModuleTheme(
            theme = R.style.Theme_AppCompat,
            configuration = Configuration().apply { uiMode = Configuration.UI_MODE_NIGHT_YES }
        )
        // Directly use this Context that wraps the Module App's theme to create a dialog
        MaterialAlertDialogBuilder(appCompatContext)
            .setTitle("AppCompat Theme Dialog")
            .setMessage("I am an AppCompat theme dialog displayed in the Host App.")
            .setPositiveButton("OK", null)
            .show()
    }
}
```

This way, we can create dialogs in the Host App very simply using `MaterialAlertDialogBuilder`.

::: warning Possible Problems

Because some **androidx** dependent libraries or custom themes used by some apps may interfere with the actual style of the current **MaterialAlertDialog**, such as the button style of the dialog.

You can refer to the **Module App Demo** in this case and see [here is the sample code](https://github.com/fankes/YukiHookAPI/tree/master/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/factory/ComponentCompatFactory.kt) to fix this problem.

**ClassCastException** may occur when some apps are created, please manually specify a new **Configuration** instance to fix.

:::

::: tip

For more functions, please refer to the [Context.applyModuleTheme](../public/com/highcapable/yukihookapi/hook/factory/YukiHookFactory#context-applymoduletheme-ext-method) method.

:::