22 May 2012

Adding Preferences On-The-Fly

(Make appropriate whooshing sounds)

Today, I will show you how to add preferences to your SharedPreferences (confused yet?) programatically. In an app that I am doing, I wanted to present a ListPreference to the user from the SharedPreferences page. However, the values contained in this list is not known at compile time. (Think Facebook groups that differ per user, or Twitter lists, or things like such, and I want to set a default option.)

Anyway, here's what I did to make this work. I added a preference in my PreferenceScreen, a sort of placeholder, if you will. Here is my ListPreference:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

 <PreferenceCategory android:title="@string/pref_settings_header">
  <ListPreference android:key="@string/pref_key_default_group" android:title="@string/pref_default_group"/>
 </PreferenceCategory>

</PreferenceScreen>
And then in the class that handles the SharedPreferences, I get the values that I want displayed and plug them in to the placeholder preference. I will not bother to explain here, just heavily commenting the code:
public class UserPreferences extends PreferenceActivity {

 private static final String LOG_TAG = "UserPreferences";
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Load the preferences from an XML resource
  addPreferencesFromResource(R.xml.my_apps_prefs);
  
  // Manage the "Default Group" preference
  addDefaultGroupPrefs();  
 }
 
 
 private void addDefaultGroupPrefs() {
  // Get our "placeholder" preference. I prefer to use strings instead of hard-coding the preference key.
  // The value passed into findPreference is the value in the android:key parameter in the preference XML file.
  ListPreference groupsListPrefs = (ListPreference) findPreference(getString(R.string.pref_key_default_group));
  
  // Get your groups, this may come from a utility class or wherever
  // In this example, I have a custom object that has a "name" and an "id"
  List<CustomGroup> myGroups = getMyGroups();
  if (myGroups == null) {
   // Disable the preference if we don't have a list
   groupsListPrefs.setEnabled(false);
  } else {
   // Enable the preference if we have a list
   groupsListPrefs.setEnabled(true);

   // We split the list into names and IDs
   // We show the names in the list, and the values as the entries
   List<String> groupNames = new ArrayList<String>();
   List<String> groupIds = new ArrayList<String>();
   for (CustomGroup group : myGroups){
    groupIds.add(group.getId());
    groupNames.add(group.getName());
    
    // I guess you can add directly to a String[]
    // just make sure the order is preserved (name1=id1, name2=id2, etc)
   }
   
   // These should be user-readable strings
   groupsListPrefs.setEntries(groupNames.toArray(new String[groupNames.size()]));
   
   // These are the actual values to be saved in the XML file
   groupsListPrefs.setEntryValues(groupIds.toArray(new String[groupIds.size()]));
  }
 }
 
 private List<customgroup> getMyGroups(){
  // do work here, query server or db or whatever
  // return the list of groups
 }
}

And we're done. :)