Merge "Fix empty state layouts"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index aeec2b2..e255fd2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -117,7 +117,9 @@
             </intent-filter>
         </activity>
 
-        <!-- Tab container for all tabs -->
+        <!-- The entrance point for Phone UI.
+             stateAlwaysHidden is set to suppress keyboard show up on
+             dialpad screen. -->
         <activity android:name=".activities.DialtactsActivity"
             android:label="@string/launcherDialer"
             android:theme="@style/DialtactsTheme"
@@ -128,7 +130,7 @@
             android:screenOrientation="nosensor"
             android:enabled="@*android:bool/config_voice_capable"
             android:taskAffinity="android.task.contacts.phone"
-        >
+            android:windowSoftInputMode="stateAlwaysHidden">
             <intent-filter>
                 <action android:name="android.intent.action.DIAL" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/res/drawable-hdpi/list_pressed_holo_light.9.png b/res/drawable-hdpi/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..cf5ff8e
--- /dev/null
+++ b/res/drawable-hdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_pressed_holo_light.9.png b/res/drawable-mdpi/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..3a9686d
--- /dev/null
+++ b/res/drawable-mdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_pressed_holo_light.9.png b/res/drawable-xhdpi/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..8ff9dca
--- /dev/null
+++ b/res/drawable-xhdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/res/layout/contact_detail_container_without_updates.xml b/res/drawable/action_bar_item_background.xml
similarity index 62%
rename from res/layout/contact_detail_container_without_updates.xml
rename to res/drawable/action_bar_item_background.xml
index 884f280..1fd4614 100644
--- a/res/layout/contact_detail_container_without_updates.xml
+++ b/res/drawable/action_bar_item_background.xml
@@ -14,15 +14,8 @@
      limitations under the License.
 -->
 
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <fragment
-        android:id="@+id/contact_detail_fragment"
-        class="com.android.contacts.detail.ContactDetailFragment"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
-
-</FrameLayout>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+    <item android:state_pressed="true" android:drawable="@drawable/list_pressed_holo_light"/>
+    <item android:drawable="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/res/layout/contact_detail_container_with_updates.xml b/res/layout-sw580dp/contact_detail_container.xml
similarity index 86%
rename from res/layout/contact_detail_container_with_updates.xml
rename to res/layout-sw580dp/contact_detail_container.xml
index 48f1800..62c4081 100644
--- a/res/layout/contact_detail_container_with_updates.xml
+++ b/res/layout-sw580dp/contact_detail_container.xml
@@ -17,7 +17,9 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:background="@color/background_primary"
+    android:padding="16dip">
 
     <android.support.v4.view.ViewPager
         android:id="@+id/pager"
@@ -32,6 +34,7 @@
         android:layout_alignParentTop="true"
         android:layout_alignParentLeft="true"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
+        android:layout_height="wrap_content"
+        android:visibility="gone"/>
 
-</RelativeLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp/contact_detail_fragment.xml b/res/layout-sw580dp/contact_detail_fragment.xml
index c9dad2a..f05dc56 100644
--- a/res/layout-sw580dp/contact_detail_fragment.xml
+++ b/res/layout-sw580dp/contact_detail_fragment.xml
@@ -30,8 +30,9 @@
 
     <!-- Real list -->
     <ListView android:id="@android:id/list"
+        android:layout_weight="1"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="0dip"
         android:cacheColorHint="#00000000"
         android:divider="@null"
     />
diff --git a/res/layout-w470dp/contact_detail_container_with_updates.xml b/res/layout-w470dp/contact_detail_container.xml
similarity index 100%
rename from res/layout-w470dp/contact_detail_container_with_updates.xml
rename to res/layout-w470dp/contact_detail_container.xml
diff --git a/res/layout-w470dp/contact_detail_fragment.xml b/res/layout-w470dp/contact_detail_fragment.xml
index 982abbb..4f26cc5 100644
--- a/res/layout-w470dp/contact_detail_fragment.xml
+++ b/res/layout-w470dp/contact_detail_fragment.xml
@@ -75,8 +75,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:visibility="gone"/>
+        android:layout_alignParentTop="true"/>
 
     <View
         android:id="@+id/touch_intercept_overlay"
diff --git a/res/layout-w470dp/contact_detail_updates_fragment.xml b/res/layout-w470dp/contact_detail_updates_fragment.xml
index ccb7123..6081cfd 100644
--- a/res/layout-w470dp/contact_detail_updates_fragment.xml
+++ b/res/layout-w470dp/contact_detail_updates_fragment.xml
@@ -31,8 +31,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:visibility="gone"/>
+        android:layout_alignParentTop="true"/>
 
     <View
         android:id="@+id/touch_intercept_overlay"
diff --git a/res/layout/carousel_about_tab.xml b/res/layout/carousel_about_tab.xml
index c7c4394..c5d4114 100644
--- a/res/layout/carousel_about_tab.xml
+++ b/res/layout/carousel_about_tab.xml
@@ -43,8 +43,7 @@
         android:layout_height="match_parent"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
-        android:layout_marginBottom="@dimen/detail_tab_carousel_tab_label_height"
-        android:visibility="gone"/>
+        android:layout_marginBottom="@dimen/detail_tab_carousel_tab_label_height"/>
 
     <TextView
         android:id="@+id/label"
diff --git a/res/layout/carousel_updates_tab.xml b/res/layout/carousel_updates_tab.xml
index 9637023..3f633fc 100644
--- a/res/layout/carousel_updates_tab.xml
+++ b/res/layout/carousel_updates_tab.xml
@@ -63,8 +63,7 @@
         android:layout_height="match_parent"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
-        android:layout_marginBottom="@dimen/detail_tab_carousel_tab_label_height"
-        android:visibility="gone"/>
+        android:layout_marginBottom="@dimen/detail_tab_carousel_tab_label_height"/>
 
     <TextView
         android:id="@+id/label"
diff --git a/res/layout/contact_detail_activity.xml b/res/layout/contact_detail_activity.xml
index 3ab40c3..1e63aa7 100644
--- a/res/layout/contact_detail_activity.xml
+++ b/res/layout/contact_detail_activity.xml
@@ -27,4 +27,11 @@
         android:layout_width="0dip"
         android:visibility="gone"/>
 
+      <!-- This layout includes all possible views needed for a contact detail page -->
+      <include
+          android:id="@+id/contact_detail_container"
+          layout="@layout/contact_detail_container"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"/>
+
 </FrameLayout>
diff --git a/res/layout/contact_detail_container.xml b/res/layout/contact_detail_container.xml
index 9204b62..ff2eab1 100644
--- a/res/layout/contact_detail_container.xml
+++ b/res/layout/contact_detail_container.xml
@@ -26,9 +26,7 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:background="@color/background_primary"
-    android:padding="16dip">
+    android:orientation="vertical">
 
     <android.support.v4.view.ViewPager
         android:id="@+id/pager"
diff --git a/res/layout/contact_detail_fragment_carousel.xml b/res/layout/contact_detail_fragment_carousel.xml
index 07c8d1c..4f65549 100644
--- a/res/layout/contact_detail_fragment_carousel.xml
+++ b/res/layout/contact_detail_fragment_carousel.xml
@@ -28,8 +28,9 @@
     -->
     <FrameLayout
         android:id="@+id/about_fragment_container"
-        android:layout_width="@dimen/detail_fragment_carousel_fragment_width"
-        android:layout_height="match_parent" />
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:layout_weight="1" />
 
     <!--
       Container for the "Updates" page fragment on the contact card for a contact
@@ -38,7 +39,9 @@
     -->
     <FrameLayout
         android:id="@+id/updates_fragment_container"
-        android:layout_width="@dimen/detail_fragment_carousel_fragment_width"
-        android:layout_height="match_parent" />
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:visibility="gone" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/dialpad_fragment.xml b/res/layout/dialpad_fragment.xml
index 90d2593..75d5938 100644
--- a/res/layout/dialpad_fragment.xml
+++ b/res/layout/dialpad_fragment.xml
@@ -25,6 +25,7 @@
     <!-- Text field and possibly soft menu button above the keypad where
          the digits are displayed. -->
     <RelativeLayout
+        android:id="@+id/digits_container"
         android:layout_width="match_parent"
         android:layout_height="0px"
         android:layout_weight="0.200"
@@ -35,7 +36,8 @@
              in the java code.
 
              Background drawable can be controlled programatically. -->
-        <EditText android:id="@+id/digits"
+        <com.android.contacts.dialpad.DigitsEditText
+            android:id="@+id/digits"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_alignParentLeft="true"
diff --git a/res/layout/dialtacts_custom_action_bar.xml b/res/layout/dialtacts_custom_action_bar.xml
new file mode 100644
index 0000000..2be66f7
--- /dev/null
+++ b/res/layout/dialtacts_custom_action_bar.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- Dimensions are set at runtime in ActionBarAdapter -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dip"
+    android:layout_height="0dip"
+    android:orientation="horizontal">
+
+    <SearchView
+        android:id="@+id/search_view"
+        android:layout_width="0px"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:iconifiedByDefault="false" />
+
+    <ImageButton
+        android:id="@+id/search_option"
+        android:layout_width="wrap_content"
+        android:paddingRight="8dip"
+        android:layout_height="match_parent"
+        android:layout_alignParentRight="true"
+        android:src="@drawable/ic_menu_overflow"
+        android:background="@android:color/transparent"
+        android:visibility="gone" />
+
+</LinearLayout>
diff --git a/res/layout/group_browse_list_item.xml b/res/layout/group_browse_list_item.xml
index 599cc13..6bac5ea 100644
--- a/res/layout/group_browse_list_item.xml
+++ b/res/layout/group_browse_list_item.xml
@@ -69,38 +69,6 @@
                 android:singleLine="true" />
 
         </LinearLayout>
-
-        <TableLayout
-            android:id="@+id/icons"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentRight="true"
-            android:layout_centerVertical="true">
-            <TableRow
-                android:layout_marginBottom="1dip">
-                <ImageView
-                    android:id="@+id/icon_1"
-                    android:layout_width="@dimen/group_list_icon_size"
-                    android:layout_height="@dimen/group_list_icon_size"
-                    android:layout_marginRight="1dip" />
-                <ImageView
-                    android:id="@+id/icon_2"
-                    android:layout_width="@dimen/group_list_icon_size"
-                    android:layout_height="@dimen/group_list_icon_size" />
-            </TableRow>
-            <TableRow>
-                <ImageView
-                    android:id="@+id/icon_3"
-                    android:layout_width="@dimen/group_list_icon_size"
-                    android:layout_height="@dimen/group_list_icon_size"
-                    android:layout_marginRight="1dip" />
-                <ImageView
-                    android:id="@+id/icon_4"
-                    android:layout_width="@dimen/group_list_icon_size"
-                    android:layout_height="@dimen/group_list_icon_size" />
-            </TableRow>
-
-        </TableLayout>
     </RelativeLayout>
 </LinearLayout>
 
diff --git a/res/menu/dialtacts_options.xml b/res/menu/dialtacts_options.xml
index cc9543a..2c83f6b 100644
--- a/res/menu/dialtacts_options.xml
+++ b/res/menu/dialtacts_options.xml
@@ -29,4 +29,9 @@
         android:id="@+id/filter_option"
         android:title="@string/menu_contacts_filter"
         android:showAsAction="withText" />
+
+    <item
+        android:id="@+id/add_contact"
+        android:title="@string/menu_newContact"
+        android:showAsAction="withText" />
 </menu>
diff --git a/res/layout/contact_detail_container_without_updates.xml b/res/menu/dialtacts_search_options.xml
similarity index 61%
copy from res/layout/contact_detail_container_without_updates.xml
copy to res/menu/dialtacts_search_options.xml
index 884f280..0979ebb 100644
--- a/res/layout/contact_detail_container_without_updates.xml
+++ b/res/menu/dialtacts_search_options.xml
@@ -13,16 +13,14 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <fragment
-        android:id="@+id/contact_detail_fragment"
-        class="com.android.contacts.detail.ContactDetailFragment"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
-
-</FrameLayout>
+<!-- Used with DialtactsActivity's search mode. -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/filter_option"
+        android:title="@string/menu_contacts_filter"
+        android:showAsAction="withText" />
+    <item
+        android:id="@+id/add_contact"
+        android:title="@string/menu_newContact"
+        android:showAsAction="withText" />
+</menu>
diff --git a/res/values-sw580dp-w720dp/styles.xml b/res/values-sw580dp-w720dp/styles.xml
index f3a3759..69548c5 100644
--- a/res/values-sw580dp-w720dp/styles.xml
+++ b/res/values-sw580dp-w720dp/styles.xml
@@ -16,6 +16,7 @@
 <resources>
     <style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
+        <item name="android:actionBarItemBackground">@drawable/action_bar_item_background</item>
         <item name="android:actionBarTabStyle">@style/ContactsActionBarTabView</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
