Android: “reset to default” preference dialog
Posted by Dimitri | Aug 26th, 2012 | Filed under Featured, Programming
This post explains how to create a DialogPreference that allows the user to reset all preference values to their defaults. This post has been created and tested in both the emulator and on a real device running Android 2.1. Therefore, some method calls used in this example may be deprecated on newer Android versions.
The best way to show how to reset all the preference values with a DialogPreference is by presenting an example featuring PreferenceActivity that sets some preferences, and them show how to revert them back to their default. This example project can be downloaded at the end of the post. So, the first thing needed for this example is a XML file that defines the contents of the preference screen:
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <ListPreference android:defaultValue="0" android:entries="@array/array1" android:entryValues="@array/array1values" android:key="listPreference1" android:persistent="true" android:summary="The first list preference" android:title="List Preference 1" > </ListPreference> <ListPreference android:defaultValue="1" android:entries="@array/array2" android:entryValues="@array/array2values" android:key="listPreference2" android:persistent="true" android:summary="The second list preference" android:title="List Preference 2" > </ListPreference> <CheckBoxPreference android:defaultValue="true" android:key="checkboxPreference1" android:persistent="true" android:summary="A check box preference. The default is to TRUE." android:title="Check Box preference" > </CheckBoxPreference> <fortyonepost.com.resetpreferencesdialog.ResetDefDiagPref android:dialogMessage="Revert all values to their defaults?" android:key="resetDialog" android:negativeButtonText="No, KEEP current values" android:persistent="false" android:positiveButtonText="Yes, RESET all values" android:summary="Reset all settings to their initial values." android:title="Reset to default" > </fortyonepost.com.resetpreferencesdialog.ResetDefDiagPref> </PreferenceScreen>
Note that, a default value for each Preference is being defined using the android:defaultValue attribute (as seen at lines 5, 15 and 25). Except for the ResetDefDiagPref (that will be explained later), all preferences must have a default value. The second thing that must be assigned to all the preferences is the persistent attribute set to true, except for the ResetDefDiagPref preference (lines 9, 19 and 27). That way, all preferences will store their values at the application’s SharedPreferences, except for the ResetDefDiagPref preference, since it’s value doesn’t need to be saved (line 36).
Also, all preferences must define a key (lines 8, 18, 26 and 34) in order to make the default values work. Additionally, all of them have been given a title and summary, although not not required to make the example work, it has been done for the sake of completion (lines 10, 11, 20, 21, 28, 29, 38 and 39). On top of that, since ResetDefDiagPref is a PreferenceDialog two more attributes have been defined: android:positiveButtonText and android:negativeButtonText. By doing so, two buttons are going to be rendered at the bottom of the Dialog containing the text defined in those two attributes. This is essential to make the ResetDefDiagPref class work.
The last observation worth mentioning about the above XML is that the entries and their respective entry values that populate both ListPreferences are being defined at another XML file, which, to make this post shorter, will be omitted but you can find a copy at the sample project available for download at the bottom of the page. So this is how the preference screen defined at this XML looks like:
The ListPreference and the CheckBoxPreference don’t require any details, it’s standard in many applications. However, the ResetDefDiagPref needs to be further explained. Here’s the class definition:
package fortyonepost.com.resetpreferencesdialog; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.preference.DialogPreference; import android.preference.PreferenceManager; import android.util.AttributeSet; public class ResetDefDiagPref extends DialogPreference { //An object that will store the Activity's context protected Context context; public ResetDefDiagPref(Context context, AttributeSet attrs) { super(context, attrs); //Store the calling Activity's context this.context = context; } @Override public void onClick(DialogInterface dialog, int which) { super.onClick(dialog, which); //If the 'positive' button has been pressed if(which == DialogInterface.BUTTON_POSITIVE) { //Get this application SharedPreferences editor SharedPreferences.Editor preferencesEditor = PreferenceManager.getDefaultSharedPreferences(this.context).edit(); //Clear all the saved preference values. preferencesEditor.clear(); //Read the default values and set them as the current values. PreferenceManager.setDefaultValues(context, R.layout.activity_prefs, true); //Commit all changes. preferencesEditor.commit(); //Call this method to trigger the execution of the setOnPreferenceChangeListener() method at the PrefsActivity getOnPreferenceChangeListener().onPreferenceChange(this, true); } } }
The above class inherits from the DialogPrefrence, an abstract class, and has only one single member variable: a Context object (line 13). It will later store a reference to the current context. The constructor that is being instantiated is the one that takes a Context and a AttributeSet as parameters, that way, an object from this class can be inflated from a XML file. At the constructor, the reference to the current Context is stored (line 20).
This class also overrides the onClick() method that is called every time a button at the DialogPreference is pressed. The code that replaces the current values by their defaults goes inside this method. Right at the start, the superclass implementation of the onClick() method is called, so that the button press is handled (line 26). Since the dialog prompts the user if the preferences should be set back to the initial settings, the values should only reset when the “positive” button of this dialog is pressed, in the case of this example the button on the left of this screenshot:
For that reason there’s a if statement checking for a positive button press (line 29). That being the case, the current Editor from the current SharedPrefence is obtained and stored at a variable named preferencesEditor (line 32). With that, all the preferences can be edited, so all their stored values are being deleted (line 34) and all the default values are being loaded again and stored at their respective preferences (line 36). The changes made to the preferences are saved (line 38). Leaving the code at this point as it is, from lines 32 through 38 would be enough to reset the preferences, however the user wouldn’t see then being updated at the Preference Screen, unless the Activity is closed and started again.
To automate this process, line 41 causes the method onPreferenceChange() of all OnPreferenceChangeListeners that may be watching for a preference change to be executed. Conveniently, a OnPreferenceChangeListener is defined at the class that inherits from PreferenceActivity which has the following declaration:
package fortyonepost.com.resetpreferencesdialog; import android.content.Intent; import android.os.Bundle; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; public class PrefsActivity extends PreferenceActivity { //A handle to the Preference that resets all values to their defaults. private Preference resetDialogPreference; //An intent object, that holds the intent that started this Activity. private Intent startIntent; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Inflate the preference screen entries from the 'activity_prefs.xml' file. addPreferencesFromResource(R.layout.activity_prefs); /* Set the default values by reading the "android:defaultValue" attributes from each preference at the 'activity_prefs.xml' file. */ PreferenceManager.setDefaultValues(this, R.layout.activity_prefs, false); //Initialize the preference object by obtaining a handle to the ResetDefDiagPref object as a Preference this.resetDialogPreference = getPreferenceScreen().findPreference("resetDialog"); //Store the Intent that started this Activity at this.startIntent. this.startIntent = getIntent(); //Set the OnPreferenceChangeListener for the resetDialogPreference this.resetDialogPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { //Both enter and exit animations are set to zero, so no transition animation is applied overridePendingTransition(0, 0); //Call this line, just to make sure that the system doesn't apply an animation startIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); //Close this Activity finish(); //Again, don't set an animation for the transition overridePendingTransition(0, 0); //Start the activity by calling the Intent that have started this same Activity startActivity(startIntent); //Return false, so that nothing happens to the preference values return false; } }); } }
What happens is that, line 41 from ResetDefDiagPref definition triggers the execution of lines 37 through 51 of the above PreferenceActivity, because a OnPreferenceChangeListener has been set in this Activity to watch for value changes at the ResetDefDiagPref dialog. This code basically cancels any enter and exit animations when starting or finishing this PreferenceActivity (lines 40, 42, 46 and 48), and as previously stated, closes and starts this same PreferenceActivity by calling the finish() and startActivity() methods respectively (lines 44 and 48), so now, not only the preference values are reset to their defaults, but the user doesn’t have to manually close and start the PreferenceActivity.
There are other lines of code worth mentioning in this PreferenceActivity, such as the line 31 that gets the Intent that started the Activity and will be used as a parameter at line 48. Also, a reference to the ResetDefDialogPref is being obtained at line 28: without it, the OnPreferenceChangeListener at line 34 couldn’t be set. Finally, line 22 inflates the PreferenceScreen with the preferences defined at the ‘activity_prefs.xml‘ file. Maybe a little more important as the previous ones, line 25 sets the default value of each preference defined at the ‘activity_prefs.xml‘ file, by reading each preference android:defaultValue attribute.
That’s it!
Final Thoughts
As explained, the above code resets the preferences to their default values and causes the current PreferenceActivity to be restarted, so that the updated preference values can be properly displayed. There should be a way to invalidate the root View causing the preference screen to be redrawn with the updated values, but I couldn’t find an easy and straight forward way to do it, at least not while targeting Android 2.1 . Sometimes, depending on the application, it may make more sense to just finish the PreferenceActivity instead of restarting it.
This can be implemented as any type of Preference, it doesn’t have to be a PreferenceDialog however, this prevents the user from accidentally resetting the preferences.
Downloads
Be the first to leave a comment!