Search code examples
androidandroid-theme

Trouble with DayNight mode status bar/controls inconsistently themed after recreate


I'm having quite a bit of trouble getting the DayNight theme to play nicely after a preference change and activity.recreate() call. Depending on the value (and which SDK I'm on), I get inconsistent theming (icons are dark on dark or light on light).

In my PreferenceFragment, I have a preference that allows the user to set one of three values: Light, Dark, or Auto, which correspond with AppCompatDelegates MODE_NIGHT_NO, MODE_NIGHT_YES, or MODE_NIGHT_AUTO, respectively. Here's what the implementation looks like:

PreferenceFragment.kt

override fun onPreferenceChange(preference: Preference, value: Any): Boolean {
    setSummary(preference, value.toString())

    return when (preference.key) {
        themePreference.key -> consume {
            AppCompatDelegate.setDefaultNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt())
            activity?.recreate()
        }
    }
}

BaseActivity.kt

 override fun onCreate(savedInstanceState: Bundle?) {
    AppCompatDelegate.setDefaultNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt())

    super.onCreate(savedInstanceState)
}

I've also tried combinations of:

delegate.setLocalNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt())

and

AppCompatDelegate.setDefaultNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt()

peppered into different places in the activity/application lifecycle with no success.

What I'm seeing mostly is dark icons on a dark status bar, and inconsistencies on pre-P app switcher (the theme is Night, but the app switcher toolbar shows up as a light theme). I don't appear to have issues with the theme of my own app controls, like tabs or text - it's mainly on the android system views like the status bar and app switcher toolbar).

If I kill the app and re-launch, I have no issues whatsoever. It's only after an activity.recreate call do I see these issues.


Solution

  • After searching for a simple solution for a long while, I found an attribute that conveniently handles this scenario. I'm not sure how proper this approach is, but from my testing it works as expected.

    Given that you're using Theme.MaterialComponents.DayNight or some similar variant in your style XML, you can use ?attr/isLightTheme as a true/false flag.

    <item name="android:windowLightStatusBar">?attr/isLightTheme</item>
    

    The value returned by ?attr/isLightTheme is handled by the system. You can check the current value with:

    AppCompatDelegate.getDefaultNightMode();
    
    // or
    
    Configuration configuration = getResources().getConfiguration();
    int currentNightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;