diff --git a/res/values-sw580dp/styles.xml b/res/values-sw580dp/styles.xml
index 5a28bf6..0a62db2 100644
--- a/res/values-sw580dp/styles.xml
+++ b/res/values-sw580dp/styles.xml
@@ -16,6 +16,7 @@
 <resources>
     <style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
+        <item name="android:actionBarItemBackground">@drawable/action_bar_item_background</item>
         <item name="android:actionBarTabStyle">@style/ContactsActionBarTabView</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c7a2bc7..11e7e38 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -783,16 +783,16 @@
     <string name="searching_vcard_message" product="default">Searching for vCard data on SD card</string>
 
     <!-- Dialog title shown when scanning VCard data failed. [CHAR LIMIT=NONE] -->
-    <string name="scanning_sdcard_failed_title" product="nosdcard">Scanning storage failed</string>
+    <string name="scanning_sdcard_failed_title" product="nosdcard">Couldn\'t scan storage</string>
     <!-- Dialog title shown when scanning VCard data failed. -->
-    <string name="scanning_sdcard_failed_title" product="default">Scanning SD card failed</string>
+    <string name="scanning_sdcard_failed_title" product="default">Couldn\'t scan SD card</string>
 
     <!-- Dialog message shown when searching VCard data failed.
          An exact reason for the failure should [CHAR LIMIT=NONE] -->
-    <string name="scanning_sdcard_failed_message" product="nosdcard">Scanning storage failed (Reason: \"<xliff:g id="fail_reason">%s</xliff:g>\")</string>
+    <string name="scanning_sdcard_failed_message" product="nosdcard">Couldn\'t scan storage (Reason: \"<xliff:g id="fail_reason">%s</xliff:g>\")</string>
     <!-- Dialog message shown when searching VCard data failed.
          An exact reason for the failure should -->
-    <string name="scanning_sdcard_failed_message" product="default">Scanning SD card failed (Reason: \"<xliff:g id="fail_reason">%s</xliff:g>\")</string>
+    <string name="scanning_sdcard_failed_message" product="default">Couldn\'t scan SD card (Reason: \"<xliff:g id="fail_reason">%s</xliff:g>\")</string>
 
     <!-- The failed reason shown when Contacts app (especially vCard importer/exporter)
          emitted some I/O error. Exact reason will be appended by the system. -->
@@ -805,14 +805,14 @@
     <!-- The failed reason shown when vCard parser was not able to be parsed by the current vCard
          implementation. This might happen even when the input vCard is completely valid, though
          we believe it is rather rare in the actual world. -->
-    <string name="fail_reason_vcard_parse_error">Failed to parse vCard for unexpected reason</string>
+    <string name="fail_reason_vcard_parse_error">Couldn\'t parse vCard for unexpected reason</string>
 
     <!-- The failed reason shown when vCard importer doesn't support the format.
          This may be shown when the vCard is corrupted [CHAR LIMIT=40] -->
     <string name="fail_reason_not_supported">The format is not supported.</string>
 
     <!-- Message used when vCard import has failed. [CHAR LIMIT=40] -->
-    <string name="vcard_import_failed">Failed to import vCard</string>
+    <string name="vcard_import_failed">Couldn\'t import vCard</string>
 
     <!-- The failure message shown when the system could not find any vCard file.
          (with extension ".vcf" in (USB) storage.)
@@ -824,11 +824,11 @@
     <string name="import_failure_no_vcard_file" product="default">No vCard file found on the SD card</string>
 
     <!-- Fail reason shown when vCard importer failed to look over meta information stored in vCard file(s). -->
-    <string name="fail_reason_failed_to_collect_vcard_meta_info">Failed to collect meta information of given vCard file(s).</string>
+    <string name="fail_reason_failed_to_collect_vcard_meta_info">Couldn\'t collect meta information of given vCard file(s).</string>
 
     <!-- The failed reason shown when the import of some of vCard files failed during multiple vCard
          files import. It includes the case where all files were failed to be imported. -->
-    <string name="fail_reason_failed_to_read_files">One or more files failed to be imported (%s).</string>
+    <string name="fail_reason_failed_to_read_files">One or more files couldn\'t be imported (%s).</string>
 
     <!-- The failed reason which should not be shown but it may in some buggy condition. -->
     <string name="fail_reason_unknown">Unknown error</string>
@@ -857,7 +857,7 @@
     <string name="importing_vcard_description">Importing <xliff:g id="name" example="Joe Due">%s</xliff:g></string>
 
     <!-- Dialog title shown when reading vCard data failed [CHAR LIMIT=40] -->
-    <string name="reading_vcard_failed_title">Failed to Read vCard data</string>
+    <string name="reading_vcard_failed_title">Couldn\'t read vCard data</string>
 
     <!-- The title shown when reading vCard is canceled (probably by a user)
          [CHAR LIMIT=40] -->
@@ -905,10 +905,10 @@
     <string name="confirm_export_message">Export your contact list to file \"<xliff:g id="vcard_filename">%s</xliff:g>\"?</string>
 
     <!-- Dialog title shown when exporting Contact data failed -->
-    <string name="exporting_contact_failed_title">Failed to export contact data</string>
+    <string name="exporting_contact_failed_title">Couldn\'t export contact data</string>
 
     <!-- Dialog message shown when exporting Contact data failed -->
-    <string name="exporting_contact_failed_message">Failed to export contact data.\nReason for failure: \"<xliff:g id="fail_reason">%s</xliff:g>\"</string>
+    <string name="exporting_contact_failed_message">Couldn\'t export contact data.\nReason: \"<xliff:g id="fail_reason">%s</xliff:g>\"</string>
 
     <!-- The failed reason shown when there's no contact which is allowed to be exported.
          Note that user may have contacts data but all of them are probably not allowed to be
@@ -951,7 +951,7 @@
 
     <!-- The error reason the vCard composer "may" emit when database is corrupted or
          something is going wrong. Usually users should not see this text. -->
-    <string name="composer_failed_to_get_database_infomation">Failed to get database information</string>
+    <string name="composer_failed_to_get_database_infomation">Couldn\'t get database information</string>
 
     <!-- This error message shown when the user actually have no contact
          (e.g. just after data-wiping), or, data providers of the contact list prohibits their
@@ -991,7 +991,7 @@
     <string name="cancel_export_confirmation_message">Are you sure to cancel exporting <xliff:g id="filename" example="export.vcf">%s</xliff:g>?</string>
 
     <!-- Title shown in a Dialog telling users cancel vCard import/export operation is failed. [CHAR LIMIT=40] -->
-    <string name="cancel_vcard_import_or_export_failed">Failed to cancel vCard import/export</string>
+    <string name="cancel_vcard_import_or_export_failed">Couldn\'t cancel vCard import/export</string>
 
     <!-- The string used to describe Contacts as a searchable item within system search settings. -->
     <string name="search_settings_description">Names of your contacts</string>
@@ -1630,16 +1630,16 @@
     <string name="notification_new_voicemail_ticker">New voicemail from <xliff:g id="caller">%1$s</xliff:g></string>
 
     <!-- Message to show when there is an error playing back the voicemail. [CHAR LIMIT=40] -->
-    <string name="voicemail_playback_error">failed to play voicemail</string>
+    <string name="voicemail_playback_error">Couldn\'t play voicemail.</string>
 
     <!-- Message to display before we have prepared the media player, i.e. before we know duration. [CHAR LIMIT=40] -->
-    <string name="voicemail_buffering">buffering...</string>
+    <string name="voicemail_buffering">Buffering\u2026</string>
 
     <!-- Message to display whilst we are waiting for the content to be fetched. [CHAR LIMIT=40] -->
-    <string name="voicemail_fetching_content">fetching voicemail...</string>
+    <string name="voicemail_fetching_content">Fetching voicemail\u2026</string>
 
     <!-- Message to display if we fail to get content within a suitable time period. [CHAR LIMIT=40] -->
-    <string name="voicemail_fetching_timout">failed to fetch voicemail</string>
+    <string name="voicemail_fetching_timout">Couldn\'t fetch voicemail.</string>
 
     <!-- The header in the call log used to identify missed calls and voicemail that have not yet been consumed [CHAR LIMIT=10] -->
     <string name="call_log_new_header">New</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index dc675b3..418d632 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -79,12 +79,14 @@
 
     <style name="ContactDetailActivityTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
+        <item name="android:actionBarItemBackground">@drawable/action_bar_item_background</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
     </style>
 
     <style name="EditorActivityTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
+        <item name="android:actionBarItemBackground">@drawable/action_bar_item_background</item>
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
@@ -125,6 +127,7 @@
 
     <style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
+        <item name="android:actionBarItemBackground">@drawable/action_bar_item_background</item>
         <item name="android:actionBarTabStyle">@style/ContactsActionBarTabView</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
@@ -174,6 +177,7 @@
     <!-- TODO: Clean up this file so themes aren't copied. -->
     <style name="GroupDetailTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
+        <item name="android:actionBarItemBackground">@drawable/action_bar_item_background</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
         <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
diff --git a/src/com/android/contacts/ContactTileLoaderFactory.java b/src/com/android/contacts/ContactTileLoaderFactory.java
index 30bd7e4..28f2164 100644
--- a/src/com/android/contacts/ContactTileLoaderFactory.java
+++ b/src/com/android/contacts/ContactTileLoaderFactory.java
@@ -23,7 +23,6 @@
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Contacts.Data;
 
 /**
  * Used to create {@link CursorLoader}s to load different groups of {@link ContactTileView}s
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index c863b23..c3dc593 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -22,34 +22,23 @@
 import com.android.contacts.R;
 import com.android.contacts.detail.ContactDetailDisplayUtils;
 import com.android.contacts.detail.ContactDetailFragment;
-import com.android.contacts.detail.ContactDetailFragmentCarousel;
-import com.android.contacts.detail.ContactDetailTabCarousel;
-import com.android.contacts.detail.ContactDetailUpdatesFragment;
-import com.android.contacts.detail.ContactDetailViewPagerAdapter;
+import com.android.contacts.detail.ContactDetailLayoutController;
 import com.android.contacts.detail.ContactLoaderFragment;
 import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener;
-import com.android.contacts.detail.TabCarouselScrollManager;
 import com.android.contacts.interactions.ContactDeletionInteraction;
 import com.android.contacts.model.AccountWithDataSet;
 import com.android.contacts.util.PhoneCapabilityTester;
 
 import android.app.ActionBar;
 import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
 import android.content.ActivityNotFoundException;
 import android.content.ContentValues;
-import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.v13.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v4.view.ViewPager.OnPageChangeListener;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -61,7 +50,6 @@
 
 import java.util.ArrayList;
 
-// TODO: Use {@link ContactDetailLayoutController} so there isn't duplicated code
 public class ContactDetailActivity extends ContactsActivity {
     private static final String TAG = "ContactDetailActivity";
 
@@ -72,38 +60,15 @@
      */
     public static final String INTENT_KEY_IGNORE_DEFAULT_UP_BEHAVIOR = "ignoreDefaultUpBehavior";
 
-    private static final String KEY_CONTACT_HAS_UPDATES = "contactHasUpdates";
-    private static final String KEY_CURRENT_PAGE_INDEX = "currentPageIndex";
-
-    public static final int FRAGMENT_COUNT = 2;
-
     private ContactLoader.Result mContactData;
     private Uri mLookupUri;
     private boolean mIgnoreDefaultUpBehavior;
 
+    private ContactDetailLayoutController mContactDetailLayoutController;
     private ContactLoaderFragment mLoaderFragment;
-    private ContactDetailFragment mDetailFragment;
-    private ContactDetailUpdatesFragment mUpdatesFragment;
-
-    private ContactDetailTabCarousel mTabCarousel;
-    private ViewPager mViewPager;
-
-    private ContactDetailFragmentCarousel mFragmentCarousel;
-
-    private boolean mFragmentsAddedToFragmentManager;
-
-    private ViewGroup mRootView;
-    private ViewGroup mContentView;
-    private LayoutInflater mInflater;
 
     private Handler mHandler = new Handler();
 
-    /**
-     * Whether or not the contact has updates, which dictates whether the
-     * {@link ContactDetailUpdatesFragment} will be shown.
-     */
-    private boolean mContactHasUpdates;
-
     @Override
     public void onCreate(Bundle savedState) {
         super.onCreate(savedState);
@@ -128,41 +93,10 @@
                 INTENT_KEY_IGNORE_DEFAULT_UP_BEHAVIOR, false);
 
         setContentView(R.layout.contact_detail_activity);
-        mRootView = (ViewGroup) findViewById(R.id.contact_detail_view);
-        mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
-        FragmentManager fragmentManager = getFragmentManager();
-        mDetailFragment = (ContactDetailFragment)
-                fragmentManager.findFragmentByTag(
-                ContactDetailViewPagerAdapter.ABOUT_FRAGMENT_TAG);
-        mUpdatesFragment = (ContactDetailUpdatesFragment)
-                fragmentManager.findFragmentByTag(
-                ContactDetailViewPagerAdapter.UPDTES_FRAGMENT_TAG);
-
-        // If the fragment were found in the {@link FragmentManager} then we don't need to add
-        // it again.
-        if (mDetailFragment != null) {
-            mFragmentsAddedToFragmentManager = true;
-        } else {
-            // Otherwise, create the fragments dynamically and remember to add them to the
-            // {@link FragmentManager}.
-            mDetailFragment = new ContactDetailFragment();
-            mUpdatesFragment = new ContactDetailUpdatesFragment();
-            mFragmentsAddedToFragmentManager = false;
-        }
-
-        mDetailFragment.setListener(mFragmentListener);
-        mDetailFragment.setData(mLookupUri, mContactData);
-        mUpdatesFragment.setData(mLookupUri, mContactData);
-
-        if (savedState != null) {
-            mContactHasUpdates = savedState.getBoolean(KEY_CONTACT_HAS_UPDATES);
-            if (mContactHasUpdates) {
-                setupContactWithUpdates(savedState.getInt(KEY_CURRENT_PAGE_INDEX, 0));
-            } else {
-                setupContactWithoutUpdates();
-            }
-        }
+        mContactDetailLayoutController = new ContactDetailLayoutController(this, savedState,
+                getFragmentManager(), findViewById(R.id.contact_detail_container),
+                mContactDetailFragmentListener);
 
         // We want the UP affordance but no app icon.
         // Setting HOME_AS_UP, SHOW_TITLE and clearing SHOW_HOME does the trick.
@@ -232,43 +166,19 @@
         if (mLoaderFragment != null && mLoaderFragment.handleKeyDown(keyCode)) return true;
 
         // Otherwise find the correct fragment to handle the event
-        FragmentKeyListener mCurrentFragment;
-        switch (getCurrentPage()) {
-            case 0:
-                mCurrentFragment = mDetailFragment;
-                break;
-            case 1:
-                mCurrentFragment = mUpdatesFragment;
-                break;
-            default:
-                throw new IllegalStateException("Invalid current item for ViewPager");
-        }
+        FragmentKeyListener mCurrentFragment = mContactDetailLayoutController.getCurrentPage();
         if (mCurrentFragment != null && mCurrentFragment.handleKeyDown(keyCode)) return true;
 
         // In the last case, give the key event to the superclass.
         return super.onKeyDown(keyCode, event);
     }
 
-    private int getCurrentPage() {
-        // If the contact doesn't have any social updates, there is only 1 page (detail fragment).
-        if (!mContactHasUpdates) {
-            return 0;
-        }
-        // Otherwise find the current page based on the {@link ViewPager} or fragment carousel.
-        if (mViewPager != null) {
-            return mViewPager.getCurrentItem();
-        } else if (mFragmentCarousel != null) {
-            return mFragmentCarousel.getCurrentPage();
-        }
-        throw new IllegalStateException("Can't figure out the currently selected page. If the " +
-                "contact has social updates, there must be a ViewPager or fragment carousel");
-    }
-
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        outState.putBoolean(KEY_CONTACT_HAS_UPDATES, mContactHasUpdates);
-        outState.putInt(KEY_CURRENT_PAGE_INDEX, getCurrentPage());
+        if (mContactDetailLayoutController != null) {
+            mContactDetailLayoutController.onSaveInstanceState(outState);
+        }
     }
 
     private final ContactLoaderFragmentListener mLoaderFragmentListener =
