AlertDialog Context Dependency Injection Issue

Viewed 453

My application is written in Kotlin and I'm using Koin for injection.

I have a class which is used for displaying dialogs and that requires context to be passed in.

The file looks like this:

class SettingsDialogHelper(val resources: Resources, val context: Context) {
    private var settingsAboutDialog: AlertDialog? = null
    private var settingsPrivacyPolicyDialog: AlertDialog? = null

    fun showSettingsAboutDialog() {
        settingsAboutDialog = AlertDialog.Builder(context, R.style.Theme_MaterialComponents_Light_Dialog)
            .setTitle(resources.getString(R.string.about_dialog_title, resources.getString(R.string.app_name)))
            .setMessage(
                resources.getString(
                    R.string.about_dialog_message,
                    resources.getString(R.string.app_name),
                    resources.getString(R.string.company_name))
            )
            .setPositiveButton(R.string.ok, null)
            .show()
    }

    fun isSettingsAboutDialogVisible(): Boolean = settingsAboutDialog?.isShowing == true
}

My module file looks like this:

val appModule = module {

    single<Resources> { androidContext().resources }

    single { SettingsDialogHelper(get(), androidContext()) }
}

Finally, in MainActivity I am injecting the like this:

private val settingsDialogHelper: SettingsDialogHelper by inject()

When my code makes a call to display the dialog, the following crash occurs:

I: |    +-- 'android.content.res.Resources'
I: |    +-- 'android.content.Context'
I: \-- (*) Created
D: Shutting down VM

    android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
        at android.view.ViewRootImpl.setView(ViewRootImpl.java:1041)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:397)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:108)
        at android.app.Dialog.show(Dialog.java:340)
        at androidx.appcompat.app.AlertDialog$Builder.show(AlertDialog.java:1008)
        at com.myapp.apps.blog.main.ui.SettingsDialogHelper.showSettingsAboutDialog(SettingsDialogHelper.kt:23)
        at com.myapp.apps.blog.main.ui.MainActivity.onOptionsItemSelected(MainActivity.kt:44)

I'm thinking the issue is to do with the fact that the AlertDialog.Builder would normally require an activity rather than context and I'd like to not live in my fragment but can't quite see how to resolve this issue.

Any help is much appreciated

2 Answers

What you need is injecting parameters with koin.

In the koin module, your definition should look like this:

single { activityContext -> SettingsDialogHelper(get(), activityContext }

Then in the injecting class, you pass the parameter at runtime. In your case, in MainActivity you can inject with:

private val settingsDialogHelper: SettingsDialogHelper by inject { parametersOf(this) }

You can find more detailed information in koin documentation

Fragment

val dialogs: Dialogs by inject { parametersOf(requireActivity()) }

KoinModule

single<Dialogs>{activityContext -> DialogsImpl(activityContext.get())}
Related