Merge "Remove "recent_updates" string from values-w470dp folder"
diff --git a/res/drawable-hdpi/ic_launcher_folder_live_contacts.png b/res/drawable-hdpi/ic_launcher_folder_live_contacts.png
deleted file mode 100644
index 84babe2..0000000
--- a/res/drawable-hdpi/ic_launcher_folder_live_contacts.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_folder_live_contacts_phone.png b/res/drawable-hdpi/ic_launcher_folder_live_contacts_phone.png
deleted file mode 100644
index 004e849..0000000
--- a/res/drawable-hdpi/ic_launcher_folder_live_contacts_phone.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_folder_live_contacts_starred.png b/res/drawable-hdpi/ic_launcher_folder_live_contacts_starred.png
deleted file mode 100644
index 73b4fa5..0000000
--- a/res/drawable-hdpi/ic_launcher_folder_live_contacts_starred.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_see_contacts_holo_dark.png b/res/drawable-hdpi/ic_see_contacts_holo_dark.png
deleted file mode 100644
index 8e2182f..0000000
--- a/res/drawable-hdpi/ic_see_contacts_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_unheard_voicemail_holo_dark.png b/res/drawable-hdpi/ic_unheard_voicemail_holo_dark.png
deleted file mode 100644
index 4f48e77..0000000
--- a/res/drawable-hdpi/ic_unheard_voicemail_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_folder_live_contacts.png b/res/drawable-mdpi/ic_launcher_folder_live_contacts.png
deleted file mode 100644
index d49cc7b..0000000
--- a/res/drawable-mdpi/ic_launcher_folder_live_contacts.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_folder_live_contacts_phone.png b/res/drawable-mdpi/ic_launcher_folder_live_contacts_phone.png
deleted file mode 100644
index 0127f84..0000000
--- a/res/drawable-mdpi/ic_launcher_folder_live_contacts_phone.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_folder_live_contacts_starred.png b/res/drawable-mdpi/ic_launcher_folder_live_contacts_starred.png
deleted file mode 100644
index 8d56b31..0000000
--- a/res/drawable-mdpi/ic_launcher_folder_live_contacts_starred.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_see_contacts_holo_dark.png b/res/drawable-mdpi/ic_see_contacts_holo_dark.png
deleted file mode 100644
index ef1d2df..0000000
--- a/res/drawable-mdpi/ic_see_contacts_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_unheard_voicemail_holo_dark.png b/res/drawable-mdpi/ic_unheard_voicemail_holo_dark.png
deleted file mode 100644
index 8992be2..0000000
--- a/res/drawable-mdpi/ic_unheard_voicemail_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_folder_live_contacts.png b/res/drawable-xhdpi/ic_launcher_folder_live_contacts.png
deleted file mode 100644
index 99bb9f8..0000000
--- a/res/drawable-xhdpi/ic_launcher_folder_live_contacts.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_folder_live_contacts_phone.png b/res/drawable-xhdpi/ic_launcher_folder_live_contacts_phone.png
deleted file mode 100644
index fd416b8..0000000
--- a/res/drawable-xhdpi/ic_launcher_folder_live_contacts_phone.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_folder_live_contacts_starred.png b/res/drawable-xhdpi/ic_launcher_folder_live_contacts_starred.png
deleted file mode 100644
index ff9e326..0000000
--- a/res/drawable-xhdpi/ic_launcher_folder_live_contacts_starred.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_see_contacts_holo_dark.png b/res/drawable-xhdpi/ic_see_contacts_holo_dark.png
deleted file mode 100644
index 279fff9..0000000
--- a/res/drawable-xhdpi/ic_see_contacts_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_unheard_voicemail_holo_dark.png b/res/drawable-xhdpi/ic_unheard_voicemail_holo_dark.png
deleted file mode 100644
index 75e771c..0000000
--- a/res/drawable-xhdpi/ic_unheard_voicemail_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/quickcontact_list_item_background.xml b/res/drawable/quickcontact_list_item_background.xml
deleted file mode 100644
index afb3947..0000000
--- a/res/drawable/quickcontact_list_item_background.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:state_window_focused="false"
-        android:drawable="@drawable/list_background_holo" />
-    <item
-        android:state_focused="true"
-        android:state_pressed="true"
-        android:drawable="@drawable/list_pressed_holo" />
-    <item
-        android:state_focused="false"
-        android:state_pressed="true"
-        android:drawable="@drawable/list_pressed_holo" />
-    <item
-        android:state_focused="true"
-        android:drawable="@drawable/list_focused_holo" />
-    <item
-        android:drawable="@color/quickcontact_list_background" />
-</selector>
diff --git a/res/layout-sw580dp-w1000dp/detail_header_contact_with_updates.xml b/res/layout-sw580dp-w1000dp/detail_header_contact_with_updates.xml
index 6ce811f..b9a9dbd 100644
--- a/res/layout-sw580dp-w1000dp/detail_header_contact_with_updates.xml
+++ b/res/layout-sw580dp-w1000dp/detail_header_contact_with_updates.xml
@@ -33,11 +33,12 @@
         android:layout_height="@dimen/detail_contact_photo_size" />
 
     <LinearLayout
-        android:layout_width="match_parent"
+        android:layout_width="0dip"
         android:layout_height="match_parent"
+        android:layout_weight="1"
         android:orientation="vertical"
         android:paddingLeft="16dip"
-        android:paddingRight="16dip">
+        android:paddingRight="4dip">
 
         <TextView
             android:id="@+id/name"
@@ -55,14 +56,9 @@
             android:textAppearance="?android:attr/textAppearanceMedium"
             android:textColor="?android:attr/textColorSecondary" />
 
-        <CheckBox
-            android:id="@+id/star"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:gravity="center_vertical"
-            android:contentDescription="@string/description_star"
-            style="?android:attr/starStyle" />
-
     </LinearLayout>
 
+    <include
+        layout="@layout/favorites_star" />
+
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp-w1000dp/detail_header_contact_without_updates.xml b/res/layout-sw580dp-w1000dp/detail_header_contact_without_updates.xml
index be82d88..6ef4651 100644
--- a/res/layout-sw580dp-w1000dp/detail_header_contact_without_updates.xml
+++ b/res/layout-sw580dp-w1000dp/detail_header_contact_without_updates.xml
@@ -25,12 +25,24 @@
     android:layout_height="wrap_content"
     android:orientation="vertical">
 
-    <TextView
-        android:id="@+id/name"
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textSize="@dimen/detail_header_name_text_size" />
+        android:paddingRight="16dip">
+
+        <TextView
+            android:id="@+id/name"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textSize="@dimen/detail_header_name_text_size"
+            android:paddingRight="16dip" />
+
+        <include
+            layout="@layout/favorites_star" />
+
+    </LinearLayout>
 
     <TextView
         android:id="@+id/company"
@@ -39,12 +51,4 @@
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="?android:attr/textColorSecondary" />
 
-    <CheckBox
-        android:id="@+id/star"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:contentDescription="@string/description_star"
-        style="?android:attr/starStyle" />
-
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp-w1000dp/group_detail_fragment.xml b/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
deleted file mode 100644
index 45419a8..0000000
--- a/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?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.
--->
-
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/group_detail"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@drawable/panel_content">
-
-    <!-- Static header containing the group title, size, and group source (if applicable) -->
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-
-        <LinearLayout
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:paddingLeft="@dimen/group_detail_border_padding"
-            android:orientation="vertical" >
-
-            <TextView
-                android:id="@+id/group_title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:paddingTop="12dip"
-                android:paddingLeft="8dip"
-                android:textAppearance="?android:attr/textAppearanceLarge"
-                android:textStyle="bold" />
-
-            <TextView
-                android:id="@+id/group_size"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:paddingBottom="12dip"
-                android:paddingLeft="8dip"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:textColor="?android:attr/textColorSecondary" />
-
-        </LinearLayout>
-
-        <FrameLayout
-            android:id="@+id/group_source_view_container"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:paddingRight="@dimen/group_detail_border_padding" />
-
-    </LinearLayout>
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="1dip"
-        android:background="@color/people_app_theme_color"
-        android:layout_marginLeft="@dimen/group_detail_border_padding"
-        android:layout_marginRight="@dimen/group_detail_border_padding" />
-
-    <!-- List of group members -->
-    <ListView android:id="@android:id/list"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginLeft="@dimen/group_detail_border_padding"
-        android:layout_marginRight="@dimen/group_detail_border_padding"
-        android:cacheColorHint="#00000000"
-        android:fadingEdge="none"
-        android:divider="@null" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp/detail_header_contact_with_updates.xml b/res/layout-sw580dp/detail_header_contact_with_updates.xml
index 6b6f02b..fa6f301 100644
--- a/res/layout-sw580dp/detail_header_contact_with_updates.xml
+++ b/res/layout-sw580dp/detail_header_contact_with_updates.xml
@@ -26,7 +26,8 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
-    android:layout_marginTop="30dip">
+    android:layout_marginTop="30dip"
+    android:paddingBottom="8dip">
 
     <!-- Add a first item that gives us enough space to show the carousel -->
     <view
@@ -43,12 +44,26 @@
 
     </view>
 
-    <TextView
-        android:id="@+id/name"
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textSize="@dimen/detail_header_name_text_size" />
+        android:orientation="horizontal"
+        android:paddingTop="8dip"
+        android:paddingRight="8dip">
+
+        <TextView
+            android:id="@+id/name"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingRight="24dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textSize="@dimen/detail_header_name_text_size" />
+
+        <include
+            layout="@layout/favorites_star" />
+
+    </LinearLayout>
 
     <TextView
         android:id="@+id/company"
@@ -57,12 +72,4 @@
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="?android:attr/textColorSecondary" />
 
-    <CheckBox
-        android:id="@+id/star"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:contentDescription="@string/description_star"
-        style="?android:attr/starStyle" />
-
 </LinearLayout>
diff --git a/res/layout-sw580dp/detail_header_contact_without_updates.xml b/res/layout-sw580dp/detail_header_contact_without_updates.xml
index 995e8f7..a55764d 100644
--- a/res/layout-sw580dp/detail_header_contact_without_updates.xml
+++ b/res/layout-sw580dp/detail_header_contact_without_updates.xml
@@ -23,7 +23,8 @@
     xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:paddingBottom="8dip">
 
     <!-- Ensure that the contact photo for a contact WITHOUT social updates is the same width and
     height as a contact WITH social updates (where the photo is 2/3 of the screen width). -->
@@ -57,12 +58,26 @@
 
     </view>
 
-    <TextView
-        android:id="@+id/name"
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textSize="@dimen/detail_header_name_text_size" />
+        android:orientation="horizontal"
+        android:paddingTop="8dip"
+        android:paddingRight="8dip">
+
+        <TextView
+            android:id="@+id/name"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingRight="24dip"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textSize="@dimen/detail_header_name_text_size" />
+
+        <include
+            layout="@layout/favorites_star" />
+
+    </LinearLayout>
 
     <TextView
         android:id="@+id/company"
@@ -71,12 +86,4 @@
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="?android:attr/textColorSecondary" />
 
-    <CheckBox
-        android:id="@+id/star"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:contentDescription="@string/description_star"
-        style="?android:attr/starStyle" />
-
 </LinearLayout>
diff --git a/res/layout-sw580dp/favorites_star.xml b/res/layout-sw580dp/favorites_star.xml
new file mode 100644
index 0000000..7959df3
--- /dev/null
+++ b/res/layout-sw580dp/favorites_star.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:minHeight="50dip">
+
+    <CheckBox
+        android:id="@+id/star"
+        android:duplicateParentState="true"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical"
+        android:contentDescription="@string/description_star"
+        style="?android:attr/starStyle"/>
+
+</FrameLayout>
diff --git a/res/layout-sw580dp/group_detail_fragment.xml b/res/layout-sw580dp/group_detail_fragment.xml
index fbe695b..8a4ce86 100644
--- a/res/layout-sw580dp/group_detail_fragment.xml
+++ b/res/layout-sw580dp/group_detail_fragment.xml
@@ -21,44 +21,66 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@color/background_primary"
+    android:background="@drawable/panel_content"
     android:paddingLeft="@dimen/group_detail_border_padding"
     android:paddingRight="@dimen/group_detail_border_padding">
 
-    <TextView
-        android:id="@+id/group_title"
+    <!-- Static header containing the group title, size, and group source (if applicable) -->
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingLeft="8dip"
-        android:paddingTop="12dip"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textStyle="bold" />
+        android:orientation="horizontal" >
 
-    <TextView
-        android:id="@+id/group_size"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingLeft="8dip"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorTertiary" />
+        <LinearLayout
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingLeft="8dip"
+            android:orientation="vertical" >
+
+            <TextView
+                android:id="@+id/group_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:paddingTop="12dip"
+                android:textAppearance="?android:attr/textAppearanceLarge"
+                android:textStyle="bold" />
+
+            <TextView
+                android:id="@+id/group_size"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:paddingBottom="12dip"
+                android:textAppearance="@style/GroupMembershipSizeTextAppearance"
+                android:textColor="?android:attr/textColorSecondary" />
+
+        </LinearLayout>
+
+        <FrameLayout
+            android:id="@+id/group_source_view_container"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent" />
+
+    </LinearLayout>
 
     <View
         android:layout_width="match_parent"
         android:layout_height="1dip"
-        android:background="@color/people_app_theme_color"
-        android:layout_marginTop="12dip" />
+        android:background="@color/people_app_theme_color" />
 
-    <FrameLayout
-        android:id="@+id/group_source_view_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical" />
-
+    <!-- List of group members -->
     <ListView android:id="@android:id/list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:cacheColorHint="#00000000"
+        android:cacheColorHint="@android:color/black"
         android:fadingEdge="none"
         android:divider="@null" />
 
+    <include
+        layout="@layout/group_detail_fragment_empty_view"/>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp/group_source_button.xml b/res/layout-sw580dp/group_source_button.xml
index e0fe4a9..43c85e9 100644
--- a/res/layout-sw580dp/group_source_button.xml
+++ b/res/layout-sw580dp/group_source_button.xml
@@ -34,7 +34,8 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:duplicateParentState="true"
-        android:textAppearance="?android:attr/textAppearanceMedium"/>
+        android:textSize="14sp"
+        style="@android:style/Widget.Holo.ActionBar.TabText"/>
 
     <ImageView
         android:id="@android:id/icon"
diff --git a/res/layout-sw580dp/list_separator.xml b/res/layout-sw580dp/list_separator.xml
index a53bd6b..34e8d79 100644
--- a/res/layout-sw580dp/list_separator.xml
+++ b/res/layout-sw580dp/list_separator.xml
@@ -17,12 +17,6 @@
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/title"
-    style="?android:attr/listSeparatorTextViewStyle"
-    android:layout_height="32dip"
+    style="@style/ContactListSeparatorTextViewStyle"
     android:paddingLeft="8dip"
-    android:paddingRight="8dip"
-    android:background="@drawable/list_section_divider_holo_custom"
-    android:textColor="@color/people_app_theme_color"
-    android:textAllCaps="true"
-    android:singleLine="true"
-    android:ellipsize="end" />
\ No newline at end of file
+    android:paddingRight="8dip" />
diff --git a/res/layout/account_filter_header.xml b/res/layout/account_filter_header.xml
index 4d45d25..26207f0 100644
--- a/res/layout/account_filter_header.xml
+++ b/res/layout/account_filter_header.xml
@@ -22,7 +22,6 @@
     android:id="@+id/account_filter_header_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="?attr/list_item_header_height"
     android:orientation="vertical"
     android:paddingTop="@dimen/contact_browser_list_top_margin"
     android:layout_marginLeft="@dimen/contact_browser_list_header_left_margin"