@@ -295,14 +205,9 @@
                     }
                     mContactData = result;
                     mLookupUri = result.getLookupUri();
-                    mContactHasUpdates = !result.getStreamItems().isEmpty();
                     invalidateOptionsMenu();
                     setupTitle();
-                    if (mContactHasUpdates) {
-                        setupContactWithUpdates(null /* Don't change the current page */);
-                    } else {
-                        setupContactWithoutUpdates();
-                    }
+                    mContactDetailLayoutController.setContactData(mContactData);
                 }
             });
         }
@@ -331,117 +236,7 @@
         actionBar.setSubtitle(company);
     }
 
-    /**
-     * Setup the layout for the contact with updates. Pass in the index of the current page to
-     * select or null if the current selection should be left as is.
-     */
-    private void setupContactWithUpdates(Integer currentPageIndex) {
-        if (mContentView == null) {
-            mContentView = (ViewGroup) mInflater.inflate(
-                    R.layout.contact_detail_container_with_updates, mRootView, false);
-            mRootView.addView(mContentView);
-
-            // Make sure all needed views are retrieved. Note that narrow width screens have a
-            // {@link ViewPager} and {@link ContactDetailTabCarousel}, while wide width screens have
-            // a {@link ContactDetailFragmentCarousel}.
-            mTabCarousel = (ContactDetailTabCarousel) findViewById(R.id.tab_carousel);
-            if (mTabCarousel != null) {
-                mTabCarousel.setListener(mTabCarouselListener);
-                TabCarouselScrollManager.bind(mTabCarousel, mDetailFragment, mUpdatesFragment);
-            }
-
-            mViewPager = (ViewPager) findViewById(R.id.pager);
-            if (mViewPager != null) {
-                // Inflate 2 view containers to pass in as children to the {@link ViewPager},
-                // which will in turn be the parents to the mDetailFragment and mUpdatesFragment
-                // since the fragments must have the same parent view IDs in both landscape and
-                // portrait layouts.
-                ViewGroup detailContainer = (ViewGroup) mInflater.inflate(
-                        R.layout.contact_detail_about_fragment_container, mViewPager, false);
-                ViewGroup updatesContainer = (ViewGroup) mInflater.inflate(
-                        R.layout.contact_detail_updates_fragment_container, mViewPager, false);
-
-                ContactDetailViewPagerAdapter adapter = new ContactDetailViewPagerAdapter();
-                adapter.setAboutFragmentView(detailContainer);
-                adapter.setUpdatesFragmentView(updatesContainer);
-
-                mViewPager.addView(detailContainer);
-                mViewPager.addView(updatesContainer);
-                mViewPager.setAdapter(adapter);
-                mViewPager.setOnPageChangeListener(mOnPageChangeListener);
-
-                if (!mFragmentsAddedToFragmentManager) {
-                    FragmentManager fragmentManager = getFragmentManager();
-                    FragmentTransaction transaction = fragmentManager.beginTransaction();
-                    transaction.add(R.id.about_fragment_container, mDetailFragment,
-                            ContactDetailViewPagerAdapter.ABOUT_FRAGMENT_TAG);
-                    transaction.add(R.id.updates_fragment_container, mUpdatesFragment,
-                            ContactDetailViewPagerAdapter.UPDTES_FRAGMENT_TAG);
-                    transaction.commit();
-                    fragmentManager.executePendingTransactions();
-                }
-
-                // Select page if applicable
-                if (currentPageIndex != null) {
-                    mViewPager.setCurrentItem(currentPageIndex);
-                }
-            }
-
-            mFragmentCarousel = (ContactDetailFragmentCarousel)
-                    findViewById(R.id.fragment_carousel);
-            // Add the fragments to the fragment containers in the carousel using a
-            // {@link FragmentTransaction} if they haven't already been added to the
-            // {@link FragmentManager}.
-            if (mFragmentCarousel != null) {
-                if (!mFragmentsAddedToFragmentManager) {
-                    FragmentManager fragmentManager = getFragmentManager();
-                    FragmentTransaction transaction = fragmentManager.beginTransaction();
-                    transaction.add(R.id.about_fragment_container, mDetailFragment,
-                            ContactDetailViewPagerAdapter.ABOUT_FRAGMENT_TAG);
-                    transaction.add(R.id.updates_fragment_container, mUpdatesFragment,
-                            ContactDetailViewPagerAdapter.UPDTES_FRAGMENT_TAG);
-                    transaction.commit();
-                    fragmentManager.executePendingTransactions();
-                }
-
-                // Select page if applicable
-                if (currentPageIndex != null) {
-                    mFragmentCarousel.setCurrentPage(currentPageIndex);
-                }
-            }
-        }
-
-        // Then reset the contact data to the appropriate views
-        if (mTabCarousel != null) {
-            mTabCarousel.loadData(mContactData);
-        }
-        if (mFragmentCarousel != null && mDetailFragment != null && mUpdatesFragment != null) {
-            mFragmentCarousel.setFragments(mDetailFragment, mUpdatesFragment);
-        }
-        if (mDetailFragment != null) {
-            mDetailFragment.setData(mLookupUri, mContactData);
-        }
-        if (mUpdatesFragment != null) {
-            mUpdatesFragment.setData(mLookupUri, mContactData);
-        }
-    }
-
-    private void setupContactWithoutUpdates() {
-        if (mContentView == null) {
-            mContentView = (ViewGroup) mInflater.inflate(
-                    R.layout.contact_detail_container_without_updates, mRootView, false);
-            mRootView.addView(mContentView);
-            mDetailFragment = (ContactDetailFragment) getFragmentManager().findFragmentById(
-                    R.id.contact_detail_fragment);
-            mDetailFragment.setListener(mFragmentListener);
-        }
-        // Reset contact data
-        if (mDetailFragment != null) {
-            mDetailFragment.setData(mLookupUri, mContactData);
-        }
-    }
-
-    private final ContactDetailFragment.Listener mFragmentListener =
+    private final ContactDetailFragment.Listener mContactDetailFragmentListener =
             new ContactDetailFragment.Listener() {
         @Override
         public void onItemClicked(Intent intent) {
@@ -465,95 +260,6 @@
         }
     };
 
-    public class ViewPagerAdapter extends FragmentPagerAdapter{
-
-        public ViewPagerAdapter(FragmentManager fm) {
-            super(fm);
-        }
-
-        @Override
-        public Fragment getItem(int position) {
-            switch (position) {
-                case 0:
-                    return new ContactDetailFragment();
-                case 1:
-                    return new ContactDetailUpdatesFragment();
-            }
-            throw new IllegalStateException("No fragment at position " + position);
-        }
-
-        @Override
-        public int getCount() {
-            return FRAGMENT_COUNT;
-        }
-    }
-
-    private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {
-
-        @Override
-        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-            // The user is horizontally dragging the {@link ViewPager}, so send
-            // these scroll changes to the tab carousel. Ignore these events though if the carousel
-            // is actually controlling the {@link ViewPager} scrolls because it will already be
-            // in the correct position.
-            if (mViewPager.isFakeDragging()) {
-                return;
-            }
-            int x = (int) ((position + positionOffset) *
-                    mTabCarousel.getAllowedHorizontalScrollLength());
-            mTabCarousel.scrollTo(x, 0);
-        }
-
-        @Override
-        public void onPageSelected(int position) {
-            // Since a new page has been selected by the {@link ViewPager},
-            // update the tab selection in the carousel.
-            mTabCarousel.setCurrentTab(position);
-        }
-
-        @Override
-        public void onPageScrollStateChanged(int state) {}
-
-    };
-
-    private ContactDetailTabCarousel.Listener mTabCarouselListener =
-            new ContactDetailTabCarousel.Listener() {
-
-        @Override
-        public void onTouchDown() {
-            // The user just started scrolling the carousel, so begin "fake dragging" the
-            // {@link ViewPager} if it's not already doing so.
-            if (mViewPager.isFakeDragging()) {
-                return;
-            }
-            mViewPager.beginFakeDrag();
-        }
-
-        @Override
-        public void onTouchUp() {
-            // The user just stopped scrolling the carousel, so stop "fake dragging" the
-            // {@link ViewPager} if was doing so before.
-            if (mViewPager.isFakeDragging()) {
-                mViewPager.endFakeDrag();
-            }
-        }
-
-        @Override
-        public void onScrollChanged(int l, int t, int oldl, int oldt) {
-            // The user is scrolling the carousel, so send the scroll deltas to the
-            // {@link ViewPager} so it can move in sync.
-            if (mViewPager.isFakeDragging()) {
-                mViewPager.fakeDragBy(oldl-l);
-            }
-        }
-
-        @Override
-        public void onTabSelected(int position) {
-            // The user selected a tab, so update the {@link ViewPager}
-            mViewPager.setCurrentItem(position);
-        }
-    };
-
     /**
      * This interface should be implemented by {@link Fragment}s within this
      * activity so that the activity can determine whether the currently
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index 2936110..c69cc04 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -151,11 +151,6 @@
         public void onSaveFinished(Intent resultIntent) {
             if (resultIntent != null) {
                 startActivity(resultIntent);
-            } else {
-                // Navigate home
-                Intent intent = new Intent(ContactEditorActivity.this, PeopleActivity.class);
-                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                startActivity(intent);
             }
             finish();
         }
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 3fd1558..048ae5f 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -46,6 +46,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.CallLog.Calls;
+import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Intents.UI;
 import android.support.v13.app.FragmentPagerAdapter;
 import android.support.v4.view.ViewPager;
@@ -57,9 +58,10 @@
 import android.view.MenuItem;
 import android.view.MenuItem.OnMenuItemClickListener;
 import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
+import android.view.View.OnClickListener;
 import android.view.ViewConfiguration;
 import android.view.inputmethod.InputMethodManager;
+import android.widget.PopupMenu;
 import android.widget.SearchView;
 import android.widget.SearchView.OnCloseListener;
 import android.widget.SearchView.OnQueryTextListener;
@@ -244,6 +246,21 @@
     private boolean mInSearchUi;
     private SearchView mSearchView;
 
+    private final OnClickListener mFilterOptionClickListener = new OnClickListener() {
+        @Override
+        public void onClick(View view) {
+            final PopupMenu popupMenu = new PopupMenu(DialtactsActivity.this, view);
+            final Menu menu = popupMenu.getMenu();
+            popupMenu.inflate(R.menu.dialtacts_search_options);
+            final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
+            filterOptionMenuItem.setOnMenuItemClickListener(mFilterOptionsMenuItemClickListener);
+            final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
+            addContactOptionMenuItem.setIntent(
+                    new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
+            popupMenu.show();
+        }
+    };
+
     /**
      * The index of the Fragment (or, the tab) that has last been manually selected.
      * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
@@ -393,7 +410,7 @@
 
     private void prepareSearchView() {
         final View searchViewLayout =
-                getLayoutInflater().inflate(R.layout.custom_action_bar, null);
+                getLayoutInflater().inflate(R.layout.dialtacts_custom_action_bar, null);
         mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
         mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
         mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
@@ -406,6 +423,14 @@
         mSearchView.setIconifiedByDefault(true);
         mSearchView.setQueryHint(getString(R.string.hint_findContacts));
         mSearchView.setIconified(false);
+
+        if (!ViewConfiguration.get(this).hasPermanentMenuKey()) {
+            // Filter option menu should be shown on the right side of SearchView.
+            final View filterOptionView = searchViewLayout.findViewById(R.id.search_option);
+            filterOptionView.setVisibility(View.VISIBLE);
+            filterOptionView.setOnClickListener(mFilterOptionClickListener);
+        }
+
         getActionBar().setCustomView(searchViewLayout,
                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
     }
@@ -547,13 +572,14 @@
         } else {
             tabIndex = mLastManuallySelectedFragment;
         }
+
+        final int previousItemIndex = mViewPager.getCurrentItem();
         mViewPager.setCurrentItem(tabIndex, false /* smoothScroll */);
