Print - Settings

Change-Id: Id662a41fb189823c6d06aafc7e98996f3ef6c930
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 068bdb9..87b1911 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3189,10 +3189,10 @@
     <!-- Title for accessibility menu item to lauch a settings activity. [CHAR LIMIT=15] -->
     <string name="accessibility_menu_item_settings">Settings</string>
 
-    <!-- Summary for the enabled state of an accessiblity feature. [CHAR LIMIT=10] -->
-    <string name="accessibility_feature_state_on">On</string>
-    <!-- Summary for the disabled state of an accessiblity feature. [CHAR LIMIT=10] -->
-    <string name="accessibility_feature_state_off">Off</string>
+    <!-- Summary for the enabled state of a feature. [CHAR LIMIT=10] -->
+    <string name="feature_state_on">On</string>
+    <!-- Summary for the disabled state of a feature. [CHAR LIMIT=10] -->
+    <string name="feature_state_off">Off</string>
 
     <!-- Title for a warning about security implications of enabling an accessibility
          service. [CHAR LIMIT=NONE] -->
@@ -3232,6 +3232,35 @@
     <!-- Accessibility settings: button for lauching settings for an accessibility service -->
     <string name="settings_button">Settings</string>
 
+     <!-- Printing settings -->
+    <skip />
+
+    <!-- Title in main settings screen for printing settings [CHAR LIMIT=15] -->
+    <string name="print_settings">Printing</string>
+    <!-- Title for print service settings screen [CHAR LIMIT=25] -->
+    <string name="print_settings_title">Printing settings</string>
+    <!-- Title for the printing preference category of print services. [CHAR LIMIT=25] -->
+    <string name="print_services_title">Services</string>
+
+    <!-- Title for a warning message about security implications of enabling a print service,
+         displayed as a dialog message when the user selects to enable a print service (tablet). [CHAR LIMIT=NONE] -->
+    <string name="print_service_security_warning_title">Use
+         <xliff:g id="service" example="My Print Service">%1$s</xliff:g>?</string>
+    <!-- Summary for a warning message about security implications of enabling a print service,
+         displayed as a dialog message when the user selects to enable a print service. [CHAR LIMIT=NONE] -->
+    <string name="print_service_security_warning_summary">
+         <xliff:g id="print_service_name">%1$s</xliff:g> can receive documents you print.
+         Such documents may contain sensitive data.</string>
+
+    <!-- Title for the prompt shown as a placeholder if no print serivices are installed. [CHAR LIMIT=50] -->
+    <string name="print_no_services_installed">No services installed</string>
+
+    <!-- Title for print menu item to launch a settings activity. [CHAR LIMIT=25] -->
+    <string name="print_menu_item_settings">Settings</string>
+
+    <!-- Title for print menu item to launch the add printers activity. [CHAR LIMIT=25] -->
+    <string name="print_menu_item_add_printers">Add printers</string>
+
     <!-- App Fuel Gauge strings -->
     <skip />
 
diff --git a/res/xml/print_settings.xml b/res/xml/print_settings.xml
new file mode 100644
index 0000000..b21f2ac
--- /dev/null
+++ b/res/xml/print_settings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+        android:title="@string/print_settings_title">
+
+    <PreferenceCategory
+            android:key="services_category"
+            android:title="@string/print_services_title">
+    </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/res/xml/settings_headers.xml b/res/xml/settings_headers.xml
index 05fa31f..e809fb9 100644
--- a/res/xml/settings_headers.xml
+++ b/res/xml/settings_headers.xml
@@ -175,6 +175,13 @@
         android:icon="@drawable/ic_settings_accessibility"
         android:title="@string/accessibility_settings" />
 
+    <!-- Print -->
+    <header
+        android:id="@+id/print_settings"
+        android:fragment="com.android.settings.PrintingSettings"
+        android:icon="@drawable/ic_settings_accessibility"
+        android:title="@string/print_settings" />
+
     <!-- Development -->
     <header
         android:id="@+id/development_settings"