@@ -31,17 +30,6 @@
     android:visibility="gone">
     <TextView
         android:id="@+id/account_filter_header"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:textStyle="bold"
-        android:textAllCaps="true"
-        android:paddingLeft="@dimen/contact_browser_list_item_text_indent"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorSecondary" />
-    <View
-        android:id="@+id/account_filter_header_bottom_divider"
-        android:layout_height="1dip"
-        style="@style/SectionDivider" />
+        style="@style/ContactListSeparatorTextViewStyle"
+        android:paddingLeft="@dimen/contact_browser_list_item_text_indent" />
 </LinearLayout>
diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml
index 4768bf6..777c7af 100644
--- a/res/layout/call_log_list_item.xml
+++ b/res/layout/call_log_list_item.xml
@@ -129,20 +129,11 @@
 
     <TextView
         android:id="@+id/call_log_header"
-        style="?android:attr/listSeparatorTextViewStyle"
+        style="@style/ContactListSeparatorTextViewStyle"
         android:layout_marginLeft="@dimen/call_log_outer_margin"
         android:layout_marginRight="@dimen/call_log_outer_margin"
         android:paddingTop="@dimen/call_log_inner_margin"
-        android:paddingBottom="@dimen/call_log_inner_margin"
-        android:background="@drawable/list_section_divider_holo_custom"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textStyle="bold"
-        android:textColor="?attr/call_log_header_color"
-        android:textAllCaps="true"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:focusable="true"
-    />
+        android:paddingBottom="@dimen/call_log_inner_margin" />
 
     <View
         android:id="@+id/call_log_divider"
diff --git a/res/layout/carousel_updates_tab.xml b/res/layout/carousel_updates_tab.xml
index 91a72d3..d471d93 100644
--- a/res/layout/carousel_updates_tab.xml
+++ b/res/layout/carousel_updates_tab.xml
@@ -56,7 +56,7 @@
         android:textAppearance="?android:attr/textAppearanceLarge"
         android:textColor="@color/detail_update_tab_text_color"
         android:textStyle="bold"
-        android:maxLines="3"/>
+        android:maxLines="@integer/updates_tab_snippet_max_lines"/>
 
     <View
         android:id="@+id/alpha_overlay"
diff --git a/res/layout/contacts_list_content.xml b/res/layout/contact_list_content.xml
similarity index 100%
rename from res/layout/contacts_list_content.xml
rename to res/layout/contact_list_content.xml
diff --git a/res/layout/contact_list_filter_custom.xml b/res/layout/contact_list_filter_custom.xml
index a4fe48e..40d9c78 100644
--- a/res/layout/contact_list_filter_custom.xml
+++ b/res/layout/contact_list_filter_custom.xml
@@ -26,7 +26,8 @@
         android:layout_height="0dip"
         android:layout_weight="1"
         android:layout_marginLeft="@dimen/contact_filter_left_margin"
-        android:layout_marginRight="@dimen/contact_filter_right_margin" />
+        android:layout_marginRight="@dimen/contact_filter_right_margin"
+        android:overScrollMode="always" />
 
     <View
         android:layout_width="match_parent"
diff --git a/res/layout/directory_header.xml b/res/layout/directory_header.xml
index a1516ef..a2adf48 100644
--- a/res/layout/directory_header.xml
+++ b/res/layout/directory_header.xml
@@ -20,7 +20,9 @@
     style="@style/DirectoryHeader"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="?attr/list_item_header_height"
+    android:minHeight="@dimen/list_section_divider_min_height"
+    android:background="@drawable/list_section_divider_holo_custom"
+    android:paddingTop="@dimen/contact_browser_list_top_margin"
     android:paddingLeft="?attr/list_item_padding_left"
     android:paddingRight="?attr/list_item_padding_right">
     <TextView
@@ -32,7 +34,7 @@
         android:layout_centerVertical="true"
         android:layout_marginLeft="8dip"
         android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorSecondary"
+        android:textColor="@color/people_app_theme_color"
         android:singleLine="true"
         android:textStyle="bold"
         android:textAllCaps="true" />
@@ -55,12 +57,8 @@
         android:layout_marginLeft="8dip"
         android:layout_marginRight="8dip"
         android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorSecondary"
+        android:textColor="@color/people_app_theme_color"
         android:singleLine="true"
         android:textStyle="bold"
         android:textAllCaps="true" />
-    <View
-        android:id="@+id/contact_filter_header_bottom_divider"
-        style="@style/SectionDivider"
-        android:layout_below="@id/display_name" />
 </RelativeLayout>
diff --git a/res/layout/edit_kind_title.xml b/res/layout/edit_kind_title.xml
index f178876..1cd6833 100644
--- a/res/layout/edit_kind_title.xml
+++ b/res/layout/edit_kind_title.xml
@@ -20,20 +20,13 @@
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="32dip"
+    android:layout_height="wrap_content"
     android:paddingLeft="8dip"
     android:paddingRight="8dip"
     android:focusable="false">
-
     <TextView
         android:id="@+id/kind_title"
-        style="?android:attr/listSeparatorTextViewStyle"
+        style="@style/ContactListSeparatorTextViewStyle"
         android:paddingLeft="8dip"
-        android:paddingRight="8dip"
-        android:background="@drawable/list_section_divider_holo_custom"
-        android:textColor="@color/people_app_theme_color"
-        android:textAllCaps="true"
-        android:singleLine="true"
-        android:ellipsize="end" />
-
-</FrameLayout>
\ No newline at end of file
+        android:paddingRight="8dip" />
+</FrameLayout>
diff --git a/res/layout/group_detail_fragment.xml b/res/layout/group_detail_fragment.xml
index 2b020c9..735b29d 100644
--- a/res/layout/group_detail_fragment.xml
+++ b/res/layout/group_detail_fragment.xml
@@ -43,6 +43,9 @@
             android:scrollbarStyle="outsideOverlay"
             android:divider="@null"/>
 
+        <include
+            layout="@layout/group_detail_fragment_empty_view"/>
+
         <!--
           Shadow overlay over the list of group members (since we have a fake stacked
           action bar)
diff --git a/res/layout/group_detail_fragment_empty_view.xml b/res/layout/group_detail_fragment_empty_view.xml
new file mode 100644
index 0000000..89a6cf8
--- /dev/null
+++ b/res/layout/group_detail_fragment_empty_view.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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/empty"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingTop="24dip"
+    android:visibility="gone">
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:text="@string/emptyGroup"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:textColor="?android:attr/textColorSecondary"/>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:text="@string/addPeopleToGroup"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorSecondary"/>
+
+</LinearLayout>
diff --git a/res/layout/join_contact_picker_show_all.xml b/res/layout/join_contact_picker_show_all.xml
deleted file mode 100644
index d332649..0000000
--- a/res/layout/join_contact_picker_show_all.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical">
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="?android:attr/dividerHorizontal" />
-
-    <TextView
-        android:id="@+id/text"
-        android:layout_height="48dip"
-        android:layout_width="match_parent"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorSecondary"
-        android:gravity="center_vertical"
-        android:text="@string/showAllContactsJoinItem" />
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="?android:attr/dividerHorizontal" />
-</LinearLayout>
diff --git a/res/layout/list_separator.xml b/res/layout/list_separator.xml
index 3ffaca7..d94e18c 100644
--- a/res/layout/list_separator.xml
+++ b/res/layout/list_separator.xml
@@ -17,20 +17,13 @@
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="32dip"
+    android:layout_height="wrap_content"
     android:paddingLeft="16dip"
     android:paddingRight="16dip"
     android:focusable="false">
-
     <TextView
         android:id="@+id/title"
-        style="?android:attr/listSeparatorTextViewStyle"
+        style="@style/ContactListSeparatorTextViewStyle"
         android:paddingLeft="8dip"
-        android:paddingRight="8dip"
-        android:background="@drawable/list_section_divider_holo_custom"
-        android:textColor="@color/people_app_theme_color"
-        android:textAllCaps="true"
-        android:singleLine="true"
-        android:ellipsize="end" />
-
+        android:paddingRight="8dip" />
 </FrameLayout>
diff --git a/res/layout/phone_favorite_account_filter_header.xml b/res/layout/phone_favorite_account_filter_header.xml
deleted file mode 100644
index d63382a..0000000
--- a/res/layout/phone_favorite_account_filter_header.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?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.
--->
-
-<!-- Layout showing the type of account filter in phone favorite screen.
-
-     Note: This xml is based on account_filter_header.xml and list_separator.xml.
-     Some configurations (e.g. padding) are from account_filter_header.xml, while
-     some other configurations (e.g. background, height, text style) are from
-     list_separator.xml. See also bug 5432231. -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/account_filter_header_container"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="32dip"
-    android:paddingTop="@dimen/contact_browser_list_top_margin"
-    android:layout_marginLeft="@dimen/contact_browser_list_header_left_margin"
-    android:layout_marginRight="@dimen/contact_browser_list_header_right_margin"
-    android:background="?android:attr/selectableItemBackground"
-    android:visibility="gone">
-    <TextView
-        android:id="@+id/account_filter_header"
-        style="?android:attr/listSeparatorTextViewStyle"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:textStyle="bold"
-        android:textAllCaps="true"
-        android:textColor="@color/people_app_theme_color"
-        android:paddingLeft="@dimen/contact_browser_list_item_text_indent"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:background="@drawable/list_section_divider_holo_custom" />
-</FrameLayout>
diff --git a/res/values-sw580dp-w1000dp/dimens.xml b/res/values-sw580dp-w1000dp/dimens.xml
index f1b607c..334cc34 100644
--- a/res/values-sw580dp-w1000dp/dimens.xml
+++ b/res/values-sw580dp-w1000dp/dimens.xml
@@ -15,6 +15,7 @@
 -->
 <resources>
     <dimen name="group_detail_border_padding">32dip</dimen>
+    <dimen name="group_detail_side_margin">32dip</dimen>
     <dimen name="detail_contact_photo_margin">16dip</dimen>
     <dimen name="contact_detail_list_top_padding">32dip</dimen>
 </resources>
diff --git a/res/values-sw580dp-w1000dp/styles.xml b/res/values-sw580dp-w1000dp/styles.xml
new file mode 100644
index 0000000..9fa3d08
--- /dev/null
+++ b/res/values-sw580dp-w1000dp/styles.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<resources>
+    <style name="GroupMembershipSizeTextAppearance" parent="@android:style/TextAppearance.Medium" />
+</resources>
diff --git a/res/values-sw580dp-w720dp/styles.xml b/res/values-sw580dp-w720dp/styles.xml
index 5ad25c6..0d750d7 100644
--- a/res/values-sw580dp-w720dp/styles.xml
+++ b/res/values-sw580dp-w720dp/styles.xml
@@ -21,6 +21,7 @@
         <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>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
         <item name="list_item_height">66dip</item>
         <item name="activated_background">@drawable/list_item_activated_background</item>
         <item name="section_header_background">@drawable/list_title_holo</item>
diff --git a/res/values-sw580dp/integers.xml b/res/values-sw580dp/integers.xml
index c629f7e..56e837e 100644
--- a/res/values-sw580dp/integers.xml
+++ b/res/values-sw580dp/integers.xml
@@ -15,4 +15,5 @@
 -->
 <resources>
     <integer name="contact_tile_column_count">2</integer>
+    <integer name="updates_tab_snippet_max_lines">5</integer>
 </resources>
diff --git a/res/values-sw580dp/styles.xml b/res/values-sw580dp/styles.xml
index 3551378..3cf50e2 100644
--- a/res/values-sw580dp/styles.xml
+++ b/res/values-sw580dp/styles.xml
@@ -21,6 +21,7 @@
         <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>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
         <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
         <item name="activated_background">@drawable/list_item_activated_background</item>
         <item name="section_header_background">@drawable/list_title_holo</item>
@@ -56,6 +57,7 @@
     </style>
 
     <style name="ContactPickerTheme" parent="@android:Theme.Holo.Light.Dialog">
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
         <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
         <item name="section_header_background">@drawable/list_title_holo</item>
         <item name="list_item_divider">?android:attr/listDivider</item>
@@ -95,6 +97,7 @@
 
     <style name="ContactListFilterTheme" parent="@android:Theme.Holo.Light.Dialog">
         <item name="android:windowCloseOnTouchOutside">true</item>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
     </style>
 
     <style name="CustomContactListFilterView" parent="ContactListFilterTheme">
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8d6b877..12ae918 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -142,7 +142,10 @@
     <dimen name="group_editor_autocomplete_left_padding">8dip</dimen>
 
     <!-- Border padding for the group detail fragment -->
-    <dimen name="group_detail_border_padding">32dip</dimen>
+    <dimen name="group_detail_border_padding">0dip</dimen>
+
+    <!-- Left and right margin for the divider in the group detail fragment -->
+    <dimen name="group_detail_side_margin">0dip</dimen>
 
     <!-- Height of edit text in dialpad fragment -->
     <dimen name="dialpad_horizontal_margin">0dip</dimen>
@@ -211,4 +214,9 @@
     <integer name="dialpad_layout_weight_digits">20</integer>
     <integer name="dialpad_layout_weight_dialpad">65</integer>
     <integer name="dialpad_layout_weight_additional_buttons">15</integer>
+
+    <!-- Minimum height used with @drawable/list_section_divider_holo_custom.
+         Right now the drawable has implicit 32dip minimal height, which is confusing.
+         This value is for making the hidden configuration explicit in xml. -->
+    <dimen name="list_section_divider_min_height">32dip</dimen>
 </resources>
diff --git a/res/values/integers.xml b/res/values/integers.xml
index 89246f9..3cae01b 100644
--- a/res/values/integers.xml
+++ b/res/values/integers.xml
@@ -23,4 +23,7 @@
 
     <!-- Determines the number of columns in a ContactTileRow -->
     <integer name="contact_tile_column_count">2</integer>
+
+    <!-- Max lines to display of a contact's snippet in the "updates" tab of the contact card tab carousel  -->
+    <integer name="updates_tab_snippet_max_lines">3</integer>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3fc1784..ae2c680 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -269,6 +269,12 @@
     <!-- The text displayed when the contacts list is empty while displaying only contacts that have phone numbers -->
     <string name="noContactsWithPhoneNumbers">No contacts with phone numbers.</string>
 
+    <!-- The text displayed when there are no members in the group while displaying the group detail page [CHAR LIMIT=40] -->
+    <string name="emptyGroup">No people in this group.</string>
+
+    <!-- The text displayed to instruct users to add members to a group (when viewing a group detail page for a group with no members) [CHAR LIMIT=50] -->
+    <string name="addPeopleToGroup">To add some, edit the group.</string>
+
     <!-- Displayed in a spinner dialog after the user creates a contact and it's being saved to the database -->
     <string name="savingContact">Saving contact\u2026</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index e5fd752..ece881e 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -21,6 +21,7 @@
         <item name="android:actionBarStyle">@style/DialtactsActionBarStyle</item>
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:windowBackground">@drawable/background_dial_holo_dark</item>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
         <item name="activated_background">@drawable/list_item_activated_background</item>
         <item name="section_header_background">@drawable/list_title_holo</item>
         <item name="list_section_header_height">32dip</item>
