androidandroid-contextdarkmodeandroid-dark-theme

Changing between Light/Dark modes doesn't update colors of running app. Theme is applied only after restarting app


Run into strange behaviour when trying to add support for dark theme in existing app.

I added values-night folder where I added new colors.xml with different hex for dark mode. Then I extended from Theme.MaterialComponents.DayNight.NoActionBar in AppTheme.

When running the app it takes correct colors depending on the theme already set in the phone. But then when I change the theme and come back to app colors don't change. From the logs I can see that activity and fragment are recreated, so this part is working as described in the documentation.

If I add uiMode to configChanges in manifest, then activity is not recreated, instead I get a callback in onConfigurationChanged(newConfig: Configuration). But calling this function

resources.configuration.uiMode.and(Configuration.UI_MODE_NIGHT_MASK)

always returns same value no matter which theme I have set the phone to.

If I restart the app, the current theme of the phone is applied correctly. So it feels like the app is stuck with initial theme (context) that it received upon startup and then never gets updated.

I have Dagger2 setup in my app to inject dependencies and one of the Singletons is applicationContext. I removed @Singleton tag from it but it didn't help.

I would appreciate any help in troubleshooting this problem. Could anyone hint where to debug?


Solution

  • It turns out that the problem was in overriding attachBaseContext method inside of Application class.

    My app needs to be locked to certain Locale, regardless of phone language settings. And to achieve that I created helper function that would accept current context and create a new context by changing locale in configurations. This wrapper function was called in attachBaseContext method of Application class and BaseActivity.

    // in both Application class and BaseActivity
    override fun attachBaseContext(newBase: Context?) {
       super.attachBaseContext(LocaleHelper.checkDefaultLanguage(newBase))
    }
    
    // in LocaleHelper object
    fun checkDefaultLanguage(context: Context): Context {
        val locale = Locale.GERMANY
    
        Locale.setDefault(locale)
    
        val configuration = context.resources.configuration
        configuration.setLocale(locale)
        configuration.setLayoutDirection(locale)
    
        return context.createConfigurationContext(configuration)
    }
    

    Removing attachBaseContext method from Application class solved the issue. So now locale is changed only in activities and app seems to be working as before. I actually don't know why attachBaseContext was overriden in Application class in the first place. I assume that if getString would have been called from applicationContext.getstring(...) then there might have been some problems with original phone's locale being used, but I haven't tested that yet. All getString calls are using activity's context as of now.

    So the main take away for me was - Don't touch the application context! Hopefully, this solution helps in some way.