Merge "Pin protect more screens."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2da0e4e..10c66d4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -973,6 +973,21 @@
                 android:resource="@id/accessibility_settings" />
         </activity>
 
+        <activity android:name="Settings$CaptioningSettingsActivity"
+                android:label="@string/accessibility_captioning_title"
+                android:taskAffinity="com.android.settings"
+                android:parentActivityName="Settings$AccessibilitySettingsActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <action android:name="android.settings.CAPTIONING_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.accessibility.ToggleCaptioningPreferenceFragment" />
+            <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+                android:resource="@id/accessibility_settings" />
+        </activity>
+
         <activity android:name="Settings$TextToSpeechSettingsActivity"
                 android:label="@string/tts_settings"
                 android:taskAffinity="com.android.settings"
diff --git a/res/drawable-hdpi/transparency.png b/res/drawable-hdpi/transparency.png
new file mode 100644
index 0000000..990c411
--- /dev/null
+++ b/res/drawable-hdpi/transparency.png
Binary files differ
diff --git a/res/drawable-mdpi/transparency.png b/res/drawable-mdpi/transparency.png
new file mode 100644
index 0000000..a915df3
--- /dev/null
+++ b/res/drawable-mdpi/transparency.png
Binary files differ
diff --git a/res/drawable-nodpi/caption_background.jpg b/res/drawable-nodpi/caption_background.jpg
new file mode 100644
index 0000000..0c5503a
--- /dev/null
+++ b/res/drawable-nodpi/caption_background.jpg
Binary files differ
diff --git a/res/drawable-xhdpi/transparency.png b/res/drawable-xhdpi/transparency.png
new file mode 100644
index 0000000..0808625
--- /dev/null
+++ b/res/drawable-xhdpi/transparency.png
Binary files differ
diff --git a/res/drawable-xxhdpi/transparency.png b/res/drawable-xxhdpi/transparency.png
new file mode 100644
index 0000000..5b61bac
--- /dev/null
+++ b/res/drawable-xxhdpi/transparency.png
Binary files differ
diff --git a/res/drawable/transparency_tileable.xml b/res/drawable/transparency_tileable.xml
new file mode 100644
index 0000000..0f676b1
--- /dev/null
+++ b/res/drawable/transparency_tileable.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/transparency"
+    android:tileMode="repeat"
+    android:dither="true" />
diff --git a/res/layout/captioning_preview.xml b/res/layout/captioning_preview.xml
new file mode 100644
index 0000000..b2e16fa
--- /dev/null
+++ b/res/layout/captioning_preview.xml
@@ -0,0 +1,49 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/captioning_preview_height" >
+
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:contentDescription="@null"
+            android:scaleType="centerCrop"
+            android:src="@drawable/caption_background" />
+
+        <com.android.settings.accessibility.CaptioningTextView
+            android:id="@+id/preview_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|start"
+            android:layout_margin="16dp"
+            android:text="@string/captioning_preview_text" />
+    </FrameLayout>
+
+    <fragment
+        android:id="@+id/properties_fragment"
+        android:name="com.android.settings.accessibility.CaptionPropertiesFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</LinearLayout>
diff --git a/res/layout/color_picker_item.xml b/res/layout/color_picker_item.xml
new file mode 100644
index 0000000..80a18f3
--- /dev/null
+++ b/res/layout/color_picker_item.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="96dp"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <ImageView
+        android:id="@+id/color_swatch"
+        android:layout_width="96dp"
+        android:layout_height="96dp"
+        android:contentDescription="@null" />
+
+    <TextView
+        android:id="@+id/summary"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center" />
+
+</LinearLayout>
diff --git a/res/layout/grid_picker_dialog.xml b/res/layout/grid_picker_dialog.xml
new file mode 100644
index 0000000..f152ba1
--- /dev/null
+++ b/res/layout/grid_picker_dialog.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <GridView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="400dp"
+        android:columnWidth="112dp"
+        android:numColumns="auto_fit"
+        android:stretchMode="spacingWidthUniform"
+        android:verticalSpacing="16dp"
+        android:paddingTop="16dp" />
+
+</LinearLayout>
diff --git a/res/layout/preference_color.xml b/res/layout/preference_color.xml
new file mode 100644
index 0000000..de4b832
--- /dev/null
+++ b/res/layout/preference_color.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/color_preview"
+    android:layout_width="?android:attr/listPreferredItemHeight"
+    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:contentDescription="@null" />
diff --git a/res/layout/preset_picker_item.xml b/res/layout/preset_picker_item.xml
new file mode 100644
index 0000000..6f99980
--- /dev/null
+++ b/res/layout/preset_picker_item.xml
@@ -0,0 +1,42 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="96dp"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <FrameLayout
+        android:layout_width="96dp"
+        android:layout_height="96dp"
+        android:background="@drawable/transparency_tileable" >
+
+        <com.android.settings.accessibility.CaptioningTextView
+            android:id="@+id/preview"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:text="@string/captioning_preview_characters" />
+    </FrameLayout>
+
+    <TextView
+        android:id="@+id/summary"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center" />
+
+</LinearLayout>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 498aa4d..288d4e3 100755
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -22,4 +22,6 @@
     <dimen name="keyguard_appwidget_picker_margin_left">2dip</dimen>
     <dimen name="keyguard_appwidget_picker_margin_right">2dip</dimen>
     <integer name="keyguard_appwidget_picker_cols">2</integer>
+
+    <dimen name="captioning_preview_height">100dp</dimen>
 </resources>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index f60268a..dbdc4b3 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -706,6 +706,144 @@
         <item>1500</item>
     </string-array>
 
+    <!-- Titles for captioning typeface preference. [CHAR LIMIT=35] -->
+    <string-array name="captioning_typeface_selector_titles">
+        <item>Default</item>
+        <item>Sans-serif</item>
+        <item>Sans-serif condensed</item>
+        <item>Serif</item>
+        <item>Monospace</item>
+    </string-array>
+
+    <!-- Values for captioning typeface preference. -->
+    <string-array name="captioning_typeface_selector_values" translatable="false" >
+        <item></item>
+        <item>sans-serif</item>
+        <item>sans-serif-condensed</item>
+        <item>serif</item>
+        <item>monospace</item>
+    </string-array>
+
+    <!-- Titles for captioning font size preference. [CHAR LIMIT=35] -->
+    <string-array name="captioning_font_size_selector_titles">
+        <item>Very small</item>
+        <item>Small</item>
+        <item>Normal</item>
+        <item>Large</item>
+        <item>Very large</item>
+    </string-array>
+
+    <!-- Values for captioning font size preference. -->
+    <string-array name="captioning_font_size_selector_values" translatable="false" >
+        <item>6.0</item>
+        <item>12.0</item>
+        <item>24.0</item>
+        <item>32.0</item>
+        <item>48.0</item>
+    </string-array>
+
+    <!-- Titles for captioning character edge type preference. [CHAR LIMIT=35] -->
+    <string-array name="captioning_edge_type_selector_titles">
+        <item>None</item>
+        <item>Outline</item>
+        <item>Drop shadow</item>
+    </string-array>
+
+    <!-- Values for captioning character edge type preference. -->
+    <integer-array name="captioning_edge_type_selector_values" translatable="false" >
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+    </integer-array>
+
+    <!-- Titles for captioning color preference. -->
+    <string-array name="captioning_color_selector_titles" translatable="false" >
+        <item>@string/color_white</item>
+        <item>@string/color_gray</item>
+        <item>@string/color_black</item>
+        <item>@string/color_red</item>
+        <item>@string/color_yellow</item>
+        <item>@string/color_green</item>
+        <item>@string/color_cyan</item>
+        <item>@string/color_blue</item>
+        <item>@string/color_magenta</item>
+    </string-array>
+
+    <!-- Values for captioning color preference. -->
+    <integer-array name="captioning_color_selector_values" translatable="false" >
+        <item>0xFFFFFFFF</item>
+        <item>0xFF808080</item>
+        <item>0xFF000000</item>
+        <item>0xFFFF0000</item>
+        <item>0xFFFFFF00</item>
+        <item>0xFF00FF00</item>
+        <item>0xFF00FFFF</item>
+        <item>0xFF0000FF</item>
+        <item>0xFFFF00FF</item>
+    </integer-array>
+
+    <!-- Titles for captioning background color preference. -->
+    <string-array name="captioning_background_color_selector_titles" translatable="false" >
+        <item>@string/color_none</item>
+        <item>@string/color_white</item>
+        <item>@string/color_gray</item>
+        <item>@string/color_black</item>
+        <item>@string/color_red</item>
+        <item>@string/color_yellow</item>
+        <item>@string/color_green</item>
+        <item>@string/color_cyan</item>
+        <item>@string/color_blue</item>
+        <item>@string/color_magenta</item>
+    </string-array>
+
+    <!-- Values for captioning background color preference. -->
+    <integer-array name="captioning_background_color_selector_values" translatable="false" >
+        <item>0x00000000</item>
+        <item>0xFFFFFFFF</item>
+        <item>0xFF808080</item>
+        <item>0xFF000000</item>
+        <item>0xFFFF0000</item>
+        <item>0xFFFFFF00</item>
+        <item>0xFF00FF00</item>
+        <item>0xFF00FFFF</item>
+        <item>0xFF0000FF</item>
+        <item>0xFFFF00FF</item>
+    </integer-array>
+
+    <!-- Titles for captioning opacity preference. [CHAR LIMIT=35] -->
+    <string-array name="captioning_opacity_selector_titles" >
+        <item>25%</item>
+        <item>50%</item>
+        <item>75%</item>
+        <item>100%</item>
+    </string-array>
+
+    <!-- Values for captioning opacity preference. -->
+    <integer-array name="captioning_opacity_selector_values" translatable="false" >
+        <item>0x40FFFFFF</item>
+        <item>0x80FFFFFF</item>
+        <item>0xC0FFFFFF</item>
+        <item>0xFFFFFFFF</item>
+    </integer-array>
+
+    <!-- Titles for captioning text style preset preference. [CHAR LIMIT=35] -->
+    <string-array name="captioning_preset_selector_titles" >
+        <item>Black on white</item>
+        <item>White on black</item>
+        <item>Yellow on black</item>
+        <item>Yellow on blue</item>
+        <item>Custom</item>
+    </string-array>
+
+    <!-- Values for captioning text style preset preference. -->
+    <integer-array name="captioning_preset_selector_values" translatable="false" >
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+        <item>-1</item>
+    </integer-array>
+
     <!-- Titles for runtime selection preference. [CHAR LIMIT=35] -->
     <string-array name="select_runtime_titles">
         <item>Dalvik</item>