@@ -68,6 +69,7 @@
     <style name="CallDetailActivityTheme" parent="android:Theme.Holo">
         <item name="android:windowBackground">@android:color/black</item>
         <item name="android:gravity">top</item>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
         <!-- CallLog -->
         <item name="call_log_primary_text_color">#FFFFFF</item>
         <item name="call_log_primary_background_color">#000000</item>
@@ -87,6 +89,7 @@
         <item name="android:actionBarWidgetTheme">@style/ContactsActionBarTheme</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
     </style>
 
     <style name="EditorActivityTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
@@ -96,6 +99,7 @@
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
     </style>
 
     <style name="BackgroundOnlyTheme" parent="@android:style/Theme.Holo.Light">
@@ -120,6 +124,7 @@
         <item name="android:backgroundDimEnabled">true</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowNoTitle">true</item>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
     </style>
 
     <style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
@@ -129,6 +134,7 @@
         <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>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
         <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
         <item name="activated_background">@drawable/list_item_activated_background</item>
         <item name="section_header_background">@drawable/list_title_holo</item>
@@ -186,6 +192,10 @@
         <item name="android:listSelector">?android:attr/listChoiceBackgroundIndicator</item>
     </style>
 
+    <style name="ListViewStyle" parent="@android:style/Widget.Holo.Light.ListView">
+        <item name="android:overScrollMode">always</item>
+    </style>
+
     <style name="ContactPickerTheme" parent="@style/PeopleTheme" />
 
     <style name="ContactPickerLayout" parent="ContactPickerTheme">
@@ -197,9 +207,11 @@
     </style>
 
     <style name="ContactsPreferencesTheme" parent="@android:Theme.Holo.Light">
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
     </style>
 
     <style name="ContactListFilterTheme" parent="@android:Theme.Holo.Light">
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
     </style>
 
     <style name="CustomContactListFilterView" parent="ContactListFilterTheme">
@@ -225,6 +237,7 @@
         <item name="android:windowCloseOnTouchOutside">true</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
     </style>
 
     <style name="SectionDivider">
@@ -300,4 +313,28 @@
         <item name="android:layout_height">2dip</item>
         <item name="android:background">@color/quickcontact_tab_indicator</item>
     </style>
+
+
+    <style name="GroupMembershipSizeTextAppearance" parent="@android:style/TextAppearance.Small"/>
+
+    <!-- TextView style with blue underline. It is most suitable for headers.
+
+         This is similar to ?android:attr/listSeparatorTextView but uses different
+         background and text color. See also android:style/Widget.Holo.TextView.ListSeparator
+         (which is private, so we cannot specify it as a parent style).  -->
+    <style name="ContactListSeparatorTextViewStyle">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <!-- See comments for @dimen/list_section_divider_min_height -->
+        <item name="android:minHeight">@dimen/list_section_divider_min_height</item>
+        <item name="android:background">@drawable/list_section_divider_holo_custom</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:textStyle">bold</item>
+        <item name="android:textColor">@color/people_app_theme_color</item>
+        <item name="android:gravity">center_vertical</item>
+        <item name="android:paddingLeft">8dip</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:textAllCaps">true</item>
+    </style>
 </resources>
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 541b7bf..caa8bce 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -294,7 +294,8 @@
             public Void doInBackground(Void... params) {
                 ContentValues values = new ContentValues();
                 values.put(Voicemails.IS_READ, true);
-                getContentResolver().update(voicemailUri, values, null, null);
+                getContentResolver().update(voicemailUri, values,
+                        Voicemails.IS_READ + " = 0", null);
                 return null;
             }
         });
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 6d55184..77caaf7 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -75,6 +75,7 @@
     private final MyTabListener mTabListener = new MyTabListener();
 
     private boolean mShowHomeIcon;
+    private boolean mShowTabsAsText;
 
     public enum TabState {
         GROUPS,
@@ -98,7 +99,8 @@
     private static final TabState DEFAULT_TAB = TabState.ALL;
     private TabState mCurrentTab = DEFAULT_TAB;
 
-    public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar) {
+    public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar,
+            boolean isUsingTwoPanes) {
         mContext = context;
         mListener = listener;
         mActionBar = actionBar;
@@ -106,6 +108,9 @@
 
         mShowHomeIcon = mContext.getResources().getBoolean(R.bool.show_home_icon);
 
+        // On wide screens, show the tabs as text (instead of icons)
+        mShowTabsAsText = isUsingTwoPanes;
+
         // Set up search view.
         View customSearchView = LayoutInflater.from(mActionBar.getThemedContext()).inflate(
                 R.layout.custom_action_bar, null);
@@ -153,12 +158,16 @@
         mListener = listener;
     }
 
-    private void addTab(TabState tabState, int icon, int contentDescription) {
+    private void addTab(TabState tabState, int icon, int description) {
         final Tab tab = mActionBar.newTab();
         tab.setTag(tabState);
-        tab.setIcon(icon);
-        tab.setContentDescription(contentDescription);
         tab.setTabListener(mTabListener);
+        if (mShowTabsAsText) {
+            tab.setText(description);
+        } else {
+            tab.setIcon(icon);
+            tab.setContentDescription(description);
+        }
         mActionBar.addTab(tab);
     }
 
@@ -211,14 +220,16 @@
         return mCurrentTab;
     }
 
+    /**
+     * @return Whether in search mode, i.e. if the search view is visible/expanded.
+     *
+     * Note even if the action bar is in search mode, if the query is empty, the search fragment
+     * will not be in search mode.
+     */
     public boolean isSearchMode() {
         return mSearchMode;
     }
 