-        if (mViewPager.getCurrentItem() == tabIndex) {
-            mPageChangeListener.setCurrentPosition(tabIndex);
-            sendFragmentVisibilityChange(tabIndex, true);
-        } else {
-            getActionBar().selectTab(getActionBar().getTabAt(tabIndex));
+        if (previousItemIndex != tabIndex) {
+            sendFragmentVisibilityChange(previousItemIndex, false);
         }
+        mPageChangeListener.setCurrentPosition(tabIndex);
+        sendFragmentVisibilityChange(tabIndex, true);
 
         // Restore to the previous manual selection
         mLastManuallySelectedFragment = savedTabIndex;
@@ -671,13 +697,23 @@
     public boolean onPrepareOptionsMenu(Menu menu) {
         final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
         final MenuItem filterOptionMenuItem = menu.findItem(R.id.filter_option);
+        final MenuItem addContactOptionMenuItem = menu.findItem(R.id.add_contact);
         final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
         Tab tab = getActionBar().getSelectedTab();
         if (mInSearchUi) {
             searchMenuItem.setVisible(false);
-            filterOptionMenuItem.setVisible(true);
-            filterOptionMenuItem.setOnMenuItemClickListener(
-                    mFilterOptionsMenuItemClickListener);
+            if (ViewConfiguration.get(this).hasPermanentMenuKey()) {
+                filterOptionMenuItem.setVisible(true);
+                filterOptionMenuItem.setOnMenuItemClickListener(
+                        mFilterOptionsMenuItemClickListener);
+                addContactOptionMenuItem.setVisible(true);
+                addContactOptionMenuItem.setIntent(
+                        new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
+            } else {
+                // Filter option menu should be not be shown as a overflow menu.
+                filterOptionMenuItem.setVisible(false);
+                addContactOptionMenuItem.setVisible(false);
+            }
             callSettingsMenuItem.setVisible(false);
         } else {
             final boolean showCallSettingsMenu;
@@ -692,6 +728,7 @@
                 showCallSettingsMenu = true;
             }
             filterOptionMenuItem.setVisible(false);
+            addContactOptionMenuItem.setVisible(false);
 
             if (showCallSettingsMenu) {
                 callSettingsMenuItem.setVisible(true);
diff --git a/src/com/android/contacts/activities/GroupEditorActivity.java b/src/com/android/contacts/activities/GroupEditorActivity.java
index b397c19..544cd16 100644
--- a/src/com/android/contacts/activities/GroupEditorActivity.java
+++ b/src/com/android/contacts/activities/GroupEditorActivity.java
@@ -147,6 +147,11 @@
         }
 
         @Override
+        public void onAccountsNotFound() {
+            finish();
+        }
+
+        @Override
         public void onSaveFinished(int resultCode, Intent resultIntent) {
             // TODO: Collapse these 2 cases into 1 that will just launch an intent with the VIEW
             // action to see the group URI (when group URIs are supported)
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index b226ad1..1c69ef9 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -157,6 +157,7 @@
     /** ViewPager for swipe, used only on the phone (i.e. one-pane mode) */
     private ViewPager mTabPager;
     private TabPagerAdapter mTabPagerAdapter;
+    private final TabPagerListener mTabPagerListener = new TabPagerListener();
 
     private ContactDetailLayoutController mContactDetailLayoutController;
 
@@ -314,7 +315,7 @@
             mTabPager = getView(R.id.tab_pager);
             mTabPagerAdapter = new TabPagerAdapter();
             mTabPager.setAdapter(mTabPagerAdapter);
-            mTabPager.setOnPageChangeListener(new TabPagerListener());
+            mTabPager.setOnPageChangeListener(mTabPagerListener);
 
             final String FAVORITE_TAG = "tab-pager-favorite";
             final String ALL_TAG = "tab-pager-all";
@@ -449,6 +450,9 @@
         // Re-register the listener, which may have been cleared when onSaveInstanceState was
         // called.  See also: onSaveInstanceState
         mActionBarAdapter.setListener(this);
+        if (mTabPager != null) {
+            mTabPager.setOnPageChangeListener(mTabPagerListener);
+        }
         // Current tab may have changed since the last onSaveInstanceState().  Make sure
         // the actual contents match the tab.
         updateFragmentsVisibility();
@@ -1527,6 +1531,9 @@
         // in order to avoid doing fragment transactions after it.
         // TODO Figure out a better way to deal with the issue.
         mActionBarAdapter.setListener(null);
+        if (mTabPager != null) {
+            mTabPager.setOnPageChangeListener(null);
+        }
     }
 
     @Override
diff --git a/src/com/android/contacts/detail/CarouselTab.java b/src/com/android/contacts/detail/CarouselTab.java
index 26397ff..677f0ad 100644
--- a/src/com/android/contacts/detail/CarouselTab.java
+++ b/src/com/android/contacts/detail/CarouselTab.java
@@ -78,13 +78,6 @@
     }
 
     @Override
-    public void enableAlphaLayer() {
-        if (mAlphaLayer != null) {
-            mAlphaLayer.setVisibility(View.VISIBLE);
-        }
-    }
-
-    @Override
     public void enableTouchInterceptor(OnClickListener clickListener) {
         if (mTouchInterceptLayer != null) {
             mTouchInterceptLayer.setVisibility(View.VISIBLE);
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index bbf9d5b..ca99c79 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -183,6 +183,11 @@
     private View mEmptyView;
 
     /**
+     * Initial alpha value to set on the alpha layer.
+     */
+    private float mInitialAlphaValue;
+
+    /**
      * This optional view adds an alpha layer over the entire fragment.
      */
     private View mAlphaLayer;
@@ -285,8 +290,9 @@
         // Don't set it to mListView yet.  We do so later when we bind the adapter.
         mEmptyView = mView.findViewById(android.R.id.empty);
 
-        mAlphaLayer = mView.findViewById(R.id.alpha_overlay);
         mTouchInterceptLayer = mView.findViewById(R.id.touch_intercept_overlay);
+        mAlphaLayer = mView.findViewById(R.id.alpha_overlay);
+        ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, mInitialAlphaValue);
 
         mQuickFixButton = (Button) mView.findViewById(R.id.contact_quick_fix);
         mQuickFixButton.setOnClickListener(new OnClickListener() {
@@ -315,13 +321,12 @@
 
     @Override
     public void setAlphaLayerValue(float alpha) {
-        ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, alpha);
-    }
-
-    @Override
-    public void enableAlphaLayer() {
-        if (mAlphaLayer != null) {
-            mAlphaLayer.setVisibility(View.VISIBLE);
+        // If the alpha layer is not ready yet, store it for later when the view is initialized
+        if (mAlphaLayer == null) {
+            mInitialAlphaValue = alpha;
+        } else {
+            // Otherwise set the value immediately
+            ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, alpha);
         }
     }
 
diff --git a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
index 7efcc51..106ff0e 100644
--- a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
@@ -54,16 +54,39 @@
      */
     private int mUpperThreshold = Integer.MIN_VALUE;
 
+    /**
+     * Minimum width of a fragment (if there is more than 1 fragment in the carousel, then this is
+     * the width of one of the fragments).
+     */
+    private int mMinFragmentWidth = Integer.MIN_VALUE;
+
+    /**
+     * Maximum alpha value of the overlay on the fragment that is not currently selected
+     * (if there are 1+ fragments in the carousel).
+     */
+    private static final float MAX_ALPHA = 0.5f;
+
+    /**
+     * Fragment width (if there are 1+ fragments in the carousel) as defined as a fraction of the
+     * screen width.
+     */
+    private static final float FRAGMENT_WIDTH_SCREEN_WIDTH_FRACTION = 0.85f;
+
     private static final int ABOUT_PAGE = 0;
     private static final int UPDATES_PAGE = 1;
 
+    private static final int MAX_FRAGMENT_VIEW_COUNT = 2;
+
+    private boolean mEnableSwipe;
+
     private int mCurrentPage = ABOUT_PAGE;
     private int mLastScrollPosition;
 
     private ViewOverlay mAboutFragment;
     private ViewOverlay mUpdatesFragment;
 
-    private static final float MAX_ALPHA = 0.5f;
+    private View mDetailFragmentView;
+    private View mUpdatesFragmentView;
 
     private final Handler mHandler = new Handler();
 
@@ -87,42 +110,86 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int screenWidth = MeasureSpec.getSize(widthMeasureSpec);
+        int screenHeight = MeasureSpec.getSize(heightMeasureSpec);
 
         // Take the width of this view as the width of the screen and compute necessary thresholds.
         // Only do this computation 1x.
         if (mAllowedHorizontalScrollLength == Integer.MIN_VALUE) {
-            int screenWidth = MeasureSpec.getSize(widthMeasureSpec);
-            int fragmentWidth = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.detail_fragment_carousel_fragment_width);
-            mAllowedHorizontalScrollLength = (2 * fragmentWidth) - screenWidth;
-            mLowerThreshold = (screenWidth - fragmentWidth) / 2;
+            mMinFragmentWidth = (int) (FRAGMENT_WIDTH_SCREEN_WIDTH_FRACTION * screenWidth);
+            mAllowedHorizontalScrollLength = (MAX_FRAGMENT_VIEW_COUNT * mMinFragmentWidth) -
+                    screenWidth;
+            mLowerThreshold = (screenWidth - mMinFragmentWidth) / MAX_FRAGMENT_VIEW_COUNT;
             mUpperThreshold = mAllowedHorizontalScrollLength - mLowerThreshold;
         }
+
+        if (getChildCount() > 0) {
+            View child = getChildAt(0);
+            // If we enable swipe, then the {@link LinearLayout} child width must be the sum of the
+            // width of all its children fragments.
+            if (mEnableSwipe) {
+                child.measure(MeasureSpec.makeMeasureSpec(
+                        mMinFragmentWidth * MAX_FRAGMENT_VIEW_COUNT, MeasureSpec.EXACTLY),
+                        MeasureSpec.makeMeasureSpec(screenHeight, MeasureSpec.EXACTLY));
+            } else {
+                // Otherwise, the {@link LinearLayout} child width will just be the screen width
+                // because it will only have 1 child fragment.
+                child.measure(MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY),
+                        MeasureSpec.makeMeasureSpec(screenHeight, MeasureSpec.EXACTLY));
+            }
+        }
+
+        setMeasuredDimension(
+                resolveSize(screenWidth, widthMeasureSpec),
+                resolveSize(screenHeight, heightMeasureSpec));
     }
 
+    /**
+     * Set the current page. This auto-scrolls the carousel to the current page and dims out
+     * the non-selected page.
+     */
     public void setCurrentPage(int pageIndex) {
-        if (mCurrentPage != pageIndex) {
-            mCurrentPage = pageIndex;
+        mCurrentPage = pageIndex;
 
-            // This method could have been called before the view has been measured, so snap to edge
-            // only after the view is ready.
-            postRunnableToSnapToEdge();
+        if (mAboutFragment != null && mUpdatesFragment != null) {
+            mAboutFragment.setAlphaLayerValue(mCurrentPage == ABOUT_PAGE ? 0 : MAX_ALPHA);
+            mUpdatesFragment.setAlphaLayerValue(mCurrentPage == UPDATES_PAGE ? 0 : MAX_ALPHA);
+            snapToEdge();
         }
     }
 
+    /**
+     * Set the view containers for the detail and updates fragment.
+     */
+    public void setFragmentViews(View detailFragmentView, View updatesFragmentView) {
+        mDetailFragmentView = detailFragmentView;
+        mUpdatesFragmentView = updatesFragmentView;
+    }
+
+    /**
+     * Set the detail and updates fragment.
+     */
     public void setFragments(ViewOverlay aboutFragment, ViewOverlay updatesFragment) {
         mAboutFragment = aboutFragment;
-        mAboutFragment.enableAlphaLayer();
-        mAboutFragment.setAlphaLayerValue(mCurrentPage == ABOUT_PAGE ? 0 : MAX_ALPHA);
-
         mUpdatesFragment = updatesFragment;
-        mUpdatesFragment.enableAlphaLayer();
-        mUpdatesFragment.setAlphaLayerValue(mCurrentPage == UPDATES_PAGE ? 0 : MAX_ALPHA);
+    }
 
-        // This method could have been called before the view has been measured, so snap to edge
-        // only after the view is ready.
-        postRunnableToSnapToEdge();
+    /**
+     * Enable swiping if the detail and update fragments should be showing. Otherwise disable
+     * swiping if only the detail fragment should be showing.
+     */
+    public void enableSwipe(boolean enable) {
+        if (mEnableSwipe != enable) {
+            mEnableSwipe = enable;
+            if (mUpdatesFragmentView != null) {
+                mUpdatesFragmentView.setVisibility(enable ? View.VISIBLE : View.GONE);
+                requestLayout();
+                invalidate();
+            }
+            // This method could have been called before the view has been measured (i.e.
+            // immediately after a rotation), so snap to edge only after the view is ready.
+            postRunnableToSnapToEdge();
+        }
     }
 
     /**
@@ -134,8 +201,7 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (isAttachedToWindow() && mAboutFragment != null &&
-                        mUpdatesFragment != null) {
+                if (isAttachedToWindow() && mAboutFragment != null && mUpdatesFragment != null) {
                     snapToEdge();
                 }
             }
@@ -187,6 +253,9 @@
     @Override
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
         super.onScrollChanged(l, t, oldl, oldt);
+        if (!mEnableSwipe) {
+            return;
+        }
         mLastScrollPosition= l;
         updateAlphaLayers();
     }
@@ -223,6 +292,9 @@
 
     @Override
     public boolean onTouch(View v, MotionEvent event) {
+        if (!mEnableSwipe) {
+            return false;
+        }
         if (event.getAction() == MotionEvent.ACTION_UP) {
             mCurrentPage = getDesiredPage();
             snapToEdge();
diff --git a/src/com/android/contacts/detail/ContactDetailLayoutController.java b/src/com/android/contacts/detail/ContactDetailLayoutController.java
index f3cc016..a10431b 100644
--- a/src/com/android/contacts/detail/ContactDetailLayoutController.java
+++ b/src/com/android/contacts/detail/ContactDetailLayoutController.java
@@ -18,7 +18,7 @@
 
 import com.android.contacts.ContactLoader;
 import com.android.contacts.R;
-import com.android.contacts.activities.PeopleActivity.ContactDetailFragmentListener;
+import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
 
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -35,12 +35,17 @@
  */
 public class ContactDetailLayoutController {
 
-    public static final int FRAGMENT_COUNT = 2;
-
     private static final String KEY_CONTACT_HAS_UPDATES = "contactHasUpdates";
+    private static final String KEY_CURRENT_PAGE_INDEX = "currentPageIndex";
 
+    /**
+     * There are 3 possible layouts for the contact detail screen:
+     * 1. TWO_COLUMN - Tall and wide screen so the 2 pages can be shown side-by-side
+     * 2. VIEW_PAGER_AND_TAB_CAROUSEL - Tall and narrow screen to allow swipe between the 2 pages
+     * 3. FRAGMENT_CAROUSEL- Short and wide screen to allow half of the other page to show at a time
+     */
     private enum LayoutMode {
-        TWO_COLUMN, VIEW_PAGER_AND_CAROUSEL,
+        TWO_COLUMN, VIEW_PAGER_AND_TAB_CAROUSEL, FRAGMENT_CAROUSEL,
     }
 
     private final LayoutInflater mLayoutInflater;
@@ -56,7 +61,9 @@
     private final ContactDetailTabCarousel mTabCarousel;
     private ContactDetailViewPagerAdapter mViewPagerAdapter;
 
-    private ContactDetailFragmentListener mContactDetailFragmentListener;
+    private ContactDetailFragmentCarousel mFragmentCarousel;
+
+    private ContactDetailFragment.Listener mContactDetailFragmentListener;
 
     private ContactLoader.Result mContactData;
 
@@ -65,7 +72,7 @@
     private LayoutMode mLayoutMode;
 
     public ContactDetailLayoutController(Context context, Bundle savedState,
-            FragmentManager fragmentManager, View viewContainer, ContactDetailFragmentListener
+            FragmentManager fragmentManager, View viewContainer, ContactDetailFragment.Listener
             contactDetailFragmentListener) {
 
         if (fragmentManager == null) {
@@ -82,16 +89,21 @@
         mViewPager = (ViewPager) viewContainer.findViewById(R.id.pager);
         mTabCarousel = (ContactDetailTabCarousel) viewContainer.findViewById(R.id.tab_carousel);
 
-        // Retrieve views in case this is 2-column layout mode
+        // Retrieve view in case this is in fragment carousel mode
+        mFragmentCarousel = (ContactDetailFragmentCarousel) viewContainer.findViewById(
+                R.id.fragment_carousel);
+
+        // Retrieve container views in case they are already in the XML layout
         mDetailFragmentView = viewContainer.findViewById(R.id.about_fragment_container);
         mUpdatesFragmentView = viewContainer.findViewById(R.id.updates_fragment_container);
 
-        // Determine the layout mode based on whether the {@link ViewPager} is null or not. If the
-        // {@link ViewPager} is null, then this is a wide screen and the content can be displayed
-        // in 2 columns side by side. If the {@link ViewPager} is non-null, then this is a narrow
-        // screen and the user will need to swipe to see all the data.
-        mLayoutMode = (mViewPager == null) ? LayoutMode.TWO_COLUMN :
-                LayoutMode.VIEW_PAGER_AND_CAROUSEL;
+        // Determine the layout mode based on the presence of certain views in the layout XML.
+        if (mViewPager != null) {
+            mLayoutMode = LayoutMode.VIEW_PAGER_AND_TAB_CAROUSEL;
+        } else {
+            mLayoutMode = (mFragmentCarousel != null) ? LayoutMode.FRAGMENT_CAROUSEL :
+                    LayoutMode.TWO_COLUMN;
+        }
 
         initialize(savedState);
     }
@@ -114,9 +126,15 @@
 
         mDetailFragment.setListener(mContactDetailFragmentListener);
 
+        // Read from savedState if possible
+        int currentPageIndex = 0;
+        if (savedState != null) {
+            mContactHasUpdates = savedState.getBoolean(KEY_CONTACT_HAS_UPDATES);
+            currentPageIndex = savedState.getInt(KEY_CURRENT_PAGE_INDEX, 0);
+        }
+
         switch (mLayoutMode) {
-            case VIEW_PAGER_AND_CAROUSEL: {
-                mTabCarousel.setListener(mTabCarouselListener);
+            case VIEW_PAGER_AND_TAB_CAROUSEL: {
                 // Inflate 2 view containers to pass in as children to the {@link ViewPager},
                 // which will in turn be the parents to the mDetailFragment and mUpdatesFragment
                 // since the fragments must have the same parent view IDs in both landscape and
@@ -135,19 +153,19 @@
                 mViewPager.setAdapter(mViewPagerAdapter);
                 mViewPager.setOnPageChangeListener(mOnPageChangeListener);
 
-                FragmentTransaction transaction = mFragmentManager.beginTransaction();
                 if (!fragmentsAddedToFragmentManager) {
+                    FragmentTransaction transaction = mFragmentManager.beginTransaction();
                     transaction.add(R.id.about_fragment_container, mDetailFragment,
                             ContactDetailViewPagerAdapter.ABOUT_FRAGMENT_TAG);
                     transaction.add(R.id.updates_fragment_container, mUpdatesFragment,
                             ContactDetailViewPagerAdapter.UPDTES_FRAGMENT_TAG);
-                } else {
-                    transaction.show(mDetailFragment);
-                    transaction.show(mUpdatesFragment);
+                    transaction.commit();
+                    mFragmentManager.executePendingTransactions();
                 }
-                transaction.commit();
-                mFragmentManager.executePendingTransactions();
+
+                mTabCarousel.setListener(mTabCarouselListener);
                 TabCarouselScrollManager.bind(mTabCarousel, mDetailFragment, mUpdatesFragment);
+                mViewPager.setCurrentItem(currentPageIndex);
                 break;
             }
             case TWO_COLUMN: {
@@ -160,11 +178,32 @@
                     transaction.commit();
                     mFragmentManager.executePendingTransactions();
                 }
+                break;
+            }
+            case FRAGMENT_CAROUSEL: {
+                // Add the fragments to the fragment containers in the carousel using a
+                // {@link FragmentTransaction} if they haven't already been added to the
+                // {@link FragmentManager}.
+                if (!fragmentsAddedToFragmentManager) {
+                    FragmentTransaction transaction = mFragmentManager.beginTransaction();
+                    transaction.add(R.id.about_fragment_container, mDetailFragment,
+                            ContactDetailViewPagerAdapter.ABOUT_FRAGMENT_TAG);
+                    transaction.add(R.id.updates_fragment_container, mUpdatesFragment,
+                            ContactDetailViewPagerAdapter.UPDTES_FRAGMENT_TAG);
+                    transaction.commit();
+                    mFragmentManager.executePendingTransactions();
+                }
+
+                mFragmentCarousel.setFragmentViews(mDetailFragmentView, mUpdatesFragmentView);
+                mFragmentCarousel.setFragments(mDetailFragment, mUpdatesFragment);
+                mFragmentCarousel.setCurrentPage(currentPageIndex);
+                break;
             }
         }
 
+        // Setup the layout if we already have a saved state
         if (savedState != null) {
-            if (savedState.getBoolean(KEY_CONTACT_HAS_UPDATES)) {
+            if (mContactHasUpdates) {
                 showContactWithUpdates();
             } else {
                 showContactWithoutUpdates();
@@ -182,7 +221,14 @@
         }
     }
 
+    /**
+     * Setup the layout for the contact with updates. Pass in the index of the current page to
+     * select or null if the current selection should be left as is.
+     */
     private void showContactWithUpdates() {
+        if (mContactData == null) {
+            return;
+        }
         switch (mLayoutMode) {
             case TWO_COLUMN: {
                 // Set the contact data (hide the static photo because the photo will already be in
@@ -192,25 +238,31 @@
                 mUpdatesFragmentView.setVisibility(View.VISIBLE);
                 break;
             }
-            case VIEW_PAGER_AND_CAROUSEL: {
+            case VIEW_PAGER_AND_TAB_CAROUSEL: {
                 // Update and show the tab carousel
                 mTabCarousel.loadData(mContactData);
                 mTabCarousel.setVisibility(View.VISIBLE);
-                // Update ViewPager so that it has the max # of tabs (to show updates)
-                mViewPagerAdapter.setFragmentViewCount(FRAGMENT_COUNT);
+                // Update ViewPager to allow swipe between all the fragments (to see updates)
+                mViewPagerAdapter.enableSwipe(true);
+                break;
+            }
+            case FRAGMENT_CAROUSEL: {
+                // Allow swiping between all fragments
+                mFragmentCarousel.enableSwipe(true);
                 break;
             }
             default:
                 throw new IllegalStateException("Invalid LayoutMode " + mLayoutMode);
         }
 
-        if (mContactData != null) {
-            mDetailFragment.setData(mContactData.getLookupUri(), mContactData);
-            mUpdatesFragment.setData(mContactData.getLookupUri(), mContactData);
-        }
+        mDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+        mUpdatesFragment.setData(mContactData.getLookupUri(), mContactData);
     }
 
     private void showContactWithoutUpdates() {
+        if (mContactData == null) {
+            return;
+        }
         switch (mLayoutMode) {
             case TWO_COLUMN:
                 // Show the static photo which is next to the list of scrolling contact details
@@ -218,24 +270,54 @@
                 // Hide the updates fragment
                 mUpdatesFragmentView.setVisibility(View.GONE);
                 break;
-            case VIEW_PAGER_AND_CAROUSEL:
+            case VIEW_PAGER_AND_TAB_CAROUSEL:
                 // Hide the tab carousel
                 mTabCarousel.setVisibility(View.GONE);
-                // Update ViewPager so that it only has 1 tab and switch to the first indexed tab
-                mViewPagerAdapter.setFragmentViewCount(1);
+                // Update ViewPager to disable swipe so that it only shows the detail fragment
+                // and switch to the detail fragment
+                mViewPagerAdapter.enableSwipe(false);
                 mViewPager.setCurrentItem(0);
                 break;
+            case FRAGMENT_CAROUSEL: {
+                // Disable swipe so only the detail fragment shows
+                mFragmentCarousel.enableSwipe(false);
+                break;
+            }
             default:
                 throw new IllegalStateException("Invalid LayoutMode " + mLayoutMode);
         }
 
-        if (mContactData != null) {
-            mDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+        mDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+    }
+
+    public FragmentKeyListener getCurrentPage() {
+        switch (getCurrentPageIndex()) {
+            case 0:
+                return mDetailFragment;
+            case 1:
+                return mUpdatesFragment;
+            default:
+                throw new IllegalStateException("Invalid current item for ViewPager");
         }
     }
 
+    private int getCurrentPageIndex() {
+        // If the contact has social updates, then retrieve the current page based on the
+        // {@link ViewPager} or fragment carousel.
+        if (mContactHasUpdates) {
+            if (mViewPager != null) {
+                return mViewPager.getCurrentItem();
+            } else if (mFragmentCarousel != null) {
+                return mFragmentCarousel.getCurrentPage();
+            }
+        }
+        // Otherwise return the default page (detail fragment).
+        return 0;
+    }
+
     public void onSaveInstanceState(Bundle outState) {
         outState.putBoolean(KEY_CONTACT_HAS_UPDATES, mContactHasUpdates);
+        outState.putInt(KEY_CURRENT_PAGE_INDEX, getCurrentPageIndex());
     }
 
     private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {
diff --git a/src/com/android/contacts/detail/ContactDetailTabCarousel.java b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
index 5daca05..bb30f7e 100644
--- a/src/com/android/contacts/detail/ContactDetailTabCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
@@ -103,11 +103,9 @@
 
         // TODO: We can't always assume the "about" page will be the current page.
         mAboutTab.showSelectedState();
-        mAboutTab.enableAlphaLayer();
         mAboutTab.setAlphaLayerValue(0);
         mAboutTab.enableTouchInterceptor(mAboutTabTouchInterceptListener);
 
-        mUpdatesTab.enableAlphaLayer();
         mUpdatesTab.setAlphaLayerValue(MAX_ALPHA);
         mUpdatesTab.enableTouchInterceptor(mUpdatesTabTouchInterceptListener);
 
diff --git a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
index ba86f9f..846a957 100644
--- a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
@@ -47,6 +47,8 @@
     private LayoutInflater mInflater;
     private StreamItemAdapter mStreamItemAdapter;
 
+    private float mInitialAlphaValue;
+
     /**
      * This optional view adds an alpha layer over the entire fragment.
      */
@@ -96,8 +98,9 @@
         View rootView = mInflater.inflate(R.layout.contact_detail_updates_fragment, container,
                 false);
 
-        mAlphaLayer = rootView.findViewById(R.id.alpha_overlay);
         mTouchInterceptLayer = rootView.findViewById(R.id.touch_intercept_overlay);
+        mAlphaLayer = rootView.findViewById(R.id.alpha_overlay);
+        ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, mInitialAlphaValue);
 
         return rootView;
     }
@@ -135,13 +138,12 @@
 
     @Override
     public void setAlphaLayerValue(float alpha) {
-        ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, alpha);
-    }
-
-    @Override
-    public void enableAlphaLayer() {
-        if (mAlphaLayer != null) {
-            mAlphaLayer.setVisibility(View.VISIBLE);
+        // If the alpha layer is not ready yet, store it for later when the view is initialized
+        if (mAlphaLayer == null) {
+            mInitialAlphaValue = alpha;
+        } else {
+            // Otherwise set the value immediately
+            ContactDetailDisplayUtils.setAlphaOnViewBackground(mAlphaLayer, alpha);
         }
     }
 
diff --git a/src/com/android/contacts/detail/ContactDetailViewPagerAdapter.java b/src/com/android/contacts/detail/ContactDetailViewPagerAdapter.java
index d7f2743..40e391d 100644
--- a/src/com/android/contacts/detail/ContactDetailViewPagerAdapter.java
+++ b/src/com/android/contacts/detail/ContactDetailViewPagerAdapter.java
@@ -50,12 +50,12 @@
         mUpdatesFragmentView = view;
     }
 
-    public void setFragmentViewCount(int viewCount) {
-        if (viewCount < 0 || viewCount > MAX_FRAGMENT_VIEW_COUNT) {
-            throw new IllegalStateException("The view count in the ViewPager adapter must not be"
-                    + "less than 0 or exceed " + MAX_FRAGMENT_VIEW_COUNT);
-        }
-        mFragmentViewCount = viewCount;
+    /**
+     * Enable swiping if the detail and update fragments should be showing. Otherwise diable
+     * swiping if only the detail fragment should be showing.
+     */
+    public void enableSwipe(boolean enable) {
+        mFragmentViewCount = enable ? MAX_FRAGMENT_VIEW_COUNT : 1;
         notifyDataSetChanged();
     }
 
diff --git a/src/com/android/contacts/detail/ViewOverlay.java b/src/com/android/contacts/detail/ViewOverlay.java
index 4c14460..58428b8 100644
--- a/src/com/android/contacts/detail/ViewOverlay.java
+++ b/src/com/android/contacts/detail/ViewOverlay.java
@@ -32,11 +32,6 @@
     public void setAlphaLayerValue(float alpha);
 
     /**
-     * Makes the alpha layer on this fragment visible (if there is one).
-     */
-    public void enableAlphaLayer();
-
-    /**
      * Makes the touch intercept layer on this fragment visible (if there is one). Also adds a click
      * listener which is called when there is a touch event on the layer.
      */
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index 93bd3e9..eef5a61 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -33,7 +33,6 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.media.ToneGenerator;
 import android.net.Uri;
@@ -97,7 +96,9 @@
         public void onSearchButtonPressed();
     }
 
+    private View mDigitsContainer;
     private EditText mDigits;
+
     private View mDelete;
     private ToneGenerator mToneGenerator;
     private Object mToneGeneratorLock = new Object();
@@ -173,34 +174,42 @@
     };
 
     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-            /**
-             * Listen for phone state changes so that we can take down the
-             * "dialpad chooser" if the phone becomes idle while the
-             * chooser UI is visible.
-             */
-            @Override
-            public void onCallStateChanged(int state, String incomingNumber) {
-                // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
-                //       + state + ", '" + incomingNumber + "'");
-                if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
-                    // Log.i(TAG, "Call ended with dialpad chooser visible!  Taking it down...");
-                    // Note there's a race condition in the UI here: the
-                    // dialpad chooser could conceivably disappear (on its
-                    // own) at the exact moment the user was trying to select
-                    // one of the choices, which would be confusing.  (But at
-                    // least that's better than leaving the dialpad chooser
-                    // onscreen, but useless...)
-                    showDialpadChooser(false);
-                }
+        /**
+         * Listen for phone state changes so that we can take down the
+         * "dialpad chooser" if the phone becomes idle while the
+         * chooser UI is visible.
+         */
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            // Log.i(TAG, "PhoneStateListener.onCallStateChanged: "
+            //       + state + ", '" + incomingNumber + "'");
+            if ((state == TelephonyManager.CALL_STATE_IDLE) && dialpadChooserVisible()) {
+                // Log.i(TAG, "Call ended with dialpad chooser visible!  Taking it down...");
+                // Note there's a race condition in the UI here: the
+                // dialpad chooser could conceivably disappear (on its
+                // own) at the exact moment the user was trying to select
+                // one of the choices, which would be confusing.  (But at
+                // least that's better than leaving the dialpad chooser
+                // onscreen, but useless...)
+                showDialpadChooser(false);
             }
-        };
+        }
+    };
+
+    private boolean mWasEmptyBeforeTextChange;
 
     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-        // Do nothing
+        mWasEmptyBeforeTextChange = TextUtils.isEmpty(s);
     }
 
     public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
