Saving preferences in your Android application

This is the third post in my series about saving data in Android. The other posts can be found here :

https://cindypotvin.com/introduction-how-to-save-data-in-your-android-application/
https://cindypotvin.com/saving-data-to-a-file-in-your-android-application/

A preference is a type of data that needs to be saved by most applications. Preferences allow users to change how the application works by giving choices about things like the layout, the features to enable and the measurement units to use to display data. Default preferences should be good enough for most users, but you may need to offer a choice for more advanced users.

If you must save preferences in your application, the Android SDK includes the Preference APIs made specifically to store this kind of data. The preferences are saved by the android.content.SharedPreferences class to an XML file that contains pairs of key-value; the values can be booleans, floats, ints, longs or strings. If you application is uninstalled, all the preferences are also removed since the file is saved to the internal storage of the application. The values for the preferences are saved in clear text, so you should encrypt your data if you want to store sensitive information like credentials.

You can save values directly to the preferences file without user action using the android.content.SharedPreferences class, but in general users will set their preferences from a Settings window that can be accessed from the action bar of your application.  To create the Settings window, the Preference APIs includes a android.preference.PreferenceFragment to add to your own activity. This fragment shows a list of preferences and saves the values selected by the user automatically using the android.content.SharedPreferences class.

Before using the android.preference.PreferenceFragment, you need to define which preferences will be available from the fragment with a configuration file that is saved to the /res/xml/ folder. Basic preferences types are available from the Android SDK, but you can also create your own custom preference type by overriding the android.preference.Preference class or one of its sub classes. Here is an example of a preferences.xml file containing a android.preference.CheckboxPreference, a android.preference.ListPreference and an android.preference.EditTextPreference :

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<EditTextPreference
   android:key="welcome_text"
   android:title="@string/pref_title_welcome_text"
   android:summary="@string/pref_summary_welcome_text"
   android:defaultValue="@string/pref_default_welcome_text" />
<ListPreference
   android:key="welcome_text_color"
   android:title="@string/pref_title_welcome_text_color"
   android:summary="@string/pref_summary_welcome_text_color"
   android:defaultValue="@string/pref_default_welcome_text_color"
   android:entries="@array/colorLabelsArray"
   android:entryValues="@array/colorValuesArray" />
<CheckBoxPreference
   android:defaultValue="true"
   android:key="show_welcome_text"
   android:title="@string/pref_title_show_welcome_text"
   android:summary="@string/pref_summary_show_welcome_text" />
</PreferenceScreen>

After that, you must use the android.preference.PreferenceFragment to create your own fragment and specify the configuration to use. Your own fragment can be extended later on if you need to add custom behaviours by implementing the android.content.SharedPreferences.OnSharedPreferenceChangeListener that is triggered when a preference is modified.

public class SettingsFragment extends PreferenceFragment {
   @Override
   public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);

   // Load the preferences as configured in the /res/xml/preferences.xml file
   // and displays them.
   // The preferences will be automatically saved.
   addPreferencesFromResource(R.xml.preferences);
   }
}

Finally, to be able to manage your preferences, you must create the activity that hosts the fragment you just defined, which can then be started from another activity by the user.

public class SettingsActivity extends Activity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);

   // Display the preferences fragment as the content of the activity
   getFragmentManager().beginTransaction()
                       .replace(android.R.id.content, new SettingsFragment()).commit();
   }
}

This is enough to save the preferences to a file automatically, but to do something interesting with the preferences saved you must retrieve them in your other activities. To get preferences values, you can use the android.content.SharedPreferences class that is returned by the android.preference.PreferenceManager for the current context. To complete the previous example, the following code gets the SharedPreferences object during the onResume event of the activity and modifies the UI according to the current preferences. The onResume event is called when the activity starts and when the user comes back from another activity, in that case the SettingsActivity we created to manage the preferences.

public class MainActivity extends Activity {
   @Override
   public void onResume() {
      super.onResume();
      SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);

      TextView welcomeTextView = (TextView) findViewById(R.id.hello_world_textview);

      String defaultWelcomeText = getResources().getString(R.string.pref_default_welcome_text);
      String welcomeText = preferences.getString("welcome_text", defaultWelcomeText);
      welcomeTextView.setText(welcomeText);

      String defaultWelcomeTextColor = getResources()
                                          .getString(R.string.pref_default_welcome_text_color);
      String welcomeTextColor = preferences.getString("welcome_text_color",
                                                      defaultWelcomeTextColor);
      welcomeTextView.setTextColor(Color.parseColor(welcomeTextColor));

      boolean showWelcomeText = preferences.getBoolean("show_welcome_text", 
                                                       true /*showWelcomeText*/);
      if (showWelcomeText)
         welcomeTextView.setVisibility(View.VISIBLE);
      else
         welcomeTextView.setVisibility(View.INVISIBLE);
     }
}

For the complete example that can be executed, see the following GitHub project : http://github.com/CindyPotvin/androidpreferences