-    public boolean shouldShowSearchResult() {
-        return mSearchMode && !TextUtils.isEmpty(mQueryString);
-    }
-
     public void setSearchMode(boolean flag) {
         if (mSearchMode != flag) {
             mSearchMode = flag;
@@ -235,7 +246,7 @@
     }
 
     public String getQueryString() {
-        return mQueryString;
+        return mSearchMode ? mQueryString : null;
     }
 
     public void setQueryString(String query) {
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index 6305d8e..e68c7bc 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -327,7 +327,6 @@
 
             case ContactsRequest.ACTION_PICK_CONTACT: {
                 ContactPickerFragment fragment = new ContactPickerFragment();
-                fragment.setSearchMode(mRequest.isSearchMode());
                 fragment.setIncludeProfile(mRequest.shouldIncludeProfile());
                 mListFragment = fragment;
                 break;
@@ -341,8 +340,6 @@
 
             case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: {
                 ContactPickerFragment fragment = new ContactPickerFragment();
-                fragment.setSearchMode(mRequest.isSearchMode());
-                fragment.setQueryString(mRequest.getQueryString(), false);
                 fragment.setShortcutRequested(true);
                 mListFragment = fragment;
                 break;
@@ -362,7 +359,6 @@
             case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
                 PhoneNumberPickerFragment fragment = new PhoneNumberPickerFragment();
                 fragment.setShortcutAction(Intent.ACTION_CALL);
-                fragment.setSearchMode(mRequest.isSearchMode());
 
                 mListFragment = fragment;
                 break;
@@ -387,9 +383,6 @@
         }
 
         mListFragment.setLegacyCompatibilityMode(mRequest.isLegacyCompatibilityMode());
-        mListFragment.setContactsRequest(mRequest);
-        mListFragment.setSearchMode(mRequest.isSearchMode());
-        mListFragment.setQueryString(mRequest.getQueryString(), false);
         mListFragment.setDirectoryResultLimit(DEFAULT_DIRECTORY_RESULT_LIMIT);
 
         getFragmentManager().beginTransaction()
@@ -537,7 +530,6 @@
     @Override
     public boolean onQueryTextChange(String newText) {
         mListFragment.setQueryString(newText, true);
-        mListFragment.setSearchMode(!TextUtils.isEmpty(newText));
         return false;
     }
 
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 571a988..917d8e0 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -20,14 +20,13 @@
 import com.android.contacts.calllog.CallLogFragment;
 import com.android.contacts.dialpad.DialpadFragment;
 import com.android.contacts.interactions.PhoneNumberInteraction;
-import com.android.contacts.list.AccountFilterActivity;
-import com.android.contacts.list.ContactListFilter;
 import com.android.contacts.list.ContactListFilterController;
 import com.android.contacts.list.ContactListFilterController.ContactListFilterListener;
 import com.android.contacts.list.ContactListItemView;
 import com.android.contacts.list.OnPhoneNumberPickerActionListener;
 import com.android.contacts.list.PhoneFavoriteFragment;
 import com.android.contacts.list.PhoneNumberPickerFragment;
+import com.android.contacts.util.AccountFilterUtil;
 import com.android.internal.telephony.ITelephony;
 
 import android.app.ActionBar;
@@ -109,6 +108,8 @@
             "DialtactsActivity_last_manually_selected_tab";
     private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER;
 
+    private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
+
     /**
      * Listener interface for Fragments accommodated in {@link ViewPager} enabling them to know
      * when it becomes visible or invisible inside the ViewPager.
@@ -301,10 +302,8 @@
             new OnMenuItemClickListener() {
         @Override
         public boolean onMenuItemClick(MenuItem item) {
-            final Intent intent =
-                    new Intent(DialtactsActivity.this, AccountFilterActivity.class);
-            ContactListFilter filter = mContactListFilterController.getFilter();
-            startActivityForResult(intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
+            AccountFilterUtil.startAccountFilterActivityForResult(
+                    DialtactsActivity.this, SUBACTIVITY_ACCOUNT_FILTER);
             return true;
         }
     };
@@ -363,7 +362,6 @@
                 public boolean onQueryTextChange(String newText) {
                     // Show search result with non-empty text. Show a bare list otherwise.
                     mSearchFragment.setQueryString(newText, true);
-                    mSearchFragment.setSearchMode(!TextUtils.isEmpty(newText));
                     return true;
                 }
     };
@@ -926,17 +924,9 @@
             return;
         }
         switch (requestCode) {
-            case AccountFilterActivity.DEFAULT_REQUEST_CODE: {
-                ContactListFilter filter = (ContactListFilter) data.getParcelableExtra(
-                        AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
-                if (filter == null) {
-                    return;
-                }
-                if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
-                    mContactListFilterController.selectCustomFilter();
-                } else {
-                    mContactListFilterController.setContactListFilter(filter, true);
-                }
+            case SUBACTIVITY_ACCOUNT_FILTER: {
+                AccountFilterUtil.handleAccountFilterResult(
+                        mContactListFilterController, resultCode, data);
             }
             break;
         }
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index b68f566..772fa54 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -33,7 +33,6 @@
 import com.android.contacts.interactions.ContactDeletionInteraction;
 import com.android.contacts.interactions.ImportExportDialogFragment;
 import com.android.contacts.interactions.PhoneNumberInteraction;
-import com.android.contacts.list.AccountFilterActivity;
 import com.android.contacts.list.ContactBrowseListFragment;
 import com.android.contacts.list.ContactEntryListFragment;
 import com.android.contacts.list.ContactListFilter;
@@ -53,6 +52,7 @@
 import com.android.contacts.model.AccountWithDataSet;
 import com.android.contacts.preference.ContactsPreferenceActivity;
 import com.android.contacts.preference.DisplayOptionsPreferenceFragment;
+import com.android.contacts.util.AccountFilterUtil;
 import com.android.contacts.util.AccountPromptUtils;
 import com.android.contacts.util.AccountSelectionUtil;
 import com.android.contacts.util.AccountsListAdapter;
@@ -61,7 +61,6 @@
 import com.android.contacts.util.DialogManager;
 import com.android.contacts.util.PhoneCapabilityTester;
 
-import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -110,8 +109,9 @@
 
     private static final int SUBACTIVITY_NEW_GROUP = 2;
     private static final int SUBACTIVITY_EDIT_GROUP = 3;
+    private static final int SUBACTIVITY_ACCOUNT_FILTER = 4;
 
-    private DialogManager mDialogManager = new DialogManager(this);
+    private final DialogManager mDialogManager = new DialogManager(this);
 
     private ContactsIntentResolver mIntentResolver;
     private ContactsRequest mRequest;
@@ -317,7 +317,8 @@
         final FragmentTransaction transaction = fragmentManager.beginTransaction();
 
         // Prepare the fragments which are used both on 1-pane and on 2-pane.
-        if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
+        boolean isUsingTwoPanes = PhoneCapabilityTester.isUsingTwoPanes(this);
+        if (isUsingTwoPanes) {
             mFavoritesFragment = getFragment(R.id.favorites_fragment);
             mAllFragment = getFragment(R.id.all_fragment);
             mGroupsFragment = getFragment(R.id.groups_fragment);
@@ -367,7 +368,7 @@
         transaction.hide(mAllFragment);
         transaction.hide(mGroupsFragment);
 
-        if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
+        if (isUsingTwoPanes) {
             // Prepare 2-pane only fragments/views...
 
             // Container views for fragments
@@ -410,7 +411,7 @@
         }
 
         // Configure action bar
-        mActionBarAdapter = new ActionBarAdapter(this, this, getActionBar());
+        mActionBarAdapter = new ActionBarAdapter(this, this, getActionBar(), isUsingTwoPanes);
         mActionBarAdapter.initialize(savedState, mRequest);
 
         invalidateOptionsMenuIfNeeded();
@@ -566,12 +567,12 @@
                 invalidateOptionsMenu();
                 break;
             case STOP_SEARCH_MODE:
-                clearSearch();
+                setQueryTextToFragment("");
                 updateFragmentsVisibility();
                 invalidateOptionsMenu();
                 break;
             case CHANGE_SEARCH_QUERY:
-                loadSearch(mActionBarAdapter.getQueryString());
+                setQueryTextToFragment(mActionBarAdapter.getQueryString());
                 break;
             default:
                 throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action);
@@ -842,26 +843,19 @@
         }
     }
 
-    private void clearSearch() {
-        loadSearch("");
-    }
-
-    private void loadSearch(String query) {
-        configureFragments(false /* from request */);
+    private void setQueryTextToFragment(String query) {
         mAllFragment.setQueryString(query, true);
+        mAllFragment.setVisibleScrollbarEnabled(!mAllFragment.isSearchMode());
     }
 
     private void configureContactListFragmentForRequest() {
-        mAllFragment.setContactsRequest(mRequest);
-
         Uri contactUri = mRequest.getContactUri();
         if (contactUri != null) {
             mAllFragment.setSelectedContactUri(contactUri);
         }
 
         mAllFragment.setFilter(mContactListFilterController.getFilter());
-        mAllFragment.setSearchMode(mActionBarAdapter.isSearchMode());
-        mAllFragment.setQueryString(mActionBarAdapter.getQueryString(), false);
+        setQueryTextToFragment(mActionBarAdapter.getQueryString());
 
         if (mRequest.isDirectorySearchEnabled()) {
             mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT);
@@ -874,11 +868,7 @@
         // Filter may be changed when this Activity is in background.
         mAllFragment.setFilter(mContactListFilterController.getFilter());
 
-        final boolean showSearchResult = mActionBarAdapter.shouldShowSearchResult();
-        mAllFragment.setSearchMode(showSearchResult);
-
         final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this);
-        mAllFragment.setVisibleScrollbarEnabled(!showSearchResult);
         mAllFragment.setVerticalScrollbarPosition(
                 useTwoPane
                         ? View.SCROLLBAR_POSITION_LEFT
@@ -1365,8 +1355,8 @@
                 return true;
             }
             case R.id.menu_contacts_filter: {
-                final Intent intent = new Intent(this, AccountFilterActivity.class);
-                startActivityForResult(intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
+                AccountFilterUtil.startAccountFilterActivityForResult(this,
+                        SUBACTIVITY_ACCOUNT_FILTER);
                 return true;
             }
             case R.id.menu_search: {
@@ -1444,19 +1434,9 @@
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         switch (requestCode) {
-            case AccountFilterActivity.DEFAULT_REQUEST_CODE: {
-                if (resultCode == Activity.RESULT_OK) {
-                    ContactListFilter filter = (ContactListFilter) data.getParcelableExtra(
-                            AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
-                    if (filter == null) {
-                        return;
-                    }
-                    if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
-                        mContactListFilterController.selectCustomFilter();
-                    } else {
-                        mContactListFilterController.setContactListFilter(filter, true);
-                    }
-                }
+            case SUBACTIVITY_ACCOUNT_FILTER: {
+                AccountFilterUtil.handleAccountFilterResult(
+                        mContactListFilterController, resultCode, data);
                 break;
             }
 
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
index 42d2edd..82acc33 100644
--- a/src/com/android/contacts/editor/GroupMembershipView.java
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -255,6 +255,7 @@
 
         ListView listView = mPopup.getListView();
         listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+        listView.setOverScrollMode(OVER_SCROLL_ALWAYS);
         int count = mAdapter.getCount();
         for (int i = 0; i < count; i++) {
             listView.setItemChecked(i, mAdapter.getItem(i).isChecked());
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index d4f6e29..b3472ab 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -98,6 +98,7 @@
     private TextView mGroupTitle;
     private TextView mGroupSize;
     private ListView mMemberListView;
+    private View mEmptyView;
 
     private Listener mListener;
 
@@ -149,6 +150,7 @@
         mGroupSize = (TextView) mRootView.findViewById(R.id.group_size);
         mGroupSourceViewContainer = (ViewGroup) mRootView.findViewById(
                 R.id.group_source_view_container);
+        mEmptyView = mRootView.findViewById(android.R.id.empty);
         mMemberListView = (ListView) mRootView.findViewById(android.R.id.list);
         mMemberListView.setAdapter(mAdapter);
 
@@ -258,6 +260,7 @@
         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
             updateSize(data.getCount());
             mAdapter.setContactCursor(data);
+            mMemberListView.setEmptyView(mEmptyView);
         }
 
         @Override
diff --git a/src/com/android/contacts/list/AccountFilterActivity.java b/src/com/android/contacts/list/AccountFilterActivity.java
index aa1fe27..0b4c6e0 100644
--- a/src/com/android/contacts/list/AccountFilterActivity.java
+++ b/src/com/android/contacts/list/AccountFilterActivity.java
@@ -55,15 +55,6 @@
 
     private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 0;
 
-    /**
-     * Request code for this Activity, which will be used with
-     * {@link Activity#startActivityForResult(Intent, int)}.
-     *
-     * This is useful when we want to share one request code among multiple Activities and
-     * Fragments.
-     */
-    public static final int DEFAULT_REQUEST_CODE = 10001;
-
     public static final String KEY_EXTRA_CONTACT_LIST_FILTER = "contactListFilter";
 
     private static final int FILTER_LOADER_ID = 0;
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index 2bde608..061646e 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -178,7 +178,7 @@
     }
 
     @Override
-    public void setSearchMode(boolean flag) {
+    protected void setSearchMode(boolean flag) {
         if (isSearchMode() != flag) {
             if (!flag) {
                 restoreSelectedUri(true);
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 91c582c..8b9aaf5 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -108,7 +108,6 @@
     private String mQueryString;
     private int mDirectorySearchMode = DirectoryListLoader.SEARCH_MODE_NONE;
     private boolean mSelectionVisible;
-    private ContactsRequest mRequest;
     private boolean mLegacyCompatibility;
 
     private boolean mEnabled = true;
@@ -249,7 +248,6 @@
         outState.putBoolean(KEY_LEGACY_COMPATIBILITY, mLegacyCompatibility);
         outState.putString(KEY_QUERY_STRING, mQueryString);
         outState.putInt(KEY_DIRECTORY_RESULT_LIMIT, mDirectoryResultLimit);
-        outState.putParcelable(KEY_REQUEST, mRequest);
         outState.putBoolean(KEY_DARK_THEME, mDarkTheme);
 
         if (mListView != null) {
@@ -282,27 +280,12 @@
         mLegacyCompatibility = savedState.getBoolean(KEY_LEGACY_COMPATIBILITY);
         mQueryString = savedState.getString(KEY_QUERY_STRING);
         mDirectoryResultLimit = savedState.getInt(KEY_DIRECTORY_RESULT_LIMIT);
-        mRequest = savedState.getParcelable(KEY_REQUEST);
         mDarkTheme = savedState.getBoolean(KEY_DARK_THEME);
 
         // Retrieve list state. This will be applied in onLoadFinished
         mListState = savedState.getParcelable(KEY_LIST_STATE);
     }
 
-    /**
-     * Returns the parsed intent that started the activity hosting this fragment.
-     */
-    public ContactsRequest getContactsRequest() {
-        return mRequest;
-    }
-
-    /**
-     * Sets a parsed intent that started the activity hosting this fragment.
-     */
-    public void setContactsRequest(ContactsRequest request) {
-        mRequest = request;
-    }
-
     @Override
     public void onStart() {
         super.onStart();
@@ -608,7 +591,14 @@
         }
     }
 
-    public void setSearchMode(boolean flag) {
+    /**
+     * Enter/exit search mode.  By design, a fragment enters search mode only when it has a
+     * non-empty query text, so the mode must be tightly related to the current query.
+     * For this reason this method must only be called by {@link #setQueryString}.
+     *
+     * Also note this method doesn't call {@link #reloadData()}; {@link #setQueryString} does it.
+     */
+    protected void setSearchMode(boolean flag) {
         if (mSearchMode != flag) {
             mSearchMode = flag;
             setSectionHeaderDisplayEnabled(!mSearchMode);
@@ -632,7 +622,6 @@
                     }
                 }
                 mAdapter.configureDefaultPartition(false, flag);
-                reloadData();
             }
 
             if (mListView != null) {
@@ -641,17 +630,22 @@
         }
     }
 
-    public boolean isSearchMode() {
+    public final boolean isSearchMode() {
         return mSearchMode;
     }
 
-    public String getQueryString() {
+    public final String getQueryString() {
         return mQueryString;
     }
 
     public void setQueryString(String queryString, boolean delaySelection) {
+        // Normalize the empty query.
+        if (TextUtils.isEmpty(queryString)) queryString = null;
+
         if (!TextUtils.equals(mQueryString, queryString)) {
             mQueryString = queryString;
+            setSearchMode(!TextUtils.isEmpty(mQueryString));
+
             if (mAdapter != null) {
                 mAdapter.setQueryString(queryString);
                 reloadData();
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 30c3c48..0cc211a 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -17,13 +17,14 @@
 
 import com.android.contacts.R;
 import com.android.contacts.editor.ContactEditorFragment;
+import com.android.contacts.util.AccountFilterUtil;
 
-import android.app.Activity;
 import android.content.CursorLoader;
 import android.content.Intent;
 import android.database.Cursor;
 import android.provider.ContactsContract.Contacts;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -40,11 +41,13 @@
  * picking a contact with one of the PICK intents).
  */
 public class DefaultContactBrowseListFragment extends ContactBrowseListFragment {
+    private static final String TAG = DefaultContactBrowseListFragment.class.getSimpleName();
+
+    private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
 
     private TextView mCounterHeaderView;
     private View mSearchHeaderView;
-    private TextView mAccountFilterHeaderView;
-    private View mAccountFilterHeaderContainer;
+    private View mAccountFilterHeader;
     private FrameLayout mProfileHeaderContainer;
     private View mProfileHeader;
     private Button mProfileMessage;
@@ -56,12 +59,8 @@
     private class FilterHeaderClickListener implements OnClickListener {
         @Override
         public void onClick(View view) {
-            final Activity activity = getActivity();
-            if (activity != null) {
-                final Intent intent = new Intent(activity, AccountFilterActivity.class);
-                activity.startActivityForResult(
-                        intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
-            }
+            AccountFilterUtil.startAccountFilterActivityForResult(
+                        DefaultContactBrowseListFragment.this, REQUEST_CODE_ACCOUNT_FILTER);
         }
     }
     private OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
@@ -92,17 +91,15 @@
 
     @Override
     protected View inflateView(LayoutInflater inflater, ViewGroup container) {
-        return inflater.inflate(R.layout.contacts_list_content, null);
+        return inflater.inflate(R.layout.contact_list_content, null);
     }
 
     @Override
     protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
         super.onCreateView(inflater, container);
 
-        mAccountFilterHeaderView = (TextView) getView().findViewById(R.id.account_filter_header);
-        mAccountFilterHeaderContainer =
-                getView().findViewById(R.id.account_filter_header_container);
-        mAccountFilterHeaderContainer.setOnClickListener(mFilterHeaderClickListener);
+        mAccountFilterHeader = getView().findViewById(R.id.account_filter_header_container);
+        mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
         mCounterHeaderView = (TextView) getView().findViewById(R.id.contacts_count);
 
         // Create an empty user profile header and hide it for now (it will be visible if the
@@ -120,7 +117,7 @@
     }
 
     @Override
-    public void setSearchMode(boolean flag) {
+    protected void setSearchMode(boolean flag) {
         super.setSearchMode(flag);
         checkHeaderViewVisibility();
     }
@@ -144,29 +141,17 @@
     }
 
     private void updateFilterHeaderView() {
-        ContactListFilter filter = getFilter();
-        if (mAccountFilterHeaderView == null) {
+        if (mAccountFilterHeader == null) {
             return; // Before onCreateView -- just ignore it.
         }
+        final ContactListFilter filter = getFilter();
         if (filter != null && !isSearchMode()) {
-            if (filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
-                mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
-                mAccountFilterHeaderView.setText(getContext().getString(
-                        R.string.listSingleContact));
-                return;
-            } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
-                mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
-                mAccountFilterHeaderView.setText(getContext().getString(
-                        R.string.listCustomView));
-                return;
-            } else if (filter.filterType != ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
-                mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
-                mAccountFilterHeaderView.setText(getContext().getString(
-                        R.string.listAllContactsInAccount, filter.accountName));
-                return;
-            }
+            final boolean shouldShowHeader = AccountFilterUtil.updateAccountFilterTitleForPeople(
+                    mAccountFilterHeader, filter, false, false);
+            mAccountFilterHeader.setVisibility(shouldShowHeader ? View.VISIBLE : View.GONE);
+        } else {
+            mAccountFilterHeader.setVisibility(View.GONE);
         }
-        mAccountFilterHeaderContainer.setVisibility(View.GONE);
     }
 
     @Override
@@ -244,6 +229,18 @@
         showEmptyUserProfile(!mUserProfileExists && !isSearchMode());
     }
 
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
+            if (getActivity() != null) {
+                AccountFilterUtil.handleAccountFilterResult(
+                        ContactListFilterController.getInstance(getActivity()), resultCode, data);
+            } else {
+                Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
+            }
+        }
+    }
+
     private void showEmptyUserProfile(boolean show) {
         // Changing visibility of just the mProfileHeader doesn't do anything unless
         // you change visibility of its children, hence the call to mCounterHeaderView
diff --git a/src/com/android/contacts/list/EmailAddressPickerFragment.java b/src/com/android/contacts/list/EmailAddressPickerFragment.java
index 168e135..14b6c23 100644
--- a/src/com/android/contacts/list/EmailAddressPickerFragment.java
+++ b/src/com/android/contacts/list/EmailAddressPickerFragment.java
@@ -55,7 +55,7 @@
 
     @Override
     protected View inflateView(LayoutInflater inflater, ViewGroup container) {
-        return inflater.inflate(R.layout.contacts_list_content, null);
+        return inflater.inflate(R.layout.contact_list_content, null);
     }
 
     private void pickEmailAddress(Uri uri) {
diff --git a/src/com/android/contacts/list/PhoneFavoriteFragment.java b/src/com/android/contacts/list/PhoneFavoriteFragment.java
index 17247a4..9ee2d3c 100644
--- a/src/com/android/contacts/list/PhoneFavoriteFragment.java
+++ b/src/com/android/contacts/list/PhoneFavoriteFragment.java
@@ -19,6 +19,7 @@
 import com.android.contacts.ContactTileLoaderFactory;
 import com.android.contacts.R;
 import com.android.contacts.preference.ContactsPreferences;
+import com.android.contacts.util.AccountFilterUtil;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -39,6 +40,7 @@
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
+import android.widget.FrameLayout;
 import android.widget.ListView;
 import android.widget.TextView;
 
@@ -62,6 +64,8 @@
 
     private static final String KEY_FILTER = "filter";
 
+    private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
+
     public interface Listener {
         public void onContactSelected(Uri contactUri);
     }
@@ -93,7 +97,7 @@
 
             // Show the filter header with "loading" state.
             updateFilterHeaderView();
-            mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
+            mAccountFilterHeader.setVisibility(View.VISIBLE);
         }
 
         @Override
@@ -137,12 +141,8 @@
     private class FilterHeaderClickListener implements OnClickListener {
         @Override
         public void onClick(View view) {
-            final Activity activity = getActivity();
-            if (activity != null) {
-                final Intent intent = new Intent(activity, AccountFilterActivity.class);
-                activity.startActivityForResult(
-                        intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
-            }
+            AccountFilterUtil.startAccountFilterActivityForResult(
+                    PhoneFavoriteFragment.this, REQUEST_CODE_ACCOUNT_FILTER);
         }
     }
 
@@ -164,6 +164,7 @@
             // FastScroller should be visible only when the user is seeing "all" contacts section.
             final boolean shouldShow = mAdapter.shouldShowFirstScroller(firstVisibleItem);
             if (shouldShow != mShouldShowFastScroller) {
+                mListView.setVerticalScrollBarEnabled(shouldShow);
                 mListView.setFastScrollEnabled(shouldShow);
                 mListView.setFastScrollAlwaysVisible(shouldShow);
                 mShouldShowFastScroller = shouldShow;
@@ -196,8 +197,11 @@
 
     private TextView mEmptyView;
     private ListView mListView;
-    private View mAccountFilterHeaderContainer;
-    private TextView mAccountFilterHeaderView;
+    /**
+     * Layout containing {@link #mAccountFilterHeader}. Used to limit area being "pressed".
+     */
+    private FrameLayout mAccountFilterHeaderContainer;
+    private View mAccountFilterHeader;
 
     private final ContactTileAdapter.Listener mContactTileAdapterListener =
             new ContactTileAdapterListener();
@@ -239,7 +243,7 @@
         mListView = (ListView) listLayout.findViewById(R.id.contact_tile_list);
         mListView.setItemsCanFocus(true);
         mListView.setOnItemClickListener(this);
-        mListView.setVerticalScrollBarEnabled(true);
+        mListView.setVerticalScrollBarEnabled(false);
         mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
         mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
 
@@ -299,11 +303,10 @@
         }
 
         // Create the account filter header but keep it hidden until "all" contacts are loaded.
-        mAccountFilterHeaderContainer = inflater.inflate(
-                R.layout.phone_favorite_account_filter_header, mListView, false);
-        mAccountFilterHeaderView =
-                (TextView) mAccountFilterHeaderContainer.findViewById(R.id.account_filter_header);
-        mAccountFilterHeaderContainer.setOnClickListener(mFilterHeaderClickListener);
+        mAccountFilterHeaderContainer = new FrameLayout(context, null);
+        mAccountFilterHeader = inflater.inflate(R.layout.account_filter_header, mListView, false);
+        mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
+        mAccountFilterHeaderContainer.addView(mAccountFilterHeader);
         mAccountFilterHeaderContainer.setVisibility(View.GONE);
 
         mAdapter = new PhoneFavoriteMergedAdapter(context,
@@ -355,6 +358,18 @@
         }
     }
 
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
+            if (getActivity() != null) {
+                AccountFilterUtil.handleAccountFilterResult(
+                        ContactListFilterController.getInstance(getActivity()), resultCode, data);
+            } else {
+                Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
+            }
+        }
+    }
+
     private boolean loadContactsPreferences() {
         if (mContactsPrefs == null || mAllContactsAdapter == null) {
             return false;
@@ -400,27 +415,12 @@
     }
 
     private void updateFilterHeaderView() {
-        if (mAccountFilterHeaderContainer == null || mAllContactsAdapter == null) {
+        final ContactListFilter filter = getFilter();
+        if (mAccountFilterHeader == null || mAllContactsAdapter == null || filter == null) {
             return;
         }
-
-        final ContactListFilter filter = getFilter();
-        if (mAllContactsAdapter.isLoading()) {
-            mAccountFilterHeaderView.setText(R.string.contact_list_loading);
-        } else if (filter != null) {
-            if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
-                mAccountFilterHeaderView.setText(R.string.list_filter_phones);
-            } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
-                mAccountFilterHeaderView.setText(getString(
-                        R.string.listAllContactsInAccount, filter.accountName));
-            } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
-                mAccountFilterHeaderView.setText(R.string.listCustomView);
-            } else {
-                Log.w(TAG, "Filter type \"" + filter.filterType + "\" isn't expected.");
-            }
-        } else {
-            Log.w(TAG, "Filter is null.");
-        }
+        AccountFilterUtil.updateAccountFilterTitleForPhone(
+                mAccountFilterHeader, filter, mAllContactsAdapter.isLoading(), true);
     }
 
     public ContactListFilter getFilter() {
diff --git a/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java b/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
index f817b4c..205e156 100644
--- a/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
+++ b/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
@@ -184,9 +184,10 @@
         if (position < contactTileAdapterCount) {
             return mContactTileAdapter.isEnabled(position);
         } else if (position == contactTileAdapterCount) {
-            return mAccountFilterHeaderContainer.isEnabled();
+            // This will be handled by View's onClick event instead of ListView's onItemClick event.
+            return false;
         } else {
-            final int localPosition = position - contactTileAdapterCount + 1;
+            final int localPosition = position - contactTileAdapterCount - 1;
             return mContactEntryListAdapter.isEnabled(localPosition);
         }
     }
diff --git a/src/com/android/contacts/list/PhoneNumberPickerFragment.java b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
index 64d5f13..052c32f 100644
--- a/src/com/android/contacts/list/PhoneNumberPickerFragment.java
+++ b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
@@ -17,8 +17,8 @@
 
 import com.android.contacts.R;
 import com.android.contacts.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
+import com.android.contacts.util.AccountFilterUtil;
 
-import android.app.Activity;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
@@ -28,7 +28,6 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.widget.TextView;
 
 /**
  * Fragment containing a phone number list for picking.
@@ -37,15 +36,16 @@
         implements OnShortcutIntentCreatedListener {
     private static final String TAG = PhoneNumberPickerFragment.class.getSimpleName();
 
+    private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
+
     private OnPhoneNumberPickerActionListener mListener;
     private String mShortcutAction;
 
     private ContactListFilter mFilter;
 
-    private TextView mAccountFilterHeaderView;
-    private View mAccountFilterHeaderContainer;
+    private View mAccountFilterHeader;
     /**
-     * Lives as ListView's header and is shown when {@link #mAccountFilterHeaderContainer} is set
+     * Lives as ListView's header and is shown when {@link #mAccountFilterHeader} is set
      * to View.GONE.
      */
     private View mPaddingView;
@@ -58,17 +58,11 @@
     private ContactListItemView.PhotoPosition mPhotoPosition =
             ContactListItemView.DEFAULT_PHOTO_POSITION;
 
-    // A complete copy from DefaultContactBrowserListFragment
-    // TODO: should be able to share logic around filter header.
     private class FilterHeaderClickListener implements OnClickListener {
         @Override
         public void onClick(View view) {
-            final Activity activity = getActivity();
-            if (activity != null) {
-                final Intent intent = new Intent(activity, AccountFilterActivity.class);
-                activity.startActivityForResult(
-                        intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
-            }
+            AccountFilterUtil.startAccountFilterActivityForResult(
+                    PhoneNumberPickerFragment.this, REQUEST_CODE_ACCOUNT_FILTER);
         }
     }
     private OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
@@ -96,45 +90,30 @@
         mPaddingView = paddingView.findViewById(R.id.contact_detail_list_padding);
         getListView().addHeaderView(paddingView);
 
-        mAccountFilterHeaderView = (TextView) getView().findViewById(R.id.account_filter_header);
-        mAccountFilterHeaderContainer =
-                getView().findViewById(R.id.account_filter_header_container);
-        mAccountFilterHeaderContainer.setOnClickListener(mFilterHeaderClickListener);
+        mAccountFilterHeader = getView().findViewById(R.id.account_filter_header_container);
+        mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
         updateFilterHeaderView();
     }
 
     @Override
-    public void setSearchMode(boolean flag) {
+    protected void setSearchMode(boolean flag) {
         super.setSearchMode(flag);
         updateFilterHeaderView();
     }
 
     private void updateFilterHeaderView() {
-        if (mAccountFilterHeaderView != null) {
-            ContactListFilter filter = getFilter();
-            if (filter != null && !isSearchMode()) {
-                if (filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
-                    mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
-                    mAccountFilterHeaderView.setText(getContext().getString(
-                            R.string.listSingleContact));
-                    mPaddingView.setVisibility(View.GONE);
-                    return;
-                } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
-                    mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
-                    mAccountFilterHeaderView.setText(getContext().getString(
-                            R.string.listCustomView));
-                    mPaddingView.setVisibility(View.GONE);
-                    return;
-                } else if (filter.filterType != ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
-                    mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
-                    mAccountFilterHeaderView.setText(getContext().getString(
-                            R.string.listAllContactsInAccount, filter.accountName));
-                    mPaddingView.setVisibility(View.GONE);
-                    return;
-                }
-            }
-            mAccountFilterHeaderContainer.setVisibility(View.GONE);
+        final ContactListFilter filter = getFilter();
+        if (mAccountFilterHeader == null || filter == null) {
+            return;
+        }
+        final boolean shouldShowHeader = AccountFilterUtil.updateAccountFilterTitleForPhone(
+                mAccountFilterHeader, filter, false, false);
+        if (shouldShowHeader) {
+            mPaddingView.setVisibility(View.GONE);
+            mAccountFilterHeader.setVisibility(View.VISIBLE);
+        } else {
             mPaddingView.setVisibility(View.VISIBLE);
+            mAccountFilterHeader.setVisibility(View.GONE);
         }
     }
 
@@ -233,7 +212,7 @@
 
     @Override
     protected View inflateView(LayoutInflater inflater, ViewGroup container) {
-        return inflater.inflate(R.layout.contacts_list_content, null);
+        return inflater.inflate(R.layout.contact_list_content, null);
     }
 
     public void pickPhoneNumber(Uri uri) {
@@ -257,6 +236,18 @@
         mListener.onPickPhoneNumberAction(data.getData());
     }
 
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
+            if (getActivity() != null) {
+                AccountFilterUtil.handleAccountFilterResult(
+                        ContactListFilterController.getInstance(getActivity()), resultCode, data);
+            } else {
+                Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
+            }
+        }
+    }
+
     public ContactListFilter getFilter() {
         return mFilter;
     }
diff --git a/src/com/android/contacts/list/PostalAddressPickerFragment.java b/src/com/android/contacts/list/PostalAddressPickerFragment.java
index 5f7ca56..85fc155 100644
--- a/src/com/android/contacts/list/PostalAddressPickerFragment.java
+++ b/src/com/android/contacts/list/PostalAddressPickerFragment.java
@@ -70,7 +70,7 @@
 
     @Override
     protected View inflateView(LayoutInflater inflater, ViewGroup container) {
-        return inflater.inflate(R.layout.contacts_list_content, null);
+        return inflater.inflate(R.layout.contact_list_content, null);
     }
 
     private void pickPostalAddress(Uri uri) {
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index 654adeb..23d39d3 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -32,7 +32,6 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
 import android.view.inputmethod.EditorInfo;
 import android.widget.EditText;
 
@@ -83,11 +82,35 @@
      */
     private HashMap<String, DataKind> mMimeKinds = Maps.newHashMap();
 
+    protected boolean mIsInitialized;
+
+    protected static class DefinitionException extends Exception {
+        public DefinitionException(String message) {
+            super(message);
+        }
+
+        public DefinitionException(String message, Exception inner) {
+            super(message, inner);
+        }
+    }
+
     /**
      * Whether this account type was able to be fully initialized.  This may be false if
      * (for example) the package name associated with the account type could not be found.
      */
-    public boolean isInitialized() {
+    public final boolean isInitialized() {
+        return mIsInitialized;
+    }
+
+    /**
+     * @return Whether this type is an "embedded" type.  i.e. any of {@link FallbackAccountType},
+     * {@link GoogleAccountType} or {@link ExternalAccountType}.
+     *
+     * If an embedded type cannot be initialized (i.e. if {@link #isInitialized()} returns
+     * {@code false}) it's considered critical, and the application will crash.  On the other
+     * hand if it's not an embedded type, we just skip loading the type.
+     */
+    public boolean isEmbedded() {
         return true;
     }
 
@@ -274,10 +297,10 @@
     /**
      * Add given {@link DataKind} to list of those provided by this source.
      */
-    public DataKind addKind(DataKind kind) {
+    public DataKind addKind(DataKind kind) throws DefinitionException {
         if (mMimeKinds.get(kind.mimeType) != null) {
-            // TODO Make it exception.
-            Log.w(TAG, "mime type '" + kind.mimeType + "' is already registered");
+            throw new DefinitionException(
+                    "mime type '" + kind.mimeType + "' is already registered");
         }
 
         kind.resPackageName = this.resPackageName;
@@ -337,6 +360,16 @@
         public int hashCode() {
             return rawValue;
         }
+
+        @Override
+        public String toString() {
+            return this.getClass().getSimpleName()
+                    + " rawValue=" + rawValue
+                    + " labelRes=" + labelRes
+                    + " secondary=" + secondary
+                    + " specificMax=" + specificMax
+                    + " customColumn=" + customColumn;
+        }
     }
 
     public static class EventEditType extends EditType {
@@ -354,6 +387,11 @@
             mYearOptional = yearOptional;
             return this;
         }
+
+        @Override
+        public String toString() {
+            return super.toString() + " mYearOptional=" + mYearOptional;
+        }
     }
 
     /**
@@ -403,6 +441,19 @@
         public boolean isMultiLine() {
             return (inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
         }
+
+
+        @Override
+        public String toString() {
+            return this.getClass().getSimpleName() + ":"
+                    + " column=" + column
+                    + " titleRes=" + titleRes
+                    + " inputType=" + inputType
+                    + " minLines=" + minLines
+                    + " optional=" + optional
+                    + " shortForm=" + shortForm
+                    + " longForm=" + longForm;
+        }
     }
 
     /**
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 92323fa..43bb579 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -411,7 +411,12 @@
                     Log.d(TAG, "Registering external account type=" + type
                             + ", packageName=" + auth.packageName);
                     accountType = new ExternalAccountType(mContext, auth.packageName, false);
-                    if (!accountType.isInitialized()) {
+                }
+                if (!accountType.isInitialized()) {
+                    if (accountType.isEmbedded()) {
+                        throw new IllegalStateException("Problem initializing embedded type "
+                                + accountType.getClass().getCanonicalName());
+                    } else {
                         // Skip external account types that couldn't be initialized.
                         continue;
                     }
diff --git a/src/com/android/contacts/model/BaseAccountType.java b/src/com/android/contacts/model/BaseAccountType.java
index 34beca2..0d766da 100644
--- a/src/com/android/contacts/model/BaseAccountType.java
+++ b/src/com/android/contacts/model/BaseAccountType.java
@@ -17,6 +17,7 @@
 package com.android.contacts.model;
 
 import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DefinitionException;
 import com.android.contacts.util.DateUtils;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
@@ -107,16 +108,6 @@
         static final int GROUP_MEMBERSHIP = 999;
     }
 
-    protected static class DefinitionException extends Exception {
-        public DefinitionException(String message) {
-            super(message);
-        }
-
-        public DefinitionException(String message, Exception inner) {
-            super(message, inner);
-        }
-    }
-
     public BaseAccountType() {
         this.accountType = null;
         this.dataSet = null;
@@ -148,11 +139,12 @@
         return new EditType(type, Relation.getTypeLabelResource(type));
     }
 
-    protected DataKind addDataKindStructuredName(Context context) {
+    protected DataKind addDataKindStructuredName(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
                 R.string.nameLabelsGroup, -1, true, R.layout.structured_name_editor_view));
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
         kind.actionBody = new SimpleInflater(Nickname.NAME);
+        kind.typeOverallMax = 1;
 
         kind.fieldList = Lists.newArrayList();
         kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
@@ -177,11 +169,12 @@
         return kind;
     }
 
-    protected DataKind addDataKindDisplayName(Context context) {
+    protected DataKind addDataKindDisplayName(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
                 R.string.nameLabelsGroup, -1, true, R.layout.text_fields_editor_view));
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
         kind.actionBody = new SimpleInflater(Nickname.NAME);
+        kind.typeOverallMax = 1;
 
         kind.fieldList = Lists.newArrayList();
         kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
@@ -217,11 +210,12 @@
         return kind;
     }
 
-    protected DataKind addDataKindPhoneticName(Context context) {
+    protected DataKind addDataKindPhoneticName(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
                 R.string.name_phonetic, -1, true, R.layout.phonetic_name_editor_view));
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
         kind.actionBody = new SimpleInflater(Nickname.NAME);
+        kind.typeOverallMax = 1;
 
         kind.fieldList = Lists.newArrayList();
         kind.fieldList.add(new EditField(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
@@ -236,7 +230,7 @@
         return kind;
     }
 
-    protected DataKind addDataKindNickname(Context context) {
+    protected DataKind addDataKindNickname(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(Nickname.CONTENT_ITEM_TYPE,
                     R.string.nicknameLabelsGroup, 115, true, R.layout.text_fields_editor_view));
         kind.typeOverallMax = 1;
@@ -252,7 +246,7 @@
         return kind;
     }
 
-    protected DataKind addDataKindPhone(Context context) {
+    protected DataKind addDataKindPhone(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(Phone.CONTENT_ITEM_TYPE, R.string.phoneLabelsGroup,
                 10, true, R.layout.text_fields_editor_view));
         kind.iconAltRes = R.drawable.ic_text_holo_light;
@@ -262,8 +256,8 @@
         kind.actionBody = new SimpleInflater(Phone.NUMBER);
         kind.typeColumn = Phone.TYPE;
         kind.typeList = Lists.newArrayList();
-        kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
         kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
+        kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
         kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
         kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
         kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
@@ -282,8 +276,7 @@
         kind.typeList.add(buildPhoneType(Phone.TYPE_TTY_TDD).setSecondary(true));
         kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_MOBILE).setSecondary(true));
         kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_PAGER).setSecondary(true));
-        kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true).setCustomColumn(
-                Phone.LABEL));
+        kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true));
         kind.typeList.add(buildPhoneType(Phone.TYPE_MMS).setSecondary(true));
 
         kind.fieldList = Lists.newArrayList();
@@ -292,7 +285,7 @@
         return kind;
     }
 
-    protected DataKind addDataKindEmail(Context context) {
+    protected DataKind addDataKindEmail(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(Email.CONTENT_ITEM_TYPE, R.string.emailLabelsGroup,
                 15, true, R.layout.text_fields_editor_view));
         kind.actionHeader = new EmailActionInflater();
@@ -312,7 +305,7 @@
         return kind;
     }
 
-    protected DataKind addDataKindStructuredPostal(Context context) {
+    protected DataKind addDataKindStructuredPostal(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(StructuredPostal.CONTENT_ITEM_TYPE,
                 R.string.postalLabelsGroup, 25, true, R.layout.text_fields_editor_view));
         kind.actionHeader = new PostalActionInflater();
@@ -333,7 +326,7 @@
         return kind;
     }
 
-    protected DataKind addDataKindIm(Context context) {
+    protected DataKind addDataKindIm(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup, 20, true,
                     R.layout.text_fields_editor_view));
         kind.actionHeader = new ImActionInflater();
@@ -364,7 +357,7 @@
         return kind;
     }
 
-    protected DataKind addDataKindOrganization(Context context) {
+    protected DataKind addDataKindOrganization(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(Organization.CONTENT_ITEM_TYPE,
                     R.string.organizationLabelsGroup, 5, true,
                     R.layout.text_fields_editor_view));
@@ -381,14 +374,15 @@
         return kind;
     }
 
-    protected DataKind addDataKindPhoto(Context context) {
+    protected DataKind addDataKindPhoto(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, true, -1));
+        kind.typeOverallMax = 1;
         kind.fieldList = Lists.newArrayList();
         kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
         return kind;
     }
 
-    protected DataKind addDataKindNote(Context context) {
+    protected DataKind addDataKindNote(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(Note.CONTENT_ITEM_TYPE,
                     R.string.label_notes, 110, true, R.layout.text_fields_editor_view));
         kind.typeOverallMax = 1;
@@ -400,7 +394,7 @@
         return kind;
     }
 
-    protected DataKind addDataKindWebsite(Context context) {
+    protected DataKind addDataKindWebsite(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(Website.CONTENT_ITEM_TYPE,
                 R.string.websiteLabelsGroup, 120, true, R.layout.text_fields_editor_view));
         kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup);
@@ -414,7 +408,7 @@
         return kind;
     }
 
-    protected DataKind addDataKindSipAddress(Context context) {
+    protected DataKind addDataKindSipAddress(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(SipAddress.CONTENT_ITEM_TYPE,
                     R.string.label_sip_address, 130, true, R.layout.text_fields_editor_view));
 
@@ -428,7 +422,7 @@
         return kind;
     }
 
-    protected DataKind addDataKindGroupMembership(Context context) {
+    protected DataKind addDataKindGroupMembership(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(GroupMembership.CONTENT_ITEM_TYPE,
                 R.string.groupsLabel, 999, true, -1));
 
@@ -496,6 +490,13 @@
                 return null;
             }
         }
+
+        @Override
+        public String toString() {
+            return this.getClass().getSimpleName()
+                    + " mStringRes=" + mStringRes
+                    + " mColumnName" + mColumnName;
+        }
     }
 
     public static abstract class CommonInflater implements StringInflater {
@@ -535,6 +536,11 @@
             final String label = values.getAsString(getLabelColumn());
             return getTypeLabel(context.getResources(), type, label);
         }
+
+        @Override
+        public String toString() {
+            return this.getClass().getSimpleName();
+        }
     }
 
     public static class PhoneActionInflater extends CommonInflater {
@@ -801,15 +807,14 @@
             kind.actionBody = actionBody;
             kind.fieldList = Lists.newArrayList();
 
-            kind.typeOverallMax = getAttr(attrs, Attr.MAX_OCCURRENCE, -1);
-
-            // Handle "types".
-            // If a kind has the type column, contacts.xml must have at least one type definition.
-            // Otherwise, it mustn't have a type definition.
-            //
-            // If it's a pseudo data kind (== data kind that doesn't have the corresponding
-            // DataKind tag in the XML), we just skip this process.
+            // Get more information from the tag...
+            // A pseudo data kind doesn't have corresponding tag the XML, so we skip this.
             if (!isPseudo) {
+                kind.typeOverallMax = getAttr(attrs, Attr.MAX_OCCURRENCE, -1);
+
+                // Process "Type" tags.
+                // If a kind has the type column, contacts.xml must have at least one type
+                // definition.  Otherwise, it mustn't have a type definition.
                 if (kind.typeColumn != null) {
                     // Parse and add types.
                     kind.typeList = Lists.newArrayList();
@@ -957,7 +962,6 @@
             throwIfList(ks);
             kinds.add(ks);
 
-
             // Note about setLongForm/setShortForm below.
             // We need to set this only when the type supports display name. (=supportsDisplayName)
             // Otherwise (i.e. Exchange) we don't set these flags, but instead make some fields
@@ -988,6 +992,7 @@
                     R.string.nameLabelsGroup, Weight.NONE, R.layout.text_fields_editor_view,
                     new SimpleInflater(R.string.nameLabelsGroup),
                     new SimpleInflater(Nickname.NAME));
+            kd.typeOverallMax = 1;
             kinds.add(kd);
 
             kd.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
@@ -1023,25 +1028,18 @@
                     R.string.name_phonetic, Weight.NONE, R.layout.phonetic_name_editor_view,
                     new SimpleInflater(R.string.nameLabelsGroup),
                     new SimpleInflater(Nickname.NAME));
+            kp.typeOverallMax = 1;
             kinds.add(kp);
 
+            // We may want to change the order depending on displayOrderPrimary too.
             kp.fieldList.add(new EditField(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
                     R.string.name_phonetic, FLAGS_PHONETIC).setShortForm(true));
-            if (!displayOrderPrimary) {
-                kp.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
-                        R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
-                kp.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
-                        R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
-                kp.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
-                        R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
-            } else {
-                kp.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
-                        R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
-                kp.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
-                        R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
-                kp.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
-                        R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
-            }
+            kp.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
+                    R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
+            kp.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
+                    R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
+            kp.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
+                    R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
             return kinds;
         }
     }
diff --git a/src/com/android/contacts/model/DataKind.java b/src/com/android/contacts/model/DataKind.java
index aaf7bb5..857f3e4 100644
--- a/src/com/android/contacts/model/DataKind.java
+++ b/src/com/android/contacts/model/DataKind.java
@@ -20,6 +20,7 @@
 import com.android.contacts.model.AccountType.EditField;
 import com.android.contacts.model.AccountType.EditType;
 import com.android.contacts.model.AccountType.StringInflater;
+import com.google.common.collect.Iterators;
 
 import android.content.ContentValues;
 import android.provider.ContactsContract.Data;
@@ -95,4 +96,43 @@
         this.typeOverallMax = -1;
         this.editorLayoutResourceId = editorLayoutResourceId;
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DataKind:");
+        sb.append(" resPackageName=").append(resPackageName);
+        sb.append(" mimeType=").append(mimeType);
+        sb.append(" titleRes=").append(titleRes);
+        sb.append(" iconAltRes=").append(iconAltRes);
+        sb.append(" iconAltDescriptionRes=").append(iconAltDescriptionRes);
+        sb.append(" weight=").append(weight);
+        sb.append(" editable=").append(editable);
+        sb.append(" actionHeader=").append(actionHeader);
+        sb.append(" actionAltHeader=").append(actionAltHeader);
+        sb.append(" actionBody=").append(actionBody);
+        sb.append(" actionBodySocial=").append(actionBodySocial);
+        sb.append(" typeColumn=").append(typeColumn);
+        sb.append(" typeOverallMax=").append(typeOverallMax);
+        sb.append(" typeList=").append(toString(typeList));
+        sb.append(" fieldList=").append(toString(fieldList));
+        sb.append(" defaultValues=").append(defaultValues);
+        sb.append(" editorLayoutResourceId=").append(editorLayoutResourceId);
+        sb.append(" dateFormatWithoutYear=").append(toString(dateFormatWithoutYear));
+        sb.append(" dateFormatWithYear=").append(toString(dateFormatWithYear));
+
+        return sb.toString();
+    }
+
+    public static String toString(SimpleDateFormat format) {
+        return format == null ? "(null)" : format.toPattern();
+    }
+
+    public static String toString(Iterable<?> list) {
+        if (list == null) {
+            return "(null)";
+        } else {
+            return Iterators.toString(list.iterator());
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/contacts/model/ExchangeAccountType.java b/src/com/android/contacts/model/ExchangeAccountType.java
index bb11cf6..e5491d2 100644
--- a/src/com/android/contacts/model/ExchangeAccountType.java
+++ b/src/com/android/contacts/model/ExchangeAccountType.java
@@ -17,6 +17,7 @@
 package com.android.contacts.model;
 
 import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DefinitionException;
 import com.android.contacts.util.DateUtils;
 import com.google.android.collect.Lists;
 
@@ -33,10 +34,12 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.util.Log;
 
 import java.util.Locale;
 
 public class ExchangeAccountType extends BaseAccountType {
+    private static final String TAG = "ExchangeAccountType";
 
     public static final String ACCOUNT_TYPE = "com.android.exchange";
 
@@ -45,24 +48,30 @@
         this.resPackageName = null;
         this.summaryResPackageName = resPackageName;
 
-        addDataKindStructuredName(context);
-        addDataKindDisplayName(context);
-        addDataKindPhoneticName(context);
-        addDataKindNickname(context);
-        addDataKindPhone(context);
-        addDataKindEmail(context);
-        addDataKindStructuredPostal(context);
-        addDataKindIm(context);
-        addDataKindOrganization(context);
-        addDataKindPhoto(context);
-        addDataKindNote(context);
-        addDataKindEvent(context);
-        addDataKindWebsite(context);
-        addDataKindGroupMembership(context);
+        try {
+            addDataKindStructuredName(context);
+            addDataKindDisplayName(context);
+            addDataKindPhoneticName(context);
+            addDataKindNickname(context);
+            addDataKindPhone(context);
+            addDataKindEmail(context);
+            addDataKindStructuredPostal(context);
+            addDataKindIm(context);
+            addDataKindOrganization(context);
+            addDataKindPhoto(context);
+            addDataKindNote(context);
+            addDataKindEvent(context);
+            addDataKindWebsite(context);
+            addDataKindGroupMembership(context);
+
+            mIsInitialized = true;
+        } catch (DefinitionException e) {
+            Log.e(TAG, "Problem building account type", e);
+        }
     }
 
     @Override
-    protected DataKind addDataKindStructuredName(Context context) {
+    protected DataKind addDataKindStructuredName(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
                 R.string.nameLabelsGroup, -1, true, R.layout.structured_name_editor_view));
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
@@ -91,7 +100,7 @@
     }
 
     @Override
-    protected DataKind addDataKindDisplayName(Context context) {
+    protected DataKind addDataKindDisplayName(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
                 R.string.nameLabelsGroup, -1, true, R.layout.text_fields_editor_view));
 
@@ -124,7 +133,7 @@
     }
 
     @Override
-    protected DataKind addDataKindPhoneticName(Context context) {
+    protected DataKind addDataKindPhoneticName(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
                 R.string.name_phonetic, -1, true, R.layout.phonetic_name_editor_view));
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
@@ -142,7 +151,7 @@
     }
 
     @Override
-    protected DataKind addDataKindNickname(Context context) {
+    protected DataKind addDataKindNickname(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindNickname(context);
 
         kind.typeOverallMax = 1;
@@ -155,7 +164,7 @@
     }
 
     @Override
-    protected DataKind addDataKindPhone(Context context) {
+    protected DataKind addDataKindPhone(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindPhone(context);
 
         kind.typeColumn = Phone.TYPE;
@@ -185,7 +194,7 @@
     }
 
     @Override
-    protected DataKind addDataKindEmail(Context context) {
+    protected DataKind addDataKindEmail(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindEmail(context);
 
         kind.typeOverallMax = 3;
@@ -197,7 +206,7 @@
     }
 
     @Override
-    protected DataKind addDataKindStructuredPostal(Context context) {
+    protected DataKind addDataKindStructuredPostal(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindStructuredPostal(context);
 
         final boolean useJapaneseOrder =
@@ -237,7 +246,7 @@
     }
 
     @Override
-    protected DataKind addDataKindIm(Context context) {
+    protected DataKind addDataKindIm(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindIm(context);
 
         // Types are not supported for IM. There can be 3 IMs, but OWA only shows only the first
@@ -253,7 +262,7 @@
     }
 
     @Override
-    protected DataKind addDataKindOrganization(Context context) {
+    protected DataKind addDataKindOrganization(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindOrganization(context);
 
         kind.typeOverallMax = 1;
@@ -268,7 +277,7 @@
     }
 
     @Override
-    protected DataKind addDataKindPhoto(Context context) {
+    protected DataKind addDataKindPhoto(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindPhoto(context);
 
         kind.typeOverallMax = 1;
@@ -280,7 +289,7 @@
     }
 
     @Override
-    protected DataKind addDataKindNote(Context context) {
+    protected DataKind addDataKindNote(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindNote(context);
 
         kind.fieldList = Lists.newArrayList();
@@ -289,7 +298,7 @@
         return kind;
     }
 
-    protected DataKind addDataKindEvent(Context context) {
+    protected DataKind addDataKindEvent(Context context) throws DefinitionException {
         DataKind kind = addKind(
                 new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, 150, true,
                 R.layout.event_field_editor_view));
@@ -311,7 +320,7 @@
     }
 
     @Override
-    protected DataKind addDataKindWebsite(Context context) {
+    protected DataKind addDataKindWebsite(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindWebsite(context);
 
         kind.typeOverallMax = 1;
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index 968993a..4eaa976 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -16,8 +16,6 @@
 
 package com.android.contacts.model;
 
-import com.android.contacts.R;
-import com.android.contacts.model.BaseAccountType.DefinitionException;
 import com.google.common.annotations.VisibleForTesting;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -88,30 +86,49 @@
     private List<String> mExtensionPackageNames;
     private String mAccountTypeLabelAttribute;
     private String mAccountTypeIconAttribute;
-    private boolean mInitSuccessful;
     private boolean mHasContactsMetadata;
     private boolean mHasEditSchema;
 
     public ExternalAccountType(Context context, String resPackageName, boolean isExtension) {
+        this(context, resPackageName, isExtension, null);
+    }
+
+    /**
+     * Constructor used for testing to initialize with any arbitrary XML.
+     *
+     * @param injectedMetadata If non-null, it'll be used to initialize the type.  Only set by
+     *     tests.  If null, the metadata is loaded from the specified package.
+     */
+    ExternalAccountType(Context context, String resPackageName, boolean isExtension,
+            XmlResourceParser injectedMetadata) {
         this.mIsExtension = isExtension;
         this.resPackageName = resPackageName;
         this.summaryResPackageName = resPackageName;
 
-        // Handle unknown sources by searching their package
         final PackageManager pm = context.getPackageManager();
-        XmlResourceParser parser = null;
+        final XmlResourceParser parser;
+        if (injectedMetadata == null) {
+            try {
+                parser = loadContactsXml(context, resPackageName);
+            } catch (NameNotFoundException e1) {
+                // If the package name is not found, we can't initialize this account type.
+                return;
+            }
+        } else {
+            parser = injectedMetadata;
+        }
         try {
-            PackageInfo packageInfo = pm.getPackageInfo(resPackageName,
-                    PackageManager.GET_SERVICES|PackageManager.GET_META_DATA);
-            for (ServiceInfo serviceInfo : packageInfo.services) {
-                parser = serviceInfo.loadXmlMetaData(pm,
-                        METADATA_CONTACTS);
-                if (parser == null) continue;
+            if (parser != null) {
                 inflate(context, parser);
             }
-        } catch (NameNotFoundException nnfe) {
-            // If the package name is not found, we can't initialize this account type.
-            return;
+
+            if (!mHasEditSchema) {
+                // Bring in name and photo from fallback source, which are non-optional
+                addDataKindStructuredName(context);
+                addDataKindDisplayName(context);
+                addDataKindPhoneticName(context);
+                addDataKindPhoto(context);
+            }
         } catch (DefinitionException e) {
             String message = "Problem reading XML";
             if (parser != null) {
@@ -135,16 +152,42 @@
         iconRes = resolveExternalResId(context, mAccountTypeIconAttribute,
                 this.resPackageName, ATTR_ACCOUNT_ICON);
 
-        if (!mHasEditSchema) {
-            // Bring in name and photo from fallback source, which are non-optional
-            addDataKindStructuredName(context);
-            addDataKindDisplayName(context);
-            addDataKindPhoneticName(context);
-            addDataKindPhoto(context);
-        }
-
         // If we reach this point, the account type has been successfully initialized.
-        mInitSuccessful = true;
+        mIsInitialized = true;
+    }
+
+    /**
+     * Returns the CONTACTS_STRUCTURE metadata (aka "contacts.xml") in the given apk package.
+     *
+     * Unfortunately, there's no public way to determine which service defines a sync service for
+     * which account type, so this method looks through all services in the package, and just
+     * returns the first CONTACTS_STRUCTURE metadata defined in any of them.
+     *
+     * Returns {@code null} if the package has no CONTACTS_STRUCTURE metadata.  In this case
+     * the account type *will* be initialized with minimal configuration.
+     *
+     * On the other hand, if the package is not found, it throws a {@link NameNotFoundException},
+     * in which case the account type will *not* be initialized.
+     */
+    private XmlResourceParser loadContactsXml(Context context, String resPackageName)
+            throws NameNotFoundException {
+        final PackageManager pm = context.getPackageManager();
+        PackageInfo packageInfo = pm.getPackageInfo(resPackageName,
+                PackageManager.GET_SERVICES|PackageManager.GET_META_DATA);
+        for (ServiceInfo serviceInfo : packageInfo.services) {
+            final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm,
+                    METADATA_CONTACTS);
+            if (parser != null) {
+                return parser;
+            }
+        }
+        // Package was found, but that doesn't contain the CONTACTS_STRUCTURE metadata.
+        return null;
+    }
+
+    @Override
+    public boolean isEmbedded() {
+        return false;
     }
 
     @Override
@@ -153,11 +196,6 @@
     }
 
     @Override
-    public boolean isInitialized() {
-        return mInitSuccessful;
-    }
-
-    @Override
     public boolean areContactsWritable() {
         return mHasEditSchema;
     }
diff --git a/src/com/android/contacts/model/FallbackAccountType.java b/src/com/android/contacts/model/FallbackAccountType.java
index 216d6d0..d81f2f5 100644
--- a/src/com/android/contacts/model/FallbackAccountType.java
+++ b/src/com/android/contacts/model/FallbackAccountType.java
@@ -19,31 +19,52 @@
 import com.android.contacts.R;
 
 import android.content.Context;
+import android.util.Log;
 
 public class FallbackAccountType extends BaseAccountType {
+    private static final String TAG = "FallbackAccountType";
 
-    public FallbackAccountType(Context context) {
+    private FallbackAccountType(Context context, String resPackageName) {
         this.accountType = null;
         this.dataSet = null;
         this.titleRes = R.string.account_phone;
         this.iconRes = R.mipmap.ic_launcher_contacts;
 
-        this.resPackageName = null;
+        this.resPackageName = resPackageName;
         this.summaryResPackageName = resPackageName;
 
-        addDataKindStructuredName(context);
-        addDataKindDisplayName(context);
-        addDataKindPhoneticName(context);
-        addDataKindNickname(context);
-        addDataKindPhone(context);
-        addDataKindEmail(context);
-        addDataKindStructuredPostal(context);
-        addDataKindIm(context);
-        addDataKindOrganization(context);
-        addDataKindPhoto(context);
-        addDataKindNote(context);
-        addDataKindWebsite(context);
-        addDataKindSipAddress(context);
+        try {
+            addDataKindStructuredName(context);
+            addDataKindDisplayName(context);
+            addDataKindPhoneticName(context);
+            addDataKindNickname(context);
+            addDataKindPhone(context);
+            addDataKindEmail(context);
+            addDataKindStructuredPostal(context);
+            addDataKindIm(context);
+            addDataKindOrganization(context);
+            addDataKindPhoto(context);
+            addDataKindNote(context);
+            addDataKindWebsite(context);
+            addDataKindSipAddress(context);
+
+            mIsInitialized = true;
+        } catch (DefinitionException e) {
+            Log.e(TAG, "Problem building account type", e);
+        }
+    }
+
+    public FallbackAccountType(Context context) {
+        this(context, null);
+    }
+
+    /**
+     * Used to compare with an {@link ExternalAccountType} built from a test contacts.xml.
+     * In order to build {@link DataKind}s with the same resource package name,
+     * {@code resPackageName} is injectable.
+     */
+    static AccountType createForTest(Context context, String resPackageName) {
+        return new FallbackAccountType(context, resPackageName);
     }
 
     @Override
diff --git a/src/com/android/contacts/model/GoogleAccountType.java b/src/com/android/contacts/model/GoogleAccountType.java
index c101602..822d829 100644
--- a/src/com/android/contacts/model/GoogleAccountType.java
+++ b/src/com/android/contacts/model/GoogleAccountType.java
@@ -17,6 +17,7 @@
 package com.android.contacts.model;
 
 import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DefinitionException;
 import com.android.contacts.util.DateUtils;
 import com.google.android.collect.Lists;
 
@@ -26,10 +27,13 @@
 import android.provider.ContactsContract.CommonDataKinds.Event;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.util.Log;
 
 import java.util.List;
 
 public class GoogleAccountType extends BaseAccountType {
+    private static final String TAG = "GoogleAccountType";
+
     public static final String ACCOUNT_TYPE = "com.google";
 
     private static final List<String> mExtensionPackages =
@@ -40,22 +44,28 @@
         this.resPackageName = null;
         this.summaryResPackageName = resPackageName;
 
-        addDataKindStructuredName(context);
-        addDataKindDisplayName(context);
-        addDataKindPhoneticName(context);
-        addDataKindNickname(context);
-        addDataKindPhone(context);
-        addDataKindEmail(context);
-        addDataKindStructuredPostal(context);
-        addDataKindIm(context);
-        addDataKindOrganization(context);
-        addDataKindPhoto(context);
-        addDataKindNote(context);
-        addDataKindWebsite(context);
-        addDataKindSipAddress(context);
-        addDataKindGroupMembership(context);
-        addDataKindRelation(context);
-        addDataKindEvent(context);
+        try {
+            addDataKindStructuredName(context);
+            addDataKindDisplayName(context);
+            addDataKindPhoneticName(context);
+            addDataKindNickname(context);
+            addDataKindPhone(context);
+            addDataKindEmail(context);
+            addDataKindStructuredPostal(context);
+            addDataKindIm(context);
+            addDataKindOrganization(context);
+            addDataKindPhoto(context);
+            addDataKindNote(context);
+            addDataKindWebsite(context);
+            addDataKindSipAddress(context);
+            addDataKindGroupMembership(context);
+            addDataKindRelation(context);
+            addDataKindEvent(context);
+
+            mIsInitialized = true;
+        } catch (DefinitionException e) {
+            Log.e(TAG, "Problem building account type", e);
+        }
     }
 
     @Override
@@ -64,7 +74,7 @@
     }
 
     @Override
-    protected DataKind addDataKindPhone(Context context) {
+    protected DataKind addDataKindPhone(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindPhone(context);
 
         kind.typeColumn = Phone.TYPE;
@@ -87,7 +97,7 @@
     }
 
     @Override
-    protected DataKind addDataKindEmail(Context context) {
+    protected DataKind addDataKindEmail(Context context) throws DefinitionException {
         final DataKind kind = super.addDataKindEmail(context);
 
         kind.typeColumn = Email.TYPE;
@@ -104,7 +114,7 @@
         return kind;
     }
 
-    private DataKind addDataKindRelation(Context context) {
+    private DataKind addDataKindRelation(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(Relation.CONTENT_ITEM_TYPE,
                 R.string.relationLabelsGroup, 160, true, R.layout.text_fields_editor_view));
         kind.actionHeader = new RelationActionInflater();
@@ -139,7 +149,7 @@
         return kind;
     }
 
-    private DataKind addDataKindEvent(Context context) {
+    private DataKind addDataKindEvent(Context context) throws DefinitionException {
         DataKind kind = addKind(new DataKind(Event.CONTENT_ITEM_TYPE,
                     R.string.eventLabelsGroup, 150, true, R.layout.event_field_editor_view));
         kind.actionHeader = new EventActionInflater();
diff --git a/src/com/android/contacts/util/AccountFilterUtil.java b/src/com/android/contacts/util/AccountFilterUtil.java
new file mode 100644
index 0000000..955c195
--- /dev/null
+++ b/src/com/android/contacts/util/AccountFilterUtil.java
@@ -0,0 +1,165 @@
+/*
+ * 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.util;
+
+import com.android.contacts.R;
+import com.android.contacts.list.AccountFilterActivity;
+import com.android.contacts.list.ContactListFilter;
+import com.android.contacts.list.ContactListFilterController;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * Utility class for account filter manipulation.
+ */
+public class AccountFilterUtil {
+    private static final String TAG = AccountFilterUtil.class.getSimpleName();
+
+    /**
+     * Find TextView with the id "account_filter_header" and set correct text for the account
+     * filter header.
+     *
+     * @param filterContainer View containing TextView with id "account_filter_header"
+     * @return true when header text is set in the call. You may use this for conditionally
+     * showing or hiding this entire view.
+     */
+    public static boolean updateAccountFilterTitleForPeople(View filterContainer,
+            ContactListFilter filter, boolean isLoading, boolean showTitleForAllAccounts) {
+        return updateAccountFilterTitle(
+                filterContainer, filter, isLoading, showTitleForAllAccounts, false);
+    }
+
+    /**
+     * Similar to {@link #updateAccountFilterTitleForPeople(View, ContactListFilter, boolean,
+     * boolean)}, but for Phone UI.
+     */
+    public static boolean updateAccountFilterTitleForPhone(View filterContainer,
+            ContactListFilter filter, boolean isLoading, boolean showTitleForAllAccounts) {
+        return updateAccountFilterTitle(
+                filterContainer, filter, isLoading, showTitleForAllAccounts, true);
+    }
+
+    private static boolean updateAccountFilterTitle(View filterContainer,
+            ContactListFilter filter, boolean isLoading, boolean showTitleForAllAccounts,
+            boolean forPhone) {
+        final Context context = filterContainer.getContext();
+        final TextView headerTextView = (TextView)
+                filterContainer.findViewById(R.id.account_filter_header);
+
+        boolean textWasSet = false;
+        if (isLoading) {
+            headerTextView.setText(R.string.contact_list_loading);
+        } else if (filter != null) {
+            if (forPhone) {
+                if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+                    if (showTitleForAllAccounts) {
+                        headerTextView.setText(R.string.list_filter_phones);
+                        textWasSet = true;
+                    }
+                } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+                    headerTextView.setText(context.getString(
+                            R.string.listAllContactsInAccount, filter.accountName));
+                    textWasSet = true;
+                } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+                    headerTextView.setText(R.string.listCustomView);
+                    textWasSet = true;
+                } else {
+                    Log.w(TAG, "Filter type \"" + filter.filterType + "\" isn't expected.");
+                }
+            } else {
+                if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+                    if (showTitleForAllAccounts) {
+                        headerTextView.setText(R.string.list_filter_all_accounts);
+                        textWasSet = true;
+                    }
+                } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+                    headerTextView.setText(context.getString(
+                            R.string.listAllContactsInAccount, filter.accountName));
+                    textWasSet = true;
+                } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+                    headerTextView.setText(R.string.listCustomView);
+                    textWasSet = true;
+                } else if (filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+                    headerTextView.setText(R.string.listSingleContact);
+                    textWasSet = true;
+                } else {
+                    Log.w(TAG, "Filter type \"" + filter.filterType + "\" isn't expected.");
+                }
+            }
+        } else {
+            Log.w(TAG, "Filter is null.");
+        }
+        return textWasSet;
+    }
+
+    /**
+     * Launches account filter setting Activity using
+     * {@link Activity#startActivityForResult(Intent, int)}.
+     *
+     * @param activity
+     * @param requestCode requestCode for {@link Activity#startActivityForResult(Intent, int)}
+     */
+    public static void startAccountFilterActivityForResult(
+            Activity activity, int requestCode) {
+        final Intent intent = new Intent(activity, AccountFilterActivity.class);
+        activity.startActivityForResult(intent, requestCode);
+    }
+
+    /**
+     * Very similar to {@link #startAccountFilterActivityForResult(Activity, int)} but uses
+     * Fragment instead.
+     */
+    public static void startAccountFilterActivityForResult(
+            Fragment fragment, int requestCode) {
+        final Activity activity = fragment.getActivity();
+        if (activity != null) {
+            final Intent intent = new Intent(activity, AccountFilterActivity.class);
+            fragment.startActivityForResult(intent, requestCode);
+        } else {
+            Log.w(TAG, "getActivity() returned null. Ignored");
+        }
+    }
+
+    /**
+     * Useful method to handle onActivityResult() for
+     * {@link #startAccountFilterActivityForResult(Activity, int)} or
+     * {@link #startAccountFilterActivityForResult(Fragment, int)}.
+     *
+     * This will update filter via a given ContactListFilterController.
+     */
+    public static void handleAccountFilterResult(
+            ContactListFilterController filterController, int resultCode, Intent data) {
+        if (resultCode == Activity.RESULT_OK) {
+            final ContactListFilter filter = (ContactListFilter)
+                    data.getParcelableExtra(AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
+            if (filter == null) {
+                return;
+            }
+            if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+                filterController.selectCustomFilter();
+            } else {
+                filterController.setContactListFilter(filter, true);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/res/xml/contacts_fallback.xml b/tests/res/xml/contacts_fallback.xml
new file mode 100644
index 0000000..ae262eb
--- /dev/null
+++ b/tests/res/xml/contacts_fallback.xml
@@ -0,0 +1,96 @@
+<?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.
+ */
+-->
+
+<!--
+    contacts.xml to build "fallback account type" equivalent.
+    This is directly used in ExternalAccountTypeTest to test the parser.  There's no sync adapter
+    that actually defined with this definition.
+-->
+
+<ContactsAccountType
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <EditSchema
+        >
+        <DataKind kind="name"
+            maxOccurs="1"
+            supportsDisplayName="true"
+            supportsPrefix="true"
+            supportsMiddleName="true"
+            supportsSuffix="true"
+            supportsPhoneticFamilyName="true"
+            supportsPhoneticMiddleName="true"
+            supportsPhoneticGivenName="true"
+            >
+        </DataKind>
+        <DataKind kind="photo" maxOccurs="1" />
+        <DataKind kind="phone" >
+            <Type type="mobile" />
+            <Type type="home" />
+            <Type type="work" />
+            <Type type="fax_work" />
+            <Type type="fax_home" />
+            <Type type="pager" />
+            <Type type="other" />
+            <Type type="custom"/>
+            <Type type="callback" />
+            <Type type="car" />
+            <Type type="company_main" />
+            <Type type="isdn" />
+            <Type type="main" />
+            <Type type="other_fax" />
+            <Type type="radio" />
+            <Type type="telex" />
+            <Type type="tty_tdd" />
+            <Type type="work_mobile"/>
+            <Type type="work_pager" />
+            <Type type="assistant" />
+            <Type type="mms" />
+        </DataKind>
+        <DataKind kind="email" >
+            <Type type="home" />
+            <Type type="work" />
+            <Type type="other" />
+            <Type type="mobile" />
+            <Type type="custom" />
+        </DataKind>
+        <DataKind kind="nickname" maxOccurs="1" />
+        <DataKind kind="im" >
+            <Type type="aim" />
+            <Type type="msn" />
+            <Type type="yahoo" />
+            <Type type="skype" />
+            <Type type="qq" />
+            <Type type="google_talk" />
+            <Type type="icq" />
+            <Type type="jabber" />
+            <Type type="custom" />
+        </DataKind>
+        <DataKind kind="postal" needsStructured="false" >
+            <Type type="home" />
+            <Type type="work" />
+            <Type type="other" />
+            <Type type="custom" />
+        </DataKind>
+        <DataKind kind="organization" maxOccurs="1" />
+        <DataKind kind="website" />
+        <DataKind kind="sip_address" maxOccurs="1" />
+        <DataKind kind="note" maxOccurs="1" />
+    </EditSchema>
+</ContactsAccountType>
diff --git a/tests/res/xml/test_basic_contacts.xml b/tests/res/xml/test_basic_contacts.xml
index ad82706..0047204 100644
--- a/tests/res/xml/test_basic_contacts.xml
+++ b/tests/res/xml/test_basic_contacts.xml
@@ -17,10 +17,6 @@
  */
 -->
 
-<!--
-    contacts.xml to build "fallback account type" equivalent.
--->
-
 <ContactsAccountType
     xmlns:android="http://schemas.android.com/apk/res/android"
     >
@@ -98,8 +94,8 @@
         <DataKind kind="phone" >
             <!-- Note: Google type doesn't have obsolete ones -->
             <Type type="mobile" />
-            <Type type="work" />
             <Type type="home" />
+            <Type type="work" />
             <Type type="fax_work" />
             <Type type="fax_home" />
             <Type type="pager" />
@@ -139,9 +135,9 @@
         <!--
             Email
         -->
-        <!-- Fallback/ Google definition.  -->
+        <!-- Fallback/Google definition.  -->
         <DataKind kind="email" >
-            <!-- Note: Google type doesn't support some of these. -->
+            <!-- Note: Google type doesn't have obsolete ones -->
             <Type type="home" />
             <Type type="work" />
             <Type type="other" />
@@ -248,17 +244,13 @@
 
         <!--
             Event
-
-            The parser should be able to handle it, but not tested.
         -->
-        <!-- Google definition.
         <DataKind kind="event" dateWithTime="false">
             <Type type="birthday" maxOccurs="1" yearOptional="true" />
             <Type type="anniversary" />
             <Type type="other" />
             <Type type="custom" />
         </DataKind>
-        -->
 
         <!--
             Exchange definition.  dateWithTime is needed only for Exchange.
@@ -268,11 +260,9 @@
         -->
 
         <!--
-            Relationship.
-
-            The parser should be able to handle it, but not tested.
-
-        <DataKind kind="relation" >
+            Relationship
+        -->
+        <DataKind kind="relationship" >
             <Type type="assistant" />
             <Type type="brother" />
             <Type type="child" />
@@ -289,8 +279,5 @@
             <Type type="spouse" />
             <Type type="custom" />
         </DataKind>
-        -->
-
     </EditSchema>
-
 </ContactsAccountType>
diff --git a/tests/src/com/android/contacts/EntityModifierTests.java b/tests/src/com/android/contacts/EntityModifierTests.java
index 4db73b3..6872604 100644
--- a/tests/src/com/android/contacts/EntityModifierTests.java
+++ b/tests/src/com/android/contacts/EntityModifierTests.java
@@ -20,8 +20,6 @@
 import static android.content.ContentProviderOperation.TYPE_INSERT;
 import static android.content.ContentProviderOperation.TYPE_UPDATE;
 
-import com.google.android.collect.Lists;
-
 import com.android.contacts.model.AccountType;
 import com.android.contacts.model.AccountType.EditType;
 import com.android.contacts.model.AccountTypeManager;
@@ -35,10 +33,10 @@
 import com.android.contacts.tests.mocks.ContactsMockContext;
 import com.android.contacts.tests.mocks.MockAccountTypeManager;
 import com.android.contacts.tests.mocks.MockContentProvider;
+import com.google.android.collect.Lists;
 
 import android.content.ContentProviderOperation;
 import android.content.ContentValues;
-import android.content.Context;
 import android.content.Entity;
 import android.net.Uri;
 import android.os.Bundle;
@@ -87,56 +85,60 @@
     public static class MockContactsSource extends AccountType {
 
         MockContactsSource() {
-            this.accountType = TEST_ACCOUNT_TYPE;
+            try {
+                this.accountType = TEST_ACCOUNT_TYPE;
 
-            final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE,
-                    R.string.nameLabelsGroup, -1, true, -1);
-            nameKind.typeOverallMax = 1;
-            addKind(nameKind);
+                final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE,
+                        R.string.nameLabelsGroup, -1, true, -1);
+                nameKind.typeOverallMax = 1;
+                addKind(nameKind);
 
-            // Phone allows maximum 2 home, 1 work, and unlimited other, with
-            // constraint of 5 numbers maximum.
-            final DataKind phoneKind = new DataKind(
-                    Phone.CONTENT_ITEM_TYPE, -1, 10, true, -1);
+                // Phone allows maximum 2 home, 1 work, and unlimited other, with
+                // constraint of 5 numbers maximum.
+                final DataKind phoneKind = new DataKind(
+                        Phone.CONTENT_ITEM_TYPE, -1, 10, true, -1);
 
-            phoneKind.typeOverallMax = 5;
-            phoneKind.typeColumn = Phone.TYPE;
-            phoneKind.typeList = Lists.newArrayList();
-            phoneKind.typeList.add(new EditType(Phone.TYPE_HOME, -1).setSpecificMax(2));
-            phoneKind.typeList.add(new EditType(Phone.TYPE_WORK, -1).setSpecificMax(1));
-            phoneKind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, -1).setSecondary(true));
-            phoneKind.typeList.add(new EditType(Phone.TYPE_OTHER, -1));
+                phoneKind.typeOverallMax = 5;
+                phoneKind.typeColumn = Phone.TYPE;
+                phoneKind.typeList = Lists.newArrayList();
+                phoneKind.typeList.add(new EditType(Phone.TYPE_HOME, -1).setSpecificMax(2));
+                phoneKind.typeList.add(new EditType(Phone.TYPE_WORK, -1).setSpecificMax(1));
+                phoneKind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, -1).setSecondary(true));
+                phoneKind.typeList.add(new EditType(Phone.TYPE_OTHER, -1));
 
-            phoneKind.fieldList = Lists.newArrayList();
-            phoneKind.fieldList.add(new EditField(Phone.NUMBER, -1, -1));
-            phoneKind.fieldList.add(new EditField(Phone.LABEL, -1, -1));
+                phoneKind.fieldList = Lists.newArrayList();
+                phoneKind.fieldList.add(new EditField(Phone.NUMBER, -1, -1));
+                phoneKind.fieldList.add(new EditField(Phone.LABEL, -1, -1));
 
-            addKind(phoneKind);
+                addKind(phoneKind);
 
-            // Email is unlimited
-            final DataKind emailKind = new DataKind(
-                    Email.CONTENT_ITEM_TYPE, -1, 10, true, -1);
-            emailKind.typeOverallMax = -1;
-            emailKind.fieldList = Lists.newArrayList();
-            emailKind.fieldList.add(new EditField(Email.DATA, -1, -1));
-            addKind(emailKind);
+                // Email is unlimited
+                final DataKind emailKind = new DataKind(
+                        Email.CONTENT_ITEM_TYPE, -1, 10, true, -1);
+                emailKind.typeOverallMax = -1;
+                emailKind.fieldList = Lists.newArrayList();
+                emailKind.fieldList.add(new EditField(Email.DATA, -1, -1));
+                addKind(emailKind);
 
-            // IM is only one
-            final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, 10,
-                    true, -1);
-            imKind.typeOverallMax = 1;
-            imKind.fieldList = Lists.newArrayList();
-            imKind.fieldList.add(new EditField(Im.DATA, -1, -1));
-            addKind(imKind);
+                // IM is only one
+                final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, 10,
+                        true, -1);
+                imKind.typeOverallMax = 1;
+                imKind.fieldList = Lists.newArrayList();
+                imKind.fieldList.add(new EditField(Im.DATA, -1, -1));
+                addKind(imKind);
 
-            // Organization is only one
-            final DataKind orgKind = new DataKind(
-                    Organization.CONTENT_ITEM_TYPE, -1, 10, true, -1);
-            orgKind.typeOverallMax = 1;
-            orgKind.fieldList = Lists.newArrayList();
-            orgKind.fieldList.add(new EditField(Organization.COMPANY, -1, -1));
-            orgKind.fieldList.add(new EditField(Organization.TITLE, -1, -1));
-            addKind(orgKind);
+                // Organization is only one
+                final DataKind orgKind = new DataKind(
+                        Organization.CONTENT_ITEM_TYPE, -1, 10, true, -1);
+                orgKind.typeOverallMax = 1;
+                orgKind.fieldList = Lists.newArrayList();
+                orgKind.fieldList.add(new EditField(Organization.COMPANY, -1, -1));
+                orgKind.fieldList.add(new EditField(Organization.TITLE, -1, -1));
+                addKind(orgKind);
+            } catch (DefinitionException e) {
+                throw new RuntimeException(e);
+            }
         }
 
         @Override
diff --git a/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java b/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
index 15a1320..ba3d0eb 100644
--- a/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
+++ b/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
@@ -19,12 +19,15 @@
 import com.android.contacts.tests.R;
 
 import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.XmlResourceParser;
 import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Event;
 import android.provider.ContactsContract.CommonDataKinds.Im;
 import android.provider.ContactsContract.CommonDataKinds.Note;
 import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
@@ -32,6 +35,10 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import java.util.List;
+
+import libcore.util.Objects;
+
 /**
  * Test case for {@link ExternalAccountType}.
  *
@@ -40,7 +47,6 @@
  */
 @SmallTest
 public class ExternalAccountTypeTest extends AndroidTestCase {
-
     public void testResolveExternalResId() {
         final Context c = getContext();
         // In this test we use the test package itself as an external package.
@@ -63,12 +69,36 @@
                 "@string/test_string", packageName, ""));
     }
 
+    /**
+     * Initialize with an invalid package name and see if type type will *not* be initialized.
+     */
+    public void testNoPackage() {
+        final ExternalAccountType type = new ExternalAccountType(getContext(),
+                "!!!no such package name!!!", false);
+        assertFalse(type.isInitialized());
+    }
+
+    /**
+     * Initialize with the name of an existing package, which has no contacts.xml metadata.
+     */
+    public void testNoMetadata() {
+        // Use the main application package, which does exist, but has no contacts.xml in it.
+        String packageName = getContext().getPackageName();
+        final ExternalAccountType type = new ExternalAccountType(getContext(),
+                packageName, false);
+        assertTrue(type.isInitialized());
+    }
+
+    /**
+     * Initialize with the test package itself and see if EditSchema is correctly parsed.
+     */
     public void testEditSchema() {
         final ExternalAccountType type = new ExternalAccountType(getContext(),
                 getTestContext().getPackageName(), false);
 
         assertTrue(type.isInitialized());
 
+        // Let's just check if the DataKinds are registered.
         assertNotNull(type.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE));
         assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME));
         assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME));
