Logo Icon Logo
A Crowd-sourced Cookbook on Writing Great Android® Apps
GitHub logo Twitter logo OReilly Book Cover Art

Checking the Consistency of Default Shared Preferences

Author: Federico Paolinelli
Published? true -- FormatLanguage: W

Problem:

Android provides a very easy way to setup default preferences by defining a PreferenceActivity and providing it a resource file. What is not clear is how to perform checks on preferences given by the user

Solution:

You can implement the

public void onSharedPreferenceChanged(SharedPreferences prefs, String key)

and perform the checks in its body. If the check fails you can restore a default value in the preference. You must be aware that even if the SharedPreferences will contain the right value, you won't see it displayed correctly. For this reason, you need to reload the preferences activity.

Discussion:

If you have a default preference activity that implements OnSharedPreferenceChangeListener


     public class MyPreferenceActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {

     public void onCreate(Bundle savedInstanceState) {	
          super.onCreate(savedInstanceState); 
          Context context = getApplicationContext(); 
          prefs = PreferenceManager.getDefaultSharedPreferences(context); 
          addPreferencesFromResource(R.xml.userprefs);
}

Your PreferenceActivity can implement the onSharedPreferenceChanged method.

This will be called after the change is committed, so every other change you perform will be permanent.

The idea is to check if you like the value, and otherwise put a default value / disable it.

To get the method notified, you have to register your activity as a valid listener. The better way is to register in onResume and unregister in onPause:

	@Override
	protected void onResume() {
		super.onResume();
	        prefs.registerOnSharedPreferenceChangeListener(this);
	}

        @Override
	protected void onPause() {		
		super.onPause();
		prefs.unregisterOnSharedPreferenceChangeListener(this);
	}

Now it's time to perform the consistency check. For example, if you have an option whose key is MY_OPTION_KEY,

public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 
     SharedPreferences.Editor prefEditor = prefs.edit();
		

     if(key.equals(MY_OPTION_KEY)){
          String optionValue = prefs.getString(MY_OPTION_KEY, "");
          if(dontLikeTheValue(optionValue)){
               prefEditor.putString(MY_OPTION_KEY, "Default value");
               prefEditor.commit();
               reload();
          }
     }
     return;
}

Of course in this way the user will be surprised and will not know why you refused his option. You can then show and error dialog and perform the reload action after the user confirms the dialog.

private void showErrorDialog(String errorString){
     String okButtonString = context.getString(R.string.ok_name); 
     AlertDialog.Builder ad = new AlertDialog.Builder(context); 
     ad.setTitle(context.getString(R.string.error_name)); 
     ad.setMessage(errorString); 
     ad.setPositiveButton(okButtonString,new OnClickListener() {
               public void onClick(DialogInterface dialog, int arg1) {
                    reload();
               } } );
     ad.show();
     return;    
}

In this way the iDontLikeTheValue if becomes:

     if(dontLikeTheValue(optionValue)){
          if(!GeneralUtils.isPhoneNumber(smsNumber)){
               showErrorDialog("I dont like the option");
               prefEditor.putString(MY_OPTION_KEY, "Default value");
               prefEditor.commit();
          }
     }

What's still missing is the reload() function, but it's pretty obvious. It relaunches the activity using the same intent that fired it

private void reload(){
          startActivity(getIntent()); 
          finish();
     }
Nicolai 2011-09-11 19:03:05.933 I think this approach is less than optimal. Better to capture the invalid input before it is even written to the SharedPreference by using Preference.OnPreferenceChangeListener instead. Something like this: Preference pref = findPreference(MY_OPTION_KEY); pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (dontLikeTheValue(newValue)) { return false; } else { return true; } } }); Then you probably don't need that iffy-looking reload either.
thmanthey 2010-07-22 13:06:25.396 Hi Federico, Shouldn't you call prefs.registerOnSharedPreferenceChangeListener(this); in your onCreate()-method? And the next question: Would a custom preference including the logic for checking validity be preferable? Greetings, Thomas Manthey