@@ -984,4 +1122,5 @@
         <!-- Always allow app to send to premium SMS short code. -->
         <item>Always allow</item>
     </string-array>
+
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d68342d..cf47657 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -69,4 +69,6 @@
     <!-- Minimum width for the popup for updating a user's photo. -->
     <dimen name="update_user_photo_popup_min_width">300dip</dimen>
 
+    <dimen name="captioning_preview_height">200dp</dimen>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 49a888b..a4a3aec 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2646,7 +2646,7 @@
         <li>Disabled apps</li>\n
         <li>Disabled app notifications</li>\n
         <li>Default applications for actions</li>\n
-        <li>Background data restrictions for apps</li>\n\n
+        <li>Background data restrictions for apps</li>\n
         <li>Any permission restrictions</li>\n\n
         You will not lose any app data.</string>
     <!-- [CHAR LIMIT=25] Manage applications screen, menu item.  Confirmation button of dialog to confirm resetting user's app preferences. -->
@@ -3202,10 +3202,12 @@
     <string name="accessibility_settings">Accessibility</string>
     <!-- Settings title for accessibility settings screen -->
     <string name="accessibility_settings_title">Accessibility settings</string>
-    <!--  Title for the accessibility preference category of accessibility services. [CHAR LIMIT=25] -->
+    <!-- Title for the accessibility preference category of accessibility services. [CHAR LIMIT=25] -->
     <string name="accessibility_services_title">Services</string>
     <!-- Title for the accessibility preference category of system related preferences. [CHAR LIMIT=25] -->
     <string name="accessibility_system_title">System</string>
+    <!-- Title for the accessibility preference screen to enable video captioning. [CHAR LIMIT=35] -->
+    <string name="accessibility_captioning_title">Captions</string>
     <!-- Title for the accessibility preference screen to enable screen magnification. [CHAR LIMIT=35] -->
     <string name="accessibility_screen_magnification_title">Magnification gestures</string>
     <!-- Summary for the accessibility preference screen to enable screen magnification. [CHAR LIMIT=none] -->
@@ -3243,6 +3245,59 @@
     <!-- Summary for the disabled state of an accessibility feature. [CHAR LIMIT=10] -->
     <string name="accessibility_feature_state_off">Off</string>
 
+    <!-- Title for the preference category containing the video caption preview. [CHAR LIMIT=35] -->
+    <string name="captioning_preview_title">Preview</string>
+    <!-- Title for the preference catgeory containing standard video caption options. [CHAR LIMIT=35] -->
+    <string name="captioning_standard_options_title">Standard options</string>
+    <!-- Title for the preference to change video caption locale. [CHAR LIMIT=35] -->
+    <string name="captioning_locale">Language</string>
+    <!-- Title for the preference to change video caption text size. [CHAR LIMIT=35] -->
+    <string name="captioning_text_size">Text size</string>
+    <!-- Title for the preference category containing video caption property presets. [CHAR LIMIT=35] -->
+    <string name="captioning_preset">Caption style</string>
+    <!-- Title for the preference catgeory containing custom video caption properties. [CHAR LIMIT=35] -->
+    <string name="captioning_custom_options_title">Custom options</string>
+    <!-- Title for the preference to change video caption background color. [CHAR LIMIT=35] -->
+    <string name="captioning_background_color">Background color</string>
+    <!-- Title for the preference to change video caption background opacity. [CHAR LIMIT=35] -->
+    <string name="captioning_background_opacity">Background opacity</string>
+    <!-- Title for the preference to change video caption text color. [CHAR LIMIT=35] -->
+    <string name="captioning_foreground_color">Text color</string>
+    <!-- Title for the preference to change video caption edge color. [CHAR LIMIT=35] -->
+    <string name="captioning_edge_color">Edge color</string>
+    <!-- Title for the preference to change video caption edge type. [CHAR LIMIT=35] -->
+    <string name="captioning_edge_type">Edge type</string>
+    <!-- Title for the preference to change video caption font family (ex. monospace, sans-serif). [CHAR LIMIT=35] -->
+    <string name="captioning_typeface">Font family</string>
+    <!-- Sample text for previewing video caption preferences. [CHAR LIMIT=NONE] -->
+    <string name="captioning_preview_text">Captions will look like this</string>
+    <!-- Sample characters for previewing video caption preferences. [CHAR LIMIT=2] -->
+    <string name="captioning_preview_characters">Aa</string>
+
+    <!-- Label for the default device locale. [CHAR LIMIT=35] -->
+    <string name="locale_default">Default</string>
+
+    <!-- Label for no color. [CHAR LIMIT=35] -->
+    <string name="color_none">None</string>
+    <!-- Label for the color white. [CHAR LIMIT=35] -->
+    <string name="color_white">White</string>
+    <!-- Label for the color gray. [CHAR LIMIT=35] -->
+    <string name="color_gray">Gray</string>
+    <!-- Label for the color black. [CHAR LIMIT=35] -->
+    <string name="color_black">Black</string>
+    <!-- Label for the color red. [CHAR LIMIT=35] -->
+    <string name="color_red">Red</string>
+    <!-- Label for the color green. [CHAR LIMIT=35] -->
+    <string name="color_green">Green</string>
+    <!-- Label for the color blue. [CHAR LIMIT=35] -->
+    <string name="color_blue">Blue</string>
+    <!-- Label for the color cyan. [CHAR LIMIT=35] -->
+    <string name="color_cyan">Cyan</string>
+    <!-- Label for the color yellow. [CHAR LIMIT=35] -->
+    <string name="color_yellow">Yellow</string>
+    <!-- Label for the color magenta. [CHAR LIMIT=35] -->
+    <string name="color_magenta">Magenta</string>
+
     <!-- Title for a warning about security implications of enabling an accessibility
          service. [CHAR LIMIT=NONE] -->
     <string name="enable_service_title">Use
@@ -3569,12 +3624,14 @@
          selected language is fully supported by the engine [CHAR LIMIT=150]-->
     <string name="tts_status_ok"><xliff:g id="locale" example="English (United States)">%1$s</xliff:g> is fully supported</string>
     <!-- On main TTS Settings screen, current TTS engine status for the current default language,
-         selected language is supported by the engine only if there's a working network connection
-	 [CHAR LIMIT=150]-->
+         selected language is supported by the engine only if there's a working network connection [CHAR LIMIT=150] -->
     <string name="tts_status_requires_network"><xliff:g id="locale" example="English (United States)">%1$s</xliff:g> requires network connection</string>
     <!-- On main TTS Settings screen, current TTS engine status for the current default language,
-         selected language is not supported by the engine [CHAR LIMIT=150]-->
+         selected language is not supported by the engine [CHAR LIMIT=150] -->
     <string name="tts_status_not_supported"><xliff:g id="locale" example="English (United States)">%1$s</xliff:g> is not supported</string>
+    <!-- On main TTS Settings screen, current TTS engine status for the current default language,
+	 tts engine is queried for status [CHAR LIMIT=150] -->
+    <string name="tts_status_checking">Checking...</string>
     <!-- On main TTS Settings screen, text for divider under which all TTS engines are listed -->
     <string name="tts_engines_section">Engines</string>
     <!-- On main TTS Settings screen, text preceded by the TTS engine name, clicking this button will launch the engine settings -->
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
index e2328d3..7b599b8 100644
--- a/res/xml/accessibility_settings.xml
+++ b/res/xml/accessibility_settings.xml
@@ -28,6 +28,11 @@
             android:title="@string/accessibility_system_title">
 
         <PreferenceScreen
