Android fundamentals 09.2: App settings

1. Welcome

Introduction

Apps often include settings that allow users to modify app features and behaviors. For example, some apps allow the user to set their home location, default units for measurements, and other settings that apply to the entire app. Users don't access settings frequently, because once a user changes a setting, such as a home location, they rarely need to go back and change it again.

Users expect to navigate to app settings by tapping Settings in side navigation, such as a navigation drawer as shown on the left side of the figure below, or in the options menu in the app bar, shown on the right side of the figure below.

Navigating to Settings from a Navigation Drawer (left) or Options Menu (right)

In the figure above:

  1. Settings in side navigation (a navigation drawer)
  2. Settings in the options menu of the app bar

In this practical you add a settings activity to an app. Users will be able to navigate to the app settings by tapping Settings, which will be located in the options menu in the app bar.

What you should already know

You should be able to:

  • Create an Android Studio project from a template and generate the main layout.
  • Run apps on the emulator or a connected device.
  • Create and edit UI elements using the layout editor and XML code.
  • Extract string resources and edit string values.
  • Access UI elements from your code using findViewById().
  • Handle a Button click.
  • Display a Toast message.
  • Add an Activity to an app.
  • Create an options menu in the app bar.
  • Add and edit the menu items in the options menu.
  • Use styles and themes in a project.
  • Use SharedPreferences.

What you'll learn

  • How to add a Fragment for managing settings.
  • How to create an XML resource file of settings with their attributes.
  • How to create navigation to the settings Activity.
  • How to set the default values of settings.
  • How to read the settings values changed by the user.
  • How to customize the Settings Activity template.

What you'll do

  • Create an app that includes Settings in the options menu.
  • Add a Settings option toggle switch.
  • Add code to set the default value for the setting, and access the setting value after it has changed.
  • Use and customize the Android Studio Settings Activity template.

2. App overview

Android Studio provides a shortcut for setting up an options menu with Settings. If you start an Android Studio project for a phone or tablet using the Basic Activity template, the new app includes Settings as shown below:

App created from the Basic Activity template

The template also includes a floating action button in the lower right corner of the screen with an envelope icon. You can ignore this button for this practical, as you won't be using it.

You'll start by creating an app named AppWithSettings using the Basic Activity template, and you'll add a settings Activity that provides one toggle switch setting that the user can turn on or off:

Tapping Settings to navigate to the Settings Activity

You will add code to read the setting and perform an action based on its value. For the sake of simplicity, the action will be to display a Toast message with the value of the setting.

In the second task, you will add the standard Settings Activity template provided by Android Studio to the DroidCafeOptionsUp app you created in a previous lesson.

The Settings Activity template is pre-populated with settings you can customize for an app, and provides a different layout for phones and tablets:

  • Phones: A main Settings screen with a header link for each group of settings, such as General for general settings, as shown below.

The options menu with Settings (left) and the main Settings screen (center) with General settings (right) on a phone

  • Tablets: A master/detail screen layout with a header link for each group on the left (master) side, and the group of settings on the right (detail) side, as shown in the figure below.

General Settings on a tablet in master/detail screens

To customize the template, you'll change the headers, setting titles, setting descriptions, and values for the settings.

The DroidCafeOptionsUp app was created in a previous lesson from the Basic Activity template, which provides an options menu in the app bar for placing the Settings option. You will customize the supplied Settings Activity template by changing a single setting's title, description, values, and default values. You will add code to read the setting's value after the user changes it, and display that value.

3. Task 1: Add a switch setting to an app

In this task, you do the following:

  • Create a new project based on the Basic Activity template, which provides an options menu.
  • Add a toggle switch ( SwitchPreference) with attributes in a preference XML file.
  • Add an activity for settings and a fragment for a specific setting. To maintain compatibility with AppCompatActivity, you use PreferenceFragmentCompat rather than PreferenceFragment. You also add the android.support.v7.preference library.
  • Connect the Settings item in the options menu to the settings activity.

1.1 Create the project and add the xml directory and resource file

  1. In Android Studio, create a new project with the following parameters:

Attribute

Value

Application Name

AppWithSettings

Company Name

android.example.com (or your own domain)

Project location

Path to your directory of projects

Phone and Tablet Minimum SDK

API15: Android 4.0.3 IceCreamSandwich

Template

Basic Activity

Activity Name

MainActivity

Layout Name

activity_main

Title

MainActivity

  1. Run the app, and tap the overflow icon in the app bar to see the options menu, as shown in the figure below. The only item in the options menu is Settings.

