4

I have a little problem. When I turn on dark mode in my app and then close my app completely in android, when I reopen it, it returns to light mode. I use AppCompatDelegate to do this. I have a settings fragment with a switch to turn on or off dark mode and it works well. I have a shared preferences on that fragment for the switch and it works. The only problem is that the rest of the application doesn't stay in dark mode after being completely closed the reopened after. Is there a way I can save and then restore the dark mode when I close the reopen it?

Here's my code for MainActivity:

package com.barzalou.lpapineau.test;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.Menu;
import com.barzalou.lpapineau.test.ui.CheckedChangeCallback;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.navigation.NavigationView;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

public class MainActivity extends AppCompatActivity implements CheckedChangeCallback {

    private AppBarConfiguration mAppBarConfiguration;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        mAppBarConfiguration = new AppBarConfiguration.Builder(
            R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow, R.id.nav_maps)
            .setDrawerLayout(drawer)
            .build();
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
        NavigationUI.setupWithNavController(navigationView, navController);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        return NavigationUI.navigateUp(navController, mAppBarConfiguration)
                || super.onSupportNavigateUp();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_exit) {
            finish();
            System.exit(0);
        }
        return false;
    }

    public void onCheckedChange(boolean isChecked) {
        if (isChecked) {
            AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
            Log.d("Dark Mode Switch State", "On");
        }
        else {
            AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
            Log.d("Dark Mode Switch State", "Off");
        }
    }

Here's my code for SettingsFragment:

package com.barzalou.lpapineau.test.ui.settings;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import com.barzalou.lpapineau.test.R;
import com.barzalou.lpapineau.test.ui.CheckedChangeCallback;
import android.content.Context;

import static android.content.Context.MODE_PRIVATE;

public class SettingsFragment extends Fragment {

    private SettingsViewModel settingsViewModel;
    private static Switch DarkMode;
    private boolean SwitchOnOff;
    private CheckedChangeCallback callback = null;

    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        settingsViewModel = ViewModelProviders.of(this).get(SettingsViewModel.class);
        View root = inflater.inflate(R.layout.fragment_settings, container, false);
        final TextView textView = root.findViewById(R.id.text_settings);
        settingsViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
                textView.setText(s);
            }
        });
        return root;
    }

    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        DarkMode = (Switch) getView().findViewById(R.id.DarkModeSwitch);

        DarkMode.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                callback.onCheckedChange(isChecked);
            }
        });
    }

    public void onAttach(final Activity activity) {
        super.onAttach(activity);
        if (activity instanceof CheckedChangeCallback) {
            this.callback = (CheckedChangeCallback) activity;
        }
    }

    public void onDetach() {
        super.onDetach();
        callback = null;
    }

    //Save and Restore Switch, Buttons, Textboxs, etc -----------------------
    @Override
    public void onStop() {
        super.onStop();
        try {
            saveData();
            Log.d("Data Save", "Data was saved");
        } catch (Exception e) {
            Log.d("Data Save", "Data could not be saved");
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        try {
            loadData();
            updateViews();
            Log.d("Data Restore", "Data was restored");
        } catch (Exception e) {
           Log.d("Data Restore", "Data was not able to get restored");
        }
    }
    //------------------------------------------------------------------------



    // Save data, load data and update views functions -----------------------
    public void saveData() {
        SharedPreferences sharedPreferences = getContext().getSharedPreferences("SharedPrefs", MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();

        // Add other Switches, Buttons, Texboxes, etc to save
        editor.putBoolean("SwitchState", DarkMode.isChecked());
        // Example: editor.putString("String1", textview.getText().toString());

        //---------------------------------------------------

        editor.apply();
    }

    public void loadData() {
        SharedPreferences sharedPreferences = getContext().getSharedPreferences("SharedPrefs", MODE_PRIVATE);
        SwitchOnOff = sharedPreferences.getBoolean("SwitchState", true); //Change true to false to make the switch on by default
    }

    public void updateViews() {
        DarkMode.setChecked(SwitchOnOff);
    }
    //------------------------------------------------------------------------
}
Celoufran
  • 93
  • 1
  • 9

3 Answers3

6

You should set it in Application's onCreate() method

class MyApp : Application() {

    @Override
    public void onCreate() {
        super.onCreate();
        boolean isNightMode = sharedPreferences.getBoolean("SwitchState", true);
        if (isNightMode) {
          AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
        } else {
       AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
     }
    }
}

Do not forget to add AndroidManifest

<application
    android:name=".MyApp"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme"
ysfcyln
  • 2,857
  • 6
  • 34
  • 61
  • The dark mode saves perfectly fine in the settings fragment. It's when I close the app and I reopen it that it doesn't save it for any of the pages except the settings fragment. Should I put that and modify it to work in the MainActivity then? – Celoufran Apr 19 '20 at 20:11
  • 2
    You should put it in the Application class so when you kill the app and reopen it, theme will change completely for all screen – ysfcyln Apr 19 '20 at 20:20
  • I'm not sure what you mean by "You shouldset it in Applications's onCreate() method". By that, do you mean the onCreate method of the MainActivity? – Celoufran Apr 19 '20 at 22:51
4

I found an easy and straight forward answer. I tested it multiple times to make sure. Just add the following variables in MainActivity:

int NightMode;
SharedPreferences sharedPreferences;
SharedPreferences.Editor editor;

After that, you'll want to add the following lines in MainActivity's OnCreate() method:

sharedPreferences = getSharedPreferences("SharedPrefs", MODE_PRIVATE);
NightMode = sharedPreferences.getInt("NightModeInt", 1);
AppCompatDelegate.setDefaultNightMode(NightMode);

And to finish it off, save all of your variables in your shared preferences using the OnSaveInstanceState method:

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);

    NightMode = AppCompatDelegate.getDefaultNightMode();

    sharedPreferences = getSharedPreferences("SharedPrefs", MODE_PRIVATE);
    editor = sharedPreferences.edit();

    editor.putInt("NightModeInt", NightMode);
    editor.apply();
}

That's all you need to do to save the dark mode state of your whole app even when the app is terminated or killed by the OS.

Celoufran
  • 93
  • 1
  • 9
0

(this code is in kotlin)

at the start of the application(any), paste this code in main activity / splash activity

private lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        sharedPreferences = getSharedPreferences(getString(R.string.app_name), MODE_PRIVATE)
        when (sharedPreferences.getInt("night_mode", 2)) {
            0 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
            1 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
            else -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
        }
}

onclick dialogue box initialization done with using below code using shared preferences for changing theme.

private fun themeDialog() {
        val items = arrayOf("Light", "Dark", "Auto (System Default)")
        var checkedItem = sharedPreferences.getInt("night_mode", 2)

        MaterialAlertDialogBuilder(this)
            .setTitle("Theme")
            .setPositiveButton("Ok") { dialog, which ->
                when (checkedItem) {
                    0 ->{
                        sharedPreferences.edit().putInt("night_mode", 0).apply()
                        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)}
                    1 -> {
                        sharedPreferences.edit().putInt("night_mode", 1).apply()
                        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)}
                    else -> {
                        sharedPreferences.edit().putInt("night_mode", 2).apply()
                        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
                    }
                }
            }
            .setSingleChoiceItems(items, checkedItem) { dialog, which ->
                checkedItem = which
            }
            .setCancelable(false)
            .show()
    }

used a button to call the themeDialog()

findViewById<Button>(R.id.themeChangeBtn).setOnClickListener {
         themeDialog()
}
Meet Bhavsar
  • 442
  • 6
  • 12