+            android:fragment="com.android.settings.accessibility.ToggleCaptioningPreferenceFragment"
+            android:key="captioning_preference_screen"
+            android:title="@string/accessibility_captioning_title" />
+
+        <PreferenceScreen
             android:fragment="com.android.settings.accessibility.ToggleScreenMagnificationPreferenceFragment"
             android:key="screen_magnification_preference_screen"
             android:title="@string/accessibility_screen_magnification_title"/>
diff --git a/res/xml/captioning_settings.xml b/res/xml/captioning_settings.xml
new file mode 100644
index 0000000..664505a
--- /dev/null
+++ b/res/xml/captioning_settings.xml
@@ -0,0 +1,87 @@
+<?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:fragment="com.android.settings.accessibility.ToggleCaptioningPreferenceFragment"
+    android:key="captioning_preference_screen"
+    android:title="@string/accessibility_captioning_title" >
+
+    <PreferenceCategory
+        android:key="standard"
+        android:title="@string/captioning_standard_options_title" >
+        <com.android.settings.accessibility.LocalePreference
+            android:key="captioning_locale"
+            android:persistent="false"
+            android:summary="%s"
+            android:title="@string/captioning_locale" />
+
+        <ListPreference
+            android:entries="@array/captioning_font_size_selector_titles"
+            android:entryValues="@array/captioning_font_size_selector_values"
+            android:key="captioning_font_size"
+            android:persistent="false"
+            android:summary="%s"
+            android:title="@string/captioning_text_size" />
+
+        <com.android.settings.accessibility.PresetPreference
+            android:key="captioning_preset"
+            android:persistent="false"
+            android:title="@string/captioning_preset" />
+    </PreferenceCategory>
+    <PreferenceCategory
+        android:key="custom"
+        android:title="@string/captioning_custom_options_title" >
+        <ListPreference
+            android:dependency="captioning_preset"
+            android:entries="@array/captioning_typeface_selector_titles"
+            android:entryValues="@array/captioning_typeface_selector_values"
+            android:key="captioning_typeface"
+            android:persistent="false"
+            android:summary="%s"
+            android:title="@string/captioning_typeface" />
+
+        <com.android.settings.accessibility.ColorPreference
+            android:dependency="captioning_preset"
+            android:key="captioning_foreground_color"
+            android:persistent="false"
+            android:title="@string/captioning_foreground_color" />
+
+        <com.android.settings.accessibility.EdgeTypePreference
+            android:dependency="captioning_preset"
+            android:key="captioning_edge_type"
+            android:persistent="false"
+            android:title="@string/captioning_edge_type" />
+
+        <com.android.settings.accessibility.ColorPreference
+            android:dependency="captioning_edge_type"
+            android:key="captioning_edge_color"
+            android:persistent="false"
+            android:title="@string/captioning_edge_color" />
+        <com.android.settings.accessibility.ColorPreference
+            android:dependency="captioning_preset"
+            android:key="captioning_background_color"
+            android:persistent="false"
+            android:title="@string/captioning_background_color" />
+        <com.android.settings.accessibility.ColorPreference
+            android:dependency="captioning_background_color"
+            android:key="captioning_background_opacity"
+            android:persistent="false"
+            android:title="@string/captioning_background_opacity" />
+    </PreferenceCategory>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/settings_headers.xml b/res/xml/settings_headers.xml
index 9a1d3b8..867fc19 100644
--- a/res/xml/settings_headers.xml
+++ b/res/xml/settings_headers.xml
@@ -97,13 +97,6 @@
         android:title="@string/applications_settings"
         android:id="@+id/application_settings" />
 
-    <!-- Manage restrictions -->
-    <header
-        android:fragment="com.android.settings.users.RestrictionSettings"
-        android:icon="@drawable/ic_settings_multiuser"
-        android:title="@string/restriction_settings_title"
-        android:id="@+id/restriction_settings" />
-
     <!-- Manage users -->
     <header
         android:fragment="com.android.settings.users.UserSettings"
@@ -118,7 +111,6 @@
         <intent android:action="com.android.settings.MANUFACTURER_APPLICATION_SETTING" />
     </header>
 
-
     <!-- PERSONAL -->
     <header android:id="@+id/personal_section"
         android:title="@string/header_category_personal" />
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 720af59..4221059 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -136,7 +136,6 @@
             R.id.location_settings,
             R.id.security_settings,
             R.id.language_settings,
-            R.id.restriction_settings,
             R.id.user_settings,
             R.id.account_settings,
             R.id.account_add,
@@ -560,11 +559,6 @@
                 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
                     target.remove(i);
                 }
-            } else if (id == R.id.restriction_settings) {
-                if (um.isLinkedUser()
-                        || um.hasUserRestriction(UserManager.DISALLOW_APP_RESTRICTIONS)) {
-                    target.remove(i);
-                }
             }
 
             if (i < target.size() && target.get(i) == header
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index d2198a7..5834f99 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -95,6 +95,8 @@
             "select_long_press_timeout_preference";
     private static final String ENABLE_ACCESSIBILITY_GESTURE_PREFERENCE_SCREEN =
             "enable_global_gesture_preference_screen";
+    private static final String CAPTIONING_PREFERENCE_SCREEN =
+            "captioning_preference_screen";
     private static final String DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN =
             "screen_magnification_preference_screen";
 
@@ -189,6 +191,7 @@
     private CheckBoxPreference mToggleSpeakPasswordPreference;
     private ListPreference mSelectLongPressTimeoutPreference;
     private Preference mNoServicesMessagePreference;
+    private PreferenceScreen mCaptioningPreferenceScreen;
     private PreferenceScreen mDisplayMagnificationPreferenceScreen;
     private PreferenceScreen mGlobalGesturePreferenceScreen;
 
@@ -360,6 +363,10 @@
             }
         }
 
+        // Captioning.
+        mCaptioningPreferenceScreen = (PreferenceScreen) findPreference(
+                CAPTIONING_PREFERENCE_SCREEN);
+
         // Display magnification.
         mDisplayMagnificationPreferenceScreen = (PreferenceScreen) findPreference(
                 DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN);
@@ -507,6 +514,15 @@
         mSelectLongPressTimeoutPreference.setValue(value);
         mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValuetoTitleMap.get(value));
 