Tapping the options menu

  1. You need to create a new resource directory to hold the XML file containing the settings. Select the res directory in the Project > Android pane, and choose File > New > Android Resource Directory. The New Resource Directory dialog appears.
  2. In the Resource type drop-down menu, choose xml. The Directory name automatically changes to xml. Click OK.
  3. The xml folder appears in the Project > Android pane inside the res folder. Select xml and choose File > New > XML resource file (or right-click xml and choose New > XML resource file).
  4. Enter the name of the XML file, preferences, in the File name field, and click OK. The preferences.xml file appears inside the xml folder, and the layout editor appears, as shown in the figure below.

Layout editor for preferences.xml

In the figure above:

  1. The preferences.xml file inside the xml directory.
  2. The layout editor showing the preferences.xml contents.

1.2 Add the XML preference and attributes for the setting

  1. Drag a SwitchPreference from the Palette pane on the left side to the top of the layout, as shown in the figure below.

Dragging a SwitchPreference into the Layout

  1. Change the values in the Attributes pane on the right side of the layout editor as follows, and as shown in the figure below:
  • defaultValue: true
  • key: example_switch
  • title: Settings option
  • summary: Turn this option on or off

Dragging a SwitchPreference into the Layout

  1. Click the Text tab at the bottom of the layout editor to see the XML code:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <SwitchPreference
        android:defaultValue="true"
        android:key="example_switch"
        android:summary="Turn this option on or off"
        android:title="Settings option" />
</PreferenceScreen>
  1. Extract the string resources for the android:title and android:summary attribute values to @string/switch_title and @string/switch_summary.

The XML attributes for a preferences are:

  • android:defaultValue: The default value of the setting when the app starts for the first time.
  • android:title: The title of the setting. For a SwitchPreference, the title appears to the left of the toggle switch.
  • android:key: The key to use for storing the setting value. Each setting has a corresponding key-value pair that the system uses to save the setting in a default SharedPreferences file for your app's settings.
  • android:summary: The text summary appears underneath the setting.

1.3 Use SwitchPreferenceCompat

In order to use the PreferenceFragmentCompat version of PreferenceFragment, you must also use the android.support.v7 version of SwitchPreference (SwitchPreferenceCompat).

  1. In the Project > Android pane, open the build.gradle (Module: app) file in the Gradle Scripts folder, and add the following to the dependencies section:
implementation 'com.android.support:preference-v7:26.1.0'

The statement shown above adds the android.support.v7.preference library in order to use the PreferenceFragmentCompat version of PreferenceFragment.

  1. In the preferences.xml file in the xml folder, change <SwitchPreference in the code to <android.support.v7.preference.SwitchPreferenceCompat:
<android.support.v7.preference.SwitchPreferenceCompat
        android:defaultValue="true"
        android:key="example_switch"
        android:summary="@string/switch_summary"
        android:title="@string/switch_title" />

The SwitchPreferenceCompat line above may show a yellow light bulb icon with a warning, but you can ignore it for now.

  1. Open the styles.xml file in the values folder, and add the following preferenceTheme item to the AppTheme declaration:
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>

In order to use PreferenceFragmentCompat, you must also declare preferenceTheme with the PreferenceThemeOverlay style to the app theme.

1.4 Add an Activity for settings