diff --git a/src/com/android/settings/AccessibilitySettings.java b/src/com/android/settings/AccessibilitySettings.java
index f4188f1..5d3fb42 100644
--- a/src/com/android/settings/AccessibilitySettings.java
+++ b/src/com/android/settings/AccessibilitySettings.java
@@ -405,9 +405,9 @@
             final boolean serviceEnabled = accessibilityEnabled
                     && enabledServices.contains(componentName);
             if (serviceEnabled) {
-                preference.setSummary(getString(R.string.accessibility_feature_state_on));
+                preference.setSummary(getString(R.string.feature_state_on));
             } else {
-                preference.setSummary(getString(R.string.accessibility_feature_state_off));
+                preference.setSummary(getString(R.string.feature_state_off));
             }
 
             preference.setOrder(i);
diff --git a/src/com/android/settings/PrintingSettings.java b/src/com/android/settings/PrintingSettings.java
new file mode 100644
index 0000000..cd22366
--- /dev/null
+++ b/src/com/android/settings/PrintingSettings.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceScreen;
+import android.printservice.PrintServiceInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.view.Gravity;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.settings.PrintingSettings.ToggleSwitch.OnBeforeCheckedChangeListener;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Activity with the printing settings.
+ */
+public class PrintingSettings extends SettingsPreferenceFragment implements DialogCreatable {
+
+    private static final char ENABLED_PRINT_SERVICES_SEPARATOR = ':';
+
+    // Preference categories
+    private static final String SERVICES_CATEGORY = "services_category";
+
+    // Extras passed to sub-fragments.
+    private static final String EXTRA_PREFERENCE_KEY = "preference_key";
+    private static final String EXTRA_CHECKED = "checked";
+    private static final String EXTRA_TITLE = "title";
+    private static final String EXTRA_ENABLE_WARNING_TITLE = "enable_warning_title";
+    private static final String EXTRA_ENABLE_WARNING_MESSAGE = "enable_warning_message";
+    private static final String EXTRA_SETTINGS_TITLE = "settings_title";
+    private static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name";
+    private static final String EXTRA_ADD_PRINTERS_TITLE = "add_printers_title";
+    private static final String EXTRA_ADD_PRINTERS_COMPONENT_NAME = "add_printers_component_name";
+    private static final String EXTRA_SERVICE_COMPONENT_NAME = "service_component_name";
+
+    // Auxiliary members.
+    private final static SimpleStringSplitter sStringColonSplitter =
+            new SimpleStringSplitter(ENABLED_PRINT_SERVICES_SEPARATOR);
+
+    private static final List<ResolveInfo> sInstalledServicesList = new ArrayList<ResolveInfo>();
+
+    private static final Set<ComponentName> sEnabledServiceNameSet = new HashSet<ComponentName>();
+
+    private final PackageMonitor mSettingsPackageMonitor = new SettingsPackageMonitor();
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void dispatchMessage(Message msg) {
+            super.dispatchMessage(msg);
+            loadInstalledServices(getActivity());
+            loadEnabledServices(getActivity());
+            updateServicesPreferences();
+        }
+    };
+
+    private final SettingsContentObserver mSettingsContentObserver =
+            new SettingsContentObserver(mHandler) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            loadInstalledServices(getActivity());
+            loadEnabledServices(getActivity());
+            updateServicesPreferences();
+        }
+    };
+
+    // Preference controls.
+    private PreferenceCategory mServicesCategory;
+
+    private Preference mNoServicesMessagePreference;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        addPreferencesFromResource(R.xml.print_settings);
+        mServicesCategory = (PreferenceCategory) findPreference(SERVICES_CATEGORY);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false);
+        mSettingsContentObserver.register(getContentResolver());
+        loadInstalledServices(getActivity());
+        loadEnabledServices(getActivity());
+        updateServicesPreferences();
+    }
+
+    @Override
+    public void onPause() {
+        mSettingsPackageMonitor.unregister();
+        mSettingsContentObserver.unregister(getContentResolver());
+        super.onPause();
+    }
+
+    private void updateServicesPreferences() {
+        // Since services category is auto generated we have to do a pass
+        // to generate it since services can come and go.
+        mServicesCategory.removeAll();
+
+       final int installedServiceCount = sInstalledServicesList.size();
+        for (int i = 0; i < installedServiceCount; i++) {
+            ResolveInfo installedService = sInstalledServicesList.get(i);
+
+            PreferenceScreen preference = getPreferenceManager().createPreferenceScreen(
+                    getActivity());
+
+            String title = installedService.loadLabel(getPackageManager()).toString();
+            preference.setTitle(title);
+
+            ComponentName componentName = new ComponentName(
+                    installedService.serviceInfo.packageName,
+                    installedService.serviceInfo.name);
+            preference.setKey(componentName.flattenToString());
+
+            final boolean serviceEnabled = sEnabledServiceNameSet.contains(componentName);
+            if (serviceEnabled) {
+                preference.setSummary(getString(R.string.feature_state_on));
+            } else {
+                preference.setSummary(getString(R.string.feature_state_off));
+            }
+
+            preference.setOrder(i);
+            preference.setFragment(TogglePrintServicePreferenceFragment.class.getName());
+            preference.setPersistent(false);
+
+            Bundle extras = preference.getExtras();
+            extras.putString(EXTRA_PREFERENCE_KEY, preference.getKey());
+            extras.putBoolean(EXTRA_CHECKED, serviceEnabled);
+            extras.putString(EXTRA_TITLE, title);
+
+            PrintServiceInfo printServiceInfo = PrintServiceInfo.create(
+                    installedService, getActivity());
+
+            CharSequence applicationLabel = installedService.loadLabel(getPackageManager());
+
+            extras.putString(EXTRA_ENABLE_WARNING_TITLE, getString(
+                    R.string.print_service_security_warning_title, applicationLabel));
+            extras.putString(EXTRA_ENABLE_WARNING_MESSAGE, getString(
+                    R.string.print_service_security_warning_summary, applicationLabel));
+
+            String settingsClassName = printServiceInfo.getSettingsActivityName();
+            if (!TextUtils.isEmpty(settingsClassName)) {
+                extras.putString(EXTRA_SETTINGS_TITLE,
+                        getString(R.string.print_menu_item_settings));
+                extras.putString(EXTRA_SETTINGS_COMPONENT_NAME,
+                        new ComponentName(installedService.serviceInfo.packageName,
+                                settingsClassName).flattenToString());
+            }
+
+            String addPrinterClassName = printServiceInfo.getAddPrintersActivityName();
+            if (!TextUtils.isEmpty(addPrinterClassName)) {
+                extras.putString(EXTRA_ADD_PRINTERS_TITLE,
+                        getString(R.string.print_menu_item_add_printers));
+                extras.putString(EXTRA_ADD_PRINTERS_COMPONENT_NAME,
+                        new ComponentName(installedService.serviceInfo.packageName,
+                                addPrinterClassName).flattenToString());
+            }
+
+            extras.putString(EXTRA_SERVICE_COMPONENT_NAME, componentName.flattenToString());
+
+            mServicesCategory.addPreference(preference);
+        }
+
+        if (mServicesCategory.getPreferenceCount() == 0) {
+            if (mNoServicesMessagePreference == null) {
+                mNoServicesMessagePreference = new Preference(getActivity()) {
+                    @Override
+                    protected void onBindView(View view) {
+                        super.onBindView(view);
+                        TextView summaryView = (TextView) view.findViewById(R.id.summary);
+                        String title = getString(R.string.print_no_services_installed);
+                        summaryView.setText(title);
+                    }
+                };
+                mNoServicesMessagePreference.setPersistent(false);
+                mNoServicesMessagePreference.setLayoutResource(
+                        R.layout.text_description_preference);
+                mNoServicesMessagePreference.setSelectable(false);
+            }
+            mServicesCategory.addPreference(mNoServicesMessagePreference);
+        }
+    }
+
+    private static void loadInstalledServices(Context context) {
+        sInstalledServicesList.clear();
+        List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentServices(
+                    new Intent(android.printservice.PrintService.SERVICE_INTERFACE),
+                    PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+        final int resolveInfoCount = resolveInfos.size();
+        for (int i = 0, count = resolveInfoCount; i < count; i++) {
+            ResolveInfo resolveInfo = resolveInfos.get(i);
+            sInstalledServicesList.add(resolveInfo);
+        }
+    }
+
+    private static void loadEnabledServices(Context context) {
+        sEnabledServiceNameSet.clear();
+
+        String enabledServicesSetting = Settings.Secure.getString(context
+                .getContentResolver(), Settings.Secure.ENABLED_PRINT_SERVICES);
+        if (enabledServicesSetting == null) {
+            enabledServicesSetting = "";
+        }
+
+        SimpleStringSplitter colonSplitter = sStringColonSplitter;
+        colonSplitter.setString(enabledServicesSetting);
+
+        while (colonSplitter.hasNext()) {
+            String componentNameString = colonSplitter.next();
+            ComponentName enabledService = ComponentName.unflattenFromString(
+                    componentNameString);
+            sEnabledServiceNameSet.add(enabledService);
+        }
+    }
+
+    private class SettingsPackageMonitor extends PackageMonitor {
+        @Override
+        public void onPackageAdded(String packageName, int uid) {
+           mHandler.obtainMessage().sendToTarget();
+        }
+
+        @Override
+        public void onPackageAppeared(String packageName, int reason) {
+            mHandler.obtainMessage().sendToTarget();
+        }
+
+        @Override
+        public void onPackageDisappeared(String packageName, int reason) {
+            mHandler.obtainMessage().sendToTarget();
+        }
+
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            mHandler.obtainMessage().sendToTarget();
+        }
+    }
+
+    public static class ToggleSwitch extends Switch {
+
+        private OnBeforeCheckedChangeListener mOnBeforeListener;
+
+        public static interface OnBeforeCheckedChangeListener {
+            public boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked);
+        }
+
+        public ToggleSwitch(Context context) {
+            super(context);
+        }
+
+        public void setOnBeforeCheckedChangeListener(OnBeforeCheckedChangeListener listener) {
+            mOnBeforeListener = listener;
+        }
+
+        @Override
+        public void setChecked(boolean checked) {
+            if (mOnBeforeListener != null
+                    && mOnBeforeListener.onBeforeCheckedChanged(this, checked)) {
+                return;
+            }
+            super.setChecked(checked);
+        }
+
+        public void setCheckedInternal(boolean checked) {
+            super.setChecked(checked);
+        }
+    }
+
+    public static class TogglePrintServicePreferenceFragment
+            extends ToggleFeaturePreferenceFragment implements DialogInterface.OnClickListener {
+
+        private static final int DIALOG_ID_ENABLE_WARNING = 1;
+
+        private final SettingsContentObserver mSettingsContentObserver =
+                new SettingsContentObserver(new Handler()) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                String settingValue = Settings.Secure.getString(getContentResolver(),
+                        Settings.Secure.ENABLED_PRINT_SERVICES);
+                final boolean enabled = settingValue.contains(mComponentName);
+                mToggleSwitch.setCheckedInternal(enabled);
+            }
+        };
+
+        private CharSequence mEnableWarningTitle;
+        private CharSequence mEnableWarningMessage;
+
+        private String mComponentName;
+
+        @Override
+        public void onResume() {
+            mSettingsContentObserver.register(getContentResolver());
+            super.onResume();
+        }
+
+        @Override
+        public void onPause() {
+            mSettingsContentObserver.unregister(getContentResolver());
+            super.onPause();
+        }
+
+        @Override
+        public void onPreferenceToggled(String preferenceKey, boolean enabled) {
+            Set<ComponentName> enabledServices = sEnabledServiceNameSet;
+            ComponentName toggledService = ComponentName.unflattenFromString(preferenceKey);
+            if (enabled) {
+                enabledServices.add(toggledService);
+            } else {
+                enabledServices.remove(toggledService);
+            }
+            StringBuilder enabledServicesBuilder = new StringBuilder();
+            for (ComponentName enabledService : enabledServices) {
+                enabledServicesBuilder.append(enabledService.flattenToString());
+                enabledServicesBuilder.append(ENABLED_PRINT_SERVICES_SEPARATOR);
+            }
+            final int enabledServicesBuilderLength = enabledServicesBuilder.length();
+            if (enabledServicesBuilderLength > 0) {
+                enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
+            }
+            Settings.Secure.putString(getContentResolver(),
+                    Settings.Secure.ENABLED_PRINT_SERVICES,
+                    enabledServicesBuilder.toString());
+        }
+
+        @Override
+        public Dialog onCreateDialog(int dialogId) {
+            CharSequence title = null;
+            CharSequence message = null;
+            switch (dialogId) {
+                case DIALOG_ID_ENABLE_WARNING:
+                    title = mEnableWarningTitle;
+                    message = mEnableWarningMessage;
+                    break;
+                default:
+                    throw new IllegalArgumentException();
+            }
+            return new AlertDialog.Builder(getActivity())
+                    .setTitle(title)
+                    .setIconAttribute(android.R.attr.alertDialogIcon)
+                    .setMessage(message)
+                    .setCancelable(true)
+                    .setPositiveButton(android.R.string.ok, this)
+                    .setNegativeButton(android.R.string.cancel, this)
+                    .create();
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            final boolean checked;
+            switch (which) {
+                case DialogInterface.BUTTON_POSITIVE:
+                    checked = true;
+                    mToggleSwitch.setCheckedInternal(checked);
+                    getArguments().putBoolean(EXTRA_CHECKED, checked);
+                    onPreferenceToggled(mPreferenceKey, checked);
+                    break;
+                case DialogInterface.BUTTON_NEGATIVE:
+                    checked = false;
+                    mToggleSwitch.setCheckedInternal(checked);
+                    getArguments().putBoolean(EXTRA_CHECKED, checked);
+                    onPreferenceToggled(mPreferenceKey, checked);
+                    break;
+                default:
+                    throw new IllegalArgumentException();
+            }
+        }
+
+        @Override
+        protected void onInstallActionBarToggleSwitch() {
+            super.onInstallActionBarToggleSwitch();
+            mToggleSwitch.setOnBeforeCheckedChangeListener(new OnBeforeCheckedChangeListener() {
+                @Override
+                public boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked) {
+                    if (checked) {
+                        if (!TextUtils.isEmpty(mEnableWarningMessage)) {
+                            toggleSwitch.setCheckedInternal(false);
+                            getArguments().putBoolean(EXTRA_CHECKED, false);
+                            showDialog(DIALOG_ID_ENABLE_WARNING);
+                            return true;
+                        }
+                        onPreferenceToggled(mPreferenceKey, true);
+                    } else {
+                        onPreferenceToggled(mPreferenceKey, false);
+                    }
+                    return false;
+                }
+            });
+        }
+
+        @Override
+        protected void onProcessArguments(Bundle arguments) {
+            super.onProcessArguments(arguments);
+            // Settings title and intent.
+            String settingsTitle = arguments.getString(EXTRA_SETTINGS_TITLE);
+            String settingsComponentName = arguments.getString(EXTRA_SETTINGS_COMPONENT_NAME);
+            if (!TextUtils.isEmpty(settingsTitle) && !TextUtils.isEmpty(settingsComponentName)) {
+                Intent settingsIntent = new Intent(Intent.ACTION_MAIN).setComponent(
+                        ComponentName.unflattenFromString(settingsComponentName.toString()));
+                if (!getPackageManager().queryIntentActivities(settingsIntent, 0).isEmpty()) {
+                    mSettingsTitle = settingsTitle;
+                    mSettingsIntent = settingsIntent;
+                    setHasOptionsMenu(true);
+                }
+            }
+            // Add printers title and intent.
+            String addPrintersTitle = arguments.getString(EXTRA_ADD_PRINTERS_TITLE);
+            String addPrintersComponentName =
+                    arguments.getString(EXTRA_ADD_PRINTERS_COMPONENT_NAME);
+            if (!TextUtils.isEmpty(addPrintersTitle)
+                    && !TextUtils.isEmpty(addPrintersComponentName)) {
+                Intent addPritnersIntent = new Intent(Intent.ACTION_MAIN).setComponent(
+                        ComponentName.unflattenFromString(addPrintersComponentName.toString()));
+                if (!getPackageManager().queryIntentActivities(addPritnersIntent, 0).isEmpty()) {
+                    mAddPrintersTitle = addPrintersTitle;
+                    mAddPrintersIntent = addPritnersIntent;
+                    setHasOptionsMenu(true);
+                }
+            }
+            // Enable warning title.
+            mEnableWarningTitle = arguments.getCharSequence(
+                    PrintingSettings.EXTRA_ENABLE_WARNING_TITLE);
+            // Enable warning message.
+            mEnableWarningMessage = arguments.getCharSequence(
+                    PrintingSettings.EXTRA_ENABLE_WARNING_MESSAGE);
+            // Component name.
+            mComponentName = arguments.getString(EXTRA_SERVICE_COMPONENT_NAME);
+        }
+    }
+
+    public static abstract class ToggleFeaturePreferenceFragment
+            extends SettingsPreferenceFragment {
+
+        protected ToggleSwitch mToggleSwitch;
+
+        protected String mPreferenceKey;
+
+        protected CharSequence mSettingsTitle;
+        protected Intent mSettingsIntent;
+
+        protected CharSequence mAddPrintersTitle;
+        protected Intent mAddPrintersIntent;
+
+        // TODO: Showing sub-sub fragment does not handle the activity title
+        // so we do it but this is wrong. Do a real fix when there is time.
+        private CharSequence mOldActivityTitle;
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
+                    getActivity());
+            setPreferenceScreen(preferenceScreen);
+        }
+
+        @Override
+        public void onViewCreated(View view, Bundle savedInstanceState) {
+            super.onViewCreated(view, savedInstanceState);
+            onInstallActionBarToggleSwitch();
+            onProcessArguments(getArguments());
+            getListView().setDivider(null);
+            getListView().setEnabled(false);
+        }
+
+        @Override
+        public void onDestroyView() {
+            getActivity().getActionBar().setCustomView(null);
+            if (mOldActivityTitle != null) {
+                getActivity().getActionBar().setTitle(mOldActivityTitle);
+            }
+            mToggleSwitch.setOnBeforeCheckedChangeListener(null);
+            super.onDestroyView();
+        }
+
+        protected abstract void onPreferenceToggled(String preferenceKey, boolean enabled);
+
+        @Override
+        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+            super.onCreateOptionsMenu(menu, inflater);
+            if (!TextUtils.isEmpty(mSettingsTitle) && mSettingsIntent != null) {
+                MenuItem menuItem = menu.add(mSettingsTitle);
+                menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+                menuItem.setIntent(mSettingsIntent);
+            }
+            if (!TextUtils.isEmpty(mAddPrintersTitle) && mAddPrintersIntent != null) {
+                MenuItem menuItem = menu.add(mAddPrintersTitle);
+                menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+                menuItem.setIntent(mAddPrintersIntent);
+            }
+        }
+
+        protected void onInstallActionBarToggleSwitch() {
+            mToggleSwitch = createAndAddActionBarToggleSwitch(getActivity());
+        }
+
+        private ToggleSwitch createAndAddActionBarToggleSwitch(Activity activity) {
+            ToggleSwitch toggleSwitch = new ToggleSwitch(activity);
+            final int padding = activity.getResources().getDimensionPixelSize(
+                    R.dimen.action_bar_switch_padding);
+            toggleSwitch.setPaddingRelative(0, 0, padding, 0);
+            activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
+                    ActionBar.DISPLAY_SHOW_CUSTOM);
+            activity.getActionBar().setCustomView(toggleSwitch,
+                    new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT,
+                            ActionBar.LayoutParams.WRAP_CONTENT,
+                            Gravity.CENTER_VERTICAL | Gravity.END));
+            return toggleSwitch;
+        }
+
+        protected void onProcessArguments(Bundle arguments) {
+            // Key.
+            mPreferenceKey = arguments.getString(EXTRA_PREFERENCE_KEY);
+            // Enabled.
+            final boolean enabled = arguments.getBoolean(EXTRA_CHECKED);
+            mToggleSwitch.setCheckedInternal(enabled);
+            // Title.
+            PreferenceActivity activity = (PreferenceActivity) getActivity();
+            if (!activity.onIsMultiPane() || activity.onIsHidingHeaders()) {
+                mOldActivityTitle = getActivity().getTitle();
+                String title = arguments.getString(EXTRA_TITLE);
+                getActivity().getActionBar().setTitle(title);
+            }
+        }
+    }
+
+    private static abstract class SettingsContentObserver extends ContentObserver {
+
+        public SettingsContentObserver(Handler handler) {
+            super(handler);
+        }
+
+        public void register(ContentResolver contentResolver) {
+            contentResolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.ENABLED_PRINT_SERVICES), false, this);
+        }
+
+        public void unregister(ContentResolver contentResolver) {
+            contentResolver.unregisterContentObserver(this);
+        }
+
+        @Override
+        public abstract void onChange(boolean selfChange, Uri uri);
+    }
+}
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 891a0c4..51e1a95 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -122,7 +122,8 @@
             R.id.system_section,
             R.id.date_time_settings,
             R.id.about_settings,
-            R.id.accessibility_settings
+            R.id.accessibility_settings,
+            R.id.print_settings
     };
 
     private SharedPreferences mDevelopmentPreferences;