1

I've implemented a theme whose parent is Theme.AppCompat.DayNight and assigned it to my activity. If my app is stopped and I change the system theme to dark mode then start my app, the dark theme works correctly. However, if I then turn system dark mode off whilst my app is running it does not switch themes until I restart the app.

I can see that other apps that support dark themes change immediately whilst running as soon as I press the system dark mode button.

What am I missing here?

Rich Browne
  • 313
  • 3
  • 11
  • See this: https://stackoverflow.com/questions/18301554/android-change-app-theme-on-onclick – null_override Aug 22 '20 at 22:27
  • Are you overriding configuration changes in your activities? This kind of thing in the manifest: ``android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"`` – cactustictacs Aug 23 '20 at 01:31
  • @null_override are you saying this has to be done by code? I would have thought android would have handled it as long as your theme inherits from DayNight surely? – Rich Browne Aug 23 '20 at 09:30
  • @cactustictacs yes I am! I removed the attribute to see if it made a difference, now when I switch to dark mode in system prefs my app disappears, when I switch tasks back to it the activity reloads and appears in dark mode. Any idea why it doesn't stay in the foreground and switch like the other apps? – Rich Browne Aug 23 '20 at 09:38
  • @cactustictacs for info the configChanges value is "orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" – Rich Browne Aug 23 '20 at 09:51

1 Answers1

1

Ok, so the way it works is the DayNight theme automatically switches between themes, either when you set a different one with AppCompatDelegate.setDefaultNightMode() or (setLocalNightMode) or (I'm assuming) when you're following the System setting, and the system sends out a "ok use night mode now" message

The way it "switches" is by recreating Activities, using the appropriate variation on the theme. Here's one of the devs explaining it:

Activity recreations Both of the methods mentioned above will recreate your Activity if a Configuration change is required, so that the new theme can be applied. This is a good opportunity to test whether your Activity + Fragments save their instance state correctly.

The problem is, you're overriding those configuration changes instead of letting the system handle it. You're basically saying "don't worry, if the Activity needs recreating for any of these reasons, don't do it - I'll take care of it". But this is complicated, and it's recommended that you don't do that

Caution: Handling the configuration change yourself can make it much more difficult to use alternative resources, because the system does not automatically apply them for you. This technique should be considered a last resort when you must avoid restarts due to a configuration change and is not recommended for most applications.

So by including those overrides, you're blocking the system from applying the changes. It works when you restart the app because then it gets to (re)create the Activity and set it up with the right styling and resources.

I'm guessing removing the overrides causes problems because you've been relying on them to stop Activities being recreated, so you don't have the code in place to save state and rebuild everything. (This is the reason a lot of people add the overrides in the first place, it's a hack to make their lives easier, but it causes problems like this!)

If that's your issue you'll have to rework parts of your app to save and restore state when an Activity is destroyed, but it's better that it can handle this stuff anyway - avoids things randomly being blank when they come back from the background, etc.

cactustictacs
  • 17,935
  • 2
  • 14
  • 25
  • 1
    That's a fantastic explanation! you've given me a lot to chew over and I'll have to go away and think about the best way to proceed. Many thanks for your time! – Rich Browne Aug 23 '20 at 21:30
  • hey no problem! And yeah, hopefully it's not too complicated for you to get things working - it's mostly a case of saving data you need in ``onPause`` and restoring it in ``onResume``, or using ``onSaveInstanceState`` if you can stick it in a ``Bundle``. If the ``savedInstanceState`` Bundle in ``onCreate`` is null then it's a fresh start, otherwise you're recreating an Activity the system had to destroy, so you can use that to work out if you need to restore state (like grabbing existing fragments from a ``FragmentManager``). Going from portrait to landscape is a good way to test things! – cactustictacs Aug 23 '20 at 22:03