In order to create a settings Activity that provides a UI for settings, add an Empty Activity to the app. Follow these steps:

  1. Select app at the top of the Project > Android pane, and choose New > Activity > Empty Activity.
  2. Name the Activity SettingsActivity. Uncheck the Generate Layout File option (you don't need one), and leave unchecked the Launcher Activity option.
  3. Leave the Backwards Compatibility (AppCompat) option checked. The Package name should already be set to com.example.android.projectname**.
  4. Click Finish.

1.5 Add a Fragment for a specific setting

A Fragment is like a modular section of an Activity—it has its own lifecycle and receives its own input events, and you can add or remove a Fragment while the Activity is running. You use a specialized Fragment subclass to display a list of settings. The best practice is to use a regular Activity that hosts a PreferenceFragment that displays the app settings. PreferenceFragment provides a more flexible architecture for your app, compared to using an Activity for the preferences.

You will use PreferenceFragmentCompat rather than PreferenceFragment in order to maintain compatibility with AppCompatActivity.

In this step you will add a blank Fragment for a group of similar settings (without a layout, factory methods, or interface callbacks) to the app, and extend PreferenceFragmentCompat.

Follow these steps:

  1. Select app again, and choose New > Fragment > Fragment (Blank).
  2. Name the fragment SettingsFragment. Uncheck the Create layout XML? option (you don't need one).
  3. Uncheck the options to include fragment factory methods and interface callbacks.
  4. The Target Source Set should be set to main.
  5. Click Finish. The result is the following class definition in SettingsFragment:
public class SettingsFragment extends Fragment {

    public SettingsFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, 
             ViewGroup container, Bundle savedInstanceState) {
        TextView textView = new TextView(getActivity());
        textView.setText(R.string.hello_blank_fragment);
        return textView;
    }
}
  1. Edit the class definition of SettingsFragment to extend PreferenceFragmentCompat:
public class SettingsFragment extends PreferenceFragmentCompat {

As you change the class definition so it matches the definition shown above, a red bulb appears in the left margin. Click the red bulb and choose Implement methods, and then choose onCreatePreferences. Android Studio creates the following onCreatePreferences() stub:

@Override
public void onCreatePreferences(Bundle 
                       savedInstanceState, String rootKey) {
}

In order to extend the Fragment, Android Studio adds the following import statement:

import android.support.v7.preference.PreferenceFragmentCompat;
  1. Delete the entire onCreateView() method in the fragment.

The reason why you are essentially replacing onCreateView() with onCreatePreferences() is because you will be adding this SettingsFragment to the existing SettingsActivity to display preferences, rather than showing a separate Fragment screen. Adding it to the existing Activity makes it easy to add or remove a Fragment while the Activity is running. The preference Fragment is rooted at the PreferenceScreen using rootKey.

You can safely delete the empty constructor from SettingsFragment as well, because the Fragment is not displayed by itself:

public SettingsFragment() {
        // Required empty public constructor
}
  1. You need to associate with this Fragment the preferences.xml settings resource you created in a previous step. Add to the newly created onCreatePreferences() stub a call to setPreferencesFromResource() passing the id of the XML file (R.xml.preferences) and the rootKey to identify the preference root in PreferenceScreen:
setPreferencesFromResource(R.xml.preferences, rootKey);

The onCreatePreferences() method should now look like this:

@Override
public void onCreatePreferences(Bundle 
                         savedInstanceState, String rootKey) {
    setPreferencesFromResource(R.xml.preferences, rootKey);
}

1.6 Display the Fragment in SettingsActivity

To display the Fragment in SettingsActivity, follow these steps:

  1. Open SettingsActivity.
  2. Add the following code to the end of the onCreate() method so that the Fragment is displayed as the main content:
getSupportFragmentManager().beginTransaction()
         .replace(android.R.id.content, new SettingsFragment())
         .commit();

The code above uses the typical pattern for adding a fragment to an activity so that the fragment appears as the main content of the activity:

The entire onCreate() method in SettingsActivity should now look like the following:

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getSupportFragmentManager().beginTransaction()
               .replace(android.R.id.content, new SettingsFragment())
               .commit();
}

1.7 Connect the Settings menu item to SettingsActivity

Use an Intent to launch SettingsActivity from MainActivity when the user selects Settings from the options menu.

  1. Open MainActivity and find the if block in the onOptionsItemSelected() method, which handles the tap on Settings in the options menu:
if (id == R.id.action_settings) {
   return true;
}
  1. Add an Intent to the if block to launch SettingsActivity:
if (id == R.id.action_settings) {
    Intent intent = new Intent(this, SettingsActivity.class);
    startActivity(intent);
    return true;
}
  1. To add the app bar Up navigation button to SettingsActivity, you need to edit its declaration in the AndroidManifest.xml file to define the SettingsActivity parent as MainActivity. Open AndroidManifest.xml and find the SettingsActivity declaration:
<activity android:name=".SettingsActivity"></activity>

Change the declaration to the following:

<activity android:name=".SettingsActivity"
            android:label="Settings"
            android:parentActivityName=".MainActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".MainActivity"/>
</activity>
  1. Run the app. Tap the overflow icon for the options menu, as shown on the left side of the figure below. Tap Settings to see the settings activity, as shown in the center of the figure below. Tap the Up button in the app bar of the settings activity, shown on the right side of the figure below, to return to the main activity.

Tapping Settings to navigate to the Settings Activity

1.8 Save the default values in shared preferences

Although the default value for the toggle switch setting has already been set in the android:defaultValue attribute (in Step 1.2 of this task), the app must save the default value in the SharedPreferences file for each setting when the user first opens the app. Follow these steps to set the default value for the toggle switch:

  1. Open MainActivity.
  2. Add the following to the end of the onCreate() method after the FloatingActionButton code:
android.support.v7.preference.PreferenceManager
            .setDefaultValues(this, R.xml.preferences, false);

The code above ensures that the settings are properly initialized with their default values. The PreferenceManager.setDefaultValues() method takes three arguments:

  • The app context, such as this.
  • The resource ID (preferences) for the XML resource file with one or more settings.
  • A boolean indicating whether the default values should be set more than once. When false, the system sets the default values only if this method has never been called. As long as you set this third argument to false, you can safely call this method every time MainActivity starts without overriding the user's saved settings values. However, if you set it to true, the method will override any previous values with the defaults.

1.9 Read the changed settings value from shared preferences

When the app starts, the MainActivity onCreate() method can read the setting values that have changed, and use the changed values rather than the default values.

Each setting is identified using a key-value pair. The Android system uses this key-value pair when saving or retrieving settings from a SharedPreferences file for your app. When the user changes a setting, the system updates the corresponding value in the SharedPreferences file. To use the value of the setting, the app can use the key to get the setting from the SharedPreferences file using PreferenceManager.getDefaultSharedPreferences().

Follow these steps to add that code:

  1. Open SettingsActivity and create a static String variable to hold the key for the value:
public static final String 
            KEY_PREF_EXAMPLE_SWITCH = "example_switch";
  1. Open MainActivity and add the following at end of the onCreate() method:
SharedPreferences sharedPref =   
           android.support.v7.preference.PreferenceManager
                      .getDefaultSharedPreferences(this);
Boolean switchPref = sharedPref.getBoolean
           (SettingsActivity.KEY_PREF_EXAMPLE_SWITCH, false);
Toast.makeText(this, switchPref.toString(), 
           Toast.LENGTH_SHORT).show();
  1. Run the app and Tap Settings to see the Settings Activity.
  2. Tap the setting to change the toggle from on to off, as shown on the left side of the figure below.
  3. Tap the Up button in the Settings Activity to return to MainActivity. The Toast message should appear in MainActivity with the value of the setting, as shown on the right side of the figure below.
  4. Repeat these steps to see the Toast message change as you change the setting.

Changing a setting and showing its new value

The code snippet shown above uses the following:

  • android.support.v7.preference.PreferenceManager.getDefaultSharedPreferences(this) to get the setting as a SharedPreferences object (sharedPref).
  • getBoolean() to get the Boolean value of the setting that uses the key (KEY_PREF_EXAMPLE_SWITCH defined in SettingsActivity) and assign it to switchPref. If there is no value for the key, the getBoolean() method sets the setting value (switchPref) to false. For other values such as strings, integers, or floating point numbers, you can use the getString(), getInt(), or getFloat() methods respectively.
  • Toast.makeText() and show() to display the value of the switchPref setting.

Whenever the MainActivity starts or restarts, the onCreate() method should read the setting values in order to use them in the app. The Toast.makeText() method would be replaced with a method that initializes the settings.

You now have a working Settings Activity in your app.

Task 1 solution code

Android Studio project: AppWithSettings

4. Task 2: Use the Settings Activity template

If you need to build several sub-screens of settings, and you want to take advantage of tablet-sized screens as well as maintain compatibility with older versions of Android for tablets, Android Studio provides a shortcut: the Settings Activity template.

In the previous task you learned how to use an empty settings Activity and a blank Fragment in order to add a setting to an app. Task 2 will now show you how to use the Settings Activity template supplied with Android Studio to:

  • Divide multiple settings into groups.
  • Customize the settings and their values.
  • Display a main Settings screen with a header link for each group of settings, such as General for general settings, as shown in the figure below.

Main Settings screen with headers (left) and General settings (right) on a phone

  • Display a master/detail screen layout with a header link for each group on the left (master) side, and the group of settings on the right (detail) side, as shown in the figure below.

General Settings on a tablet in master/detail Screens

In a previous practical you created an app called DroidCafeOptionsUp using the Basic Activity template, which provides an options menu in the app bar as shown below.

The options menu in the app bar

Legend for the figure above:

  1. App bar
  2. Options menu action icons
  3. Overflow button
  4. Options overflow menu

2.1 Explore the Settings Activity template

To include the Settings Activity template in an app project in Android Studio, follow these steps:

  1. Copy the DroidCafeOptionsUp project folder, and rename it to DroidCafeWithSettings. Run the app to make sure it runs properly.
  2. Select app at the top of the Project > Android pane, and choose New > Activity > Settings Activity.
  3. In the dialog that appears, accept the Activity Name (SettingsActivity is the suggested name) and the Title (Settings).
  4. Click the three dots at the end of the Hierarchical Parent menu, and click the Project tab in the Select Activity dialog that appears (refer to the figure below).
  5. Expand DroidCafeWithSettings > app > src > main > java > com.example.android.droidcafeinput and select MainActivity as the parent activity, as shown in the figure below. Click OK.

Choosing the hierarchical parent for the new Activity

You choose MainActivity as the parent so that the Up app bar button in the Settings Activity returns the user to the MainActivity. Choosing the parent Activity automatically updates the AndroidManifest.xml file to support Up button navigation.

  1. Click Finish.
  2. In the Project > Android pane, expand the app > res > xml folder to see the XML files created by the Settings Activity template.

XML files for the Settings Activity

You can open and then add to or customize the XML files for the settings you want:

  • pref_data_sync.xml: PreferenceScreen layout for "Data & sync" settings.
  • pref_general.xml: PreferenceScreen layout for "General" settings.
  • pref_headers.xml: Layout of headers for the Settings main screen.
  • pref_notification.xml: PreferenceScreen layout for "Notifications" settings.

The above XML layouts use various subclasses of the Preference class rather than View, and direct subclasses provide containers for layouts involving multiple settings. For example, PreferenceScreen represents a top-level Preference that is the root of a Preference hierarchy. The above files use PreferenceScreen at the top of each screen of settings. Other Preference subclasses for settings provide the appropriate UI for users to change the setting. For example:

The Settings Activity template also creates:

  • SettingsActivity in the java/com.example.android.projectname folder, which you can use as-is. This is the activity that displays the settings. SettingsActivity extends AppCompatPreferenceActivity for maintaining compatibility with older versions of Android.
  • AppCompatPreferenceActivity in the java/com.example.android.projectname folder, which you use as is. This Activity is a helper class that SettingsActivity uses to maintain backward compatibility with previous versions of Android.

2.2 Add the Settings menu item and connect it to the activity

As you learned in another practical, you can edit the menu_main.xml file for the options menu to add or remove menu items.

  1. Expand the res folder in the Project > Android pane, and open menu_main.xml file. Click the Text tab to show the XML code.
  2. Add another menu item called Settings with the new resource id action_settings:
<item
    android:id="@+id/action_settings"
    android:orderInCategory="50"
    android:title="Settings"
    app:showAsAction="never" />

You specify "never" for the app:showAsAction attribute so that Settings appears only in the overflow options menu and not in the app bar itself, because it should not be used often. You specify "50" for the android:orderInCategory attribute so that Settings appears below Favorites (set to "30") but above Contact (set to "100").

  1. Extract the string resource for "Settings" in the android:title attribute to the resource name settings.
  2. Open MainActivity, and find the switch case block in the onOptionsItemSelected() method which handles the tap on items in the options menu. Shown below is a snippet of that method showing the first case (for action_order):
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_order:
            Intent intent = new Intent(MainActivity.this, 
                            OrderActivity.class);
            intent.putExtra(EXTRA_MESSAGE, mOrderMessage);
            startActivity(intent);
            return true;
        case R.id.action_status:
            // Code for action_status and other cases...
        }

    return super.onOptionsItemSelected(item);
}
  1. Note in the above code that the first case uses an Intent to launch OrderActivity. Add a new case for action_settings to the switch case block with similar Intent code to launch SettingsActivity (but without the intent.putExtra):