-        // Do nothing
+        if (mWasEmptyBeforeTextChange != TextUtils.isEmpty(input)) {
+            final Activity activity = getActivity();
+            if (activity != null) {
+                activity.invalidateOptionsMenu();
+            }
+        }
+
         // DTMF Tones do not need to be played here any longer -
         // the DTMF dialer handles that functionality now.
     }
@@ -241,6 +250,7 @@
         // Load up the resources for the text field.
         Resources r = getResources();
 
+        mDigitsContainer = fragmentView.findViewById(R.id.digits_container);
         mDigits = (EditText) fragmentView.findViewById(R.id.digits);
         mDigits.setKeyListener(DialerKeyListener.getInstance());
         mDigits.setOnClickListener(this);
@@ -298,12 +308,10 @@
         mDialpad = fragmentView.findViewById(R.id.dialpad);  // This is null in landscape mode.
 
         // In landscape we put the keyboard in phone mode.
-        // In portrait we prevent the soft keyboard to show since the
-        // dialpad acts as one already.
         if (null == mDialpad) {
             mDigits.setInputType(android.text.InputType.TYPE_CLASS_PHONE);
         } else {
-            mDigits.setInputType(android.text.InputType.TYPE_NULL);
+            mDigits.setCursorVisible(false);
         }
 
         // Set up the "dialpad chooser" UI; see showDialpadChooser().
