Add Car Settings

Most pages in Car Settings are implemented as a series of fragments that extend SettingsFragment, with each of them having their own activity defined in CarSettingActivities. These static activities are extended from BaseCarSettingsActivity. In addition to these settings, you can inject preferences from other system apps to appear in CarSettings.

Add a new preference in Car Settings

To add a new setting:

  1. Define an XML file:
    1. Ensure all preferences have defined android:key. The list of keys is maintained in preference_keys.xml. Preferences keys should be unique.
    2. For search indexing purposes, preference screens should also have a defined android:key. The list of preference screen keys is maintained in preference_screen_keys.xml. Preference screen keys should also be unique.
    3. If the preference displays static information only (for example, no special business logic), set the preference controller as com.android.car.settings.common.DefaultRestrictionsPreferenceController.
    4. If the preference requires business logic, set the preference controller with a new preference controller name.
  2. (If required) Create the preference controller in the appropriate package, which extends PreferenceController. See the Javadoc if needed.
  3. Create a fragment with getPreferenceScreenResId returning the XML file defined in Step 1.
  4. Create an activity in CarSettingActivities that extends BaseCarSettingsActivity and then implement getInitialFragment(), returning the fragment defined in Step 3.
  5. Update AndroidManifest.xml to include the activity defined in Step 4.

Example

The following material illustrates this process.

  1. Define an XML file named demo_fragment.xml:
    <PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:settings="http://schemas.android.com/apk/res-auto"
        android:title="@string/demo_label"
        android:key="@string/psk_demo">
        <Preference
            android:icon="@drawable/ic_settings_demo_preference_1"
            android:key="@string/pk_demo_preference_1"
            android:title="@string/demo_preference_1_title"
    settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController">
            <intent android:targetPackage="com.android.car.settings"
    android:targetClass="com.android.car.settings.common.CarSettingActivities$DemoSetting1Activity"/>
        </Preference>
        <Preference
            android:icon="@drawable/ic_settings_demo_preference_2"
            android:key="@string/pk_demo_preference_2"
            android:title="@string/demo_preference_2_title"
    settings:controller="com.android.car.settings.example.MyCustomRestrictionsPreferenceController">
            <intent android:targetPackage="com.android.car.settings"
    android:targetClass="com.android.car.settings.common.CarSettingActivities$DemoSetting2Activity"/>
        </Preference>
    </PreferenceScreen>
  2. Add the preference keys to preference_keys:
    <resources>
        [...]
        <string name="pk_demo_preference_1" translatable="false">demo_preference_1</string>
        <string name="pk_demo_preference_2" translatable="false">demo_preference_2</string>
    </resources>
  3. Add the preference screen key to preference_screen_keys.xml:
    <resources>
        [...]
        <string name="psk_demo" translatable="false">demo_screen</string>
    </resources>

    For the first example preference, use DefaultRestrictionsPreferenceController. For the second preference, use a custom preference controller, which needs to be defined. For this example, you can customize this preference to available admin users only. To do so, define the following custom controller:

    public class MyCustomRestrictionsPreferenceController extends 
        PreferenceController<Preference> {
    
        private final UserManager mUserManager;
    
        public MyCustomRestrictionsPreferenceController(Context context, String 
            preferenceKey, FragmentController fragmentController, 
            CarUxRestrictions uxRestrictions) {
            super(context, preferenceKey, fragmentController, uxRestrictions);
            mUserManager = UserManager.get(context);
        }
    
        @Override
        protected Class<Preference> getPreferenceType() {
            return Preference.class;
        }
    
        @Override
        public int getAvailabilityStatus() {
            return mUserManager.isAdminUser() ? AVAILABLE : DISABLED_FOR_USER;
        }
    }
  4. To create a fragment, override getPreferenceScreenResId:
  5. public class DemoFragment extends SettingsFragment {
    
        @Override
        @XmlRes
        protected int getPreferenceScreenResId() {
            return R.xml.demo_fragment;
        }
    }
  6. To hold the new fragment, create an activity in CarSettingActivities:
  7. public class CarSettingActivities {
        [...]
        public static class DemoActivity extends BaseCarSettingsActivity {
            @Nullable
            @Override
            protected Fragment getInitialFragment() {
                return new DemoFragment();
            }
        }
    }
  8. Update the manifest file with the new activity:
  9. <application
        [...]
        <activity
            android:name=".common.CarSettingActivities$DemoActivity"
            android:exported="true">
            <meta-data android:name="distractionOptimized" android:value="true"/>
        </activity>
        [...]
    </application>

    Add an external intent preference within Car Settings

    As an alternative to injected preferences, it's also possible to insert a preference directly into Car Settings that intents out into another app. This can be done by simply adding a preference to a preference screen with an intent action that resolves to an external app. Like other preferences in Car Settings, these preferences have the same XML attributes available to them.

    <Preference
        android:key="@string/pk_demo_preference"
        android:title="@string/demo_preference_title"
        android:summary="@string/demo_preference_summary"
    settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController">
            <intent android:action="android.intent.action.DEMO_ACTION"/>
    </Preference>

    Add an injected preference

    Injected preferences contain intents leading to external or internal activities. As an example, the Google setting item on the Settings home page is an injected preference. Injected preferences are particularly useful when any of the following is true. The setting:

    • Isn't directly implemented in the CarSettings app (such as injecting a setting implemented by OEMs).
    • Should appear in the CarSettings app.

    To configure an activity as an injected setting:

    1. To mark the activity as an injected setting, add an intent-filter to the activity.
    2. Tell the CarSettings app which category it belongs to. The category is a constant, defined in CategoryKey, and is used to indicate at which level of CarSettings the injected setting should appear in. We provide a set of categories inside CategoryKey, but there are no restrictions for OEMs to define their own categories.
    3. (optional) Add summary text when the setting is displayed:
      <activity android:name="Settings$DemoSettingsActivity"
          <!-- Mark the activity as an injected setting -->
          <intent-filter>
              <action android:name="com.android.settings.action.EXTRA_SETTINGS"/>
          </intent-filter>
          <!-- Tell CarSettings app which category it belongs to -->
          <meta-data android:name="com.android.settings.category"
                     android:value="com.android.settings.category.demo_category"/>
          <!-- Tell CarSettings the what the preference title should be -->
          <meta-data android:name="com.android.settings.title"
                     android:value="@string/app_name" />
          <!-- Optional: specify the icon to show with the preference -->
          <meta-data android:name="com.android.settings.icon"
                     android:resource="@drawable/ic_demo"
                     android:value="true"/>
          <!-- Optional: Add a summary text when the string is displayed -->
          <meta-data android:name="com.android.settings.summary"
                     android:resource="@string/demo_summary"/>
      </activity>

    To have the injected setting appear on a specific page in the CarSettings app, include the following sample code in the XML, modifying variables where appropriate:

    <com.android.car.settings.common.LogicalPreferenceGroup
        <!-- Replace key string -->
        android:key="@string/pk_system_extra_settings"
        <!-- Indicates the preferences in the group should be injected in.
             ExtraSettingsPreferenceController contains the logic to pull in injected
             preferences. -->
    settings:controller="com.android.settings.common.ExtraSettingsPreferenceController">
        <!-- Tells the controller what activities should be pulled into this preference
             group. -->
        <intent android:action="com.android.settings.action.EXTRA_SETTINGS">
            <!-- Name and value should match the metadata in your activity -->
            <extra android:name="com.android.settings.category"
                   android:value="com.android.settings.category.demo_category"/>
        </intent>
    </com.android.car.settings.common.LogicalPreferenceGroup>