@@ -80,8 +110,69 @@
         assertNotNull(type.getKindForMimetype(Note.CONTENT_ITEM_TYPE));
         assertNotNull(type.getKindForMimetype(Website.CONTENT_ITEM_TYPE));
         assertNotNull(type.getKindForMimetype(SipAddress.CONTENT_ITEM_TYPE));
-        assertNotNull(type.getKindForMimetype(GroupMembership.CONTENT_ITEM_TYPE));
+        assertNotNull(type.getKindForMimetype(Event.CONTENT_ITEM_TYPE));
+        assertNotNull(type.getKindForMimetype(Relation.CONTENT_ITEM_TYPE));
+    }
 
-        // TODO Write more extensive check -- compare to FallbackAccountType?
+    /**
+     * Initialize with "contacts_fallback.xml" and compare the DataKinds to those of
+     * {@link FallbackAccountType}.
+     */
+    public void testEditSchema_fallback() {
+        final ExternalAccountType type = new ExternalAccountType(getContext(),
+                getTestContext().getPackageName(), false,
+                getTestContext().getResources().getXml(R.xml.contacts_fallback)
+                );
+
+        assertTrue(type.isInitialized());
+
+        // Create a fallback type with the same resource package name, and compare all the data
+        // kinds to its.
+        final AccountType reference = FallbackAccountType.createForTest(
+                getContext(), type.resPackageName);
+
+        assertsDataKindEquals(reference.getSortedDataKinds(), type.getSortedDataKinds());
+    }
+
+    private static void assertsDataKindEquals(List<DataKind> expectedKinds,
+            List<DataKind> actualKinds) {
+        final int count = Math.max(actualKinds.size(), expectedKinds.size());
+        for (int i = 0; i < count; i++) {
+            String actual =  actualKinds.size() > i ? actualKinds.get(i).toString() : "(n/a)";
+            String expected =  expectedKinds.size() > i ? expectedKinds.get(i).toString() : "(n/a)";
+
+            // Because assertEquals()'s output is not very friendly when comparing two similar
+            // strings, we manually do the check.
+            if (!Objects.equal(actual, expected)) {
+                final int commonPrefixEnd = findCommonPrefixEnd(actual, expected);
+                fail("Kind #" + i
+                        + "\n[Actual]\n" + insertMarkerAt(actual, commonPrefixEnd)
+                        + "\n[Expected]\n" + insertMarkerAt(expected, commonPrefixEnd));
+            }
+        }
+    }
+
+    private static int findCommonPrefixEnd(String s1, String s2) {
+        int i = 0;
+        for (;;) {
+            final boolean s1End = (s1.length() <= i);
+            final boolean s2End = (s2.length() <= i);
+            if (s1End || s2End) {
+                return i;
+            }
+            if (s1.charAt(i) != s2.charAt(i)) {
+                return i;
+            }
+            i++;
+        }
+    }
+
+    private static String insertMarkerAt(String s, int position) {
+        final String MARKER = "***";
+        if (position > s.length()) {
+            return s + MARKER;
+        } else {
+            return new StringBuilder(s).insert(position, MARKER).toString();
+        }
     }
 }