@@ -923,7 +931,7 @@
 
         if (enabled) {
             // Log.i(TAG, "Showing dialpad chooser!");
-            mDigits.setVisibility(View.GONE);
+            mDigitsContainer.setVisibility(View.GONE);
             if (mDialpad != null) mDialpad.setVisibility(View.GONE);
             mAdditionalButtonsRow.setVisibility(View.GONE);
             mDialpadChooser.setVisibility(View.VISIBLE);
@@ -936,7 +944,7 @@
             mDialpadChooser.setAdapter(mDialpadChooserAdapter);
         } else {
             // Log.i(TAG, "Displaying normal Dialer UI.");
-            mDigits.setVisibility(View.VISIBLE);
+            mDigitsContainer.setVisibility(View.VISIBLE);
             if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
             mAdditionalButtonsRow.setVisibility(View.VISIBLE);
             mDialpadChooser.setVisibility(View.GONE);
diff --git a/src/com/android/contacts/dialpad/DigitsEditText.java b/src/com/android/contacts/dialpad/DigitsEditText.java
new file mode 100644
index 0000000..930717a
--- /dev/null
+++ b/src/com/android/contacts/dialpad/DigitsEditText.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 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.contacts.dialpad;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+/**
+ * EditText which suppresses IME show up.
+ */
+public class DigitsEditText extends EditText {
+    public DigitsEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setSuggestionsEnabled(false);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+        super.onFocusChanged(focused, direction, previouslyFocusedRect);
+        final InputMethodManager imm = ((InputMethodManager) getContext()
+                .getSystemService(Context.INPUT_METHOD_SERVICE));
+        if (imm != null && imm.isActive(this)) {
+            imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        final boolean ret = super.onTouchEvent(event);
+        // Must be done after super.onTouchEvent()
+        final InputMethodManager imm = ((InputMethodManager) getContext()
+                .getSystemService(Context.INPUT_METHOD_SERVICE));
+        if (imm != null && imm.isActive(this)) {
+            imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
+        }
+        return ret;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/editor/ExternalRawContactEditorView.java b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
index cabf639..eb496f2 100644
--- a/src/com/android/contacts/editor/ExternalRawContactEditorView.java
+++ b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
@@ -168,7 +168,8 @@
 
         // Name
         primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
-        mName.setText(primary.getAsString(StructuredName.DISPLAY_NAME));
+        mName.setText(primary != null ? primary.getAsString(StructuredName.DISPLAY_NAME) :
+                mContext.getString(R.string.missing_name));
 
         if (type.readOnly) {
             mAccountContainer.setOnClickListener(new OnClickListener() {
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
index c8c16e6..63a5d2c 100644
--- a/src/com/android/contacts/group/GroupBrowseListAdapter.java
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -23,155 +23,26 @@
 import com.android.contacts.model.AccountTypeManager;
 import com.android.internal.util.Objects;
 
-import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
-import android.os.AsyncTask;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
-import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Groups;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
-import android.widget.ImageView;
 import android.widget.TextView;
 
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
 /**
  * Adapter to populate the list of groups.
  */
 public class GroupBrowseListAdapter extends BaseAdapter {
 
-    private static final int MAX_ICONS_PER_GROUP_ROW = 4;
-
-    private static final String[] PROJECTION_GROUP_MEMBERSHIP_INFO = new String[] {
-        GroupMembership._ID,
-        GroupMembership.PHOTO_ID
-    };
-    private static final int GROUP_MEMBERSHIP_COLUMN_PHOTO_ID = 1;
-
-    /**
-     * Arguments for asynchronous photo ID loading. See {@link AsyncPhotoIdLoadTask}
-     */
-    private static class AsyncPhotoIdLoadArg {
-        public final View icons;
-        public final long groupId;
-        public final Map<Long, ArrayList<Long>> groupPhotoIdMap;
-        public final ContentResolver contentResolver;
-        public final ContactPhotoManager contactPhotoManager;
-
-        public AsyncPhotoIdLoadArg(
-                View icons, long groupId, Map<Long, ArrayList<Long>> groupPhotoIdMap,
-                ContentResolver contentResolver, ContactPhotoManager contactPhotoManager) {
-            this.icons = icons;
-            this.groupId = groupId;
-            this.groupPhotoIdMap = groupPhotoIdMap;
-            this.contentResolver = contentResolver;
-            this.contactPhotoManager = contactPhotoManager;
-        }
-    }
-
-    /**
-     * Loads photo IDs associated with a group ID supplied from {@link AsyncPhotoIdLoadArg#groupId},
-     * storing them in {@link GroupBrowseListAdapter#mGroupPhotoIdMap}.
-     *
-     * This AsyncTask also remembers a View which is associated with the group ID at the moment it
-     * is initiated (we use {@link View#setTag(Object) and View#getTag() to associate them}. If the
-     * View is still associated with the group ID after the asynchronous photo ID load, this class
-     * also asks {@link ContactPhotoManager} to load actual photo contents. Its parent (typically
-     * ListView) may reuse Views for different group IDs, so the photo content load often don't
-     * occur.
-     */
-    private static class AsyncPhotoIdLoadTask extends
-            AsyncTask<AsyncPhotoIdLoadArg, Void, ArrayList<Long>> {
-
-        private View mIcons;
-        private long mGroupId;
-        private Map<Long, ArrayList<Long>> mGroupPhotoIdMap;
-        private ContentResolver mContentResolver;
-        private ContactPhotoManager mContactPhotoManager;
-
-        @Override
-        protected ArrayList<Long> doInBackground(AsyncPhotoIdLoadArg... params) {
-            final AsyncPhotoIdLoadArg arg = params[0];
-            mIcons = arg.icons;
-            mGroupId = arg.groupId;
-            mGroupPhotoIdMap = arg.groupPhotoIdMap;
-            mContentResolver = arg.contentResolver;
-            mContactPhotoManager = arg.contactPhotoManager;
-
-            // Multiple requests for one group ID is possible. We just ignore duplicates,
-            // assuming query results won't change.
-            if (mGroupPhotoIdMap.containsKey(mGroupId)) {
-                return null;
-            }
-
-            final ArrayList<Long> photoIds = new ArrayList<Long>(MAX_ICONS_PER_GROUP_ROW);
-            Cursor cursor = null;
-            try {
-                cursor = mContentResolver.query(Data.CONTENT_URI,
-                        PROJECTION_GROUP_MEMBERSHIP_INFO,
-                        GroupMembership.MIMETYPE + "=? AND "
-                                + GroupMembership.PHOTO_ID + " IS NOT NULL AND "
-                                + GroupMembership.GROUP_ROW_ID + "=?",
-                        new String[] { GroupMembership.CONTENT_ITEM_TYPE,
-                                String.valueOf(mGroupId) }, null);
-                if (cursor != null) {
-                    int count = 0;
-                    while (cursor.moveToNext() && count < MAX_ICONS_PER_GROUP_ROW) {
-                        photoIds.add(cursor.getLong(GROUP_MEMBERSHIP_COLUMN_PHOTO_ID));
-                        count++;
-                    }
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-            return photoIds;
-        }
-
-        @Override
-        protected void onPostExecute(ArrayList<Long> photoIds) {
-            if (photoIds == null) {
-                return;
-            }
-
-            mGroupPhotoIdMap.put(mGroupId, photoIds);
-
-            final View icons = mIcons;
-            // If the original group ID, which was supplied when this AsyncTask was executed, is
-            // consistent with the ID inside mArgs, it means the View isn't reused by the
-            // other groups, and thus we can assume these Views are available for the group ID.
-            final Long currentGroupId = (Long) icons.getTag();
-            if (currentGroupId == mGroupId) {
-                final ImageView[] children = getIconViewsSordedByFillOrder(icons);
-                for (int i = 0; i < children.length; i++) {
-                    if (i < photoIds.size()) {
-                        mContactPhotoManager.loadPhoto(children[i], photoIds.get(i));
-                    } else {
-                        mContactPhotoManager.removePhoto(children[i]);
-                    }
-                }
-            }
-        }
-    }
-
     private final Context mContext;
     private final LayoutInflater mLayoutInflater;
     private final AccountTypeManager mAccountTypeManager;
 
-    private final Map<Long, ArrayList<Long>> mGroupPhotoIdMap =
-            new ConcurrentHashMap<Long, ArrayList<Long>>();
-
-    private final ContactPhotoManager mContactPhotoManager;
-
     private Cursor mCursor;
 
     private boolean mSelectionVisible;
@@ -181,7 +52,6 @@
         mContext = context;
         mLayoutInflater = LayoutInflater.from(context);
         mAccountTypeManager = AccountTypeManager.getInstance(mContext);
-        mContactPhotoManager = ContactPhotoManager.getInstance(mContext);
     }
 
     public void setCursor(Cursor cursor) {
@@ -308,35 +178,6 @@
         viewCache.groupTitle.setText(entry.getTitle());
         viewCache.groupMemberCount.setText(memberCountString);
 
-        final View icons = result.findViewById(R.id.icons);
-        final ImageView[] children = getIconViewsSordedByFillOrder(icons);
-        final ArrayList<Long> photoIds = mGroupPhotoIdMap.get(entry.getGroupId());
-
-        // Let the icon holder remember its associated group ID.
-        // Each AsyncTask loading photo IDs will compare this ID with the AsyncTask's argument, and
-        // check if the bound View is reused by the other list items or not. If the View is reused,
-        // the group ID set here will be overridden by the new owner, thus ID inconsistency happens.
-        icons.setTag(entry.getGroupId());
-        if (photoIds != null) {
-            // Cache is available. Let the photo manager load those IDs.
-            for (int i = 0; i < children.length; i++) {
-                if (i < photoIds.size()) {
-                    mContactPhotoManager.loadPhoto(children[i], photoIds.get(i));
-                } else {
-                    mContactPhotoManager.removePhoto(children[i]);
-                }
-            }
-        } else {
-            // Cache is not available. Load photo IDs asynchronously.
-            for (ImageView child : children) {
-                mContactPhotoManager.removePhoto(child);
-            }
-            new AsyncPhotoIdLoadTask().execute(
-                    new AsyncPhotoIdLoadArg(icons, entry.getGroupId(),
-                            mGroupPhotoIdMap, mContext.getContentResolver(),
-                            mContactPhotoManager));
-        }
-
         if (mSelectionVisible) {
             result.setActivated(isSelectedGroup(groupUri));
         }
@@ -355,19 +196,6 @@
     }
 
     /**
-     * Get ImageView objects inside the given View, sorted by the order photos should be filled.
-     */
-    private static ImageView[] getIconViewsSordedByFillOrder(View icons) {
-        final ImageView[] children = new ImageView[] {
-                (ImageView) icons.findViewById(R.id.icon_4),
-                (ImageView) icons.findViewById(R.id.icon_2),
-                (ImageView) icons.findViewById(R.id.icon_3),
-                (ImageView) icons.findViewById(R.id.icon_1)
-        };
-        return children;
-    }
-
-    /**
      * Cache of the children views of a contact detail entry represented by a
      * {@link GroupListItem}
      */
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index 4712871..2287557 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -107,6 +107,11 @@
          * Contact was saved and the Fragment can now be closed safely.
          */
         void onSaveFinished(int resultCode, Intent resultIntent);
+
+        /**
+         * Fragment is created but there's no accounts set up.
+         */
+        void onAccountsNotFound();
     }
 
     private static final int LOADER_GROUP_METADATA = 1;
@@ -308,7 +313,11 @@
                 AccountTypeManager.getInstance(mContext).getAccounts(true /* writeable */);
         // No Accounts available
         if (accounts.isEmpty()) {
-            throw new IllegalStateException("No accounts were found.");
+            Log.e(TAG, "No accounts were found.");
+            if (mListener != null) {
+                mListener.onAccountsNotFound();
+            }
+            return;
         }
 
         // In the common case of a single account being writable, auto-select
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index 51cc965..2e511bc 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -45,14 +45,13 @@
         Contacts.SORT_KEY_PRIMARY,              // 3
         Contacts.STARRED,                       // 4
         Contacts.CONTACT_PRESENCE,              // 5
-        Contacts.CONTACT_CHAT_CAPABILITY,       // 6
-        Contacts.CONTACT_STATUS,                // 7
-        Contacts.PHOTO_ID,                      // 8
-        Contacts.PHOTO_THUMBNAIL_URI,           // 9
-        Contacts.LOOKUP_KEY,                    // 10
-        Contacts.PHONETIC_NAME,                 // 11
-        Contacts.HAS_PHONE_NUMBER,              // 12
-        Contacts.IS_USER_PROFILE,               // 13
+        Contacts.CONTACT_STATUS,                // 6
+        Contacts.PHOTO_ID,                      // 7
+        Contacts.PHOTO_THUMBNAIL_URI,           // 8
+        Contacts.LOOKUP_KEY,                    // 9
+        Contacts.PHONETIC_NAME,                 // 10
+        Contacts.HAS_PHONE_NUMBER,              // 11
+        Contacts.IS_USER_PROFILE,               // 12
     };
 
     protected static final String[] PROJECTION_DATA = new String[] {
@@ -62,13 +61,12 @@
         Data.SORT_KEY_PRIMARY,                  // 3
         Data.STARRED,                           // 4
         Data.CONTACT_PRESENCE,                  // 5
-        Data.CONTACT_CHAT_CAPABILITY,           // 6
-        Data.CONTACT_STATUS,                    // 7
-        Data.PHOTO_ID,                          // 8
-        Data.PHOTO_THUMBNAIL_URI,               // 9
-        Data.LOOKUP_KEY,                        // 10
-        Data.PHONETIC_NAME,                     // 11
-        Data.HAS_PHONE_NUMBER,                  // 12
+        Data.CONTACT_STATUS,                    // 6
+        Data.PHOTO_ID,                          // 7
+        Data.PHOTO_THUMBNAIL_URI,               // 8
+        Data.LOOKUP_KEY,                        // 9
+        Data.PHONETIC_NAME,                     // 10
+        Data.HAS_PHONE_NUMBER,                  // 11
     };
 
     protected static final String[] FILTER_PROJECTION = new String[] {
@@ -78,15 +76,14 @@
         Contacts.SORT_KEY_PRIMARY,              // 3
         Contacts.STARRED,                       // 4
         Contacts.CONTACT_PRESENCE,              // 5
-        Contacts.CONTACT_CHAT_CAPABILITY,       // 6
-        Contacts.CONTACT_STATUS,                // 7
-        Contacts.PHOTO_ID,                      // 8
-        Contacts.PHOTO_THUMBNAIL_URI,           // 9
-        Contacts.LOOKUP_KEY,                    // 10
-        Contacts.PHONETIC_NAME,                 // 11
-        Contacts.HAS_PHONE_NUMBER,              // 12
-        Contacts.IS_USER_PROFILE,               // 13
-        SearchSnippetColumns.SNIPPET,           // 14
+        Contacts.CONTACT_STATUS,                // 6
+        Contacts.PHOTO_ID,                      // 7
+        Contacts.PHOTO_THUMBNAIL_URI,           // 8
+        Contacts.LOOKUP_KEY,                    // 9
+        Contacts.PHONETIC_NAME,                 // 10
+        Contacts.HAS_PHONE_NUMBER,              // 11
+        Contacts.IS_USER_PROFILE,               // 12
+        SearchSnippetColumns.SNIPPET,           // 13
     };
 
     protected static final int CONTACT_ID_COLUMN_INDEX = 0;
@@ -95,15 +92,14 @@
     protected static final int CONTACT_SORT_KEY_PRIMARY_COLUMN_INDEX = 3;
     protected static final int CONTACT_STARRED_COLUMN_INDEX = 4;
     protected static final int CONTACT_PRESENCE_STATUS_COLUMN_INDEX = 5;
-    protected static final int CONTACT_CHAT_CAPABILITY_COLUMN_INDEX = 6;
-    protected static final int CONTACT_CONTACT_STATUS_COLUMN_INDEX = 7;
-    protected static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 8;
-    protected static final int CONTACT_PHOTO_URI_COLUMN_INDEX = 9;
-    protected static final int CONTACT_LOOKUP_KEY_COLUMN_INDEX = 10;
-    protected static final int CONTACT_PHONETIC_NAME_COLUMN_INDEX = 11;
-    protected static final int CONTACT_HAS_PHONE_COLUMN_INDEX = 12;
-    protected static final int CONTACT_IS_USER_PROFILE = 13;
-    protected static final int CONTACT_SNIPPET_COLUMN_INDEX = 14;
+    protected static final int CONTACT_CONTACT_STATUS_COLUMN_INDEX = 6;
+    protected static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 7;
+    protected static final int CONTACT_PHOTO_URI_COLUMN_INDEX = 8;
+    protected static final int CONTACT_LOOKUP_KEY_COLUMN_INDEX = 9;
+    protected static final int CONTACT_PHONETIC_NAME_COLUMN_INDEX = 10;
+    protected static final int CONTACT_HAS_PHONE_COLUMN_INDEX = 11;
+    protected static final int CONTACT_IS_USER_PROFILE = 12;
+    protected static final int CONTACT_SNIPPET_COLUMN_INDEX = 13;
 
     private CharSequence mUnknownNameText;
     private int mDisplayNameColumnIndex;
@@ -281,7 +277,7 @@
 
     protected void bindPresenceAndStatusMessage(final ContactListItemView view, Cursor cursor) {
         view.showPresenceAndStatusMessage(cursor, CONTACT_PRESENCE_STATUS_COLUMN_INDEX,
-                CONTACT_CHAT_CAPABILITY_COLUMN_INDEX, CONTACT_CONTACT_STATUS_COLUMN_INDEX);
+                CONTACT_CONTACT_STATUS_COLUMN_INDEX);
     }
 
     protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index 4c4b780..ce1b119 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -1050,17 +1050,12 @@
      * Sets the proper icon (star or presence or nothing) and/or status message.
      */
     public void showPresenceAndStatusMessage(Cursor cursor, int presenceColumnIndex,
-            int capabilityColumnIndex, int contactStatusColumnIndex) {
+            int contactStatusColumnIndex) {
         Drawable icon = null;
         int presence = 0;
-        int chatCapability = 0;
         if (!cursor.isNull(presenceColumnIndex)) {
             presence = cursor.getInt(presenceColumnIndex);
-            if (capabilityColumnIndex != 0 && !cursor.isNull(presenceColumnIndex)) {
-                chatCapability = cursor.getInt(capabilityColumnIndex);
-            }
-            icon = ContactPresenceIconUtil.getChatCapabilityIcon(
-                    getContext(), presence, chatCapability);
+            icon = ContactPresenceIconUtil.getPresenceIcon(getContext(), presence);
         }
         setPresence(icon);
 
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 63d44be..b7434b1 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -16,6 +16,8 @@
 package com.android.contacts.list;
 
 import com.android.contacts.ContactPhotoManager;
+import com.android.contacts.ContactPresenceIconUtil;
+import com.android.contacts.ContactStatusUtil;
 import com.android.contacts.ContactTileLoaderFactory;
 import com.android.contacts.GroupMemberLoader;
 import com.android.contacts.R;
@@ -25,6 +27,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Contacts;
@@ -238,8 +241,8 @@
         contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
         contact.lookupKey = ContentUris.withAppendedId(
                 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
-        contact.presence = cursor.isNull(mPresenceIndex) ? null : cursor.getInt(mPresenceIndex);
 
+        // Set phone number and label
         if (mDisplayType == DisplayType.STREQUENT_PHONE_ONLY) {
             int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
             String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
@@ -247,8 +250,25 @@
                     phoneNumberCustomLabel);
             contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
         } else {
-            contact.status = cursor.getString(mStatusIndex);
-            contact.presence = cursor.isNull(mPresenceIndex) ? null : cursor.getInt(mPresenceIndex);
+            // Set presence icon and status message
+            Drawable icon = null;
+            int presence = 0;
+            if (!cursor.isNull(mPresenceIndex)) {
+                presence = cursor.getInt(mPresenceIndex);
+                icon = ContactPresenceIconUtil.getPresenceIcon(mContext, presence);
+            }
+            contact.presenceIcon = icon;
+
+            String statusMessage = null;
+            if (mStatusIndex != 0 && !cursor.isNull(mStatusIndex)) {
+                statusMessage = cursor.getString(mStatusIndex);
+            }
+            // If there is no status message from the contact, but there was a presence value,
+            // then use the default status message string
+            if (statusMessage == null && presence != 0) {
+                statusMessage = ContactStatusUtil.getStatusString(mContext, presence);
+            }
+            contact.status = statusMessage;
         }
 
         return contact;
@@ -530,7 +550,7 @@
         public String phoneNumber;
         public Uri photoUri;
         public Uri lookupKey;
-        public Integer presence;
+        public Drawable presenceIcon;
     }
 
     private static class ViewTypes {
diff --git a/src/com/android/contacts/list/ContactTileView.java b/src/com/android/contacts/list/ContactTileView.java
index 25edd7f..bfc4a2e 100644
--- a/src/com/android/contacts/list/ContactTileView.java
+++ b/src/com/android/contacts/list/ContactTileView.java
@@ -96,22 +96,12 @@
             mLookupUri = entry.lookupKey;
 
             if (mStatus != null) {
-                String statusText;
-                if (entry.presence == null) {
+                if (entry.status == null) {
                     mStatus.setVisibility(View.GONE);
                 } else {
-                    statusText =
-                          (entry.status != null ? entry.status :
-                          ContactStatusUtil.getStatusString(mContext, entry.presence));
-                    mStatus.setText(statusText);
-                    int presenceDrawableResId = (entry.presence == null ? 0 :
-                            StatusUpdates.getPresenceIconResourceId(entry.presence));
-                    if (presenceDrawableResId != 0) {
-                        Log.i(TAG, "iconId = " + presenceDrawableResId);
-                        mStatus.setCompoundDrawablesWithIntrinsicBounds(
-                                getResources().getDrawable(presenceDrawableResId),
-                                null, null, null);
-                    }
+                    mStatus.setText(entry.status);
+                    mStatus.setCompoundDrawablesWithIntrinsicBounds(entry.presenceIcon,
+                            null, null, null);
                     mStatus.setVisibility(View.VISIBLE);
                 }
             }
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
index bb49027..007af6c 100644
--- a/src/com/android/contacts/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -186,7 +186,7 @@
                 break;
             }
             case ContactListFilter.FILTER_TYPE_ACCOUNT: {
-                // TODO: avoid the use of private API
+                // TODO (stopship): avoid the use of private API
                 selection.append(
                         Contacts._ID + " IN ("
                                 + "SELECT DISTINCT " + RawContacts.CONTACT_ID
@@ -201,7 +201,11 @@
                 } else {
                     selection.append(" AND " + RawContacts.DATA_SET + " IS NULL");
                 }
-                selection.append(" OR " + Contacts.IS_USER_PROFILE + "=1)");
+                // TODO (stopship): And also this private API, which is even worse
+                selection.append(") OR " + Contacts._ID + "=(" +
+                        "SELECT contact_id " +
+                        "FROM raw_contacts rc inner join accounts a" +
+                        " ON a.profile_raw_contact_id = rc._id)");
                 break;
             }
             case ContactListFilter.FILTER_TYPE_GROUP: {
diff --git a/src/com/android/contacts/list/LegacyContactListAdapter.java b/src/com/android/contacts/list/LegacyContactListAdapter.java
index b3ab2af..b21d484 100644
--- a/src/com/android/contacts/list/LegacyContactListAdapter.java
+++ b/src/com/android/contacts/list/LegacyContactListAdapter.java
@@ -91,6 +91,6 @@
     }
 
     protected void bindPresence(final ContactListItemView view, Cursor cursor) {
-        view.showPresenceAndStatusMessage(cursor, PERSON_PRESENCE_STATUS_COLUMN_INDEX, 0, 0);
+        view.showPresenceAndStatusMessage(cursor, PERSON_PRESENCE_STATUS_COLUMN_INDEX, 0);
     }
 }
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 60a7357..2d821e6 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -338,6 +338,17 @@
                         // Skip external account types that couldn't be initialized.
                         continue;
                     }
+                    if (!accountType.hasContactsMetadata()) {
+                        Log.w(TAG, "Skipping extension package " + extensionPackage + " because"
+                                + " it doesn't have the CONTACTS_STRUCTURE metadata");
+                        continue;
+                    }
+                    if (TextUtils.isEmpty(accountType.accountType)) {
+                        Log.w(TAG, "Skipping extension package " + extensionPackage + " because"
+                                + " the CONTACTS_STRUCTURE metadata doesn't have the accountType"
+                                + " attribute");
+                        continue;
+                    }
                     Log.d(TAG, "Registering extension package account type="
                             + accountType.accountType + ", dataSet=" + accountType.dataSet
                             + ", packageName=" + extensionPackage);
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index 791f0ae..b6649c9 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -22,11 +22,9 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -46,7 +44,6 @@
 public class ExternalAccountType extends BaseAccountType {
     private static final String TAG = "ExternalAccountType";
 
-    private static final String ACTION_SYNC_ADAPTER = "android.content.SyncAdapter";
     private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
 
     private static final String TAG_CONTACTS_SOURCE_LEGACY = "ContactsSource";
@@ -85,6 +82,7 @@
     private String mAccountTypeLabelAttribute;
     private String mAccountTypeIconAttribute;
     private boolean mInitSuccessful;
+    private boolean mHasContactsMetadata;
 
     public ExternalAccountType(Context context, String resPackageName) {
         this.resPackageName = resPackageName;
@@ -137,6 +135,13 @@
         return mInitSuccessful;
     }
 
+    /**
+     * Whether this account type has the android.provider.CONTACTS_STRUCTURE metadata xml.
+     */
+    public boolean hasContactsMetadata() {
+        return mHasContactsMetadata;
+    }
+
     @Override
     public String getEditContactActivityClassName() {
         return mEditContactActivityClassName;
@@ -207,6 +212,8 @@
                         + TAG_CONTACTS_ACCOUNT_TYPE + ", not " + rootTag);
             }
 
+            mHasContactsMetadata = true;
+
             int attributeCount = parser.getAttributeCount();
             for (int i = 0; i < attributeCount; i++) {
                 String attr = parser.getAttributeName(i);
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
index 682f700..b29a9cd 100644
--- a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
+++ b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
@@ -36,6 +36,7 @@
 import android.net.Uri;
 import android.provider.ContactsContract.QuickContact;
 import android.provider.ContactsContract.StreamItems;
+import android.text.Html;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.text.style.AbsoluteSizeSpan;
@@ -204,7 +205,7 @@
         } else {
             // TODO: Rotate between all the stream items?
             StreamItemEntry streamItem = streamItems.get(0);
-            CharSequence status = streamItem.getText();
+            CharSequence status = Html.fromHtml(streamItem.getText());
             if (status.length() <= SHORT_SNIPPET_LENGTH) {
                 sb.append("\n");
             } else {
diff --git a/src/com/android/contacts/util/ContactBadgeUtil.java b/src/com/android/contacts/util/ContactBadgeUtil.java
index 806264d..a89177a 100644
--- a/src/com/android/contacts/util/ContactBadgeUtil.java
+++ b/src/com/android/contacts/util/ContactBadgeUtil.java
@@ -57,10 +57,15 @@
 
         final String statusLabelRes = streamItem.getLabelRes();
         final String statusResPackage = streamItem.getResPackage();
+
+        // Package name used for resources.getIdentifier()
+        String identiferPackage = statusResPackage;
         if (statusLabelRes  != null) {
             Resources resources;
             if (TextUtils.isEmpty(statusResPackage)) {
                 resources = context.getResources();
+                // In this case, we're using the framework resources.
+                identiferPackage = "android";
             } else {
                 PackageManager pm = context.getPackageManager();
                 try {
@@ -74,7 +79,7 @@
 
             if (resources != null) {
                 final int resId = resources.getIdentifier(statusLabelRes, "string",
-                        statusResPackage);
+                        identiferPackage);
                 if (resId == 0) {
                     Log.w(TAG, "Contact status update resource not found: " + statusLabelRes +
                             " in " + statusResPackage);