+        // Captioning.
+        final boolean captioningEnabled = Settings.Secure.getInt(getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 0) == 1;
+        if (captioningEnabled) {
+            mCaptioningPreferenceScreen.setSummary(R.string.accessibility_feature_state_on);
+        } else {
+            mCaptioningPreferenceScreen.setSummary(R.string.accessibility_feature_state_off);
+        }
+
         // Screen magnification.
         final boolean magnificationEnabled = Settings.Secure.getInt(getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1;
diff --git a/src/com/android/settings/accessibility/AccessibilityUtils.java b/src/com/android/settings/accessibility/AccessibilityUtils.java
index fd4a34f..66a3ed2 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtils.java
@@ -1,37 +1,77 @@
+/*
+ * 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.accessibility;
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.provider.Settings;
-import android.provider.Settings.Secure;
 import android.text.TextUtils.SimpleStringSplitter;
 
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.Locale;
 import java.util.Set;
 
 /**
- * TODO: Insert description here. (generated by alanv)
+ * Utility methods used within accessibility settings.
  */
-public class AccessibilityUtils {
-
+class AccessibilityUtils {
+    /**
+     * @return the set of enabled accessibility services
+     */
     static Set<ComponentName> getEnabledServicesFromSettings(Context context) {
-        String enabledServicesSetting = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+        final String enabledServicesSetting = Settings.Secure.getString(
+                context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
         if (enabledServicesSetting == null) {
-            enabledServicesSetting = "";
+            return Collections.emptySet();
         }
-        Set<ComponentName> enabledServices = new HashSet<ComponentName>();
-        SimpleStringSplitter colonSplitter = AccessibilitySettings.sStringColonSplitter;
+
+        final Set<ComponentName> enabledServices = new HashSet<ComponentName>();
+        final SimpleStringSplitter colonSplitter = AccessibilitySettings.sStringColonSplitter;
         colonSplitter.setString(enabledServicesSetting);
+
         while (colonSplitter.hasNext()) {
-            String componentNameString = colonSplitter.next();
-            ComponentName enabledService = ComponentName.unflattenFromString(
+            final String componentNameString = colonSplitter.next();
+            final ComponentName enabledService = ComponentName.unflattenFromString(
                     componentNameString);
             if (enabledService != null) {
                 enabledServices.add(enabledService);
             }
         }
+
         return enabledServices;
     }
 
+    /**
+     * @return a localized version of the text resource specified by resId
+     */
+    static CharSequence getTextForLocale(Context context, Locale locale, int resId) {
+        final Resources res = context.getResources();
+        final Configuration config = res.getConfiguration();
+        final Locale prevLocale = config.locale;
+        try {
+            config.locale = locale;
+            res.updateConfiguration(config, null);
+            return res.getText(resId);
+        } finally {
+            config.locale = prevLocale;
+            res.updateConfiguration(config, null);
+        }
+    }
 }
diff --git a/src/com/android/settings/accessibility/CaptionPropertiesFragment.java b/src/com/android/settings/accessibility/CaptionPropertiesFragment.java
new file mode 100644
index 0000000..1b53374
--- /dev/null
+++ b/src/com/android/settings/accessibility/CaptionPropertiesFragment.java
@@ -0,0 +1,208 @@
+/*
+ * 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.accessibility;
+
+import android.content.ContentResolver;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.provider.Settings;
+import android.view.accessibility.CaptioningManager;
+import android.view.accessibility.CaptioningManager.CaptionStyle;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
+
+/**
+ * Settings fragment containing captioning properties.
+ */
+public class CaptionPropertiesFragment extends SettingsPreferenceFragment
+        implements OnPreferenceChangeListener, OnValueChangedListener {
+    private ToggleCaptioningPreferenceFragment mParent;
+
+    // Standard options.
+    private LocalePreference mLocale;
+    private ListPreference mFontSize;
+    private PresetPreference mPreset;
+
+    // Custom options.
+    private ListPreference mTypeface;
+    private ColorPreference mForegroundColor;
+    private EdgeTypePreference mEdgeType;
+    private ColorPreference mEdgeColor;
+    private ColorPreference mBackgroundColor;
+    private ColorPreference mBackgroundOpacity;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        addPreferencesFromResource(R.xml.captioning_settings);
+        initializeAllPreferences();
+        updateAllPreferences();
+        installUpdateListeners();
+    }
+
+    /**
+     * Sets the parent fragment, which is used to update the live preview.
+     *
+     * @param parent the parent fragment
+     */
+    public void setParent(ToggleCaptioningPreferenceFragment parent) {
+        mParent = parent;
+    }
+
+    private void initializeAllPreferences() {
+        final Resources res = getResources();
+        final int[] presetValues = res.getIntArray(R.array.captioning_preset_selector_values);
+        final String[] presetTitles = res.getStringArray(R.array.captioning_preset_selector_titles);
+        mPreset = (PresetPreference) findPreference("captioning_preset");
+        mPreset.setValues(presetValues);
+        mPreset.setTitles(presetTitles);
+
+        final int[] colorValues = res.getIntArray(R.array.captioning_color_selector_values);
+        final String[] colorTitles = res.getStringArray(R.array.captioning_color_selector_titles);
+        mForegroundColor = (ColorPreference) findPreference("captioning_foreground_color");
+        mForegroundColor.setTitles(colorTitles);
+        mForegroundColor.setValues(colorValues);
+        mEdgeColor = (ColorPreference) findPreference("captioning_edge_color");
+        mEdgeColor.setTitles(colorTitles);
+        mEdgeColor.setValues(colorValues);
+
+        final int[] bgColorValues = res.getIntArray(
+                R.array.captioning_background_color_selector_values);
+        final String[] bgColorTitles = res.getStringArray(
+                R.array.captioning_background_color_selector_titles);
+        mBackgroundColor = (ColorPreference) findPreference("captioning_background_color");
+        mBackgroundColor.setTitles(bgColorTitles);
+        mBackgroundColor.setValues(bgColorValues);
+
+        final int[] opacityValues = res.getIntArray(R.array.captioning_opacity_selector_values);
+        final String[] opacityTitles = res.getStringArray(
+                R.array.captioning_opacity_selector_titles);
+        mBackgroundOpacity = (ColorPreference) findPreference("captioning_background_opacity");
+        mBackgroundOpacity.setTitles(opacityTitles);
+        mBackgroundOpacity.setValues(opacityValues);
+
+        mEdgeType = (EdgeTypePreference) findPreference("captioning_edge_type");
+        mTypeface = (ListPreference) findPreference("captioning_typeface");
+        mFontSize = (ListPreference) findPreference("captioning_font_size");
+        mLocale = (LocalePreference) findPreference("captioning_locale");
+    }
+
+    private void installUpdateListeners() {
+        mPreset.setOnValueChangedListener(this);
+        mForegroundColor.setOnValueChangedListener(this);
+        mEdgeColor.setOnValueChangedListener(this);
+        mBackgroundColor.setOnValueChangedListener(this);
+        mBackgroundOpacity.setOnValueChangedListener(this);
+        mEdgeType.setOnValueChangedListener(this);
+
+        mTypeface.setOnPreferenceChangeListener(this);
+        mFontSize.setOnPreferenceChangeListener(this);
+        mLocale.setOnPreferenceChangeListener(this);
+    }
+
+    private void updateAllPreferences() {
+        final ContentResolver cr = getContentResolver();
+        final int preset = CaptionStyle.getRawPreset(cr);
+        mPreset.setValue(preset);
+
+        final float fontSize = CaptioningManager.getFontSize(cr);
+        mFontSize.setValue(Float.toString(fontSize));
+
+        final CaptionStyle attrs = CaptionStyle.getCustomStyle(cr);
+        mForegroundColor.setValue(attrs.foregroundColor);
+        mEdgeType.setValue(attrs.edgeType);
+        mEdgeColor.setValue(attrs.edgeColor);
+
+        final int backgroundColor = attrs.backgroundColor;
+        final int bgColor;
+        final int bgAlpha;
+        if (Color.alpha(backgroundColor) == 0) {
+            bgColor = Color.TRANSPARENT;
+            bgAlpha = (backgroundColor & 0xFF) << 24;
+        } else {
+            bgColor = backgroundColor | 0xFF000000;
+            bgAlpha = backgroundColor & 0xFF000000;
+        }
+        mBackgroundColor.setValue(bgColor);
+        mBackgroundOpacity.setValue(bgAlpha | 0xFFFFFF);
+
+        final String rawTypeface = attrs.mRawTypeface;
+        mTypeface.setValue(rawTypeface == null ? "" : rawTypeface);
+
+        final String rawLocale = CaptioningManager.getRawLocale(cr);
+        mLocale.setValue(rawLocale == null ? "" : rawLocale);
+    }
+
+    private void refreshPreviewText() {
+        if (mParent != null) {
+            mParent.refreshPreviewText();
+        }
+    }
+
+    @Override
+    public void onValueChanged(ListDialogPreference preference, int value) {
+        final ContentResolver cr = getActivity().getContentResolver();
+        if (mForegroundColor == preference) {
+            Settings.Secure.putInt(
+                    cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR, value);
+        } else if (mBackgroundColor == preference || mBackgroundOpacity == preference) {
+            final int bgColor = mBackgroundColor.getValue();
+            final int bgAlpha = mBackgroundOpacity.getValue();
+            final int argb;
+            if (Color.alpha(bgColor) == 0) {
+                argb = Color.alpha(bgAlpha);
+            } else {
+                argb = bgColor & 0x00FFFFFF | bgAlpha & 0xFF000000;
+            }
+            Settings.Secure.putInt(
+                    cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR, argb);
+        } else if (mEdgeColor == preference) {
+            Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_COLOR, value);
+        } else if (mPreset == preference) {
+            Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET, value);
+        } else if (mEdgeType == preference) {
+            Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_TYPE, value);
+        }
+
+        refreshPreviewText();
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object value) {
+        final ContentResolver cr = getActivity().getContentResolver();
+        if (mTypeface == preference) {
+            Settings.Secure.putString(
+                    cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE, (String) value);
+        } else if (mFontSize == preference) {
+            Settings.Secure.putFloat(
+                    cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SIZE,
+                    Float.parseFloat((String) value));
+        } else if (mLocale == preference) {
+            Settings.Secure.putString(
+                    cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_LOCALE, (String) value);
+        }
+
+        refreshPreviewText();
+        return true;
+    }
+}
diff --git a/src/com/android/settings/accessibility/CaptioningTextView.java b/src/com/android/settings/accessibility/CaptioningTextView.java
new file mode 100644
index 0000000..7bb677e
--- /dev/null
+++ b/src/com/android/settings/accessibility/CaptioningTextView.java
@@ -0,0 +1,309 @@
+/*
+ * 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.accessibility;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Cap;
+import android.graphics.Paint.Join;
+import android.graphics.Paint.Style;
+import android.os.Parcel;
+import android.support.v4.view.ViewCompat;
+import android.text.Editable;
+import android.text.ParcelableSpan;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.CharacterStyle;
+import android.text.style.UpdateAppearance;
+import android.util.AttributeSet;
+import android.view.accessibility.CaptioningManager;
+import android.view.accessibility.CaptioningManager.CaptionStyle;
+import android.widget.TextView;
+
+public class CaptioningTextView extends TextView {
+    private MutableBackgroundColorSpan mBackgroundSpan;
+    private ColorStateList mOutlineColorState;
+    private float mOutlineWidth;
+    private int mOutlineColor;
+
+    private int mEdgeType = CaptionStyle.EDGE_TYPE_NONE;
+    private int mEdgeColor = Color.TRANSPARENT;
+    private float mEdgeWidth = 0;
+
+    private boolean mHasBackground = false;
+
+    public CaptioningTextView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public CaptioningTextView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CaptioningTextView(Context context) {
+        super(context);
+    }
+
+    public void applyStyleAndFontSize(int styleId) {
+        final Context context = mContext;
+        final ContentResolver cr = context.getContentResolver();
+        final CaptionStyle style;
+        if (styleId == CaptionStyle.PRESET_CUSTOM) {
+            style = CaptionStyle.getCustomStyle(cr);
+        } else {
+            style = CaptionStyle.PRESETS[styleId];
+        }
+
+        setTextColor(style.foregroundColor);
+        setBackgroundColor(style.backgroundColor);
+        setTypeface(style.getTypeface());
+
+        // Clears all outlines.
+        applyEdge(style.edgeType, style.edgeColor, 4.0f);
+
+        final float fontSize = CaptioningManager.getFontSize(cr);
+        if (fontSize != 0) {
+            setTextSize(fontSize);
+        }
+    }
+
+    /**
+     * Applies an edge preset using a combination of {@link #setOutlineLayer}
+     * and {@link #setShadowLayer}. Any subsequent calls to either of these
+     * methods will invalidate the applied preset.
+     *
+     * @param type Type of edge to apply, one of:
+     *            <ul>
+     *            <li>{@link CaptionStyle#EDGE_TYPE_NONE}
+     *            <li>{@link CaptionStyle#EDGE_TYPE_OUTLINE}
+     *            <li>{@link CaptionStyle#EDGE_TYPE_DROP_SHADOW}
+     *            </ul>
+     * @param color Edge color as a packed 32-bit ARGB color.
+     * @param width Width of the edge in pixels.
+     */
+    public void applyEdge(int type, int color, float width) {
+        if (mEdgeType != type || mEdgeColor != color || mEdgeWidth != width) {
+            final int textColor = getTextColors().getDefaultColor();
+            switch (type) {
+                case CaptionStyle.EDGE_TYPE_DROP_SHADOW:
+                    setOutlineLayer(0, 0);
+                    super.setShadowLayer(width, width, width, color);
+                    break;
+                case CaptionStyle.EDGE_TYPE_OUTLINE:
+                    setOutlineLayer(width, color);
+                    super.setShadowLayer(0, 0, 0, 0);
+                    break;
+                default:
+                    super.setShadowLayer(0, 0, 0, 0);
+                    setOutlineLayer(0, 0);
+            }
+
+            mEdgeType = type;
+            mEdgeColor = color;
+            mEdgeWidth = width;
+        }
+    }
+
+    @Override
+    public void setShadowLayer(float radius, float dx, float dy, int color) {
+        mEdgeType = CaptionStyle.EDGE_TYPE_NONE;
+
+        super.setShadowLayer(radius, dx, dy, color);
+    }
+
+    /**
+     * Gives the text an outline of the specified pixel width and color.
+     */
+    public void setOutlineLayer(float width, int color) {
+        width *= 2.0f;
+
+        mEdgeType = CaptionStyle.EDGE_TYPE_NONE;
+
+        if (mOutlineColor != color || mOutlineWidth != width) {
+            mOutlineColorState = ColorStateList.valueOf(color);
+            mOutlineColor = color;
+            mOutlineWidth = width;
+            invalidate();
+
+            // TODO: Remove after display list bug is fixed.
+            if (width > 0 && Color.alpha(color) != 0) {
+                setLayerType(ViewCompat.LAYER_TYPE_SOFTWARE, null);
+            } else {
+                setLayerType(ViewCompat.LAYER_TYPE_HARDWARE, null);
+            }
+        }
+    }
+
+    /**
+     * @return the color of the outline layer
+     * @see #setOutlineLayer(float, int)
+     */
+    public int getOutlineColor() {
+        return mOutlineColor;
+    }
+
+    /**
+     * @return the width of the outline layer
+     * @see #setOutlineLayer(float, int)
+     */
+    public float getOutlineWidth() {
+        return mOutlineWidth;
+    }
+
+    @Override
+    public Editable getEditableText() {
+        final CharSequence text = getText();
+        if (text instanceof Editable) {
+            return (Editable) text;
+        }
+
+        setText(text, BufferType.EDITABLE);
+        return (Editable) getText();
+    }
+
+    @Override
+    public void setBackgroundColor(int color) {
+        if (Color.alpha(color) == 0) {
+            if (mHasBackground) {
+                mHasBackground = false;
+                getEditableText().removeSpan(mBackgroundSpan);
+            }
+        } else {
+            if (mBackgroundSpan == null) {
+                mBackgroundSpan = new MutableBackgroundColorSpan(color);
+            } else {
+                mBackgroundSpan.setColor(color);
+            }
+
+            if (mHasBackground) {
+                invalidate();
+            } else {
+                mHasBackground = true;
+                getEditableText().setSpan(mBackgroundSpan, 0, length(), 0);
+            }
+        }
+    }
+
+    @Override
+    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+        super.onTextChanged(text, start, lengthBefore, lengthAfter);
+
+        if (mBackgroundSpan != null) {
+            getEditableText().setSpan(mBackgroundSpan, 0, lengthAfter, 0);
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas c) {
+        if (mOutlineWidth > 0 && Color.alpha(mOutlineColor) > 0) {
+            final TextPaint textPaint = getPaint();
+            final Paint.Style previousStyle = textPaint.getStyle();
+            final ColorStateList previousColors = getTextColors();
+            textPaint.setStyle(Style.STROKE);
+            textPaint.setStrokeWidth(mOutlineWidth);
+            textPaint.setStrokeCap(Cap.ROUND);
+            textPaint.setStrokeJoin(Join.ROUND);
+
+            setTextColor(mOutlineColorState);
+
+            // Remove the shadow.
+            final float shadowRadius = getShadowRadius();
+            final float shadowDx = getShadowDx();
+            final float shadowDy = getShadowDy();
+            final int shadowColor = getShadowColor();
+            if (shadowRadius > 0) {
+                setShadowLayer(0, 0, 0, 0);
+            }
+
+            // Draw outline and background only.
+            super.onDraw(c);
+
+            // Restore the shadow.
+            if (shadowRadius > 0) {
+                setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
+            }
+
+            // Restore original settings.
+            textPaint.setStyle(previousStyle);
+            setTextColor(previousColors);
+
+            // Remove the background.
+            final int color;
+            if (mBackgroundSpan != null) {
+                color = mBackgroundSpan.getBackgroundColor();
+                mBackgroundSpan.setColor(Color.TRANSPARENT);
+            } else {
+                color = 0;
+            }
+
+            // Draw foreground only.
+            super.onDraw(c);
+
+            // Restore the background.
+            if (mBackgroundSpan != null) {
+                mBackgroundSpan.setColor(color);
+            }
+        } else {
+            super.onDraw(c);
+        }
+    }
+
+    public static class MutableBackgroundColorSpan extends CharacterStyle
+            implements UpdateAppearance, ParcelableSpan {
+        private int mColor;
+
+        public MutableBackgroundColorSpan(int color) {
+            mColor = color;
+        }
+
+        public MutableBackgroundColorSpan(Parcel src) {
+            mColor = src.readInt();
+        }
+
+        public void setColor(int color) {
+            mColor = color;
+        }
+
+        @Override
+        public int getSpanTypeId() {
+            return TextUtils.BACKGROUND_COLOR_SPAN;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mColor);
+        }
+
+        public int getBackgroundColor() {
+            return mColor;
+        }
+
+        @Override
+        public void updateDrawState(TextPaint ds) {
+            ds.bgColor = mColor;
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/ColorPreference.java b/src/com/android/settings/accessibility/ColorPreference.java
new file mode 100644
index 0000000..68af6b2
--- /dev/null
+++ b/src/com/android/settings/accessibility/ColorPreference.java
@@ -0,0 +1,122 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+/**
+ * Grid preference that allows the user to pick a color from a predefined set of
+ * colors. Optionally shows a preview in the preference item.
+ */
+public class ColorPreference extends ListDialogPreference {
+    private ColorDrawable mPreviewColor;
+    private boolean mPreviewEnabled;
+
+    public ColorPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        setDialogLayoutResource(R.layout.grid_picker_dialog);
+        setListItemLayoutResource(R.layout.color_picker_item);
+    }
+
+    /**
+     * @param enabled whether to show a preview in the preference item
+     */
+    public void setPreviewEnabled(boolean enabled) {
+        if (mPreviewEnabled != enabled) {
+            mPreviewEnabled = enabled;
+
+            if (enabled) {
+                setWidgetLayoutResource(R.layout.preference_color);
+            } else {
+                setWidgetLayoutResource(0);
+            }
+        }
+    }
+
+    @Override
+    public boolean shouldDisableDependents() {
+        return getValue() == Color.TRANSPARENT || super.shouldDisableDependents();
+    }
+
+    @Override
+    protected void onBindView(View view) {
+        super.onBindView(view);
+
+        if (mPreviewEnabled) {
+            final ImageView previewImage = (ImageView) view.findViewById(R.id.color_preview);
+            final int argb = getValue();
+            if (Color.alpha(argb) < 255) {
+                previewImage.setBackgroundResource(R.drawable.transparency_tileable);
+            } else {
+                previewImage.setBackground(null);
+            }
+
+            if (mPreviewColor == null) {
+                mPreviewColor = new ColorDrawable(argb);
+                previewImage.setImageDrawable(mPreviewColor);
+            } else {
+                mPreviewColor.setColor(argb);
+            }
+
+            final CharSequence summary = getSummary();
+            if (!TextUtils.isEmpty(summary)) {
+                previewImage.setContentDescription(summary);
+            } else {
+                previewImage.setContentDescription(null);
+            }
+
+            previewImage.setAlpha(isEnabled() ? 1f : 0.2f);
+        }
+    }
+
+    @Override
+    protected void onBindListItem(View view, int index) {
+        final int argb = getValueAt(index);
+        final int alpha = Color.alpha(argb);
+
+        final ImageView swatch = (ImageView) view.findViewById(R.id.color_swatch);
+        if (alpha < 255) {
+            swatch.setBackgroundResource(R.drawable.transparency_tileable);
+        } else {
+            swatch.setBackground(null);
+        }
+
+        final Drawable foreground = swatch.getDrawable();
+        if (foreground instanceof ColorDrawable) {
+            ((ColorDrawable) foreground).setColor(argb);
+        } else {
+            swatch.setImageDrawable(new ColorDrawable(argb));
+        }
+
+        final CharSequence title = getTitleAt(index);
+        if (title != null) {
+            final TextView summary = (TextView) view.findViewById(R.id.summary);
+            summary.setText(title);
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/EdgeTypePreference.java b/src/com/android/settings/accessibility/EdgeTypePreference.java
new file mode 100644
index 0000000..d0dee1d
--- /dev/null
+++ b/src/com/android/settings/accessibility/EdgeTypePreference.java
@@ -0,0 +1,66 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.CaptioningManager;
+import android.view.accessibility.CaptioningManager.CaptionStyle;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+/**
+ * Grid preference that allows the user to pick a captioning edge type.
+ */
+public class EdgeTypePreference extends ListDialogPreference {
+    public EdgeTypePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        final Resources res = context.getResources();
+        setValues(res.getIntArray(R.array.captioning_edge_type_selector_values));
+        setTitles(res.getStringArray(R.array.captioning_edge_type_selector_titles));
+        setDialogLayoutResource(R.layout.grid_picker_dialog);
+        setListItemLayoutResource(R.layout.preset_picker_item);
+    }
+
+    @Override
+    public boolean shouldDisableDependents() {
+        return getValue() == CaptionStyle.EDGE_TYPE_NONE || super.shouldDisableDependents();
+    }
+
+    @Override
+    protected void onBindListItem(View view, int index) {
+        final float fontSize = CaptioningManager.getFontSize(getContext().getContentResolver());
+        final CaptioningTextView preview = (CaptioningTextView) view.findViewById(R.id.preview);
+        preview.setTextColor(Color.WHITE);
+        preview.setBackgroundColor(Color.TRANSPARENT);
+        preview.setTextSize(fontSize);
+
+        final int value = getValueAt(index);
+        preview.applyEdge(value, Color.BLACK, 4.0f);
+
+        final CharSequence title = getTitleAt(index);
+        if (title != null) {
+            final TextView summary = (TextView) view.findViewById(R.id.summary);
+            summary.setText(title);
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/ListDialogPreference.java b/src/com/android/settings/accessibility/ListDialogPreference.java
new file mode 100644
index 0000000..a252454
--- /dev/null
+++ b/src/com/android/settings/accessibility/ListDialogPreference.java
@@ -0,0 +1,322 @@
+/*
+ * 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.accessibility;
+
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+
+/**
+ * Abstract dialog preference that displays a set of values and optional titles.
+ */
+public abstract class ListDialogPreference extends DialogPreference {
+    private CharSequence[] mEntryTitles;
+    private int[] mEntryValues;
+
+    private OnValueChangedListener mOnValueChangedListener;
+
+    /** The layout resource to use for grid items. */
+    private int mListItemLayout;
+
+    /** The current value of this preference. */
+    private int mValue;
+
+    /** The index within the value set of the current value. */
+    private int mValueIndex;
+
+    /** Whether the value had been set using {@link #setValue}. */
+    private boolean mValueSet;
+
+    public ListDialogPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /**
+     * Sets a listened to invoke when the value of this preference changes.
+     *
+     * @param listener the listener to invoke
+     */
+    public void setOnValueChangedListener(OnValueChangedListener listener) {
+        mOnValueChangedListener = listener;
+    }
+
+    /**
+     * Sets the layout to use for grid items.
+     *
+     * @param layoutResId the layout to use for displaying grid items
+     */
+    public void setListItemLayoutResource(int layoutResId) {
+        mListItemLayout = layoutResId;
+    }
+
+    /**
+     * Sets the list of item values. Values must be distinct.
+     *
+     * @param values the list of item values
+     */
+    public void setValues(int[] values) {
+        mEntryValues = values;
+    }
+
+    /**
+     * Sets the list of item titles. May be null if no titles are specified, or
+     * may be shorter than the list of values to leave some titles unspecified.
+     *
+     * @param titles the list of item titles
+     */
+    public void setTitles(CharSequence[] titles) {
+        mEntryTitles = titles;
+    }
+
+    /**
+     * Populates a list item view with data for the specified index.
+     *
+     * @param view the view to populate
+     * @param index the index for which to populate the view
+     * @see #setListItemLayoutResource(int)
+     * @see #getValueAt(int)
+     * @see #getTitleAt(int)
+     */
+    protected abstract void onBindListItem(View view, int index);
+
+    /**
+     * @return the title at the specified index, or null if none specified
+     */
+    protected CharSequence getTitleAt(int index) {
+        if (mEntryTitles == null || mEntryTitles.length <= index) {
+            return null;
+        }
+
+        return mEntryTitles[index];
+    }
+
+    /**
+     * @return the value at the specified index
+     */
+    protected int getValueAt(int index) {
+        return mEntryValues[index];
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        if (mValueIndex >= 0) {
+            return getTitleAt(mValueIndex);
+        }
+
+        return null;
+    }
+
+    @Override
+    protected void onPrepareDialogBuilder(Builder builder) {
+        super.onPrepareDialogBuilder(builder);
+
+        final Context context = getContext();
+        final int dialogLayout = getDialogLayoutResource();
+        final View picker = LayoutInflater.from(context).inflate(dialogLayout, null);
+        final ListPreferenceAdapter adapter = new ListPreferenceAdapter();
+        final AbsListView list = (AbsListView) picker.findViewById(android.R.id.list);
+        list.setAdapter(adapter);
+        list.setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
+                if (callChangeListener((int) id)) {
+                    setValue((int) id);
+                }
+
+                final Dialog dialog = getDialog();
+                if (dialog != null) {
+                    dialog.dismiss();
+                }
+            }
+        });
+
+        // Set initial selection.
+        final int selectedPosition = getIndexForValue(mValue);
+        if (selectedPosition != AbsListView.INVALID_POSITION) {
+            list.setSelection(selectedPosition);
+        }
+
+        builder.setView(picker);
+        builder.setPositiveButton(null, null);
+    }
+
+    /**
+     * @return the index of the specified value within the list of entry values,
+     *         or {@link AbsListView#INVALID_POSITION} if not found
+     */
+    protected int getIndexForValue(int value) {
+        final int[] values = mEntryValues;
+        final int count = values.length;
+        for (int i = 0; i < count; i++) {
+            if (values[i] == value) {
+                return i;
+            }
+        }
+
+        return AbsListView.INVALID_POSITION;
+    }
+
+    /**
+     * Sets the current value. If the value exists within the set of entry
+     * values, updates the selection index.
+     *
+     * @param value the value to set
+     */
+    public void setValue(int value) {
+        final boolean changed = mValue != value;
+        if (changed || !mValueSet) {
+            mValue = value;
+            mValueIndex = getIndexForValue(value);
+            mValueSet = true;
+            persistInt(value);
+            if (changed) {
+                notifyDependencyChange(shouldDisableDependents());
+                notifyChanged();
+            }
+            if (mOnValueChangedListener != null) {
+                mOnValueChangedListener.onValueChanged(this, value);
+            }
+        }
+    }
+
+    /**
+     * @return the current value
+     */
+    public int getValue() {
+        return mValue;
+    }
+
+    @Override
+    protected Object onGetDefaultValue(TypedArray a, int index) {
+        return a.getInt(index, 0);
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setValue(restoreValue ? getPersistedInt(mValue) : (Integer) defaultValue);
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        final Parcelable superState = super.onSaveInstanceState();
+        if (isPersistent()) {
+            // No need to save instance state since it's persistent
+            return superState;
+        }
+
+        final SavedState myState = new SavedState(superState);
+        myState.value = getValue();
+        return myState;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        if (state == null || !state.getClass().equals(SavedState.class)) {
+            // Didn't save state for us in onSaveInstanceState
+            super.onRestoreInstanceState(state);
+            return;
+        }
+
+        SavedState myState = (SavedState) state;
+        super.onRestoreInstanceState(myState.getSuperState());
+        setValue(myState.value);
+    }
+
+    private class ListPreferenceAdapter extends BaseAdapter {
+        private LayoutInflater mInflater;
+
+        @Override
+        public int getCount() {
+            return mEntryValues.length;
+        }
+
+        @Override
+        public Integer getItem(int position) {
+            return mEntryValues[position];
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return mEntryValues[position];
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return true;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                if (mInflater == null) {
+                    mInflater = LayoutInflater.from(parent.getContext());
+                }
+                convertView = mInflater.inflate(mListItemLayout, parent, false);
+            }
+            onBindListItem(convertView, position);
+            return convertView;
+        }
+    }
+
+    private static class SavedState extends BaseSavedState {
+        public int value;
+
+        public SavedState(Parcel source) {
+            super(source);
+            value = source.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(value);
+        }
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        @SuppressWarnings({ "hiding", "unused" })
+        public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    public interface OnValueChangedListener {
+        public void onValueChanged(ListDialogPreference preference, int value);
+    }
+}
diff --git a/src/com/android/settings/accessibility/LocalePreference.java b/src/com/android/settings/accessibility/LocalePreference.java
new file mode 100644
index 0000000..41cb1e5
--- /dev/null
+++ b/src/com/android/settings/accessibility/LocalePreference.java
@@ -0,0 +1,150 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.preference.ListPreference;
+import android.util.AttributeSet;
+
+import com.android.settings.R;
+
+import java.text.Collator;
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * List preference that allows the user to pick a locale from the list of
+ * supported device locales.
+ */
+public class LocalePreference extends ListPreference {
+    public LocalePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public LocalePreference(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public void init(Context context) {
+        final String[] systemLocales = Resources.getSystem().getAssets().getLocales();
+        Arrays.sort(systemLocales);
+
+        final Resources resources = context.getResources();
+        final String[] specialLocaleCodes = resources.getStringArray(
+                com.android.internal.R.array.special_locale_codes);
+        final String[] specialLocaleNames = resources.getStringArray(
+                com.android.internal.R.array.special_locale_names);
+
+        int finalSize = 0;
+
+        final int origSize = systemLocales.length;
+        final LocaleInfo[] localeInfos = new LocaleInfo[origSize];
+        for (int i = 0; i < origSize; i++) {
+            final String localeStr = systemLocales[i];
+            final int len = localeStr.length();
+            if (len != 5) {
+                continue;
+            }
+
+            final String language = localeStr.substring(0, 2);
+            final String country = localeStr.substring(3, 5);
+            final Locale l = new Locale(language, country);
+
+            if (finalSize == 0) {
+                localeInfos[finalSize++] = new LocaleInfo(l.getDisplayLanguage(l), l);
+            } else {
+                // check previous entry:
+                // same lang and a country -> upgrade to full name and
+                // insert ours with full name
+                // diff lang -> insert ours with lang-only name
+                final LocaleInfo previous = localeInfos[finalSize - 1];
+                if (previous.locale.getLanguage().equals(language)
+                        && !previous.locale.getLanguage().equals("zz")) {
+                    previous.label = getDisplayName(
+                            localeInfos[finalSize - 1].locale, specialLocaleCodes,
+                            specialLocaleNames);
+                    localeInfos[finalSize++] = new LocaleInfo(getDisplayName(l,
+                            specialLocaleCodes, specialLocaleNames), l);
+                } else {
+                    final String displayName;
+                    if (localeStr.equals("zz_ZZ")) {
+                        displayName = "[Developer] Accented English";
+                    } else if (localeStr.equals("zz_ZY")) {
+                        displayName = "[Developer] Fake Bi-Directional";
+                    } else {
+                        displayName = l.getDisplayLanguage(l);
+                    }
+                    localeInfos[finalSize++] = new LocaleInfo(displayName, l);
+                }
+            }
+        }
+
+        final CharSequence[] entries = new CharSequence[finalSize + 1];
+        final CharSequence[] entryValues = new CharSequence[finalSize + 1];
+        Arrays.sort(localeInfos, 0, finalSize);
+
+        entries[0] = resources.getString(R.string.locale_default);
+        entryValues[0] = "";
+
+        for (int i = 0; i < finalSize; i++) {
+            final LocaleInfo info = localeInfos[i];
+            entries[i + 1] = info.toString();
+            entryValues[i + 1] = info.locale.toString();
+        }
+
+        setEntries(entries);
+        setEntryValues(entryValues);
+    }
+
+    private static String getDisplayName(
+            Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
+        String code = l.toString();
+
+        for (int i = 0; i < specialLocaleCodes.length; i++) {
+            if (specialLocaleCodes[i].equals(code)) {
+                return specialLocaleNames[i];
+            }
+        }
+
+        return l.getDisplayName(l);
+    }
+
+    private static class LocaleInfo implements Comparable<LocaleInfo> {
+        private static final Collator sCollator = Collator.getInstance();
+
+        public String label;
+        public Locale locale;
+
+        public LocaleInfo(String label, Locale locale) {
+            this.label = label;
+            this.locale = locale;
+        }
+
+        @Override
+        public String toString() {
+            return label;
+        }
+
+        @Override
+        public int compareTo(LocaleInfo another) {
+            return sCollator.compare(this.label, another.label);
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/PresetPreference.java b/src/com/android/settings/accessibility/PresetPreference.java
new file mode 100644
index 0000000..cd01082
--- /dev/null
+++ b/src/com/android/settings/accessibility/PresetPreference.java
@@ -0,0 +1,54 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.CaptioningManager.CaptionStyle;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+public class PresetPreference extends ListDialogPreference {
+    public PresetPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        setDialogLayoutResource(R.layout.grid_picker_dialog);
+        setListItemLayoutResource(R.layout.preset_picker_item);
+    }
+
+    @Override
+    public boolean shouldDisableDependents() {
+        return getValue() != CaptionStyle.PRESET_CUSTOM
+                || super.shouldDisableDependents();
+    }
+
+    @Override
+    protected void onBindListItem(View view, int index) {
+        final CaptioningTextView previewText = (CaptioningTextView) view.findViewById(
+                R.id.preview);
+        final int value = getValueAt(index);
+        ToggleCaptioningPreferenceFragment.applyCaptionProperties(previewText, value);
+
+        final CharSequence title = getTitleAt(index);
+        if (title != null) {
+            final TextView summary = (TextView) view.findViewById(R.id.summary);
+            summary.setText(title);
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/ToggleCaptioningPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleCaptioningPreferenceFragment.java
new file mode 100644
index 0000000..d0daf66
--- /dev/null
+++ b/src/com/android/settings/accessibility/ToggleCaptioningPreferenceFragment.java
@@ -0,0 +1,133 @@
+/*
+ * 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.accessibility;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.Bundle;
+import android.preference.PreferenceFrameLayout;
+import android.provider.Settings;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.CaptioningManager;
+import android.view.accessibility.CaptioningManager.CaptionStyle;
+
+import com.android.settings.R;
+import com.android.settings.accessibility.ToggleSwitch.OnBeforeCheckedChangeListener;
+
+import java.util.Locale;
+
+public class ToggleCaptioningPreferenceFragment extends Fragment {
+    private CaptionPropertiesFragment mPropsFragment;
+    private CaptioningTextView mPreviewText;
+
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        final View rootView = inflater.inflate(R.layout.captioning_preview, container, false);
+
+        // We have to do this now because PreferenceFrameLayout looks at it
+        // only when the view is added.
+        if (container instanceof PreferenceFrameLayout) {
+            ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true;
+        }
+
+        return rootView;
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        mPropsFragment = ((CaptionPropertiesFragment) getFragmentManager()
+                .findFragmentById(R.id.properties_fragment));
+        mPropsFragment.setParent(this);
+
+        mPreviewText = (CaptioningTextView) view.findViewById(R.id.preview_text);
+
+        installActionBarToggleSwitch();
+        refreshPreviewText();
+    }
+
+    public void refreshPreviewText() {
+        final CaptioningTextView preview = mPreviewText;
+        if (preview != null) {
+            final Activity activity = getActivity();
+            final ContentResolver cr = activity.getContentResolver();
+            final int styleId = CaptionStyle.getRawPreset(cr);
+            applyCaptionProperties(preview, styleId);
+
+            final Locale locale = CaptioningManager.getLocale(cr);
+            if (locale != null) {
+                final CharSequence localizedText = AccessibilityUtils.getTextForLocale(
+                        activity, locale, R.string.captioning_preview_text);
+                preview.setText(localizedText);
+            }
+        }
+    }
+
+    public static void applyCaptionProperties(CaptioningTextView previewText, int styleId) {
+        previewText.applyStyleAndFontSize(styleId);
+
+        final Context context = previewText.getContext();
+        final ContentResolver cr = context.getContentResolver();
+        final Locale locale = CaptioningManager.getLocale(cr);
+        if (locale != null) {
+            final CharSequence localizedText = AccessibilityUtils.getTextForLocale(
+                    context, locale, R.string.captioning_preview_characters);
+            previewText.setText(localizedText);
+        }
+    }
+
+    private void installActionBarToggleSwitch() {
+        final Activity activity = getActivity();
+        final ToggleSwitch toggleSwitch = new ToggleSwitch(activity);
+
+        final int padding = getResources().getDimensionPixelSize(
+                R.dimen.action_bar_switch_padding);
+        toggleSwitch.setPaddingRelative(0, 0, padding, 0);
+
+        final ActionBar actionBar = activity.getActionBar();
+        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM);
+
+        final ActionBar.LayoutParams params = new ActionBar.LayoutParams(
+                ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT,
+                        Gravity.CENTER_VERTICAL | Gravity.END);
+        actionBar.setCustomView(toggleSwitch, params);
+
+        final boolean enabled = CaptioningManager.isEnabled(getActivity().getContentResolver());
+        mPropsFragment.getPreferenceScreen().setEnabled(enabled);
+        mPreviewText.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
+        toggleSwitch.setCheckedInternal(enabled);
+        toggleSwitch.setOnBeforeCheckedChangeListener(new OnBeforeCheckedChangeListener() {
+            @Override
+            public boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked) {
+                toggleSwitch.setCheckedInternal(checked);
+                Settings.Secure.putInt(getActivity().getContentResolver(),
+                        Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, checked ? 1 : 0);
+                mPropsFragment.getPreferenceScreen().setEnabled(checked);
+                mPreviewText.setVisibility(checked ? View.VISIBLE : View.INVISIBLE);
+                return false;
+            }
+        });
+    }
+}
diff --git a/src/com/android/settings/tts/TextToSpeechSettings.java b/src/com/android/settings/tts/TextToSpeechSettings.java
index 10ac575..fa48a3f 100644
--- a/src/com/android/settings/tts/TextToSpeechSettings.java
+++ b/src/com/android/settings/tts/TextToSpeechSettings.java
@@ -42,6 +42,7 @@
 import android.util.Log;
 import android.widget.Checkable;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -104,6 +105,7 @@
 
     private String mSampleText = "";
     private Locale mCurrentDefaultLocale;
+    private List<String> mAvailableStrLocals;
 
     /**
      * The initialization listener used when we are initalizing the settings
@@ -144,6 +146,7 @@
         mDefaultRatePref = (ListPreference) findPreference(KEY_DEFAULT_RATE);
 
         mEngineStatus = findPreference(KEY_STATUS);
+        updateEngineStatus(R.string.tts_status_checking);
 
         mTts = new TextToSpeech(getActivity().getApplicationContext(), mInitListener);
         mEnginesHelper = new TtsEngines(getActivity().getApplicationContext());
@@ -253,17 +256,50 @@
         mCurrentDefaultLocale = defaultLocale;
 
         int defaultAvailable = mTts.setLanguage(defaultLocale);
-        if (defaultAvailable == TextToSpeech.LANG_NOT_SUPPORTED) {
+        if (evaluateDefaultLocale()) {
+            getSampleText();
+        }
+    }
+
+    private boolean evaluateDefaultLocale() {
+        if (mCurrentDefaultLocale == null) {
+            return false;
+        }
+        int defaultAvailable = mTts.setLanguage(mCurrentDefaultLocale);
+
+        // Check if language is listed in CheckVoices Action result as available voice.
+        String defaultLocaleStr = mCurrentDefaultLocale.getISO3Language();
+        boolean notInAvailableLangauges = true;
+        if (!TextUtils.isEmpty(mCurrentDefaultLocale.getISO3Country())) {
+            defaultLocaleStr += "-" + mCurrentDefaultLocale.getISO3Country();
+        }
+        if (!TextUtils.isEmpty(mCurrentDefaultLocale.getVariant())) {
+            defaultLocaleStr += "-" + mCurrentDefaultLocale.getVariant();
+        }
+        if (mAvailableStrLocals != null) {
+            for (String loc : mAvailableStrLocals) {
+                if (loc.equalsIgnoreCase(defaultLocaleStr)) {
+                    notInAvailableLangauges = false;
+                    break;
+                }
+            }
+        }
+
+        if (defaultAvailable == TextToSpeech.LANG_NOT_SUPPORTED ||
+                defaultAvailable == TextToSpeech.LANG_MISSING_DATA ||
+                mAvailableStrLocals != null && notInAvailableLangauges) {
             if (DBG) Log.d(TAG, "Default locale for this TTS engine is not supported.");
-            updateWidgetState(false);
             updateEngineStatus(R.string.tts_status_not_supported);
+            updateWidgetState(false);
+            return false;
         } else {
             if (isNetworkRequiredForSynthesis()) {
                 updateEngineStatus(R.string.tts_status_requires_network);
             } else {
                 updateEngineStatus(R.string.tts_status_ok);
             }
-            getSampleText();
+            updateWidgetState(true);
+            return true;
         }
     }
 
@@ -431,6 +467,7 @@
         // Disable the "play sample text" preference and the speech
         // rate preference while the engine is being swapped.
         updateWidgetState(false);
+        updateEngineStatus(R.string.tts_status_checking);
 
         // Keep track of the previous engine that was being used. So that
         // we can reuse the previous engine.
@@ -515,6 +552,10 @@
 
         Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, engine);
 
+        mAvailableStrLocals = data.getStringArrayListExtra(
+                TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
+        evaluateDefaultLocale();
+
         final int engineCount = mEnginePreferenceCategory.getPreferenceCount();
         for (int i = 0; i < engineCount; ++i) {
             final Preference p = mEnginePreferenceCategory.getPreference(i);
diff --git a/src/com/android/settings/users/RestrictionSettings.java b/src/com/android/settings/users/RestrictionSettings.java
deleted file mode 100644
index 91b8bd3..0000000
--- a/src/com/android/settings/users/RestrictionSettings.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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.users;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-
-import com.android.settings.R;
-
-import java.util.List;
-
-/**
- * Used for restricting regular users, including single-user devices.
- */
-public class RestrictionSettings extends AppRestrictionsFragment {
-
-    private static final int REQUEST_PIN_CHALLENGE = 10;
-
-    private static final int MENU_RESET = Menu.FIRST + 1;
-    private static final int MENU_CHANGE_PIN = Menu.FIRST + 2;
-
-    private static final String KEY_CHALLENGE_SUCCEEDED = "chsc";
-    private static final String KEY_CHALLENGE_REQUESTED = "chrq";
-
-    private boolean mChallengeSucceeded;
-    private boolean mChallengeRequested;
-    private boolean mDisableSelf;
-
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        if (UserManager.get(getActivity()).hasUserRestriction(
-                UserManager.DISALLOW_APP_RESTRICTIONS)) {
-            mDisableSelf = true;
-            return;
-        }
-        init(icicle);
-        if (icicle != null) {
-            mChallengeSucceeded = icicle.getBoolean(KEY_CHALLENGE_SUCCEEDED, false);
-            mChallengeRequested = icicle.getBoolean(KEY_CHALLENGE_REQUESTED, false);
-        }
-        setHasOptionsMenu(true);
-    }
-
-    public void onResume() {
-        super.onResume();
-        if (!mDisableSelf) {
-            ensurePin();
-        }
-    }
-
-    private void ensurePin() {
-        if (!mChallengeSucceeded) {
-            getListView().setEnabled(false);
-            final UserManager um = UserManager.get(getActivity());
-            if (!mChallengeRequested) {
-                if (um.hasRestrictionsPin()) {
-                    Intent requestPin =
-                            new Intent(Intent.ACTION_RESTRICTIONS_PIN_CHALLENGE);
-                    startActivityForResult(requestPin, REQUEST_PIN_CHALLENGE);
-                } else {
-                    Intent requestPin =
-                            new Intent("android.intent.action.RESTRICTIONS_PIN_CREATE");
-                    startActivityForResult(requestPin, REQUEST_PIN_CHALLENGE);
-                }
-                mChallengeRequested = true;
-            }
-        }
-        mChallengeSucceeded = false;
-    }
-
-    private void resetAndRemovePin() {
-        final UserManager um = UserManager.get(getActivity());
-        um.removeRestrictions();
-        clearSelectedApps();
-        finishFragment();
-    }
-
-    private void changePin() {
-        final UserManager um = UserManager.get(getActivity());
-        Intent requestPin = new Intent("android.intent.action.RESTRICTIONS_PIN_CREATE");
-        startActivityForResult(requestPin, REQUEST_PIN_CHALLENGE);
-    }
-
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == REQUEST_PIN_CHALLENGE) {
-            mChallengeRequested = false;
-            if (resultCode == Activity.RESULT_OK) {
-                getListView().setEnabled(true);
-                mChallengeSucceeded = true;
-            } else if (!isDetached()) {
-                finishFragment();
-            }
-            return;
-        }
-
-        super.onActivityResult(requestCode, resultCode, data);
-    }
-
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        outState.putBoolean(KEY_CHALLENGE_REQUESTED, mChallengeRequested);
-        if (getActivity().isChangingConfigurations()) {
-            outState.putBoolean(KEY_CHALLENGE_SUCCEEDED, mChallengeSucceeded);
-        }
-    }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        if (!mDisableSelf) {
-            menu.add(0, MENU_RESET, 0, R.string.restriction_menu_reset);
-            menu.add(0, MENU_CHANGE_PIN, 0, R.string.restriction_menu_change_pin);
-        }
-        super.onCreateOptionsMenu(menu, inflater);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-        case MENU_RESET:
-            resetAndRemovePin();
-            return true;
-        case MENU_CHANGE_PIN:
-            changePin();
-            return true;
-        }
-
-        return super.onOptionsItemSelected(item);
-    }
-}