case R.id.action_settings:
    Intent settingsIntent = new Intent(this, 
                            SettingsActivity.class);
    startActivity(settingsIntent);
    return true;
  1. Run the app using a phone or emulator so that you can see how the Settings Activity template handles the phone screen size.
  2. Tap the overflow icon for the options menu, and tap Settings to see the Settings Activity, as shown on the left side of the figure below.
  3. Tap each setting header (General, Notifications, and Data & sync), as shown in the center of the figure below, to see the group of settings on each child screen of the Settings screen, shown on the right side of the figure below.
  4. Tap the Up button in the Settings Activity to return to MainActivity.

The options menu with Settings (left), the Settings Activity main screen (center), and General settings (right)

You use the Settings Activity template code as-is. It not only provides layouts for phone-sized and tablet-sized screens, but also provides the function of listening to a settings change, and changing the summary to reflect the settings change. For example, if you change the "Add friends to messages" setting (the choices are Always, When possible, or Never), the choice you make appears in the summary underneath the setting:

The setting summary changes with the new value

In general, you need not change the Settings Activity template code in order to customize the Activity for the settings you want in your app. You can customize the settings titles, summaries, possible values, and default values without changing the template code, and even add more settings to the groups that are provided.

2.3 Customize the settings provided by the template

To customize the settings provided by the Settings Activity template, edit the string and string array resources in the strings.xml file and the layout attributes for each setting in the files in the xml directory.

