10

I implemented the DayNight theme in my app and added a setting to switch between day and night mode, but I'm not able to switch between modes dynamically without a restart.

If I use setDefaultNightMode() after the setting has been changed, the settings activity doesn't change mode, but the activities in the backstack do. If I additionally use setLocalNightMode() the settings activity gets recreated and changes its mode, but now the activities in the backstack don't. I could not find a way to accomplish both. Is there a way to do this?

Alex P.
  • 30,437
  • 17
  • 118
  • 169
Özgür
  • 121
  • 1
  • 6

2 Answers2

6

Here is the implementation in the MainActivity.java module of the CheeseSquare repo located here:

private void setNightMode(@AppCompatDelegate.NightMode int nightMode) {
    AppCompatDelegate.setDefaultNightMode(nightMode);

    if (Build.VERSION.SDK_INT >= 11) {
        recreate();
    }
}

Here is the description of recreate() as of V25. I can't seem to find other documentation for this call - note that it was added at V11.

/* Cause this Activity to be recreated with a new instance.  This results
 * in essentially the same flow as when the Activity is created due to
 * a configuration change -- the current instance will go through its
 * lifecycle to {@link #onDestroy} and a new instance then created after it.
 */
Jim Andreas
  • 1,557
  • 1
  • 17
  • 29
  • 2
    It works if you do this in the MainActivity, but if you do this in another activity with the MainActivity in the backstack the problem remains. – Özgür May 24 '17 at 20:16
  • What works for me is to "turn off" the backstack by turning off history in the manifest file. Add `android:noHistory="true"` to your activities as described here: https://stackoverflow.com/questions/5794506/android-clear-the-back-stack/6512788#6512788 – Jim Andreas May 25 '17 at 05:48
5

I got it to work, here is my code and screen record. Activities in the backstack are also changing their themes.

findPreference(getString(R.string.pref_key_night_mode)).setOnPreferenceChangeListener(new OnPreferenceChangeListener() {

        @Override
        public boolean onPreferenceChange(Preference preference, Object newValue) {
            if ((Boolean) newValue) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
                getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
            } else {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
                getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
            }
            getDelegate().applyDayNight();
            recreate();
            return true;
        }

    });

record

Update

The solution above worked for Android 4.4 well, but preserved previous DayNight state in backstack on Android 7.1.

I've added manual check for night mode setting change in onResume:

@Override
protected void onResume() {
    super.onResume();
    if (mApplyNightMode) {
        mApplyNightMode = false;
        getDelegate().setLocalNightMode(PeshkaPreferences.getNightModeEnabled(this) ? AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
        getDelegate().applyDayNight();
        recreate();
    }
}

and added OnSharedPreferencesChangeListener:

protected OnSharedPreferenceChangeListener mPreferencesListener = new OnSharedPreferenceChangeListener() {

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (key.equals(getString(R.string.pref_key_night_mode))) {
            mApplyNightMode = true;
        }
    }
};
Artem Mostyaev
  • 3,874
  • 10
  • 53
  • 60