In this step you will change the "Data & sync" settings.

  1. Expand the res > values folder and open the strings.xml file. Scroll the contents to the <!-- Example settings for Data & Sync --> comment:
<!-- Example settings for Data & Sync -->
    <string name="pref_header_data_sync">Data &amp; sync</string>

    <string name="pref_title_sync_frequency">Sync frequency</string>
    <string-array name="pref_sync_frequency_titles">
        <item>15 minutes</item>
        <item>30 minutes</item>
        <item>1 hour</item>
        <item>3 hours</item>
        <item>6 hours</item>
        <item>Never</item>
    </string-array>
    <string-array name="pref_sync_frequency_values">
        <item>15</item>
        <item>30</item>
        <item>60</item>
        <item>180</item>
        <item>360</item>
        <item>-1</item>
    </string-array>

    <string-array name="list_preference_entries">
        <item>Entry 1</item>
        <item>Entry 2</item>
        <item>Entry 3</item>
    </string-array>

    <string-array name="list_preference_entry_values">
        <item>1</item>
        <item>2</item>
        <item>3</item>
    </string-array>

    <string-array name="multi_select_list_preference_default_value" />

    <string name="pref_title_system_sync_settings">System sync settings</string>
  1. Edit the pref_header_data_sync string resource, which is set to Data & sync (the & is HTML code for an ampersand). Change the value to Account (without quotation marks).
  2. You should now refactor the resource name (the app will still work without refactoring the name, but refactoring makes the code easier to understand). Right-click (or Control-click) the pref_header_data_sync resource name choose Refactor > Rename. Change the name to pref_header_account, click the option to search in comments and strings, and click Refactor.
  3. You should also refactor the XML file name (the app will still work without refactoring the name, but refactoring makes the code easier to understand). Right-click (or Control-click) the pref_data_sync resource name in the Project > Android pane, and choose Refactor > Rename. Change the name to pref_account, click the option to search in comments and strings, and click Refactor.
  4. Edit the pref_title_sync_frequency string resource (which is set to Sync frequency) to Market.
  5. Refactor > Rename the pref_title_sync_frequency resource name to pref_title_account as you did previously.
  6. Refactor > Rename the string array resource name pref_sync_frequency_titles to pref_market_titles.
  7. Change each value in the pref_market_titles string array (15 minutes, 30 minutes, 1 hour, etc.) to be the titles of markets, such as United States, Canada, etc., rather than frequencies:
<string-array name="pref_market_titles">
        <item>United States</item>
        <item>Canada</item>
        <item>United Kingdom</item>
        <item>India</item>
        <item>Japan</item>
        <item>Other</item>
</string-array>
  1. Refactor > Rename the string array resource name pref_sync_frequency_values to pref_market_values.
  2. Change each value in the pref_market_values string array (15, 30, 60, etc.) to be values for the markets—abbreviations that correspond to the countries above, such as US, CA, etc.:
<string-array name="pref_market_values">
        <item>US</item>
        <item>CA</item>
        <item>UK</item>
        <item>IN</item>
        <item>JA0</item>
        <item>-1</item>
</string-array>
  1. Scroll down to the pref_title_system_sync_settings string resource, and edit the resource (which is set to System sync settings) to Account settings.
  2. Refactor > Rename the string array resource name pref_title_system_sync_settings to pref_title_account_settings.
  3. Open the pref_account.xml file. The ListPreference in this layout defines the setting you just changed. Note that the string resources for the android:entries, android:entryValues and android:title attributes are now changed to the values you supplied in the previous steps:
<ListPreference
        android:defaultValue="180"
        android:entries="@array/pref_market_titles"
        android:entryValues="@array/pref_market_values"
        android:key="sync_frequency"
        android:negativeButtonText="@null"
        android:positiveButtonText="@null"
        android:title="@string/pref_title_account" />
  1. Change the android:defaultValue attribute to "US":
android:defaultValue="US"

Because the key for this setting preference ("sync_frequency") is hard-coded elsewhere in the Java code, don't change the android:key attribute. Instead, keep using "sync_frequency" as the key for this setting in this example. If you were thoroughly customizing the settings for a real-world app, you would take the time to change the hard-coded keys throughout the code.

2.4 Add code to set the default values for the settings

To add code to set the default values for the settings, follow these steps:

  1. Open MainActivity and find the onCreate() method.
  2. Add the following PreferenceManager.setDefaultValues statements at the end of the onCreate() method:
PreferenceManager.setDefaultValues(this, 
                R.xml.pref_general, false);
PreferenceManager.setDefaultValues(this, 
                R.xml.pref_notification, false);
PreferenceManager.setDefaultValues(this, 
                R.xml.pref_account, false);

The default values are already specified in the XML file with the android:defaultValue attribute, but the above statements ensure that the SharedPreferences file is properly initialized with the default values. The setDefaultValues() method takes three arguments:

  • The app context, such as this.
  • The resource ID for the settings layout XML file which includes the default values set by the android:defaultValue attribute.
  • A boolean indicating whether the default values should be set more than once. When false, the system sets the default values only when this method is called for the first time. As long as you set this third argument to false, you can safely call this method every time your Activity starts without overriding the user's saved settings values by resetting them to the default values. However, if you set it to true, the method will override any previous values with the defaults.

2.5 Add code to read values for the settings

  1. Add the following code at the end of the MainActivity onCreate() method. You can add it immediately after the code you added in the previous step to set the defaults for the settings:
SharedPreferences sharedPref = PreferenceManager
                        .getDefaultSharedPreferences(this);
String marketPref = sharedPref
                        .getString("sync_frequency", "-1");
displayToast(marketPref);

As you learned in the previous task, you use PreferenceManager.getDefaultSharedPreferences(this) to get the setting as a SharedPreferences object (marketPref). You then use getString() to get the string value of the setting that uses the key (sync_frequency), and assign it to marketPref. If there is no value for the key, the getString() method assigns the setting value of marketPref to -1, which is the value of Other in the pref_market_values array.

  1. Run the app. When the app's main screen first appears, you see a Toast message at the bottom of the screen. The first time you run the app, you should see "-1" displayed in the Toast because you haven't changed the setting yet.
  2. Tap Settings in the options menu, and tap Account in the Settings screen. Tap Market, and choose Canada as shown below:

Choosing Account (left) and changing the Market setting (right)

  1. Tap the Up button in the app bar to return to the Settings screen, and tap it again to return to the main screen.
  2. Run the app again from Android Studio. You should see a Toast message with "CA" (for Canada), and the Market setting is now set to Canada.

Toast showing the Market setting (left) and the Account screen showing the Market setting (right)

You have successfully integrated the Settings Activity with the app.

  1. Now run the app on a tablet or tablet emulator. Because a tablet has a physically larger screen, the Android runtime takes advantage of the extra space. On a tablet, the settings and details are displayed on the same screen making it easier for users to manage their settings.

Settings Activity on a tablet in horizontal orientation

Task 2 solution code

Android Studio project: DroidCafeWithSettings

5. Coding challenge

Challenge: The DroidCafeWithSettings app displays the settings on a tablet-sized screen properly, but the Up button in the app bar doesn't return the user to the MainActivity as it does on a phone-sized screen. This is due to the three onOptionsItemSelected() methods—one for each Fragment—in SettingsActivity. It uses the following to restart the SettingsActivity when the user taps the Up button:

startActivity(new Intent(getActivity(), SettingsActivity.class));

The above is the appropriate action on phone screens in which Settings headers (General, Notifications, and Account) appear in a separate screen (SettingsActivity). After changing a setting, you want the user's tap on the Up button to take the user back to the Settings headers in SettingsActivity. A further tap on Up takes the user back to MainActivity.

However, on a tablet, the headers are always visible in the left pane (while the settings are in the right pane). As a result, tapping the Up button doesn't take the user to MainActivity.

Find a way to make the Up button work properly in SettingsActivity on tablet-sized screens.

Hint: While there are several ways to fix this problem, consider the following steps:

  1. Add another dimens.xml file to specifically accommodate screen sizes larger than 600dp. When the app runs on a specific device, the appropriate dimens.xml file is chosen based on the qualifiers for the dimens.xml files. You can add another dimens.xml file with the Smallest Screen Width qualifier set to 600 dp (sw600dp), as you learned in the practical on supporting landscape and screen sizes, to specify any device with a large screen, such as a tablet.
  2. Add the following bool resource between the <resources> and </resources> tags in the dimens.xml (sw600dp) file, which is automatically chosen for tablets:
<bool name="isTablet">true</bool>
  1. Add the following bool resource to the standard dimens.xml file, which is chosen when the app runs on any device that is not large:
<bool name="isTablet">false</bool>
  1. In SettingsActivity, you can add to all three onOptionsItemSelected() methods (one for each Fragment) an if else block that checks to see if isTablet is true. If it is, your code can redirect the Up button action to MainActivity.

6. Challenge solution code

Android Studio project: DroidCafeWithSettingsChallenge

7. Summary

Users expect to navigate to app settings by tapping Settings in side navigation, such as a navigation drawer, or in the options menu in the app bar.

To provide user settings for your app, provide an Activity for settings:

Show each fragment in the settings activity:

Add XML resource files for the settings:

  1. Create a new resource directory (File > New > Android Resource Directory).
  2. In the Resource type drop-down menu, select xml. The xml folder appears inside the res folder.
  3. Click on xml and select File > New > XML resource file.
  4. Enter preferences as the name of the XML file. The preferences.xml file appears inside the xml folder.

Add UI controls such as toggle switches, with attributes in a preferences XML file:

Use attributes with each UI element for settings:

  • android:defaultValue is the value of the setting when the app starts for the first time.
  • android:title is the user-visible setting title.
  • android:key is the key used for storing the setting value.
  • android:summary is the user-visible text that appears under the setting.

Save and read settings values:

  • When the app starts, the MainActivity onCreate() method can read the setting values that have changed, and use the changed values rather than the default values.
  • Each setting is identified using a key-value pair. The Android system uses this key-value pair when saving or retrieving settings from a SharedPreferences file for your app. When the user changes a setting, the system updates the corresponding value in the SharedPreferences file.
  • To use the value of the setting, your app can use the key to get the setting from the SharedPreferences file.
  • Your app reads settings values from SharedPreferences using PreferenceManager.getDefaultSharedPreferences(), and obtains each setting value using .getString, .getBoolean, etc.

8. Related concepts

The related concept documentation is in 9.2: App settings.

9. Learn more

Android Studio documentation: Android Studio User Guide

Android developer documentation:

Material Design specification: Android settings

Stack Overflow:

10. Homework

This section lists possible homework assignments for students who are working through this codelab as part of a course led by an instructor. It's up to the instructor to do the following:

  • Assign homework if required.
  • Communicate to students how to submit homework assignments.
  • Grade the homework assignments.

Instructors can use these suggestions as little or as much as they want, and should feel free to assign any other homework they feel is appropriate.

If you're working through this codelab on your own, feel free to use these homework assignments to test your knowledge.

Build and run an app

Open the DroidCafeWithSettings app project.

  1. Add a ListPreference (a dialog with radio buttons) to the general settings. Put the dialog in the General settings screen, below the "Add friends to order messages" ListPreference.
  2. Edit the string arrays used for the ListPreference to include the title "Choose a delivery method." Use the same delivery choices that are used in the radio buttons in the OrderActivity.
  3. Make the user's chosen delivery method appear in the same Toast message as the chosen Market setting.
  4. Extra credit: Show the selected delivery method as the setting summary text that appears underneath the ListPreference title. Enable this text to change with each update.

Delivery settings in the General settings screen

Answer these questions

Question 1

In which file of the DroidCafeWithSettings project do you define the array of entries and the array of values for the ListPreference? Choose one:

  • pref_general.xml
  • strings.xml
  • menu_main.xml
  • activity_main.xml
  • content_main.xml

Question 2

In which file of the DroidCafeWithSettings project do you use the array of entries and the array of values in setting up the ListPreference, and also set the ListPreference key and default value? Choose one:

  • pref_general.xml
  • strings.xml
  • menu_main.xml
  • content_main.xml
  • SettingsActivity.java

Question 3

How do you set up a settings Activity and a Fragment with a SwitchPreference for the UI, and still remain compatible with the v7 appcompat library for backward compatibility with older versions of Android?

  • Use a settings activity that extends Activity, a fragment that extends PreferenceFragment, and a SwitchPreference for the UI.
  • Change MainActivity to extend Activity.
  • Use a settings activity that extends AppCompatActivity, a fragment that extends PreferenceFragmentCompat, and a SwitchPreferenceCompat for the UI.
  • You can't use a fragment with a SwitchPreference and remain compatible with the v7 appcompat library.

Submit your app for grading

Guidance for graders

Check that the app has the following features:

  • The onCreate() method reads the deliveryPref setting using sharedPref.getString().
  • The pref_general.xml file includes a ListPreference that uses for its entries an array of delivery choices.
  • Extra credit: The statement bindPreferenceSummaryToValue(findPreference("delivery")) has been added to the onCreate() method of the GeneralPreferenceFragment class in the SettingsActivity in order to show the delivery choice in the preference summary.

11. Next codelab

To find the next practical codelab in the Android Developer Fundamentals (V2) course, see Codelabs for Android Developer Fundamentals (V2).

For an overview of the course, including links to the concept chapters, apps, and slides, see Android Developer Fundamentals (Version 2).