am 208cec02: (-s ours) am 256847d2: DO NOT MERGE Reduce edge fade

* commit '208cec02eab6cf2cbe2fdab6e14bb21119883297':
  DO NOT MERGE Reduce edge fade
diff --git a/Android.mk b/Android.mk
index ff1b7e2..1c9dcc4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -5,7 +5,11 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := com.android.phone.common com.android.vcard android-common
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    com.android.phone.common \
+    com.android.vcard \
+    android-common \
+    guava
 
 LOCAL_PACKAGE_NAME := Contacts
 LOCAL_CERTIFICATE := shared
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 52562e0..5969097 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -46,7 +46,7 @@
     >
 
         <!-- A virtual 12 key dialer -->
-        <activity android:name="TwelveKeyDialer"
+        <activity android:name=".activities.DialpadActivity"
             android:launchMode="singleTop"
         >
             <intent-filter>
@@ -57,7 +57,7 @@
         </activity>
 
         <!-- A list of recent calls -->
-        <activity android:name="RecentCallsListActivity"
+        <activity android:name=".activities.CallLogActivity"
             android:label="@string/recentCallsIconLabel"
         >
             <intent-filter>
@@ -119,7 +119,7 @@
         </activity>
 
         <!-- Tab container for all tabs -->
-        <activity android:name="DialtactsActivity"
+        <activity android:name=".activities.DialtactsActivity"
             android:label="@string/launcherDialer"
             android:theme="@style/DialtactsTheme"
             android:launchMode="singleTask"
@@ -185,16 +185,6 @@
                 <category android:name="android.intent.category.LAUNCHER" />
                 <category android:name="android.intent.category.BROWSABLE" />
             </intent-filter>
-
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.dir/person" />
-                <data android:mimeType="vnd.android.cursor.dir/contact" />
-                <data android:mimeType="vnd.android.cursor.item/person" />
-                <data android:mimeType="vnd.android.cursor.item/contact" />
-                <data android:mimeType="vnd.android.cursor.item/raw_contact" />
-            </intent-filter>
         </activity>
 
         <!-- The actual list of contacts -->
@@ -291,6 +281,7 @@
                 <data android:mimeType="vnd.android.cursor.dir/phone" />
                 <data android:mimeType="vnd.android.cursor.dir/postal-address_v2" />
                 <data android:mimeType="vnd.android.cursor.dir/postal-address" />
+                <data android:mimeType="vnd.android.cursor.dir/email_v2" />
             </intent-filter>
 
             <intent-filter>
@@ -335,7 +326,7 @@
 
         <activity
             android:name=".activities.ShowOrCreateActivity"
-            android:theme="@style/FullyTranslucent">
+            android:theme="@android:style/Theme.Translucent.NoTitleBar">
 
             <intent-filter>
                 <action android:name="com.android.contacts.action.SHOW_OR_CREATE_CONTACT" />
@@ -345,16 +336,36 @@
             </intent-filter>
         </activity>
 
+
+        <!-- List of groups -->
+        <activity android:name=".activities.GroupBrowserActivity"
+            android:label="@string/contactsGroupsLabel"
+            android:theme="@style/ContactBrowserTheme"
+            android:launchMode="singleTop"
+            android:clearTaskOnLaunch="true">
+            <!-- TODO: Remove this temporary intent action name when the fragmentization
+                 work is done. -->
+            <intent-filter>
+                <action android:name="com.android.phone.action.GROUPS_LIST" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.TAB" />
+            </intent-filter>
+        </activity>
+
+        <!-- Views the details of a single group -->
+        <activity android:name=".activities.GroupDetailActivity"
+            android:label=""
+            android:theme="@style/GroupDetailTheme" />
+
         <!-- Used to show QuickContact window over a translucent activity, which is a
              temporary hack until we add better framework support. -->
         <activity
-            android:name=".quickcontact.QuickContactActivity"
-            android:theme="@style/FullyTranslucent.QuickContact"
+            android:name=".quickcontact.QuickContactWindow"
+            android:theme="@style/Theme.QuickContact"
             android:launchMode="singleTop"
             android:excludeFromRecents="true"
             android:taskAffinity="android.task.quickcontact"
-            android:windowSoftInputMode="stateUnchanged"
-            >
+            android:windowSoftInputMode="stateUnchanged">
 
             <intent-filter>
                 <action android:name="com.android.contacts.action.QUICK_CONTACT" />
@@ -442,6 +453,11 @@
             <intent-filter android:label="@string/viewContactDesription">
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.dir/person" />
+                <data android:mimeType="vnd.android.cursor.dir/contact" />
+                <data android:mimeType="vnd.android.cursor.item/person" />
+                <data android:mimeType="vnd.android.cursor.item/contact" />
+                <data android:mimeType="vnd.android.cursor.item/raw_contact" />
             </intent-filter>
         </activity>
 
@@ -550,7 +566,7 @@
 
         <!-- vCard related -->
         <activity android:name=".vcard.ImportVCardActivity"
-            android:configChanges="orientation"
+            android:configChanges="orientation|screenSize|keyboardHidden"
             android:theme="@style/BackgroundOnly">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
diff --git a/res/drawable-hdpi/divider_horizontal_light.png b/res/drawable-hdpi/divider_horizontal_light.png
new file mode 100644
index 0000000..20d7a01
--- /dev/null
+++ b/res/drawable-hdpi/divider_horizontal_light.png
Binary files differ
diff --git a/res/drawable-sw580dp-hdpi/panel_message.9.png b/res/drawable-hdpi/panel_message.9.png
similarity index 100%
rename from res/drawable-sw580dp-hdpi/panel_message.9.png
rename to res/drawable-hdpi/panel_message.9.png
Binary files differ
diff --git a/res/drawable-mdpi/divider_horizontal_light.png b/res/drawable-mdpi/divider_horizontal_light.png
new file mode 100644
index 0000000..20d7a01
--- /dev/null
+++ b/res/drawable-mdpi/divider_horizontal_light.png
Binary files differ
diff --git a/res/drawable-sw580dp-mdpi/panel_message.9.png b/res/drawable-mdpi/panel_message.9.png
similarity index 100%
rename from res/drawable-sw580dp-mdpi/panel_message.9.png
rename to res/drawable-mdpi/panel_message.9.png
Binary files differ
diff --git a/res/drawable-sw580dp-nodpi/divider_vertical_dark.9.png b/res/drawable-xlarge-nodpi/divider_vertical_dark.9.png
similarity index 100%
rename from res/drawable-sw580dp-nodpi/divider_vertical_dark.9.png
rename to res/drawable-xlarge-nodpi/divider_vertical_dark.9.png
Binary files differ
diff --git a/res/drawable/quickactions_arrow_left_holo_light.xml b/res/drawable/quickactions_arrow_left_holo_light.xml
new file mode 100644
index 0000000..c1e18bd
--- /dev/null
+++ b/res/drawable/quickactions_arrow_left_holo_light.xml
@@ -0,0 +1,21 @@
+<?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_first="true" android:drawable="@drawable/quickactions_arrowdown_left_holo_light" />
+    <item android:state_last="true" android:drawable="@drawable/quickactions_arrowup_left_holo_light" />
+    <!-- TODO: provide callout-less state -->
+    <item android:drawable="@drawable/quickactions_arrowup_left_holo_light" />
+</selector>
diff --git a/res/drawable/quickactions_arrow_middle_holo_light.xml b/res/drawable/quickactions_arrow_middle_holo_light.xml
new file mode 100644
index 0000000..f88b513
--- /dev/null
+++ b/res/drawable/quickactions_arrow_middle_holo_light.xml
@@ -0,0 +1,21 @@
+<?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_first="true" android:drawable="@drawable/quickactions_arrowdown_middle_holo_light" />
+    <item android:state_last="true" android:drawable="@drawable/quickactions_arrowup_middle_holo_light" />
+    <!-- TODO: provide callout-less state -->
+    <item android:drawable="@drawable/quickactions_arrowup_middle_holo_light" />
+</selector>
diff --git a/res/drawable/quickactions_arrow_right_holo_light.xml b/res/drawable/quickactions_arrow_right_holo_light.xml
new file mode 100644
index 0000000..3e309fe
--- /dev/null
+++ b/res/drawable/quickactions_arrow_right_holo_light.xml
@@ -0,0 +1,21 @@
+<?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_first="true" android:drawable="@drawable/quickactions_arrowdown_right_holo_light" />
+    <item android:state_last="true" android:drawable="@drawable/quickactions_arrowup_right_holo_light" />
+    <!-- TODO: provide callout-less state -->
+    <item android:drawable="@drawable/quickactions_arrowup_right_holo_light" />
+</selector>
diff --git a/res/layout-land/twelve_key_dialer.xml b/res/layout-land/dialpad_fragment.xml
similarity index 97%
rename from res/layout-land/twelve_key_dialer.xml
rename to res/layout-land/dialpad_fragment.xml
index 985d047..fe9fb28 100644
--- a/res/layout-land/twelve_key_dialer.xml
+++ b/res/layout-land/dialpad_fragment.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 The Android Open Source Project
+<!-- 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.
diff --git a/res/layout-sw580dp/contact_detail_header_view.xml b/res/layout-large/contact_detail_header_view.xml
similarity index 87%
rename from res/layout-sw580dp/contact_detail_header_view.xml
rename to res/layout-large/contact_detail_header_view.xml
index 52c21eb..19132dd 100644
--- a/res/layout-sw580dp/contact_detail_header_view.xml
+++ b/res/layout-large/contact_detail_header_view.xml
@@ -25,7 +25,7 @@
     <TextView
         android:id="@+id/attribution"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/detail_header_attribution_height"
+        android:layout_height="56dip"
         android:paddingRight="24dip"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="?android:attr/textColorTertiary"
@@ -39,7 +39,7 @@
 
         <ImageView
             android:id="@+id/photo"
-            android:layout_marginLeft="@dimen/detail_header_view_margin"
+            android:layout_marginLeft="16dip"
             android:layout_width="96dip"
             android:layout_height="96dip" />
 
@@ -60,7 +60,7 @@
                 android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 android:paddingTop="16dip"
-                android:layout_marginLeft="@dimen/detail_header_view_margin">
+                android:layout_marginLeft="16dip">
 
                 <!-- Star -->
                 <CheckBox
@@ -77,24 +77,31 @@
                 <LinearLayout
                     android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
-                    android:paddingLeft="@dimen/detail_header_view_margin"
+                    android:paddingLeft="16dip"
                     android:orientation="vertical">
 
                     <TextView
                         android:id="@+id/name"
-                        style="@style/ContactDetailHeaderTextView"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:singleLine="true"
+                        android:ellipsize="end"
                         android:textSize="@dimen/contact_name_text_size" />
 
                     <TextView
                         android:id="@+id/phonetic_name"
-                        style="@style/ContactDetailHeaderTextView"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
                         android:textAppearance="?android:attr/textAppearanceMedium"
+                        android:singleLine="true"
+                        android:ellipsize="end"
                         android:layout_marginTop="-2dip"
                         android:visibility="gone" />
 
                     <TextView
                         android:id="@+id/organization"
-                        style="@style/ContactDetailHeaderTextView"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
                         android:layout_marginTop="-2dip"
                         android:visibility="gone"
                         android:textAppearance="?android:attr/textAppearanceMedium"
diff --git a/res/layout-long-land/twelve_key_dialer.xml b/res/layout-long-land/dialpad_fragment.xml
similarity index 97%
rename from res/layout-long-land/twelve_key_dialer.xml
rename to res/layout-long-land/dialpad_fragment.xml
index 1cf9690..f287741 100644
--- a/res/layout-long-land/twelve_key_dialer.xml
+++ b/res/layout-long-land/dialpad_fragment.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- 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.
diff --git a/res/layout-long/twelve_key_dialer.xml b/res/layout-long/dialpad_fragment.xml
similarity index 88%
rename from res/layout-long/twelve_key_dialer.xml
rename to res/layout-long/dialpad_fragment.xml
index bd90df4..85250e2 100644
--- a/res/layout-long/twelve_key_dialer.xml
+++ b/res/layout-long/dialpad_fragment.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- 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.
@@ -14,11 +14,14 @@
      limitations under the License.
 -->
 
+<!-- TODO (stopship) We don't want to specify a background color here. For now we just
+keep it because otherwise the dialer needs some imagination to use (white on white) -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/top"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
+    android:background="@android:color/black"
 >
 
     <!-- Text field above the keypad where the digits are displayed.
diff --git a/res/layout-sw580dp-w1000dp/contact_detail_empty.xml b/res/layout-sw580dp-w1000dp/contact_detail_empty.xml
deleted file mode 100644
index 51ea04c..0000000
--- a/res/layout-sw580dp-w1000dp/contact_detail_empty.xml
+++ /dev/null
@@ -1,37 +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.
--->
-
-<!-- Placeholder for empty details for a contact -->
-
-<com.android.contacts.widget.InterpolatingLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-    <TextView android:id="@+id/emptyText"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/no_contact_details"
-        android:textSize="20sp"
-        android:textColor="?android:attr/textColorSecondary"
-        ex:layout_wideParentWidth="800dip"
-        ex:layout_wideMarginLeft="80dip"
-        ex:layout_narrowParentWidth="500dip"
-        ex:layout_narrowMarginLeft="44dip"
-        android:paddingTop="10dip"
-        android:lineSpacingMultiplier="0.92"
-    />
-</com.android.contacts.widget.InterpolatingLayout>
diff --git a/res/layout-sw580dp-w1000dp/contact_detail_list_item.xml b/res/layout-sw580dp-w1000dp/contact_detail_list_item.xml
deleted file mode 100644
index 529059a..0000000
--- a/res/layout-sw580dp-w1000dp/contact_detail_list_item.xml
+++ /dev/null
@@ -1,134 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2010, 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">
-
-    <!-- Longer separating line (between kinds) -->
-    <View
-        android:id="@+id/kind_divider"
-        android:layout_width="match_parent"
-        android:layout_height="1px"
-        android:background="@drawable/list_item_divider_holo" />
-
-    <!-- Shorter separating line if there was no kind-separator -->
-    <View
-        android:id="@+id/in_kind_divider"
-        android:layout_width="match_parent"
-        android:layout_height="1px"
-        android:layout_marginLeft="164dip"
-        android:background="@drawable/list_item_divider_holo" />
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center_vertical"
-        android:orientation="vertical"
-        android:minHeight="@dimen/detail_min_line_item_height">
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            android:layout_gravity="center_vertical">
-            <TextView
-                android:id="@+id/kind"
-                android:layout_width="164dip"
-                android:layout_height="@dimen/detail_min_line_item_height"
-                android:paddingLeft="19dip"
-                android:textAppearance="?android:attr/textAppearanceSmall"
-                android:textColor="?android:attr/textColorTertiary"
-                android:singleLine="true"
-                android:ellipsize="marquee"
-                android:gravity="center_vertical" />
-
-            <LinearLayout
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:layout_weight="1"
-                android:paddingLeft="12dip"
-                android:orientation="vertical">
-                <TextView
-                    android:id="@+id/data"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:textAppearance="?android:attr/textAppearanceMedium" />
-                <TextView
-                    android:id="@+id/footer"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:textAppearance="?android:attr/textAppearanceSmall"
-                    android:visibility="gone" />
-            </LinearLayout>
-            <TextView
-                android:id="@+id/type"
-                android:layout_width="wrap_content"
-                android:layout_height="@dimen/detail_min_line_item_height"
-                android:maxWidth="150dip"
-                android:gravity="right|center_vertical"
-                android:textColor="@color/detail_item_type_color"
-                android:textAppearance="?android:attr/textAppearanceSmall" />
-
-            <ImageView
-                android:id="@+id/presence_icon"
-                android:layout_width="32dip"
-                android:layout_height="@dimen/detail_min_line_item_height"
-                android:layout_marginLeft="5dip"
-                android:gravity="center"
-                android:scaleType="centerInside" />
-
-            <ImageView
-                android:id="@+id/action_icon"
-                android:layout_width="32dip"
-                android:layout_height="@dimen/detail_min_line_item_height"
-                android:layout_marginLeft="16dip"
-                android:layout_gravity="center_vertical"
-                android:scaleType="centerInside" />
-
-            <View
-                android:id="@+id/divider"
-                android:layout_width="1px"
-                android:layout_height="match_parent"
-                android:layout_marginTop="5dip"
-                android:layout_marginBottom="5dip"
-                android:layout_marginLeft="14dip"
-                android:layout_marginRight="14dip"
-                android:background="?android:attr/dividerVertical" />
-
-            <ImageView
-                android:id="@+id/secondary_action_button"
-                android:layout_width="32dip"
-                android:layout_height="match_parent"
-                android:layout_centerVertical="true"
-                android:gravity="center"
-                android:scaleType="center"
-                android:background="?android:attr/selectableItemBackground" />
-        </LinearLayout>
-    </LinearLayout>
-
-    <!-- Longer separating line (only for the last row) -->
-    <View
-        android:id="@+id/line_below_last"
-        android:layout_width="match_parent"
-        android:layout_height="1px"
-        android:background="@drawable/list_item_divider_holo" />
-</LinearLayout>
diff --git a/res/layout-sw580dp/item_kind_section.xml b/res/layout-sw580dp/item_kind_section.xml
deleted file mode 100644
index eaae563..0000000
--- a/res/layout-sw580dp/item_kind_section.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<!-- the body surrounding all editors for a specific kind -->
-
-<com.android.contacts.editor.KindSectionView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="horizontal">
-
-    <TextView
-        android:id="@+id/kind_title"
-        android:layout_width="@dimen/editor_title_label_width"
-        android:layout_height="@dimen/editor_min_line_item_height"
-        android:gravity="center_vertical"
-        android:textColor="#7F7F7F"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:singleLine="true"
-        android:ellipsize="marquee" />
-
-    <LinearLayout
-        android:id="@+id/kind_editors"
-        android:layout_width="0dip"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:layout_gravity="bottom"
-        android:orientation="vertical" />
-
-    <FrameLayout
-        android:id="@+id/kind_plus_container"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/editor_min_line_item_height">
-        <ImageButton
-            android:id="@+id/kind_plus"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:paddingLeft="@dimen/editor_round_button_padding_left"
-            android:paddingRight="@dimen/editor_round_button_padding_right"
-            android:paddingTop="@dimen/editor_round_button_padding_top"
-            android:paddingBottom="@dimen/editor_round_button_padding_bottom"
-            android:background="?android:attr/selectableItemBackground"
-            android:src="@drawable/ic_menu_add_field_holo_light"
-            android:contentDescription="@string/description_plus_button" />
-    </FrameLayout>
-</com.android.contacts.editor.KindSectionView>
diff --git a/res/layout-sw580dp/list_section.xml b/res/layout-sw580dp/list_section.xml
deleted file mode 100644
index c684a0f..0000000
--- a/res/layout-sw580dp/list_section.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
--->
-
-<!-- Layout used for list section separators. -->
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="37dip"
-    android:background="@drawable/section_header_holo"
-    >
-    <TextView
-        android:id="@+id/header_text"
-        android:layout_width="56dip"
-        android:layout_height="wrap_content"
-        android:layout_centerVertical="true"
-        android:layout_alignParentLeft="true"
-        android:textStyle="bold"
-        android:textColor="@color/section_header_text_color"
-        android:textSize="14sp"
-        android:gravity="center"
-    />
-</RelativeLayout>
diff --git a/res/layout-sw580dp/raw_contact_editor_header.xml b/res/layout-sw580dp/raw_contact_editor_header.xml
deleted file mode 100644
index a973464..0000000
--- a/res/layout-sw580dp/raw_contact_editor_header.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-
-<!-- Account info header -->
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="64dip"
-    android:layout_width="match_parent"
-    android:background="?android:attr/selectableItemBackground">
-
-    <ImageView
-        android:id="@+id/header_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="7dip"
-        android:layout_marginRight="7dip"
-        android:layout_centerVertical="true"
-        android:layout_alignParentRight="true"
-        android:layout_below="@id/header_color_bar" />
-
-    <TextView
-        android:id="@+id/header_account_type"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_toLeftOf="@+id/header_icon"
-        android:layout_alignTop="@id/header_icon"
-        android:layout_marginTop="-4dip"
-
-        android:textSize="24sp"
-        android:textColor="?android:attr/textColorPrimary"
-        android:singleLine="true" />
-
-    <TextView
-        android:id="@+id/header_account_name"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_toLeftOf="@+id/header_icon"
-        android:layout_alignBottom="@+id/header_icon"
-        android:layout_marginBottom="2dip"
-
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorPrimary"
-        android:singleLine="true" />
-
-    <View
-        android:id="@+id/divider"
-        android:layout_width="match_parent"
-        android:layout_height="1px"
-        android:layout_alignParentBottom="true"
-        android:background="?android:attr/listDivider"
-        android:visibility="gone" />
-
-</RelativeLayout>
diff --git a/res/layout-sw580dp-w1000dp/contact_detail_header_view.xml b/res/layout-xlarge-land/contact_detail_header_view.xml
similarity index 87%
rename from res/layout-sw580dp-w1000dp/contact_detail_header_view.xml
rename to res/layout-xlarge-land/contact_detail_header_view.xml
index 82432a0..fb57202 100644
--- a/res/layout-sw580dp-w1000dp/contact_detail_header_view.xml
+++ b/res/layout-xlarge-land/contact_detail_header_view.xml
@@ -25,7 +25,7 @@
     <TextView
         android:id="@+id/attribution"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/detail_header_attribution_height"
+        android:layout_height="56dip"
         android:paddingRight="16dip"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textColor="?android:attr/textColorTertiary"
@@ -60,7 +60,7 @@
                 android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 android:paddingTop="16dip"
-                android:layout_marginLeft="@dimen/detail_header_view_margin">
+                android:layout_marginLeft="16dip">
 
                 <!-- Star -->
                 <CheckBox
@@ -77,24 +77,33 @@
                 <LinearLayout
                     android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
-                    android:paddingLeft="@dimen/detail_header_view_margin"
+                    android:paddingLeft="16dip"
                     android:orientation="vertical">
 
                     <TextView
                         android:id="@+id/name"
-                        style="@style/ContactDetailHeaderTextView"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:singleLine="true"
+                        android:ellipsize="end"
                         android:textSize="@dimen/contact_name_text_size" />
 
                     <TextView
                         android:id="@+id/phonetic_name"
-                        style="@style/ContactDetailHeaderTextView"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
                         android:textAppearance="?android:attr/textAppearanceMedium"
+                        android:singleLine="true"
+                        android:ellipsize="end"
                         android:layout_marginTop="-2dip"
                         android:visibility="gone" />
 
                     <TextView
                         android:id="@+id/organization"
-                        style="@style/ContactDetailHeaderTextView"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:singleLine="true"
+                        android:ellipsize="end"
                         android:layout_marginTop="-2dip"
                         android:visibility="gone"
                         android:textAppearance="?android:attr/textAppearanceMedium"
diff --git a/res/layout-xlarge-land/contact_detail_list_item.xml b/res/layout-xlarge-land/contact_detail_list_item.xml
new file mode 100644
index 0000000..9ad0e1b
--- /dev/null
+++ b/res/layout-xlarge-land/contact_detail_list_item.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, 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">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:orientation="vertical"
+        android:minHeight="@dimen/detail_min_line_item_height">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_gravity="center_vertical">
+
+            <FrameLayout
+                android:layout_width="@dimen/detail_item_type_width"
+                android:layout_height="@dimen/detail_min_line_item_height"
+                android:paddingLeft="@dimen/detail_item_side_margin">
+
+                <TextView
+                    android:id="@+id/kind"
+                    style="@style/ContactDetailItemType" />
+
+                <TextView
+                    android:id="@+id/type"
+                    style="@style/ContactDetailItemType" />
+
+            </FrameLayout>
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="1"
+                android:paddingLeft="12dip"
+                android:orientation="vertical">
+
+                <TextView
+                    android:id="@+id/data"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textAppearance="?android:attr/textAppearanceMedium" />
+
+                <TextView
+                    android:id="@+id/footer"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:visibility="gone" />
+
+            </LinearLayout>
+
+            <ImageView
+                android:id="@+id/presence_icon"
+                android:layout_width="32dip"
+                android:layout_height="@dimen/detail_min_line_item_height"
+                android:layout_marginLeft="5dip"
+                android:gravity="center"
+                android:scaleType="centerInside" />
+
+            <View
+                android:id="@+id/divider"
+                android:layout_width="1px"
+                android:layout_height="match_parent"
+                android:layout_marginTop="5dip"
+                android:layout_marginBottom="5dip"
+                android:layout_marginLeft="14dip"
+                android:layout_marginRight="14dip"
+                android:background="?android:attr/dividerVertical" />
+
+            <FrameLayout
+                android:id="@+id/secondary_action_button_container"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginTop="10dip"
+                android:paddingLeft="@dimen/detail_item_icon_margin"
+                android:paddingRight="@dimen/detail_item_icon_margin"
+                android:duplicateParentState="false"
+                android:background="?android:attr/selectableItemBackground">
+
+                <ImageView
+                    android:id="@+id/secondary_action_button"
+                    android:layout_width="32dip"
+                    android:layout_height="match_parent"
+                    android:layout_centerVertical="true"
+                    android:gravity="center"
+                    android:scaleType="center"
+                    android:duplicateParentState="false" />
+
+             </FrameLayout>
+        </LinearLayout>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout-xlarge/account_selector_list_item.xml b/res/layout-xlarge/account_selector_list_item.xml
new file mode 100644
index 0000000..38acfc5
--- /dev/null
+++ b/res/layout-xlarge/account_selector_list_item.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+>
+    <ImageView android:id="@android:id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="6dip"
+        android:layout_centerVertical="true"
+    />
+
+    <TextView android:id="@android:id/text1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="6dip"
+        android:layout_marginTop="6dip"
+        android:layout_toRightOf="@android:id/icon"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+    />
+
+    <TextView android:id="@android:id/text2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/text1"
+        android:layout_alignLeft="@android:id/text1"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+    />
+</RelativeLayout>
diff --git a/res/layout-sw580dp/aggregation_suggestions.xml b/res/layout-xlarge/aggregation_suggestions.xml
similarity index 100%
rename from res/layout-sw580dp/aggregation_suggestions.xml
rename to res/layout-xlarge/aggregation_suggestions.xml
diff --git a/res/layout-sw580dp/aggregation_suggestions_item.xml b/res/layout-xlarge/aggregation_suggestions_item.xml
similarity index 95%
rename from res/layout-sw580dp/aggregation_suggestions_item.xml
rename to res/layout-xlarge/aggregation_suggestions_item.xml
index f12c608..5ea8347 100644
--- a/res/layout-sw580dp/aggregation_suggestions_item.xml
+++ b/res/layout-xlarge/aggregation_suggestions_item.xml
@@ -24,6 +24,8 @@
     android:orientation="horizontal"
     android:paddingLeft="5dip"
     android:paddingRight="15dip"
+    android:background="?android:attr/selectableItemBackground"
+    android:focusable="true"
 >
     <ImageView
         android:id="@+id/aggregation_suggestion_photo"
diff --git a/res/layout-sw580dp/contact_browser.xml b/res/layout-xlarge/contact_browser.xml
similarity index 100%
rename from res/layout-sw580dp/contact_browser.xml
rename to res/layout-xlarge/contact_browser.xml
diff --git a/res/layout-sw580dp/contact_detail_fragment.xml b/res/layout-xlarge/contact_detail_fragment.xml
similarity index 76%
rename from res/layout-sw580dp/contact_detail_fragment.xml
rename to res/layout-xlarge/contact_detail_fragment.xml
index 7778736..4d6a900 100644
--- a/res/layout-sw580dp/contact_detail_fragment.xml
+++ b/res/layout-xlarge/contact_detail_fragment.xml
@@ -23,31 +23,27 @@
     android:layout_height="match_parent"
     android:background="@drawable/panel_content">
 
-    <!-- Header View (including social status) -->
+    <!-- Placeholder for empty list -->
     <com.android.contacts.widget.InterpolatingLayout
+        android:id="@android:id/empty"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        >
-        <com.android.contacts.detail.ContactDetailHeaderView
-            android:id="@+id/contact_header_widget"
-            android:layout_width="match_parent"
+        android:visibility="gone">
+        <TextView android:id="@+id/emptyText"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginBottom="28dip"
+            android:text="@string/no_contact_details"
+            android:textSize="20sp"
+            android:textColor="?android:attr/textColorSecondary"
             ex:layout_wideParentWidth="800dip"
-            ex:layout_wideMarginLeft="80dip"
-            ex:layout_wideMarginRight="48dip"
-            ex:layout_narrowParentWidth="300dip"
-            ex:layout_narrowMarginLeft="5dip"
-            ex:layout_narrowMarginRight="16dip"
+            ex:layout_wideMarginLeft="64dip"
+            ex:layout_narrowParentWidth="500dip"
+            ex:layout_narrowMarginLeft="42dip"
+            android:paddingTop="10dip"
+            android:lineSpacingMultiplier="0.92"
         />
     </com.android.contacts.widget.InterpolatingLayout>
 
-    <!-- Placeholder for empty list -->
-    <include
-        android:id="@android:id/empty"
-        layout="@layout/contact_detail_empty"
-        android:visibility="gone" />
-
     <!-- Real list -->
     <com.android.contacts.widget.InterpolatingLayout
         android:layout_width="match_parent"
@@ -59,11 +55,11 @@
             ex:layout_wideParentWidth="800dip"
             ex:layout_wideMarginLeft="64dip"
             ex:layout_widePaddingRight="48dip"
-            ex:layout_narrowParentWidth="300dip"
-            ex:layout_narrowMarginLeft="5dip"
+            ex:layout_narrowParentWidth="500dip"
+            ex:layout_narrowMarginLeft="32dip"
             ex:layout_narrowPaddingRight="16dip"
             android:cacheColorHint="#00000000"
-            android:divider="@android:color/transparent"
+            android:divider="@null"
         />
     </com.android.contacts.widget.InterpolatingLayout>
 
diff --git a/res/layout-sw580dp/contact_editor_activity.xml b/res/layout-xlarge/contact_editor_activity.xml
similarity index 100%
rename from res/layout-sw580dp/contact_editor_activity.xml
rename to res/layout-xlarge/contact_editor_activity.xml
diff --git a/res/layout-sw580dp/contact_editor_fragment.xml b/res/layout-xlarge/contact_editor_fragment.xml
similarity index 100%
rename from res/layout-sw580dp/contact_editor_fragment.xml
rename to res/layout-xlarge/contact_editor_fragment.xml
diff --git a/res/layout-sw580dp/contact_picker_content.xml b/res/layout-xlarge/contact_picker_content.xml
similarity index 100%
rename from res/layout-sw580dp/contact_picker_content.xml
rename to res/layout-xlarge/contact_picker_content.xml
diff --git a/res/layout/contact_detail_empty.xml b/res/layout-xlarge/edit_spinner.xml
similarity index 68%
copy from res/layout/contact_detail_empty.xml
copy to res/layout-xlarge/edit_spinner.xml
index e1ba04a..f1909fe 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout-xlarge/edit_spinner.xml
@@ -14,16 +14,14 @@
      limitations under the License.
 -->
 
-<!-- Placeholder for empty details for a contact -->
+<!-- Spinner for a field in the contact editor. -->
 
-<TextView
+<Spinner
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:id="@+id/spinner"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:layout_marginLeft="@dimen/editor_field_left_padding"
+    android:textAppearance="?android:attr/textAppearanceLarge"
+    android:paddingLeft="5dip"
+    android:paddingTop="5dip"/>
\ No newline at end of file
diff --git a/res/layout-xlarge/event_field_editor_view.xml b/res/layout-xlarge/event_field_editor_view.xml
new file mode 100644
index 0000000..c9eef08
--- /dev/null
+++ b/res/layout-xlarge/event_field_editor_view.xml
@@ -0,0 +1,56 @@
+<?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.
+-->
+
+<!-- Editor for a single event entry in the contact editor -->
+
+<com.android.contacts.editor.EventFieldEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="bottom"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/date_view"
+            layout="@layout/edit_date_picker" />
+
+        <include
+            android:id="@+id/spinner"
+            layout="@layout/edit_spinner"
+            android:paddingTop="15dip"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.EventFieldEditorView>
diff --git a/res/layout-sw580dp/external_raw_contact_editor_view.xml b/res/layout-xlarge/external_raw_contact_editor_view.xml
similarity index 92%
rename from res/layout-sw580dp/external_raw_contact_editor_view.xml
rename to res/layout-xlarge/external_raw_contact_editor_view.xml
index b185c8a..0304c48 100644
--- a/res/layout-sw580dp/external_raw_contact_editor_view.xml
+++ b/res/layout-xlarge/external_raw_contact_editor_view.xml
@@ -43,7 +43,7 @@
                 android:layout_gravity="top|left"
                 ex:layout_wideParentWidth="960dip"
                 ex:layout_wideMarginLeft="96dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
+                ex:layout_narrowParentWidth="800dip"
                 ex:layout_narrowMarginLeft="15dip">
 
                 <include
@@ -58,7 +58,7 @@
                 android:layout_gravity="top"
                 ex:layout_wideParentWidth="960dip"
                 ex:layout_wideMarginRight="48dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
+                ex:layout_narrowParentWidth="800dip"
                 ex:layout_narrowMarginRight="15dip"
                 android:orientation="vertical">
 
@@ -71,7 +71,7 @@
                     android:id="@+id/read_only_name"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_marginLeft="@dimen/editor_name_text_field_left_margin"
+                    android:layout_marginLeft="64dip"
                     android:layout_marginRight="48dip"
                     android:layout_marginTop="6dip"
                     android:layout_marginBottom="4dip"
@@ -91,7 +91,7 @@
                 ex:layout_wideParentWidth="960dip"
                 ex:layout_wideMarginLeft="96dip"
                 ex:layout_wideMarginRight="96dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
+                ex:layout_narrowParentWidth="800dip"
                 ex:layout_narrowMarginLeft="15dip"
                 ex:layout_narrowMarginRight="15dip">
 
@@ -132,7 +132,7 @@
                 ex:layout_wideParentWidth="960dip"
                 ex:layout_wideMarginLeft="246dip"
                 ex:layout_wideMarginRight="156dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
+                ex:layout_narrowParentWidth="800dip"
                 ex:layout_narrowMarginLeft="165dip"
                 ex:layout_narrowMarginRight="121dip"
                 android:layout_width="match_parent"
diff --git a/res/layout-sw580dp/item_group_membership.xml b/res/layout-xlarge/item_group_membership.xml
similarity index 97%
rename from res/layout-sw580dp/item_group_membership.xml
rename to res/layout-xlarge/item_group_membership.xml
index 985b7ae..41d77b7 100644
--- a/res/layout-sw580dp/item_group_membership.xml
+++ b/res/layout-xlarge/item_group_membership.xml
@@ -24,7 +24,7 @@
 
     <TextView
         android:id="@+id/kind_title"
-        android:layout_width="@dimen/editor_title_label_width"
+        android:layout_width="150dip"
         android:layout_height="@dimen/editor_min_line_item_height"
         android:gravity="center_vertical"
         android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/res/layout-xlarge/item_kind_section.xml b/res/layout-xlarge/item_kind_section.xml
new file mode 100644
index 0000000..f0ae1dc
--- /dev/null
+++ b/res/layout-xlarge/item_kind_section.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!-- the body surrounding all editors for a specific kind -->
+
+<com.android.contacts.editor.KindSectionView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="@dimen/editor_field_bottom_padding"
+    android:orientation="vertical">
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/divider_horizontal_light" />
+
+    <LinearLayout
+        android:id="@+id/kind_editors"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" />
+
+    <include
+        android:id="@+id/add_field_footer"
+        layout="@layout/edit_add_field" />
+
+</com.android.contacts.editor.KindSectionView>
diff --git a/res/layout-xlarge/phonetic_name_editor_view.xml b/res/layout-xlarge/phonetic_name_editor_view.xml
new file mode 100644
index 0000000..c3fa6a3
--- /dev/null
+++ b/res/layout-xlarge/phonetic_name_editor_view.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+
+<com.android.contacts.editor.PhoneticNameEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical"
+    android:paddingRight="?android:attr/scrollbarSize"
+    android:layout_marginLeft="52dip"
+    android:layout_marginRight="48dip"
+    android:layout_marginTop="6dip"
+    android:layout_marginBottom="4dip">
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/expansion_view_container"
+            layout="@layout/edit_expansion_view"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.PhoneticNameEditorView>
diff --git a/res/layout-xlarge/raw_contact_editor_header.xml b/res/layout-xlarge/raw_contact_editor_header.xml
new file mode 100644
index 0000000..498998d
--- /dev/null
+++ b/res/layout-xlarge/raw_contact_editor_header.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+
+<!-- Account info header -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="64dip"
+    android:layout_width="match_parent">
+
+    <RelativeLayout
+        android:id="@+id/account"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="?android:attr/selectableItemBackground">
+
+        <ImageView
+            android:id="@+id/account_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="7dip"
+            android:layout_marginRight="7dip"
+            android:layout_centerVertical="true"
+            android:layout_alignParentRight="true"
+            android:layout_below="@id/header_color_bar" />
+
+        <TextView
+            android:id="@+id/account_type"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toLeftOf="@+id/account_icon"
+            android:layout_alignTop="@id/account_icon"
+            android:layout_marginTop="-4dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="?android:attr/textColorPrimary"
+            android:singleLine="true" />
+
+        <TextView
+            android:id="@+id/account_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toLeftOf="@+id/account_icon"
+            android:layout_alignBottom="@+id/account_icon"
+            android:layout_marginBottom="2dip"
+
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorPrimary"
+            android:singleLine="true" />
+
+        <include
+              android:id="@+id/divider"
+              android:layout_alignParentBottom="true"
+              layout="@layout/edit_divider" />
+    </RelativeLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp/raw_contact_editor_view.xml b/res/layout-xlarge/raw_contact_editor_view.xml
similarity index 73%
rename from res/layout-sw580dp/raw_contact_editor_view.xml
rename to res/layout-xlarge/raw_contact_editor_view.xml
index eef82ec..0e3d227 100644
--- a/res/layout-sw580dp/raw_contact_editor_view.xml
+++ b/res/layout-xlarge/raw_contact_editor_view.xml
@@ -21,9 +21,14 @@
     android:layout_height="wrap_content"
     android:orientation="vertical">
 
-    <include
-        layout="@layout/raw_contact_editor_header"
-        android:id="@+id/header" />
+    <FrameLayout
+        android:id="@+id/anchor_for_account_switcher"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <include
+            layout="@layout/raw_contact_editor_header"
+            android:id="@+id/header" />
+    </FrameLayout>
 
     <LinearLayout
         android:id="@+id/body"
@@ -43,8 +48,8 @@
                 android:layout_gravity="top|left"
                 ex:layout_wideParentWidth="960dip"
                 ex:layout_wideMarginLeft="96dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
-                ex:layout_narrowMarginLeft="20dip">
+                ex:layout_narrowParentWidth="800dip"
+                ex:layout_narrowMarginLeft="15dip">
 
                 <include
                     android:id="@+id/edit_photo"
@@ -58,10 +63,8 @@
                 android:layout_gravity="top"
                 ex:layout_wideParentWidth="960dip"
                 ex:layout_wideMarginRight="48dip"
-                ex:layout_wideMarginLeft="10dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
+                ex:layout_narrowParentWidth="800dip"
                 ex:layout_narrowMarginRight="15dip"
-                ex:layout_narrowMarginLeft="5dip"
                 android:orientation="vertical">
 
                 <View
@@ -69,30 +72,18 @@
                     android:layout_height="1px"
                     android:background="@color/contact_detail_header_divider_color" />
 
-                <com.android.contacts.editor.StructuredNameEditorView
+                <include
                     android:id="@+id/edit_name"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:paddingRight="?android:attr/scrollbarSize"
-                    android:layout_marginLeft="@dimen/editor_name_text_field_left_margin"
-                    android:layout_marginRight="48dip"
-                    android:layout_marginTop="6dip"
-                    android:layout_marginBottom="4dip" />
+                    layout="@layout/structured_name_editor_view" />
 
-                <com.android.contacts.editor.PhoneticNameEditorView
+                <include
                     android:id="@+id/edit_phonetic_name"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:paddingRight="?android:attr/scrollbarSize"
-                    android:layout_marginLeft="@dimen/editor_name_text_field_left_margin"
-                    android:layout_marginRight="48dip"
-                    android:layout_marginTop="6dip"
-                    android:layout_marginBottom="4dip" />
+                    layout="@layout/phonetic_name_editor_view" />
 
                 <FrameLayout
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_marginLeft="@dimen/editor_name_text_field_left_margin"
+                    android:layout_marginLeft="52dip"
                     android:layout_marginRight="48dip">
                     <ViewStub
                         android:id="@+id/aggregation_suggestion_stub"
@@ -115,16 +106,16 @@
                 ex:layout_wideParentWidth="960dip"
                 ex:layout_wideMarginLeft="96dip"
                 ex:layout_wideMarginRight="48dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
-                ex:layout_narrowMarginLeft="25dip"
-                ex:layout_narrowMarginRight="10dip">
+                ex:layout_narrowParentWidth="800dip"
+                ex:layout_narrowMarginLeft="15dip"
+                ex:layout_narrowMarginRight="15dip">
 
                 <LinearLayout
                     android:id="@+id/sect_fields"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:orientation="vertical"
-                    android:paddingRight="@dimen/editor_name_text_field_right_margin" />
+                    android:paddingRight="?android:attr/scrollbarSize" />
             </LinearLayout>
         </com.android.contacts.widget.InterpolatingLayout>
 
@@ -139,7 +130,7 @@
                 ex:layout_wideParentWidth="960dip"
                 ex:layout_wideMarginLeft="246dip"
                 ex:layout_wideMarginRight="156dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
+                ex:layout_narrowParentWidth="800dip"
                 ex:layout_narrowMarginLeft="165dip"
                 ex:layout_narrowMarginRight="121dip"
                 android:layout_width="match_parent"
diff --git a/res/layout-sw580dp/search_header.xml b/res/layout-xlarge/search_header.xml
similarity index 100%
rename from res/layout-sw580dp/search_header.xml
rename to res/layout-xlarge/search_header.xml
diff --git a/res/layout-xlarge/structured_name_editor_view.xml b/res/layout-xlarge/structured_name_editor_view.xml
new file mode 100644
index 0000000..37b5536
--- /dev/null
+++ b/res/layout-xlarge/structured_name_editor_view.xml
@@ -0,0 +1,58 @@
+<?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.
+-->
+
+<com.android.contacts.editor.StructuredNameEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical"
+    android:paddingRight="?android:attr/scrollbarSize"
+    android:layout_marginLeft="52dip"
+    android:layout_marginRight="48dip"
+    android:layout_marginTop="6dip"
+    android:layout_marginBottom="4dip">
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/expansion_view_container"
+            layout="@layout/edit_expansion_view"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.StructuredNameEditorView>
diff --git a/res/layout-xlarge/text_fields_editor_view.xml b/res/layout-xlarge/text_fields_editor_view.xml
new file mode 100644
index 0000000..8be354b
--- /dev/null
+++ b/res/layout-xlarge/text_fields_editor_view.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+
+<com.android.contacts.editor.TextFieldsEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="top"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/spinner"
+            layout="@layout/edit_spinner"
+            android:paddingTop="15dip"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/expansion_view_container"
+            layout="@layout/edit_expansion_view"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.TextFieldsEditorView>
diff --git a/res/layout-sw580dp/total_contacts.xml b/res/layout-xlarge/total_contacts.xml
similarity index 100%
rename from res/layout-sw580dp/total_contacts.xml
rename to res/layout-xlarge/total_contacts.xml
diff --git a/res/layout/account_selector_list_item.xml b/res/layout/account_selector_list_item.xml
index 38acfc5..6fe2a50 100644
--- a/res/layout/account_selector_list_item.xml
+++ b/res/layout/account_selector_list_item.xml
@@ -17,29 +17,23 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
->
-    <ImageView android:id="@android:id/icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="6dip"
-        android:layout_centerVertical="true"
-    />
+    android:paddingLeft="@dimen/account_selector_horizontal_margin"
+    android:paddingRight="@dimen/account_selector_horizontal_margin"
+    android:minHeight="@dimen/account_selector_min_item_height" >
 
     <TextView android:id="@android:id/text1"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="6dip"
-        android:layout_marginTop="6dip"
-        android:layout_toRightOf="@android:id/icon"
-        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:layout_alignParentLeft="true"
+        android:layout_toLeftOf="@android:id/icon"
+        android:layout_centerVertical="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
     />
 
-    <TextView android:id="@android:id/text2"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@android:id/text1"
-        android:layout_alignLeft="@android:id/text1"
-        android:textAppearance="?android:attr/textAppearanceSmall"
+    <ImageView android:id="@android:id/icon"
+        android:layout_width="@dimen/account_selector_icon_size"
+        android:layout_height="@dimen/account_selector_icon_size"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
     />
 </RelativeLayout>
diff --git a/res/layout/aggregation_suggestions_item.xml b/res/layout/aggregation_suggestions_item.xml
index f2477cb..9ed1bf3 100644
--- a/res/layout/aggregation_suggestions_item.xml
+++ b/res/layout/aggregation_suggestions_item.xml
@@ -24,6 +24,8 @@
     android:orientation="horizontal"
     android:paddingLeft="5dip"
     android:paddingRight="15dip"
+    android:background="?android:attr/selectableItemBackground"
+    android:focusable="true"
 >
 
     <ImageView
diff --git a/res/layout/recent_calls.xml b/res/layout/call_log_activity.xml
similarity index 63%
copy from res/layout/recent_calls.xml
copy to res/layout/call_log_activity.xml
index f054b70..b391795 100644
--- a/res/layout/recent_calls.xml
+++ b/res/layout/call_log_activity.xml
@@ -18,17 +18,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
 >
-    <ListView android:id="@android:id/list"
-        android:layout_width="match_parent" 
-        android:layout_height="match_parent"
-        android:scrollbarStyle="outsideOverlay"
-    />
-    
-    <TextView android:id="@android:id/empty"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:text="@string/recentCalls_empty"
-        android:gravity="center"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-    />
+    <fragment class="com.android.contacts.calllog.CallLogFragment"
+            android:id="@+id/call_log_fragment"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
 </FrameLayout>
diff --git a/res/layout/recent_calls.xml b/res/layout/call_log_fragment.xml
similarity index 91%
rename from res/layout/recent_calls.xml
rename to res/layout/call_log_fragment.xml
index f054b70..2a27fcd 100644
--- a/res/layout/recent_calls.xml
+++ b/res/layout/call_log_fragment.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
+<!-- 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.
@@ -19,11 +19,11 @@
     android:layout_height="match_parent"
 >
     <ListView android:id="@android:id/list"
-        android:layout_width="match_parent" 
+        android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:scrollbarStyle="outsideOverlay"
     />
-    
+
     <TextView android:id="@android:id/empty"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/res/layout/recent_calls_list_child_item.xml b/res/layout/call_log_list_child_item.xml
similarity index 95%
rename from res/layout/recent_calls_list_child_item.xml
rename to res/layout/call_log_list_child_item.xml
index 527e259..bb48fcf 100644
--- a/res/layout/recent_calls_list_child_item.xml
+++ b/res/layout/call_log_list_child_item.xml
@@ -33,6 +33,6 @@
         android:background="@drawable/call_background_secondary"
     />
 
-    <include layout="@layout/recent_calls_list_item_layout"/>
+    <include layout="@layout/call_log_list_item_layout"/>
 
 </RelativeLayout>
diff --git a/res/layout/recent_calls_list_group_item.xml b/res/layout/call_log_list_group_item.xml
similarity index 100%
rename from res/layout/recent_calls_list_group_item.xml
rename to res/layout/call_log_list_group_item.xml
diff --git a/res/layout/recent_calls_list_item.xml b/res/layout/call_log_list_item.xml
similarity index 95%
rename from res/layout/recent_calls_list_item.xml
rename to res/layout/call_log_list_item.xml
index 2c519d6..4ea0563 100644
--- a/res/layout/recent_calls_list_item.xml
+++ b/res/layout/call_log_list_item.xml
@@ -32,6 +32,6 @@
         android:background="@drawable/call_background"
     />
 
-    <include layout="@layout/recent_calls_list_item_layout"/>
+    <include layout="@layout/call_log_list_item_layout"/>
 
 </RelativeLayout>
diff --git a/res/layout/recent_calls_list_item_layout.xml b/res/layout/call_log_list_item_layout.xml
similarity index 100%
rename from res/layout/recent_calls_list_item_layout.xml
rename to res/layout/call_log_list_item_layout.xml
diff --git a/res/layout/contact_browser.xml b/res/layout/contact_browser.xml
index 0b4bb63..8affd46 100644
--- a/res/layout/contact_browser.xml
+++ b/res/layout/contact_browser.xml
@@ -18,4 +18,25 @@
     android:id="@+id/list_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
+    <FrameLayout
+        android:id="@+id/main_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <fragment
+            android:id="@+id/list_fragment"
+            class="com.android.contacts.list.DefaultContactBrowseListFragment"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+        />
+    </FrameLayout>
+    <FrameLayout
+        android:id="@+id/contacts_unavailable_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone">
+        <FrameLayout
+            android:id="@+id/contacts_unavailable_container"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent" />
+    </FrameLayout>
 </FrameLayout>
diff --git a/res/layout/contact_detail_fragment.xml b/res/layout/contact_detail_fragment.xml
index 90a075a..70a9a28 100644
--- a/res/layout/contact_detail_fragment.xml
+++ b/res/layout/contact_detail_fragment.xml
@@ -20,16 +20,12 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <com.android.contacts.detail.ContactDetailHeaderView
-        android:id="@+id/contact_header_widget"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
-        
     <ListView android:id="@android:id/list"
         android:layout_width="match_parent"
         android:layout_height="0px"
         android:layout_weight="1"
-        android:background="@drawable/title_bar_shadow"
+        android:background="@color/background_primary"
+        android:divider="@null"
     />
 
     <ScrollView android:id="@android:id/empty"
diff --git a/res/layout/contact_detail_header_view.xml b/res/layout/contact_detail_header_view.xml
index 3248920..328a5ff 100644
--- a/res/layout/contact_detail_header_view.xml
+++ b/res/layout/contact_detail_header_view.xml
@@ -14,46 +14,53 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/banner"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="horizontal"
-    android:background="@drawable/title_bar_medium"
-    android:paddingRight="5dip">
+    android:layout_height="150dip">
 
     <ImageView android:id="@+id/photo"
-        android:layout_gravity="center_vertical"
-        android:layout_marginLeft="-1dip"
-        android:layout_marginTop="4dip"
-        android:layout_marginBottom="4dip"
-        android:layout_marginRight="8dip"
-        android:layout_width="64dip"
-        android:layout_height="64dip"
-    />
+        android:scaleType="centerCrop"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true"
+        />
+
+    <!-- Transparent view to overlay on the contact's photo
+    (to allow white text to appear over a white photo). -->
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true"
+        android:background="#000000"
+        android:alpha=".25"
+        />
 
     <LinearLayout
-        android:layout_width="0dip"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:orientation="vertical"
-        android:layout_gravity="center_vertical" >
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentLeft="true"
+        android:layout_marginLeft="10dip"
+        android:orientation="vertical" >
 
         <TextView android:id="@+id/name"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_height="0dip"
+            android:layout_weight="1"
             android:singleLine="true"
             android:ellipsize="end"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textStyle="bold"
-            android:shadowColor="#BB000000"
-            android:shadowRadius="2.75"
+            android:gravity="bottom"
+            android:textSize="@dimen/detail_header_name_text_size"
+            android:textColor="@color/detail_header_view_text_color"
          />
 
         <TextView android:id="@+id/phonetic_name"
-            android:layout_width="match_parent"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/detail_header_view_text_color"
             android:singleLine="true"
             android:ellipsize="end"
             android:layout_marginTop="-2dip"
@@ -61,9 +68,10 @@
         />
 
         <TextView android:id="@+id/organization"
-            android:layout_width="match_parent"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/detail_header_view_text_color"
             android:singleLine="true"
             android:ellipsize="end"
             android:layout_marginTop="-2dip"
@@ -71,7 +79,7 @@
         />
 
         <TextView android:id="@+id/attribution"
-            android:layout_width="match_parent"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textAppearance="?android:attr/textAppearanceSmall"
             android:textColor="?android:attr/textColorSecondary"
@@ -82,9 +90,10 @@
         />
 
         <TextView android:id="@+id/status"
-            android:layout_width="match_parent"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="@color/detail_header_view_text_color"
             android:singleLine="true"
             android:ellipsize="end"
             android:layout_marginTop="-2dip"
@@ -92,9 +101,8 @@
         />
 
         <TextView android:id="@+id/status_date"
-            android:layout_width="match_parent"
-            android:layout_height="0dip"
-            android:layout_weight="1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
             android:textAppearance="?android:attr/textAppearanceSmall"
             android:textSize="12sp"
             android:layout_marginTop="-2dip"
@@ -106,9 +114,13 @@
         android:id="@+id/star"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginTop="10dip"
+        android:layout_marginRight="10dip"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentRight="true"
         android:layout_gravity="center_vertical"
         android:contentDescription="@string/description_star"
         android:visibility="invisible"
         style="?android:attr/starStyle"
     />
-</LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/contact_detail_header_view_list_item.xml
similarity index 68%
copy from res/layout/contact_detail_empty.xml
copy to res/layout/contact_detail_header_view_list_item.xml
index e1ba04a..cf149df 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/contact_detail_header_view_list_item.xml
@@ -13,17 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<!-- Placeholder for empty details for a contact -->
-
-<TextView
+<com.android.contacts.detail.ContactDetailHeaderView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/contact_header_widget"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:layout_height="wrap_content"/>
\ No newline at end of file
diff --git a/res/layout/contact_detail_list_item.xml b/res/layout/contact_detail_list_item.xml
index fb71d87..2e4a3e5 100644
--- a/res/layout/contact_detail_list_item.xml
+++ b/res/layout/contact_detail_list_item.xml
@@ -21,114 +21,71 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical">
+    android:paddingLeft="@dimen/detail_item_side_margin"
+    android:orientation="horizontal"
+    android:gravity="center_vertical">
 
-    <!-- Seperator that shows the kind -->
     <LinearLayout
-        android:id="@+id/kind_divider"
-        android:layout_width="match_parent"
+        android:layout_width="0dip"
         android:layout_height="wrap_content"
-        android:orientation="vertical">
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="1px"
-            android:background="@drawable/list_item_divider_holo" />
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:gravity="center_vertical">
+
         <TextView
             android:id="@+id/kind"
-            android:gravity="center_vertical"
-            android:layout_width="match_parent"
-            android:layout_height="31dip"
-            android:paddingLeft="8dip"
+            style="@style/ContactDetailItemType" />
+
+        <TextView
+            android:id="@+id/type"
+            style="@style/ContactDetailItemType" />
+
+        <TextView
+            android:id="@+id/data"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:paddingBottom="5dip"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
+
+        <TextView
+            android:id="@+id/footer"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
             android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorTertiary" />
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="1px"
-            android:background="@drawable/list_item_divider_holo" />
+            android:visibility="gone" />
     </LinearLayout>
 
-    <!-- Shorter seperating line if there was no kind-seperator -->
     <ImageView
-        android:id="@+id/in_kind_divider"
-        android:layout_marginLeft="31dip"
-        android:layout_width="match_parent"
-        android:layout_height="1px"
-        android:background="@drawable/list_item_divider_holo" />
-
-    <LinearLayout
-        android:layout_width="match_parent"
+        android:id="@+id/presence_icon"
+        android:layout_width="32dip"
         android:layout_height="wrap_content"
-        android:minHeight="@dimen/detail_min_line_item_height"
-        android:orientation="horizontal"
-        android:paddingLeft="15dip"
-        android:gravity="center_vertical">
-        <LinearLayout
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:layout_marginLeft="5dip"
-            android:orientation="vertical"
-            android:paddingTop="5dip"
-            android:paddingBottom="7dip"
-            android:gravity="center_vertical">
+        android:layout_marginLeft="5dip"
+        android:gravity="center"
+        android:scaleType="centerInside" />
 
-            <TextView
-                android:id="@+id/data"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:textAppearance="?android:attr/textAppearanceMedium" />
+    <View
+        android:id="@+id/divider"
+        android:layout_width="1px"
+        android:layout_height="match_parent"
+        android:layout_marginTop="15dip"
+        android:layout_marginBottom="10dip"
+        android:background="?android:attr/dividerVertical" />
 
-            <TextView
-                android:id="@+id/type"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textAppearance="?android:attr/textAppearanceSmall"
-                android:textColor="@color/detail_item_type_color" />
-
-            <TextView
-                android:id="@+id/footer"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:textAppearance="?android:attr/textAppearanceSmall"
-                android:visibility="gone" />
-
-        </LinearLayout>
-
-        <ImageView
-            android:id="@+id/presence_icon"
-            android:layout_width="32dip"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="5dip"
-            android:gravity="center"
-            android:scaleType="centerInside" />
-
-        <ImageView
-            android:id="@+id/action_icon"
-            android:layout_width="32dip"
-            android:layout_height="32dip"
-            android:layout_marginLeft="16dip"
-            android:gravity="center"
-            android:scaleType="centerInside" />
-
-        <View
-            android:id="@+id/divider"
-            android:layout_width="1px"
-            android:layout_height="match_parent"
-            android:layout_marginTop="5dip"
-            android:layout_marginBottom="5dip"
-            android:layout_marginLeft="14dip"
-            android:layout_marginRight="14dip"
-            android:background="?android:attr/dividerVertical" />
-
+    <FrameLayout
+        android:id="@+id/secondary_action_button_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginTop="5dip"
+        android:paddingLeft="@dimen/detail_item_icon_margin"
+        android:paddingRight="@dimen/detail_item_icon_margin"
+        android:duplicateParentState="false"
+        android:background="?android:attr/selectableItemBackground">
         <ImageView
             android:id="@+id/secondary_action_button"
             android:layout_width="32dip"
             android:layout_height="match_parent"
-            android:layout_centerVertical="true"
-            android:gravity="center"
-            android:scaleType="center"
-            android:background="?android:attr/selectableItemBackground" />
-    </LinearLayout>
+            android:duplicateParentState="false" />
+    </FrameLayout>
 </LinearLayout>
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/contact_detail_separator_list_item.xml
similarity index 66%
copy from res/layout/contact_detail_empty.xml
copy to res/layout/contact_detail_separator_list_item.xml
index e1ba04a..87f0597 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/contact_detail_separator_list_item.xml
@@ -14,16 +14,9 @@
      limitations under the License.
 -->
 
-<!-- Placeholder for empty details for a contact -->
-
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<View xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:layout_marginTop="8dip"
+    android:background="@drawable/divider_horizontal_light"
+    />
\ No newline at end of file
diff --git a/res/layout/contact_editor_activity.xml b/res/layout/contact_editor_activity.xml
index 630e82c..8c13629 100644
--- a/res/layout/contact_editor_activity.xml
+++ b/res/layout/contact_editor_activity.xml
@@ -22,21 +22,4 @@
             android:id="@+id/contact_editor_fragment"
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="50dip"
-        android:orientation="horizontal"
-        android:gravity="center_horizontal">
-
-        <Button
-            android:id="@+id/done"
-            android:layout_width="150dip"
-            android:layout_height="match_parent"
-            android:text="@string/menu_done" />
-        <Button
-            android:id="@+id/revert"
-            android:layout_width="150dip"
-            android:layout_height="match_parent"
-            android:text="@string/menu_doNotSave" />
-    </LinearLayout>
 </FrameLayout>
diff --git a/res/layout/contact_editor_fragment.xml b/res/layout/contact_editor_fragment.xml
index 602f7f9..f3989e1 100644
--- a/res/layout/contact_editor_fragment.xml
+++ b/res/layout/contact_editor_fragment.xml
@@ -17,6 +17,7 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:background="@color/background_primary"
 >
 
     <ScrollView
diff --git a/res/layout/dialer_activity.xml b/res/layout/dialer_activity.xml
deleted file mode 100644
index 14a6b39..0000000
--- a/res/layout/dialer_activity.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 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.
--->
-
-<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/tabhost"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <LinearLayout
-        android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-        <TabWidget android:id="@android:id/tabs"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-        />
-
-        <FrameLayout android:id="@android:id/tabcontent"
-            android:layout_width="match_parent"
-            android:layout_height="0dip"
-            android:layout_weight="1"
-        />
-    </LinearLayout>
-</TabHost>
-
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/dialpad_activity.xml
similarity index 63%
copy from res/layout/contact_detail_empty.xml
copy to res/layout/dialpad_activity.xml
index e1ba04a..93e9523 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/dialpad_activity.xml
@@ -14,16 +14,12 @@
      limitations under the License.
 -->
 
-<!-- Placeholder for empty details for a contact -->
-
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:layout_height="match_parent"
+>
+    <fragment class="com.android.contacts.dialpad.DialpadFragment"
+            android:id="@+id/dialpad_fragment"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+</FrameLayout>
diff --git a/res/layout/twelve_key_dialer.xml b/res/layout/dialpad_fragment.xml
similarity index 89%
rename from res/layout/twelve_key_dialer.xml
rename to res/layout/dialpad_fragment.xml
index d4c9d8f..c516bd8 100644
--- a/res/layout/twelve_key_dialer.xml
+++ b/res/layout/dialpad_fragment.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 The Android Open Source Project
+<!-- 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.
@@ -14,11 +14,14 @@
      limitations under the License.
 -->
 
+<!-- TODO (stopship) We don't want to specify a background color here. For now we just
+keep it because otherwise the dialer needs some imagination to use (white on white) -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/top"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
+    android:background="@android:color/black"
 >
 
     <!-- Text field above the keypad where the digits are displayed.
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
new file mode 100644
index 0000000..63ca3bd
--- /dev/null
+++ b/res/layout/dialtacts_activity.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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="match_parent"
+    android:layout_height="match_parent">
+
+    <!-- Dialer --> -->
+    <fragment
+        class="com.android.contacts.dialpad.DialpadFragment"
+        android:id="@+id/dialpad_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <!-- Call Log -->
+    <fragment
+        class="com.android.contacts.calllog.CallLogFragment"
+        android:id="@+id/call_log_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <!-- Contacts -->
+    <fragment
+        android:id="@+id/contacts_fragment"
+        class="com.android.contacts.list.DefaultContactBrowseListFragment"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent" />
+
+    <!-- Favorites -->
+    <fragment
+        android:id="@+id/favorites_fragment"
+        class="com.android.contacts.list.DefaultContactBrowseListFragment"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/edit_add_field.xml b/res/layout/edit_add_field.xml
new file mode 100644
index 0000000..1c8c740
--- /dev/null
+++ b/res/layout/edit_add_field.xml
@@ -0,0 +1,39 @@
+<?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 of "add field" row in contact editor -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingLeft="13dip"
+    android:background="?android:attr/selectableItemBackground">
+    <TextView
+        android:id="@+id/add_text"
+        android:layout_gravity="center_vertical"
+        android:layout_width="0dip"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorTertiary" />
+    <ImageView
+        android:id="@+id/add_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_menu_add_field_holo_light"
+        android:paddingLeft="@dimen/editor_round_button_padding_left"
+        android:paddingRight="@dimen/editor_round_button_padding_right"
+        android:contentDescription="@string/description_plus_button" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/edit_date_picker.xml
similarity index 69%
copy from res/layout/contact_detail_empty.xml
copy to res/layout/edit_date_picker.xml
index e1ba04a..ca5e281 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/edit_date_picker.xml
@@ -14,16 +14,14 @@
      limitations under the License.
 -->
 
-<!-- Placeholder for empty details for a contact -->
+<!-- Button to select a date in the contact editor. -->
 
-<TextView
+<Button
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:id="@+id/date_view"
+    android:layout_width="0dip"
     android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:layout_weight="1"
+    android:layout_marginLeft="@dimen/editor_field_left_padding"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    style="@android:style/Widget.Holo.Light.Spinner" />
\ No newline at end of file
diff --git a/res/layout/edit_delete_button.xml b/res/layout/edit_delete_button.xml
new file mode 100644
index 0000000..f05b0e4
--- /dev/null
+++ b/res/layout/edit_delete_button.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+
+<!-- "Delete field" button in the contact editor. -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="top">
+    <ImageView
+        android:id="@+id/delete_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:duplicateParentState="true"
+        android:background="?android:attr/selectableItemBackground"
+        android:src="@drawable/ic_menu_remove_field_holo_light"
+        android:paddingLeft="@dimen/editor_round_button_padding_left"
+        android:paddingRight="@dimen/editor_round_button_padding_right"
+        android:paddingTop="@dimen/editor_round_button_padding_top"
+        android:paddingBottom="@dimen/editor_round_button_padding_bottom"
+        android:contentDescription="@string/description_minus_button" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/edit_divider.xml b/res/layout/edit_divider.xml
index eb2a49a..786bfca 100644
--- a/res/layout/edit_divider.xml
+++ b/res/layout/edit_divider.xml
@@ -16,6 +16,6 @@
 
 <View xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="1dip"
     android:background="?android:attr/listDivider"
     />
diff --git a/res/layout/edit_expansion_view.xml b/res/layout/edit_expansion_view.xml
new file mode 100644
index 0000000..96fe8de
--- /dev/null
+++ b/res/layout/edit_expansion_view.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+
+<!-- "More" or "less" expansion button in the contact editor. -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="top">
+    <ImageView
+        android:id="@+id/expansion_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:duplicateParentState="true"
+        android:background="?android:attr/selectableItemBackground"
+        android:paddingLeft="@dimen/editor_round_button_padding_left"
+        android:paddingRight="@dimen/editor_round_button_padding_right"
+        android:paddingTop="@dimen/editor_round_button_padding_top"
+        android:paddingBottom="@dimen/editor_round_button_padding_bottom" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/edit_field_list.xml
similarity index 69%
rename from res/layout/contact_detail_empty.xml
rename to res/layout/edit_field_list.xml
index e1ba04a..ba715c7 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/edit_field_list.xml
@@ -14,16 +14,13 @@
      limitations under the License.
 -->
 
-<!-- Placeholder for empty details for a contact -->
+<!-- Layout to contain a list of fields in the contact editor. -->
 
-<TextView
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:id="@+id/editors"
+    android:layout_width="0dip"
+    android:layout_weight="1"
     android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:paddingLeft="@dimen/editor_field_left_padding"
+    android:orientation="vertical" />
\ No newline at end of file
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/edit_field_title.xml
similarity index 66%
copy from res/layout/contact_detail_empty.xml
copy to res/layout/edit_field_title.xml
index e1ba04a..4918c4d 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/edit_field_title.xml
@@ -14,16 +14,16 @@
      limitations under the License.
 -->
 
-<!-- Placeholder for empty details for a contact -->
+<!-- Title of a field in the contact editor. -->
 
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:id="@+id/title"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:paddingTop="@dimen/editor_field_top_padding"
+    android:paddingLeft="@dimen/editor_field_left_padding"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:singleLine="true"
+    android:ellipsize="marquee"
+    android:fadingEdge="horizontal" />
\ No newline at end of file
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/edit_spinner.xml
similarity index 69%
copy from res/layout/contact_detail_empty.xml
copy to res/layout/edit_spinner.xml
index e1ba04a..9e22ee7 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/edit_spinner.xml
@@ -14,16 +14,13 @@
      limitations under the License.
 -->
 
-<!-- Placeholder for empty details for a contact -->
+<!-- Spinner for a field in the contact editor. -->
 
-<TextView
+<Spinner
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:id="@+id/spinner"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:layout_marginLeft="@dimen/editor_field_left_padding"
+    android:textAppearance="?android:attr/textAppearanceLarge"
+    android:paddingLeft="5dip"/>
\ No newline at end of file
diff --git a/res/layout/event_field_editor_view.xml b/res/layout/event_field_editor_view.xml
new file mode 100644
index 0000000..6625279
--- /dev/null
+++ b/res/layout/event_field_editor_view.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+
+<!-- Editor for a single event entry in the contact editor -->
+
+<com.android.contacts.editor.EventFieldEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/date_view"
+            layout="@layout/edit_date_picker" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.EventFieldEditorView>
diff --git a/res/layout/external_raw_contact_editor_view.xml b/res/layout/external_raw_contact_editor_view.xml
index a964ce4..c524a74 100644
--- a/res/layout/external_raw_contact_editor_view.xml
+++ b/res/layout/external_raw_contact_editor_view.xml
@@ -51,7 +51,7 @@
                 android:background="@color/edit_divider"
             />
 
-            <ImageView android:id="@+id/header_icon"
+            <ImageView android:id="@+id/account_icon"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginLeft="7dip"
@@ -60,11 +60,11 @@
                 android:layout_below="@id/header_color_bar"
             />
 
-            <TextView android:id="@+id/header_account_type"
+            <TextView android:id="@+id/account_type"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_toRightOf="@+id/header_icon"
-                android:layout_alignTop="@id/header_icon"
+                android:layout_toRightOf="@+id/account_icon"
+                android:layout_alignTop="@id/account_icon"
                 android:layout_marginTop="-4dip"
 
                 android:textSize="24sp"
@@ -72,11 +72,11 @@
                 android:singleLine="true"
             />
 
-            <TextView android:id="@+id/header_account_name"
+            <TextView android:id="@+id/account_name"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_toRightOf="@+id/header_icon"
-                android:layout_alignBottom="@+id/header_icon"
+                android:layout_toRightOf="@+id/account_icon"
+                android:layout_alignBottom="@+id/account_icon"
                 android:layout_marginBottom="2dip"
 
                 android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/res/layout/group_browse_list_fragment.xml b/res/layout/group_browse_list_fragment.xml
new file mode 100644
index 0000000..50c02c8
--- /dev/null
+++ b/res/layout/group_browse_list_fragment.xml
@@ -0,0 +1,39 @@
+<?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:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <ListView
+      android:id="@+id/list"
+      android:layout_width="match_parent"
+      android:layout_height="0dip"
+      android:fastScrollEnabled="true"
+      android:scrollbarStyle="outsideOverlay"
+      android:layout_weight="1" />
+
+   <TextView
+     android:id="@+id/empty"
+     android:layout_width="match_parent"
+     android:layout_height="match_parent"
+     android:gravity="center"
+     android:text="@string/noGroups"
+     android:visibility="gone"/>
+
+</LinearLayout>
diff --git a/res/layout/group_browse_list_item.xml b/res/layout/group_browse_list_item.xml
new file mode 100644
index 0000000..accbedd
--- /dev/null
+++ b/res/layout/group_browse_list_item.xml
@@ -0,0 +1,60 @@
+<?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:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:scaleType="center"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="10dip"
+        android:layout_marginRight="10dip"
+        android:layout_gravity="center_vertical" />
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:paddingTop="5dip"
+        android:paddingBottom="5dip">
+
+        <TextView
+            android:id="@+id/label"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:gravity="center_vertical"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:ellipsize="end"
+            android:singleLine="true" />
+
+        <TextView
+            android:id="@+id/account"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:gravity="center_vertical"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textStyle="italic" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/group_browser_activity.xml
similarity index 63%
copy from res/layout/contact_detail_empty.xml
copy to res/layout/group_browser_activity.xml
index e1ba04a..f7187d9 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/group_browser_activity.xml
@@ -14,16 +14,15 @@
      limitations under the License.
 -->
 
-<!-- Placeholder for empty details for a contact -->
-
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:layout_height="match_parent">
+
+    <fragment
+        android:id="@+id/list_fragment"
+        class="com.android.contacts.group.GroupBrowseListFragment"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+    />
+
+</FrameLayout>
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/group_detail_activity.xml
similarity index 63%
copy from res/layout/contact_detail_empty.xml
copy to res/layout/group_detail_activity.xml
index e1ba04a..707a65c 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/group_detail_activity.xml
@@ -14,16 +14,14 @@
      limitations under the License.
 -->
 
-<!-- Placeholder for empty details for a contact -->
-
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:layout_height="match_parent">
+
+    <fragment
+        class="com.android.contacts.group.GroupDetailFragment"
+        android:id="@+id/group_detail_fragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</FrameLayout>
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/group_detail_fragment.xml
similarity index 63%
copy from res/layout/contact_detail_empty.xml
copy to res/layout/group_detail_fragment.xml
index e1ba04a..70d67b6 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/group_detail_fragment.xml
@@ -14,16 +14,16 @@
      limitations under the License.
 -->
 
-<!-- Placeholder for empty details for a contact -->
-
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<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="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:layout_height="match_parent">
+
+    <ListView
+        android:id="@+id/member_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:scrollbarStyle="outsideOverlay"/>
+
+</LinearLayout>
diff --git a/res/layout/item_group_membership.xml b/res/layout/item_group_membership.xml
index 65730d4..cdf4e8a 100644
--- a/res/layout/item_group_membership.xml
+++ b/res/layout/item_group_membership.xml
@@ -21,11 +21,6 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:orientation="vertical">
 
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="1px"
-        android:background="?android:attr/listDivider" />
-
     <LinearLayout
         android:id="@+id/kind_header"
         android:layout_width="match_parent"
diff --git a/res/layout/item_kind_section.xml b/res/layout/item_kind_section.xml
index fdb55c9..6c6f960 100644
--- a/res/layout/item_kind_section.xml
+++ b/res/layout/item_kind_section.xml
@@ -4,9 +4,9 @@
      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.
@@ -20,59 +20,22 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingBottom="@dimen/editor_field_bottom_padding"
     android:orientation="vertical">
 
     <View
         android:layout_width="match_parent"
-        android:layout_height="1px"
-        android:background="?android:attr/listDivider" />
-
-    <LinearLayout
-        android:id="@+id/kind_header"
-        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:layout_marginLeft="14dip"
-        android:layout_marginTop="2dip"
-        android:layout_marginBottom="2dip"
-        android:layout_marginRight="?android:attr/scrollbarSize"
-        android:orientation="horizontal"
-        android:gravity="center_vertical"
-        android:focusable="true"
-        android:clickable="true">
-
-        <TextView
-            android:id="@+id/kind_title"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textColor="@color/kind_title"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal" />
-
-        <ImageButton
-            android:id="@+id/kind_plus"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:duplicateParentState="true"
-            android:background="?android:attr/selectableItemBackground"
-            android:src="@drawable/ic_menu_add_field_holo_light"
-            android:paddingLeft="@dimen/editor_round_button_padding_left"
-            android:paddingRight="@dimen/editor_round_button_padding_right"
-            android:paddingTop="@dimen/editor_round_button_padding_top"
-            android:paddingBottom="@dimen/editor_round_button_padding_bottom"
-            android:contentDescription="@string/description_plus_button" />
-
-    </LinearLayout>
+        android:background="@drawable/divider_horizontal_light" />
 
     <LinearLayout
         android:id="@+id/kind_editors"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingBottom="6dip"
         android:orientation="vertical" />
 
-</com.android.contacts.editor.KindSectionView>
+    <include
+        android:id="@+id/add_field_footer"
+        layout="@layout/edit_add_field" />
+
+</com.android.contacts.editor.KindSectionView>
\ No newline at end of file
diff --git a/res/layout/item_photo_editor.xml b/res/layout/item_photo_editor.xml
index 642908e..3590963 100644
--- a/res/layout/item_photo_editor.xml
+++ b/res/layout/item_photo_editor.xml
@@ -27,7 +27,7 @@
         android:src="@drawable/ic_contact_picture"
         android:cropToPadding="true"
         android:scaleType="centerCrop"
-        android:gravity="center"
+        android:gravity="left"
     />
     <View
         android:id="@+id/frame"
diff --git a/res/layout/list_section.xml b/res/layout/list_section.xml
index e920673..5265f88 100644
--- a/res/layout/list_section.xml
+++ b/res/layout/list_section.xml
@@ -18,8 +18,7 @@
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="25dip"
-    android:background="@drawable/section_header"
+    android:layout_height="@dimen/list_section_height"
     >
     <TextView
         android:id="@+id/header_text"
diff --git a/res/layout/phonetic_name_editor_view.xml b/res/layout/phonetic_name_editor_view.xml
new file mode 100644
index 0000000..832ca2c
--- /dev/null
+++ b/res/layout/phonetic_name_editor_view.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+
+<com.android.contacts.editor.PhoneticNameEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="@dimen/editor_field_bottom_padding"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/expansion_view_container"
+            layout="@layout/edit_expansion_view"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.PhoneticNameEditorView>
diff --git a/res/layout/quickcontact.xml b/res/layout/quickcontact.xml
index a74424c..e2b291c 100644
--- a/res/layout/quickcontact.xml
+++ b/res/layout/quickcontact.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!-- Copyright (C) 2009 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,13 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<view
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    class="com.android.contacts.quickcontact.QuickContactRootLayout"
-    android:id="@+id/root"
-    android:layout_width="match_parent"
+    android:id="@android:id/content"
+    android:layout_width="@dimen/quick_contact_width"
     android:layout_height="wrap_content"
+    android:visibility="invisible"
     android:orientation="vertical">
 
     <FrameLayout
@@ -133,4 +133,4 @@
                 android:text="@string/quickcontact_clear_defaults_button" />
         </LinearLayout>
     </FrameLayout>
-</view>
+</LinearLayout>
diff --git a/res/layout/contact_detail_empty.xml b/res/layout/quickcontact_activity.xml
similarity index 68%
copy from res/layout/contact_detail_empty.xml
copy to res/layout/quickcontact_activity.xml
index e1ba04a..aced4a8 100644
--- a/res/layout/contact_detail_empty.xml
+++ b/res/layout/quickcontact_activity.xml
@@ -13,17 +13,16 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<!-- Placeholder for empty details for a contact -->
-
-<TextView
+<view
     xmlns:android="http://schemas.android.com/apk/res/android"
+    class="com.android.contacts.quickcontact.FloatingChildLayout"
+    android:id="@+id/floating_layout"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:text="@string/no_contact_details"
-    android:textSize="20sp"
-    android:textColor="?android:attr/textColorSecondary"
-    android:layout_marginLeft="15dip"
-    android:paddingTop="10dip"
-    android:lineSpacingMultiplier="0.92"
-/>
\ No newline at end of file
+    android:layout_height="match_parent"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
+    android:descendantFocusability="afterDescendants">
+
+    <include layout="@layout/quickcontact" />
+
+</view>
diff --git a/res/layout/quickcontact_default_item.xml b/res/layout/quickcontact_default_item.xml
index 25b6910..3a918f0 100755
--- a/res/layout/quickcontact_default_item.xml
+++ b/res/layout/quickcontact_default_item.xml
@@ -28,13 +28,13 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textStyle="bold"
-        android:textAppearance="?android:attr/textAppearanceMediumInverse" />
+        android:textAppearance="?android:attr/textAppearanceMedium" />
 
     <TextView
         android:id="@android:id/text2"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="-4dip"
-        android:textAppearance="?android:attr/textAppearanceSmallInverse" />
+        android:textAppearance="?android:attr/textAppearanceSmall" />
 
 </LinearLayout>
diff --git a/res/layout/quickcontact_resolve_item.xml b/res/layout/quickcontact_resolve_item.xml
index 55de80e..2805722 100755
--- a/res/layout/quickcontact_resolve_item.xml
+++ b/res/layout/quickcontact_resolve_item.xml
@@ -28,13 +28,13 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textStyle="bold"
-        android:textAppearance="?android:attr/textAppearanceMediumInverse" />
+        android:textAppearance="?android:attr/textAppearanceMedium" />
 
     <TextView
         android:id="@android:id/text2"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="-4dip"
-        android:textAppearance="?android:attr/textAppearanceSmallInverse" />
+        android:textAppearance="?android:attr/textAppearanceSmall" />
 
 </LinearLayout>
diff --git a/res/layout/raw_contact_editor_view.xml b/res/layout/raw_contact_editor_view.xml
index ac30a6b..5aa1e95 100644
--- a/res/layout/raw_contact_editor_view.xml
+++ b/res/layout/raw_contact_editor_view.xml
@@ -21,96 +21,80 @@
     android:orientation="vertical"
 >
 
-    <!-- Account info header -->
-    <RelativeLayout android:id="@+id/header"
-        android:layout_height="64dip"
-        android:layout_width="match_parent"
-        android:background="@android:drawable/list_selector_background"
-    >
-
-        <ImageView android:id="@+id/header_color_bar"
-            android:layout_width="match_parent"
-            android:layout_height="4dip"
-            android:layout_marginBottom="5dip"
-            android:background="@color/edit_divider"
-        />
-
-        <ImageView android:id="@+id/header_icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="7dip"
-            android:layout_marginRight="7dip"
-            android:layout_centerVertical="true"
-            android:layout_below="@id/header_color_bar"
-        />
-
-        <TextView android:id="@+id/header_account_type"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_toRightOf="@+id/header_icon"
-            android:layout_alignTop="@id/header_icon"
-            android:layout_marginTop="-4dip"
-
-            android:textSize="24sp"
-            android:textColor="?android:attr/textColorPrimary"
-            android:singleLine="true"
-        />
-
-        <TextView android:id="@+id/header_account_name"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_toRightOf="@+id/header_icon"
-            android:layout_alignBottom="@+id/header_icon"
-            android:layout_marginBottom="2dip"
-
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorPrimary"
-            android:singleLine="true"
-        />
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1px"
-            android:layout_alignParentBottom="true"
-
-            android:background="?android:attr/listDivider"
-        />
-    </RelativeLayout>
-
     <LinearLayout
         android:id="@+id/body"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical">
 
-        <FrameLayout
-            android:id="@+id/stub_photo"
-            android:layout_width="match_parent"
+        <LinearLayout
+            android:id="@+id/anchor_for_account_switcher"
             android:layout_height="wrap_content"
-            android:paddingLeft="12dip"
-            android:paddingTop="10dip">
+            android:layout_width="match_parent"
+            android:layout_marginRight="4dip"
+            android:background="@android:drawable/list_selector_background"
+            android:orientation="horizontal"
+            android:gravity="left|center_vertical">
 
-            <include
-                android:id="@+id/edit_photo"
-                layout="@layout/item_photo_editor" />
+            <FrameLayout
+                android:id="@+id/stub_photo"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content">
 
-        </FrameLayout>
+                <include
+                    android:id="@+id/edit_photo"
+                    layout="@layout/item_photo_editor" />
 
-        <com.android.contacts.editor.StructuredNameEditorView
+            </FrameLayout>
+
+            <LinearLayout
+                android:id="@+id/account"
+                android:layout_height="match_parent"
+                android:layout_width="match_parent"
+                android:paddingLeft="@dimen/account_selector_horizontal_margin"
+                android:paddingRight="@dimen/account_selector_horizontal_margin"
+                android:background="@color/account_selection_background"
+                android:orientation="vertical"
+                android:gravity="left|center_vertical">
+                <TextView
+                    android:id="@+id/account_type"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textSize="20sp"
+                    android:textColor="?android:attr/textColorPrimaryInverse"
+                    android:singleLine="true" />
+
+                <LinearLayout
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:orientation="horizontal"
+                    android:gravity="center_vertical">
+
+                    <TextView
+                         android:id="@+id/account_name"
+                         android:layout_width="wrap_content"
+                         android:layout_height="wrap_content"
+
+                         android:textAppearance="?android:attr/textAppearanceSmall"
+                         android:textColor="?android:attr/textColorPrimaryInverse"
+                         android:singleLine="true" />
+
+                    <ImageView
+                         android:id="@+id/account_icon"
+                         android:layout_width="@dimen/account_selector_icon_size"
+                         android:layout_height="@dimen/account_selector_icon_size"  />
+                </LinearLayout>
+            </LinearLayout>
+        </LinearLayout>
+
+
+        <include
             android:id="@+id/edit_name"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingRight="?android:attr/scrollbarSize"
-            android:layout_marginTop="6dip"
-            android:layout_marginBottom="4dip" />
+            layout="@layout/structured_name_editor_view" />
 
-        <com.android.contacts.editor.PhoneticNameEditorView
+        <include
             android:id="@+id/edit_phonetic_name"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingRight="?android:attr/scrollbarSize"
-            android:layout_marginTop="6dip"
-            android:layout_marginBottom="4dip" />
+            layout="@layout/phonetic_name_editor_view" />
 
         <ViewStub android:id="@+id/aggregation_suggestion_stub"
             android:inflatedId="@+id/aggregation_suggestion"
diff --git a/res/layout/structured_name_editor_view.xml b/res/layout/structured_name_editor_view.xml
new file mode 100644
index 0000000..83efa6f
--- /dev/null
+++ b/res/layout/structured_name_editor_view.xml
@@ -0,0 +1,56 @@
+<?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.
+-->
+
+<com.android.contacts.editor.StructuredNameEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/divider"
+        layout="@layout/edit_divider" />
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/expansion_view_container"
+            layout="@layout/edit_expansion_view"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.StructuredNameEditorView>
diff --git a/res/layout/text_fields_editor_view.xml b/res/layout/text_fields_editor_view.xml
new file mode 100644
index 0000000..d33e804
--- /dev/null
+++ b/res/layout/text_fields_editor_view.xml
@@ -0,0 +1,57 @@
+<?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.
+-->
+
+<com.android.contacts.editor.TextFieldsEditorView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/title"
+        layout="@layout/edit_field_title" />
+
+    <include
+        android:id="@+id/spinner"
+        layout="@layout/edit_spinner"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:focusable="true"
+        android:clickable="true">
+
+        <include
+            android:id="@+id/editors"
+            layout="@layout/edit_field_list" />
+
+        <include
+            android:id="@+id/expansion_view_container"
+            layout="@layout/edit_expansion_view"
+            android:visibility="gone" />
+
+        <include
+            android:id="@+id/delete_button_container"
+            layout="@layout/edit_delete_button"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.android.contacts.editor.TextFieldsEditorView>
diff --git a/res/menu-sw580dp-w720dp/actions.xml b/res/menu-sw580dp-w720dp/actions.xml
deleted file mode 100644
index 86b77b6..0000000
--- a/res/menu-sw580dp-w720dp/actions.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.
--->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:id="@+id/menu_add"
-        android:showAsAction="always" />
-
-    <item
-        android:id="@+id/menu_settings"
-        android:icon="@drawable/ic_menu_settings_holo_light"
-        android:title="@string/menu_settings" />
-
-    <item
-        android:id="@+id/menu_accounts"
-        android:icon="@drawable/ic_menu_accounts_holo_light"
-        android:title="@string/menu_accounts" />
-
-    <item
-        android:id="@+id/menu_import_export"
-        android:icon="@drawable/ic_menu_import_export_holo_light"
-        android:title="@string/menu_import_export" />
-
-    <item
-        android:id="@+id/menu_rename_group"
-        android:icon="@drawable/ic_menu_settings_holo_light"
-        android:title="@string/menu_renameGroup" />
-
-    <item
-        android:id="@+id/menu_delete_group"
-        android:icon="@drawable/ic_menu_trash_holo_light"
-        android:title="@string/menu_deleteGroup" />
-</menu>
diff --git a/res/menu-sw580dp-w720dp/view.xml b/res/menu-sw580dp-w720dp/view.xml
deleted file mode 100644
index 5be1b6b..0000000
--- a/res/menu-sw580dp-w720dp/view.xml
+++ /dev/null
@@ -1,40 +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.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:id="@+id/menu_edit"
-        android:icon="@drawable/ic_menu_compose_holo_light"
-        android:title="@string/menu_editContact"
-        android:alphabeticShortcut="e"
-        android:showAsAction="always" />
-
-    <item
-        android:id="@+id/menu_share"
-        android:icon="@drawable/ic_menu_share_holo_light"
-        android:title="@string/menu_share"
-        android:alphabeticShortcut="s" />
-
-    <item
-        android:id="@+id/menu_options"
-        android:icon="@drawable/ic_menu_mark"
-        android:title="@string/menu_contactOptions" />
-
-    <item
-        android:id="@+id/menu_delete"
-        android:icon="@drawable/ic_menu_trash_holo_light"
-        android:title="@string/menu_deleteContact" />
-</menu>
diff --git a/res/menu-sw580dp/actions.xml b/res/menu-sw580dp/actions.xml
deleted file mode 100644
index de5110f..0000000
--- a/res/menu-sw580dp/actions.xml
+++ /dev/null
@@ -1,46 +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.
--->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:id="@+id/menu_new_contact"
-        android:icon="@drawable/ic_menu_add_contact_holo_light"
-        android:title="@string/menu_new_contact_action_bar" />
-
-    <item
-        android:id="@+id/menu_settings"
-        android:icon="@drawable/ic_menu_settings_holo_light"
-        android:title="@string/menu_settings" />
-
-    <item
-        android:id="@+id/menu_accounts"
-        android:icon="@drawable/ic_menu_accounts_holo_light"
-        android:title="@string/menu_accounts" />
-
-    <item
-        android:id="@+id/menu_import_export"
-        android:icon="@drawable/ic_menu_import_export_holo_light"
-        android:title="@string/menu_import_export" />
-
-    <item
-        android:id="@+id/menu_rename_group"
-        android:icon="@drawable/ic_menu_settings_holo_light"
-        android:title="@string/menu_renameGroup" />
-
-    <item
-        android:id="@+id/menu_delete_group"
-        android:icon="@drawable/ic_menu_trash_holo_light"
-        android:title="@string/menu_deleteGroup" />
-</menu>
diff --git a/res/menu-sw580dp/view.xml b/res/menu-sw580dp/view.xml
deleted file mode 100644
index 7cf17d6..0000000
--- a/res/menu-sw580dp/view.xml
+++ /dev/null
@@ -1,39 +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.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:id="@+id/menu_edit"
-        android:icon="@drawable/ic_menu_compose_holo_light"
-        android:title="@string/menu_editContact"
-        android:alphabeticShortcut="e" />
-
-    <item
-        android:id="@+id/menu_share"
-        android:icon="@drawable/ic_menu_share_holo_light"
-        android:title="@string/menu_share"
-        android:alphabeticShortcut="s" />
-
-    <item
-        android:id="@+id/menu_options"
-        android:icon="@drawable/ic_menu_mark"
-        android:title="@string/menu_contactOptions" />
-
-    <item
-        android:id="@+id/menu_delete"
-        android:icon="@drawable/ic_menu_trash_holo_light"
-        android:title="@string/menu_deleteContact" />
-</menu>
diff --git a/res/menu/list.xml b/res/menu/list.xml
index 2b3f453..56d92f8 100644
--- a/res/menu/list.xml
+++ b/res/menu/list.xml
@@ -27,9 +27,9 @@
         android:alphabeticShortcut="n" />
 
     <item
-        android:id="@+id/menu_display_groups"
-        android:icon="@*android:drawable/ic_menu_allfriends"
-        android:title="@string/menu_displayGroup" />
+        android:id="@+id/menu_settings"
+        android:icon="@drawable/ic_menu_settings_holo_light"
+        android:title="@string/menu_settings" />
 
     <item
         android:id="@+id/menu_accounts"
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index dcca0f1..313bcfe 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"افتراضي"</string>
     <string name="removePhoto" msgid="4898105274130284565">"إزالة الصور"</string>
     <string name="noContacts" msgid="8579310973261953559">"لا توجد جهات اتصال."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"لم يتم العثور على أية جهات اتصال متطابقة."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"ليس هناك جهات اتصال تشتمل على أرقام هواتف."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"جهات الاتصال فقط التي بها صور"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"تم العثور على <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"جهات الاتصال"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"المفضلة"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"الهاتف"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"سجل المكالمات"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"سجل المكالمات فارغ."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"محو سجل المكالمات"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"هل أنت متأكد من أنك تريد محو سجل المكالمات؟"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"جارٍ محو سجل المكالمات"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"البريد الصوتي"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"اسم الأسرة الصوتي"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"الاسم"</string>
     <string name="account_type_format" msgid="718948015590343010">"جهة اتصال <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"من <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"استخدام هذه الصورة"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"معلومات جهة اتصال <xliff:g id="SOURCE">%1$s</xliff:g> غير قابلة للتعديل على هذا الجهاز."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"ليس هناك معلومات إضافية لجهة الاتصال هذه"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"لم يتم تحديد جهات اتصال."</string>
     <string name="add_field" msgid="2384260056674995230">"إضافة حقل آخر"</string>
+    <string name="add_phone" msgid="4421904942555210013">"إضافة رقم هاتف جديد"</string>
+    <string name="add_email" msgid="175079666329862215">"إضافة بريد إلكتروني جديد"</string>
+    <string name="add_im" msgid="5158094627521120439">"إضافة حساب مراسلة فورية جديد"</string>
+    <string name="add_address" msgid="418292312672970688">"إضافة عنوان جديد"</string>
+    <string name="add_note" msgid="2753771325725383279">"إضافة ملاحظة جديدة"</string>
+    <string name="add_website" msgid="4312391288948517344">"إضافة موقع إلكتروني جديد"</string>
+    <string name="add_event" msgid="7488781591843886426">"إضافة حدث جديد"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"إضافة علاقة جديدة"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"عبر <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> عبر <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"مفضل"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 6f62119..d18e39d 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"По подразбиране"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Премахване на снимката"</string>
     <string name="noContacts" msgid="8579310973261953559">"Няма контакти."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Не са намерени съответстващи контакти."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Няма контакти с тел. номера."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Само контакти с тел. номера"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> намерени"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Контакти"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Любими"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Телефон"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Списък на обажданията"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Списъкът на обажданията е празен."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Изчистване на списъка с обажданията"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Наистина ли искате да изчистите списъка на обажданията?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Списъкът с обажданията се изчиства"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Гласова поща"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Фамилия, както се произнася"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Име, както се произнася"</string>
     <string name="account_type_format" msgid="718948015590343010">"Контакт от <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"от <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Използване на тази снимка"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Информацията за контакта от <xliff:g id="SOURCE">%1$s</xliff:g> не може да бъде редактирана на това устройство."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Няма допълнителна информация за този контакт"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Няма избрани контакти."</string>
     <string name="add_field" msgid="2384260056674995230">"Добавяне на друго поле"</string>
+    <string name="add_phone" msgid="4421904942555210013">"+ Нов телефонен номер"</string>
+    <string name="add_email" msgid="175079666329862215">"+ Нов имейл"</string>
+    <string name="add_im" msgid="5158094627521120439">"+ Нов профил за чат"</string>
+    <string name="add_address" msgid="418292312672970688">"+ Нов адрес"</string>
+    <string name="add_note" msgid="2753771325725383279">"+ Нова бележка"</string>
+    <string name="add_website" msgid="4312391288948517344">"+ Нов уебсайт"</string>
+    <string name="add_event" msgid="7488781591843886426">"+ Ново събитие"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"+ Ново взаимоотношение"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"чрез <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> чрез <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"любимо"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 31de36e..5d78297 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Predeterminat"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Elimina la foto"</string>
     <string name="noContacts" msgid="8579310973261953559">"No hi ha contactes."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"No s\'ha trobat cap contacte coincident."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"No hi ha cap contacte amb número de telèfon."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Només els contactes amb telèfons"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> contactes"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Contactes"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Preferits"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telèfon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Registre de trucades"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"El registre de trucades és buit."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Esborrament del registre de trucades"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Esteu segur que voleu esborrar el registre de trucades?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Esborrant el registre de trucada"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Correu de veu"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Cognoms fonètics"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Nom fonètic"</string>
     <string name="account_type_format" msgid="718948015590343010">"Contacte de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Utilitza aquesta foto"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"La informació de contacte de <xliff:g id="SOURCE">%1$s</xliff:g> no es pot editar en aquest dispositiu."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"No hi ha informació addicional per a aquest contacte"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"No s\'ha seleccionat cap contacte."</string>
     <string name="add_field" msgid="2384260056674995230">"Afegeix un altre camp"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Afegeix núm. tel. nou"</string>
+    <string name="add_email" msgid="175079666329862215">"Afegeix correu el. nou"</string>
+    <string name="add_im" msgid="5158094627521120439">"Afegeix compte MI nou"</string>
+    <string name="add_address" msgid="418292312672970688">"Afegeix adreça nova"</string>
+    <string name="add_note" msgid="2753771325725383279">"Afegeix nota nova"</string>
+    <string name="add_website" msgid="4312391288948517344">"Afegeix lloc web nou"</string>
+    <string name="add_event" msgid="7488781591843886426">"Afegeix esdevenim. nou"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Afegeix relació nova"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"mitjançant <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> mitjançant <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"preferit"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index b4ec252..a346747 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Výchozí nastavení"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Odebrat fotografii"</string>
     <string name="noContacts" msgid="8579310973261953559">"Žádné kontakty."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Nebyly nalezeny žádné odpovídající kontakty."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Ke kontaktům nejsou přiřazena žádná telefonní čísla."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Pouze kontakty s telefony"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Počet nalezených položek: <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kontakty"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Oblíbené"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Hovory"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Záznam hovorů je prázdný."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Vymazat hovory"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Opravdu chcete vymazat hovory?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Vymazání hovorů"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Hlasová schránka"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Příjmení (foneticky)"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Jméno (foneticky)"</string>
     <string name="account_type_format" msgid="718948015590343010">"Kontakt ze zdroje <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"z účtu <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Použít tuto fotografii"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Kontaktní informace ze zdroje <xliff:g id="SOURCE">%1$s</xliff:g> není možné na tomto zařízení upravit."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"U tohoto kontaktu nejsou uvedeny dodatečné informace"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nevybrali jste žádné kontakty."</string>
     <string name="add_field" msgid="2384260056674995230">"Přidat další pole"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Přidat nové tel. číslo"</string>
+    <string name="add_email" msgid="175079666329862215">"Přidat nový e-mail"</string>
+    <string name="add_im" msgid="5158094627521120439">"Přidat nový účet IM"</string>
+    <string name="add_address" msgid="418292312672970688">"Přidat novou adresu"</string>
+    <string name="add_note" msgid="2753771325725383279">"Přidat novou poznámku"</string>
+    <string name="add_website" msgid="4312391288948517344">"Přidat nový web"</string>
+    <string name="add_event" msgid="7488781591843886426">"Přidat novou událost"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Přidat nový vztah"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"pomocí služby <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> prostřednictvím služby <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"oblíbené"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 92d2477..0c17939 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Standard"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Fjern billede"</string>
     <string name="noContacts" msgid="8579310973261953559">"Der er ingen kontakter."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Der blev ikke fundet nogen matchende kontakter."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Der er ingen kontakter med telefonnumre."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Kun kontakter med telefoner"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> fundet"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kontakter"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favorit"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Opk.liste"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Opkaldslisten er tom."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Ryd opkaldsliste"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Er du sikker på, at du vil rydde opkaldslisten?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Rydder opkaldsliste"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI-nummer"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Fonetisk efternavn"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Fonetisk navn"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> kontaktperson"</string>
-    <string name="from_account_format" msgid="687567483928582084">"fra<xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Brug dette billede"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g> kontaktoplysninger kan ikke redigeres på denne enhed."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Ingen yderligere oplysninger for denne kontakt"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Ingen kontaktpersoner er valgt."</string>
     <string name="add_field" msgid="2384260056674995230">"Tilføj et felt mere"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Tilføj nyt telefonnr."</string>
+    <string name="add_email" msgid="175079666329862215">"Tilføj ny e-mail"</string>
+    <string name="add_im" msgid="5158094627521120439">"Tilføj ny IM-konto"</string>
+    <string name="add_address" msgid="418292312672970688">"Tilføj ny adresse"</string>
+    <string name="add_note" msgid="2753771325725383279">"Tilføj ny bemærkning"</string>
+    <string name="add_website" msgid="4312391288948517344">"Tilføj nyt websted"</string>
+    <string name="add_event" msgid="7488781591843886426">"Tilføj ny begivenhed"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Tilføj nyt forhold"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"foretrukken"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 8fad512..e1382f8 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Standard"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Foto entfernen"</string>
     <string name="noContacts" msgid="8579310973261953559">"Keine Kontakte"</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Keine passenden Kontakte gefunden"</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Keine sichtbaren Kontakte mit Telefonnummern"</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Nur Kontakte mit Telefon"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> gefunden"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kontakte"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoriten"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Anrufe"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Anrufliste ist leer"</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Anrufprotokoll löschen"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Möchten Sie das Anrufprotokoll wirklich löschen?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Anrufprotokoll wird gelöscht."</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Mailbox"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Phonetischer Nachname"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Phonetischer Name"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g>-Kontakt"</string>
-    <string name="from_account_format" msgid="687567483928582084">"von <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Dieses Foto verwenden"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g>-Kontaktinformationen können auf diesem Gerät nicht bearbeitet werden."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Keine Zusatzinformationen zu diesem Kontakt"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Kein Kontakt ausgewählt"</string>
     <string name="add_field" msgid="2384260056674995230">"Weiteres Feld hinzufügen"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Neue Telefonnummer"</string>
+    <string name="add_email" msgid="175079666329862215">"Neue E-Mail-Adresse"</string>
+    <string name="add_im" msgid="5158094627521120439">"Chat-Konto hinzufügen"</string>
+    <string name="add_address" msgid="418292312672970688">"Adresse hinzufügen"</string>
+    <string name="add_note" msgid="2753771325725383279">"Neue Notiz hinzufügen"</string>
+    <string name="add_website" msgid="4312391288948517344">"Website hinzufügen"</string>
+    <string name="add_event" msgid="7488781591843886426">"Ereignis hinzufügen"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Beziehung hinzufügen"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"über <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> über <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"Favorit"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index ce14e4d..06c45a2 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Προεπιλογή"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Κατάργηση εικόνας"</string>
     <string name="noContacts" msgid="8579310973261953559">"Δεν υπάρχουν επαφές."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Δεν βρέθηκαν επαφές που να αντιστοιχούν."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Δεν υπάρχουν επαφές με αριθμούς τηλεφώνου."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Μόνο επαφές με τηλέφωνα"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Βρέθηκαν <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Επαφές"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Αγαπ."</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Τηλέφωνο"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Αρχείο"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Το αρχείο καταγραφής κλήσεων είναι κενό."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Εκκαθάριση αρχείου καταγραφής κλήσεων"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Είστε βέβαιοι ότι θέλετε να γίνει εκκαθάριση του αρχείου καταγραφής κλήσεων;"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Εκκαθάριση αρχείου καταγρ. κλήσεων"</string>
     <string name="imei" msgid="3045126336951684285">"Αριθμός ΙΜΕΙ"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Αυτόματος τηλεφωνητής"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Φωνητική γραφή επιθέτου"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Όνομα σε φωνητική γραφή"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> επαφή"</string>
-    <string name="from_account_format" msgid="687567483928582084">"από <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Χρήση αυτής της φωτογραφίας"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Τα στοιχεία επικοινωνίας της επαφής <xliff:g id="SOURCE">%1$s</xliff:g> δεν μπορούν να υποβληθούν σε επεξεργασία σε αυτήν τη συσκευή."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Δεν υπάρχουν πρόσθετες πληροφορίες για αυτήν την επαφή"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Δεν έχουν επιλεγεί επαφές."</string>
     <string name="add_field" msgid="2384260056674995230">"Προσθήκη άλλου πεδίου"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Προσθ. νέου αριθ. τηλ."</string>
+    <string name="add_email" msgid="175079666329862215">"Νέα διεύθ. ηλ. ταχ."</string>
+    <string name="add_im" msgid="5158094627521120439">"Προσθήκη νέου λογ. IM"</string>
+    <string name="add_address" msgid="418292312672970688">"Προσθήκη νέας διεύθ."</string>
+    <string name="add_note" msgid="2753771325725383279">"Προσθ. νέας σημείωσης"</string>
+    <string name="add_website" msgid="4312391288948517344">"Προσθ. νέου ιστότοπου"</string>
+    <string name="add_event" msgid="7488781591843886426">"Προσθ. νέου συμβάντος"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Προσθήκη νέας σχέσης"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"μέσω <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> μέσω <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"αγαπημένο"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 551be6d..ed3d6e5 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -95,6 +95,7 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Default"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Remove photo"</string>
     <string name="noContacts" msgid="8579310973261953559">"No contacts."</string>
+    <string name="noGroups" msgid="8614664663561385253">"No groups."</string>
     <string name="noMatchingContacts" msgid="4266283206853990471">"No matching contacts found."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"No contacts with phone numbers."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Only contacts with phones"</string>
@@ -134,6 +135,7 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> found"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Contacts"</string>
+    <string name="contactsGroupsLabel" msgid="2841971472518003524">"Groups"</string>
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favourites"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Phone"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Call log"</string>
@@ -149,6 +151,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Call log is empty."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Clear call log"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Are you sure that you want to clear the call log?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Clearing call log"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
@@ -399,7 +402,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Phonetic family name"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Phonetic name"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> contact"</string>
-    <string name="from_account_format" msgid="687567483928582084">"from <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Use this photo"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g> contact information cannot be edited on this device."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"No additional information for this contact"</string>
@@ -431,6 +435,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"No contacts selected."</string>
     <string name="add_field" msgid="2384260056674995230">"Add another field"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Add new phone number"</string>
+    <string name="add_email" msgid="175079666329862215">"Add new email"</string>
+    <string name="add_im" msgid="5158094627521120439">"Add new IM account"</string>
+    <string name="add_address" msgid="418292312672970688">"Add new address"</string>
+    <string name="add_note" msgid="2753771325725383279">"Add new note"</string>
+    <string name="add_website" msgid="4312391288948517344">"Add new website"</string>
+    <string name="add_event" msgid="7488781591843886426">"Add new event"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Add new relationship"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favourite"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 1dfa06c..d42a49f 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Predeterminado"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Eliminar foto"</string>
     <string name="noContacts" msgid="8579310973261953559">"No hay contactos."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"No se encontraron contactos coincidentes."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"No hay contactos con números de teléfono."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Sólo contactos con teléfonos"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g>Se encontró un"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Contactos"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoritos"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Teléfono"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Llamadas"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"El registro de llamadas está vacío."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Borrar registro de llamadas"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"¿Estás seguro de que deseas borrar el registro de llamadas?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Borrando el registro de llamadas"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Correo de voz"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Apellido fonético"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Nombre fonético"</string>
     <string name="account_type_format" msgid="718948015590343010">"Contacto de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Usar esta foto"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"La información de <xliff:g id="SOURCE">%1$s</xliff:g> contactos no se puede editar en este dispositivo."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"No hay información adicional para este contacto."</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"No hay contactos seleccionados."</string>
     <string name="add_field" msgid="2384260056674995230">"Agregar otro campo"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Agregar nuevo número de teléfono"</string>
+    <string name="add_email" msgid="175079666329862215">"Agregar nuevo correo"</string>
+    <string name="add_im" msgid="5158094627521120439">"Agregar nueva cuenta de MI"</string>
+    <string name="add_address" msgid="418292312672970688">"Agregar nueva dirección"</string>
+    <string name="add_note" msgid="2753771325725383279">"Agregar nueva nota"</string>
+    <string name="add_website" msgid="4312391288948517344">"Agregar nuevo sitio web"</string>
+    <string name="add_event" msgid="7488781591843886426">"Agregar nuevo evento"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Agregar nueva relación"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"a través de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> a través de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favorito"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index c9dcac8..9bc3152 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Predeterminado"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Eliminar foto"</string>
     <string name="noContacts" msgid="8579310973261953559">"No hay ningún contacto."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"No se ha encontrado ningún contacto coincidente."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"No hay ningún contacto con número de teléfono."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Solo contactos con teléfono"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> encontrados"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Contactos"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoritos"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Teléfono"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Llamadas"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"El registro de llamadas está vacío."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Borrar registro de llamadas"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"¿Estás seguro de que quieres borrar el registro de llamadas?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Borrando registro de llamadas"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Buzón de voz"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Apellido fonético"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Transcripción fonética del nombre"</string>
     <string name="account_type_format" msgid="718948015590343010">"Contacto de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Utilizar esta foto"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"No se puede editar la información del contacto de <xliff:g id="SOURCE">%1$s</xliff:g> en este dispositivo."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"No hay información adicional para este contacto."</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"No hay ningún contacto seleccionado."</string>
     <string name="add_field" msgid="2384260056674995230">"Añadir otro campo"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Añadir nuevo teléfono"</string>
+    <string name="add_email" msgid="175079666329862215">"Añadir nuevo correo"</string>
+    <string name="add_im" msgid="5158094627521120439">"Añadir nueva cuenta MI"</string>
+    <string name="add_address" msgid="418292312672970688">"Añadir nueva dirección"</string>
+    <string name="add_note" msgid="2753771325725383279">"Añadir nueva nota"</string>
+    <string name="add_website" msgid="4312391288948517344">"Añadir nuevo sitio web"</string>
+    <string name="add_event" msgid="7488781591843886426">"Añadir nuevo evento"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Añadir nueva relación"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"con <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> con <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favoritos"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 627d3eb..f6ac2e2 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"پیش فرض"</string>
     <string name="removePhoto" msgid="4898105274130284565">"حذف عکس"</string>
     <string name="noContacts" msgid="8579310973261953559">"مخاطبی موجود نیست."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"مخاطب منطبقی یافت نشد."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"مخاطبی با شماره تلفن موجود نیست."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"فقط مخاطبین دارای شماره تلفن"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> یافت شد"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"مخاطبین"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"موارد دلخواه"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"تلفن"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"گزارش تماس"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"گزارش تماس خالی است."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"پاک کردن گزارش تماس"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"آیا مطمئن هستید که می خواهید گزارش تماس را پاک کنید؟"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"پاک کردن گزارش تماس"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"پست صوتی"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"تلفظ آوایی نام خانوادگی"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"تلفظ نام"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> مخاطب"</string>
-    <string name="from_account_format" msgid="687567483928582084">"از <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"استفاده از این عکس"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g> اطلاعات مخاطب در این دستگاه قابل ویرایش نمی باشد."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"اطلاعات دیگری برای این مخاطب موجود نیست"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"مخاطبی انتخاب نشده است."</string>
     <string name="add_field" msgid="2384260056674995230">"افزودن یک قسمت دیگر"</string>
+    <string name="add_phone" msgid="4421904942555210013">"افزودن شماره تلفن جدید"</string>
+    <string name="add_email" msgid="175079666329862215">"افزودن ایمیل جدید"</string>
+    <string name="add_im" msgid="5158094627521120439">"افزودن حساب IM جدید"</string>
+    <string name="add_address" msgid="418292312672970688">"افزودن آدرس جدید"</string>
+    <string name="add_note" msgid="2753771325725383279">"افزودن یادداشت جدید"</string>
+    <string name="add_website" msgid="4312391288948517344">"افزودن وب سایت جدید"</string>
+    <string name="add_event" msgid="7488781591843886426">"افزودن رویداد جدید"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"افزودن رابطه جدید"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"از طریق <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> از طریق <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"مورد دلخواه"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 6a9586c..92944c1 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Oletus"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Poista kuva"</string>
     <string name="noContacts" msgid="8579310973261953559">"Ei yhteystietoja."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Hakua vastaavia yhteystietoja ei löydy."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Ei yhteystietoja, joissa on puhelinnumero."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Vain yhteystiedot, joissa on puhelinnumero"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Löytyi <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Yhteystiedot"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Suosikit"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Puhelin"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Puheluloki"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Puheluloki on tyhjä."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Tyhjennä puheluloki"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Haluatko varmasti tyhjentää puhelulokin?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Tyhjennetään puhelulokia"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI-koodi"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Vastaaja"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Sukunimen ääntämistapa"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Nimen ääntämistapa"</string>
     <string name="account_type_format" msgid="718948015590343010">"Tilin <xliff:g id="SOURCE">%1$s</xliff:g> yhteystieto"</string>
-    <string name="from_account_format" msgid="687567483928582084">"tilistä <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Käytä valokuvaa"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Tilin <xliff:g id="SOURCE">%1$s</xliff:g> yhteystietoja ei voi muokata tällä laitteella."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Tässä yhteystiedossa ei ole muita tietoja"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Ei valittuja yhteystietoja."</string>
     <string name="add_field" msgid="2384260056674995230">"Lisää toinen kenttä"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Lisää uusi puh.numero"</string>
+    <string name="add_email" msgid="175079666329862215">"Lisää uusi sähköposti"</string>
+    <string name="add_im" msgid="5158094627521120439">"Lisää uusi pikav.tili"</string>
+    <string name="add_address" msgid="418292312672970688">"Lisää uusi osoite"</string>
+    <string name="add_note" msgid="2753771325725383279">"Lisää uusi huomautus"</string>
+    <string name="add_website" msgid="4312391288948517344">"Lisää uusi sivusto"</string>
+    <string name="add_event" msgid="7488781591843886426">"Lisää uusi tapahtuma"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Lisää uusi suhde"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"lähteestä: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> lähteestä: <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"lisää suosikkeihin"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index f71ef11..5e89379 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Par défaut"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Supprimer la photo"</string>
     <string name="noContacts" msgid="8579310973261953559">"Aucun contact."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Aucun contact correspondant n\'a été trouvé."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Aucun contact disposant d\'un numéro téléphone."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Contacts avec un n° de tél."</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> contact(s) trouvé(s)"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Contacts"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoris"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Tél."</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Appels"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Aucun appel."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Effacer le journal d\'appels"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Voulez-vous vraiment effacer le journal d\'appels ?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Suppression du journal d\'appels"</string>
     <string name="imei" msgid="3045126336951684285">"Code IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Messagerie vocale"</string>
@@ -161,12 +166,12 @@
     <string name="simContacts_title" msgid="27341688347689769">"Contacts de carte SIM"</string>
     <string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Vous n\'avez aucun contact à afficher. Si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes."</string>
     <string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Vous n\'avez aucun contact à afficher."</string>
-    <!-- syntax error in translation for noContactsHelpText (6450346791169710787) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:560 in java.io.StringReader@4e106082)  -->
-    <!-- syntax error in translation for noContactsHelpText (7633826236417884130) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:567 in java.io.StringReader@15301ed8)  -->
+    <!-- syntax error in translation for noContactsHelpText (6450346791169710787) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:560 in java.io.StringReader@17dfafd1)  -->
+    <!-- syntax error in translation for noContactsHelpText (7633826236417884130) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:567 in java.io.StringReader@5e8fce95)  -->
     <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Vous n\'avez aucun contact à afficher (si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes)."\n\n"Pour ajouter des contacts, appuyez sur "<font fgcolor="#ffffffff"><b>"Menu"</b></font>", puis sur :"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" pour ajouter ou configurer un compte dont vous pourrez synchroniser les contacts vers la tablette ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Options d\'affichage"</b></font>" pour modifier les paramètres de visibilité des contacts ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un contact ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter"</b></font>" pour importer des contacts depuis votre carte SIM ou SD."\n</li></string>
     <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Vous n\'avez aucun contact à afficher (si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes)."\n\n"Pour ajouter des contacts, appuyez sur "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" et sélectionnez :"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" pour ajouter ou configurer un compte dont vous pourrez synchroniser les contacts sur le téléphone ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Options d\'affichage"</b></font>" pour modifier le paramètre de visibilité des contacts ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un contact ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter"</b></font>" pour importer des contacts depuis votre carte SIM ou SD."\n</li></string>
-    <!-- syntax error in translation for noContactsNoSimHelpText (6031363021287849874) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:565 in java.io.StringReader@a3901c6)  -->
-    <!-- syntax error in translation for noContactsNoSimHelpText (467658807711582876) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:571 in java.io.StringReader@24a37368)  -->
+    <!-- syntax error in translation for noContactsNoSimHelpText (6031363021287849874) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:565 in java.io.StringReader@3343c8b3)  -->
+    <!-- syntax error in translation for noContactsNoSimHelpText (467658807711582876) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:571 in java.io.StringReader@272d7a10)  -->
     <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Vous n\'avez aucun contact à afficher (si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes)."\n\n"Pour ajouter des contacts, appuyez sur "<font fgcolor="#ffffffff"><b>"Menu"</b></font>", puis sur :"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" pour ajouter ou configurer un compte dont vous pourrez synchroniser les contacts vers la tablette ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Options d\'affichage"</b></font>" pour modifier les paramètres de visibilité des contacts ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un contact ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter"</b></font>" pour importer des contacts depuis votre carte SD."\n</li></string>
     <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Vous n\'avez aucun contact à afficher (si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes)."\n\n"Pour ajouter des contacts, appuyez sur "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" et sélectionnez :"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" pour ajouter ou configurer un compte dont vous pourrez synchroniser les contacts sur le téléphone ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Options d\'affichage"</b></font>" pour modifier le paramètre de visibilité des contacts ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un contact ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter"</b></font>" pour importer des contacts depuis votre carte SD."\n</li></string>
     <string name="noFavoritesHelpText" msgid="3744655776704833277">"Vous ne disposez d\'aucun favoris."\n\n"Pour ajouter un contact à la liste de favoris :"\n\n"        "<li>"Appuyez sur l\'onglet "<b>"Contacts"</b>"."\n</li>" "\n<li>"Appuyez sur le contact à ajouter à vos favoris."\n</li>" "\n<li>"Appuyez sur l\'étoile en regard du nom du contact."\n</li></string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Phonétique du nom de famille"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Nom phonétique"</string>
     <string name="account_type_format" msgid="718948015590343010">"Contact <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Utiliser cette photo"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Vous ne pouvez pas modifier les informations du contact <xliff:g id="SOURCE">%1$s</xliff:g> sur cet appareil."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Aucune autre information pour ce contact"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Aucun contact sélectionné"</string>
     <string name="add_field" msgid="2384260056674995230">"Ajouter un champ"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Ajouter un numéro"</string>
+    <string name="add_email" msgid="175079666329862215">"Ajouter adresse e-mail"</string>
+    <string name="add_im" msgid="5158094627521120439">"Ajouter compte chat"</string>
+    <string name="add_address" msgid="418292312672970688">"Ajouter une adresse"</string>
+    <string name="add_note" msgid="2753771325725383279">"Ajouter une remarque"</string>
+    <string name="add_website" msgid="4312391288948517344">"Ajouter un site Web"</string>
+    <string name="add_event" msgid="7488781591843886426">"Ajouter un événement"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Ajouter une relation"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favori"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 3f06446..97b4cd7 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Zadano"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Ukloni fotografiju"</string>
     <string name="noContacts" msgid="8579310973261953559">"Nema kontakata."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Nisu pronađeni podudarni kontakti."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nema kontakata s telefonskim brojevima."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Samo kontakti s telefonima"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Pronađeno kontakata: <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kontakti"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoriti"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Zapisnik poziva"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Zapisnik poziva je prazan"</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Izbriši zapisnik poziva"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Jeste li sigurni da želite izbrisati zapisnik poziva?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Brisanje zapisnika poziva"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Govorna pošta"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Fonetsko prezime"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Ime fonetski"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> kontakt"</string>
-    <string name="from_account_format" msgid="687567483928582084">"s lokacije <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Upotrijebi ovu fotografiju"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g> informacije kontakta nije moguće uređivati na ovom uređaju."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Nema dodatnih informacija za ovaj kontakt"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nije odabran nijedan kontakt."</string>
     <string name="add_field" msgid="2384260056674995230">"Dodaj drugo polje"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Dodajte novi tel. broj"</string>
+    <string name="add_email" msgid="175079666329862215">"Dodajte novu e-poštu"</string>
+    <string name="add_im" msgid="5158094627521120439">"Dodajte novi IM račun"</string>
+    <string name="add_address" msgid="418292312672970688">"Dodajte novu adresu"</string>
+    <string name="add_note" msgid="2753771325725383279">"Dodajte novu napomenu"</string>
+    <string name="add_website" msgid="4312391288948517344">"Dodajte novu stranicu"</string>
+    <string name="add_event" msgid="7488781591843886426">"Dodajte novi događaj"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Dodajte novi odnos"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"preko izvora <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> preko izvora <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favorit"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index d7427e4..d4dd0f9 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Alapértelmezett"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Fénykép törlése"</string>
     <string name="noContacts" msgid="8579310973261953559">"Nincsenek névjegyek."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Nincsenek a keresési feltételeknek megfelelő névjegyek."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nincsenek telefonszámot is tartalmazó névjegyek."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Csak a telefonszámmal rendelkező névjegyek"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> találat"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Címtár"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Kedvencek"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Híváslista"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"A híváslista üres."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Híváslista törlése"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Biztosan törli a híváslistát?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Hívási napló törölve"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Hangposta"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Vezetéknév fonetikusan"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Név fonetikusan"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> névjegy"</string>
-    <string name="from_account_format" msgid="687567483928582084">"<xliff:g id="SOURCE">%1$s</xliff:g> fiókból"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Fotó felhasználása"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"A(z) <xliff:g id="SOURCE">%1$s</xliff:g> névjegyadatai nem szerkeszthetők ezen az eszközön."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Nincsenek további adatok ennél a névjegynél"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nincs kijelölt névjegy."</string>
     <string name="add_field" msgid="2384260056674995230">"Más mező hozzáadása"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Új telefonszám"</string>
+    <string name="add_email" msgid="175079666329862215">"Új e-mail hozzáadása"</string>
+    <string name="add_im" msgid="5158094627521120439">"Új IM-fiók hozzáadása"</string>
+    <string name="add_address" msgid="418292312672970688">"Új cím hozzáadása"</string>
+    <string name="add_note" msgid="2753771325725383279">"Új megjegyzés"</string>
+    <string name="add_website" msgid="4312391288948517344">"Új webhely hozzáadása"</string>
+    <string name="add_event" msgid="7488781591843886426">"Új esemény hozzáadása"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Új kapcsolat"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"- <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> - <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"kedvenc"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index c031cac..8418108 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Bawaan"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Hapus foto"</string>
     <string name="noContacts" msgid="8579310973261953559">"Tidak ada kenalan."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Tidak ditemukan kenalan yang cocok."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Tidak ada kenalan dengan nomor telepon."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Hanya kenalan dengan telepon"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Ada <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kenalan"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favorit"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telepon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Log panggilan"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Log panggilan kosong."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Hapus log panggilan"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Apakah Anda yakin ingin menghapus log panggilan?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Membersihkan log panggilan"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Kotak Pesan"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Nama keluarga fonetik"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Nama fonetik"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> kenalan"</string>
-    <string name="from_account_format" msgid="687567483928582084">"dari <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Gunakan foto ini"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Informasi kenalan <xliff:g id="SOURCE">%1$s</xliff:g> tidak dapat diedit pada perangkat ini."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Tidak ada informasi tambahan untuk kenalan ini"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Tidak ada kenalan yang dipilih."</string>
     <string name="add_field" msgid="2384260056674995230">"Tambahkan bidang lain"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Menambah no. tlp. baru"</string>
+    <string name="add_email" msgid="175079666329862215">"Menambah email baru"</string>
+    <string name="add_im" msgid="5158094627521120439">"Menambah akun IM baru"</string>
+    <string name="add_address" msgid="418292312672970688">"Menambah alamat baru"</string>
+    <string name="add_note" msgid="2753771325725383279">"Menambah catatan baru"</string>
+    <string name="add_website" msgid="4312391288948517344">"Menambah situs baru"</string>
+    <string name="add_event" msgid="7488781591843886426">"Menambah acara baru"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Menambah hubungan baru"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"melalui <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> melalui <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favorit"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 8568daf..a64e592 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -95,6 +95,7 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Predefinita"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Rimuovi foto"</string>
     <string name="noContacts" msgid="8579310973261953559">"Nessun contatto."</string>
+    <string name="noGroups" msgid="8614664663561385253">"Nessun gruppo."</string>
     <string name="noMatchingContacts" msgid="4266283206853990471">"Nessun contatto corrispondente trovato."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nessun contatto con numeri di telefono."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Solo contatti con numeri di tel."</string>
@@ -134,6 +135,7 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> trovati"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Contatti"</string>
+    <string name="contactsGroupsLabel" msgid="2841971472518003524">"Gruppi"</string>
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Preferiti"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefono"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Chiamate"</string>
@@ -149,6 +151,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Il registro chiamate è vuoto."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Cancella registro chiamate"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Cancellare il registro chiamate?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Cancellazione registro chiamate"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Segreteria"</string>
@@ -399,7 +402,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Cognome fonetico"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Nome fonetico"</string>
     <string name="account_type_format" msgid="718948015590343010">"Contatto da <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"da <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Utilizza questa foto"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Informazioni del contatto da <xliff:g id="SOURCE">%1$s</xliff:g> non modificabili su questo dispositivo."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Nessuna informazione aggiuntiva per questo contatto"</string>
@@ -431,6 +435,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nessun contatto selezionato."</string>
     <string name="add_field" msgid="2384260056674995230">"Aggiungi un altro campo"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Nuovo n. telefono"</string>
+    <string name="add_email" msgid="175079666329862215">"Nuova email"</string>
+    <string name="add_im" msgid="5158094627521120439">"Nuovo account IM"</string>
+    <string name="add_address" msgid="418292312672970688">"Nuovo indirizzo"</string>
+    <string name="add_note" msgid="2753771325725383279">"Nuova nota"</string>
+    <string name="add_website" msgid="4312391288948517344">"Nuovo sito web"</string>
+    <string name="add_event" msgid="7488781591843886426">"Nuovo evento"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Nuova relazione"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"tramite <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> tramite <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"preferiti"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index a8fc33e..7a28703 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"ברירת מחדל"</string>
     <string name="removePhoto" msgid="4898105274130284565">"הסר תמונה"</string>
     <string name="noContacts" msgid="8579310973261953559">"אין אנשי קשר."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"לא נמצאו אנשי קשר תואמים."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"אין אנשי קשר עם מספרי טלפון."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"רק אנשי קשר עם מספרי טלפון"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> נמצאו"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"אנשי קשר"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"מועדפים"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"טלפון"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"יומן שיחות"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"יומן השיחות ריק."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"נקה את יומן השיחות"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"האם אתה בטוח שברצונך לנקות את יומן השיחות?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"ניקוי יומן שיחות"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"דואר קולי"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"שם משפחה פונטי"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"שם פונטי"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g>איש קשר"</string>
-    <string name="from_account_format" msgid="687567483928582084">"מ-<xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"השתמש בתמונה זו"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g> פרטי איש הקשר אינם ניתנים לעריכה במכשיר זה."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"אין מידע נוסף על איש קשר זה"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"לא נבחרו אנשי קשר."</string>
     <string name="add_field" msgid="2384260056674995230">"הוסף שדה נוסף"</string>
+    <string name="add_phone" msgid="4421904942555210013">"הוסף מספר טלפון חדש"</string>
+    <string name="add_email" msgid="175079666329862215">"הוסף דוא\"ל חדש"</string>
+    <string name="add_im" msgid="5158094627521120439">"הוסף חשבון IM חדש"</string>
+    <string name="add_address" msgid="418292312672970688">"הוסף כתובת חדשה"</string>
+    <string name="add_note" msgid="2753771325725383279">"הוסף הערה חדשה"</string>
+    <string name="add_website" msgid="4312391288948517344">"הוסף אתר חדש"</string>
+    <string name="add_event" msgid="7488781591843886426">"הוסף אירוע חדש"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"הוסף קשר חדש"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"דרך <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> דרך <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"מועדף"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 4f2354a..9f2f257 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"プリセット着信音"</string>
     <string name="removePhoto" msgid="4898105274130284565">"写真を削除"</string>
     <string name="noContacts" msgid="8579310973261953559">"連絡先がありません。"</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"一致する連絡先が見つかりません。"</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"電話番号付きの連絡先はありません。"</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"電話番号のある連絡先のみ"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g>件見つかりました"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"連絡先"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"お気入り"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"電話"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"通話履歴"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"通話履歴なし"</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"通話履歴を消去"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"通話履歴を消去してもよろしいですか?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"通話履歴の消去中"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI(端末識別番号)"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"ボイスメール"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"姓のよみがな"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"よみがな"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g>からの連絡先"</string>
-    <string name="from_account_format" msgid="687567483928582084">"<xliff:g id="SOURCE">%1$s</xliff:g>からの連絡先"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"この写真を使用"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g>からの連絡先情報はこの携帯端末では編集できません。"</string>
     <string name="no_contact_details" msgid="6754415338321837001">"この連絡先の詳細情報はありません"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"連絡先が選択されていません。"</string>
     <string name="add_field" msgid="2384260056674995230">"別のフィールドを追加"</string>
+    <string name="add_phone" msgid="4421904942555210013">"新しい電話番号を追加"</string>
+    <string name="add_email" msgid="175079666329862215">"新しいメールを追加"</string>
+    <string name="add_im" msgid="5158094627521120439">"新規IMアカウントを追加"</string>
+    <string name="add_address" msgid="418292312672970688">"新しい住所を追加"</string>
+    <string name="add_note" msgid="2753771325725383279">"新しいメモを追加"</string>
+    <string name="add_website" msgid="4312391288948517344">"新しいサイトを追加"</string>
+    <string name="add_event" msgid="7488781591843886426">"新しいイベントを追加"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"新しい関係を追加"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"<xliff:g id="SOURCE">%1$s</xliff:g>経由"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g>、<xliff:g id="SOURCE">%2$s</xliff:g>経由"</string>
     <string name="description_star" msgid="2605854427360036550">"お気に入り"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index ee93888..5ccbf47 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"기본값"</string>
     <string name="removePhoto" msgid="4898105274130284565">"사진 삭제"</string>
     <string name="noContacts" msgid="8579310973261953559">"주소록이 없습니다."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"일치하는 연락처가 없습니다."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"전화번호가 포함된 주소록이 없습니다."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"전화번호가 있는 연락처만"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g>개를 찾았습니다."</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"주소록"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"즐겨찾기"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"휴대전화"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"통화기록"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"통화기록이 없습니다."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"통화 기록 지우기"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"통화 기록을 삭제하시겠습니까?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"통화기록 지우기"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"음성사서함"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"성(소리나는 대로)"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"이름(소리나는 대로)"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> 연락처"</string>
-    <string name="from_account_format" msgid="687567483928582084">"출처: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"사진 사용"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g> 연락처 정보는 이 기기에서 수정할 수 없습니다."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"연락처에 대한 추가 정보가 없습니다."</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"선택한 연락처가 없습니다."</string>
     <string name="add_field" msgid="2384260056674995230">"다른 입력란 추가"</string>
+    <string name="add_phone" msgid="4421904942555210013">"새 전화번호 추가"</string>
+    <string name="add_email" msgid="175079666329862215">"새 이메일 추가"</string>
+    <string name="add_im" msgid="5158094627521120439">"새 메신저 계정 추가"</string>
+    <string name="add_address" msgid="418292312672970688">"새 주소 추가"</string>
+    <string name="add_note" msgid="2753771325725383279">"새 메모 추가"</string>
+    <string name="add_website" msgid="4312391288948517344">"새 웹사이트 추가"</string>
+    <string name="add_event" msgid="7488781591843886426">"새 일정 추가"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"새 관계 추가"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"출처: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g>(출처: <xliff:g id="SOURCE">%2$s</xliff:g>)"</string>
     <string name="description_star" msgid="2605854427360036550">"즐겨찾기"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 7eb3506..1e063e4 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Numatytasis"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Pašalinti nuotrauką"</string>
     <string name="noContacts" msgid="8579310973261953559">"Adresatų nėra."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Nerasta atitinkančių adresatų."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nėra adresatų su telefono numeriais."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Tik adresatai su telefono numeriais"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Rasta <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Adresinė"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Adresynas"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefonas"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Skambučių žurnalas"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Skambučių žurnalas tuščias."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Išvalyti skambučių žurnalą"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Ar tikrai norite išvalyti skambučių žurnalą?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Valomas skambučių žurnalas"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Balso paštas"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Fonetinė pavardė"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Fonetinis vardas"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> adresatas"</string>
-    <string name="from_account_format" msgid="687567483928582084">"nuo <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Naudoti šią nuotrauką"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g> adresato informacijos šiame įrenginyje redaguoti negalima."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Nėra papildomos informacijos apie šį adresatą"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nepasirinkote kontaktų."</string>
     <string name="add_field" msgid="2384260056674995230">"Pridėti kitą lauką"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Pridėti naują tel. nr."</string>
+    <string name="add_email" msgid="175079666329862215">"Prid. n. el. pšt. adr."</string>
+    <string name="add_im" msgid="5158094627521120439">"Pridėti naują TP pask."</string>
+    <string name="add_address" msgid="418292312672970688">"Pridėti naują adresą"</string>
+    <string name="add_note" msgid="2753771325725383279">"Pridėti naują pastabą"</string>
+    <string name="add_website" msgid="4312391288948517344">"Pridėti naują svetainę"</string>
+    <string name="add_event" msgid="7488781591843886426">"Pridėti naują įvykį"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Pridėti naujus santyk."</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"naudojant „<xliff:g id="SOURCE">%1$s</xliff:g>“"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> naudojant „<xliff:g id="SOURCE">%2$s</xliff:g>“"</string>
     <string name="description_star" msgid="2605854427360036550">"įtraukti į adresyną"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 31f4cee..59668ab 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Noklusējums"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Noņemt fotoattēlu"</string>
     <string name="noContacts" msgid="8579310973261953559">"Nav kontaktpersonu."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Nav atrasta neviena atbilstoša kontaktpersona."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nav nevienas kontaktpersonas ar tālruņa numuru."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Tikai kontaktpersonas ar tālruņiem"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Atrastas <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kontaktpersonas"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Izlase"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Zvanīt"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Zvanu žurnāls"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Zvanu žurnāls ir tukšs."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Notīrīt zvanu žurnālu"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Vai tiešām vēlaties notīrīt zvanu žurnālu?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Zvanu žurnāla tīrīšana"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Balss pasts"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Uzvārda izruna"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Vārda izruna"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> kontaktpersona"</string>
-    <string name="from_account_format" msgid="687567483928582084">"no <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Izmantot šo fotoattēlu"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Kontaktinformāciju no pakalpojuma <xliff:g id="SOURCE">%1$s</xliff:g> šajā ierīcē nevar rediģēt."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Nav pieejama papildu informācija par šo kontaktpersonu."</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nav atlasīta neviena kontaktpersona."</string>
     <string name="add_field" msgid="2384260056674995230">"Pievienot vēl vienu lauku"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Piev. jaunu tālr. nr."</string>
+    <string name="add_email" msgid="175079666329862215">"Piev. jaunu e-pastu"</string>
+    <string name="add_im" msgid="5158094627521120439">"Piev. jaunu t. ziņ. k."</string>
+    <string name="add_address" msgid="418292312672970688">"Pievienot jaunu adresi"</string>
+    <string name="add_note" msgid="2753771325725383279">"Piev. jaunu piezīmi"</string>
+    <string name="add_website" msgid="4312391288948517344">"Pievienot jaunu vietni"</string>
+    <string name="add_event" msgid="7488781591843886426">"Piev. jaunu notikumu"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Piev. jaunu saistību"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"izmantojot <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g>, izmantojot <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"izlase"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index b2d7b85..c02662d 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Standardvalg"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Fjern bilde"</string>
     <string name="noContacts" msgid="8579310973261953559">"Ingen kontakter."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Fant ingen kontakter."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Ingen kontakter med telefonnummer."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Kun kontakter med telefon"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> funnet"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kontakter"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoritter"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Logg"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Anropsloggen er tom."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Tøm samtalelogg"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Er du sikker på at du vil tømme samtaleloggen?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Tømming av anropslogg"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Telefonsvarer"</string>
@@ -161,12 +166,12 @@
     <string name="simContacts_title" msgid="27341688347689769">"Kontakter på SIM-kort"</string>
     <string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Du har ingen kontakter å vise. (Hvis du nettopp har lagt til en konto, kan det ta noen minutter å synkronisere kontaktene.)"</string>
     <string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Du har ingen kontakter å vise."</string>
-    <!-- syntax error in translation for noContactsHelpText (6450346791169710787) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:292 in java.io.StringReader@721cdeff)  -->
-    <!-- syntax error in translation for noContactsHelpText (7633826236417884130) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:293 in java.io.StringReader@457471e0)  -->
+    <!-- syntax error in translation for noContactsHelpText (6450346791169710787) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:292 in java.io.StringReader@457471e0)  -->
+    <!-- syntax error in translation for noContactsHelpText (7633826236417884130) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:293 in java.io.StringReader@5fe04cbf)  -->
     <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Du har ingen kontakter å vise. (Hvis du nylig la til en konto, kan det ta noen minutter å synkronisere kontaktene.)"\n\n"Slik legger du til kontakter: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>", og trykk deretter på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer"</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til nettbrettet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativer"</b></font>" for å endre hvilke kontakter som vises"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt fra grunnen av"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Eksporter"</b></font>" for å importere kontakter fra SIM- eller SD-kort"\n</li></string>
     <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Du har ingen kontakter å vise. (Hvis du nylig la til en konto, kan det ta noen minutter å synkronisere kontaktene.)"\n\n"Slik legger du til kontakter: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" og trykk deretter på: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer "</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativer"</b></font>" for å endre hvilke kontakter som vises"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importér/Eksportér"</b></font>" for å importere kontakter fra SIM- eller SD-kort"\n</li></string>
-    <!-- syntax error in translation for noContactsNoSimHelpText (6031363021287849874) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:297 in java.io.StringReader@5fe04cbf)  -->
-    <!-- syntax error in translation for noContactsNoSimHelpText (467658807711582876) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:297 in java.io.StringReader@7ecec0c5)  -->
+    <!-- syntax error in translation for noContactsNoSimHelpText (6031363021287849874) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:297 in java.io.StringReader@7ecec0c5)  -->
+    <!-- syntax error in translation for noContactsNoSimHelpText (467658807711582876) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:297 in java.io.StringReader@37d2068d)  -->
     <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Du har ingen kontakter å vise. (Hvis du nylig la til en konto, kan det ta noen minutter å synkronisere kontaktene.)"\n\n"Slik legger du til kontakter: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>", og trykk deretter på: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer"</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til nettbrettet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativer"</b></font>" for å endre hvilke kontakter som vises"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt fra grunnen av"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>" for å importere kontakter fra SD-kortet"\n</li></string>
     <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Du har ingen kontakter å vise. (Hvis du nylig la til en konto, kan det ta noen minutter å synkronisere kontaktene.)"\n\n"Slik legger du til kontakter: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" og trykk deretter på: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer "</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativer"</b></font>" for å endre hvilke kontakter som vises"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importér/eksportér"</b></font>" for å importere kontakter fra SD-kortet"\n</li></string>
     <string name="noFavoritesHelpText" msgid="3744655776704833277">"Du har ingen favoritter."\n\n"Slik legger du til en kontakt i favorittlisten:"\n\n" "<li>"Trykk på fanen "<b>"Kontakter"</b>" "\n</li>" "\n<li>"Trykk på kontakten du vil legge til i favoritter"\n</li>" "\n<li>"Trykk på stjernen ved siden av kontaktnavnet"\n</li></string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Fonetisk etternavn"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Fonetisk navn"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g>-kontakt"</string>
-    <string name="from_account_format" msgid="687567483928582084">"fra <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Bruk dette bildet"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g>-kontaktinformasjon kan ikke redigeres på denne enheten."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Ingen utfyllende informasjon for denne kontakten"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Ingen kontakter valgt."</string>
     <string name="add_field" msgid="2384260056674995230">"Legg til et annet felt"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Legg til nytt tlf.nr."</string>
+    <string name="add_email" msgid="175079666329862215">"Legg til ny e-post"</string>
+    <string name="add_im" msgid="5158094627521120439">"Legg til nettpratkonto"</string>
+    <string name="add_address" msgid="418292312672970688">"Legg til ny adresse"</string>
+    <string name="add_note" msgid="2753771325725383279">"Legg til ny merknad:"</string>
+    <string name="add_website" msgid="4312391288948517344">"Legg til nytt nettsted"</string>
+    <string name="add_event" msgid="7488781591843886426">"Legg til ny aktivitet"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Legg til nytt forhold"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favoritt"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index cc70f61..d33e633 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Standaard"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Foto verwijderen"</string>
     <string name="noContacts" msgid="8579310973261953559">"Geen contacten."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Kan geen overeenkomende contacten vinden."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Geen contacten met telefoonnummers."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Alleen contacten met tel.nr."</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> gevonden"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Contact"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoriet"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefoon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Gesprek"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Gesprekken is leeg"</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Oproeplogboek wissen"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Weet u zeker dat u het oproeplogboek wilt wissen?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Oproeplogboek wissen"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI-nummer"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Fonetische achternaam"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Fonetisch gespelde naam"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> contact"</string>
-    <string name="from_account_format" msgid="687567483928582084">"van <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Deze foto gebruiken"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Contactgegevens van <xliff:g id="SOURCE">%1$s</xliff:g> kunnen niet worden bewerkt op dit apparaat."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Geen extra gegevens voor dit contact"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Geen contacten geselecteerd."</string>
     <string name="add_field" msgid="2384260056674995230">"Nog een veld toevoegen"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Nieuw telefoonnummer"</string>
+    <string name="add_email" msgid="175079666329862215">"Nieuw e-mailadres"</string>
+    <string name="add_im" msgid="5158094627521120439">"Nieuw IM-account"</string>
+    <string name="add_address" msgid="418292312672970688">"Nieuw adres"</string>
+    <string name="add_note" msgid="2753771325725383279">"Nieuwe opmerking"</string>
+    <string name="add_website" msgid="4312391288948517344">"Nieuwe website"</string>
+    <string name="add_event" msgid="7488781591843886426">"Nieuwe gebeurtenis"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Nieuwe relatie"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favoriet"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 98cc2e8..f58f81c 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Domyślny"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Usuń zdjęcie"</string>
     <string name="noContacts" msgid="8579310973261953559">"Brak kontaktów"</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Nie znaleziono pasujących kontaktów."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Brak kontaktów z numerami telefonów"</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Tylko kontakty z telefonami"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Znaleziono: <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kontakty"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Ulubione"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Rejestr"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Rejestr połączeń jest pusty."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Wyczyść rejestr połączeń"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Czy na pewno chcesz wyczyścić rejestr połączeń?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Czyszczenie dziennika połączeń"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"Numer MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Poczta głosowa"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Nazwisko (fonetycznie)"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Nazwisko (fonetycznie)"</string>
     <string name="account_type_format" msgid="718948015590343010">"Kontakt <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"z <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Użyj tego zdjęcia"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"W tym urządzeniu nie można edytować informacji kontaktowych <xliff:g id="SOURCE">%1$s</xliff:g>."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Brak dodatkowych informacji dla tego kontaktu"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nie wybrano kontaktów."</string>
     <string name="add_field" msgid="2384260056674995230">"Dodaj inne pole"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Dodaj nowy nr telefonu"</string>
+    <string name="add_email" msgid="175079666329862215">"Dodaj nowy e-mail"</string>
+    <string name="add_im" msgid="5158094627521120439">"Dodaj nowe konto IM"</string>
+    <string name="add_address" msgid="418292312672970688">"Dodaj nowy adres"</string>
+    <string name="add_note" msgid="2753771325725383279">"Dodaj nową notatkę"</string>
+    <string name="add_website" msgid="4312391288948517344">"Dodaj nową witrynę"</string>
+    <string name="add_event" msgid="7488781591843886426">"Dodaj nowe wydarzenie"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Dodaj nową relację"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"za pośrednictwem: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g>, za pośrednictwem: <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"ulubione"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 556e529..91e33a9 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Predefinido"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Remover fotografia"</string>
     <string name="noContacts" msgid="8579310973261953559">"Sem contactos."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Não foram encontrados contactos correspondentes."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Sem contactos com números de telefone."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Apenas contactos com telefones"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> encontrado(s)"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Contactos"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoritos"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefone"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Chamadas"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"O registo de chamadas está vazio."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Limpar registo de chamadas"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Tem a certeza de que pretende limpar o registo de chamadas?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Limpar registo de chamadas"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Correio de voz"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Fonética do segundo apelido"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Nome fonético"</string>
     <string name="account_type_format" msgid="718948015590343010">"Contacto de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Utilizar esta fotografia"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Não é possível editar as informações de contacto do <xliff:g id="SOURCE">%1$s</xliff:g> neste dispositivo."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Não há informações adicionais para este contacto"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nenhum contacto seleccionado."</string>
     <string name="add_field" msgid="2384260056674995230">"Adicionar outro campo"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Adic. novo n.º telef."</string>
+    <string name="add_email" msgid="175079666329862215">"Adicionar novo e-mail"</string>
+    <string name="add_im" msgid="5158094627521120439">"Adic. nova conta MI"</string>
+    <string name="add_address" msgid="418292312672970688">"Adicionar novo endereço"</string>
+    <string name="add_note" msgid="2753771325725383279">"Adicionar nova nota"</string>
+    <string name="add_website" msgid="4312391288948517344">"Adicionar novo site"</string>
+    <string name="add_event" msgid="7488781591843886426">"Adicionar novo evento"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Adicionar nova relação"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"através do <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> através do <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favorito"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 6a573f7..dc1ea58 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Padrão"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Remover foto"</string>
     <string name="noContacts" msgid="8579310973261953559">"Nenhum contato."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Nenhum contato correspondente foi encontrado."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nenhum contato com números de telefone."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Apenas contatos com telefones"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> encontrados"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Contatos"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoritos"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefone"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Chamadas"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"O registro de chamadas está vazio."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Limpar registro de chamadas"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Tem certeza de que deseja limpar o registro de chamadas?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Limpando registro de chamadas"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Correio de voz"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Sobrenome fonético"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Nome fonético"</string>
     <string name="account_type_format" msgid="718948015590343010">"Contato de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Usar esta foto"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"As informações de contato de <xliff:g id="SOURCE">%1$s</xliff:g> não podem ser editadas neste aparelho."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Não há informações adicionais para este contato"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nenhum contato selecionado."</string>
     <string name="add_field" msgid="2384260056674995230">"Adicionar outro campo"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Adicionar num. de tel."</string>
+    <string name="add_email" msgid="175079666329862215">"Adicionar novo e-mail"</string>
+    <string name="add_im" msgid="5158094627521120439">"Adic. conta de IM"</string>
+    <string name="add_address" msgid="418292312672970688">"Adicionar endereço"</string>
+    <string name="add_note" msgid="2753771325725383279">"Adicionar nota"</string>
+    <string name="add_website" msgid="4312391288948517344">"Adicionar novo site"</string>
+    <string name="add_event" msgid="7488781591843886426">"Adicionar evento"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Adicionar relac."</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"por meio de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favorito"</string>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index 7892abb..e558a89 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -101,6 +101,8 @@
     <!-- no translation found for removePhoto (4898105274130284565) -->
     <skip />
     <string name="noContacts" msgid="8579310973261953559">"Nagins contacts"</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Betg chattà in contact correspundent."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nagin contact dispona d\'in numer da telefon."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Mo contacts cun telefon"</string>
@@ -138,6 +140,8 @@
     <item quantity="other" msgid="7988132539476575389">"Chattà <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Contacts"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favurits"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Cloms"</string>
@@ -153,6 +157,8 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"La glista da cloms e vida"</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Stizzar il protocol da cloms"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Vulais Vus propi stizzar il protocol da cloms?"</string>
+    <!-- no translation found for clearCallLogProgress_title (6870412675015656948) -->
+    <skip />
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Mailbox"</string>
@@ -428,7 +434,8 @@
     <!-- no translation found for name_phonetic (4259595234312430484) -->
     <skip />
     <string name="account_type_format" msgid="718948015590343010">"Contact <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"da <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Utilisar quest foto"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Vus na pudais betg modifitgar las infurmaziuns dal contact <xliff:g id="SOURCE">%1$s</xliff:g> sin quest apparat."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Naginas infurmaziuns supplementaras per quest contact"</string>
@@ -469,6 +476,22 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Betg tschernì in contact"</string>
     <!-- outdated translation 5257149039253569615 -->     <string name="add_field" msgid="2384260056674995230">"Agiuntar infurmaziuns"</string>
+    <!-- no translation found for add_phone (4421904942555210013) -->
+    <skip />
+    <!-- no translation found for add_email (175079666329862215) -->
+    <skip />
+    <!-- no translation found for add_im (5158094627521120439) -->
+    <skip />
+    <!-- no translation found for add_address (418292312672970688) -->
+    <skip />
+    <!-- no translation found for add_note (2753771325725383279) -->
+    <skip />
+    <!-- no translation found for add_website (4312391288948517344) -->
+    <skip />
+    <!-- no translation found for add_event (7488781591843886426) -->
+    <skip />
+    <!-- no translation found for add_relationship (3083762399737240006) -->
+    <skip />
     <string name="contact_status_update_attribution" msgid="752179367353018597">"entras <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> entras <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favurit"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index b52a70c..5cabe0e 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Prestabilit"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Eliminaţi fotografia"</string>
     <string name="noContacts" msgid="8579310973261953559">"Nicio persoană în agendă."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Nu a fost găsită nicio persoană potrivită în agendă."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nicio persoană din agendă cu numere de telefon."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Numai persoanele din agendă cu numere de telefon"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Au fost găsite <xliff:g id="COUNT">%d</xliff:g> (de) persoane din agendă"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Agendă"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favorite"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Jurnal de apeluri"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Jurnalul de apeluri este gol."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Ştergeţi jurnalul de apeluri"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Sigur doriţi ştergerea jurnalului de apeluri?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Se goleşte jurnalul de apeluri"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Mesagerie vocală"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Numele de familie fonetic"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Nume fonetic"</string>
     <string name="account_type_format" msgid="718948015590343010">"Persoana din agendă din <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"din <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Utilizaţi această fotografie"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g> Informaţiile despre această persoană din agendă nu se pot edita pe acest dispozitiv."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Nicio informaţie suplimentară pentru această persoană din agendă"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nu aţi selectat nicio persoană din Agendă."</string>
     <string name="add_field" msgid="2384260056674995230">"Adăugaţi alt câmp"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Adăugaţi nr. tel. nou"</string>
+    <string name="add_email" msgid="175079666329862215">"Adăugaţi e-mail nou"</string>
+    <string name="add_im" msgid="5158094627521120439">"Adăugaţi cont IM nou"</string>
+    <string name="add_address" msgid="418292312672970688">"Adăugaţi adresă nouă"</string>
+    <string name="add_note" msgid="2753771325725383279">"Adăugaţi notă nouă"</string>
+    <string name="add_website" msgid="4312391288948517344">"Adăugaţi site web nou"</string>
+    <string name="add_event" msgid="7488781591843886426">"Adăugaţi eveniment nou"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Adăugaţi relaţie nouă"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"de pe <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"La ora <xliff:g id="DATE">%1$s</xliff:g> de pe <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"preferate"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 6d2a2a8..690207b 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"По умолчанию"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Удалить фото"</string>
     <string name="noContacts" msgid="8579310973261953559">"Нет контактов."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Подходящие контакты не найдены."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Нет контактов с номерами телефонов."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Контакты с телефонами"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Найдено: <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Контакты"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Избранное"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Кнопки"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Вызовы"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Список вызовов пуст."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Очистить список вызовов"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Действительно очистить список вызовов?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Очистка списка вызовов"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Голосовая почта"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Фамилия (транскрипция)"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Транскрипция имени"</string>
     <string name="account_type_format" msgid="718948015590343010">"Контакт <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"из <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Использовать эту фотографию"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Невозможно изменить контактную информацию <xliff:g id="SOURCE">%1$s</xliff:g> на этом устройстве."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Отсутствует дополнительная информация об этом контакте."</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Контакты не выбраны."</string>
     <string name="add_field" msgid="2384260056674995230">"Добавить другое поле"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Добавить тел. номер"</string>
+    <string name="add_email" msgid="175079666329862215">"Добавить эл. почту"</string>
+    <string name="add_im" msgid="5158094627521120439">"Добавить чат"</string>
+    <string name="add_address" msgid="418292312672970688">"Добавить адрес"</string>
+    <string name="add_note" msgid="2753771325725383279">"Добавить примечание"</string>
+    <string name="add_website" msgid="4312391288948517344">"Добавить веб-сайт"</string>
+    <string name="add_event" msgid="7488781591843886426">"Добавить событие"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Добавить отношение"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"с помощью <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> с помощью <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"избранное"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 6a78891..59cc4e7 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Predvolené"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Odstrániť fotografiu"</string>
     <string name="noContacts" msgid="8579310973261953559">"Žiadne kontakty."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Nenašli sa žiadne zodpovedajúce kontakty."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Ku kontaktom nie sú priradené žiadne telefónne čísla."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Iba kontakty s telefónmi"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Počet nájdených položiek: <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kontakty"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Obľúbené"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefón"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Denník hovorov"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Záznam hovorov je prázdny."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Vymazať záznam hovorov"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Naozaj chcete odstrániť denník hovorov?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Vymazávanie denníka hovorov"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Hlasová schránka"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Priezvisko (foneticky)"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Meno (foneticky)"</string>
     <string name="account_type_format" msgid="718948015590343010">"Kontakt <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"z miesta: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Použiť túto fotografiu"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Kontaktné informácie zo zdroja <xliff:g id="SOURCE">%1$s</xliff:g> nie je možné na tomto zariadení upraviť."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Pri tomto kontakte nie sú uvedené ďalšie informácie"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Nie sú vybraté žiadne kontakty."</string>
     <string name="add_field" msgid="2384260056674995230">"Pridať ďalšie pole"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Pridať nové tel. číslo"</string>
+    <string name="add_email" msgid="175079666329862215">"Pridať nový e-mail"</string>
+    <string name="add_im" msgid="5158094627521120439">"Pridať nový účet IM"</string>
+    <string name="add_address" msgid="418292312672970688">"Pridať novú adresu"</string>
+    <string name="add_note" msgid="2753771325725383279">"Pridať novú poznámku"</string>
+    <string name="add_website" msgid="4312391288948517344">"Pridať novú lokalitu"</string>
+    <string name="add_event" msgid="7488781591843886426">"Pridať novú udalosť"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Pridať nový vzťah"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">", zdroj: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g>, zdroj: <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"zaradiť medzi obľúbené"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 0bb51c4..5f4a480 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Privzeto"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Odstrani fotografijo"</string>
     <string name="noContacts" msgid="8579310973261953559">"Ni stikov."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Najti ni bilo mogoče nobenega ustreznega stika."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Ni stikov s telefonskimi številkami."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Samo stiki s telefoni"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> najdenih"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Stiki"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Priljubljeno"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Dnevnik klicev"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Dnevnik klicev je prazen."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Počisti dnevnik klicev"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Ali ste prepričani, da želite počistiti dnevnik klicev?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Brisanje dnevnika klicev"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Glasovna pošta"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Priimek – fonetično"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Ime – fonetično"</string>
     <string name="account_type_format" msgid="718948015590343010">"Stik <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"od <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Uporabi to fotografijo"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Podatkov o stiku <xliff:g id="SOURCE">%1$s</xliff:g> s to napravo ni mogoče urejati."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Ni dodatnih informacij za ta stik"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Izbran ni noben stik."</string>
     <string name="add_field" msgid="2384260056674995230">"Dodaj drugo polje"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Dodaj novo telefonsko številko"</string>
+    <string name="add_email" msgid="175079666329862215">"Dodaj novo e-pošto"</string>
+    <string name="add_im" msgid="5158094627521120439">"Dodaj nov račun za NS"</string>
+    <string name="add_address" msgid="418292312672970688">"Dodaj nov naslov"</string>
+    <string name="add_note" msgid="2753771325725383279">"Dodaj novo opombo"</string>
+    <string name="add_website" msgid="4312391288948517344">"Dodaj novo spletno mesto"</string>
+    <string name="add_event" msgid="7488781591843886426">"Dodaj nov dogodek"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Dodaj novo razmerje"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"prek <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> prek <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"priljubljeno"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 614548e..2cd8648 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Подразумевано"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Уклони фотографију"</string>
     <string name="noContacts" msgid="8579310973261953559">"Нема контаката."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Није пронађен ниједан одговарајући контакт."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Нема контаката за које су унети бројеви телефона."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Само контакти за које су унети бројеви телефона"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> пронађено"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Контакти"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Омиљено"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Телефон"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Евиденција позива"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Евиденција позива је празна."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Обриши евиденцију позива"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Желите ли заиста да обришете евиденцију позива?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Брисање евиденције позива"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Говорна пошта"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Презиме – фонетски"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Име – фонетски"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> контакт"</string>
-    <string name="from_account_format" msgid="687567483928582084">"са <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Користи ову фотографију"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"На овом уређају није могуће изменити информације о контакту <xliff:g id="SOURCE">%1$s</xliff:g>."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Нема додатних информације за овај контакт"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Није изабран ниједан контакт."</string>
     <string name="add_field" msgid="2384260056674995230">"Додај друго поље"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Додајте нови телефон"</string>
+    <string name="add_email" msgid="175079666329862215">"Додајте нову адресу"</string>
+    <string name="add_im" msgid="5158094627521120439">"Додајте нови IM налoг"</string>
+    <string name="add_address" msgid="418292312672970688">"Додајте нову адресу"</string>
+    <string name="add_note" msgid="2753771325725383279">"Додајте нову белешку"</string>
+    <string name="add_website" msgid="4312391288948517344">"Додајте нови веб сајт"</string>
+    <string name="add_event" msgid="7488781591843886426">"Додајте нови догађај"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Додајте нову везу"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"преко <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> преко <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"омиљено"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index dbe0f1b..9483563 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Standardinställning"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Ta bort foto"</string>
     <string name="noContacts" msgid="8579310973261953559">"Inga kontakter."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Inga matchande kontakter hittades."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Inga kontakter med telefonnummer."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Bara kontakter med telefoner"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> hittades"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kontakter"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoriter"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Samtalshistorik"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Samtalshistoriken är tom."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Rensa samtalshistorik"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Vill du rensa samtalshistoriken?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Rensar samtalshistorik"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI-kod"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Röstbrevlåda"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Fonetiskt efternamn"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Fonetiskt namn"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g>-kontakt"</string>
-    <string name="from_account_format" msgid="687567483928582084">"från <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Använd det här fotot"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Kontaktinformation för <xliff:g id="SOURCE">%1$s</xliff:g> kan inte redigeras i den här enheten."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Det finns ingen mer information för kontakten"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Inga kontakter har markerats."</string>
     <string name="add_field" msgid="2384260056674995230">"Lägg till ett fält"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Lägg till nytt nummer"</string>
+    <string name="add_email" msgid="175079666329862215">"Lägg till ny e-post"</string>
+    <string name="add_im" msgid="5158094627521120439">"Lägg till IM-konto"</string>
+    <string name="add_address" msgid="418292312672970688">"Lägg till ny adress"</string>
+    <string name="add_note" msgid="2753771325725383279">"Lägg till kommentar"</string>
+    <string name="add_website" msgid="4312391288948517344">"Lägg till ny webbplats"</string>
+    <string name="add_event" msgid="7488781591843886426">"Lägg till ny aktivitet"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Lägg till ny relation"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favorit"</string>
diff --git a/res/values-sw580dp-w720dp/dimens.xml b/res/values-sw580dp-w720dp/dimens.xml
deleted file mode 100644
index 399c26c..0000000
--- a/res/values-sw580dp-w720dp/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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>
-    <dimen name="editor_type_label_width">180dip</dimen>
-    <dimen name="editor_round_button_padding_left">8dip</dimen>
-    <dimen name="editor_round_button_padding_right">8dip</dimen>
-    <dimen name="editor_name_text_field_left_margin">52dip</dimen>
-    <dimen name="editor_title_label_width">150dip</dimen>
-    <dimen name="editor_interpolator_narrow_width">800dip</dimen>
-    <dimen name="editor_name_text_field_right_margin">10dip</dimen>
-</resources>
diff --git a/res/values-sw580dp-w720dp/styles.xml b/res/values-sw580dp-w720dp/styles.xml
deleted file mode 100644
index c01ef25..0000000
--- a/res/values-sw580dp-w720dp/styles.xml
+++ /dev/null
@@ -1,48 +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.
--->
-<resources>
-    <style name="ContactBrowserTheme" parent="@android:Theme.Holo.Light">
-        <item name="list_item_height">66dip</item>
-        <item name="activated_background">@drawable/list_item_activated_background</item>
-        <item name="android:windowContentOverlay">@null</item>
-        <item name="android:actionBarStyle">@style/TransparentActionBarStyle</item>
-        <item name="section_header_background">@drawable/list_title_holo</item>
-        <item name="list_item_divider">?android:attr/listDivider</item>
-        <item name="list_item_padding_top">0dip</item>
-        <item name="list_item_padding_right">24dip</item>
-        <item name="list_item_padding_bottom">0dip</item>
-        <item name="list_item_padding_left">0dip</item>
-        <item name="list_item_fading_edge_length">5dip</item>
-        <item name="list_item_gap_between_image_and_text">16dip</item>
-        <item name="list_item_gap_between_label_and_data">5dip</item>
-        <item name="list_item_call_button_padding">14dip</item>
-        <item name="list_item_vertical_divider_margin">5dip</item>
-        <item name="list_item_presence_icon_margin">30dip</item>
-        <item name="list_item_photo_size">64dip</item>
-        <item name="list_item_prefix_highlight_color">#729a27</item>
-        <item name="list_item_header_text_indent">77dip</item>
-        <item name="list_item_header_text_color">?color/section_header_text_color</item>
-        <item name="list_item_header_text_size">14sp</item>
-        <item name="contact_filter_popup_width">320dip</item>
-    </style>
-
-    <style name="ContactDetailHeaderTextView">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:singleLine">true</item>
-        <item name="android:ellipsize">end</item>
-    </style>
-</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index d545369..b7407b4 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"เริ่มต้น"</string>
     <string name="removePhoto" msgid="4898105274130284565">"นำภาพออก"</string>
     <string name="noContacts" msgid="8579310973261953559">"ไม่มีรายชื่อ"</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"ไม่พบรายชื่อที่ตรงกัน"</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"ไม่รายชื่อที่มีหมายเลขโทรศัพท์"</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"เฉพาะรายชื่อที่มีหมายเลขโทรศัพท์"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"พบ <xliff:g id="COUNT">%d</xliff:g> รายการ"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"รายชื่อ"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"รายการโปรด"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"โทรศัพท์"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"บันทึกการโทร"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"บันทึกการโทรว่างเปล่า"</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"ล้างบันทึกการโทร"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"คุณแน่ใจหรือไม่ว่าต้องการล้างบันทึกการโทรทั้งหมด"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"การล้างบันทึกการโทร"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"ข้อความเสียง"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"นามสกุลแบบออกเสียง"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"ชื่อแบบออกเสียง"</string>
     <string name="account_type_format" msgid="718948015590343010">"รายชื่อในสมุดโทรศัพท์จาก <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"จาก <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"ใช้ภาพนี้"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"ข้อมูลรายชื่อของ <xliff:g id="SOURCE">%1$s</xliff:g> แก้ไขบนอุปกรณ์นี้ไม่ได้"</string>
     <string name="no_contact_details" msgid="6754415338321837001">"ไม่มีข้อมูลเพิ่มเติมสำหรับรายชื่อนี้"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"ไม่ได้เลือกสมุดโทรศัพท์ไว้"</string>
     <string name="add_field" msgid="2384260056674995230">"เพิ่มฟิลด์อื่น"</string>
+    <string name="add_phone" msgid="4421904942555210013">"เพิ่มหมายเลขโทรศัพท์ใหม่"</string>
+    <string name="add_email" msgid="175079666329862215">"เพิ่มอีเมลใหม่"</string>
+    <string name="add_im" msgid="5158094627521120439">"เพิ่มบัญชี IM ใหม่"</string>
+    <string name="add_address" msgid="418292312672970688">"เพิ่มที่อยู่ใหม่"</string>
+    <string name="add_note" msgid="2753771325725383279">"เพิ่มบันทึกใหม่"</string>
+    <string name="add_website" msgid="4312391288948517344">"เพิ่มเว็บไซต์ใหม่"</string>
+    <string name="add_event" msgid="7488781591843886426">"เพิ่มกิจกรรมใหม่"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"เพิ่มความเกี่ยวข้องใหม่"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"ผ่านทาง <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> ผ่านทาง <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"รายการโปรด"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index d8321ce..55d5b9e 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Default"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Alisin ang larawan"</string>
     <string name="noContacts" msgid="8579310973261953559">"Walang mga contact."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Walang nakitang magkakatugmang mga contact."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Walang mga contact na may mga numero ng telepono."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Mga contact lang na may mga telepono"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> ang nakita"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Mga Contact"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Mga Paborito"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telepono"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Log ng tawag"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Walang laman ang log ng tawag."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"I-clear ang log ng tawag"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Sigurado ka bang gusto mong i-clear ang log ng tawag?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Kini-clear ang log ng tawag"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Phonetic na apelyido"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Phonetic na pangalan"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> contact"</string>
-    <string name="from_account_format" msgid="687567483928582084">"mula sa <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Gamitin ang larawang ito"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Ang impormasyon sa contact ng <xliff:g id="SOURCE">%1$s</xliff:g> ay hindi nae-edit sa device na ito."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Walang karagdagang impormasyon para sa contact na ito"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Walang napiling contact."</string>
     <string name="add_field" msgid="2384260056674995230">"Magdagdag ng ibang field"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Magdagdag ng bagong numero ng telepono"</string>
+    <string name="add_email" msgid="175079666329862215">"Magdagdag ng bagong email"</string>
+    <string name="add_im" msgid="5158094627521120439">"Magdagdag ng bagong IM account"</string>
+    <string name="add_address" msgid="418292312672970688">"Magdagdag ng bagong address"</string>
+    <string name="add_note" msgid="2753771325725383279">"Magdagdag ng bagong paalala"</string>
+    <string name="add_website" msgid="4312391288948517344">"Magdagdag ng bagong website"</string>
+    <string name="add_event" msgid="7488781591843886426">"Magdagdag ng bagong kaganapan"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Magdagdag ng bagong ugnayan"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"sa pamamagitan ng <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> sa pamamagitan ng <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"paborito"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 3857046..190eeb4 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Varsayılan"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Fotoğrafı kaldır"</string>
     <string name="noContacts" msgid="8579310973261953559">"Hiçbir kişi yok."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Eşleşen kişi bulunamadı."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Telefon numarası olan hiçbir kişi yok."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Yalnızca telefonu olan kişiler"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> kişi bulundu"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Kişiler"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Sık Kullanılanlar"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Telefon"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Çağrı kaydı"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Çağrı kaydı boş."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Çağrı kaydını temizle"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Çağrı kaydını silmek istediğinizden emin misiniz?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Çağrı kaydı temizleniyor"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Sesli Mesaj"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Fonetik soyadı"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Fonetik ad"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> kişi"</string>
-    <string name="from_account_format" msgid="687567483928582084">"kaynak: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Bu fotoğrafı kullan"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g> kişi bilgileri bu cihazda düzenlenemez."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Bu kişi için ek bilgi yok"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Seçili kişi yok."</string>
     <string name="add_field" msgid="2384260056674995230">"Başka alan ekle"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Yeni tel no ekle"</string>
+    <string name="add_email" msgid="175079666329862215">"Yeni e-posta ekle"</string>
+    <string name="add_im" msgid="5158094627521120439">"Yeni IM hesabı ekle"</string>
+    <string name="add_address" msgid="418292312672970688">"Yeni adres ekle"</string>
+    <string name="add_note" msgid="2753771325725383279">"Yeni not ekle"</string>
+    <string name="add_website" msgid="4312391288948517344">"Yeni web sitesi ekle"</string>
+    <string name="add_event" msgid="7488781591843886426">"Yeni etkinlik ekle"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Yeni ilişki ekle"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"<xliff:g id="SOURCE">%1$s</xliff:g> aracılığıyla"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="SOURCE">%2$s</xliff:g> üzerinden şu saatte: <xliff:g id="DATE">%1$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"favori"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 35576b4..f87dddc 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"За умовч."</string>
     <string name="removePhoto" msgid="4898105274130284565">"Видалити фото"</string>
     <string name="noContacts" msgid="8579310973261953559">"Немає контакт."</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"Не знайдено відпов. контактів."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Немає контактів з номерами тел."</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Лише контакти з ном. тел."</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"Знайдено <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Контакти"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Вибране"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Тел."</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Журн. викл."</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Журн. викл. порожній."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Очист. журнал викл."</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Дійсно очистити журнал викликів?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Очищення журналу викликів"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Голос. пошта"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Вимова прізвища"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Вимова імені"</string>
     <string name="account_type_format" msgid="718948015590343010">"Контакт <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"з <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Викор. це фото"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Конт. інф-цію <xliff:g id="SOURCE">%1$s</xliff:g> неможливо редагув. на цьому пристрої."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Немає додатк. інформації для цього контакту"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Контакти не вибрано."</string>
     <string name="add_field" msgid="2384260056674995230">"Додати ще одне поле"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Додати нов. номер тел."</string>
+    <string name="add_email" msgid="175079666329862215">"Додати нову ел. адресу"</string>
+    <string name="add_im" msgid="5158094627521120439">"Дод. нов.обл.зап. чату"</string>
+    <string name="add_address" msgid="418292312672970688">"Додати нову адресу"</string>
+    <string name="add_note" msgid="2753771325725383279">"Додати нову примітку"</string>
+    <string name="add_website" msgid="4312391288948517344">"Додати новий веб-сайт"</string>
+    <string name="add_event" msgid="7488781591843886426">"Додати нову подію"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Додати нові стосунки"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"через <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> через <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"вибране"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 4c78800..7571f61 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -95,6 +95,7 @@
     <string name="default_ringtone" msgid="9099988849649827972">"Mặc định"</string>
     <string name="removePhoto" msgid="4898105274130284565">"Xóa ảnh"</string>
     <string name="noContacts" msgid="8579310973261953559">"Không có liên hệ nào."</string>
+    <string name="noGroups" msgid="8614664663561385253">"Không có nhóm nào."</string>
     <string name="noMatchingContacts" msgid="4266283206853990471">"Không tìm thấy liên hệ nào phù hợp."</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Không có liên hệ nào có số điện thoại"</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"Chỉ các liên hệ có số điện thoại"</string>
@@ -134,6 +135,7 @@
     <item quantity="other" msgid="7988132539476575389">"Đã tìm thấy <xliff:g id="COUNT">%d</xliff:g>"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"Danh bạ"</string>
+    <string name="contactsGroupsLabel" msgid="2841971472518003524">"Nhóm"</string>
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"Mục ưa thích"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"Điện thoại"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"Nhật ký cuộc gọi"</string>
@@ -149,6 +151,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"Nhật ký cuộc gọi trống."</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Xoá nhật ký cuộc gọi"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"Bạn có chắc chắn muốn xoá nhật ký cuộc gọi không?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Đang xóa nhật ký cuộc gọi"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"Thư thoại"</string>
@@ -399,7 +402,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"Họ đúng phát âm"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"Tên theo phiên âm"</string>
     <string name="account_type_format" msgid="718948015590343010">"Liên hệ <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="from_account_format" msgid="687567483928582084">"từ <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"Sử dụng ảnh này"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"Không thể chỉnh sửa thông tin liên hệ <xliff:g id="SOURCE">%1$s</xliff:g> trên thiết bị này."</string>
     <string name="no_contact_details" msgid="6754415338321837001">"Không có thông tin bổ sung cho liên hệ này"</string>
@@ -431,6 +435,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"Không có địa chỉ liên hệ nào được chọn."</string>
     <string name="add_field" msgid="2384260056674995230">"Thêm trường khác"</string>
+    <string name="add_phone" msgid="4421904942555210013">"Thêm số điện thoại mới"</string>
+    <string name="add_email" msgid="175079666329862215">"Thêm email mới"</string>
+    <string name="add_im" msgid="5158094627521120439">"Thêm tài khoản IM mới"</string>
+    <string name="add_address" msgid="418292312672970688">"Thêm địa chỉ mới"</string>
+    <string name="add_note" msgid="2753771325725383279">"Thêm chú thích mới"</string>
+    <string name="add_website" msgid="4312391288948517344">"Thêm trang web mới"</string>
+    <string name="add_event" msgid="7488781591843886426">"Thêm sự kiện mới"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"Thêm mối quan hệ mới"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">" qua <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> qua <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"Yêu thích"</string>
diff --git a/res/values-sw580dp-w1000dp/dimens.xml b/res/values-xlarge-land/dimens.xml
similarity index 88%
rename from res/values-sw580dp-w1000dp/dimens.xml
rename to res/values-xlarge-land/dimens.xml
index 45da0a8..52c521e 100644
--- a/res/values-sw580dp-w1000dp/dimens.xml
+++ b/res/values-xlarge-land/dimens.xml
@@ -19,6 +19,4 @@
     <dimen name="action_bar_filter_max_width">300dip</dimen>
     <dimen name="action_bar_search_max_width">336dip</dimen>
     <dimen name="action_bar_search_spacing">32dip</dimen>
-    <dimen name="detail_header_view_margin">16dip</dimen>
-    <dimen name="detail_header_attribution_height">56dip</dimen>
 </resources>
diff --git a/res/values-sw580dp/colors.xml b/res/values-xlarge/colors.xml
similarity index 100%
rename from res/values-sw580dp/colors.xml
rename to res/values-xlarge/colors.xml
diff --git a/res/values-sw580dp/dimens.xml b/res/values-xlarge/dimens.xml
similarity index 71%
rename from res/values-sw580dp/dimens.xml
rename to res/values-xlarge/dimens.xml
index d29eba2..2d39186 100644
--- a/res/values-sw580dp/dimens.xml
+++ b/res/values-xlarge/dimens.xml
@@ -16,6 +16,13 @@
 <resources>
     <dimen name="edit_photo_size">96dip</dimen>
     <dimen name="aggregation_suggestion_icon_size">64dip</dimen>
+    <dimen name="editor_type_label_width">180dip</dimen>
+    <dimen name="editor_field_spinner_text_size">15sp</dimen>
+    <dimen name="editor_round_button_padding_left">8dip</dimen>
+    <dimen name="editor_round_button_padding_right">8dip</dimen>
+    <dimen name="editor_field_top_padding">12dip</dimen>
+    <dimen name="editor_field_bottom_padding">12dip</dimen>
+    <dimen name="detail_item_side_margin">19dip</dimen>
     <dimen name="quick_contact_width">356dip</dimen>
     <dimen name="contact_name_text_size">26sp</dimen>
     <dimen name="action_bar_filter_min_width">120dip</dimen>
@@ -23,4 +30,5 @@
     <dimen name="action_bar_search_max_width">300dip</dimen>
     <dimen name="action_bar_search_spacing">12dip</dimen>
     <dimen name="shortcut_icon_size">64dip</dimen>
+    <dimen name="list_section_height">37dip</dimen>
 </resources>
diff --git a/res/values-xlarge/donottranslate_config.xml b/res/values-xlarge/donottranslate_config.xml
new file mode 100644
index 0000000..fcb7da9
--- /dev/null
+++ b/res/values-xlarge/donottranslate_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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>
+    <bool name="config_use_two_panes">true</bool>
+</resources>
diff --git a/res/values-sw580dp/styles.xml b/res/values-xlarge/styles.xml
similarity index 86%
rename from res/values-sw580dp/styles.xml
rename to res/values-xlarge/styles.xml
index 4e5fda6..dcb32ad 100644
--- a/res/values-sw580dp/styles.xml
+++ b/res/values-xlarge/styles.xml
@@ -22,15 +22,14 @@
         <item name="section_header_background">@drawable/list_title_holo</item>
         <item name="list_item_divider">?android:attr/listDivider</item>
         <item name="list_item_padding_top">0dip</item>
-        <item name="list_item_padding_right">20dip</item>
+        <item name="list_item_padding_right">24dip</item>
         <item name="list_item_padding_bottom">0dip</item>
         <item name="list_item_padding_left">0dip</item>
-        <item name="list_item_fading_edge_length">5dip</item>
-        <item name="list_item_gap_between_image_and_text">8dip</item>
+        <item name="list_item_gap_between_image_and_text">16dip</item>
         <item name="list_item_gap_between_label_and_data">5dip</item>
         <item name="list_item_call_button_padding">14dip</item>
         <item name="list_item_vertical_divider_margin">5dip</item>
-        <item name="list_item_presence_icon_margin">18dip</item>
+        <item name="list_item_presence_icon_margin">30dip</item>
         <item name="list_item_photo_size">64dip</item>
         <item name="list_item_prefix_highlight_color">#729a27</item>
         <item name="list_item_header_text_indent">77dip</item>
@@ -58,15 +57,12 @@
         <item name="list_item_header_text_size">14sp</item>
     </style>
 
-    <style name="ContactsPreferencesTheme" parent="@android:Theme.Holo.Light">
-    </style>
-
     <style name="CustomContactListFilterTheme" parent="@android:Theme.Holo.Light.Dialog">
     </style>
 
     <style name="CustomContactListFilterView" parent="CustomContactListFilterTheme">
         <item name="android:layout_width">400dip</item>
-        <item name="android:layout_height">400dip</item>
+        <item name="android:layout_height">600dip</item>
     </style>
 
     <style name="ContactPickerLayout" parent="ContactPickerTheme">
@@ -106,4 +102,15 @@
         <item name="android:windowNoDisplay">true</item>
         <item name="android:windowIsFloating">true</item>
     </style>
+
+    <style name="ContactDetailItemType">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:textColor">@color/detail_item_type_color</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:gravity">center_vertical</item>
+        <item name="android:paddingTop">5dip</item>
+    </style>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index a4aef23..c566ad1 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"默认日历"</string>
     <string name="removePhoto" msgid="4898105274130284565">"删除照片"</string>
     <string name="noContacts" msgid="8579310973261953559">"没有联系人。"</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"未找到匹配的联系人。"</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"没有联系人拥有电话号码。"</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"仅显示有电话号码的联系人"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"找到 <xliff:g id="COUNT">%d</xliff:g> 个联系人"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"通讯录"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"收藏"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"拨号"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"通话记录"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"通话记录为空。"</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"清除通话记录"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"确定要清除通话记录吗?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"正在清除通话记录"</string>
     <string name="imei" msgid="3045126336951684285">"移动通信国际识别码"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"语音信箱"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"姓氏拼音"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"姓名拼音"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> 联系人"</string>
-    <string name="from_account_format" msgid="687567483928582084">"来自 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"使用此照片"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"<xliff:g id="SOURCE">%1$s</xliff:g> 联系人信息在此设备上不可编辑。"</string>
     <string name="no_contact_details" msgid="6754415338321837001">"无此联系人的其他信息"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"未选择联系人。"</string>
     <string name="add_field" msgid="2384260056674995230">"添加其他字段"</string>
+    <string name="add_phone" msgid="4421904942555210013">"添加新电话号码"</string>
+    <string name="add_email" msgid="175079666329862215">"添加新电子邮件地址"</string>
+    <string name="add_im" msgid="5158094627521120439">"添加新即时通讯帐户"</string>
+    <string name="add_address" msgid="418292312672970688">"添加新地址"</string>
+    <string name="add_note" msgid="2753771325725383279">"添加新备注"</string>
+    <string name="add_website" msgid="4312391288948517344">"添加新网站"</string>
+    <string name="add_event" msgid="7488781591843886426">"添加新活动"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"添加新关系"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"来源:<xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"时间:<xliff:g id="DATE">%1$s</xliff:g>,来源:<xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="description_star" msgid="2605854427360036550">"收藏"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 3cfddeb..5bb8550 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -95,6 +95,8 @@
     <string name="default_ringtone" msgid="9099988849649827972">"預設值"</string>
     <string name="removePhoto" msgid="4898105274130284565">"移除相片"</string>
     <string name="noContacts" msgid="8579310973261953559">"沒有聯絡人。"</string>
+    <!-- no translation found for noGroups (8614664663561385253) -->
+    <skip />
     <string name="noMatchingContacts" msgid="4266283206853990471">"找不到符合的聯絡人。"</string>
     <string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"沒有包含電話號碼的聯絡人資訊。"</string>
     <string name="showFilterPhones" msgid="4184858075465653970">"僅顯示有電話號碼的聯絡人"</string>
@@ -134,6 +136,8 @@
     <item quantity="other" msgid="7988132539476575389">"找到 <xliff:g id="COUNT">%d</xliff:g> 位聯絡人"</item>
   </plurals>
     <string name="contactsIconLabel" msgid="7666609097606552806">"聯絡人"</string>
+    <!-- no translation found for contactsGroupsLabel (2841971472518003524) -->
+    <skip />
     <string name="contactsFavoritesLabel" msgid="8417039765586853670">"我的最愛"</string>
     <string name="dialerIconLabel" msgid="6500826552823403796">"電話"</string>
     <string name="recentCallsIconLabel" msgid="1419116422359067949">"通話記錄"</string>
@@ -149,6 +153,7 @@
     <string name="recentCalls_empty" msgid="247053222448663107">"無通話記錄。"</string>
     <string name="clearCallLogConfirmation_title" msgid="718072843006222703">"清除通話記錄"</string>
     <string name="clearCallLogConfirmation" msgid="7625927669136267636">"確定要清除通話記錄嗎?"</string>
+    <string name="clearCallLogProgress_title" msgid="6870412675015656948">"正在清除通話記錄"</string>
     <string name="imei" msgid="3045126336951684285">"IMEI"</string>
     <string name="meid" msgid="6210568493746275750">"MEID"</string>
     <string name="voicemail" msgid="3851469869202611441">"語音留言"</string>
@@ -399,7 +404,8 @@
     <string name="name_phonetic_family" msgid="462095502140180305">"姓氏 (拼音)"</string>
     <string name="name_phonetic" msgid="4259595234312430484">"姓名拼音"</string>
     <string name="account_type_format" msgid="718948015590343010">"<xliff:g id="SOURCE">%1$s</xliff:g> 聯絡人"</string>
-    <string name="from_account_format" msgid="687567483928582084">"來自 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+    <!-- no translation found for from_account_format (4469138575127580203) -->
+    <skip />
     <string name="use_photo_as_primary" msgid="8807110122951157246">"使用此相片"</string>
     <string name="contact_read_only" msgid="1203216914575723978">"無法在此裝置編輯 <xliff:g id="SOURCE">%1$s</xliff:g> 的聯絡人資訊"</string>
     <string name="no_contact_details" msgid="6754415338321837001">"沒有此聯絡人的其他資訊"</string>
@@ -431,6 +437,14 @@
   </plurals>
     <string name="no_contacts_selected" msgid="5877803471037324613">"未選取任何聯絡人。"</string>
     <string name="add_field" msgid="2384260056674995230">"新增其他欄位"</string>
+    <string name="add_phone" msgid="4421904942555210013">"新增電話號碼"</string>
+    <string name="add_email" msgid="175079666329862215">"新增電子郵件"</string>
+    <string name="add_im" msgid="5158094627521120439">"新增即時通訊帳戶"</string>
+    <string name="add_address" msgid="418292312672970688">"新增地址"</string>
+    <string name="add_note" msgid="2753771325725383279">"新增附註"</string>
+    <string name="add_website" msgid="4312391288948517344">"新增網站"</string>
+    <string name="add_event" msgid="7488781591843886426">"新增活動"</string>
+    <string name="add_relationship" msgid="3083762399737240006">"新增關係"</string>
     <string name="contact_status_update_attribution" msgid="752179367353018597">"透過 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
     <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> (透過 <xliff:g id="SOURCE">%2$s</xliff:g>)"</string>
     <string name="description_star" msgid="2605854427360036550">"我的最愛"</string>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 69f92ad..ace8869 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -21,10 +21,16 @@
     <color name="quickcontact_disambig_divider">#afafaf</color>
 
     <color name="edit_divider">#ff666666</color>
+
+    <!-- Color of the background of the contact detail and editor pages -->
+    <color name="background_primary">#FFFFFF</color>
+
     <color name="background_secondary">#ff202020</color>
 
     <color name="translucent_search_background">#cc000000</color>
 
+    <color name="account_selection_background">#ff000000</color>
+
     <!-- Color used for the letter in the A-Z section header -->
     <color name="section_header_text_color">#ff999999</color>
 
@@ -39,4 +45,7 @@
 
     <!-- Color of the text indicating the type of entry (e.g. Home, Work etc) -->
     <color name="detail_item_type_color">#B4B4B4</color>
+
+    <!-- Color of the text indicating the type of entry (e.g. Home, Work etc) -->
+    <color name="detail_header_view_text_color">#FFFFFF</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3aca306..ff7d873 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="edit_photo_size">76dip</dimen>
+    <dimen name="edit_photo_size">120dip</dimen>
 
     <!-- The height of the ScrollingTabWidget -->
     <dimen name="tab_height">40dip</dimen>
@@ -26,6 +26,9 @@
     <dimen name="aggregation_suggestion_icon_size">40dip</dimen>
 
     <dimen name="account_selector_popup_width">400dip</dimen>
+    <dimen name="account_selector_icon_size">30dip</dimen>
+    <dimen name="account_selector_min_item_height">48dip</dimen>
+    <dimen name="account_selector_horizontal_margin">6dip</dimen>
 
     <dimen name="photo_action_popup_width">400dip</dimen>
 
@@ -35,38 +38,53 @@
     <dimen name="quick_contact_width">352dip</dimen>
 
     <!-- Padding of the rounded plus/minus/expand/collapse buttons in the editor  -->
-    <dimen name="editor_round_button_padding_left">2dip</dimen>
-    <dimen name="editor_round_button_padding_right">2dip</dimen>
+    <dimen name="editor_round_button_padding_left">4dip</dimen>
+    <dimen name="editor_round_button_padding_right">4dip</dimen>
     <dimen name="editor_round_button_padding_top">8dip</dimen>
     <dimen name="editor_round_button_padding_bottom">8dip</dimen>
 
+    <!-- Font size for the structured name in the contact detail page -->
+    <dimen name="editor_structured_name_text_size">25sp</dimen>
+
     <!-- Width of the Type-Label in the Editor -->
-    <dimen name="editor_type_label_width">140dip</dimen>
-    
+    <dimen name="editor_type_label_width">120dip</dimen>
+
+    <!-- Left padding of a field in the Editor -->
+    <dimen name="editor_field_left_padding">5dip</dimen>
+
+    <!-- Top padding of a field in the Editor -->
+    <dimen name="editor_field_top_padding">10dip</dimen>
+
+    <!-- Bottom padding of a field in the Editor -->
+    <dimen name="editor_field_bottom_padding">5dip</dimen>
+
     <!-- Minimum height of a row in the Editor -->
     <dimen name="editor_min_line_item_height">48dip</dimen>
 
-    <!-- Left margin of the name text input fields and the photo in the contact editor -->
-    <dimen name="editor_name_text_field_left_margin">25dip</dimen>
+    <!-- Font size used for the value of a field in the contact editor. -->
+    <dimen name="editor_field_text_size">18sp</dimen>
 
-    <!-- Right margin of the text input fields in the contact editor -->
-    <dimen name="editor_name_text_field_right_margin">0dip</dimen>
+    <!-- Font size used for the title of a field in the contact editor. -->
+    <dimen name="editor_field_title_text_size">15sp</dimen>
 
-    <!-- Width of the title labels in the contact editor -->
-    <dimen name="editor_title_label_width">120dip</dimen>
+    <!-- Font size for the entries in a spinner in the contact editor. -->
+    <dimen name="editor_field_spinner_text_size">10sp</dimen>
 
-    <!-- Interpolator layout narrow width value of the contact editor -->
-    <dimen name="editor_interpolator_narrow_width">600dip</dimen>
+    <!-- Left and right padding for a contact detail item -->
+    <dimen name="detail_item_icon_margin">10dip</dimen>
 
-    <!-- Height of the attribution text view in the contact detail header view -->
-    <dimen name="detail_header_attribution_height">40dip</dimen>
-
-    <!-- Margin between the photo, the star, and text in the contact detail header view -->
-    <dimen name="detail_header_view_margin">8dip</dimen>
+    <!-- Left and right padding for a contact detail item -->
+    <dimen name="detail_item_side_margin">10dip</dimen>
 
     <!-- Minimum height of a row in the contact detail -->
     <dimen name="detail_min_line_item_height">48dip</dimen>
 
+    <!-- Width of a contact detail item type (i.e. Nickname or Website). -->
+    <dimen name="detail_item_type_width">164dip</dimen>
+
+    <!-- Font size for the display name in header of the contact detail page -->
+    <dimen name="detail_header_name_text_size">30sp</dimen>
+
     <!-- Padding to be used between a visible scrollbar and the contact list -->
     <dimen name="list_visible_scrollbar_padding">40dip</dimen>
 
@@ -76,9 +94,6 @@
     <!-- Font size used for the social status in the widget -->
     <dimen name="widget_text_size_snippet">13sp</dimen>
 
-    <!-- Font size used for the contact name in the detail and the editor -->
-    <dimen name="contact_name_text_size">18sp</dimen>
-
     <!-- Minimum width of the filter selector in the action bar -->
     <dimen name="action_bar_filter_min_width">100dip</dimen>
 
@@ -93,4 +108,7 @@
 
     <!-- Size of the shortcut icon. 0dip means: use the system default -->    
     <dimen name="shortcut_icon_size">0dip</dimen>
+
+    <!-- Height of list sections (A, B, C) that show the first character of the contacts -->
+    <dimen name="list_section_height">25dip</dimen>
 </resources>
diff --git a/res/values/donottranslate_config.xml b/res/values/donottranslate_config.xml
index f5efa13..e310953 100644
--- a/res/values/donottranslate_config.xml
+++ b/res/values/donottranslate_config.xml
@@ -99,4 +99,8 @@
 
     <!-- If true, phonetic name is included in the contact editor by default -->
     <bool name="config_editor_include_phonetic_name">false</bool>
+
+    <!-- If true, Contacts uses two panes: List and Detail. If false, Details are
+         shown in their own screens. This flag must be in sync with the layout definitions. -->
+    <bool name="config_use_two_panes">false</bool>
 </resources>
diff --git a/res/values/ids.xml b/res/values/ids.xml
index 1a553d1..2de327a 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -18,7 +18,6 @@
     <item type="id" name="data"/>
     <item type="id" name="header_phones"/>
     <item type="id" name="dialog_sync_add"/>
-    <item type="id" name="dialog_import_export"/>
 
     <!-- For vcard.ImportVCardActivity -->
     <item type="id" name="dialog_searching_vcard"/>
@@ -39,9 +38,6 @@
     <item type="id" name="dialog_delete_contact_confirmation"/>
     <item type="id" name="dialog_delete_contact_loader_id" />
 
-    <!-- For ImportExportInteraction -->
-    <item type="id" name="dialog_import_export_options"/>
-
     <!-- For ExportVCardActivity -->
     <item type="id" name="dialog_export_confirmation"/>
     <item type="id" name="dialog_exporting_vcard"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ff69899..5605d25 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -264,6 +264,9 @@
     <!-- The text displayed when the contacts list is empty while displaying all contacts -->
     <string name="noContacts">No contacts.</string>
 
+    <!-- The text displayed when the groups list is empty while displaying all groups [CHAR LIMIT=NONE] -->
+    <string name="noGroups">No groups.</string>
+
     <!-- The text displayed when the contacts list is empty while displaying results after searching contacts -->
     <string name="noMatchingContacts">No matching contacts found.</string>
 
@@ -349,6 +352,9 @@
     <!-- The description text for the contacts tab. Space is limited for this string, so the shorter the better -->
     <string name="contactsIconLabel">Contacts</string>
 
+    <!-- The description text for the groups tab. Space is limited for this string, so the shorter the better -->
+    <string name="contactsGroupsLabel">Groups</string>
+
     <!-- The description text for the favorites tab. Space is limited for this string, so the shorter the better -->
     <string name="contactsFavoritesLabel">Favorites</string>
 
@@ -388,6 +394,9 @@
     <!-- Confirmation dialog for clearing the call log  -->
     <string name="clearCallLogConfirmation">Are you sure you want to clear the call log?</string>
 
+    <!-- Title of the "Clearing call log" progress-dialog [CHAR LIMIT=35] -->
+    <string name="clearCallLogProgress_title">Clearing call log</string>
+
     <!-- The title of a dialog that displays the IMEI of the phone -->
     <string name="imei">IMEI</string>
 
@@ -1223,7 +1232,7 @@
     <string name="account_type_format"><xliff:g id="source" example="Gmail">%1$s</xliff:g> contact</string>
 
     <!-- String describing which account a contact came from when editing it -->
-    <string name="from_account_format">from <xliff:g id="source" example="user@gmail.com">%1$s</xliff:g></string>
+    <string name="from_account_format"><xliff:g id="source" example="user@gmail.com">%1$s</xliff:g></string>
 
     <!-- Checkbox asking the user if they want to display a particular photo for a contact -->
     <string name="use_photo_as_primary">Use this photo</string>
@@ -1316,6 +1325,30 @@
     <!-- The add field button shown in the editor under each editable Raw Contact [CHAR LIMIT=30] -->
     <string name="add_field">Add another field</string>
 
+    <!-- The editable field hint text to add a new phone number to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+    <string name="add_phone">Add new phone number</string>
+
+    <!-- The editable field hint text to add a new email to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+    <string name="add_email">Add new email</string>
+
+    <!-- The editable field hint text to add a new IM account to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+    <string name="add_im">Add new IM account</string>
+
+    <!-- The editable field hint text to add a new postal address to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+    <string name="add_address">Add new address</string>
+
+    <!-- The editable field hint text to add a new note to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+    <string name="add_note">Add new note</string>
+
+    <!-- The editable field hint text to add a new website to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+    <string name="add_website">Add new website</string>
+
+    <!-- The editable field hint text to add a new event to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+    <string name="add_event">Add new event</string>
+
+    <!-- The editable field hint text to add a relationship field to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+    <string name="add_relationship">Add new relationship</string>
+
     <!-- Attbution of a contact status update, when the time of update is unknown -->
     <string name="contact_status_update_attribution">via <xliff:g id="source" example="Google Talk">%1$s</xliff:g></string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 1b494af..0582fd4 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -14,18 +14,37 @@
      limitations under the License.
 -->
 <resources>
-    <style name="DialtactsTheme" parent="@android:Theme">
-        <item name="android:windowNoTitle">true</item>
+    <style name="DialtactsTheme" parent="android:Theme.Holo.Light">
         <item name="android:windowContentOverlay">@null</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>
+        <item name="list_section_header_height">32dip</item>
+        <item name="list_item_divider">@drawable/list_item_divider</item>
+        <item name="list_item_padding_top">4dip</item>
+        <item name="list_item_padding_right">11dip</item>
+        <item name="list_item_padding_bottom">4dip</item>
+        <item name="list_item_padding_left">4dip</item>
+        <item name="list_item_gap_between_image_and_text">8dip</item>
+        <item name="list_item_gap_between_label_and_data">5dip</item>
+        <item name="list_item_call_button_padding">14dip</item>
+        <item name="list_item_vertical_divider_margin">5dip</item>
+        <item name="list_item_presence_icon_margin">5dip</item>
+        <item name="list_item_photo_size">56dip</item>
+        <item name="list_item_prefix_highlight_color">#729a27</item>
+        <item name="list_item_header_text_indent">56dip</item>
+        <item name="list_item_header_text_color">?color/section_header_text_color</item>
+        <item name="list_item_header_text_size">14sp</item>
+        <item name="contact_filter_popup_width">320dip</item>
     </style>
 
-    <style name="CallDetailActivityTheme" parent="android:Theme.NoTitleBar">
+    <style name="CallDetailActivityTheme" parent="android:Theme.Holo.Light">
         <item name="android:windowContentOverlay">@null</item>
     </style>
-    <style name="ContactDetailActivityTheme" parent="android:Theme.NoTitleBar">
+    <style name="ContactDetailActivityTheme" parent="android:Theme.Holo.Light">
         <item name="android:windowContentOverlay">@null</item>
     </style>
-    <style name="ContactEditorActivityTheme" parent="android:Theme.NoTitleBar">
+    <style name="ContactEditorActivityTheme" parent="android:Theme.Holo.Light">
         <item name="android:windowContentOverlay">@null</item>
     </style>
 
@@ -38,38 +57,19 @@
         <item name="android:windowIsFloating">true</item>
     </style>
 
-    <style name="FullyTranslucent" parent="android:Theme.Translucent.NoTitleBar">
-        <item name="android:windowContentOverlay">@null</item>
+    <style name="Theme">
     </style>
 
-    <style name="FullyTranslucent.QuickContact">
-        <!-- This is a hack because we want to be able to animate away the
-             QuickContact window, and we close its containing activity at the
-             same time.  So put in a dummy animation so this guy sticks around
-             while the fast track window is animating. -->
-        <item name="android:windowAnimationStyle">@style/DummyAnimation</item>
-    </style>
-
-    <style name="QuickContact" parent="@android:Theme.Holo.Light">
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowFrame">@null</item>
+    <style name="Theme.QuickContact" parent="@android:style/Theme.Holo.Light">
         <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:windowIsFloating">true</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowFrame">@null</item>
         <item name="android:windowContentOverlay">@null</item>
-        <!-- TODO: create our own animation style in framework -->
-        <!--
-        <item name="android:windowAnimationStyle">@*android:style/Animation.ZoomButtons</item>
-        -->
-    </style>
-
-    <style name="QuickContactAboveAnimation">
-        <item name="android:windowEnterAnimation">@anim/quickcontact_above_enter</item>
-        <item name="android:windowExitAnimation">@anim/quickcontact_above_exit</item>
-    </style>
-
-    <style name="QuickContactBelowAnimation">
-        <item name="android:windowEnterAnimation">@anim/quickcontact_below_enter</item>
-        <item name="android:windowExitAnimation">@anim/quickcontact_below_exit</item>
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:windowIsFloating">false</item>
+        <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowNoTitle">true</item>
     </style>
 
     <style name="ContactsSearchAnimation">
@@ -95,7 +95,6 @@
         <attr name="list_item_padding_right" format="dimension"/>
         <attr name="list_item_padding_bottom" format="dimension"/>
         <attr name="list_item_padding_left" format="dimension"/>
-        <attr name="list_item_fading_edge_length" format="dimension"/>
         <attr name="list_item_gap_between_image_and_text" format="dimension"/>
         <attr name="list_item_gap_between_label_and_data" format="dimension"/>
         <attr name="list_item_call_button_padding" format="dimension"/>
@@ -111,14 +110,37 @@
     <style name="ContactBrowserTheme" parent="@android:Theme">
         <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/section_header</item>
+        <item name="section_header_background">@drawable/list_title_holo</item>
         <item name="list_section_header_height">32dip</item>
         <item name="list_item_divider">@drawable/list_item_divider</item>
         <item name="list_item_padding_top">4dip</item>
         <item name="list_item_padding_right">11dip</item>
         <item name="list_item_padding_bottom">4dip</item>
         <item name="list_item_padding_left">4dip</item>
-        <item name="list_item_fading_edge_length">5dip</item>
+        <item name="list_item_gap_between_image_and_text">8dip</item>
+        <item name="list_item_gap_between_label_and_data">5dip</item>
+        <item name="list_item_call_button_padding">14dip</item>
+        <item name="list_item_vertical_divider_margin">5dip</item>
+        <item name="list_item_presence_icon_margin">5dip</item>
+        <item name="list_item_photo_size">56dip</item>
+        <item name="list_item_prefix_highlight_color">#729a27</item>
+        <item name="list_item_header_text_indent">56dip</item>
+        <item name="list_item_header_text_color">?color/section_header_text_color</item>
+        <item name="list_item_header_text_size">14sp</item>
+        <item name="contact_filter_popup_width">320dip</item>
+    </style>
+
+    <!-- TODO: Clean up this file so themes aren't copied. -->
+    <style name="GroupDetailTheme" parent="@android:Theme">
+        <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>
+        <item name="list_section_header_height">32dip</item>
+        <item name="list_item_divider">@drawable/list_item_divider</item>
+        <item name="list_item_padding_top">4dip</item>
+        <item name="list_item_padding_right">11dip</item>
+        <item name="list_item_padding_bottom">4dip</item>
+        <item name="list_item_padding_left">4dip</item>
         <item name="list_item_gap_between_image_and_text">8dip</item>
         <item name="list_item_gap_between_label_and_data">5dip</item>
         <item name="list_item_call_button_padding">14dip</item>
@@ -154,7 +176,7 @@
     <style name="JoinContactActivityTheme" parent="ContactPickerTheme" >
     </style>
 
-    <style name="ContactsPreferencesTheme" parent="@android:Theme">
+    <style name="ContactsPreferencesTheme" parent="@android:Theme.Holo.Light">
     </style>
 
     <style name="CustomContactListFilterTheme" parent="@android:Theme">
@@ -211,8 +233,14 @@
         <item name="android:layout_width">match_parent</item>
     </style>
 
-    <style name="ContactDetailHeaderTextView">
-        <item name="android:layout_width">match_parent</item>
+    <style name="ContactDetailItemType">
+        <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">wrap_content</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
+        <item name="android:textColor">@color/detail_item_type_color</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:gravity">center_vertical</item>
+        <item name="android:paddingTop">5dip</item>
     </style>
 </resources>
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index f4baf3b..4b0bbaa 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -93,6 +93,7 @@
         private final long mPhotoId;
         private final String mPhotoUri;
         private final String mDisplayName;
+        private final String mAltDisplayName;
         private final String mPhoneticName;
         private final boolean mStarred;
         private final Integer mPresence;
@@ -131,6 +132,7 @@
             mPhotoId = -1;
             mPhotoUri = null;
             mDisplayName = null;
+            mAltDisplayName = null;
             mPhoneticName = null;
             mStarred = false;
             mPresence = null;
@@ -145,8 +147,9 @@
          */
         private Result(Uri uri, Uri lookupUri, long directoryId, String lookupKey, long id,
                 long nameRawContactId, int displayNameSource, long photoId, String photoUri,
-                String displayName, String phoneticName, boolean starred, Integer presence,
-                String status, Long statusTimestamp, Integer statusLabel, String statusResPackage) {
+                String displayName, String altDisplayName, String phoneticName, boolean starred,
+                Integer presence, String status, Long statusTimestamp, Integer statusLabel,
+                String statusResPackage) {
             mLookupUri = lookupUri;
             mUri = uri;
             mDirectoryId = directoryId;
@@ -159,6 +162,7 @@
             mPhotoId = photoId;
             mPhotoUri = photoUri;
             mDisplayName = displayName;
+            mAltDisplayName = altDisplayName;
             mPhoneticName = phoneticName;
             mStarred = starred;
             mPresence = presence;
@@ -179,6 +183,7 @@
             mPhotoId = from.mPhotoId;
             mPhotoUri = from.mPhotoUri;
             mDisplayName = from.mDisplayName;
+            mAltDisplayName = from.mAltDisplayName;
             mPhoneticName = from.mPhoneticName;
             mStarred = from.mStarred;
             mPresence = from.mPresence;
@@ -257,6 +262,10 @@
             return mDisplayName;
         }
 
+        public String getAltDisplayName() {
+            return mAltDisplayName;
+        }
+
         public String getPhoneticName() {
             return mPhoneticName;
         }
@@ -380,6 +389,7 @@
                 Contacts.DISPLAY_NAME_SOURCE,
                 Contacts.LOOKUP_KEY,
                 Contacts.DISPLAY_NAME,
+                Contacts.DISPLAY_NAME_ALTERNATIVE,
                 Contacts.PHONETIC_NAME,
                 Contacts.PHOTO_ID,
                 Contacts.STARRED,
@@ -447,67 +457,68 @@
         public final static int DISPLAY_NAME_SOURCE = 1;
         public final static int LOOKUP_KEY = 2;
         public final static int DISPLAY_NAME = 3;
-        public final static int PHONETIC_NAME = 4;
-        public final static int PHOTO_ID = 5;
-        public final static int STARRED = 6;
-        public final static int CONTACT_PRESENCE = 7;
-        public final static int CONTACT_STATUS = 8;
-        public final static int CONTACT_STATUS_TIMESTAMP = 9;
-        public final static int CONTACT_STATUS_RES_PACKAGE = 10;
-        public final static int CONTACT_STATUS_LABEL = 11;
-        public final static int CONTACT_ID = 12;
-        public final static int RAW_CONTACT_ID = 13;
+        public final static int ALT_DISPLAY_NAME = 4;
+        public final static int PHONETIC_NAME = 5;
+        public final static int PHOTO_ID = 6;
+        public final static int STARRED = 7;
+        public final static int CONTACT_PRESENCE = 8;
+        public final static int CONTACT_STATUS = 9;
+        public final static int CONTACT_STATUS_TIMESTAMP = 10;
+        public final static int CONTACT_STATUS_RES_PACKAGE = 11;
+        public final static int CONTACT_STATUS_LABEL = 12;
+        public final static int CONTACT_ID = 13;
+        public final static int RAW_CONTACT_ID = 14;
 
-        public final static int ACCOUNT_NAME = 14;
-        public final static int ACCOUNT_TYPE = 15;
-        public final static int DIRTY = 16;
-        public final static int VERSION = 17;
-        public final static int SOURCE_ID = 18;
-        public final static int SYNC1 = 19;
-        public final static int SYNC2 = 20;
-        public final static int SYNC3 = 21;
-        public final static int SYNC4 = 22;
-        public final static int DELETED = 23;
-        public final static int IS_RESTRICTED = 24;
-        public final static int NAME_VERIFIED = 25;
+        public final static int ACCOUNT_NAME = 15;
+        public final static int ACCOUNT_TYPE = 16;
+        public final static int DIRTY = 17;
+        public final static int VERSION = 18;
+        public final static int SOURCE_ID = 19;
+        public final static int SYNC1 = 20;
+        public final static int SYNC2 = 21;
+        public final static int SYNC3 = 22;
+        public final static int SYNC4 = 23;
+        public final static int DELETED = 24;
+        public final static int IS_RESTRICTED = 25;
+        public final static int NAME_VERIFIED = 26;
 
-        public final static int DATA_ID = 26;
-        public final static int DATA1 = 27;
-        public final static int DATA2 = 28;
-        public final static int DATA3 = 29;
-        public final static int DATA4 = 30;
-        public final static int DATA5 = 31;
-        public final static int DATA6 = 32;
-        public final static int DATA7 = 33;
-        public final static int DATA8 = 34;
-        public final static int DATA9 = 35;
-        public final static int DATA10 = 36;
-        public final static int DATA11 = 37;
-        public final static int DATA12 = 38;
-        public final static int DATA13 = 39;
-        public final static int DATA14 = 40;
-        public final static int DATA15 = 41;
-        public final static int DATA_SYNC1 = 42;
-        public final static int DATA_SYNC2 = 43;
-        public final static int DATA_SYNC3 = 44;
-        public final static int DATA_SYNC4 = 45;
-        public final static int DATA_VERSION = 46;
-        public final static int IS_PRIMARY = 47;
-        public final static int IS_SUPERPRIMARY = 48;
-        public final static int MIMETYPE = 49;
-        public final static int RES_PACKAGE = 50;
+        public final static int DATA_ID = 27;
+        public final static int DATA1 = 28;
+        public final static int DATA2 = 29;
+        public final static int DATA3 = 30;
+        public final static int DATA4 = 31;
+        public final static int DATA5 = 32;
+        public final static int DATA6 = 33;
+        public final static int DATA7 = 34;
+        public final static int DATA8 = 35;
+        public final static int DATA9 = 36;
+        public final static int DATA10 = 37;
+        public final static int DATA11 = 38;
+        public final static int DATA12 = 39;
+        public final static int DATA13 = 40;
+        public final static int DATA14 = 41;
+        public final static int DATA15 = 42;
+        public final static int DATA_SYNC1 = 43;
+        public final static int DATA_SYNC2 = 44;
+        public final static int DATA_SYNC3 = 45;
+        public final static int DATA_SYNC4 = 46;
+        public final static int DATA_VERSION = 47;
+        public final static int IS_PRIMARY = 48;
+        public final static int IS_SUPERPRIMARY = 49;
+        public final static int MIMETYPE = 50;
+        public final static int RES_PACKAGE = 51;
 
-        public final static int GROUP_SOURCE_ID = 51;
+        public final static int GROUP_SOURCE_ID = 52;
 
-        public final static int PRESENCE = 52;
-        public final static int CHAT_CAPABILITY = 53;
-        public final static int STATUS = 54;
-        public final static int STATUS_RES_PACKAGE = 55;
-        public final static int STATUS_ICON = 56;
-        public final static int STATUS_LABEL = 57;
-        public final static int STATUS_TIMESTAMP = 58;
+        public final static int PRESENCE = 53;
+        public final static int CHAT_CAPABILITY = 54;
+        public final static int STATUS = 55;
+        public final static int STATUS_RES_PACKAGE = 56;
+        public final static int STATUS_ICON = 57;
+        public final static int STATUS_LABEL = 58;
+        public final static int STATUS_TIMESTAMP = 59;
 
-        public final static int PHOTO_URI = 59;
+        public final static int PHOTO_URI = 60;
     }
 
     private static class DirectoryQuery {
@@ -699,6 +710,7 @@
             final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
             final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
             final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
+            final String altDisplayName = cursor.getString(ContactQuery.ALT_DISPLAY_NAME);
             final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
             final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
             final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
@@ -726,8 +738,8 @@
 
             return new Result(contactUri, lookupUri, directoryId, lookupKey, contactId,
                     nameRawContactId, displayNameSource, photoId, photoUri, displayName,
-                    phoneticName, starred, presence, status, statusTimestamp, statusLabel,
-                    statusResPackage);
+                    altDisplayName, phoneticName, starred, presence, status, statusTimestamp,
+                    statusLabel, statusResPackage);
         }
 
         /**
diff --git a/src/com/android/contacts/ContactPhotoManager.java b/src/com/android/contacts/ContactPhotoManager.java
index 914b697..150856a 100644
--- a/src/com/android/contacts/ContactPhotoManager.java
+++ b/src/com/android/contacts/ContactPhotoManager.java
@@ -625,12 +625,13 @@
             try {
                 Uri uri = Contacts.CONTENT_URI.buildUpon().appendQueryParameter(
                         ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+                        .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
+                                String.valueOf(MAX_PHOTOS_TO_PRELOAD))
                         .build();
                 cursor = mResolver.query(uri, new String[] { Contacts.PHOTO_ID },
                         Contacts.PHOTO_ID + " NOT NULL AND " + Contacts.PHOTO_ID + "!=0",
                         null,
-                        Contacts.STARRED + " DESC, " + Contacts.LAST_TIME_CONTACTED + " DESC"
-                                + " LIMIT " + MAX_PHOTOS_TO_PRELOAD);
+                        Contacts.STARRED + " DESC, " + Contacts.LAST_TIME_CONTACTED + " DESC");
 
                 if (cursor != null) {
                     while (cursor.moveToNext()) {
diff --git a/src/com/android/contacts/ContactsUtils.java b/src/com/android/contacts/ContactsUtils.java
index 0e75a7f..76cbc7d 100644
--- a/src/com/android/contacts/ContactsUtils.java
+++ b/src/com/android/contacts/ContactsUtils.java
@@ -16,6 +16,11 @@
 
 package com.android.contacts;
 
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.PhoneNumberUtil.MatchType;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+
 import android.content.Context;
 import android.content.Intent;
 import android.location.CountryDetector;
@@ -114,9 +119,19 @@
             if (dataParts1.length != dataParts2.length) {
                 return false;
             }
+            PhoneNumberUtil util = PhoneNumberUtil.getInstance();
             for (int i = 0; i < dataParts1.length; i++) {
-                if (!PhoneNumberUtils.compare(context, dataParts1[i], dataParts2[i])) {
-                    return false;
+                try {
+                    PhoneNumber phoneNumber1 = util.parse(dataParts1[i], "ZZ" /* Unknown */);
+                    PhoneNumber phoneNumber2 = util.parse(dataParts2[i], "ZZ" /* Unknown */);
+                    MatchType matchType = util.isNumberMatch(phoneNumber1, phoneNumber2);
+                    if (matchType != MatchType.SHORT_NSN_MATCH) {
+                        return false;
+                    }
+                } catch (NumberParseException e) {
+                    if (!TextUtils.equals(dataParts1[i], dataParts2[i])) {
+                        return false;
+                    }
                 }
             }
 
diff --git a/src/com/android/contacts/DialtactsActivity.java b/src/com/android/contacts/DialtactsActivity.java
deleted file mode 100644
index dc69194..0000000
--- a/src/com/android/contacts/DialtactsActivity.java
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import com.android.contacts.activities.ContactsFrontDoor;
-import com.android.contacts.activities.ContactBrowserActivity;
-import com.android.internal.telephony.ITelephony;
-
-import android.app.Activity;
-import android.app.TabActivity;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.Intents.UI;
-import android.util.Log;
-import android.view.Window;
-import android.widget.TabHost;
-
-/**
- * The dialer activity that has one tab with the virtual 12key
- * dialer, a tab with recent calls in it, a tab with the contacts and
- * a tab with the favorite. This is the container and the tabs are
- * embedded using intents.
- * The dialer tab's title is 'phone', a more common name (see strings.xml).
- */
-public class DialtactsActivity extends TabActivity implements TabHost.OnTabChangeListener {
-    private static final String TAG = "Dailtacts";
-
-    private static final int TAB_INDEX_DIALER = 0;
-    private static final int TAB_INDEX_CALL_LOG = 1;
-    private static final int TAB_INDEX_CONTACTS = 2;
-    private static final int TAB_INDEX_FAVORITES = 3;
-
-    static final String EXTRA_IGNORE_STATE = "ignore-state";
-
-    /** Name of the dialtacts shared preferences */
-    static final String PREFS_DIALTACTS = "dialtacts";
-    /** If true, when handling the contacts intent the favorites tab will be shown instead */
-    static final String PREF_FAVORITES_AS_CONTACTS = "favorites_as_contacts";
-    static final boolean PREF_FAVORITES_AS_CONTACTS_DEFAULT = false;
-
-    /** Last manually selected tab index */
-    private static final String PREF_LAST_MANUALLY_SELECTED_TAB = "last_manually_selected_tab";
-    private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER;
-
-    private TabHost mTabHost;
-    private String mFilterText;
-    private Uri mDialUri;
-
-    /**
-     * The index of the tab that has last been manually selected (the user clicked on a tab).
-     * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
-     */
-    private int mLastManuallySelectedTab;
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        final Intent intent = getIntent();
-        fixIntent(intent);
-
-        requestWindowFeature(Window.FEATURE_NO_TITLE);
-        setContentView(R.layout.dialer_activity);
-
-        mTabHost = getTabHost();
-        mTabHost.setOnTabChangedListener(this);
-
-        // Setup the tabs
-        setupDialerTab();
-        setupCallLogTab();
-        setupContactsTab();
-        setupFavoritesTab();
-
-        // Load the last manually loaded tab
-        final SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
-        mLastManuallySelectedTab = prefs.getInt(PREF_LAST_MANUALLY_SELECTED_TAB,
-                PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT);
-
-        setCurrentTab(intent);
-
-        if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
-                && icicle == null) {
-            setupFilterText(intent);
-        }
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-
-        final int currentTabIndex = mTabHost.getCurrentTab();
-        final SharedPreferences.Editor editor =
-                getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE).edit();
-        if (currentTabIndex == TAB_INDEX_CONTACTS || currentTabIndex == TAB_INDEX_FAVORITES) {
-            editor.putBoolean(PREF_FAVORITES_AS_CONTACTS, currentTabIndex == TAB_INDEX_FAVORITES);
-        }
-        editor.putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedTab);
-
-        editor.apply();
-    }
-
-    private void fixIntent(Intent intent) {
-        // This should be cleaned up: the call key used to send an Intent
-        // that just said to go to the recent calls list.  It now sends this
-        // abstract action, but this class hasn't been rewritten to deal with it.
-        if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
-            intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
-            intent.putExtra("call_key", true);
-            setIntent(intent);
-        }
-    }
-
-    private void setupCallLogTab() {
-        // Force the class since overriding tab entries doesn't work
-        Intent intent = new Intent("com.android.phone.action.RECENT_CALLS");
-        intent.setClass(this, RecentCallsListActivity.class);
-
-        mTabHost.addTab(mTabHost.newTabSpec("call_log")
-                .setIndicator(getString(R.string.recentCallsIconLabel),
-                        getResources().getDrawable(R.drawable.ic_tab_recent))
-                .setContent(intent));
-    }
-
-    private void setupDialerTab() {
-        Intent intent = new Intent("com.android.phone.action.TOUCH_DIALER");
-        intent.setClass(this, TwelveKeyDialer.class);
-
-        mTabHost.addTab(mTabHost.newTabSpec("dialer")
-                .setIndicator(getString(R.string.dialerIconLabel),
-                        getResources().getDrawable(R.drawable.ic_tab_dialer))
-                .setContent(intent));
-    }
-
-    private void setupContactsTab() {
-        Intent intent = new Intent(UI.LIST_DEFAULT);
-        intent.setClass(this, ContactBrowserActivity.class);
-
-        mTabHost.addTab(mTabHost.newTabSpec("contacts")
-                .setIndicator(getText(R.string.contactsIconLabel),
-                        getResources().getDrawable(R.drawable.ic_tab_contacts))
-                .setContent(intent));
-    }
-
-    private void setupFavoritesTab() {
-        Intent intent = new Intent(UI.LIST_STREQUENT_ACTION);
-        intent.setClass(this, ContactBrowserActivity.class);
-
-        mTabHost.addTab(mTabHost.newTabSpec("favorites")
-                .setIndicator(getString(R.string.contactsFavoritesLabel),
-                        getResources().getDrawable(R.drawable.ic_tab_starred))
-                .setContent(intent));
-    }
-
-    /**
-     * Returns true if the intent is due to hitting the green send key while in a call.
-     *
-     * @param intent the intent that launched this activity
-     * @param recentCallsRequest true if the intent is requesting to view recent calls
-     * @return true if the intent is due to hitting the green send key while in a call
-     */
-    private boolean isSendKeyWhileInCall(final Intent intent, final boolean recentCallsRequest) {
-        // If there is a call in progress go to the call screen
-        if (recentCallsRequest) {
-            final boolean callKey = intent.getBooleanExtra("call_key", false);
-
-            try {
-                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
-                if (callKey && phone != null && phone.showCallScreen()) {
-                    return true;
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to handle send while in call", e);
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Sets the current tab based on the intent's request type
-     *
-     * @param intent Intent that contains information about which tab should be selected
-     */
-    private void setCurrentTab(Intent intent) {
-        // If we got here by hitting send and we're in call forward along to the in-call activity
-        final boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.getType());
-        if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
-            finish();
-            return;
-        }
-
-        // Dismiss menu provided by any children activities
-        Activity activity = getLocalActivityManager().
-                getActivity(mTabHost.getCurrentTabTag());
-        if (activity != null) {
-            activity.closeOptionsMenu();
-        }
-
-        // Tell the children activities that they should ignore any possible saved
-        // state and instead reload their state from the parent's intent
-        intent.putExtra(EXTRA_IGNORE_STATE, true);
-
-        // Remember the old manually selected tab index so that it can be restored if it is
-        // overwritten by one of the programmatic tab selections
-        final int savedTabIndex = mLastManuallySelectedTab;
-
-        // Choose the tab based on the inbound intent
-        if (intent.getBooleanExtra(ContactsFrontDoor.EXTRA_FRONT_DOOR, false)) {
-            // Launched through the contacts front door, set the proper contacts tab (sticky
-            // between favorites and contacts)
-            SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
-            boolean favoritesAsContacts = prefs.getBoolean(PREF_FAVORITES_AS_CONTACTS,
-                    PREF_FAVORITES_AS_CONTACTS_DEFAULT);
-            if (favoritesAsContacts) {
-                mTabHost.setCurrentTab(TAB_INDEX_FAVORITES);
-            } else {
-                mTabHost.setCurrentTab(TAB_INDEX_CONTACTS);
-            }
-        } else {
-            // Not launched through the front door, look at the component to determine the tab
-            String componentName = intent.getComponent().getClassName();
-            if (getClass().getName().equals(componentName)) {
-                if (recentCallsRequest) {
-                    mTabHost.setCurrentTab(TAB_INDEX_CALL_LOG);
-                } else {
-                    mTabHost.setCurrentTab(TAB_INDEX_DIALER);
-                }
-            } else {
-                mTabHost.setCurrentTab(mLastManuallySelectedTab);
-            }
-        }
-
-        // Restore to the previous manual selection
-        mLastManuallySelectedTab = savedTabIndex;
-
-        // Tell the children activities that they should honor their saved states
-        // instead of the state from the parent's intent
-        intent.putExtra(EXTRA_IGNORE_STATE, false);
-    }
-
-    @Override
-    public void onNewIntent(Intent newIntent) {
-        setIntent(newIntent);
-        fixIntent(newIntent);
-        setCurrentTab(newIntent);
-        final String action = newIntent.getAction();
-        if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
-            setupFilterText(newIntent);
-        } else if (isDialIntent(newIntent)) {
-            setupDialUri(newIntent);
-        }
-    }
-
-    /** Returns true if the given intent contains a phone number to populate the dialer with */
-    private boolean isDialIntent(Intent intent) {
-        final String action = intent.getAction();
-        if (Intent.ACTION_DIAL.equals(action)) {
-            return true;
-        }
-        if (Intent.ACTION_VIEW.equals(action)) {
-            final Uri data = intent.getData();
-            if (data != null && "tel".equals(data.getScheme())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
-     * This text originally came from a FILTER_CONTACTS_ACTION intent received
-     * by this activity. The stored text will then be cleared after after this
-     * method returns.
-     *
-     * @return The stored filter text
-     */
-    public String getAndClearFilterText() {
-        String filterText = mFilterText;
-        mFilterText = null;
-        return filterText;
-    }
-
-    /**
-     * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
-     * This is so child activities can check if they are supposed to display a filter.
-     *
-     * @param intent The intent received in {@link #onNewIntent(Intent)}
-     */
-    private void setupFilterText(Intent intent) {
-        // If the intent was relaunched from history, don't apply the filter text.
-        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
-            return;
-        }
-        String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY);
-        if (filter != null && filter.length() > 0) {
-            mFilterText = filter;
-        }
-    }
-
-    /**
-     * Retrieves the uri stored in {@link #setupDialUri(Intent)}. This uri
-     * originally came from a dial intent received by this activity. The stored
-     * uri will then be cleared after after this method returns.
-     *
-     * @return The stored uri
-     */
-    public Uri getAndClearDialUri() {
-        Uri dialUri = mDialUri;
-        mDialUri = null;
-        return dialUri;
-    }
-
-    /**
-     * Stores the uri associated with a dial intent. This is so child activities can
-     * check if they are supposed to display new dial info.
-     *
-     * @param intent The intent received in {@link #onNewIntent(Intent)}
-     */
-    private void setupDialUri(Intent intent) {
-        // If the intent was relaunched from history, don't reapply the intent.
-        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
-            return;
-        }
-        mDialUri = intent.getData();
-    }
-
-    @Override
-    public void onBackPressed() {
-        if (isTaskRoot()) {
-            // Instead of stopping, simply push this to the back of the stack.
-            // This is only done when running at the top of the stack;
-            // otherwise, we have been launched by someone else so need to
-            // allow the user to go back to the caller.
-            moveTaskToBack(false);
-        } else {
-            super.onBackPressed();
-        }
-    }
-
-    /** {@inheritDoc} */
-    public void onTabChanged(String tabId) {
-        // Because we're using Activities as our tab children, we trigger
-        // onWindowFocusChanged() to let them know when they're active.  This may
-        // seem to duplicate the purpose of onResume(), but it's needed because
-        // onResume() can't reliably check if a keyguard is active.
-        Activity activity = getLocalActivityManager().getActivity(tabId);
-        if (activity != null) {
-            activity.onWindowFocusChanged(true);
-        }
-
-        // Remember this tab index. This function is also called, if the tab is set automatically
-        // in which case the setter (setCurrentTab) has to set this to its old value afterwards
-        mLastManuallySelectedTab = mTabHost.getCurrentTab();
-    }
-}
diff --git a/src/com/android/contacts/SpecialCharSequenceMgr.java b/src/com/android/contacts/SpecialCharSequenceMgr.java
index a047a68..9f9f40d 100644
--- a/src/com/android/contacts/SpecialCharSequenceMgr.java
+++ b/src/com/android/contacts/SpecialCharSequenceMgr.java
@@ -54,7 +54,7 @@
     private SpecialCharSequenceMgr() {
     }
 
-    static boolean handleChars(Context context, String input, EditText textField) {
+    public static boolean handleChars(Context context, String input, EditText textField) {
         return handleChars(context, input, false, textField);
     }
 
diff --git a/src/com/android/contacts/TypePrecedence.java b/src/com/android/contacts/TypePrecedence.java
index 2adf3b5..e89c5aa 100644
--- a/src/com/android/contacts/TypePrecedence.java
+++ b/src/com/android/contacts/TypePrecedence.java
@@ -42,6 +42,7 @@
     //TODO These may need to be tweaked.
     private static final int[] TYPE_PRECEDENCE_PHONES = {
             Phone.TYPE_CUSTOM,
+            Phone.TYPE_MAIN,
             Phone.TYPE_MOBILE,
             Phone.TYPE_HOME,
             Phone.TYPE_WORK,
diff --git a/src/com/android/contacts/activities/CallLogActivity.java b/src/com/android/contacts/activities/CallLogActivity.java
new file mode 100644
index 0000000..13c3209
--- /dev/null
+++ b/src/com/android/contacts/activities/CallLogActivity.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 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.activities;
+
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.calllog.CallLogFragment;
+import com.android.internal.telephony.ITelephony;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * Displays a list of call log entries.
+ */
+public class CallLogActivity extends Activity {
+    private static final String TAG = "CallLogActivity";
+
+    private CallLogFragment mFragment;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+
+        setContentView(R.layout.call_log_activity);
+
+        // Typing here goes to the dialer
+        setDefaultKeyMode(DEFAULT_KEYS_DIALER);
+
+        mFragment = (CallLogFragment) getFragmentManager().findFragmentById(
+                R.id.call_log_fragment);
+    }
+
+    public CallLogFragment getFragment() {
+        return mFragment;
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+
+        // Clear notifications only when window gains focus.  This activity won't
+        // immediately receive focus if the keyguard screen is above it.
+        if (hasFocus) {
+            try {
+                ITelephony telephony =
+                        ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
+                if (telephony != null) {
+                    telephony.cancelMissedCallsNotification();
+                } else {
+                    Log.w(TAG, "Telephony service is null, can't call " +
+                            "cancelMissedCallsNotification");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to clear missed calls notification due to remote exception");
+            }
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL: {
+                long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
+                if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
+                    // Launch voice dialer
+                    Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    try {
+                        startActivity(intent);
+                    } catch (ActivityNotFoundException e) {
+                    }
+                    return true;
+                }
+            }
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL:
+                try {
+                    ITelephony phone = ITelephony.Stub.asInterface(
+                            ServiceManager.checkService("phone"));
+                    if (phone != null && !phone.isIdle()) {
+                        // Let the super class handle it
+                        break;
+                    }
+                } catch (RemoteException re) {
+                    // Fall through and try to call the contact
+                }
+
+                mFragment.callSelectedEntry();
+                return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            ContactsSearchManager.startSearch(this, initialQuery);
+        }
+    }
+}
diff --git a/src/com/android/contacts/activities/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
index f58a28c..f6b3f2f 100644
--- a/src/com/android/contacts/activities/ContactBrowserActivity.java
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -23,7 +23,7 @@
 import com.android.contacts.interactions.ContactDeletionInteraction;
 import com.android.contacts.interactions.GroupDeletionDialogFragment;
 import com.android.contacts.interactions.GroupRenamingDialogFragment;
-import com.android.contacts.interactions.ImportExportInteraction;
+import com.android.contacts.interactions.ImportExportDialogFragment;
 import com.android.contacts.interactions.PhoneNumberInteraction;
 import com.android.contacts.list.ContactBrowseListContextMenuAdapter;
 import com.android.contacts.list.ContactBrowseListFragment;
@@ -42,7 +42,6 @@
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.preference.ContactsPreferenceActivity;
 import com.android.contacts.util.AccountSelectionUtil;
-import com.android.contacts.util.AccountsListAdapter;
 import com.android.contacts.util.DialogManager;
 import com.android.contacts.widget.ContextMenuAdapter;
 
@@ -69,9 +68,6 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.Window;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ListPopupWindow;
 import android.widget.Toast;
 
 import java.util.ArrayList;
@@ -88,9 +84,8 @@
     private static final String TAG = "ContactBrowserActivity";
 
     private static final int SUBACTIVITY_NEW_CONTACT = 2;
-    private static final int SUBACTIVITY_SETTINGS = 3;
-    private static final int SUBACTIVITY_EDIT_CONTACT = 4;
-    private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 5;
+    private static final int SUBACTIVITY_EDIT_CONTACT = 3;
+    private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 4;
 
     private static final String KEY_SEARCH_MODE = "searchMode";
 
@@ -105,13 +100,18 @@
     private boolean mSearchMode;
 
     private ContactBrowseListFragment mListFragment;
+
+    /**
+     * Whether we have a right-side contact pane for displaying contact info while browsing.
+     * Generally means "this is a tablet".
+     */
     private boolean mContactContentDisplayed;
+
     private ContactDetailFragment mDetailFragment;
     private DetailFragmentListener mDetailFragmentListener = new DetailFragmentListener();
 
     private PhoneNumberInteraction mPhoneNumberCallInteraction;
     private PhoneNumberInteraction mSendTextMessageInteraction;
-    private ImportExportInteraction mImportExportInteraction;
 
     private boolean mSearchInitiated;
 
@@ -125,7 +125,6 @@
 
     private boolean mOptionsMenuContactsAvailable;
     private boolean mOptionsMenuGroupActionsEnabled;
-    private boolean mOptionsMenuCustomFilterChangeable;
 
     public ContactBrowserActivity() {
         mIntentResolver = new ContactsIntentResolver(this);
@@ -163,6 +162,17 @@
     protected void onCreate(Bundle savedState) {
         super.onCreate(savedState);
 
+        mAddContactImageView = getLayoutInflater().inflate(
+                R.layout.add_contact_menu_item, null, false);
+        View item = mAddContactImageView.findViewById(R.id.menu_item);
+        item.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+                startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
+            }
+        });
+
         configureContentView(true, savedState);
     }
 
@@ -205,13 +215,15 @@
 
         setTitle(mRequest.getActivityTitle());
 
-        mHasActionBar = getWindow().hasFeature(Window.FEATURE_ACTION_BAR);
-        if (mHasActionBar) {
-            ActionBar actionBar = getActionBar();
+        if (createContentView) {
+            mHasActionBar = getWindow().hasFeature(Window.FEATURE_ACTION_BAR);
+            if (mHasActionBar) {
+                ActionBar actionBar = getActionBar();
 
-            mActionBarAdapter = new ActionBarAdapter(this);
-            mActionBarAdapter.onCreate(savedState, mRequest, actionBar);
-            mActionBarAdapter.setContactListFilterController(mContactListFilterController);
+                mActionBarAdapter = new ActionBarAdapter(this);
+                mActionBarAdapter.onCreate(savedState, mRequest, actionBar);
+                mActionBarAdapter.setContactListFilterController(mContactListFilterController);
+            }
         }
 
         configureFragments(savedState == null);
@@ -224,7 +236,6 @@
         }
 
         mOptionsMenuContactsAvailable = false;
-        mOptionsMenuCustomFilterChangeable = false;
         mOptionsMenuGroupActionsEnabled = false;
 
         mProviderStatus = -1;
@@ -621,26 +632,11 @@
         if (mHasActionBar) {
             inflater.inflate(R.menu.actions, menu);
 
-            // On narrow screens we specify a NEW contact button in the {@link ActionBar}, so that
-            // it can be in the overflow menu. On wide screens, we use a custom view because we need
-            // its location for anchoring the account-selector popup.
-            // TODO: Take this hack out later when the account switcher in the contact editor is
-            // present.
+            // Change add contact button to button with a custom view
             final MenuItem addContact = menu.findItem(R.id.menu_add);
-            if (addContact != null) {
-                mAddContactImageView = getLayoutInflater().inflate(
-                        R.layout.add_contact_menu_item, null, false);
-                View item = mAddContactImageView.findViewById(R.id.menu_item);
-                item.setOnClickListener(new OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        createNewContact();
-                    }
-                });
-                addContact.setActionView(mAddContactImageView);
-            }
+            addContact.setActionView(mAddContactImageView);
             return true;
-        } else if (mRequest.getActionCode() == ContactsRequest.ACTION_DEFAULT ||
+        } else if (mRequest.getActionCode() == ContactsRequest.ACTION_ALL_CONTACTS ||
                 mRequest.getActionCode() == ContactsRequest.ACTION_STREQUENT) {
             inflater.inflate(R.menu.list, menu);
             return true;
@@ -668,10 +664,6 @@
             return true;
         }
 
-        if (mOptionsMenuCustomFilterChangeable != isCustomFilterChangeable()) {
-            return true;
-        }
-
         if (mListFragment != null && mListFragment.isOptionsMenuChanged()) {
             return true;
         }
@@ -691,13 +683,8 @@
         }
 
         MenuItem settings = menu.findItem(R.id.menu_settings);
-        settings.setVisible(!ContactsPreferenceActivity.isEmpty(this));
-
-        mOptionsMenuCustomFilterChangeable = isCustomFilterChangeable();
-
-        MenuItem displayGroups = menu.findItem(R.id.menu_display_groups);
-        if (displayGroups != null) {
-            displayGroups.setVisible(mOptionsMenuCustomFilterChangeable);
+        if (settings != null) {
+            settings.setVisible(!ContactsPreferenceActivity.isEmpty(this));
         }
 
         mOptionsMenuGroupActionsEnabled = areGroupActionsEnabled();
@@ -728,16 +715,12 @@
         return groupActionsEnabled;
     }
 
-    public boolean isCustomFilterChangeable() {
-        return mRequest != null && mRequest.getActionCode() == ContactsRequest.ACTION_DEFAULT;
-    }
-
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             case R.id.menu_settings: {
                 final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
-                startActivityForResult(intent, SUBACTIVITY_SETTINGS);
+                startActivity(intent);
                 return true;
             }
             case R.id.menu_search: {
@@ -745,16 +728,12 @@
                 return true;
             }
             case R.id.menu_add: {
-                createNewContact();
-                return true;
-            }
-            case R.id.menu_new_contact: {
                 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
                 startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
                 return true;
             }
             case R.id.menu_import_export: {
-                getImportExportInteraction().startInteraction();
+                ImportExportDialogFragment.show(getFragmentManager());
                 return true;
             }
             case R.id.menu_accounts: {
@@ -782,35 +761,6 @@
         return false;
     }
 
-    private void createNewContact() {
-        final ArrayList<Account> accounts =
-                AccountTypeManager.getInstance(this).getAccounts(true);
-        if (accounts.size() <= 1 || mAddContactImageView == null) {
-            // No account to choose or no control to anchor the popup-menu to
-            // ==> just go straight to the editor which will disambig if necessary
-            final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
-            startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
-            return;
-        }
-
-        final ListPopupWindow popup = new ListPopupWindow(this, null);
-        popup.setWidth(getResources().getDimensionPixelSize(R.dimen.account_selector_popup_width));
-        popup.setAnchorView(mAddContactImageView);
-        final AccountsListAdapter adapter = new AccountsListAdapter(this, true);
-        popup.setAdapter(adapter);
-        popup.setOnItemClickListener(new OnItemClickListener() {
-            @Override
-            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-                popup.dismiss();
-                final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
-                intent.putExtra(Intents.Insert.ACCOUNT, adapter.getItem(position));
-                startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
-            }
-        });
-        popup.setModal(true);
-        popup.show();
-    }
-
     @Override
     public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
             boolean globalSearch) {
@@ -831,9 +781,6 @@
         dialog = getSendTextMessageInteraction().onCreateDialog(id, bundle);
         if (dialog != null) return dialog;
 
-        dialog = getImportExportInteraction().onCreateDialog(id, bundle);
-        if (dialog != null) return dialog;
-
         return super.onCreateDialog(id, bundle);
     }
 
@@ -862,16 +809,13 @@
 
             case SUBACTIVITY_EDIT_CONTACT:
             case SUBACTIVITY_NEW_CONTACT: {
-                if (resultCode == RESULT_OK) {
+                if (resultCode == RESULT_OK && mContactContentDisplayed) {
                     mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
                     mListFragment.reloadDataAndSetSelectedUri(data.getData());
                 }
                 break;
             }
 
-            case SUBACTIVITY_SETTINGS:
-                break;
-
             // TODO: Using the new startActivityWithResultFromFragment API this should not be needed
             // anymore
             case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER:
@@ -919,8 +863,7 @@
             default: {
                 // Bring up the search UI if the user starts typing
                 final int unicodeChar = event.getUnicodeChar();
-
-                if (unicodeChar != 0) {
+                if (unicodeChar != 0 && !Character.isWhitespace(unicodeChar)) {
                     String query = new String(new int[]{ unicodeChar }, 0, 1);
                     if (mHasActionBar) {
                         if (!mActionBarAdapter.isSearchMode()) {
@@ -998,13 +941,6 @@
         return mSendTextMessageInteraction;
     }
 
-    private ImportExportInteraction getImportExportInteraction() {
-        if (mImportExportInteraction == null) {
-            mImportExportInteraction = new ImportExportInteraction(this);
-        }
-        return mImportExportInteraction;
-    }
-
     @Override
     public DialogManager getDialogManager() {
         return mDialogManager;
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index 56e8353..0036256 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -22,6 +22,7 @@
 import com.android.contacts.R;
 import com.android.contacts.detail.ContactDetailFragment;
 import com.android.contacts.interactions.ContactDeletionInteraction;
+import com.android.contacts.util.PhoneCapabilityTester;
 
 import android.accounts.Account;
 import android.content.ActivityNotFoundException;
@@ -44,6 +45,23 @@
     public void onCreate(Bundle savedState) {
         super.onCreate(savedState);
 
+        if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
+            // This activity must not be shown. We have to select the contact in the
+            // ContactBrowserActivity instead ==> Create a forward intent and finish
+            final Intent originalIntent = getIntent();
+            Intent intent = new Intent();
+            intent.setAction(originalIntent.getAction());
+            intent.setDataAndType(originalIntent.getData(), originalIntent.getType());
+            intent.setFlags(
+                    Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_FORWARD_RESULT
+                            | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+            intent.setClass(this, ContactBrowserActivity.class);
+            startActivity(intent);
+            finish();
+            return;
+        }
+
         setContentView(R.layout.contact_detail_activity);
 
         mFragment = (ContactDetailFragment) getFragmentManager().findFragmentById(
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index 35f7413..29ebd59 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -53,8 +53,6 @@
     public static final String ACTION_SAVE_COMPLETED = "saveCompleted";
 
     private ContactEditorFragment mFragment;
-    private Button mDoneButton;
-    private Button mRevertButton;
 
     private DialogManager mDialogManager = new DialogManager(this);
 
@@ -90,22 +88,6 @@
         mFragment.setListener(mFragmentListener);
         Uri uri = Intent.ACTION_EDIT.equals(action) ? getIntent().getData() : null;
         mFragment.load(action, uri, getIntent().getExtras());
-
-        // Depending on the use-case, this activity has Done and Revert buttons or not.
-        mDoneButton = (Button) findViewById(R.id.done);
-        mRevertButton = (Button) findViewById(R.id.revert);
-        if (mDoneButton != null) mDoneButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mFragment.save(SaveMode.CLOSE);
-            }
-        });
-        if (mRevertButton != null) mRevertButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                finish();
-            }
-        });
     }
 
     @Override
@@ -188,11 +170,6 @@
         }
 
         @Override
-        public void onAccountSelectorAborted() {
-            finish();
-        }
-
-        @Override
         public void onContactNotFound() {
             setResult(Activity.RESULT_CANCELED, null);
             finish();
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index 4efc43f..c255ba1 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -23,7 +23,9 @@
 import com.android.contacts.list.ContactsIntentResolver;
 import com.android.contacts.list.ContactsRequest;
 import com.android.contacts.list.DirectoryListLoader;
+import com.android.contacts.list.EmailAddressPickerFragment;
 import com.android.contacts.list.OnContactPickerActionListener;
+import com.android.contacts.list.OnEmailAddressPickerActionListener;
 import com.android.contacts.list.OnPhoneNumberPickerActionListener;
 import com.android.contacts.list.OnPostalAddressPickerActionListener;
 import com.android.contacts.list.PhoneNumberPickerFragment;
@@ -171,6 +173,11 @@
                 break;
             }
 
+            case ContactsRequest.ACTION_PICK_EMAIL: {
+                setTitle(R.string.contactPickerActivityTitle);
+                break;
+            }
+
             case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
                 setTitle(R.string.callShortcutActivityTitle);
                 break;
@@ -236,6 +243,11 @@
                 break;
             }
 
+            case ContactsRequest.ACTION_PICK_EMAIL: {
+                mListFragment = new EmailAddressPickerFragment();
+                break;
+            }
+
             case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
                 PhoneNumberPickerFragment fragment = new PhoneNumberPickerFragment();
                 fragment.setShortcutAction(Intent.ACTION_CALL);
@@ -284,6 +296,9 @@
         } else if (mListFragment instanceof PostalAddressPickerFragment) {
             ((PostalAddressPickerFragment) mListFragment).setOnPostalAddressPickerActionListener(
                     new PostalAddressPickerActionListener());
+        } else if (mListFragment instanceof EmailAddressPickerFragment) {
+            ((EmailAddressPickerFragment) mListFragment).setOnEmailAddressPickerActionListener(
+                    new EmailAddressPickerActionListener());
         } else {
             throw new IllegalStateException("Unsupported list fragment type: " + mListFragment);
         }
@@ -334,6 +349,14 @@
         }
     }
 
+    private final class EmailAddressPickerActionListener implements
+            OnEmailAddressPickerActionListener {
+        @Override
+        public void onPickEmailAddressAction(Uri dataUri) {
+            returnPickerResult(dataUri);
+        }
+    }
+
     public void startActivityAndForwardResult(final Intent intent) {
         intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
 
diff --git a/src/com/android/contacts/activities/ContactsFrontDoor.java b/src/com/android/contacts/activities/ContactsFrontDoor.java
index 3677cce..d34f2c4 100644
--- a/src/com/android/contacts/activities/ContactsFrontDoor.java
+++ b/src/com/android/contacts/activities/ContactsFrontDoor.java
@@ -17,7 +17,6 @@
 package com.android.contacts.activities;
 
 import com.android.contacts.ContactsActivity;
-import com.android.contacts.DialtactsActivity;
 import com.android.contacts.util.PhoneCapabilityTester;
 
 import android.content.Intent;
@@ -39,12 +38,12 @@
                         | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
         intent.putExtra(EXTRA_FRONT_DOOR, true);
 
-        if (PhoneCapabilityTester.isPhone(this)) {
-            // Default to the normal dialtacts layout
-            intent.setClass(this, DialtactsActivity.class);
-        } else {
+        if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
             // No tabs, just a contact list
             intent.setClass(this, ContactBrowserActivity.class);
+        } else {
+            // Default to the normal dialtacts layout
+            intent.setClass(this, DialtactsActivity.class);
         }
 
         startActivity(intent);
diff --git a/src/com/android/contacts/activities/DialpadActivity.java b/src/com/android/contacts/activities/DialpadActivity.java
new file mode 100644
index 0000000..bb122df
--- /dev/null
+++ b/src/com/android/contacts/activities/DialpadActivity.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2007 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.activities;
+
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.dialpad.DialpadFragment;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.ViewConfiguration;
+import android.view.Window;
+import android.view.inputmethod.InputMethodManager;
+
+/**
+ * Activity that displays a twelve-key phone dialpad.
+ * This is just a simple container around DialpadFragment.
+ * @see DialpadFragment
+ */
+public class DialpadActivity extends Activity {
+    private static final String TAG = "DialpadActivity";
+
+    private DialpadFragment mFragment;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        setContentView(R.layout.dialpad_activity);
+
+        Resources r = getResources();
+        // Do not show title in the case the device is in carmode.
+        if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) ==
+                Configuration.UI_MODE_TYPE_CAR) {
+            requestWindowFeature(Window.FEATURE_NO_TITLE);
+        }
+
+        mFragment = (DialpadFragment) getFragmentManager().findFragmentById(
+                R.id.dialpad_fragment);
+
+        // Manually run the onRestoreInstanceState() sequence here, but only if
+        // our intent does *not* have the DialtactsActivity.EXTRA_IGNORE_STATE
+        // set (see the references to EXTRA_IGNORE_STATE in DialtactsActivity).
+        // TODO: Find a cleaner way of doing this.
+        if (!mFragment.resolveIntent() && (icicle != null)) {
+            super.onRestoreInstanceState(icicle);
+        }
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle icicle) {
+        // Do nothing, state is restored in onCreate() if needed
+    }
+
+    @Override
+    protected void onNewIntent(Intent newIntent) {
+        setIntent(newIntent);
+        mFragment.resolveIntent();
+    }
+
+    @Override
+    protected void onPostCreate(Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+
+        // Pass this lifecycle event down to the fragment
+        mFragment.onPostCreate();
+    }
+
+    public DialpadFragment getFragment() {
+        return mFragment;
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        if (hasFocus) {
+            // Hide soft keyboard, if visible (it's fugly over button dialer).
+            // The only known case where this will be true is when launching the dialer with
+            // ACTION_DIAL via a soft keyboard.  we dismiss it here because we don't
+            // have a window token yet in onCreate / onNewIntent
+            InputMethodManager inputMethodManager = (InputMethodManager)
+                    getSystemService(Context.INPUT_METHOD_SERVICE);
+            inputMethodManager.hideSoftInputFromWindow(
+                    mFragment.getDigitsWidget().getWindowToken(), 0);
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL: {
+                long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
+                if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
+                    // Launch voice dialer
+                    Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    try {
+                        startActivity(intent);
+                    } catch (ActivityNotFoundException e) {
+                        Log.w(TAG, "Failed to launch voice dialer: " + e);
+                    }
+                }
+                return true;
+            }
+            case KeyEvent.KEYCODE_1: {
+                long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
+                if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
+                    // Long press detected, call voice mail
+                    mFragment.callVoicemail();
+                }
+                return true;
+            }
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_CALL: {
+                mFragment.dialButtonPressed();
+                return true;
+            }
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            ContactsSearchManager.startSearch(this, initialQuery);
+        }
+    }
+}
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
new file mode 100644
index 0000000..baae703
--- /dev/null
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2008 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.activities;
+
+import com.android.contacts.R;
+import com.android.contacts.calllog.CallLogFragment;
+import com.android.contacts.dialpad.DialpadFragment;
+import com.android.contacts.interactions.ImportExportDialogFragment;
+import com.android.contacts.list.ContactListFilter;
+import com.android.contacts.list.ContactsIntentResolver;
+import com.android.contacts.list.ContactsRequest;
+import com.android.contacts.list.DefaultContactBrowseListFragment;
+import com.android.contacts.list.DirectoryListLoader;
+import com.android.contacts.list.OnContactBrowserActionListener;
+import com.android.contacts.preference.ContactsPreferenceActivity;
+import com.android.internal.telephony.ITelephony;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.ActionBar.TabListener;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Intents.UI;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+/**
+ * The dialer activity that has one tab with the virtual 12key
+ * dialer, a tab with recent calls in it, a tab with the contacts and
+ * a tab with the favorite. This is the container and the tabs are
+ * embedded using intents.
+ * The dialer tab's title is 'phone', a more common name (see strings.xml).
+ */
+public class DialtactsActivity extends Activity {
+    private static final String TAG = "DialtactsActivity";
+
+    private static final int TAB_INDEX_DIALER = 0;
+    private static final int TAB_INDEX_CALL_LOG = 1;
+    private static final int TAB_INDEX_CONTACTS = 2;
+    private static final int TAB_INDEX_FAVORITES = 3;
+
+    public static final String EXTRA_IGNORE_STATE = "ignore-state";
+
+    /** Name of the dialtacts shared preferences */
+    static final String PREFS_DIALTACTS = "dialtacts";
+    /** If true, when handling the contacts intent the favorites tab will be shown instead */
+    static final String PREF_FAVORITES_AS_CONTACTS = "favorites_as_contacts";
+    static final boolean PREF_FAVORITES_AS_CONTACTS_DEFAULT = false;
+
+    /** Last manually selected tab index */
+    private static final String PREF_LAST_MANUALLY_SELECTED_TAB = "last_manually_selected_tab";
+    private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER;
+
+    private String mFilterText;
+    private Uri mDialUri;
+    private DialpadFragment mDialpadFragment;
+    private CallLogFragment mCallLogFragment;
+    private DefaultContactBrowseListFragment mContactsFragment;
+    private DefaultContactBrowseListFragment mFavoritesFragment;
+
+    /**
+     * The index of the tab that has last been manually selected (the user clicked on a tab).
+     * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
+     */
+    private int mLastManuallySelectedTab;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final Intent intent = getIntent();
+        fixIntent(intent);
+
+        setContentView(R.layout.dialtacts_activity);
+
+        final FragmentManager fragmentManager = getFragmentManager();
+        mDialpadFragment = (DialpadFragment) fragmentManager
+                .findFragmentById(R.id.dialpad_fragment);
+        mCallLogFragment = (CallLogFragment) fragmentManager
+                .findFragmentById(R.id.call_log_fragment);
+        mContactsFragment = (DefaultContactBrowseListFragment) fragmentManager
+                .findFragmentById(R.id.contacts_fragment);
+        mFavoritesFragment = (DefaultContactBrowseListFragment) fragmentManager
+                .findFragmentById(R.id.favorites_fragment);
+
+        // Hide all tabs (the current tab will later be reshown once a tab is selected)
+        final FragmentTransaction transaction = fragmentManager.beginTransaction();
+        transaction.hide(mDialpadFragment);
+        transaction.hide(mCallLogFragment);
+        transaction.hide(mContactsFragment);
+        transaction.hide(mFavoritesFragment);
+        transaction.commit();
+
+        // Setup the ActionBar tabs (the order matches the tab-index contants TAB_INDEX_*)
+        setupDialer();
+        setupCallLog();
+        setupContacts();
+        setupFavorites();
+        getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+        getActionBar().setDisplayShowTitleEnabled(false);
+        getActionBar().setDisplayShowHomeEnabled(false);
+
+        // Load the last manually loaded tab
+        final SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
+        mLastManuallySelectedTab = prefs.getInt(PREF_LAST_MANUALLY_SELECTED_TAB,
+                PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT);
+
+        setCurrentTab(intent);
+
+        if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
+                && icicle == null) {
+            setupFilterText(intent);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        final int currentTabIndex = getActionBar().getSelectedTab().getPosition();
+        final SharedPreferences.Editor editor =
+                getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE).edit();
+        if (currentTabIndex == TAB_INDEX_CONTACTS || currentTabIndex == TAB_INDEX_FAVORITES) {
+            editor.putBoolean(PREF_FAVORITES_AS_CONTACTS, currentTabIndex == TAB_INDEX_FAVORITES);
+        }
+        editor.putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedTab);
+
+        editor.apply();
+    }
+
+    private void fixIntent(Intent intent) {
+        // This should be cleaned up: the call key used to send an Intent
+        // that just said to go to the recent calls list.  It now sends this
+        // abstract action, but this class hasn't been rewritten to deal with it.
+        if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
+            intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
+            intent.putExtra("call_key", true);
+            setIntent(intent);
+        }
+    }
+
+    private void setupDialer() {
+        final Tab tab = getActionBar().newTab();
+        tab.setText(R.string.dialerIconLabel);
+        tab.setTabListener(new TabChangeListener(mDialpadFragment));
+        tab.setIcon(R.drawable.ic_tab_dialer);
+        getActionBar().addTab(tab);
+        mDialpadFragment.resolveIntent();
+    }
+
+    private void setupCallLog() {
+        final Tab tab = getActionBar().newTab();
+        tab.setText(R.string.recentCallsIconLabel);
+        tab.setIcon(R.drawable.ic_tab_recent);
+        tab.setTabListener(new TabChangeListener(mCallLogFragment));
+        getActionBar().addTab(tab);
+    }
+
+    private void setupContacts() {
+        final Tab tab = getActionBar().newTab();
+        tab.setText(R.string.contactsIconLabel);
+        tab.setIcon(R.drawable.ic_tab_contacts);
+        tab.setTabListener(new TabChangeListener(mContactsFragment));
+        getActionBar().addTab(tab);
+
+        // TODO: We should not artificially create Intents and put them into the Fragment.
+        // It would be nicer to directly pass in the UI constant
+        Intent intent = new Intent(UI.LIST_ALL_CONTACTS_ACTION);
+        intent.setClass(this, ContactBrowserActivity.class);
+
+        ContactsIntentResolver resolver = new ContactsIntentResolver(this);
+        ContactsRequest request = resolver.resolveIntent(intent);
+        final ContactListFilter filter = new ContactListFilter(
+                ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+        mContactsFragment.setFilter(filter, false);
+        mContactsFragment.setSearchMode(request.isSearchMode());
+        mContactsFragment.setQueryString(request.getQueryString(), false);
+        mContactsFragment.setContactsRequest(request);
+        mContactsFragment.setDirectorySearchMode(request.isDirectorySearchEnabled()
+                ? DirectoryListLoader.SEARCH_MODE_DEFAULT
+                : DirectoryListLoader.SEARCH_MODE_NONE);
+        mContactsFragment.setOnContactListActionListener(mListFragmentListener);
+    }
+
+    private void setupFavorites() {
+        final Tab tab = getActionBar().newTab();
+        tab.setText(R.string.contactsFavoritesLabel);
+        tab.setIcon(R.drawable.ic_tab_starred);
+        tab.setTabListener(new TabChangeListener(mFavoritesFragment));
+        getActionBar().addTab(tab);
+
+        // TODO: We should not artificially create Intents and put them into the Fragment.
+        // It would be nicer to directly pass in the UI constant
+        Intent intent = new Intent(UI.LIST_STREQUENT_ACTION);
+        intent.setClass(this, ContactBrowserActivity.class);
+
+        ContactsIntentResolver resolver = new ContactsIntentResolver(this);
+        ContactsRequest request = resolver.resolveIntent(intent);
+        final ContactListFilter filter = new ContactListFilter(
+                ContactListFilter.FILTER_TYPE_STARRED);
+        mFavoritesFragment.setFilter(filter, false);
+        mFavoritesFragment.setSearchMode(request.isSearchMode());
+        mFavoritesFragment.setQueryString(request.getQueryString(), false);
+        mFavoritesFragment.setContactsRequest(request);
+        mFavoritesFragment.setDirectorySearchMode(request.isDirectorySearchEnabled()
+                ? DirectoryListLoader.SEARCH_MODE_DEFAULT
+                : DirectoryListLoader.SEARCH_MODE_NONE);
+        mFavoritesFragment.setOnContactListActionListener(mListFragmentListener);
+    }
+
+    /**
+     * Returns true if the intent is due to hitting the green send key while in a call.
+     *
+     * @param intent the intent that launched this activity
+     * @param recentCallsRequest true if the intent is requesting to view recent calls
+     * @return true if the intent is due to hitting the green send key while in a call
+     */
+    private boolean isSendKeyWhileInCall(final Intent intent, final boolean recentCallsRequest) {
+        // If there is a call in progress go to the call screen
+        if (recentCallsRequest) {
+            final boolean callKey = intent.getBooleanExtra("call_key", false);
+
+            try {
+                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+                if (callKey && phone != null && phone.showCallScreen()) {
+                    return true;
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to handle send while in call", e);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Sets the current tab based on the intent's request type
+     *
+     * @param intent Intent that contains information about which tab should be selected
+     */
+    private void setCurrentTab(Intent intent) {
+        // If we got here by hitting send and we're in call forward along to the in-call activity
+        final boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.getType());
+        if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
+            finish();
+            return;
+        }
+
+        // Tell the children activities that they should ignore any possible saved
+        // state and instead reload their state from the parent's intent
+        intent.putExtra(EXTRA_IGNORE_STATE, true);
+
+        // Remember the old manually selected tab index so that it can be restored if it is
+        // overwritten by one of the programmatic tab selections
+        final int savedTabIndex = mLastManuallySelectedTab;
+
+        // Choose the tab based on the inbound intent
+        if (intent.getBooleanExtra(ContactsFrontDoor.EXTRA_FRONT_DOOR, false)) {
+            // Launched through the contacts front door, set the proper contacts tab (sticky
+            // between favorites and contacts)
+            SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
+            boolean favoritesAsContacts = prefs.getBoolean(PREF_FAVORITES_AS_CONTACTS,
+                    PREF_FAVORITES_AS_CONTACTS_DEFAULT);
+            if (favoritesAsContacts) {
+                getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_FAVORITES));
+            } else {
+                getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_CONTACTS));
+            }
+        } else {
+            // Not launched through the front door, look at the component to determine the tab
+            String componentName = intent.getComponent().getClassName();
+            if (getClass().getName().equals(componentName)) {
+                if (recentCallsRequest) {
+                    getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_CALL_LOG));
+                } else {
+                    getActionBar().selectTab(getActionBar().getTabAt(TAB_INDEX_DIALER));
+                }
+            } else {
+                getActionBar().selectTab(getActionBar().getTabAt(mLastManuallySelectedTab));
+            }
+        }
+
+        // Restore to the previous manual selection
+        mLastManuallySelectedTab = savedTabIndex;
+
+        // Tell the children activities that they should honor their saved states
+        // instead of the state from the parent's intent
+        intent.putExtra(EXTRA_IGNORE_STATE, false);
+    }
+
+    @Override
+    public void onNewIntent(Intent newIntent) {
+        setIntent(newIntent);
+        fixIntent(newIntent);
+        setCurrentTab(newIntent);
+        final String action = newIntent.getAction();
+        if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
+            setupFilterText(newIntent);
+        } else if (isDialIntent(newIntent)) {
+            setupDialUri(newIntent);
+        }
+    }
+
+    /** Returns true if the given intent contains a phone number to populate the dialer with */
+    private boolean isDialIntent(Intent intent) {
+        final String action = intent.getAction();
+        if (Intent.ACTION_DIAL.equals(action)) {
+            return true;
+        }
+        if (Intent.ACTION_VIEW.equals(action)) {
+            final Uri data = intent.getData();
+            if (data != null && "tel".equals(data.getScheme())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
+     * This text originally came from a FILTER_CONTACTS_ACTION intent received
+     * by this activity. The stored text will then be cleared after after this
+     * method returns.
+     *
+     * @return The stored filter text
+     */
+    public String getAndClearFilterText() {
+        String filterText = mFilterText;
+        mFilterText = null;
+        return filterText;
+    }
+
+    /**
+     * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
+     * This is so child activities can check if they are supposed to display a filter.
+     *
+     * @param intent The intent received in {@link #onNewIntent(Intent)}
+     */
+    private void setupFilterText(Intent intent) {
+        // If the intent was relaunched from history, don't apply the filter text.
+        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
+            return;
+        }
+        String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY);
+        if (filter != null && filter.length() > 0) {
+            mFilterText = filter;
+        }
+    }
+
+    /**
+     * Retrieves the uri stored in {@link #setupDialUri(Intent)}. This uri
+     * originally came from a dial intent received by this activity. The stored
+     * uri will then be cleared after after this method returns.
+     *
+     * @return The stored uri
+     */
+    public Uri getAndClearDialUri() {
+        Uri dialUri = mDialUri;
+        mDialUri = null;
+        return dialUri;
+    }
+
+    /**
+     * Stores the uri associated with a dial intent. This is so child activities can
+     * check if they are supposed to display new dial info.
+     *
+     * @param intent The intent received in {@link #onNewIntent(Intent)}
+     */
+    private void setupDialUri(Intent intent) {
+        // If the intent was relaunched from history, don't reapply the intent.
+        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
+            return;
+        }
+        mDialUri = intent.getData();
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (isTaskRoot()) {
+            // Instead of stopping, simply push this to the back of the stack.
+            // This is only done when running at the top of the stack;
+            // otherwise, we have been launched by someone else so need to
+            // allow the user to go back to the caller.
+            moveTaskToBack(false);
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
+    protected void onPostCreate(Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+
+        // Pass this lifecycle event down to the fragment
+        mDialpadFragment.onPostCreate();
+    }
+
+    /**
+     * Tab change listener that is instantiated once for each tab. Handles showing/hiding tabs
+     * and remembers manual tab selections
+     */
+    private class TabChangeListener implements TabListener {
+        private final Fragment mFragment;
+
+        public TabChangeListener(Fragment fragment) {
+            mFragment = fragment;
+        }
+
+        @Override
+        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+            ft.hide(mFragment);
+        }
+
+        @Override
+        public void onTabSelected(Tab tab, FragmentTransaction ft) {
+            ft.show(mFragment);
+
+            // Remember this tab index. This function is also called, if the tab is set
+            // automatically in which case the setter (setCurrentTab) has to set this to its old
+            // value afterwards
+            mLastManuallySelectedTab = tab.getPosition();
+        }
+
+        @Override
+        public void onTabReselected(Tab tab, FragmentTransaction ft) {
+        }
+    }
+
+    private OnContactBrowserActionListener mListFragmentListener =
+            new OnContactBrowserActionListener() {
+        @Override
+        public void onViewContactAction(Uri contactLookupUri) {
+            startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri));
+        }
+
+        @Override
+        public void onSmsContactAction(Uri contactUri) {
+        }
+
+        @Override
+        public void onSelectionChange() {
+        }
+
+        @Override
+        public void onRemoveFromFavoritesAction(Uri contactUri) {
+        }
+
+        @Override
+        public void onInvalidSelection() {
+        }
+
+        @Override
+        public void onFinishAction() {
+        }
+
+        @Override
+        public void onEditContactAction(Uri contactLookupUri) {
+        }
+
+        @Override
+        public void onDeleteContactAction(Uri contactUri) {
+        }
+
+        @Override
+        public void onCreateNewContactAction() {
+        }
+
+        @Override
+        public void onCallContactAction(Uri contactUri) {
+        }
+
+        @Override
+        public void onAddToFavoritesAction(Uri contactUri) {
+        }
+    };
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // For now, create the menu in here. It would be nice to do this in the Fragment,
+        // but that Fragment is re-used in other views.
+        final ActionBar actionBar = getActionBar();
+        if (actionBar == null) return false;
+        final Tab tab = actionBar.getSelectedTab();
+        if (tab == null) return false;
+        final int tabIndex = tab.getPosition();
+        if (tabIndex != TAB_INDEX_CONTACTS && tabIndex != TAB_INDEX_FAVORITES) return false;
+
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.list, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // This is currently a copy of the equivalent code of ContactBrowserActivity (with the
+        // exception of menu_add, because we do not select items in the list).
+        // Should be consolidated
+        switch (item.getItemId()) {
+        case R.id.menu_settings: {
+            final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
+            startActivity(intent);
+            return true;
+        }
+        case R.id.menu_search: {
+            onSearchRequested();
+            return true;
+        }
+        case R.id.menu_add: {
+            final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+            startActivity(intent);
+            return true;
+        }
+        case R.id.menu_import_export: {
+            ImportExportDialogFragment.show(getFragmentManager());
+            return true;
+        }
+        case R.id.menu_accounts: {
+            final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
+            intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
+                ContactsContract.AUTHORITY
+            });
+            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+            startActivity(intent);
+            return true;
+        }
+        default:
+            return super.onOptionsItemSelected(item);
+        }
+    }
+}
diff --git a/src/com/android/contacts/activities/GroupBrowserActivity.java b/src/com/android/contacts/activities/GroupBrowserActivity.java
new file mode 100644
index 0000000..e8b3ad8
--- /dev/null
+++ b/src/com/android/contacts/activities/GroupBrowserActivity.java
@@ -0,0 +1,55 @@
+/*
+ * 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.activities;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Displays a list to browse groups.
+ */
+public class GroupBrowserActivity extends ContactsActivity {
+
+    private static final String TAG = "GroupBrowserActivity";
+
+    public GroupBrowserActivity() {
+    }
+
+    @Override
+    protected void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+        configureContentView(true, savedState);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        setIntent(intent);
+        configureContentView(false, null);
+    }
+
+    private void configureContentView(boolean createContentView, Bundle savedState) {
+        // TODO: Create Intent Resolver to handle the different ways users can get to this list.
+        // TODO: Use savedState if necessary
+        // TODO: Setup action bar
+        if (createContentView) {
+            setContentView(R.layout.group_browser_activity);
+        }
+    }
+}
diff --git a/src/com/android/contacts/activities/GroupDetailActivity.java b/src/com/android/contacts/activities/GroupDetailActivity.java
new file mode 100644
index 0000000..edc460c
--- /dev/null
+++ b/src/com/android/contacts/activities/GroupDetailActivity.java
@@ -0,0 +1,44 @@
+/*
+ * 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.activities;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+
+import android.os.Bundle;
+
+public class GroupDetailActivity extends ContactsActivity {
+
+    private static final String TAG = "GroupDetailActivity";
+
+    public static final String KEY_ACCOUNT_TYPE = "accountType";
+    public static final String KEY_ACCOUNT_NAME = "accountName";
+    public static final String KEY_GROUP_ID = "groupId";
+    public static final String KEY_GROUP_SOURCE_ID = "groupSourceId";
+    public static final String KEY_GROUP_READ_ONLY = "groupReadOnly";
+    public static final String KEY_GROUP_TITLE = "title";
+
+    @Override
+    public void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+
+        // TODO: Create Intent Resolver to handle the different ways users can get to this list.
+        // TODO: Handle search or key down
+
+        setContentView(R.layout.group_detail_activity);
+    }
+}
diff --git a/src/com/android/contacts/RecentCallsListActivity.java b/src/com/android/contacts/calllog/CallLogFragment.java
similarity index 76%
rename from src/com/android/contacts/RecentCallsListActivity.java
rename to src/com/android/contacts/calllog/CallLogFragment.java
index 0a3c178..5872043 100644
--- a/src/com/android/contacts/RecentCallsListActivity.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -14,23 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.contacts;
+package com.android.contacts.calllog;
 
 import com.android.common.widget.GroupingListAdapter;
+import com.android.contacts.CallDetailActivity;
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
 import com.android.internal.telephony.CallerInfo;
-import com.android.internal.telephony.ITelephony;
 
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ListActivity;
-import android.content.ActivityNotFoundException;
+import android.app.ListFragment;
 import android.content.AsyncQueryHandler;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.DialogInterface.OnClickListener;
 import android.database.CharArrayBuffer;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabaseCorruptException;
@@ -43,9 +40,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
@@ -60,15 +54,14 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.ContextMenu;
-import android.view.KeyEvent;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
 import android.view.Menu;
+import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.widget.AdapterView;
 import android.widget.ImageView;
 import android.widget.ListView;
@@ -81,65 +74,70 @@
 /**
  * Displays a list of call log entries.
  */
-public class RecentCallsListActivity extends ListActivity
+public class CallLogFragment extends ListFragment
         implements View.OnCreateContextMenuListener {
-    private static final String TAG = "RecentCallsList";
+    private static final String TAG = "CallLogFragment";
 
-    /** The projection to use when querying the call log table */
-    static final String[] CALL_LOG_PROJECTION = new String[] {
-            Calls._ID,
-            Calls.NUMBER,
-            Calls.DATE,
-            Calls.DURATION,
-            Calls.TYPE,
-            Calls.CACHED_NAME,
-            Calls.CACHED_NUMBER_TYPE,
-            Calls.CACHED_NUMBER_LABEL,
-            Calls.COUNTRY_ISO};
+    /** The query for the call log table */
+    private static final class CallLogQuery {
+        public static final String[] _PROJECTION = new String[] {
+                Calls._ID,
+                Calls.NUMBER,
+                Calls.DATE,
+                Calls.DURATION,
+                Calls.TYPE,
+                Calls.CACHED_NAME,
+                Calls.CACHED_NUMBER_TYPE,
+                Calls.CACHED_NUMBER_LABEL,
+                Calls.COUNTRY_ISO};
 
-    static final int ID_COLUMN_INDEX = 0;
-    static final int NUMBER_COLUMN_INDEX = 1;
-    static final int DATE_COLUMN_INDEX = 2;
-    static final int DURATION_COLUMN_INDEX = 3;
-    static final int CALL_TYPE_COLUMN_INDEX = 4;
-    static final int CALLER_NAME_COLUMN_INDEX = 5;
-    static final int CALLER_NUMBERTYPE_COLUMN_INDEX = 6;
-    static final int CALLER_NUMBERLABEL_COLUMN_INDEX = 7;
-    static final int COUNTRY_ISO_COLUMN_INDEX = 8;
+        public static final int ID = 0;
+        public static final int NUMBER = 1;
+        public static final int DATE = 2;
+        public static final int DURATION = 3;
+        public static final int CALL_TYPE = 4;
+        public static final int CALLER_NAME = 5;
+        public static final int CALLER_NUMBERTYPE = 6;
+        public static final int CALLER_NUMBERLABEL = 7;
+        public static final int COUNTRY_ISO = 8;
+    }
 
-    /** The projection to use when querying the phones table */
-    static final String[] PHONES_PROJECTION = new String[] {
-            PhoneLookup._ID,
-            PhoneLookup.DISPLAY_NAME,
-            PhoneLookup.TYPE,
-            PhoneLookup.LABEL,
-            PhoneLookup.NUMBER,
-            PhoneLookup.NORMALIZED_NUMBER};
+    /** The query to use for the phones table */
+    private static final class PhoneQuery {
+        public static final String[] _PROJECTION = new String[] {
+                PhoneLookup._ID,
+                PhoneLookup.DISPLAY_NAME,
+                PhoneLookup.TYPE,
+                PhoneLookup.LABEL,
+                PhoneLookup.NUMBER,
+                PhoneLookup.NORMALIZED_NUMBER};
 
-    static final int PERSON_ID_COLUMN_INDEX = 0;
-    static final int NAME_COLUMN_INDEX = 1;
-    static final int PHONE_TYPE_COLUMN_INDEX = 2;
-    static final int LABEL_COLUMN_INDEX = 3;
-    static final int MATCHED_NUMBER_COLUMN_INDEX = 4;
-    static final int NORMALIZED_NUMBER_COLUMN_INDEX = 5;
+        public static final int PERSON_ID = 0;
+        public static final int NAME = 1;
+        public static final int PHONE_TYPE = 2;
+        public static final int LABEL = 3;
+        public static final int MATCHED_NUMBER = 4;
+        public static final int NORMALIZED_NUMBER = 5;
+    }
 
-    private static final int MENU_ITEM_DELETE = 1;
-    private static final int MENU_ITEM_DELETE_ALL = 2;
-    private static final int MENU_ITEM_VIEW_CONTACTS = 3;
+    private static final class MenuItems {
+        public static final int DELETE = 1;
+    }
+
+    private static final class OptionsMenuItems {
+        public static final int DELETE_ALL = 1;
+    }
 
     private static final int QUERY_TOKEN = 53;
     private static final int UPDATE_TOKEN = 54;
 
-    private static final int DIALOG_CONFIRM_DELETE_ALL = 1;
-
-    RecentCallsAdapter mAdapter;
+    private CallLogAdapter mAdapter;
     private QueryHandler mQueryHandler;
-    String mVoiceMailNumber;
+    private String mVoiceMailNumber;
     private String mCurrentCountryIso;
-
     private boolean mScrollToTop;
 
-    static final class ContactInfo {
+    public static final class ContactInfo {
         public long personId;
         public String name;
         public int type;
@@ -151,27 +149,27 @@
         public static ContactInfo EMPTY = new ContactInfo();
     }
 
-    public static final class RecentCallsListItemViews {
-        TextView line1View;
-        TextView labelView;
-        TextView numberView;
-        TextView dateView;
-        ImageView iconView;
-        View callView;
-        ImageView groupIndicator;
-        TextView groupSize;
+    public static final class CallLogListItemViews {
+        public TextView line1View;
+        public TextView labelView;
+        public TextView numberView;
+        public TextView dateView;
+        public ImageView iconView;
+        public View callView;
+        public ImageView groupIndicator;
+        public TextView groupSize;
     }
 
-    static final class CallerInfoQuery {
-        String number;
-        int position;
-        String name;
-        int numberType;
-        String numberLabel;
+    public static final class CallerInfoQuery {
+        public String number;
+        public int position;
+        public String name;
+        public int numberType;
+        public String numberLabel;
     }
 
     /** Adapter class to fill in data for the Call Log */
-    final class RecentCallsAdapter extends GroupingListAdapter
+    public final class CallLogAdapter extends GroupingListAdapter
             implements Runnable, ViewTreeObserver.OnPreDrawListener, View.OnClickListener {
         HashMap<String,ContactInfo> mContactInfo;
         private final LinkedList<CallerInfoQuery> mRequests;
@@ -195,6 +193,7 @@
         private CharArrayBuffer mBuffer1 = new CharArrayBuffer(128);
         private CharArrayBuffer mBuffer2 = new CharArrayBuffer(128);
 
+        @Override
         public void onClick(View view) {
             String number = (String) view.getTag();
             if (!TextUtils.isEmpty(number)) {
@@ -211,6 +210,7 @@
             }
         }
 
+        @Override
         public boolean onPreDraw() {
             if (mFirst) {
                 mHandler.sendEmptyMessageDelayed(START_THREAD, 1000);
@@ -233,8 +233,8 @@
             }
         };
 
-        public RecentCallsAdapter() {
-            super(RecentCallsListActivity.this);
+        public CallLogAdapter() {
+            super(getActivity());
 
             mContactInfo = new HashMap<String,ContactInfo>();
             mRequests = new LinkedList<CallerInfoQuery>();
@@ -307,7 +307,7 @@
             values.put(Calls.CACHED_NUMBER_LABEL, ci.label);
 
             try {
-                RecentCallsListActivity.this.getContentResolver().update(Calls.CONTENT_URI, values,
+                getActivity().getContentResolver().update(Calls.CONTENT_URI, values,
                         Calls.NUMBER + "='" + ciq.number + "'", null);
             } catch (SQLiteDiskIOException e) {
                 Log.w(TAG, "Exception while updating call info", e);
@@ -369,7 +369,7 @@
                     String[] selectionArgs = new String[] { ciq.number.toUpperCase() };
 
                     Cursor dataTableCursor =
-                            RecentCallsListActivity.this.getContentResolver().query(
+                            getActivity().getContentResolver().query(
                                     contactRef,
                                     null,  // projection
                                     selection,  // selection
@@ -410,20 +410,21 @@
                     // "number" is a regular phone number, so use the
                     // PhoneLookup table:
                     Cursor phonesCursor =
-                            RecentCallsListActivity.this.getContentResolver().query(
+                            getActivity().getContentResolver().query(
                                 Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
                                         Uri.encode(ciq.number)),
-                                PHONES_PROJECTION, null, null, null);
+                                        PhoneQuery._PROJECTION, null, null, null);
                     if (phonesCursor != null) {
                         if (phonesCursor.moveToFirst()) {
                             info = new ContactInfo();
-                            info.personId = phonesCursor.getLong(PERSON_ID_COLUMN_INDEX);
-                            info.name = phonesCursor.getString(NAME_COLUMN_INDEX);
-                            info.type = phonesCursor.getInt(PHONE_TYPE_COLUMN_INDEX);
-                            info.label = phonesCursor.getString(LABEL_COLUMN_INDEX);
-                            info.number = phonesCursor.getString(MATCHED_NUMBER_COLUMN_INDEX);
-                            info.normalizedNumber =
-                                    phonesCursor.getString(NORMALIZED_NUMBER_COLUMN_INDEX);
+                            info.personId = phonesCursor.getLong(PhoneQuery.PERSON_ID);
+                            info.name = phonesCursor.getString(PhoneQuery.NAME);
+                            info.type = phonesCursor.getInt(PhoneQuery.PHONE_TYPE);
+                            info.label = phonesCursor.getString(PhoneQuery.LABEL);
+                            info.number = phonesCursor
+                                    .getString(PhoneQuery.MATCHED_NUMBER);
+                            info.normalizedNumber = phonesCursor
+                                    .getString(PhoneQuery.NORMALIZED_NUMBER);
 
                             infoUpdated = true;
                         }
@@ -452,6 +453,7 @@
          * Handles requests for contact name and number type
          * @see java.lang.Runnable#run()
          */
+        @Override
         public void run() {
             boolean needNotify = false;
             while (!mDone) {
@@ -490,11 +492,11 @@
             CharArrayBuffer currentValue = mBuffer1;
             CharArrayBuffer value = mBuffer2;
             cursor.moveToFirst();
-            cursor.copyStringToBuffer(NUMBER_COLUMN_INDEX, currentValue);
-            int currentCallType = cursor.getInt(CALL_TYPE_COLUMN_INDEX);
+            cursor.copyStringToBuffer(CallLogQuery.NUMBER, currentValue);
+            int currentCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
             for (int i = 1; i < count; i++) {
                 cursor.moveToNext();
-                cursor.copyStringToBuffer(NUMBER_COLUMN_INDEX, value);
+                cursor.copyStringToBuffer(CallLogQuery.NUMBER, value);
                 boolean sameNumber = equalPhoneNumbers(value, currentValue);
 
                 // Group adjacent calls with the same number. Make an exception
@@ -520,7 +522,7 @@
                     if (sameNumber && currentCallType == Calls.MISSED_TYPE) {
                         currentCallType = 0;       // "not a missed call"
                     } else {
-                        currentCallType = cursor.getInt(CALL_TYPE_COLUMN_INDEX);
+                        currentCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
                     }
                 }
             }
@@ -539,16 +541,16 @@
 
 
         @Override
-        protected View newStandAloneView(Context context, ViewGroup parent) {
+        public View newStandAloneView(Context context, ViewGroup parent) {
             LayoutInflater inflater =
                     (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            View view = inflater.inflate(R.layout.recent_calls_list_item, parent, false);
+            View view = inflater.inflate(R.layout.call_log_list_item, parent, false);
             findAndCacheViews(view);
             return view;
         }
 
         @Override
-        protected void bindStandAloneView(View view, Context context, Cursor cursor) {
+        public void bindStandAloneView(View view, Context context, Cursor cursor) {
             bindView(context, view, cursor);
         }
 
@@ -556,7 +558,7 @@
         protected View newChildView(Context context, ViewGroup parent) {
             LayoutInflater inflater =
                     (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            View view = inflater.inflate(R.layout.recent_calls_list_child_item, parent, false);
+            View view = inflater.inflate(R.layout.call_log_list_child_item, parent, false);
             findAndCacheViews(view);
             return view;
         }
@@ -570,7 +572,7 @@
         protected View newGroupView(Context context, ViewGroup parent) {
             LayoutInflater inflater =
                     (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            View view = inflater.inflate(R.layout.recent_calls_list_group_item, parent, false);
+            View view = inflater.inflate(R.layout.call_log_list_group_item, parent, false);
             findAndCacheViews(view);
             return view;
         }
@@ -578,7 +580,7 @@
         @Override
         protected void bindGroupView(View view, Context context, Cursor cursor, int groupSize,
                 boolean expanded) {
-            final RecentCallsListItemViews views = (RecentCallsListItemViews) view.getTag();
+            final CallLogListItemViews views = (CallLogListItemViews) view.getTag();
             int groupIndicator = expanded
                     ? com.android.internal.R.drawable.expander_ic_maximized
                     : com.android.internal.R.drawable.expander_ic_minimized;
@@ -590,7 +592,7 @@
         private void findAndCacheViews(View view) {
 
             // Get the views to bind to
-            RecentCallsListItemViews views = new RecentCallsListItemViews();
+            CallLogListItemViews views = new CallLogListItemViews();
             views.line1View = (TextView) view.findViewById(R.id.line1);
             views.labelView = (TextView) view.findViewById(R.id.label);
             views.numberView = (TextView) view.findViewById(R.id.number);
@@ -604,14 +606,14 @@
         }
 
         public void bindView(Context context, View view, Cursor c) {
-            final RecentCallsListItemViews views = (RecentCallsListItemViews) view.getTag();
+            final CallLogListItemViews views = (CallLogListItemViews) view.getTag();
 
-            String number = c.getString(NUMBER_COLUMN_INDEX);
+            String number = c.getString(CallLogQuery.NUMBER);
             String formattedNumber = null;
-            String callerName = c.getString(CALLER_NAME_COLUMN_INDEX);
-            int callerNumberType = c.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX);
-            String callerNumberLabel = c.getString(CALLER_NUMBERLABEL_COLUMN_INDEX);
-            String countryIso = c.getString(COUNTRY_ISO_COLUMN_INDEX);
+            String callerName = c.getString(CallLogQuery.CALLER_NAME);
+            int callerNumberType = c.getInt(CallLogQuery.CALLER_NUMBERTYPE);
+            String callerNumberLabel = c.getString(CallLogQuery.CALLER_NUMBERLABEL);
+            String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
             // Store away the number so we can call it directly if you click on the call icon
             views.callView.setTag(number);
 
@@ -728,7 +730,7 @@
                 views.labelView.setVisibility(View.GONE);
             }
 
-            long date = c.getLong(DATE_COLUMN_INDEX);
+            long date = c.getLong(CallLogQuery.DATE);
 
             // Set the date/time field by mixing relative and absolute times.
             int flags = DateUtils.FORMAT_ABBREV_RELATIVE;
@@ -737,7 +739,7 @@
                     System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, flags));
 
             if (views.iconView != null) {
-                int type = c.getInt(CALL_TYPE_COLUMN_INDEX);
+                int type = c.getInt(CallLogQuery.CALL_TYPE);
                 // Set the icon
                 switch (type) {
                     case Calls.INCOMING_TYPE:
@@ -764,7 +766,7 @@
     }
 
     private static final class QueryHandler extends AsyncQueryHandler {
-        private final WeakReference<RecentCallsListActivity> mActivity;
+        private final WeakReference<CallLogFragment> mFragment;
 
         /**
          * Simple handler that wraps background calls to catch
@@ -796,25 +798,26 @@
             return new CatchingWorkerHandler(looper);
         }
 
-        public QueryHandler(Context context) {
-            super(context.getContentResolver());
-            mActivity = new WeakReference<RecentCallsListActivity>(
-                    (RecentCallsListActivity) context);
+        public QueryHandler(CallLogFragment fragment) {
+            super(fragment.getActivity().getContentResolver());
+            mFragment = new WeakReference<CallLogFragment>(fragment);
         }
 
         @Override
         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
-            final RecentCallsListActivity activity = mActivity.get();
-            if (activity != null && !activity.isFinishing()) {
-                final RecentCallsListActivity.RecentCallsAdapter callsAdapter = activity.mAdapter;
+            final CallLogFragment fragment = mFragment.get();
+            if (fragment != null && fragment.getActivity() != null &&
+                    !fragment.getActivity().isFinishing()) {
+                final CallLogFragment.CallLogAdapter callsAdapter = fragment.mAdapter;
                 callsAdapter.setLoading(false);
                 callsAdapter.changeCursor(cursor);
-                if (activity.mScrollToTop) {
-                    if (activity.mList.getFirstVisiblePosition() > 5) {
-                        activity.mList.setSelection(5);
+                if (fragment.mScrollToTop) {
+                    final ListView listView = fragment.getListView();
+                    if (listView.getFirstVisiblePosition() > 5) {
+                        listView.setSelection(5);
                     }
-                    activity.mList.smoothScrollToPosition(0);
-                    activity.mScrollToTop = false;
+                    listView.smoothScrollToPosition(0);
+                    fragment.mScrollToTop = false;
                 }
             } else {
                 cursor.close();
@@ -823,33 +826,39 @@
     }
 
     @Override
-    protected void onCreate(Bundle state) {
+    public void onCreate(Bundle state) {
         super.onCreate(state);
 
-        setContentView(R.layout.recent_calls);
-
-        // Typing here goes to the dialer
-        setDefaultKeyMode(DEFAULT_KEYS_DIALER);
-
-        mAdapter = new RecentCallsAdapter();
-        getListView().setOnCreateContextMenuListener(this);
-        setListAdapter(mAdapter);
-
-        mVoiceMailNumber = ((TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE))
-                .getVoiceMailNumber();
+        mVoiceMailNumber = ((TelephonyManager) getActivity().getSystemService(
+                Context.TELEPHONY_SERVICE)).getVoiceMailNumber();
         mQueryHandler = new QueryHandler(this);
 
-        mCurrentCountryIso = ContactsUtils.getCurrentCountryIso(this);
+        mCurrentCountryIso = ContactsUtils.getCurrentCountryIso(getActivity());
+
+        setHasOptionsMenu(true);
     }
 
     @Override
-    protected void onStart() {
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+        return inflater.inflate(R.layout.call_log_fragment, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        getListView().setOnCreateContextMenuListener(this);
+        mAdapter = new CallLogAdapter();
+        setListAdapter(mAdapter);
+    }
+
+    @Override
+    public void onStart() {
         mScrollToTop = true;
         super.onStart();
     }
 
     @Override
-    protected void onResume() {
+    public void onResume() {
         // The adapter caches looked up numbers, clear it so they will get
         // looked up again.
         if (mAdapter != null) {
@@ -865,7 +874,7 @@
     }
 
     @Override
-    protected void onPause() {
+    public void onPause() {
         super.onPause();
 
         // Kill the requests thread
@@ -873,34 +882,12 @@
     }
 
     @Override
-    protected void onDestroy() {
+    public void onDestroy() {
         super.onDestroy();
         mAdapter.stopRequestProcessing();
         mAdapter.changeCursor(null);
     }
 
-    @Override
-    public void onWindowFocusChanged(boolean hasFocus) {
-        super.onWindowFocusChanged(hasFocus);
-
-        // Clear notifications only when window gains focus.  This activity won't
-        // immediately receive focus if the keyguard screen is above it.
-        if (hasFocus) {
-            try {
-                ITelephony iTelephony =
-                        ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
-                if (iTelephony != null) {
-                    iTelephony.cancelMissedCallsNotification();
-                } else {
-                    Log.w(TAG, "Telephony service is null, can't call " +
-                            "cancelMissedCallsNotification");
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to clear missed calls notification due to remote exception");
-            }
-        }
-    }
-
     /**
      * Format the given phone number
      *
@@ -944,14 +931,14 @@
         // Cancel any pending queries
         mQueryHandler.cancelOperation(QUERY_TOKEN);
         mQueryHandler.startQuery(QUERY_TOKEN, null, Calls.CONTENT_URI,
-                CALL_LOG_PROJECTION, null, null, Calls.DEFAULT_SORT_ORDER);
+                CallLogQuery._PROJECTION, null, null, Calls.DEFAULT_SORT_ORDER);
     }
 
     @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        menu.add(0, MENU_ITEM_DELETE_ALL, 0, R.string.recentCalls_deleteAll)
-                .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
-        return true;
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+        menu.add(0, OptionsMenuItems.DELETE_ALL, 0, R.string.recentCalls_deleteAll).setIcon(
+                android.R.drawable.ic_menu_close_clear_cancel);
     }
 
     @Override
@@ -966,7 +953,7 @@
 
         Cursor cursor = (Cursor) mAdapter.getItem(menuInfo.position);
 
-        String number = cursor.getString(NUMBER_COLUMN_INDEX);
+        String number = cursor.getString(CallLogQuery.NUMBER);
         Uri numberUri = null;
         boolean isVoicemail = false;
         boolean isSipNumber = false;
@@ -1033,45 +1020,14 @@
             menu.add(0, 0, 0, R.string.recentCalls_addToContact)
                     .setIntent(intent);
         }
-        menu.add(0, MENU_ITEM_DELETE, 0, R.string.recentCalls_removeFromRecentList);
-    }
-
-    @Override
-    protected Dialog onCreateDialog(int id, Bundle args) {
-        switch (id) {
-            case DIALOG_CONFIRM_DELETE_ALL:
-                return new AlertDialog.Builder(this)
-                    .setTitle(R.string.clearCallLogConfirmation_title)
-                    .setIconAttribute(android.R.attr.alertDialogIcon)
-                    .setMessage(R.string.clearCallLogConfirmation)
-                    .setNegativeButton(android.R.string.cancel, null)
-                    .setPositiveButton(android.R.string.ok, new OnClickListener() {
-                        public void onClick(DialogInterface dialog, int which) {
-                            getContentResolver().delete(Calls.CONTENT_URI, null, null);
-                            // TODO The change notification should do this automatically, but it
-                            // isn't working right now. Remove this when the change notification
-                            // is working properly.
-                            startQuery();
-                        }
-                    })
-                    .setCancelable(false)
-                    .create();
-        }
-        return null;
+        menu.add(0, MenuItems.DELETE, 0, R.string.recentCalls_removeFromRecentList);
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
-            case MENU_ITEM_DELETE_ALL: {
-                showDialog(DIALOG_CONFIRM_DELETE_ALL);
-                return true;
-            }
-
-            case MENU_ITEM_VIEW_CONTACTS: {
-                Intent intent = new Intent(Intent.ACTION_VIEW, Contacts.CONTENT_URI);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                startActivity(intent);
+            case OptionsMenuItems.DELETE_ALL: {
+                ClearCallLogDialog.show(getFragmentManager());
                 return true;
             }
         }
@@ -1090,7 +1046,7 @@
         }
 
         switch (item.getItemId()) {
-            case MENU_ITEM_DELETE: {
+            case MenuItems.DELETE: {
                 Cursor cursor = (Cursor)mAdapter.getItem(menuInfo.position);
                 int groupSize = 1;
                 if (mAdapter.isGroupHeader(menuInfo.position)) {
@@ -1103,58 +1059,17 @@
                         sb.append(",");
                         cursor.moveToNext();
                     }
-                    long id = cursor.getLong(ID_COLUMN_INDEX);
+                    long id = cursor.getLong(CallLogQuery.ID);
                     sb.append(id);
                 }
 
-                getContentResolver().delete(Calls.CONTENT_URI, Calls._ID + " IN (" + sb + ")",
-                        null);
+                getActivity().getContentResolver().delete(Calls.CONTENT_URI,
+                        Calls._ID + " IN (" + sb + ")", null);
             }
         }
         return super.onContextItemSelected(item);
     }
 
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_CALL: {
-                long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
-                if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
-                    // Launch voice dialer
-                    Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
-                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    try {
-                        startActivity(intent);
-                    } catch (ActivityNotFoundException e) {
-                    }
-                    return true;
-                }
-            }
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_CALL:
-                try {
-                    ITelephony phone = ITelephony.Stub.asInterface(
-                            ServiceManager.checkService("phone"));
-                    if (phone != null && !phone.isIdle()) {
-                        // Let the super class handle it
-                        break;
-                    }
-                } catch (RemoteException re) {
-                    // Fall through and try to call the contact
-                }
-
-                callEntry(getListView().getSelectedItemPosition());
-                return true;
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
     /*
      * Get the number from the Contacts, if available, since sometimes
      * the number provided by caller id may not be formatted properly
@@ -1172,14 +1087,12 @@
             matchingNumber = ci.number;
         } else {
             try {
-                Cursor phonesCursor =
-                    RecentCallsListActivity.this.getContentResolver().query(
-                            Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
-                                    number),
-                    PHONES_PROJECTION, null, null, null);
+                Cursor phonesCursor = getActivity().getContentResolver().query(
+                        Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number),
+                        PhoneQuery._PROJECTION, null, null, null);
                 if (phonesCursor != null) {
                     if (phonesCursor.moveToFirst()) {
-                        matchingNumber = phonesCursor.getString(MATCHED_NUMBER_COLUMN_INDEX);
+                        matchingNumber = phonesCursor.getString(PhoneQuery.MATCHED_NUMBER);
                     }
                     phonesCursor.close();
                 }
@@ -1195,7 +1108,8 @@
         return number;
     }
 
-    private void callEntry(int position) {
+    public void callSelectedEntry() {
+        int position = getListView().getSelectedItemPosition();
         if (position < 0) {
             // In touch mode you may often not have something selected, so
             // just call the first entry to make sure that [send] [send] calls the
@@ -1204,7 +1118,7 @@
         }
         final Cursor cursor = (Cursor)mAdapter.getItem(position);
         if (cursor != null) {
-            String number = cursor.getString(NUMBER_COLUMN_INDEX);
+            String number = cursor.getString(CallLogQuery.NUMBER);
             if (TextUtils.isEmpty(number)
                     || number.equals(CallerInfo.UNKNOWN_NUMBER)
                     || number.equals(CallerInfo.PRIVATE_NUMBER)
@@ -1220,7 +1134,7 @@
             } else {
                 // We're calling a regular PSTN phone number.
                 // Construct a tel: URI, but do some other possible cleanup first.
-                int callType = cursor.getInt(CALL_TYPE_COLUMN_INDEX);
+                int callType = cursor.getInt(CallLogQuery.CALL_TYPE);
                 if (!number.startsWith("+") &&
                        (callType == Calls.INCOMING_TYPE
                                 || callType == Calls.MISSED_TYPE)) {
@@ -1237,23 +1151,21 @@
     }
 
     @Override
-    protected void onListItemClick(ListView l, View v, int position, long id) {
+    public void onListItemClick(ListView l, View v, int position, long id) {
         if (mAdapter.isGroupHeader(position)) {
             mAdapter.toggleGroup(position);
         } else {
-            Intent intent = new Intent(this, CallDetailActivity.class);
+            Intent intent = new Intent(getActivity(), CallDetailActivity.class);
             intent.setData(ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI, id));
             startActivity(intent);
         }
     }
 
-    @Override
-    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
-            boolean globalSearch) {
-        if (globalSearch) {
-            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
-        } else {
-            ContactsSearchManager.startSearch(this, initialQuery);
-        }
+    public CallLogAdapter getAdapter() {
+        return mAdapter;
+    }
+
+    public String getVoiceMailNumber() {
+        return mVoiceMailNumber;
     }
 }
diff --git a/src/com/android/contacts/calllog/ClearCallLogDialog.java b/src/com/android/contacts/calllog/ClearCallLogDialog.java
new file mode 100644
index 0000000..426732a
--- /dev/null
+++ b/src/com/android/contacts/calllog/ClearCallLogDialog.java
@@ -0,0 +1,78 @@
+/*
+ * 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.calllog;
+
+import com.android.contacts.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.app.ProgressDialog;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.CallLog.Calls;
+
+/**
+ * Dialog that clears the call log after confirming with the user
+ */
+public class ClearCallLogDialog extends DialogFragment {
+    /** Preferred way to show this dialog */
+    public static void show(FragmentManager fragmentManager) {
+        ClearCallLogDialog dialog = new ClearCallLogDialog();
+        dialog.show(fragmentManager, "deleteCallLog");
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final ContentResolver resolver = getActivity().getContentResolver();
+        final OnClickListener okListener = new OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                final ProgressDialog progressDialog = ProgressDialog.show(getActivity(),
+                        getString(R.string.clearCallLogProgress_title),
+                        "", true, false);
+                final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
+                    @Override
+                    protected Void doInBackground(Void... params) {
+                        resolver.delete(Calls.CONTENT_URI, null, null);
+                        return null;
+                    }
+                    @Override
+                    protected void onPostExecute(Void result) {
+                        progressDialog.dismiss();
+                    }
+                };
+                // TODO: Once we have the API, we should configure this ProgressDialog
+                // to only show up after a certain time (e.g. 150ms)
+                progressDialog.show();
+                task.execute();
+            }
+        };
+        return new AlertDialog.Builder(getActivity())
+            .setTitle(R.string.clearCallLogConfirmation_title)
+            .setIconAttribute(android.R.attr.alertDialogIcon)
+            .setMessage(R.string.clearCallLogConfirmation)
+            .setNegativeButton(android.R.string.cancel, null)
+            .setPositiveButton(android.R.string.ok, okListener)
+            .setCancelable(true)
+            .create();
+    }
+}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 42b3293..f3a4805 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -27,9 +27,9 @@
 import com.android.contacts.TypePrecedence;
 import com.android.contacts.editor.SelectAccountDialogFragment;
 import com.android.contacts.model.AccountType;
-import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.AccountType.EditType;
 import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.util.Constants;
 import com.android.contacts.util.DataStatus;
 import com.android.contacts.util.DateUtils;
@@ -164,34 +164,26 @@
      * A list of distinct contact IDs included in the current contact.
      */
     private ArrayList<Long> mRawContactIds = new ArrayList<Long>();
-    private ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mPostalEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mImEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mNicknameEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mGroupEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mRelationEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
-    private ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
+    private ArrayList<DetailViewEntry> mPhoneEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mSmsEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mEmailEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mPostalEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mImEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mNicknameEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mGroupEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mRelationEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mNoteEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mWebsiteEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mSipEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mEventEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<DetailViewEntry> mOtherEntries = new ArrayList<DetailViewEntry>();
+    private ArrayList<ViewEntry> mAllEntries = new ArrayList<ViewEntry>();
     private LayoutInflater mInflater;
 
     private boolean mTransitionAnimationRequested;
 
     public ContactDetailFragment() {
         // Explicit constructor for inflation
-
-        // Build the list of sections. The order they're added to mSections dictates the
-        // order they are displayed in the list.
-        mSections.add(mPhoneEntries);
-        mSections.add(mSmsEntries);
-        mSections.add(mEmailEntries);
-        mSections.add(mImEntries);
-        mSections.add(mPostalEntries);
-        mSections.add(mNicknameEntries);
-        mSections.add(mOtherEntries);
-        mSections.add(mRelationEntries);
-        mSections.add(mGroupEntries);
     }
 
     @Override
@@ -223,9 +215,6 @@
 
         mInflater = inflater;
 
-        mHeaderView = (ContactDetailHeaderView) mView.findViewById(R.id.contact_header_widget);
-        mHeaderView.setListener(mHeaderViewListener);
-
         mListView = (ListView) mView.findViewById(android.R.id.list);
         mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
         mListView.setOnItemClickListener(this);
@@ -304,19 +293,22 @@
             return;
         }
 
-        // Set the header
-        mHeaderView.loadData(mContactData);
+        // Clear old header
+        mHeaderView = null;
 
         // Build up the contact entries
         buildEntries();
 
-        // Collapse similar data items in select sections.
+        // Collapse similar data items for select {@link DataKind}s.
         Collapser.collapseList(mPhoneEntries);
         Collapser.collapseList(mSmsEntries);
         Collapser.collapseList(mEmailEntries);
         Collapser.collapseList(mPostalEntries);
         Collapser.collapseList(mImEntries);
 
+        // Make one aggregated list of all entries for display to the user.
+        flattenAllLists();
+
         if (mAdapter == null) {
             mAdapter = new ViewAdapter();
             mListView.setAdapter(mAdapter);
@@ -350,10 +342,7 @@
         mHasSip = PhoneCapabilityTester.isSipPhone(mContext);
 
         // Clear out the old entries
-        final int numSections = mSections.size();
-        for (int i = 0; i < numSections; i++) {
-            mSections.get(i).clear();
-        }
+        mAllEntries.clear();
 
         mRawContactIds.clear();
 
@@ -409,8 +398,8 @@
                         accountType, mimeType);
                 if (kind == null) continue;
 
-                final ViewEntry entry = ViewEntry.fromValues(mContext, mimeType, kind, dataId,
-                        entryValues, mContactData.isDirectoryEntry(),
+                final DetailViewEntry entry = DetailViewEntry.fromValues(mContext, mimeType, kind,
+                        dataId, entryValues, mContactData.isDirectoryEntry(),
                         mContactData.getDirectoryId());
 
                 final boolean hasData = !TextUtils.isEmpty(entry.data);
@@ -464,8 +453,8 @@
                         final String imMime = Im.CONTENT_ITEM_TYPE;
                         final DataKind imKind = accountTypes.getKindOrFallback(accountType,
                                 imMime);
-                        final ViewEntry imEntry = ViewEntry.fromValues(mContext, imMime, imKind,
-                                dataId, entryValues, mContactData.isDirectoryEntry(),
+                        final DetailViewEntry imEntry = DetailViewEntry.fromValues(mContext, imMime,
+                                imKind, dataId, entryValues, mContactData.isDirectoryEntry(),
                                 mContactData.getDirectoryId());
                         buildImActions(imEntry, entryValues);
                         imEntry.applyStatus(status, false);
@@ -507,7 +496,7 @@
                     // Build note entries
                     entry.uri = null;
                     entry.maxLines = 100;
-                    mOtherEntries.add(entry);
+                    mNoteEntries.add(entry);
                 } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     // Build Website entries
                     entry.uri = null;
@@ -519,7 +508,7 @@
                     } catch (ParseException e) {
                         Log.e(TAG, "Couldn't parse website: " + entry.data);
                     }
-                    mOtherEntries.add(entry);
+                    mWebsiteEntries.add(entry);
                 } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     // Build SipAddress entries
                     entry.uri = null;
@@ -531,17 +520,17 @@
                         entry.intent = null;
                         entry.actionIcon = -1;
                     }
-                    mOtherEntries.add(entry);
-                    // TODO: Consider moving the SipAddress into its own
-                    // section (rather than lumping it in with mOtherEntries)
-                    // so that we can reposition it right under the phone number.
+                    mSipEntries.add(entry);
+                    // TODO: Now that SipAddress is in its own list of entries
+                    // (instead of grouped in mOtherEntries), consider
+                    // repositioning it right under the phone number.
                     // (Then, we'd also update FallbackAccountType.java to set
                     // secondary=false for this field, and tweak the weight
                     // of its DataKind.)
                 } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     entry.data = DateUtils.formatDate(mContext, entry.data);
                     entry.uri = null;
-                    mOtherEntries.add(entry);
+                    mEventEntries.add(entry);
                 } else if (Relation.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     entry.intent = new Intent(Intent.ACTION_SEARCH);
                     entry.intent.putExtra(SearchManager.QUERY, entry.data);
@@ -567,7 +556,7 @@
         }
 
         if (!groups.isEmpty()) {
-            ViewEntry entry = new ViewEntry();
+            DetailViewEntry entry = new DetailViewEntry();
             Collections.sort(groups);
             StringBuilder sb = new StringBuilder();
             int size = groups.size();
@@ -585,6 +574,49 @@
     }
 
     /**
+     * Collapse all contact detail entries into one aggregated list with a {@link HeaderViewEntry}
+     * at the top.
+     */
+    private void flattenAllLists() {
+        // All contacts should have a header view (even if there is no data for the contact).
+        mAllEntries.add(new HeaderViewEntry());
+
+        flattenList(mPhoneEntries);
+        flattenList(mSmsEntries);
+        flattenList(mEmailEntries);
+        flattenList(mImEntries);
+        flattenList(mPostalEntries);
+        flattenList(mNicknameEntries);
+        flattenList(mNoteEntries);
+        flattenList(mWebsiteEntries);
+        flattenList(mSipEntries);
+        flattenList(mEventEntries);
+        flattenList(mOtherEntries);
+        flattenList(mRelationEntries);
+        flattenList(mGroupEntries);
+    }
+
+    /**
+     * Iterate through {@link DetailViewEntry} in the given list and add it to a list of all
+     * entries. Add a {@link SeparatorViewEntry} at the end if the length of the list was not 0.
+     * Clear the original list.
+     */
+    private void flattenList(ArrayList<DetailViewEntry> entries) {
+        int count = entries.size();
+
+        for (int i = 0; i < count; i++) {
+            mAllEntries.add(entries.get(i));
+        }
+
+        if (count > 0) {
+            mAllEntries.add(new SeparatorViewEntry());
+        }
+
+        // Clear old list because it's not needed anymore.
+        entries.clear();
+    }
+
+    /**
      * Maps group ID to the corresponding group name, collapses all synonymous groups.
      * Ignores default groups (e.g. My Contacts) and favorites groups.
      */
@@ -621,7 +653,7 @@
      * {@link Email} row. If the result is non-null, it either contains one or two Intents
      * (e.g. [Text, Videochat] or just [Text])
      */
-    public static void buildImActions(ViewEntry entry, ContentValues values) {
+    public static void buildImActions(DetailViewEntry entry, ContentValues values) {
         final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(values.getAsString(Data.MIMETYPE));
 
         if (!isEmail && !isProtocolValid(values)) {
@@ -690,15 +722,65 @@
     }
 
     /**
-     * A basic structure with the data for a contact entry in the list.
+     * Base class for an item in the {@link ViewAdapter} list of data, which is
+     * supplied to the {@link ListView}.
      */
-    static class ViewEntry implements Collapsible<ViewEntry> {
+    static class ViewEntry {
+        private final int viewTypeForAdapter;
+        protected long id = -1;
+        /** Whether or not the entry can be focused on or not. */
+        protected boolean isEnabled = false;
+
+        ViewEntry(int viewType) {
+            viewTypeForAdapter = viewType;
+        }
+
+        int getViewType() {
+            return viewTypeForAdapter;
+        }
+
+        long getId() {
+            return id;
+        }
+
+        boolean isEnabled(){
+            return isEnabled;
+        }
+    }
+
+    /**
+     * Header item in the {@link ViewAdapter} list of data.
+     */
+    static class HeaderViewEntry extends ViewEntry {
+
+        HeaderViewEntry() {
+            super(ViewAdapter.VIEW_TYPE_HEADER_ENTRY);
+        }
+
+    }
+
+    /**
+     * Separator between items of the same {@link DataKind} in the
+     * {@link ViewAdapter} list of data.
+     */
+    static class SeparatorViewEntry extends ViewEntry {
+
+        SeparatorViewEntry() {
+            super(ViewAdapter.VIEW_TYPE_SEPARATOR_ENTRY);
+        }
+
+    }
+
+    /**
+     * An item with a single detail for a contact in the {@link ViewAdapter}
+     * list of data.
+     */
+    static class DetailViewEntry extends ViewEntry implements Collapsible<DetailViewEntry> {
         public int type = -1;
         public String kind;
         public String typeString;
         public String data;
         public Uri uri;
-        public long id = 0;
         public int maxLines = 1;
         public String mimetype;
 
@@ -717,17 +799,19 @@
 
         public CharSequence footerLine = null;
 
-        ViewEntry() {
+        DetailViewEntry() {
+            super(ViewAdapter.VIEW_TYPE_DETAIL_ENTRY);
+            isEnabled = true;
         }
 
         /**
-         * Build new {@link ViewEntry} and populate from the given values.
+         * Build new {@link DetailViewEntry} and populate from the given values.
          */
-        public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
+        public static DetailViewEntry fromValues(Context context, String mimeType, DataKind kind,
                 long dataId, ContentValues values, boolean isDirectoryEntry, long directoryId) {
-            final ViewEntry entry = new ViewEntry();
-            entry.context = context;
+            final DetailViewEntry entry = new DetailViewEntry();
             entry.id = dataId;
+            entry.context = context;
             entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
             if (isDirectoryEntry) {
                 entry.uri = entry.uri.buildUpon().appendQueryParameter(
@@ -770,13 +854,13 @@
         }
 
         /**
-         * Apply given {@link DataStatus} values over this {@link ViewEntry}
+         * Apply given {@link DataStatus} values over this {@link DetailViewEntry}
          *
          * @param fillData When true, the given status replaces {@link #data}
          *            and {@link #footerLine}. Otherwise only {@link #presence}
          *            is updated.
          */
-        public ViewEntry applyStatus(DataStatus status, boolean fillData) {
+        public DetailViewEntry applyStatus(DataStatus status, boolean fillData) {
             presence = status.getPresence();
             if (fillData && status.isValid()) {
                 this.data = status.getStatus().toString();
@@ -787,7 +871,7 @@
         }
 
         @Override
-        public boolean collapseWith(ViewEntry entry) {
+        public boolean collapseWith(DetailViewEntry entry) {
             // assert equal collapse keys
             if (!shouldCollapseWith(entry)) {
                 return false;
@@ -816,13 +900,13 @@
             // uri, and contactdId, shouldn't make a difference. Just keep the original.
 
             // Keep track of all the ids that have been collapsed with this one.
-            ids.add(entry.id);
+            ids.add(entry.getId());
             collapseCount++;
             return true;
         }
 
         @Override
-        public boolean shouldCollapseWith(ViewEntry entry) {
+        public boolean shouldCollapseWith(DetailViewEntry entry) {
             if (entry == null) {
                 return false;
             }
@@ -845,23 +929,64 @@
 
     /** Cache of the children views of a row */
     private static class ViewCache {
-        public View kindDivider;
-        public View inKindDivider;
-        public View lineBelowLast;
         public TextView kind;
         public TextView type;
         public TextView data;
         public TextView footer;
-        public ImageView actionIcon;
         public ImageView presenceIcon;
         public ImageView secondaryActionButton;
+        public View secondaryActionButtonContainer;
         public View secondaryActionDivider;
     }
 
     private final class ViewAdapter extends BaseAdapter {
+
+        public static final int VIEW_TYPE_DETAIL_ENTRY = 0;
+        public static final int VIEW_TYPE_HEADER_ENTRY = 1;
+        public static final int VIEW_TYPE_SEPARATOR_ENTRY = 2;
+        private static final int VIEW_TYPE_COUNT = 3;
+
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
-            final ViewEntry entry = getEntry(position);
+            switch (getItemViewType(position)) {
+                case VIEW_TYPE_HEADER_ENTRY:
+                    return getHeaderEntryView(convertView, parent);
+                case VIEW_TYPE_SEPARATOR_ENTRY:
+                    return getSeparatorEntryView(convertView, parent);
+                case VIEW_TYPE_DETAIL_ENTRY:
+                    return getDetailEntryView(position, convertView, parent);
+                default:
+                    throw new IllegalStateException("Invalid view type ID " +
+                            getItemViewType(position));
+            }
+        }
+
+        private View getHeaderEntryView(View convertView, ViewGroup parent) {
+            // We don't want to rely on the recycled header view because it may
+            // have been left over from a previously viewed contact (since we
+            // reuse the adapter), so we would have to bind the data to the
+            // header each time. However, since there is only 1 header per list,
+            // just hold onto the original header view for this contact and
+            // return that each time.
+            if (mHeaderView != null) {
+                return mHeaderView;
+            }
+            mHeaderView = (ContactDetailHeaderView) mInflater.inflate(
+                    R.layout.contact_detail_header_view_list_item, parent, false);
+            mHeaderView.setListener(mHeaderViewListener);
+            mHeaderView.loadData(mContactData);
+            return mHeaderView;
+        }
+
+        private View getSeparatorEntryView(View convertView, ViewGroup parent) {
+            if (convertView != null) {
+                return convertView;
+            }
+            return mInflater.inflate(R.layout.contact_detail_separator_list_item, parent, false);
+        }
+
+        private View getDetailEntryView(int position, View convertView, ViewGroup parent) {
+            final DetailViewEntry entry = (DetailViewEntry) getItem(position);
             final View v;
             final ViewCache viewCache;
 
@@ -876,46 +1001,49 @@
                 // Cache the children
                 viewCache = new ViewCache();
                 viewCache.kind = (TextView) v.findViewById(R.id.kind);
-                viewCache.kindDivider = v.findViewById(R.id.kind_divider);
-                viewCache.lineBelowLast = v.findViewById(R.id.line_below_last);
-                viewCache.inKindDivider = v.findViewById(R.id.in_kind_divider);
                 viewCache.type = (TextView) v.findViewById(R.id.type);
                 viewCache.data = (TextView) v.findViewById(R.id.data);
                 viewCache.footer = (TextView) v.findViewById(R.id.footer);
-                viewCache.actionIcon = (ImageView) v.findViewById(R.id.action_icon);
                 viewCache.presenceIcon = (ImageView) v.findViewById(R.id.presence_icon);
                 viewCache.secondaryActionButton = (ImageView) v.findViewById(
                         R.id.secondary_action_button);
-                viewCache.secondaryActionButton.setOnClickListener(mSecondaryActionClickListener);
+                viewCache.secondaryActionButtonContainer = v.findViewById(
+                        R.id.secondary_action_button_container);
+                viewCache.secondaryActionButtonContainer.setOnClickListener(
+                        mSecondaryActionClickListener);
                 viewCache.secondaryActionDivider = v.findViewById(R.id.divider);
                 v.setTag(viewCache);
             }
 
-            final ViewEntry previousEntry = position == 0 ? null : getEntry(position - 1);
-            final boolean isFirstOfItsKind =
-                    previousEntry == null ? true : !previousEntry.kind.equals(entry.kind);
-            final boolean isLast = position == getCount() - 1;
+            final ViewEntry previousEntry = position == 0 ? null : getItem(position - 1);
+            final boolean isFirstOfItsKind = (previousEntry == null) ? true :
+                    (previousEntry.getViewType() != VIEW_TYPE_DETAIL_ENTRY);
 
             // Bind the data to the view
-            bindView(v, entry, isFirstOfItsKind, isLast);
+            bindView(v, entry, isFirstOfItsKind);
             return v;
         }
 
-        protected void bindView(View view, ViewEntry entry, boolean isFirstOfItsKind,
-                boolean isLast) {
+        private void bindView(View view, DetailViewEntry entry, boolean isFirstOfItsKind) {
             final Resources resources = mContext.getResources();
             ViewCache views = (ViewCache) view.getTag();
 
-            views.kind.setText(isFirstOfItsKind ? entry.kind : "");
-            views.kindDivider.setVisibility(isFirstOfItsKind ? View.VISIBLE : View.GONE);
-            views.inKindDivider.setVisibility(isFirstOfItsKind ? View.GONE : View.VISIBLE);
-            if (views.lineBelowLast != null) {
-                views.lineBelowLast.setVisibility(isLast ? View.VISIBLE : View.GONE);
+            if (isFirstOfItsKind) {
+                views.kind.setText(entry.kind != null ? entry.kind.toUpperCase() : "");
+                views.kind.setVisibility(View.VISIBLE);
+            } else {
+                views.kind.setVisibility(View.GONE);
             }
 
-            views.type.setText(entry.typeString);
-            views.type.setVisibility(
-                    TextUtils.isEmpty(entry.typeString) ? View.GONE : View.VISIBLE);
+            if (!TextUtils.isEmpty(entry.typeString)) {
+                views.type.setText(entry.typeString.toUpperCase());
+                views.type.setVisibility(View.VISIBLE);
+                if (isFirstOfItsKind) {
+                    views.kind.setVisibility(View.GONE);
+                }
+            } else {
+                views.type.setVisibility(View.GONE);
+            }
 
             views.data.setText(entry.data);
             setMaxLines(views.data, entry.maxLines);
@@ -928,24 +1056,6 @@
                 views.footer.setVisibility(View.GONE);
             }
 
-            // Set the action icon
-            final ImageView action = views.actionIcon;
-            if (entry.actionIcon != -1) {
-                Drawable actionIcon;
-                if (entry.resPackageName != null) {
-                    // Load external resources through PackageManager
-                    actionIcon = mContext.getPackageManager().getDrawable(entry.resPackageName,
-                            entry.actionIcon, null);
-                } else {
-                    actionIcon = resources.getDrawable(entry.actionIcon);
-                }
-                action.setImageDrawable(actionIcon);
-                action.setVisibility(View.VISIBLE);
-            } else {
-                // Things should still line up as if there was an icon, so make it invisible
-                action.setVisibility(View.INVISIBLE);
-            }
-
             // Set the presence icon
             final Drawable presenceIcon = ContactPresenceIconUtil.getPresenceIcon(
                     mContext, entry.presence);
@@ -972,11 +1082,11 @@
 
             if (entry.secondaryIntent != null && secondaryActionIcon != null) {
                 secondaryActionView.setImageDrawable(secondaryActionIcon);
-                secondaryActionView.setTag(entry);
-                secondaryActionView.setVisibility(View.VISIBLE);
+                views.secondaryActionButtonContainer.setTag(entry);
+                views.secondaryActionButtonContainer.setVisibility(View.VISIBLE);
                 views.secondaryActionDivider.setVisibility(View.VISIBLE);
             } else {
-                secondaryActionView.setVisibility(View.GONE);
+                views.secondaryActionButtonContainer.setVisibility(View.GONE);
                 views.secondaryActionDivider.setVisibility(View.GONE);
             }
         }
@@ -998,8 +1108,9 @@
                 if (mListener == null) return;
                 if (v == null) return;
                 final ViewEntry entry = (ViewEntry) v.getTag();
-                if (entry == null) return;
-                final Intent intent = entry.secondaryIntent;
+                if (entry == null || !(entry instanceof DetailViewEntry)) return;
+                final DetailViewEntry detailViewEntry = (DetailViewEntry) entry;
+                final Intent intent = detailViewEntry.secondaryIntent;
                 if (intent == null) return;
                 mListener.onItemClicked(intent);
             }
@@ -1007,40 +1118,42 @@
 
         @Override
         public int getCount() {
-            int count = 0;
-            final int numSections = mSections.size();
-            for (int i = 0; i < numSections; i++) {
-                final ArrayList<ViewEntry> section = mSections.get(i);
-                count += section.size();
-            }
-            return count;
+            return mAllEntries.size();
         }
 
         @Override
-        public Object getItem(int position) {
-            return getEntry(position);
+        public ViewEntry getItem(int position) {
+            return mAllEntries.get(position);
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            return mAllEntries.get(position).getViewType();
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return VIEW_TYPE_COUNT;
         }
 
         @Override
         public long getItemId(int position) {
-            final ViewEntry entry = getEntry(position);
+            final ViewEntry entry = mAllEntries.get(position);
             if (entry != null) {
-                return entry.id;
+                return entry.getId();
             }
             return -1;
         }
 
-        private ViewEntry getEntry(int position) {
-            final int numSections = mSections.size();
-            for (int i = 0; i < numSections; i++) {
-                final ArrayList<ViewEntry> section = mSections.get(i);
-                final int sectionSize = section.size();
-                if (position < sectionSize) {
-                    return section.get(position);
-                }
-                position -= sectionSize;
-            }
-            return null;
+        @Override
+        public boolean areAllItemsEnabled() {
+            // Header will always be an item that is not enabled.
+            return false;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return getItem(position).isEnabled();
         }
     }
 
@@ -1173,7 +1286,7 @@
     }
 
     @Override
-    public void onAccountChosen(Account account) {
+    public void onAccountChosen(int requestCode, Account account) {
         createCopy(account);
     }
 
@@ -1186,9 +1299,9 @@
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         if (mListener == null) return;
-        final ViewEntry entry = mAdapter.getEntry(position);
-        if (entry == null) return;
-        final Intent intent = entry.intent;
+        final ViewEntry entry = mAdapter.getItem(position);
+        if (entry == null || !(entry instanceof DetailViewEntry)) return;
+        final Intent intent = ((DetailViewEntry) entry).intent;
         if (intent == null) return;
         mListener.onItemClicked(intent);
     }
@@ -1224,7 +1337,7 @@
 
                 int index = mListView.getSelectedItemPosition();
                 if (index != -1) {
-                    final ViewEntry entry = mAdapter.getEntry(index);
+                    final DetailViewEntry entry = (DetailViewEntry) mAdapter.getItem(index);
                     if (entry != null && entry.intent != null &&
                             entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
                         mContext.startActivity(entry.intent);
@@ -1262,7 +1375,7 @@
 
         @Override
         public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
-            if (!((ContactLoader)loader).getLookupUri().equals(mLookupUri)) {
+            if (!mLookupUri.equals(data.getUri())) {
                 return;
             }
 
diff --git a/src/com/android/contacts/detail/ContactDetailHeaderView.java b/src/com/android/contacts/detail/ContactDetailHeaderView.java
index 4b211b6..795ed62 100644
--- a/src/com/android/contacts/detail/ContactDetailHeaderView.java
+++ b/src/com/android/contacts/detail/ContactDetailHeaderView.java
@@ -20,6 +20,8 @@
 import com.android.contacts.ContactLoader.Result;
 import com.android.contacts.ContactSaveService;
 import com.android.contacts.R;
+import com.android.contacts.format.FormatUtils;
+import com.android.contacts.preference.ContactsPreferences;
 import com.android.contacts.util.ContactBadgeUtil;
 
 import android.content.ClipData;
@@ -31,10 +33,13 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Typeface;
 import android.net.Uri;
+import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.DisplayNameSources;
+import android.text.Spanned;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -122,7 +127,8 @@
     public void loadData(ContactLoader.Result contactData) {
         mContactUri = contactData.getLookupUri();
 
-        setDisplayName(contactData.getDisplayName(), contactData.getPhoneticName());
+        setDisplayName(contactData.getDisplayName(), contactData.getAltDisplayName(),
+                contactData.getPhoneticName());
         setCompany(contactData);
         if (contactData.isLoadingPhoto()) {
             setPhoto(null, false);
@@ -188,8 +194,38 @@
     /**
      * Set the display name and phonetic name to show in the header.
      */
-    private void setDisplayName(CharSequence displayName, CharSequence phoneticName) {
-        mDisplayNameView.setText(displayName);
+    private void setDisplayName(CharSequence displayName, CharSequence altDisplayName,
+            CharSequence phoneticName) {
+
+        // Check the preference for display name ordering, and bold the contact's first name if
+        // possible.
+        ContactsPreferences prefs = new ContactsPreferences(getContext());
+        CharSequence styledName = "";
+        if (!TextUtils.isEmpty(displayName) && !TextUtils.isEmpty(altDisplayName)) {
+            if (prefs.getDisplayOrder() == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+                int overlapPoint = FormatUtils.overlapPoint(
+                        displayName.toString(), altDisplayName.toString());
+                if (overlapPoint > 0) {
+                    styledName = FormatUtils.applyStyleToSpan(Typeface.BOLD,
+                            displayName, 0, overlapPoint, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {
+                    styledName = displayName;
+                }
+            } else {
+                // Displaying alternate display name.
+                int overlapPoint = FormatUtils.overlapPoint(
+                        altDisplayName.toString(), displayName.toString());
+                if (overlapPoint > 0) {
+                    styledName = FormatUtils.applyStyleToSpan(Typeface.BOLD,
+                            altDisplayName, overlapPoint, altDisplayName.length(),
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {
+                    styledName = altDisplayName;
+                }
+            }
+        }
+        mDisplayNameView.setText(styledName);
+
         if (TextUtils.isEmpty(phoneticName)) {
             mPhoneticNameView.setVisibility(View.GONE);
         } else {
diff --git a/src/com/android/contacts/TwelveKeyDialer.java b/src/com/android/contacts/dialpad/DialpadFragment.java
similarity index 85%
rename from src/com/android/contacts/TwelveKeyDialer.java
rename to src/com/android/contacts/dialpad/DialpadFragment.java
index b93375c..58ffb9e 100644
--- a/src/com/android/contacts/TwelveKeyDialer.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -14,17 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.contacts;
+package com.android.contacts.dialpad;
 
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
+import com.android.contacts.SpecialCharSequenceMgr;
+import com.android.contacts.activities.DialtactsActivity;
 import com.android.internal.telephony.ITelephony;
 import com.android.phone.CallLogAsync;
 import com.android.phone.HapticFeedback;
 
 import android.app.Activity;
-import android.content.ActivityNotFoundException;
+import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
@@ -36,12 +39,11 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.provider.Settings;
+import android.provider.Contacts.Intents.Insert;
 import android.provider.Contacts.People;
 import android.provider.Contacts.Phones;
 import android.provider.Contacts.PhonesColumns;
-import android.provider.Contacts.Intents.Insert;
+import android.provider.Settings;
 import android.telephony.PhoneNumberFormattingTextWatcher;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.PhoneStateListener;
@@ -54,12 +56,10 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
+import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
-import android.view.Window;
-import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.EditText;
@@ -68,14 +68,15 @@
 import android.widget.TextView;
 
 /**
- * Dialer activity that displays the typical twelve key interface.
+ * Fragment that displays a twelve-key phone dialpad.
  */
-@SuppressWarnings("deprecation")
-public class TwelveKeyDialer extends Activity implements View.OnClickListener,
+public class DialpadFragment extends Fragment
+        implements View.OnClickListener,
         View.OnLongClickListener, View.OnKeyListener,
         AdapterView.OnItemClickListener, TextWatcher {
+    private static final String TAG = "DialpadFragment";
+
     private static final String EMPTY_NUMBER = "";
-    private static final String TAG = "TwelveKeyDialer";
 
     /** The length of DTMF tones in milliseconds */
     private static final int TONE_LENGTH_MS = 150;
@@ -173,7 +174,7 @@
     }
 
     public void afterTextChanged(Editable input) {
-        if (SpecialCharSequenceMgr.handleChars(this, input.toString(), mDigits)) {
+        if (SpecialCharSequenceMgr.handleChars(getActivity(), input.toString(), mDigits)) {
             // A special sequence was entered, clear the digits
             mDigits.getText().clear();
         }
@@ -189,24 +190,31 @@
     }
 
     @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
 
-        mCurrentCountryIso = ContactsUtils.getCurrentCountryIso(this);
-        Resources r = getResources();
-        // Do not show title in the case the device is in carmode.
-        if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) ==
-                Configuration.UI_MODE_TYPE_CAR) {
-            requestWindowFeature(Window.FEATURE_NO_TITLE);
+        mCurrentCountryIso = ContactsUtils.getCurrentCountryIso(getActivity());
+
+        try {
+            mHaptic.init(getActivity(),
+                         getResources().getBoolean(R.bool.config_enable_dialer_key_vibration));
+        } catch (Resources.NotFoundException nfe) {
+             Log.e(TAG, "Vibrate control bool missing.", nfe);
         }
-        // Set the content view
-        setContentView(getContentViewResource());
+
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+        View fragmentView = inflater.inflate(R.layout.dialpad_fragment, container, false);
 
         // Load up the resources for the text field.
+        Resources r = getResources();
         mDigitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
         mDigitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield);
 
-        mDigits = (EditText) findViewById(R.id.digits);
+        mDigits = (EditText) fragmentView.findViewById(R.id.digits);
         mDigits.setKeyListener(DialerKeyListener.getInstance());
         mDigits.setOnClickListener(this);
         mDigits.setOnKeyListener(this);
@@ -214,12 +222,12 @@
         maybeAddNumberFormatting();
 
         // Check for the presence of the keypad
-        View view = findViewById(R.id.one);
-        if (view != null) {
-            setupKeypad();
+        View oneButton = fragmentView.findViewById(R.id.one);
+        if (oneButton != null) {
+            setupKeypad(fragmentView);
         }
 
-        mVoicemailDialAndDeleteRow = findViewById(R.id.voicemailAndDialAndDelete);
+        mVoicemailDialAndDeleteRow = fragmentView.findViewById(R.id.voicemailAndDialAndDelete);
 
         initVoicemailButton();
 
@@ -233,12 +241,11 @@
             mDialButton = null;
         }
 
-        view = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
-        view.setOnClickListener(this);
-        view.setOnLongClickListener(this);
-        mDelete = view;
+        mDelete = mVoicemailDialAndDeleteRow.findViewById(R.id.deleteButton);
+        mDelete.setOnClickListener(this);
+        mDelete.setOnLongClickListener(this);
 
-        mDialpad = findViewById(R.id.dialpad);  // This is null in landscape mode.
+        mDialpad = fragmentView.findViewById(R.id.dialpad);  // This is null in landscape mode.
 
         // In landscape we put the keyboard in phone mode.
         // In portrait we prevent the soft keyboard to show since the
@@ -250,47 +257,42 @@
         }
 
         // Set up the "dialpad chooser" UI; see showDialpadChooser().
-        mDialpadChooser = (ListView) findViewById(R.id.dialpadChooser);
+        mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser);
         mDialpadChooser.setOnItemClickListener(this);
 
-        if (!resolveIntent() && icicle != null) {
-            super.onRestoreInstanceState(icicle);
-        }
-
-        try {
-            mHaptic.init(this, r.getBoolean(R.bool.config_enable_dialer_key_vibration));
-        } catch (Resources.NotFoundException nfe) {
-             Log.e(TAG, "Vibrate control bool missing.", nfe);
-        }
-
+        return fragmentView;
     }
 
-    @Override
-    protected void onRestoreInstanceState(Bundle icicle) {
-        // Do nothing, state is restored in onCreate() if needed
+    public EditText getDigitsWidget() {
+        return mDigits;
     }
 
-    protected void maybeAddNumberFormatting() {
+    private void maybeAddNumberFormatting() {
         mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher(mCurrentCountryIso));
     }
 
     /**
-     * Overridden by subclasses to control the resource used by the content view.
+     * Handles the intent that launched us.
+     *
+     * We can be launched either with ACTION_DIAL or ACTION_VIEW (which
+     * may include a phone number to pre-load), or ACTION_MAIN (which just
+     * brings up a blank dialpad).
+     *
+     * @return true IFF the current intent has the DialtactsActivity.EXTRA_IGNORE_STATE
+     *    extra set to true, which indicates (to our container) that we should ignore
+     *    any possible saved state, and instead reset our state based on the parent's
+     *    intent.
      */
-    protected int getContentViewResource() {
-        return R.layout.twelve_key_dialer;
-    }
-
-    private boolean resolveIntent() {
+    public boolean resolveIntent() {
         boolean ignoreState = false;
 
         // Find the proper intent
         final Intent intent;
-        if (isChild()) {
-            intent = getParent().getIntent();
+        if (getActivity().isChild()) {
+            intent = getActivity().getParent().getIntent();
             ignoreState = intent.getBooleanExtra(DialtactsActivity.EXTRA_IGNORE_STATE, false);
         } else {
-            intent = getIntent();
+            intent = getActivity().getIntent();
         }
         // Log.i(TAG, "==> resolveIntent(): intent: " + intent);
 
@@ -317,7 +319,7 @@
                     if (People.CONTENT_ITEM_TYPE.equals(type)
                             || Phones.CONTENT_ITEM_TYPE.equals(type)) {
                         // Query the phone number
-                        Cursor c = getContentResolver().query(intent.getData(),
+                        Cursor c = getActivity().getContentResolver().query(intent.getData(),
                                 new String[] {PhonesColumns.NUMBER, PhonesColumns.NUMBER_KEY},
                                 null, null, null);
                         if (c != null) {
@@ -363,7 +365,7 @@
         return ignoreState;
     }
 
-    protected void setFormattedDigits(String data, String normalizedNumber) {
+    private void setFormattedDigits(String data, String normalizedNumber) {
         // strip the non-dialable numbers out of the data string.
         String dialString = PhoneNumberUtils.extractNetworkPortion(data);
         dialString =
@@ -377,16 +379,32 @@
         }
     }
 
-    @Override
-    protected void onNewIntent(Intent newIntent) {
-        setIntent(newIntent);
-        resolveIntent();
+    private void setupKeypad(View fragmentView) {
+        // Setup the listeners for the buttons
+        View view = fragmentView.findViewById(R.id.one);
+        view.setOnClickListener(this);
+        view.setOnLongClickListener(this);
+
+        fragmentView.findViewById(R.id.two).setOnClickListener(this);
+        fragmentView.findViewById(R.id.three).setOnClickListener(this);
+        fragmentView.findViewById(R.id.four).setOnClickListener(this);
+        fragmentView.findViewById(R.id.five).setOnClickListener(this);
+        fragmentView.findViewById(R.id.six).setOnClickListener(this);
+        fragmentView.findViewById(R.id.seven).setOnClickListener(this);
+        fragmentView.findViewById(R.id.eight).setOnClickListener(this);
+        fragmentView.findViewById(R.id.nine).setOnClickListener(this);
+        fragmentView.findViewById(R.id.star).setOnClickListener(this);
+
+        view = fragmentView.findViewById(R.id.zero);
+        view.setOnClickListener(this);
+        view.setOnLongClickListener(this);
+
+        fragmentView.findViewById(R.id.pound).setOnClickListener(this);
     }
 
-    @Override
-    protected void onPostCreate(Bundle savedInstanceState) {
-        super.onPostCreate(savedInstanceState);
-
+    // Do some stuff that needs to happen only once, but which we
+    // can't do directly from onCreate().
+    public void onPostCreate() {
         // This can't be done in onCreate(), since the auto-restoring of the digits
         // will play DTMF tones for all the old digits if it is when onRestoreSavedInstanceState()
         // is called. This method will be called every time the activity is created, and
@@ -394,31 +412,8 @@
         mDigits.addTextChangedListener(this);
     }
 
-    private void setupKeypad() {
-        // Setup the listeners for the buttons
-        View view = findViewById(R.id.one);
-        view.setOnClickListener(this);
-        view.setOnLongClickListener(this);
-
-        findViewById(R.id.two).setOnClickListener(this);
-        findViewById(R.id.three).setOnClickListener(this);
-        findViewById(R.id.four).setOnClickListener(this);
-        findViewById(R.id.five).setOnClickListener(this);
-        findViewById(R.id.six).setOnClickListener(this);
-        findViewById(R.id.seven).setOnClickListener(this);
-        findViewById(R.id.eight).setOnClickListener(this);
-        findViewById(R.id.nine).setOnClickListener(this);
-        findViewById(R.id.star).setOnClickListener(this);
-
-        view = findViewById(R.id.zero);
-        view.setOnClickListener(this);
-        view.setOnLongClickListener(this);
-
-        findViewById(R.id.pound).setOnClickListener(this);
-    }
-
     @Override
-    protected void onResume() {
+    public void onResume() {
         super.onResume();
 
         // Query the last dialed number. Do it first because hitting
@@ -426,7 +421,7 @@
         queryLastOutgoingCall();
 
         // retrieve the DTMF tone play back setting.
-        mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
+        mDTMFToneEnabled = Settings.System.getInt(getActivity().getContentResolver(),
                 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
 
         // Retrieve the haptic feedback setting.
@@ -434,14 +429,14 @@
 
         // if the mToneGenerator creation fails, just continue without it.  It is
         // a local audio signal, and is not as important as the dtmf tone itself.
-        synchronized(mToneGeneratorLock) {
+        synchronized (mToneGeneratorLock) {
             if (mToneGenerator == null) {
                 try {
                     // we want the user to be able to control the volume of the dial tones
                     // outside of a call, so we use the stream type that is also mapped to the
                     // volume control keys for this activity
                     mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
-                    setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
+                    getActivity().setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
                 } catch (RuntimeException e) {
                     Log.w(TAG, "Exception caught while creating local tone generator: " + e);
                     mToneGenerator = null;
@@ -449,7 +444,7 @@
             }
         }
 
-        Activity parent = getParent();
+        Activity parent = getActivity().getParent();
         // See if we were invoked with a DIAL intent. If we were, fill in the appropriate
         // digits in the dialer field.
         if (parent != null && parent instanceof DialtactsActivity) {
@@ -462,7 +457,8 @@
         // While we're in the foreground, listen for phone state changes,
         // purely so that we can take down the "dialpad chooser" if the
         // phone becomes idle while the chooser UI is visible.
-        TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
+        TelephonyManager telephonyManager =
+                (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
         telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
 
         // Potentially show hint text in the mDigits field when the user
@@ -489,27 +485,15 @@
     }
 
     @Override
-    public void onWindowFocusChanged(boolean hasFocus) {
-        if (hasFocus) {
-            // Hide soft keyboard, if visible (it's fugly over button dialer).
-            // The only known case where this will be true is when launching the dialer with
-            // ACTION_DIAL via a soft keyboard.  we dismiss it here because we don't
-            // have a window token yet in onCreate / onNewIntent
-            InputMethodManager inputMethodManager = (InputMethodManager)
-                    getSystemService(Context.INPUT_METHOD_SERVICE);
-            inputMethodManager.hideSoftInputFromWindow(mDigits.getWindowToken(), 0);
-        }
-    }
-
-    @Override
-    protected void onPause() {
+    public void onPause() {
         super.onPause();
 
         // Stop listening for phone state changes.
-        TelephonyManager telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
+        TelephonyManager telephonyManager =
+                (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
         telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
 
-        synchronized(mToneGeneratorLock) {
+        synchronized (mToneGeneratorLock) {
             if (mToneGenerator != null) {
                 mToneGenerator.release();
                 mToneGenerator = null;
@@ -521,22 +505,25 @@
     }
 
     @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+
         mAddToContactMenuItem = menu.add(0, MENU_ADD_CONTACTS, 0, R.string.recentCalls_addToContact)
                 .setIcon(android.R.drawable.ic_menu_add);
         m2SecPauseMenuItem = menu.add(0, MENU_2S_PAUSE, 0, R.string.add_2sec_pause)
                 .setIcon(R.drawable.ic_menu_2sec_pause);
         mWaitMenuItem = menu.add(0, MENU_WAIT, 0, R.string.add_wait)
                 .setIcon(R.drawable.ic_menu_wait);
-        return true;
     }
 
     @Override
-    public boolean onPrepareOptionsMenu(Menu menu) {
+    public void onPrepareOptionsMenu(Menu menu) {
+        // If we have not been inflated yet, there is no menu
+        if (mDialpadChooser == null) return;
+
         // We never show a menu if the "choose dialpad" UI is up.
-        if (dialpadChooserVisible()) {
-            return false;
-        }
+        // Otherwise the menu is allowed (see onPrepareOptionsMenu() below.)
+        if (!dialpadChooserVisible()) return;
 
         if (isDigitsEmpty()) {
             mAddToContactMenuItem.setVisible(false);
@@ -587,46 +574,6 @@
                                                       strLength, strDigits));
             }
         }
-        return true;
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_CALL: {
-                long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
-                if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
-                    // Launch voice dialer
-                    Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
-                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    try {
-                        startActivity(intent);
-                    } catch (ActivityNotFoundException e) {
-                    }
-                }
-                return true;
-            }
-            case KeyEvent.KEYCODE_1: {
-                long timeDiff = SystemClock.uptimeMillis() - event.getDownTime();
-                if (timeDiff >= ViewConfiguration.getLongPressTimeout()) {
-                    // Long press detected, call voice mail
-                    callVoicemail();
-                }
-                return true;
-            }
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_CALL: {
-                dialButtonPressed();
-                return true;
-            }
-        }
-        return super.onKeyUp(keyCode, event);
     }
 
     private void keyPressed(int keyCode) {
@@ -647,6 +594,7 @@
         return false;
     }
 
+    @Override
     public void onClick(View view) {
         switch (view.getId()) {
             case R.id.one: {
@@ -759,10 +707,10 @@
         return false;
     }
 
-    void callVoicemail() {
+    public void callVoicemail() {
         startActivity(newVoicemailIntent());
         mDigits.getText().clear(); // TODO: Fix bug 1745781
-        finish();
+        getActivity().finish();
     }
 
     /**
@@ -784,7 +732,7 @@
      * user needs to press the dial button again, to dial it (general
      * case described above).
      */
-    void dialButtonPressed() {
+    public void dialButtonPressed() {
         if (isDigitsEmpty()) { // No number entered.
             if (phoneIsCdma() && phoneIsOffhook()) {
                 // This is really CDMA specific. On GSM is it possible
@@ -809,11 +757,10 @@
 
             startActivity(newDialNumberIntent(number));
             mDigits.getText().clear();  // TODO: Fix bug 1745781
-            finish();
+            getActivity().finish();
         }
     }
 
-
     /**
      * Plays the specified tone for TONE_LENGTH_MS milliseconds.
      *
@@ -834,16 +781,17 @@
         // call, rather than keeping a local flag that's updated in
         // onResume(), since it's possible to toggle silent mode without
         // leaving the current activity (via the ENDCALL-longpress menu.)
-        AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        AudioManager audioManager =
+                (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
         int ringerMode = audioManager.getRingerMode();
         if ((ringerMode == AudioManager.RINGER_MODE_SILENT)
             || (ringerMode == AudioManager.RINGER_MODE_VIBRATE)) {
             return;
         }
 
-        synchronized(mToneGeneratorLock) {
+        synchronized (mToneGeneratorLock) {
             if (mToneGenerator == null) {
-                Log.w(TAG, "playTone: mToneGenerator == null, tone: "+tone);
+                Log.w(TAG, "playTone: mToneGenerator == null, tone: " + tone);
                 return;
             }
 
@@ -879,7 +827,7 @@
             // Instantiate the DialpadChooserAdapter and hook it up to the
             // ListView.  We do this only once.
             if (mDialpadChooserAdapter == null) {
-                mDialpadChooserAdapter = new DialpadChooserAdapter(this);
+                mDialpadChooserAdapter = new DialpadChooserAdapter(getActivity());
                 mDialpadChooser.setAdapter(mDialpadChooserAdapter);
             }
         } else {
@@ -1049,7 +997,7 @@
         // the dialpad chooser is up.  In this case we can't show the
         // InCallScreen, and there's no point staying here in the Dialer,
         // so we just take the user back where he came from...)
-        finish();
+        getActivity().finish();
     }
 
     /**
@@ -1097,7 +1045,6 @@
         return phoneOffhook;
     }
 
-
     /**
      * Returns true whenever any one of the options from the menu is selected.
      * Code changes to support dialpad options
@@ -1131,7 +1078,7 @@
         selectionEnd = Math.max(anchor, point);
 
         Editable digits = mDigits.getText();
-        if (selectionStart != -1 ) {
+        if (selectionStart != -1) {
             if (selectionStart == selectionEnd) {
                 // then there is no selection. So insert the pause at this
                 // position and update the mDigits.
@@ -1171,7 +1118,6 @@
         mDelete.setEnabled(digitsNotEmpty);
     }
 
-
     /**
      * Check if voicemail is enabled/accessible.
      */
@@ -1202,7 +1148,7 @@
             if (start > digits.length()) return false;
 
             // preceding char is ';', so visible should be false
-            if (digits.charAt(start-1) == ';') return false;
+            if (digits.charAt(start - 1) == ';') return false;
 
             // next char is ';', so visible should be false
             if ((digits.length() > start) && (digits.charAt(start) == ';')) return false;
@@ -1212,7 +1158,7 @@
 
             // In this case we need to just check for ';' preceding to start
             // or next to end
-            if (digits.charAt(start-1) == ';') return false;
+            if (digits.charAt(start - 1) == ';') return false;
         }
         return true;
     }
@@ -1234,7 +1180,7 @@
         mLastNumberDialed = EMPTY_NUMBER;
         CallLogAsync.GetLastOutgoingCallArgs lastCallArgs =
                 new CallLogAsync.GetLastOutgoingCallArgs(
-                    this,
+                    getActivity(),
                     new CallLogAsync.OnLastOutgoingCallComplete() {
                         public void lastOutgoingCall(String number) {
                             // TODO: Filter out emergency numbers if
@@ -1247,16 +1193,6 @@
         mCallLog.getLastOutgoingCall(lastCallArgs);
     }
 
-    @Override
-    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
-            boolean globalSearch) {
-        if (globalSearch) {
-            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
-        } else {
-            ContactsSearchManager.startSearch(this, initialQuery);
-        }
-    }
-
     // Helpers for the call intents.
     private Intent newVoicemailIntent() {
         final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
diff --git a/src/com/android/contacts/editor/BaseRawContactEditorView.java b/src/com/android/contacts/editor/BaseRawContactEditorView.java
index 49ca863..38636f7 100644
--- a/src/com/android/contacts/editor/BaseRawContactEditorView.java
+++ b/src/com/android/contacts/editor/BaseRawContactEditorView.java
@@ -49,7 +49,6 @@
     private PhotoEditorView mPhoto;
     private boolean mHasPhotoEditor = false;
 
-    private View mHeader;
     private View mBody;
     private View mDivider;
 
@@ -67,15 +66,8 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mHeader = findViewById(R.id.header);
         mBody = findViewById(R.id.body);
         mDivider = findViewById(R.id.divider);
-        mHeader.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                setExpanded(!mExpanded);
-            }
-        });
 
         mPhoto = (PhotoEditorView)findViewById(R.id.edit_photo);
         mPhoto.setEnabled(isEnabled());
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index ae6788b..96d5843 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -31,6 +31,7 @@
 import com.android.contacts.model.EntityDeltaList;
 import com.android.contacts.model.EntityModifier;
 import com.android.contacts.model.GoogleAccountType;
+import com.android.contacts.util.AccountsListAdapter;
 
 import android.accounts.Account;
 import android.app.Activity;
@@ -78,7 +79,9 @@
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewGroup.MarginLayoutParams;
 import android.view.ViewStub;
+import android.widget.AdapterView;
 import android.widget.LinearLayout;
+import android.widget.ListPopupWindow;
 import android.widget.Toast;
 
 import java.io.File;
@@ -90,7 +93,7 @@
 import java.util.List;
 
 public class ContactEditorFragment extends Fragment implements
-        SplitContactConfirmationDialogFragment.Listener, SelectAccountDialogFragment.Listener,
+        SplitContactConfirmationDialogFragment.Listener,
         AggregationSuggestionEngine.Listener, AggregationSuggestionView.Listener,
         ExternalRawContactEditorView.Listener {
 
@@ -313,7 +316,7 @@
                 } else {
                     // No Account specified. Let the user choose
                     // Load Accounts async so that we can present them
-                    selectAccountAndCreateContact();
+                    createContact();
                 }
             } else if (ContactEditorActivity.ACTION_SAVE_COMPLETED.equals(mAction)) {
                 // do nothing
@@ -437,7 +440,11 @@
         }
     }
 
-    private void selectAccountAndCreateContact() {
+    /**
+     * Shows the account creation screen. An account associated with the contact is automatically
+     * selected. If there's no available account, device-local contact should be created.
+     */
+    private void createContact() {
         final ArrayList<Account> accounts =
                 AccountTypeManager.getInstance(mContext).getAccounts(true);
         // No Accounts available.  Create a phone-local contact.
@@ -446,21 +453,16 @@
             return;  // Don't show a dialog.
         }
 
-        // In the common case of a single account being writable, auto-select
-        // it without showing a dialog.
-        if (accounts.size() == 1) {
-            createContact(accounts.get(0));
-            return;  // Don't show a dialog.
-        }
-
-        final SelectAccountDialogFragment dialog = new SelectAccountDialogFragment();
-        dialog.setTargetFragment(this, 0);
-        dialog.show(getFragmentManager(), SelectAccountDialogFragment.TAG);
+        // We have an account switcher in "create-account" screen, so don't need to ask a user to
+        // select an account here.
+        createContact(accounts.get(0));
     }
 
+
     /**
-     * @param account may be null to signal a device-local contact should
-     *     be created.
+     * Shows account creation screen associated with a given account.
+     *
+     * @param account may be null to signal a device-local contact should be created.
      */
     private void createContact(Account account) {
         final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
@@ -476,31 +478,66 @@
         }
     }
 
+    /**
+     * Removes a current editor ({@link #mState}) and rebinds new editor for a new account.
+     * Some of old data are reused with new restriction enforced by the new account.
+     *
+     * @param oldState Old data being editted.
+     * @param oldAccount Old account associated with oldState.
+     * @param newAccount New account to be used.
+     */
+    private void rebindEditorsForNewContact(
+            EntityDelta oldState, Account oldAccount, Account newAccount) {
+        AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+        AccountType oldAccountType = accountTypes.getAccountType(oldAccount.type);
+        AccountType newAccountType = accountTypes.getAccountType(newAccount.type);
+
+        if (newAccountType.getCreateContactActivityClassName() != null) {
+            Log.w(TAG, "external activity called in rebind situation");
+            if (mListener != null) {
+                mListener.onCustomCreateContactActivityRequested(newAccount, mIntentExtras);
+            }
+        } else {
+            mState = null;
+            bindEditorsForNewContact(newAccount, newAccountType, oldState, oldAccountType);
+        }
+    }
+
     private void bindEditorsForNewContact(Account account, final AccountType accountType) {
+        bindEditorsForNewContact(account, accountType, null, null);
+    }
+
+    private void bindEditorsForNewContact(Account newAccount, final AccountType newAccountType,
+            EntityDelta oldState, AccountType oldAccountType) {
         mStatus = Status.EDITING;
 
         final ContentValues values = new ContentValues();
-        if (account != null) {
-            values.put(RawContacts.ACCOUNT_NAME, account.name);
-            values.put(RawContacts.ACCOUNT_TYPE, account.type);
+        if (newAccount != null) {
+            values.put(RawContacts.ACCOUNT_NAME, newAccount.name);
+            values.put(RawContacts.ACCOUNT_TYPE, newAccount.type);
         } else {
             values.putNull(RawContacts.ACCOUNT_NAME);
             values.putNull(RawContacts.ACCOUNT_TYPE);
         }
 
-        // Parse any values from incoming intent
         EntityDelta insert = new EntityDelta(ValuesDelta.fromAfter(values));
-        EntityModifier.parseExtras(mContext, accountType, insert, mIntentExtras);
+        if (oldState == null) {
+            // Parse any values from incoming intent
+            EntityModifier.parseExtras(mContext, newAccountType, insert, mIntentExtras);
+        } else {
+            EntityModifier.migrateStateForNewContact(mContext, oldState, insert,
+                    oldAccountType, newAccountType);
+        }
 
         // Ensure we have some default fields (if the account type does not support a field,
         // ensureKind will not add it, so it is safe to add e.g. Event)
-        EntityModifier.ensureKindExists(insert, accountType, Phone.CONTENT_ITEM_TYPE);
-        EntityModifier.ensureKindExists(insert, accountType, Email.CONTENT_ITEM_TYPE);
-        EntityModifier.ensureKindExists(insert, accountType, Note.CONTENT_ITEM_TYPE);
-        EntityModifier.ensureKindExists(insert, accountType, Organization.CONTENT_ITEM_TYPE);
-        EntityModifier.ensureKindExists(insert, accountType, Event.CONTENT_ITEM_TYPE);
-        EntityModifier.ensureKindExists(insert, accountType, Website.CONTENT_ITEM_TYPE);
-        EntityModifier.ensureKindExists(insert, accountType, StructuredPostal.CONTENT_ITEM_TYPE);
+        EntityModifier.ensureKindExists(insert, newAccountType, Phone.CONTENT_ITEM_TYPE);
+        EntityModifier.ensureKindExists(insert, newAccountType, Email.CONTENT_ITEM_TYPE);
+        EntityModifier.ensureKindExists(insert, newAccountType, Note.CONTENT_ITEM_TYPE);
+        EntityModifier.ensureKindExists(insert, newAccountType, Organization.CONTENT_ITEM_TYPE);
+        EntityModifier.ensureKindExists(insert, newAccountType, Event.CONTENT_ITEM_TYPE);
+        EntityModifier.ensureKindExists(insert, newAccountType, Website.CONTENT_ITEM_TYPE);
+        EntityModifier.ensureKindExists(insert, newAccountType, StructuredPostal.CONTENT_ITEM_TYPE);
 
         if (mState == null) {
             // Create state if none exists yet
@@ -545,6 +582,13 @@
                 editor = (BaseRawContactEditorView)
                         inflater.inflate(R.layout.raw_contact_editor_view, mContent, false);
             }
+            if (Intent.ACTION_INSERT.equals(mAction) && size == 1) {
+                final ArrayList<Account> accounts =
+                        AccountTypeManager.getInstance(mContext).getAccounts(true);
+                if (accounts.size() > 1) {
+                    addAccountSwitcher(mState.get(0), editor);
+                }
+            }
             editor.setEnabled(mEnabled);
 
             mContent.addView(editor);
@@ -613,6 +657,40 @@
         }
     }
 
+    private void addAccountSwitcher(
+            final EntityDelta currentState, BaseRawContactEditorView editor) {
+        ValuesDelta values = currentState.getValues();
+        final Account currentAccount = new Account(
+                values.getAsString(RawContacts.ACCOUNT_NAME),
+                values.getAsString(RawContacts.ACCOUNT_TYPE));
+        final View accountView = editor.findViewById(R.id.account);
+        final View anchorView = editor.findViewById(R.id.anchor_for_account_switcher);
+        accountView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                final ListPopupWindow popup = new ListPopupWindow(mContext, null);
+                final AccountsListAdapter adapter = new AccountsListAdapter(mContext, true);
+                popup.setWidth(anchorView.getWidth());
+                popup.setAnchorView(anchorView);
+                popup.setAdapter(adapter);
+                popup.setModal(true);
+                popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
+                popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                    @Override
+                    public void onItemClick(AdapterView<?> parent, View view, int position,
+                            long id) {
+                        popup.dismiss();
+                        Account newAccount = adapter.getItem(position);
+                        if (!newAccount.equals(currentAccount)) {
+                            rebindEditorsForNewContact(currentState, currentAccount, newAccount);
+                        }
+                    }
+                });
+                popup.show();
+            }
+        });
+    }
+
     @Override
     public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
         inflater.inflate(R.menu.edit, menu);
@@ -980,11 +1058,6 @@
         void onContactSplit(Uri newLookupUri);
 
         /**
-         * User was presented with an account selection and couldn't decide.
-         */
-        void onAccountSelectorAborted();
-
-        /**
          * User has tapped Revert, close the fragment now.
          */
         void onReverted();
@@ -1538,24 +1611,6 @@
         save(SaveMode.SPLIT);
     }
 
-    /**
-     * Account was chosen in the selector. Create a RawContact for this account now
-     */
-    @Override
-    public void onAccountChosen(Account account) {
-        createContact(account);
-    }
-
-    /**
-     * The account selector has been aborted. If we are in "New" mode, we have to close now
-     */
-    @Override
-    public void onAccountSelectorCancelled() {
-        if (!hasValidState() && mListener != null) {
-            mListener.onAccountSelectorAborted();
-        }
-    }
-
     private final class PhotoEditorListener
             implements EditorListener, PhotoActionPopup.Listener {
         private final BaseRawContactEditorView mEditor;
diff --git a/src/com/android/contacts/editor/Editor.java b/src/com/android/contacts/editor/Editor.java
index d733e68..a70bf3f 100644
--- a/src/com/android/contacts/editor/Editor.java
+++ b/src/com/android/contacts/editor/Editor.java
@@ -16,8 +16,8 @@
 
 package com.android.contacts.editor;
 
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 
 import android.provider.ContactsContract.Data;
@@ -27,9 +27,7 @@
  * {@link ValuesDelta} object.
  */
 public interface Editor {
-    /**
-     * Listener for an {@link Editor}, usually to handle deleted items.
-     */
+
     public interface EditorListener {
         /**
          * Called when the given {@link Editor} has been deleted.
@@ -44,14 +42,21 @@
 
         public static final int REQUEST_PICK_PHOTO = 1;
         public static final int FIELD_CHANGED = 2;
+        public static final int FIELD_TURNED_EMPTY = 3;
+        public static final int FIELD_TURNED_NON_EMPTY = 4;
 
         // The editor has switched between different representations of the same
         // data, e.g. from full name to structured name
-        public static final int EDITOR_FORM_CHANGED = 3;
+        public static final int EDITOR_FORM_CHANGED = 5;
     }
 
     /**
-     * Prepare this editor for the given {@link ValuesDelta}, which
+     * Returns whether or not all the fields are empty in this {@link Editor}.
+     */
+    public boolean isEmpty();
+
+    /**
+     * Prepares this editor for the given {@link ValuesDelta}, which
      * builds any needed views. Any changes performed by the user will be
      * written back to that same object.
      */
diff --git a/src/com/android/contacts/editor/EventFieldEditorView.java b/src/com/android/contacts/editor/EventFieldEditorView.java
index 906dbad..bf93d26 100644
--- a/src/com/android/contacts/editor/EventFieldEditorView.java
+++ b/src/com/android/contacts/editor/EventFieldEditorView.java
@@ -20,9 +20,9 @@
 import com.android.contacts.datepicker.DatePicker;
 import com.android.contacts.datepicker.DatePickerDialog;
 import com.android.contacts.datepicker.DatePickerDialog.OnDateSetListener;
-import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.AccountType.EditField;
 import com.android.contacts.model.AccountType.EventEditType;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.android.contacts.util.DateUtils;
@@ -32,10 +32,8 @@
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.Gravity;
 import android.view.View;
 import android.widget.Button;
-import android.widget.LinearLayout;
 
 import java.text.ParsePosition;
 import java.util.Calendar;
@@ -49,7 +47,7 @@
     /**
      * Exchange requires 8:00 for birthdays
      */
-    private final int DEFAULT_HOUR = 8;
+    private final static int DEFAULT_HOUR = 8;
 
     private Button mDateView;
 
@@ -65,57 +63,30 @@
         super(context, attrs, defStyle);
     }
 
+    /** {@inheritDoc} */
     @Override
-    public int getBaseline(int row) {
-        int baseline = super.getBaseline(row);
-        if (mDateView != null) {
-            // The date view will be centered vertically in the corresponding line item
-            int lineItemHeight = getLineItemHeight(row);
-            int offset = (lineItemHeight - mDateView.getMeasuredHeight()) / 2;
-            baseline = Math.max(baseline, offset + mDateView.getBaseline());
-        }
-        return baseline;
-    }
+    protected void onFinishInflate() {
+        super.onFinishInflate();
 
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-
-        int l1 = getPaddingLeft();
-        int t1 = getPaddingTop();
-        int r1 = getMeasuredWidth() - getPaddingRight();
-
-        // Fields
-        // Subtract buttons left and right if necessary
-        final int labelWidth = (getLabel() != null) ? getLabel().getMeasuredWidth() : 0;
-        final int deleteWidth = (getDelete() != null) ? getDelete().getMeasuredWidth() : 0;
-        final int r2 = r1 - deleteWidth - labelWidth;
-        if (mDateView != null) {
-            int height = mDateView.getMeasuredHeight();
-            int baseline = getBaseline(0);
-            int top = t1 + baseline - mDateView.getBaseline();
-            mDateView.layout(
-                    l1, top,
-                    r2, top + height);
-        }
-    }
-
-    @Override
-    protected int getLineItemHeight(int row) {
-        int height = mDateView == null ? 0 : mDateView.getMeasuredHeight();
-        return Math.max(height, super.getLineItemHeight(row));
+        mDateView = (Button) findViewById(R.id.date_view);
+        mDateView.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showDialog(R.id.dialog_event_date_picker);
+            }
+        });
     }
 
     @Override
     protected void requestFocusForFirstEditField() {
-        if (mDateView != null) mDateView.requestFocus();
+        mDateView.requestFocus();
     }
 
     @Override
     public void setEnabled(boolean enabled) {
         super.setEnabled(enabled);
 
-        if (mDateView != null) mDateView.setEnabled(!isReadOnly() && enabled);
+        mDateView.setEnabled(!isReadOnly() && enabled);
     }
 
     @Override
@@ -124,20 +95,7 @@
         if (kind.fieldList.size() != 1) throw new IllegalStateException("kind must have 1 field");
         super.setValues(kind, entry, state, readOnly, vig);
 
-        if (mDateView == null) {
-
-            mDateView = new Button(getContext(), null, android.R.attr.spinnerStyle);
-            mDateView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
-                    LayoutParams.WRAP_CONTENT));
-            mDateView.setEnabled(isEnabled() && !readOnly);
-            mDateView.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    showDialog(R.id.dialog_event_date_picker);
-                }
-            });
-            addView(mDateView);
-        }
+        mDateView.setEnabled(isEnabled() && !readOnly);
 
         rebuildDateView();
     }
@@ -153,6 +111,11 @@
     }
 
     @Override
+    public boolean isEmpty() {
+        return TextUtils.isEmpty(mDateView.getText());
+    }
+
+    @Override
     public Dialog createDialog(Bundle bundle) {
         if (bundle == null) throw new IllegalArgumentException("bundle must not be null");
         int dialogId = bundle.getInt(DIALOG_ID_KEY);
@@ -269,4 +232,11 @@
                 oldYear, oldMonth, oldDay, isYearOptional);
         return resultDialog;
     }
+
+    /**
+     * @return Default hour which should be used for birthday field.
+     */
+    public static int getDefaultHourForBirthday() {
+        return DEFAULT_HOUR;
+    }
 }
diff --git a/src/com/android/contacts/editor/ExternalRawContactEditorView.java b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
index aaa1e44..89cace0 100644
--- a/src/com/android/contacts/editor/ExternalRawContactEditorView.java
+++ b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
@@ -20,7 +20,7 @@
 import com.android.contacts.R;
 import com.android.contacts.editor.ExternalRawContactEditorView.Listener;
 import com.android.contacts.model.AccountType;
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.android.contacts.model.EntityModifier;
@@ -60,9 +60,9 @@
     private Button mEditExternallyButton;
     private ViewGroup mGeneral;
 
-    private ImageView mHeaderIcon;
-    private TextView mHeaderAccountType;
-    private TextView mHeaderAccountName;
+    private ImageView mAccountIcon;
+    private TextView mAccountTypeTextView;
+    private TextView mAccountNameTextView;
 
     private String mAccountName;
     private String mAccountType;
@@ -102,9 +102,9 @@
         mEditExternallyButton.setOnClickListener(this);
         mGeneral = (ViewGroup)findViewById(R.id.sect_general);
 
-        mHeaderIcon = (ImageView) findViewById(R.id.header_icon);
-        mHeaderAccountType = (TextView) findViewById(R.id.header_account_type);
-        mHeaderAccountName = (TextView) findViewById(R.id.header_account_name);
+        mAccountIcon = (ImageView) findViewById(R.id.account_icon);
+        mAccountTypeTextView = (TextView) findViewById(R.id.account_type);
+        mAccountNameTextView = (TextView) findViewById(R.id.account_name);
     }
 
     /**
@@ -132,11 +132,11 @@
             accountType = mContext.getString(R.string.account_phone);
         }
         if (!TextUtils.isEmpty(mAccountName)) {
-            mHeaderAccountName.setText(
+            mAccountNameTextView.setText(
                     mContext.getString(R.string.from_account_format, mAccountName));
         }
-        mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType));
-        mHeaderIcon.setImageDrawable(type.getDisplayIcon(mContext));
+        mAccountTypeTextView.setText(mContext.getString(R.string.account_type_format, accountType));
+        mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
 
         mRawContactId = values.getAsLong(RawContacts._ID);
 
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
index 8845aa3..d242a1b 100644
--- a/src/com/android/contacts/editor/GroupMembershipView.java
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -19,7 +19,7 @@
 import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
 import com.android.contacts.interactions.GroupCreationDialogFragment;
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.android.contacts.model.EntityModifier;
diff --git a/src/com/android/contacts/editor/KindSectionView.java b/src/com/android/contacts/editor/KindSectionView.java
index aaeafac..b472279 100644
--- a/src/com/android/contacts/editor/KindSectionView.java
+++ b/src/com/android/contacts/editor/KindSectionView.java
@@ -18,23 +18,22 @@
 
 import com.android.contacts.R;
 import com.android.contacts.editor.Editor.EditorListener;
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.android.contacts.model.EntityModifier;
 
 import android.content.Context;
-import android.os.Handler;
-import android.provider.ContactsContract.Data;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Custom view for an entire section of data as segmented by
@@ -45,9 +44,8 @@
     private static final String TAG = "KindSectionView";
 
     private ViewGroup mEditors;
-    private View mAddPlusButtonContainer;
-    private ImageButton mAddPlusButton;
-    private TextView mTitle;
+    private View mAddFieldFooter;
+    private TextView mAddFieldText;
     private String mTitleString;
 
     private DataKind mKind;
@@ -56,7 +54,7 @@
 
     private ViewIdGenerator mViewIdGenerator;
 
-    private int mMinLineItemHeight;
+    private LayoutInflater mInflater;
 
     public KindSectionView(Context context) {
         this(context, null);
@@ -64,8 +62,6 @@
 
     public KindSectionView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mMinLineItemHeight = context.getResources().getDimensionPixelSize(
-                R.dimen.editor_min_line_item_height);
     }
 
     @Override
@@ -78,26 +74,13 @@
             }
         }
 
-        if (mAddPlusButton != null) {
-            mAddPlusButton.setEnabled(enabled && !mReadOnly);
+        if (enabled && !mReadOnly) {
+            mAddFieldFooter.setVisibility(View.VISIBLE);
+        } else {
+            mAddFieldFooter.setVisibility(View.GONE);
         }
     }
 
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-
-        if (mAddPlusButton == null || mEditors == null || mEditors.getChildCount() < 2) {
-            return;
-        }
-
-        // Align the "+" button with the "-" button in the last editor
-        View lastEditor = mEditors.getChildAt(mEditors.getChildCount() - 1);
-        int top = lastEditor.getTop();
-        mAddPlusButtonContainer.layout(mAddPlusButtonContainer.getLeft(), top,
-                mAddPlusButtonContainer.getRight(), top + mAddPlusButtonContainer.getHeight());
-    }
-
     public boolean isReadOnly() {
         return mReadOnly;
     }
@@ -108,37 +91,36 @@
         setDrawingCacheEnabled(true);
         setAlwaysDrawnWithCacheEnabled(true);
 
-        mEditors = (ViewGroup)findViewById(R.id.kind_editors);
+        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
-        mAddPlusButtonContainer = findViewById(R.id.kind_plus_container);
-        mAddPlusButton = (ImageButton) findViewById(R.id.kind_plus);
-        mAddPlusButton.setOnClickListener(new OnClickListener() {
+        mEditors = (ViewGroup)findViewById(R.id.kind_editors);
+        mAddFieldText = (TextView) findViewById(R.id.add_text);
+        mAddFieldFooter = findViewById(R.id.add_field_footer);
+        mAddFieldFooter.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                // defer action so that the pressed state of the button is visible shortly
-                new Handler().post(new Runnable() {
-                    @Override
-                    public void run() {
-                        addItem();
-                    }
-                });
+                // Setup click listener to add an empty field when the footer is clicked.
+                mAddFieldFooter.setVisibility(View.GONE);
+                addItem();
             }
         });
-
-        mTitle = (TextView)findViewById(R.id.kind_title);
     }
 
     /** {@inheritDoc} */
     @Override
     public void onDeleted(Editor editor) {
-        updateAddVisible();
-        updateVisible();
+        updateAddFooterVisible();
+        updateSectionVisible();
     }
 
     /** {@inheritDoc} */
     @Override
     public void onRequest(int request) {
-        // Ignore requests
+        // If a field has become empty or non-empty, then check if another row
+        // can be added dynamically.
+        if (request == FIELD_TURNED_EMPTY || request == FIELD_TURNED_NON_EMPTY) {
+            updateAddFooterVisible();
+        }
     }
 
     public void setState(DataKind kind, EntityDelta state, boolean readOnly, ViewIdGenerator vig) {
@@ -153,11 +135,17 @@
         mTitleString = (kind.titleRes == -1 || kind.titleRes == 0)
                 ? ""
                 : getResources().getString(kind.titleRes);
-        mTitle.setText(mTitleString);
+
+        // Set "add field" footer message according to MIME type. Some MIME types
+        // can only have max 1 field, so the resource ID will be -1 if these sections
+        // should not have an "Add field" option.
+        if (kind.addNewFieldTextResourceId != -1) {
+            mAddFieldText.setText(getResources().getString(kind.addNewFieldTextResourceId));
+        }
 
         rebuildFromState();
-        updateAddVisible();
-        updateVisible();
+        updateAddFooterVisible();
+        updateSectionVisible();
     }
 
     public String getTitle() {
@@ -193,16 +181,13 @@
      */
     private View createEditorView(ValuesDelta entry) {
         final View view;
-        if (mKind.editorClass == null) {
-            view = new TextFieldsEditorView(mContext);
-        } else {
-            try {
-                view = mKind.editorClass.getConstructor(Context.class).newInstance(
-                        mContext);
-            } catch (Exception e) {
-                throw new RuntimeException(
-                        "Cannot allocate editor for " + mKind.editorClass);
-            }
+        try {
+            view = mInflater.inflate(mKind.editorLayoutResourceId, mEditors, false);
+        } catch (Exception e) {
+            throw new RuntimeException(
+                    "Cannot allocate editor with layout resource ID " +
+                    mKind.editorLayoutResourceId + " for MIME type " + mKind.mimeType +
+                    " with error " + e.toString());
         }
 
         view.setEnabled(isEnabled());
@@ -231,26 +216,68 @@
         return true;
     }
 
-    private void updateVisible() {
+    private void updateSectionVisible() {
         setVisibility(getEditorCount() != 0 ? VISIBLE : GONE);
     }
 
-
-    protected void updateAddVisible() {
-        final boolean isVisible;
-        if (!mKind.isList) {
-            isVisible = false;
-        } else {
-            // Set enabled state on the "add" view
-            final boolean canInsert = EntityModifier.canInsert(mState, mKind);
-            isVisible = !mReadOnly && canInsert;
+    protected void updateAddFooterVisible() {
+        if (!mReadOnly && mKind.isList) {
+            // First determine whether there are any existing empty editors.
+            updateEmptyEditors();
+            // If there are no existing empty editors and it's possible to add
+            // another field, then make the "add footer" field visible.
+            if (!hasEmptyEditor() && EntityModifier.canInsert(mState, mKind)) {
+                mAddFieldFooter.setVisibility(View.VISIBLE);
+                return;
+            }
         }
-        mAddPlusButton.setVisibility(isVisible ? View.VISIBLE : View.INVISIBLE);
+        mAddFieldFooter.setVisibility(View.GONE);
+    }
+
+    /**
+     * Updates the editors being displayed to the user removing extra empty
+     * {@link Editor}s, so there is only max 1 empty {@link Editor} view at a time.
+     */
+    private void updateEmptyEditors() {
+        List<View> emptyEditors = getEmptyEditors();
+
+        // If there is more than 1 empty editor, then remove it from the list of editors.
+        if (emptyEditors.size() > 1) {
+            for (View emptyEditorView : emptyEditors) {
+                // If no child {@link View}s are being focused on within
+                // this {@link View}, then remove this empty editor.
+                if (emptyEditorView.findFocus() == null) {
+                    mEditors.removeView(emptyEditorView);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a list of empty editor views in this section.
+     */
+    private List<View> getEmptyEditors() {
+        List<View> emptyEditorViews = new ArrayList<View>();
+        for (int i = 0; i < mEditors.getChildCount(); i++) {
+            View view = mEditors.getChildAt(i);
+            if (((Editor) view).isEmpty()) {
+                emptyEditorViews.add(view);
+            }
+        }
+        return emptyEditorViews;
+    }
+
+    /**
+     * Returns true if one of the editors has all of its fields empty, or false
+     * otherwise.
+     */
+    private boolean hasEmptyEditor() {
+        return getEmptyEditors().size() > 0;
     }
 
     public void addItem() {
         ValuesDelta values = null;
-        // if this is a list, we can freely add. if not, only allow adding the first
+        // If this is a list, we can freely add. If not, only allow adding the first.
         if (!mKind.isList) {
             if (getEditorCount() == 1) {
                 return;
@@ -277,12 +304,11 @@
             }
         });
 
-        // For non-lists (e.g. Notes we can only have one field. in that case we need to disable
-        // the add button
-        updateAddVisible();
+        // Hide the "add field" footer because there is now a blank field.
+        mAddFieldFooter.setVisibility(View.GONE);
 
         // Ensure we are visible
-        updateVisible();
+        updateSectionVisible();
     }
 
     public int getEditorCount() {
diff --git a/src/com/android/contacts/editor/LabeledEditorView.java b/src/com/android/contacts/editor/LabeledEditorView.java
index a6815a7..af5ae65 100644
--- a/src/com/android/contacts/editor/LabeledEditorView.java
+++ b/src/com/android/contacts/editor/LabeledEditorView.java
@@ -18,21 +18,19 @@
 
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
-import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.android.contacts.model.EntityModifier;
 import com.android.contacts.util.DialogManager;
 import com.android.contacts.util.DialogManager.DialogShowingView;
-import com.android.contacts.util.ThemeUtils;
 
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Entity;
-import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
 import android.text.TextUtils;
@@ -45,32 +43,36 @@
 import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.ArrayAdapter;
 import android.widget.EditText;
-import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.Spinner;
 import android.widget.TextView;
 
 import java.util.List;
 
 /**
- * Base class for editors that handles labels and values.
- * Uses {@link ValuesDelta} to read any existing
- * {@link Entity} values, and to correctly write any changes values.
+ * Base class for editors that handles labels and values. Uses
+ * {@link ValuesDelta} to read any existing {@link Entity} values, and to
+ * correctly write any changes values.
  */
-public abstract class LabeledEditorView extends ViewGroup implements Editor, DialogShowingView {
+public abstract class LabeledEditorView extends LinearLayout implements Editor, DialogShowingView {
     protected static final String DIALOG_ID_KEY = "dialog_id";
     private static final int DIALOG_ID_CUSTOM = 1;
 
     private static final int INPUT_TYPE_CUSTOM = EditorInfo.TYPE_CLASS_TEXT
             | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
 
+    private TextView mTitle;
     private Spinner mLabel;
     private EditTypeAdapter mEditTypeAdapter;
-    private ImageButton mDelete;
+    private View mDeleteContainer;
+    private ImageView mDelete;
 
     private DataKind mKind;
     private ValuesDelta mEntry;
     private EntityDelta mState;
     private boolean mReadOnly;
+    private boolean mWasEmpty = true;
 
     private EditType mType;
 
@@ -117,6 +119,39 @@
                 R.dimen.editor_min_line_item_height);
     }
 
+    /** {@inheritDoc} */
+    @Override
+    protected void onFinishInflate() {
+
+        mTitle = (TextView) findViewById(R.id.title);
+
+        mLabel = (Spinner) findViewById(R.id.spinner);
+        mLabel.setOnItemSelectedListener(mSpinnerListener);
+
+        mDelete = (ImageView) findViewById(R.id.delete_button);
+        mDeleteContainer = findViewById(R.id.delete_button_container);
+        mDeleteContainer.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // defer removal of this button so that the pressed state is visible shortly
+                new Handler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        // Keep around in model, but mark as deleted
+                        mEntry.markDeleted();
+
+                        ((ViewGroup) getParent()).removeView(LabeledEditorView.this);
+
+                        if (mListener != null) {
+                            // Notify listener when present
+                            mListener.onDeleted(LabeledEditorView.this);
+                        }
+                    }
+                });
+            }
+        });
+    }
+
     public boolean isReadOnly() {
         return mReadOnly;
     }
@@ -129,96 +164,21 @@
     }
 
     /**
-     * Returns the number of rows in this editor, including the invisible ones.
-     */
-    protected int getLineItemCount() {
-        return 1;
-    }
-
-    protected boolean isLineItemVisible(int row) {
-        return true;
-    }
-
-    protected int getLineItemHeight(int row) {
-        int fieldHeight = 0;
-        int buttonHeight = 0;
-        if (row == 0) {
-            // summarize the EditText heights
-            if (mLabel != null) {
-                fieldHeight = mLabel.getMeasuredHeight();
-            }
-
-            // Ensure there is enough space for the minus button
-            View deleteButton = getDelete();
-            final int deleteHeight = (deleteButton != null) ? deleteButton.getMeasuredHeight() : 0;
-            buttonHeight += deleteHeight;
-        }
-
-        return Math.max(Math.max(buttonHeight, fieldHeight), mMinLineItemHeight);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        measureChildren(widthMeasureSpec, heightMeasureSpec);
-
-        int height = 0;
-        height += getPaddingTop() + getPaddingBottom();
-
-        int count = getLineItemCount();
-        for (int i = 0; i < count; i++) {
-            if (isLineItemVisible(i)) {
-                height += getLineItemHeight(i);
-            }
-        }
-
-        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
-                resolveSize(height, heightMeasureSpec));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        // Subtract padding from the borders ==> x1 variables
-        int t1 = getPaddingTop();
-        int r1 = getMeasuredWidth() - getPaddingRight();
-        int b1 = getMeasuredHeight() - getPaddingBottom();
-
-        final int r2;
-        if (mDelete != null) {
-            r2 = r1 - mDelete.getMeasuredWidth();
-            // Vertically center the delete button in the first line item
-            int height = mDelete.getMeasuredHeight();
-            int top = t1 + (mMinLineItemHeight - height) / 2;
-            mDelete.layout(
-                    r2, top,
-                    r1, top + height);
-        } else {
-            r2 = r1;
-        }
-
-        if (mLabel != null) {
-            int baseline = getBaseline(0);
-            int y = t1 + baseline - mLabel.getBaseline();
-            mLabel.layout(
-                    r2 - mLabel.getMeasuredWidth(), y,
-                    r2, y + mLabel.getMeasuredHeight());
-        }
-    }
-
-    /**
      * Creates or removes the type/label button. Doesn't do anything if already correctly configured
      */
     private void setupLabelButton(boolean shouldExist) {
-        if (shouldExist && mLabel == null) {
-            mLabel = new Spinner(mContext);
-            final int width =
-                    mContext.getResources().getDimensionPixelSize(R.dimen.editor_type_label_width);
-            mLabel.setLayoutParams(new LayoutParams(width, LayoutParams.WRAP_CONTENT));
-            mLabel.setOnItemSelectedListener(mSpinnerListener);
+        if (mTitle == null) {
+            return;
+        }
+        if (shouldExist) {
             mLabel.setEnabled(!mReadOnly && isEnabled());
-            addView(mLabel);
-        } else if (!shouldExist && mLabel != null) {
-            removeView(mLabel);
-            mLabel = null;
+            mLabel.setVisibility(View.VISIBLE);
+
+            // Since there's a spinner for this editor, use this as the title
+            // instead of the title TextView.
+            mTitle.setVisibility(View.GONE);
+        } else {
+            mLabel.setVisibility(View.GONE);
         }
     }
 
@@ -226,46 +186,11 @@
      * Creates or removes the remove button. Doesn't do anything if already correctly configured
      */
     private void setupDeleteButton(boolean shouldExist) {
-        if (shouldExist && mDelete == null) {
-            mDelete = new ImageButton(mContext);
-            mDelete.setImageResource(R.drawable.ic_menu_remove_field_holo_light);
-            mDelete.setBackgroundResource(
-                    ThemeUtils.getSelectableItemBackground(mContext.getTheme()));
-            final Resources resources = mContext.getResources();
-            mDelete.setPadding(
-                    resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_left),
-                    resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_top),
-                    resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_right),
-                    resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_bottom));
-            mDelete.setContentDescription(
-                    getResources().getText(R.string.description_minus_button));
-            mDelete.setLayoutParams(
-                    new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-            mDelete.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    // defer removal of this button so that the pressed state is visible shortly
-                    new Handler().post(new Runnable() {
-                        @Override
-                        public void run() {
-                            // Keep around in model, but mark as deleted
-                            mEntry.markDeleted();
-
-                            ((ViewGroup) getParent()).removeView(LabeledEditorView.this);
-
-                            if (mListener != null) {
-                                // Notify listener when present
-                                mListener.onDeleted(LabeledEditorView.this);
-                            }
-                        }
-                    });
-                }
-            });
+        if (shouldExist) {
+            mDeleteContainer.setVisibility(View.VISIBLE);
             mDelete.setEnabled(!mReadOnly && isEnabled());
-            addView(mDelete);
-        } else if (!shouldExist && mDelete != null) {
-            removeView(mDelete);
-            mDelete = null;
+        } else {
+            mDeleteContainer.setVisibility(View.GONE);
         }
     }
 
@@ -288,15 +213,15 @@
     @Override
     public void setEnabled(boolean enabled) {
         super.setEnabled(enabled);
-        if (mLabel != null) mLabel.setEnabled(!mReadOnly && enabled);
-        if (mDelete != null) mDelete.setEnabled(!mReadOnly && enabled);
+        mLabel.setEnabled(!mReadOnly && enabled);
+        mDelete.setEnabled(!mReadOnly && enabled);
     }
 
     public Spinner getLabel() {
         return mLabel;
     }
 
-    public ImageButton getDelete() {
+    public ImageView getDelete() {
         return mDelete;
     }
 
@@ -317,7 +242,6 @@
      * possible custom label string.
      */
     private void rebuildLabel() {
-        if (mLabel == null) return;
         mEditTypeAdapter = new EditTypeAdapter(mContext);
         mLabel.setAdapter(mEditTypeAdapter);
         if (mEditTypeAdapter.hasCustomSelection()) {
@@ -338,6 +262,16 @@
         if (mListener != null) {
             mListener.onRequest(EditorListener.FIELD_CHANGED);
         }
+
+        boolean isEmpty = isEmpty();
+        if (mWasEmpty != isEmpty) {
+            if (isEmpty) {
+                mListener.onRequest(EditorListener.FIELD_TURNED_EMPTY);
+            } else {
+                mListener.onRequest(EditorListener.FIELD_TURNED_NON_EMPTY);
+            }
+            mWasEmpty = isEmpty;
+        }
     }
 
     protected boolean isFieldChanged(String column, String value) {
@@ -374,10 +308,22 @@
         }
         setVisibility(View.VISIBLE);
 
+        // TODO: handle resources from remote packages
+        String titleString = (kind.titleRes == -1 || kind.titleRes == 0)
+                ? ""
+                : getResources().getString(kind.titleRes);
+
+        // If there is a title field, then setup the title (although it may not be shown if there is
+        // a Spinner setup later). There are cases where a title may not be present (i.e. structured
+        // name).
+        if (mTitle != null) {
+            mTitle.setText(titleString.toUpperCase());
+        }
+
         // Display label selector if multiple types available
         final boolean hasTypes = EntityModifier.hasEditTypes(kind);
         setupLabelButton(hasTypes);
-        if (mLabel != null) mLabel.setEnabled(!readOnly && isEnabled());
+        mLabel.setEnabled(!readOnly && isEnabled());
         if (hasTypes) {
             mType = EntityModifier.getCurrentType(entry, kind);
             rebuildLabel();
@@ -556,7 +502,7 @@
             } else {
                 text = getContext().getString(type.labelRes);
             }
-            textView.setText(text);
+            textView.setText(text.toUpperCase());
             return view;
         }
     }
diff --git a/src/com/android/contacts/editor/PhoneticNameEditorView.java b/src/com/android/contacts/editor/PhoneticNameEditorView.java
index d3aa00d..cfc9b13 100644
--- a/src/com/android/contacts/editor/PhoneticNameEditorView.java
+++ b/src/com/android/contacts/editor/PhoneticNameEditorView.java
@@ -16,10 +16,11 @@
 
 package com.android.contacts.editor;
 
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 
+import android.content.ContentValues;
 import android.content.Context;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.text.TextUtils;
@@ -60,55 +61,20 @@
         }
 
         private void parsePhoneticName(String value) {
-            String family = null;
-            String middle = null;
-            String given = null;
-
-            if (!TextUtils.isEmpty(value)) {
-                String[] strings = value.split(" ", 3);
-                switch (strings.length) {
-                    case 1:
-                        family = strings[0];
-                        break;
-                    case 2:
-                        family = strings[0];
-                        given = strings[1];
-                        break;
-                    case 3:
-                        family = strings[0];
-                        middle = strings[1];
-                        given = strings[2];
-                        break;
-                }
-            }
-
-            mValues.put(StructuredName.PHONETIC_FAMILY_NAME, family);
-            mValues.put(StructuredName.PHONETIC_MIDDLE_NAME, middle);
-            mValues.put(StructuredName.PHONETIC_GIVEN_NAME, given);
+            ContentValues values = PhoneticNameEditorView.parsePhoneticName(value, null);
+            mValues.put(StructuredName.PHONETIC_FAMILY_NAME,
+                    values.getAsString(StructuredName.PHONETIC_FAMILY_NAME));
+            mValues.put(StructuredName.PHONETIC_MIDDLE_NAME,
+                    values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME));
+            mValues.put(StructuredName.PHONETIC_GIVEN_NAME,
+                    values.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
         }
 
         private void buildPhoneticName() {
             String family = mValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME);
             String middle = mValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
             String given = mValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME);
-
-            if (!TextUtils.isEmpty(family) || !TextUtils.isEmpty(middle)
-                    || !TextUtils.isEmpty(given)) {
-                StringBuilder sb = new StringBuilder();
-                if (!TextUtils.isEmpty(family)) {
-                    sb.append(family.trim()).append(' ');
-                }
-                if (!TextUtils.isEmpty(middle)) {
-                    sb.append(middle.trim()).append(' ');
-                }
-                if (!TextUtils.isEmpty(given)) {
-                    sb.append(given.trim()).append(' ');
-                }
-                sb.setLength(sb.length() - 1);  // Yank the last space
-                mPhoneticName = sb.toString();
-            } else {
-                mPhoneticName = null;
-            }
+            mPhoneticName = PhoneticNameEditorView.buildPhoneticName(family, middle, given);
         }
 
         @Override
@@ -122,6 +88,73 @@
         }
     }
 
+    /**
+     * Parses phonetic name and returns parsed data (family, middle, given) as ContentValues.
+     * Parsed data should be {@link StructuredName#PHONETIC_FAMILY_NAME},
+     * {@link StructuredName#PHONETIC_MIDDLE_NAME}, and
+     * {@link StructuredName#PHONETIC_GIVEN_NAME}.
+     * If this method cannot parse given phoneticName, null values will be stored.
+     *
+     * @param phoneticName Phonetic name to be parsed
+     * @param values ContentValues to be used for storing data. If null, new instance will be
+     * created.
+     * @return ContentValues with parsed data. Those data can be null.
+     */
+    public static ContentValues parsePhoneticName(String phoneticName, ContentValues values) {
+        String family = null;
+        String middle = null;
+        String given = null;
+
+        if (!TextUtils.isEmpty(phoneticName)) {
+            String[] strings = phoneticName.split(" ", 3);
+            switch (strings.length) {
+                case 1:
+                    family = strings[0];
+                    break;
+                case 2:
+                    family = strings[0];
+                    given = strings[1];
+                    break;
+                case 3:
+                    family = strings[0];
+                    middle = strings[1];
+                    given = strings[2];
+                    break;
+            }
+        }
+
+        if (values == null) {
+            values = new ContentValues();
+        }
+        values.put(StructuredName.PHONETIC_FAMILY_NAME, family);
+        values.put(StructuredName.PHONETIC_MIDDLE_NAME, middle);
+        values.put(StructuredName.PHONETIC_GIVEN_NAME, given);
+        return values;
+    }
+
+    /**
+     * Constructs and returns a phonetic full name from given parts.
+     */
+    public static String buildPhoneticName(String family, String middle, String given) {
+        if (!TextUtils.isEmpty(family) || !TextUtils.isEmpty(middle)
+                || !TextUtils.isEmpty(given)) {
+            StringBuilder sb = new StringBuilder();
+            if (!TextUtils.isEmpty(family)) {
+                sb.append(family.trim()).append(' ');
+            }
+            if (!TextUtils.isEmpty(middle)) {
+                sb.append(middle.trim()).append(' ');
+            }
+            if (!TextUtils.isEmpty(given)) {
+                sb.append(given.trim()).append(' ');
+            }
+            sb.setLength(sb.length() - 1);  // Yank the last space
+            return sb.toString();
+        } else {
+            return null;
+        }
+    }
+
     public PhoneticNameEditorView(Context context) {
         super(context);
     }
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
index 89633fd..7b86291 100644
--- a/src/com/android/contacts/editor/PhotoEditorView.java
+++ b/src/com/android/contacts/editor/PhotoEditorView.java
@@ -17,8 +17,8 @@
 package com.android.contacts.editor;
 
 import com.android.contacts.R;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 
 import android.content.Context;
@@ -178,4 +178,9 @@
     public void setDeletable(boolean deletable) {
         // Photo is not deletable
     }
+
+    @Override
+    public boolean isEmpty() {
+        return !mHasSetPhoto;
+    }
 }
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 633b343..5ef2c21 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -19,12 +19,14 @@
 import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
 import com.android.contacts.model.AccountType;
-import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.android.contacts.model.EntityModifier;
 
+import android.accounts.Account;
 import android.content.Context;
 import android.content.Entity;
 import android.database.Cursor;
@@ -68,9 +70,10 @@
 
     private ViewGroup mFields;
 
-    private ImageView mHeaderIcon;
-    private TextView mHeaderAccountType;
-    private TextView mHeaderAccountName;
+    private View mAccountContainer;
+    private ImageView mAccountIcon;
+    private TextView mAccountTypeTextView;
+    private TextView mAccountNameTextView;
 
     private Button mAddFieldButton;
 
@@ -130,16 +133,16 @@
         final int photoSize = getResources().getDimensionPixelSize(R.dimen.edit_photo_size);
 
         mName = (StructuredNameEditorView)findViewById(R.id.edit_name);
-        mName.setMinimumHeight(photoSize);
         mName.setDeletable(false);
 
         mPhoneticName = (PhoneticNameEditorView)findViewById(R.id.edit_phonetic_name);
 
         mFields = (ViewGroup)findViewById(R.id.sect_fields);
 
-        mHeaderIcon = (ImageView) findViewById(R.id.header_icon);
-        mHeaderAccountType = (TextView) findViewById(R.id.header_account_type);
-        mHeaderAccountName = (TextView) findViewById(R.id.header_account_name);
+        mAccountContainer = findViewById(R.id.account);
+        mAccountIcon = (ImageView) findViewById(R.id.account_icon);
+        mAccountTypeTextView = (TextView) findViewById(R.id.account_type);
+        mAccountNameTextView = (TextView) findViewById(R.id.account_name);
 
         mAddFieldButton = (Button) findViewById(R.id.button_add_field);
         mAddFieldButton.setOnClickListener(new OnClickListener() {
@@ -170,29 +173,36 @@
         // Make sure we have StructuredName
         EntityModifier.ensureKindExists(state, type, StructuredName.CONTENT_ITEM_TYPE);
 
-        // Fill in the header info
         ValuesDelta values = state.getValues();
-        String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
-        CharSequence accountType = type.getDisplayLabel(mContext);
-        if (TextUtils.isEmpty(accountType)) {
-            accountType = mContext.getString(R.string.account_phone);
-        }
-        if (!TextUtils.isEmpty(accountName)) {
-            mHeaderAccountName.setText(
-                    mContext.getString(R.string.from_account_format, accountName));
-        }
-        mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType));
-        mHeaderIcon.setImageDrawable(type.getDisplayIcon(mContext));
-
         mRawContactId = values.getAsLong(RawContacts._ID);
 
+        final ArrayList<Account> accounts =
+                AccountTypeManager.getInstance(mContext).getAccounts(true);
+        if (accounts.size() > 1) {
+            // Fill in the account info
+            String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+            CharSequence accountType = type.getDisplayLabel(mContext);
+            if (TextUtils.isEmpty(accountType)) {
+                accountType = mContext.getString(R.string.account_phone);
+            }
+            if (!TextUtils.isEmpty(accountName)) {
+                mAccountNameTextView.setText(
+                        mContext.getString(R.string.from_account_format, accountName));
+            }
+            mAccountTypeTextView.setText(
+                    mContext.getString(R.string.account_type_format, accountType));
+            mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
+        } else {
+            mAccountContainer.setVisibility(View.GONE);
+        }
+
         // Show photo editor when supported
         EntityModifier.ensureKindExists(state, type, Photo.CONTENT_ITEM_TYPE);
         setHasPhotoEditor((type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null));
         getPhotoEditor().setEnabled(isEnabled());
         mName.setEnabled(isEnabled());
-        mName.setEditorTextSize(
-                mContext.getResources().getDimensionPixelSize(R.dimen.contact_name_text_size));
+        mName.setEditorTextSize(mContext.getResources().getDimensionPixelSize(
+                R.dimen.editor_structured_name_text_size));
 
         mPhoneticName.setEnabled(isEnabled());
 
diff --git a/src/com/android/contacts/editor/SelectAccountDialogFragment.java b/src/com/android/contacts/editor/SelectAccountDialogFragment.java
index 67c5f6e..65370f5 100644
--- a/src/com/android/contacts/editor/SelectAccountDialogFragment.java
+++ b/src/com/android/contacts/editor/SelectAccountDialogFragment.java
@@ -53,8 +53,7 @@
             public void onClick(DialogInterface dialog, int which) {
                 dialog.dismiss();
 
-                final Listener target = (Listener) getTargetFragment();
-                target.onAccountChosen(accountAdapter.getItem(which));
+                onAccountSelected(accountAdapter.getItem(which));
             }
         };
 
@@ -67,12 +66,27 @@
     @Override
     public void onCancel(DialogInterface dialog) {
         super.onCancel(dialog);
-        final Listener target = (Listener) getTargetFragment();
-        target.onAccountSelectorCancelled();
+        final Fragment targetFragment = getTargetFragment();
+        if (targetFragment != null && targetFragment instanceof Listener) {
+            final Listener target = (Listener) targetFragment;
+            target.onAccountSelectorCancelled();
+        }
+    }
+
+    /**
+     * Calls {@link Listener#onAccountChosen(int, Account)} if the target fragment is castable
+     * to {@link Listener}. Subclasses can also overide to directly perform an operation
+     */
+    protected void onAccountSelected(Account account) {
+        final Fragment targetFragment = getTargetFragment();
+        if (targetFragment != null && targetFragment instanceof Listener) {
+            final Listener target = (Listener) targetFragment;
+            target.onAccountChosen(getTargetRequestCode(), account);
+        }
     }
 
     public interface Listener {
-        void onAccountChosen(Account account);
+        void onAccountChosen(int requestCode, Account account);
         void onAccountSelectorCancelled();
     }
 }
diff --git a/src/com/android/contacts/editor/StructuredNameEditorView.java b/src/com/android/contacts/editor/StructuredNameEditorView.java
index cf97c6c..5a2ffd3 100644
--- a/src/com/android/contacts/editor/StructuredNameEditorView.java
+++ b/src/com/android/contacts/editor/StructuredNameEditorView.java
@@ -16,22 +16,23 @@
 
 package com.android.contacts.editor;
 
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.util.NameConverter;
 
 import android.content.ContentValues;
 import android.content.Context;
-import android.database.Cursor;
 import android.net.Uri;
-import android.net.Uri.Builder;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * A dedicated editor for structured name.  When the user collapses/expands
  * the structured name, it will reparse or recompose the name, but only
@@ -107,41 +108,20 @@
         ValuesDelta values = getValues();
 
         if (!mChanged) {
-            values.put(StructuredName.PREFIX,
-                    mSnapshot.getAsString(StructuredName.PREFIX));
-            values.put(StructuredName.GIVEN_NAME,
-                    mSnapshot.getAsString(StructuredName.GIVEN_NAME));
-            values.put(StructuredName.MIDDLE_NAME,
-                    mSnapshot.getAsString(StructuredName.MIDDLE_NAME));
-            values.put(StructuredName.FAMILY_NAME,
-                    mSnapshot.getAsString(StructuredName.FAMILY_NAME));
-            values.put(StructuredName.SUFFIX,
-                    mSnapshot.getAsString(StructuredName.SUFFIX));
+            for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
+                values.put(field, mSnapshot.getAsString(field));
+            }
             return;
         }
 
         String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
-        Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name");
-        appendQueryParameter(builder, StructuredName.DISPLAY_NAME, displayName);
-        Cursor cursor = getContext().getContentResolver().query(builder.build(), new String[]{
-                StructuredName.PREFIX,
-                StructuredName.GIVEN_NAME,
-                StructuredName.MIDDLE_NAME,
-                StructuredName.FAMILY_NAME,
-                StructuredName.SUFFIX,
-        }, null, null, null);
-
-        try {
-            if (cursor.moveToFirst()) {
-                eraseFullName(values);
-                values.put(StructuredName.PREFIX, cursor.getString(0));
-                values.put(StructuredName.GIVEN_NAME, cursor.getString(1));
-                values.put(StructuredName.MIDDLE_NAME, cursor.getString(2));
-                values.put(StructuredName.FAMILY_NAME, cursor.getString(3));
-                values.put(StructuredName.SUFFIX, cursor.getString(4));
+        Map<String, String> structuredNameMap = NameConverter.displayNameToStructuredName(
+                getContext(), displayName);
+        if (!structuredNameMap.isEmpty()) {
+            eraseFullName(values);
+            for (String field : structuredNameMap.keySet()) {
+                values.put(field, structuredNameMap.get(field));
             }
-        } finally {
-            cursor.close();
         }
 
         mSnapshot.clear();
@@ -158,39 +138,27 @@
             return;
         }
 
-        String prefix = values.getAsString(StructuredName.PREFIX);
-        String givenName = values.getAsString(StructuredName.GIVEN_NAME);
-        String middleName = values.getAsString(StructuredName.MIDDLE_NAME);
-        String familyName = values.getAsString(StructuredName.FAMILY_NAME);
-        String suffix = values.getAsString(StructuredName.SUFFIX);
-
-        Uri.Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath(
-                "complete_name");
-        appendQueryParameter(builder, StructuredName.PREFIX, prefix);
-        appendQueryParameter(builder, StructuredName.GIVEN_NAME, givenName);
-        appendQueryParameter(builder, StructuredName.MIDDLE_NAME, middleName);
-        appendQueryParameter(builder, StructuredName.FAMILY_NAME, familyName);
-        appendQueryParameter(builder, StructuredName.SUFFIX, suffix);
-        Cursor cursor = getContext().getContentResolver().query(builder.build(), new String[]{
-                StructuredName.DISPLAY_NAME,
-        }, null, null, null);
-
-        try {
-            if (cursor.moveToFirst()) {
-                eraseStructuredName(values);
-                values.put(StructuredName.DISPLAY_NAME, cursor.getString(0));
-            }
-        } finally {
-            cursor.close();
+        Map<String, String> structuredNameMap = valuesToStructuredNameMap(values);
+        String displayName = NameConverter.structuredNameToDisplayName(getContext(),
+                structuredNameMap);
+        if (!TextUtils.isEmpty(displayName)) {
+            eraseStructuredName(values);
+            values.put(StructuredName.DISPLAY_NAME, displayName);
         }
 
         mSnapshot.clear();
         mSnapshot.put(StructuredName.DISPLAY_NAME, values.getAsString(StructuredName.DISPLAY_NAME));
-        mSnapshot.put(StructuredName.PREFIX, prefix);
-        mSnapshot.put(StructuredName.GIVEN_NAME, givenName);
-        mSnapshot.put(StructuredName.MIDDLE_NAME, middleName);
-        mSnapshot.put(StructuredName.FAMILY_NAME, familyName);
-        mSnapshot.put(StructuredName.SUFFIX, suffix);
+        for (String field : structuredNameMap.keySet()) {
+            mSnapshot.put(field, structuredNameMap.get(field));
+        }
+    }
+
+    private Map<String, String> valuesToStructuredNameMap(ValuesDelta values) {
+        Map<String, String> structuredNameMap = new HashMap<String, String>();
+        for (String key : NameConverter.STRUCTURED_NAME_FIELDS) {
+            structuredNameMap.put(key, values.getAsString(key));
+        }
+        return structuredNameMap;
     }
 
     private void eraseFullName(ValuesDelta values) {
@@ -198,14 +166,12 @@
     }
 
     private void eraseStructuredName(ValuesDelta values) {
-        values.putNull(StructuredName.PREFIX);
-        values.putNull(StructuredName.GIVEN_NAME);
-        values.putNull(StructuredName.MIDDLE_NAME);
-        values.putNull(StructuredName.FAMILY_NAME);
-        values.putNull(StructuredName.SUFFIX);
+        for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
+            values.putNull(field);
+        }
     }
 
-    private void appendQueryParameter(Uri.Builder builder, String field, String value) {
+    private static void appendQueryParameter(Uri.Builder builder, String field, String value) {
         if (!TextUtils.isEmpty(value)) {
             builder.appendQueryParameter(field, value);
         }
diff --git a/src/com/android/contacts/editor/TextFieldsEditorView.java b/src/com/android/contacts/editor/TextFieldsEditorView.java
index 031ab18..04a9485 100644
--- a/src/com/android/contacts/editor/TextFieldsEditorView.java
+++ b/src/com/android/contacts/editor/TextFieldsEditorView.java
@@ -18,40 +18,49 @@
 
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
-import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.AccountType.EditField;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.util.ThemeUtils;
+import com.android.contacts.util.NameConverter;
 
 import android.content.Context;
 import android.content.Entity;
-import android.content.res.Resources;
 import android.graphics.Rect;
+import android.graphics.Typeface;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.telephony.PhoneNumberFormattingTextWatcher;
 import android.text.Editable;
 import android.text.InputType;
+import android.text.Spannable;
+import android.text.TextUtils;
 import android.text.TextWatcher;
+import android.text.style.StyleSpan;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.EditText;
-import android.widget.ImageButton;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 
+import java.util.Map;
+
 /**
- * Simple editor that handles labels and any {@link EditField} defined for
- * the entry. Uses {@link ValuesDelta} to read any existing
- * {@link Entity} values, and to correctly write any changes values.
+ * Simple editor that handles labels and any {@link EditField} defined for the
+ * entry. Uses {@link ValuesDelta} to read any existing {@link Entity} values,
+ * and to correctly write any changes values.
  */
 public class TextFieldsEditorView extends LabeledEditorView {
     private EditText[] mFieldEditTexts = null;
-    private ImageButton mMoreOrLess;
+    private ViewGroup mFields = null;
+    private View mExpansionViewContainer;
+    private ImageView mExpansionView;
     private boolean mHideOptional = true;
     private boolean mHasShortAndLongForms;
-    private int mEditorTextSize;
+    private int mEditorTextSize = 0;
 
     public TextFieldsEditorView(Context context) {
         super(context);
@@ -65,97 +74,46 @@
         super(context, attrs, defStyle);
     }
 
-    public void setEditorTextSize(int textSize) {
-        this.mEditorTextSize = textSize;
-    }
-
+    /** {@inheritDoc} */
     @Override
-    protected int getLineItemCount() {
-        int count = mFieldEditTexts == null ? 0 : mFieldEditTexts.length;
-        return Math.max(count, super.getLineItemCount());
-    }
+    protected void onFinishInflate() {
+        super.onFinishInflate();
 
-    @Override
-    protected boolean isLineItemVisible(int row) {
-        return mFieldEditTexts != null && mFieldEditTexts[row].getVisibility() != View.GONE;
-    }
+        setDrawingCacheEnabled(true);
+        setAlwaysDrawnWithCacheEnabled(true);
 
-    @Override
-    public int getBaseline(int row) {
-        int baseline = super.getBaseline(row);
-        if (mFieldEditTexts != null) {
-            EditText editText = mFieldEditTexts[row];
-            // The text field will be centered vertically in the corresponding line item
-            int lineItemHeight = getLineItemHeight(row);
-            int offset = (lineItemHeight - editText.getMeasuredHeight()) / 2;
-            baseline = Math.max(baseline, offset + editText.getBaseline());
-        }
-        return baseline;
-    }
+        mFields = (ViewGroup) findViewById(R.id.editors);
+        mExpansionView = (ImageView) findViewById(R.id.expansion_view);
+        mExpansionViewContainer = findViewById(R.id.expansion_view_container);
+        mExpansionViewContainer.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // Save focus
+                final View focusedChild = getFocusedChild();
+                final int focusedViewId = focusedChild == null ? -1 : focusedChild.getId();
 
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
+                // Reconfigure GUI
+                mHideOptional = !mHideOptional;
+                onOptionalFieldVisibilityChange();
+                rebuildValues();
 
-        int l1 = getPaddingLeft();
-        int t1 = getPaddingTop();
-        int r1 = getMeasuredWidth() - getPaddingRight();
-
-        if ((mMoreOrLess != null)) {
-            mMoreOrLess.layout(
-                    r1 - mMoreOrLess.getMeasuredWidth(), t1,
-                    r1, t1 + mMoreOrLess.getMeasuredHeight());
-        }
-
-        // Subtract buttons if necessary
-        final int labelWidth = (getLabel() != null) ? getLabel().getMeasuredWidth() : 0;
-        final int deleteWidth = (getDelete() != null) ? getDelete().getMeasuredWidth() : 0;
-        final int moreOrLessWidth = mMoreOrLess != null ? mMoreOrLess.getMeasuredWidth() : 0;
-        final int r2 = r1 - Math.max(deleteWidth, moreOrLessWidth) - labelWidth;
-
-        // Layout text fields
-        int y = t1;
-        if (mFieldEditTexts != null) {
-            for (int i = 0; i < mFieldEditTexts.length; i++) {
-                int baseline = getBaseline(i);
-                EditText editText = mFieldEditTexts[i];
-                if (editText.getVisibility() != View.GONE) {
-                    int height = editText.getMeasuredHeight();
-                    int top = t1 + y + baseline - editText.getBaseline();
-                    editText.layout(
-                            l1, top,
-                            r2, top + height);
-                    y += getLineItemHeight(i);
+                // Restore focus
+                View newFocusView = findViewById(focusedViewId);
+                if (newFocusView == null || newFocusView.getVisibility() == GONE) {
+                    // find first visible child
+                    newFocusView = TextFieldsEditorView.this;
                 }
+                newFocusView.requestFocus();
             }
-        }
+        });
     }
 
-    @Override
-    protected int getLineItemHeight(int row) {
-        int fieldHeight = 0;
-        int buttonHeight = 0;
-
-        boolean lastLineItem = true;
-        if (mFieldEditTexts != null) {
-            fieldHeight = mFieldEditTexts[row].getMeasuredHeight();
-            lastLineItem = (row == mFieldEditTexts.length - 1);
-        }
-
-        // Ensure there is enough space for the more/less button
-        if (row == 0) {
-            final int moreOrLessHeight = mMoreOrLess != null ? mMoreOrLess.getMeasuredHeight() : 0;
-            buttonHeight += moreOrLessHeight;
-        }
-
-        // Ensure there is enough space for the minus button
-        if (lastLineItem) {
-            View deleteButton = getDelete();
-            final int deleteHeight = (deleteButton != null) ? deleteButton.getMeasuredHeight() : 0;
-            buttonHeight += deleteHeight;
-        }
-
-        return Math.max(Math.max(buttonHeight, fieldHeight), super.getLineItemHeight(row));
+    /**
+     * Set the text size of the value of all fields in this class, which will override the default
+     * text appearance style for the associated {@link DataKind}.
+     */
+    public void setEditorTextSize(int textSize) {
+        mEditorTextSize = textSize;
     }
 
     @Override
@@ -167,59 +125,20 @@
                 mFieldEditTexts[index].setEnabled(!isReadOnly() && enabled);
             }
         }
-        if (mMoreOrLess != null) mMoreOrLess.setEnabled(!isReadOnly() && enabled);
+        mExpansionView.setEnabled(!isReadOnly() && enabled);
     }
 
     /**
      * Creates or removes the type/label button. Doesn't do anything if already correctly configured
      */
-    private void setupMoreOrLessButton(boolean shouldExist, boolean collapsed) {
+    private void setupExpansionView(boolean shouldExist, boolean collapsed) {
         if (shouldExist) {
-            if (mMoreOrLess == null) {
-                mMoreOrLess = new ImageButton(mContext);
-                mMoreOrLess.setBackgroundResource(
-                        ThemeUtils.getSelectableItemBackground(mContext.getTheme()));
-                final Resources resources = mContext.getResources();
-                mMoreOrLess.setPadding(
-                        resources.getDimensionPixelOffset(
-                                R.dimen.editor_round_button_padding_left),
-                        resources.getDimensionPixelOffset(
-                                R.dimen.editor_round_button_padding_top),
-                        resources.getDimensionPixelOffset(
-                                R.dimen.editor_round_button_padding_right),
-                        resources.getDimensionPixelOffset(
-                                R.dimen.editor_round_button_padding_bottom));
-                mMoreOrLess.setLayoutParams(
-                        new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-                mMoreOrLess.setOnClickListener(new OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        // Save focus
-                        final View focusedChild = getFocusedChild();
-                        final int focusedViewId = focusedChild == null ? -1 : focusedChild.getId();
-
-                        // Reconfigure GUI
-                        mHideOptional = !mHideOptional;
-                        onOptionalFieldVisibilityChange();
-                        rebuildValues();
-
-                        // Restore focus
-                        View newFocusView = findViewById(focusedViewId);
-                        if (newFocusView == null || newFocusView.getVisibility() == GONE) {
-                            // find first visible child
-                            newFocusView = TextFieldsEditorView.this;
-                        }
-                        newFocusView.requestFocus();
-                    }
-                });
-                addView(mMoreOrLess);
-            }
-            mMoreOrLess.setImageResource(collapsed
+            mExpansionViewContainer.setVisibility(View.VISIBLE);
+            mExpansionView.setImageResource(collapsed
                     ? R.drawable.ic_menu_expander_minimized_holo_light
                     : R.drawable.ic_menu_expander_maximized_holo_light);
-        } else if (mMoreOrLess != null) {
-            removeView(mMoreOrLess);
-            mMoreOrLess = null;
+        } else {
+            mExpansionViewContainer.setVisibility(View.GONE);
         }
     }
 
@@ -250,7 +169,7 @@
         // Remove edit texts that we currently have
         if (mFieldEditTexts != null) {
             for (EditText fieldEditText : mFieldEditTexts) {
-                removeView(fieldEditText);
+                mFields.removeView(fieldEditText);
             }
         }
         boolean hidePossible = false;
@@ -262,10 +181,11 @@
             final EditText fieldView = new EditText(mContext);
             fieldView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                     LayoutParams.WRAP_CONTENT));
-            fieldView.setGravity(Gravity.TOP);
+            fieldView.setTextAppearance(getContext(), kind.textAppearanceResourceId);
             if (mEditorTextSize != 0) {
                 fieldView.setTextSize(mEditorTextSize);
             }
+            fieldView.setGravity(Gravity.TOP);
             mFieldEditTexts[index] = fieldView;
             fieldView.setId(vig.getId(state, kind, entry, index));
             if (field.titleRes > 0) {
@@ -273,6 +193,9 @@
             }
             int inputType = field.inputType;
             fieldView.setInputType(inputType);
+            if (field.isFullName) {
+                fieldView.addTextChangedListener(new NameFormattingTextWatcher());
+            }
             if (inputType == InputType.TYPE_CLASS_PHONE) {
                 fieldView.addTextChangedListener(new PhoneNumberFormattingTextWatcher(
                         ContactsUtils.getCurrentCountryIso(mContext)));
@@ -319,12 +242,23 @@
                 hidePossible = hidePossible || couldHide;
             }
 
-            addView(fieldView);
+            mFields.addView(fieldView);
         }
 
         // When hiding fields, place expandable
-        setupMoreOrLessButton(hidePossible, mHideOptional);
-        if (mMoreOrLess != null) mMoreOrLess.setEnabled(!readOnly && isEnabled());
+        setupExpansionView(hidePossible, mHideOptional);
+        mExpansionView.setEnabled(!readOnly && isEnabled());
+    }
+
+    @Override
+    public boolean isEmpty() {
+        for (int i = 0; i < mFields.getChildCount(); i++) {
+            EditText editText = (EditText) mFields.getChildAt(i);
+            if (!TextUtils.isEmpty(editText.getText())) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
@@ -424,4 +358,60 @@
             }
         };
     }
+
+    private class NameFormattingTextWatcher implements TextWatcher {
+
+
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+        @Override
+        public void afterTextChanged(Editable s) {
+            String displayName = s.toString();
+            Map<String, String> structuredName = NameConverter.displayNameToStructuredName(
+                    getContext(), displayName);
+            String givenName = structuredName.get(StructuredName.GIVEN_NAME);
+            if (!TextUtils.isEmpty(givenName)) {
+                int spanStart = -1;
+                int spanEnd = -1;
+                if (displayName.startsWith(givenName + " ")) {
+                    spanStart = 0;
+                    spanEnd = givenName.length();
+                } else {
+                    spanStart = displayName.lastIndexOf(" " + givenName);
+                    if (spanStart > -1) {
+                        spanStart++;
+                        spanEnd = spanStart + givenName.length();
+                    }
+                }
+
+                // If the requested range is already bolded, don't make any changes.
+                if (spanStart > -1) {
+                    StyleSpan[] existingSpans = s.getSpans(0, s.length(), StyleSpan.class);
+                    for (StyleSpan span : existingSpans) {
+                        if (span.getStyle() == Typeface.BOLD
+                                && s.getSpanStart(span.getUnderlying()) == spanStart
+                                && s.getSpanEnd(span.getUnderlying()) == spanEnd) {
+                            // Nothing to do - the correct portion is already bolded.
+                            return;
+                        }
+                    }
+
+                    // Clear any existing bold style spans.
+                    for (StyleSpan span : existingSpans) {
+                        if (span.getStyle() == Typeface.BOLD) {
+                            s.removeSpan(span);
+                        }
+                    }
+
+                    // Set the new bold span.
+                    s.setSpan(new StyleSpan(Typeface.BOLD), spanStart, spanEnd,
+                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+            }
+        }
+    }
 }
diff --git a/src/com/android/contacts/editor/ViewIdGenerator.java b/src/com/android/contacts/editor/ViewIdGenerator.java
index 93bb002..c3cd08b 100644
--- a/src/com/android/contacts/editor/ViewIdGenerator.java
+++ b/src/com/android/contacts/editor/ViewIdGenerator.java
@@ -16,8 +16,8 @@
 
 package com.android.contacts.editor;
 
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 
 import android.os.Bundle;
diff --git a/src/com/android/contacts/format/FormatUtils.java b/src/com/android/contacts/format/FormatUtils.java
new file mode 100644
index 0000000..ac34d6d
--- /dev/null
+++ b/src/com/android/contacts/format/FormatUtils.java
@@ -0,0 +1,108 @@
+/*
+ * 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.format;
+
+import android.database.CharArrayBuffer;
+import android.graphics.Typeface;
+import android.text.SpannableString;
+import android.text.style.StyleSpan;
+
+import java.util.Arrays;
+
+/**
+ * Assorted utility methods related to text formatting in Contacts.
+ */
+public class FormatUtils {
+
+    /**
+     * Finds the earliest point in buffer1 at which the first part of buffer2 matches.  For example,
+     * overlapPoint("abcd", "cdef") == 2.
+     */
+    public static int overlapPoint(CharArrayBuffer buffer1, CharArrayBuffer buffer2) {
+        if (buffer1 == null || buffer2 == null) {
+            return -1;
+        }
+        return overlapPoint(Arrays.copyOfRange(buffer1.data, 0, buffer1.sizeCopied),
+                Arrays.copyOfRange(buffer2.data, 0, buffer2.sizeCopied));
+    }
+
+    /**
+     * Finds the earliest point in string1 at which the first part of string2 matches.  For example,
+     * overlapPoint("abcd", "cdef") == 2.
+     */
+    public static int overlapPoint(String string1, String string2) {
+        if (string1 == null || string2 == null) {
+            return -1;
+        }
+        return overlapPoint(string1.toCharArray(), string2.toCharArray());
+    }
+
+    /**
+     * Finds the earliest point in array1 at which the first part of array2 matches.  For example,
+     * overlapPoint("abcd", "cdef") == 2.
+     */
+    public static int overlapPoint(char[] array1, char[] array2) {
+        if (array1 == null || array2 == null) {
+            return -1;
+        }
+        int count1 = array1.length;
+        int count2 = array2.length;
+
+        // Ignore matching tails of the two arrays.
+        while (count1 > 0 && count2 > 0 && array1[count1 - 1] == array2[count2 - 1]) {
+            count1--;
+            count2--;
+        }
+
+        int size = count2;
+        for (int i = 0; i < count1; i++) {
+            if (i + size > count1) {
+                size = count1 - i;
+            }
+            int j;
+            for (j = 0; j < size; j++) {
+                if (array1[i+j] != array2[j]) {
+                    break;
+                }
+            }
+            if (j == size) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Applies the given style to a range of the input CharSequence.
+     * @param style The style to apply (see the style constants in {@link Typeface}).
+     * @param input The CharSequence to style.
+     * @param start Starting index of the range to style (will be clamped to be a minimum of 0).
+     * @param end Ending index of the range to style (will be clamped to a maximum of the input
+     *     length).
+     * @param flags Bitmask for configuring behavior of the span.  See {@link android.text.Spanned}.
+     * @return The styled CharSequence.
+     */
+    public static CharSequence applyStyleToSpan(int style, CharSequence input, int start, int end,
+            int flags) {
+        // Enforce bounds of the char sequence.
+        start = Math.max(0, start);
+        end = Math.min(input.length(), end);
+        SpannableString text = new SpannableString(input);
+        text.setSpan(new StyleSpan(style), start, end, flags);
+        return text;
+    }
+}
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
new file mode 100644
index 0000000..bccc207
--- /dev/null
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -0,0 +1,79 @@
+/*
+ * 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.group;
+
+import com.android.contacts.R;
+import com.android.contacts.GroupMetaData;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Adapter to populate the list of groups.
+ */
+public class GroupBrowseListAdapter extends BaseAdapter {
+
+    private LayoutInflater mLayoutInflater;
+    private List<GroupMetaData> mGroupList;
+
+    public GroupBrowseListAdapter(Context context, List<GroupMetaData> groupList) {
+        mLayoutInflater = LayoutInflater.from(context);
+        mGroupList = groupList;
+    }
+
+    @Override
+    public int getCount() {
+        return mGroupList.size();
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return getItem(position).getGroupId();
+    }
+
+    @Override
+    public GroupMetaData getItem(int position) {
+        return mGroupList.get(position);
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        if (convertView == null) {
+            convertView = mLayoutInflater.inflate(R.layout.group_browse_list_item, parent, false);
+        }
+        GroupMetaData group = getItem(position);
+        ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+        TextView label = (TextView) convertView.findViewById(R.id.label);
+        TextView account = (TextView) convertView.findViewById(R.id.account);
+        icon.setImageResource(R.drawable.ic_menu_display_all_holo_light);
+        label.setText(group.getTitle());
+        account.setText(group.getAccountName());
+
+        // Set the tag to be the GroupMetaData object, in order to extract group attributes from the
+        // view later.
+        convertView.setTag(group);
+        return convertView;
+    }
+
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
new file mode 100644
index 0000000..d9d020e
--- /dev/null
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -0,0 +1,194 @@
+/*
+ * 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.group;
+
+import com.android.contacts.GroupMetaData;
+import com.android.contacts.GroupMetaDataLoader;
+import com.android.contacts.R;
+import com.android.contacts.activities.GroupDetailActivity;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment to display the list of groups.
+ */
+public class GroupBrowseListFragment extends Fragment
+        implements OnFocusChangeListener, OnTouchListener {
+
+    private static final String TAG = "GroupBrowseListFragment";
+
+    private static final int LOADER_GROUPS = 1;
+
+    private Context mContext;
+    private Cursor mGroupListCursor;
+    private List<GroupMetaData> mGroupList = new ArrayList<GroupMetaData>();
+
+    private View mRootView;
+    private ListView mListView;
+    private View mEmptyView;
+
+    public GroupBrowseListFragment() {
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        mRootView = inflater.inflate(R.layout.group_browse_list_fragment, null);
+        mListView = (ListView) mRootView.findViewById(R.id.list);
+        mListView.setOnFocusChangeListener(this);
+        mListView.setOnTouchListener(this);
+        mEmptyView = mRootView.findViewById(R.id.empty);
+        return mRootView;
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        mContext = activity;
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        mContext = null;
+    }
+
+    @Override
+    public void onStart() {
+        getLoaderManager().initLoader(LOADER_GROUPS, null, mGroupLoaderListener);
+        super.onStart();
+    }
+
+    /**
+     * The listener for the group meta data loader.
+     */
+    private final LoaderManager.LoaderCallbacks<Cursor> mGroupLoaderListener =
+            new LoaderCallbacks<Cursor>() {
+
+        @Override
+        public CursorLoader onCreateLoader(int id, Bundle args) {
+            return new GroupMetaDataLoader(mContext);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+            mGroupListCursor = data;
+            bindGroupList();
+        }
+
+        public void onLoaderReset(Loader<Cursor> loader) {
+        }
+    };
+
+    private void bindGroupList() {
+        if (mGroupListCursor == null) {
+            return;
+        }
+        mGroupList.clear();
+        mGroupListCursor.moveToPosition(-1);
+        while (mGroupListCursor.moveToNext()) {
+            String accountName = mGroupListCursor.getString(GroupMetaDataLoader.ACCOUNT_NAME);
+            String accountType = mGroupListCursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+            long groupId = mGroupListCursor.getLong(GroupMetaDataLoader.GROUP_ID);
+            String title = mGroupListCursor.getString(GroupMetaDataLoader.TITLE);
+            boolean defaultGroup = mGroupListCursor.isNull(GroupMetaDataLoader.AUTO_ADD)
+                    ? false
+                    : mGroupListCursor.getInt(GroupMetaDataLoader.AUTO_ADD) != 0;
+            boolean favorites = mGroupListCursor.isNull(GroupMetaDataLoader.FAVORITES)
+                    ? false
+                    : mGroupListCursor.getInt(GroupMetaDataLoader.FAVORITES) != 0;
+
+            // TODO: Separate groups according to account name and type.
+            mGroupList.add(new GroupMetaData(
+                    accountName, accountType, groupId, title, defaultGroup, favorites));
+        }
+
+        mListView.setAdapter(new GroupBrowseListAdapter(mContext, mGroupList));
+        mListView.setEmptyView(mEmptyView);
+        mListView.setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                startGroupDetailActivity((GroupMetaData) view.getTag());
+            }
+        });
+    }
+
+    private void startGroupDetailActivity(GroupMetaData group) {
+        if (group == null) {
+            return;
+        }
+        Intent intent = new Intent(mContext, GroupDetailActivity.class);
+        intent.putExtra(GroupDetailActivity.KEY_ACCOUNT_TYPE, group.getAccountType());
+        intent.putExtra(GroupDetailActivity.KEY_ACCOUNT_NAME, group.getAccountName());
+        intent.putExtra(GroupDetailActivity.KEY_GROUP_ID, group.getGroupId());
+        intent.putExtra(GroupDetailActivity.KEY_GROUP_TITLE, group.getTitle());
+        mContext.startActivity(intent);
+    }
+
+    private void hideSoftKeyboard() {
+        if (mContext == null) {
+            return;
+        }
+        // Hide soft keyboard, if visible
+        InputMethodManager inputMethodManager = (InputMethodManager)
+                mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+        inputMethodManager.hideSoftInputFromWindow(mListView.getWindowToken(), 0);
+    }
+
+    /**
+     * Dismisses the soft keyboard when the list takes focus.
+     */
+    @Override
+    public void onFocusChange(View view, boolean hasFocus) {
+        if (view == mListView && hasFocus) {
+            hideSoftKeyboard();
+        }
+    }
+
+    /**
+     * Dismisses the soft keyboard when the list is touched.
+     */
+    @Override
+    public boolean onTouch(View view, MotionEvent event) {
+        if (view == mListView) {
+            hideSoftKeyboard();
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
new file mode 100644
index 0000000..98babac
--- /dev/null
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -0,0 +1,180 @@
+/*
+ * 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.group;
+
+import com.android.contacts.ContactPhotoManager;
+import com.android.contacts.R;
+import com.android.contacts.activities.GroupDetailActivity;
+import com.android.contacts.list.ContactListAdapter;
+import com.android.contacts.list.ContactListFilter;
+import com.android.contacts.list.DefaultContactListAdapter;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Directory;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+
+/**
+ * Displays the details of a group and shows a list of actions possible for the group.
+ */
+public class GroupDetailFragment extends Fragment implements OnScrollListener {
+
+    private static final String TAG = "GroupDetailFragment";
+
+    private static final int LOADER_MEMBERS = 0;
+
+    private Context mContext;
+
+    private View mRootView;
+    private ListView mMemberListView;
+
+    private ContactListAdapter mAdapter;
+    private ContactPhotoManager mPhotoManager;
+
+    public GroupDetailFragment() {
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        mContext = activity;
+        configurePhotoLoader();
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        mContext = null;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+        mRootView = inflater.inflate(R.layout.group_detail_fragment, container, false);
+        mMemberListView = (ListView) mRootView.findViewById(R.id.member_list);
+        mMemberListView.setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                // TODO: Open contact detail for this person
+            }
+        });
+        return mRootView;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        Intent intent = getActivity().getIntent();
+        String accountType = intent.getStringExtra(GroupDetailActivity.KEY_ACCOUNT_TYPE);
+        String accountName = intent.getStringExtra(GroupDetailActivity.KEY_ACCOUNT_NAME);
+        long groupId = intent.getLongExtra(GroupDetailActivity.KEY_GROUP_ID, -1);
+        String groupTitle = intent.getStringExtra(GroupDetailActivity.KEY_GROUP_TITLE);
+
+        configureAdapter(accountType, accountName, groupId, groupTitle);
+        startGroupMembersLoader();
+    }
+
+    private void configureAdapter(String accountType, String accountName,
+                long groupId, String groupTitle) {
+        mAdapter = new DefaultContactListAdapter(getActivity());
+        mAdapter.setSectionHeaderDisplayEnabled(false);
+        mAdapter.setDisplayPhotos(true);
+        mAdapter.setHasHeader(0, false);
+        mAdapter.setQuickContactEnabled(false);
+        mAdapter.setPinnedPartitionHeadersEnabled(false);
+        mAdapter.setContactNameDisplayOrder(ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY);
+        mAdapter.setSortOrder(ContactsContract.Preferences.SORT_ORDER_PRIMARY);
+        mAdapter.setPhotoLoader(mPhotoManager);
+        mAdapter.setFilter(new ContactListFilter(accountType, accountName, groupId, "", false,
+                groupTitle));
+        mMemberListView.setAdapter(mAdapter);
+    }
+
+    private void configurePhotoLoader() {
+        if (mContext != null) {
+            if (mPhotoManager == null) {
+                mPhotoManager = ContactPhotoManager.getInstance(mContext);
+            }
+            if (mMemberListView != null) {
+                mMemberListView.setOnScrollListener(this);
+            }
+            if (mAdapter != null) {
+                mAdapter.setPhotoLoader(mPhotoManager);
+            }
+        }
+    }
+
+    /**
+     * Start the loader to retrieve the list of group members.
+     */
+    private void startGroupMembersLoader() {
+        getLoaderManager().destroyLoader(LOADER_MEMBERS);
+        getLoaderManager().restartLoader(LOADER_MEMBERS, null, mGroupMemberListLoaderListener);
+    }
+
+    /**
+     * The listener for the group members list loader
+     */
+    private final LoaderManager.LoaderCallbacks<Cursor> mGroupMemberListLoaderListener =
+            new LoaderCallbacks<Cursor>() {
+
+        @Override
+        public CursorLoader onCreateLoader(int id, Bundle args) {
+            CursorLoader loader = new CursorLoader(mContext, null, null, null, null, null);
+            mAdapter.configureLoader(loader, Directory.DEFAULT);
+            return loader;
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+            mAdapter.changeCursor(loader.getId(), data);
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Cursor> loader) {}
+    };
+
+    @Override
+    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+            int totalItemCount) {
+    }
+
+    @Override
+    public void onScrollStateChanged(AbsListView view, int scrollState) {
+        if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
+            mPhotoManager.pause();
+        } else {
+            mPhotoManager.resume();
+        }
+    }
+}
diff --git a/src/com/android/contacts/interactions/ContactDeletionInteraction.java b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
index 08f8d00..f195c2f 100644
--- a/src/com/android/contacts/interactions/ContactDeletionInteraction.java
+++ b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
@@ -206,8 +206,13 @@
 
         final Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
         showDialog(mMessageId, contactUri);
+
+        // We don't want onLoadFinished() calls any more, which may come when the database is
+        // updating.
+        getLoaderManager().destroyLoader(R.id.dialog_delete_contact_loader_id);
     }
 
+    @Override
     public void onLoaderReset(Loader<Cursor> loader) {
     }
 
@@ -235,7 +240,6 @@
     public void onDismiss(DialogInterface dialog) {
         mActive = false;
         mDialog = null;
-        getLoaderManager().destroyLoader(R.id.dialog_delete_contact_loader_id);
     }
 
     @Override
diff --git a/src/com/android/contacts/interactions/ImportExportInteraction.java b/src/com/android/contacts/interactions/ImportExportDialogFragment.java
similarity index 62%
rename from src/com/android/contacts/interactions/ImportExportInteraction.java
rename to src/com/android/contacts/interactions/ImportExportDialogFragment.java
index bd8ac16..4e22fa2 100644
--- a/src/com/android/contacts/interactions/ImportExportInteraction.java
+++ b/src/com/android/contacts/interactions/ImportExportDialogFragment.java
@@ -17,14 +17,16 @@
 package com.android.contacts.interactions;
 
 import com.android.contacts.R;
+import com.android.contacts.editor.SelectAccountDialogFragment;
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.util.AccountSelectionUtil;
 import com.android.contacts.vcard.ExportVCardActivity;
 
 import android.accounts.Account;
-import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -35,7 +37,6 @@
 import android.provider.ContactsContract.Contacts;
 import android.telephony.TelephonyManager;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -46,70 +47,39 @@
 import java.util.List;
 
 /**
- * An interaction invoked to import/export contacts.
+ * An dialog invoked to import/export contacts.
  */
-public class ImportExportInteraction {
-
-    private static final String TAG = "ImportExportInteraction";
+public class ImportExportDialogFragment extends DialogFragment {
+    public static final String TAG = "ImportExportDialogFragment";
 
     private final String[] LOOKUP_PROJECTION = new String[] {
             Contacts.LOOKUP_KEY
     };
 
-    private final Context mContext;
-
-    public ImportExportInteraction(Context context) {
-        this.mContext = context;
+    /** Preferred way to show this dialog */
+    public static void show(FragmentManager fragmentManager) {
+        final ImportExportDialogFragment fragment = new ImportExportDialogFragment();
+        fragment.show(fragmentManager, ImportExportDialogFragment.TAG);
     }
 
-    /**
-     * Creates a {@link Dialog} that allows the user to choose between a bulk import
-     * and bulk export task across all contacts.
-     */
-    public void startInteraction() {
-        showDialog(R.id.dialog_import_export_options, null);
-    }
-
-    public Dialog onCreateDialog(int id, Bundle bundle) {
-        switch (id) {
-            case R.id.dialog_import_export_options: {
-                return createOptionsDialog();
-            }
-            case R.string.import_from_sim:
-            case R.string.import_from_sdcard: {
-                return AccountSelectionUtil.getSelectAccountDialog(mContext, id);
-            }
-            case R.id.dialog_sdcard_not_found: {
-                return new AlertDialog.Builder(mContext)
-                        .setTitle(R.string.no_sdcard_title)
-                        .setIconAttribute(android.R.attr.alertDialogIcon)
-                        .setMessage(R.string.no_sdcard_message)
-                        .setPositiveButton(android.R.string.ok, null).create();
-            }
-        }
-
-        return null;
-    }
-
-    private Dialog createOptionsDialog() {
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
         // Wrap our context to inflate list items using the correct theme
-        final Resources res = mContext.getResources();
-        final LayoutInflater dialogInflater = (LayoutInflater)mContext
+        final Resources res = getActivity().getResources();
+        final LayoutInflater dialogInflater = (LayoutInflater)getActivity()
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
         // Adapter that shows a list of string resources
-        final ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(mContext,
+        final ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(getActivity(),
                 android.R.layout.select_dialog_item) {
             @Override
             public View getView(int position, View convertView, ViewGroup parent) {
-                if (convertView == null) {
-                    convertView = dialogInflater.inflate(android.R.layout.select_dialog_item,
-                            parent, false);
-                }
+                final TextView result = (TextView)(convertView != null ? convertView :
+                        dialogInflater.inflate(android.R.layout.select_dialog_item, parent, false));
 
-                final int resId = this.getItem(position);
-                ((TextView)convertView).setText(resId);
-                return convertView;
+                final int resId = getItem(position);
+                result.setText(resId);
+                return result;
             }
         };
 
@@ -128,6 +98,7 @@
 
         final DialogInterface.OnClickListener clickListener =
                 new DialogInterface.OnClickListener() {
+            @Override
             public void onClick(DialogInterface dialog, int which) {
                 dialog.dismiss();
 
@@ -139,8 +110,8 @@
                         break;
                     }
                     case R.string.export_to_sdcard: {
-                        Intent exportIntent = new Intent(mContext, ExportVCardActivity.class);
-                        mContext.startActivity(exportIntent);
+                        Intent exportIntent = new Intent(getActivity(), ExportVCardActivity.class);
+                        getActivity().startActivity(exportIntent);
                         break;
                     }
                     case R.string.share_visible_contacts: {
@@ -148,14 +119,13 @@
                         break;
                     }
                     default: {
-                        Log.e(TAG, "Unexpected resource: " +
-                                mContext.getResources().getResourceEntryName(resId));
+                        Log.e(TAG, "Unexpected resource: "
+                                + getActivity().getResources().getResourceEntryName(resId));
                     }
                 }
             }
         };
-
-        return new AlertDialog.Builder(mContext)
+        return new AlertDialog.Builder(getActivity())
                 .setTitle(R.string.dialog_import_export)
                 .setNegativeButton(android.R.string.cancel, null)
                 .setSingleChoiceItems(adapter, -1, clickListener)
@@ -163,13 +133,12 @@
     }
 
     private void doShareVisibleContacts() {
-
-        // TODO move the query into a loader
-        final Cursor cursor = mContext.getContentResolver().query(Contacts.CONTENT_URI,
+        // TODO move the query into a loader and do this in a background thread
+        final Cursor cursor = getActivity().getContentResolver().query(Contacts.CONTENT_URI,
                 LOOKUP_PROJECTION, Contacts.IN_VISIBLE_GROUP + "!=0", null, null);
         try {
             if (!cursor.moveToFirst()) {
-                Toast.makeText(mContext, R.string.share_error, Toast.LENGTH_SHORT).show();
+                Toast.makeText(getActivity(), R.string.share_error, Toast.LENGTH_SHORT).show();
                 return;
             }
 
@@ -188,7 +157,7 @@
             final Intent intent = new Intent(Intent.ACTION_SEND);
             intent.setType(Contacts.CONTENT_VCARD_TYPE);
             intent.putExtra(Intent.EXTRA_STREAM, uri);
-            mContext.startActivity(intent);
+            getActivity().startActivity(intent);
         } finally {
             cursor.close();
         }
@@ -199,19 +168,37 @@
         // - more than one accounts -> ask the user
         // - just one account -> use the account without asking the user
         // - no account -> use phone-local storage without asking the user
-        final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+        final AccountTypeManager accountTypes = AccountTypeManager.getInstance(getActivity());
         final List<Account> accountList = accountTypes.getAccounts(true);
         final int size = accountList.size();
         if (size > 1) {
-            showDialog(resId, null);
+            // Send over to the account selector
+            ImportExportAccountSelectorDialog.show(getFragmentManager(), resId);
             return;
         }
 
-        AccountSelectionUtil.doImport(mContext, resId, (size == 1 ? accountList.get(0) : null));
+        AccountSelectionUtil.doImport(getActivity(), resId,
+                (size == 1 ? accountList.get(0) : null));
     }
 
-    /* Visible for testing */
-    void showDialog(int dialogId, Bundle bundle) {
-        ((Activity)mContext).showDialog(dialogId, bundle);
+    /** Sub-Dialog for showing an account selector in case there are several accounts */
+    public static class ImportExportAccountSelectorDialog extends SelectAccountDialogFragment {
+        private static final String SELECTOR_TAG = "ImportExportAccountSelectorDialog";
+        private static final String BUNDLE_RES_ID = "resourceId";
+
+        public static void show(FragmentManager manager, int resId) {
+            final ImportExportAccountSelectorDialog dialog =
+                new ImportExportAccountSelectorDialog();
+            final Bundle bundle = new Bundle();
+            bundle.putInt(BUNDLE_RES_ID, resId);
+            dialog.setArguments(bundle);
+            dialog.show(manager, SELECTOR_TAG);
+        }
+
+        @Override
+        protected void onAccountSelected(Account account) {
+            final int resourceId = getArguments().getInt(BUNDLE_RES_ID);
+            AccountSelectionUtil.doImport(getActivity(), resourceId, account);
+        }
     }
 }
diff --git a/src/com/android/contacts/interactions/PhoneNumberInteraction.java b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
index 277fa56..399b0ea 100644
--- a/src/com/android/contacts/interactions/PhoneNumberInteraction.java
+++ b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
@@ -16,14 +16,19 @@
 package com.android.contacts.interactions;
 
 
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.PhoneNumberUtil.MatchType;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+
 import com.android.contacts.Collapser;
 import com.android.contacts.Collapser.Collapsible;
 import com.android.contacts.ContactSaveService;
 import com.android.contacts.R;
 import com.android.contacts.model.AccountType;
-import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.AccountType.StringInflater;
 import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.DataKind;
 
 import android.app.Activity;
 import android.app.AlertDialog;
@@ -46,7 +51,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
-import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -112,8 +117,16 @@
         }
 
         public boolean shouldCollapseWith(PhoneItem phoneItem) {
-            if (PhoneNumberUtils.compareStrictly(phoneNumber, phoneItem.phoneNumber)) {
-                return true;
+            try {
+                PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+                PhoneNumber phoneNumber1 = util.parse(phoneNumber, "ZZ" /* Unknown */);
+                PhoneNumber phoneNumber2 = util.parse(phoneItem.phoneNumber, "ZZ" /* Unknown */);
+                MatchType matchType = util.isNumberMatch(phoneNumber1, phoneNumber2);
+                if (matchType == MatchType.SHORT_NSN_MATCH) {
+                    return true;
+                }
+            } catch (NumberParseException e) {
+                return TextUtils.equals(phoneNumber, phoneItem.phoneNumber);
             }
             return false;
         }
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index be920db..1ecfff1 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -583,7 +583,7 @@
 
     public void viewContact(Uri contactUri) {
         setSelectedContactUri(contactUri, false, false, true, false);
-        if (mListener != null) { mListener.onViewContactAction(contactUri); }
+        if (mListener != null) mListener.onViewContactAction(contactUri);
     }
 
     public void editContact(Uri contactUri) {
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index c85f582..fcced62 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -821,6 +821,7 @@
         }
     }
 
+    @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         hideSoftKeyboard();
 
@@ -840,6 +841,7 @@
     /**
      * Dismisses the soft keyboard when the list takes focus.
      */
+    @Override
     public void onFocusChange(View view, boolean hasFocus) {
         if (view == mListView && hasFocus) {
             hideSoftKeyboard();
@@ -849,6 +851,7 @@
     /**
      * Dismisses the soft keyboard when the list is touched.
      */
+    @Override
     public boolean onTouch(View view, MotionEvent event) {
         if (view == mListView) {
             hideSoftKeyboard();
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index cfacc85..2c57983 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -274,8 +274,8 @@
     }
 
     protected void bindName(final ContactListItemView view, Cursor cursor) {
-        view.showDisplayName(cursor, mDisplayNameColumnIndex, isNameHighlightingEnabled(),
-                mAlternativeDisplayNameColumnIndex);
+        view.showDisplayName(cursor, mDisplayNameColumnIndex, mAlternativeDisplayNameColumnIndex,
+                isNameHighlightingEnabled(), getContactNameDisplayOrder());
         view.showPhoneticName(cursor, CONTACT_PHONETIC_NAME_COLUMN_INDEX);
     }
 
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index 116f3f6..022c2f6 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -18,6 +18,7 @@
 
 import com.android.contacts.ContactPresenceIconUtil;
 import com.android.contacts.R;
+import com.android.contacts.format.FormatUtils;
 import com.android.contacts.widget.TextWithHighlighting;
 import com.android.contacts.widget.TextWithHighlightingFactory;
 
@@ -30,8 +31,11 @@
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
+import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
+import android.text.Spannable;
 import android.text.SpannableString;
+import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.text.style.ForegroundColorSpan;
@@ -45,6 +49,8 @@
 import android.widget.QuickContactBadge;
 import android.widget.TextView;
 
+import java.util.Arrays;
+
 /**
  * A custom view for an item in the contact list.
  */
@@ -63,7 +69,6 @@
     private final int mPaddingRight;
     private final int mPaddingBottom;
     private final int mPaddingLeft;
-    private final int mFadingEdgeLength;
     private final int mGapBetweenImageAndText;
     private final int mGapBetweenLabelAndData;
     private final int mCallButtonPadding;
@@ -169,8 +174,6 @@
                 R.styleable.ContactListItemView_list_item_padding_left, 0);
         mPaddingRight = a.getDimensionPixelOffset(
                 R.styleable.ContactListItemView_list_item_padding_right, 0);
-        mFadingEdgeLength = a.getDimensionPixelOffset(
-                R.styleable.ContactListItemView_list_item_fading_edge_length, 0);
         mGapBetweenImageAndText = a.getDimensionPixelOffset(
                 R.styleable.ContactListItemView_list_item_gap_between_image_and_text, 0);
         mGapBetweenLabelAndData = a.getDimensionPixelOffset(
@@ -617,8 +620,6 @@
         if (mNameTextView == null) {
             mNameTextView = new TextView(mContext);
             mNameTextView.setSingleLine(true);
-            mNameTextView.setHorizontalFadingEdgeEnabled(true);
-            mNameTextView.setFadingEdgeLength(mFadingEdgeLength);
             mNameTextView.setEllipsize(getTextEllipsis());
             mNameTextView.setTextAppearance(mContext, android.R.style.TextAppearance_Medium);
             mNameTextView.setGravity(Gravity.CENTER_VERTICAL);
@@ -674,8 +675,6 @@
         if (mPhoneticNameTextView == null) {
             mPhoneticNameTextView = new TextView(mContext);
             mPhoneticNameTextView.setSingleLine(true);
-            mPhoneticNameTextView.setHorizontalFadingEdgeEnabled(true);
-            mPhoneticNameTextView.setFadingEdgeLength(mFadingEdgeLength);
             mPhoneticNameTextView.setEllipsize(getTextEllipsis());
             mPhoneticNameTextView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
             mPhoneticNameTextView.setTypeface(mPhoneticNameTextView.getTypeface(), Typeface.BOLD);
@@ -721,8 +720,6 @@
         if (mLabelView == null) {
             mLabelView = new TextView(mContext);
             mLabelView.setSingleLine(true);
-            mLabelView.setHorizontalFadingEdgeEnabled(true);
-            mLabelView.setFadingEdgeLength(mFadingEdgeLength);
             mLabelView.setEllipsize(getTextEllipsis());
             mLabelView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
             mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD);
@@ -754,8 +751,6 @@
         if (mDataView == null) {
             mDataView = new TextView(mContext);
             mDataView.setSingleLine(true);
-            mDataView.setHorizontalFadingEdgeEnabled(true);
-            mDataView.setFadingEdgeLength(mFadingEdgeLength);
             mDataView.setEllipsize(getTextEllipsis());
             mDataView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
             addView(mDataView);
@@ -785,8 +780,6 @@
         if (mSnippetView == null) {
             mSnippetView = new TextView(mContext);
             mSnippetView.setSingleLine(true);
-            mSnippetView.setHorizontalFadingEdgeEnabled(true);
-            mSnippetView.setFadingEdgeLength(mFadingEdgeLength);
             mSnippetView.setEllipsize(getTextEllipsis());
             mSnippetView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
             mSnippetView.setTypeface(mSnippetView.getTypeface(), Typeface.BOLD);
@@ -818,9 +811,21 @@
         return mActivatedStateSupported ? TruncateAt.START : TruncateAt.MARQUEE;
     }
 
-    public void showDisplayName(Cursor cursor, int nameColumnIndex, boolean highlightingEnabled,
-            int alternativeNameColumnIndex) {
+    public void showDisplayName(Cursor cursor, int nameColumnIndex, int alternativeNameColumnIndex,
+            boolean highlightingEnabled, int displayOrder) {
+
+        // Copy out the display name and alternate display name, and compute the point at which
+        // the two overlap (for bolding).
         cursor.copyStringToBuffer(nameColumnIndex, mNameBuffer);
+        cursor.copyStringToBuffer(alternativeNameColumnIndex, mHighlightedTextBuffer);
+        int overlapPoint = FormatUtils.overlapPoint(mNameBuffer, mHighlightedTextBuffer);
+        int boldStart = 0;
+        int boldEnd = overlapPoint;
+        if (displayOrder == ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE) {
+            boldStart = overlapPoint;
+            boldEnd = mNameBuffer.sizeCopied;
+        }
+
         TextView nameView = getNameTextView();
         int size = mNameBuffer.sizeCopied;
         if (size != 0) {
@@ -831,11 +836,24 @@
                     mTextWithHighlighting =
                             mTextWithHighlightingFactory.createTextWithHighlighting();
                 }
-                cursor.copyStringToBuffer(alternativeNameColumnIndex, mHighlightedTextBuffer);
                 mTextWithHighlighting.setText(mNameBuffer, mHighlightedTextBuffer);
-                nameView.setText(mTextWithHighlighting);
+                if (overlapPoint > 0) {
+                    // Bold the first name.
+                    nameView.setText(FormatUtils.applyStyleToSpan(Typeface.BOLD,
+                            mTextWithHighlighting, boldStart, boldEnd,
+                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE));
+                } else {
+                    nameView.setText(mTextWithHighlighting);
+                }
             } else {
-                nameView.setText(mNameBuffer.data, 0, size);
+                if (overlapPoint > 0) {
+                    // Bold the first name.
+                    nameView.setText(FormatUtils.applyStyleToSpan(Typeface.BOLD,
+                            new String(Arrays.copyOfRange(mNameBuffer.data, 0, size)),
+                            boldStart, boldEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE));
+                } else {
+                    nameView.setText(mNameBuffer.data, 0, size);
+                }
             }
         } else {
             nameView.setText(mUnknownNameText);
@@ -947,7 +965,9 @@
             }
 
             String string = new String(text.data, 0, text.sizeCopied);
-            SpannableString name = new SpannableString(string);
+            SpannableString name = new SpannableString(
+                    FormatUtils.applyStyleToSpan(Typeface.BOLD, string, 0, index,
+                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE));
             name.setSpan(mPrefixColorSpan, index, index + mHighlightedPrefix.length, 0 /* flags */);
             textView.setText(name);
         } else {
diff --git a/src/com/android/contacts/list/ContactsIntentResolver.java b/src/com/android/contacts/list/ContactsIntentResolver.java
index afe29df..3ef68d8 100644
--- a/src/com/android/contacts/list/ContactsIntentResolver.java
+++ b/src/com/android/contacts/list/ContactsIntentResolver.java
@@ -27,6 +27,7 @@
 import android.provider.Contacts.ContactMethods;
 import android.provider.Contacts.People;
 import android.provider.Contacts.Phones;
+import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.Contacts;
@@ -95,6 +96,8 @@
             } else if (ContactMethods.CONTENT_POSTAL_TYPE.equals(resolvedType)) {
                 request.setActionCode(ContactsRequest.ACTION_PICK_POSTAL);
                 request.setLegacyCompatibilityMode(true);
+            } else if (Email.CONTENT_TYPE.equals(resolvedType)) {
+                request.setActionCode(ContactsRequest.ACTION_PICK_EMAIL);
             }
         } else if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
             String component = intent.getComponent().getClassName();
diff --git a/src/com/android/contacts/list/ContactsRequest.java b/src/com/android/contacts/list/ContactsRequest.java
index aefa451..e20d189 100644
--- a/src/com/android/contacts/list/ContactsRequest.java
+++ b/src/com/android/contacts/list/ContactsRequest.java
@@ -62,6 +62,9 @@
     /** Show all postal addresses and pick them when clicking */
     public static final int ACTION_PICK_POSTAL = 100;
 
+    /** Show all postal addresses and pick them when clicking */
+    public static final int ACTION_PICK_EMAIL = 105;
+
     /** Show all contacts and create a shortcut for the picked contact */
     public static final int ACTION_CREATE_SHORTCUT_CONTACT = 110;
 
diff --git a/src/com/android/contacts/list/EmailAddressListAdapter.java b/src/com/android/contacts/list/EmailAddressListAdapter.java
new file mode 100644
index 0000000..eae02b5
--- /dev/null
+++ b/src/com/android/contacts/list/EmailAddressListAdapter.java
@@ -0,0 +1,190 @@
+/*
+ * 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.list;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.ContactCounts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A cursor adapter for the {@link Email#CONTENT_TYPE} content type.
+ */
+public class EmailAddressListAdapter extends ContactEntryListAdapter {
+
+    static final String[] EMAILS_PROJECTION = new String[] {
+        Email._ID,                       // 0
+        Email.TYPE,                      // 1
+        Email.LABEL,                     // 2
+        Email.DATA,                      // 3
+        Email.DISPLAY_NAME_PRIMARY,      // 4
+        Email.DISPLAY_NAME_ALTERNATIVE,  // 5
+        Email.PHOTO_ID,                  // 6
+    };
+
+    protected static final int EMAIL_ID_COLUMN_INDEX = 0;
+    protected static final int EMAIL_TYPE_COLUMN_INDEX = 1;
+    protected static final int EMAIL_LABEL_COLUMN_INDEX = 2;
+    protected static final int EMAIL_ADDRESS_COLUMN_INDEX = 3;
+    protected static final int EMAIL_PRIMARY_DISPLAY_NAME_COLUMN_INDEX = 4;
+    protected static final int EMAIL_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX = 5;
+    protected static final int EMAIL_PHOTO_ID_COLUMN_INDEX = 6;
+
+    private CharSequence mUnknownNameText;
+    private int mDisplayNameColumnIndex;
+    private int mAlternativeDisplayNameColumnIndex;
+
+    public EmailAddressListAdapter(Context context) {
+        super(context);
+
+        mUnknownNameText = context.getText(android.R.string.unknownName);
+    }
+
+    @Override
+    public void configureLoader(CursorLoader loader, long directoryId) {
+        final Builder builder;
+        if (isSearchMode()) {
+            builder = Email.CONTENT_FILTER_URI.buildUpon();
+            String query = getQueryString();
+            builder.appendPath(TextUtils.isEmpty(query) ? "" : query);
+        } else {
+            builder = Email.CONTENT_URI.buildUpon();
+        }
+        builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+                String.valueOf(directoryId));
+        applyDataRestriction(builder);
+        loader.setUri(builder.build());
+        loader.setProjection(EMAILS_PROJECTION);
+
+        if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
+            loader.setSortOrder(Email.SORT_KEY_PRIMARY);
+        } else {
+            loader.setSortOrder(Email.SORT_KEY_ALTERNATIVE);
+        }
+    }
+
+    protected static Builder buildSectionIndexerUri(Uri uri) {
+        return uri.buildUpon()
+                .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true");
+    }
+
+    @Override
+    public String getContactDisplayName(int position) {
+        return ((Cursor)getItem(position)).getString(mDisplayNameColumnIndex);
+    }
+
+    @Override
+    public void setContactNameDisplayOrder(int displayOrder) {
+        super.setContactNameDisplayOrder(displayOrder);
+        if (getContactNameDisplayOrder() == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+            mDisplayNameColumnIndex = EMAIL_PRIMARY_DISPLAY_NAME_COLUMN_INDEX;
+            mAlternativeDisplayNameColumnIndex = EMAIL_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX;
+        } else {
+            mDisplayNameColumnIndex = EMAIL_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX;
+            mAlternativeDisplayNameColumnIndex = EMAIL_PRIMARY_DISPLAY_NAME_COLUMN_INDEX;
+        }
+    }
+
+    /**
+     * Builds a {@link Data#CONTENT_URI} for the current cursor
+     * position.
+     */
+    public Uri getDataUri(int position) {
+        long id = ((Cursor)getItem(position)).getLong(EMAIL_ID_COLUMN_INDEX);
+        return ContentUris.withAppendedId(Data.CONTENT_URI, id);
+    }
+
+    @Override
+    protected View newView(Context context, int partition, Cursor cursor, int position,
+            ViewGroup parent) {
+        final ContactListItemView view = new ContactListItemView(context, null);
+        view.setUnknownNameText(mUnknownNameText);
+        view.setTextWithHighlightingFactory(getTextWithHighlightingFactory());
+        view.setQuickContactEnabled(isQuickContactEnabled());
+        return view;
+    }
+
+    @Override
+    protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+        ContactListItemView view = (ContactListItemView)itemView;
+        bindSectionHeaderAndDivider(view, position);
+        bindName(view, cursor);
+        bindPhoto(view, cursor);
+        bindEmailAddress(view, cursor);
+    }
+
+    protected void bindEmailAddress(ContactListItemView view, Cursor cursor) {
+        CharSequence label = null;
+        if (!cursor.isNull(EMAIL_TYPE_COLUMN_INDEX)) {
+            final int type = cursor.getInt(EMAIL_TYPE_COLUMN_INDEX);
+            final String customLabel = cursor.getString(EMAIL_LABEL_COLUMN_INDEX);
+
+            // TODO cache
+            label = Email.getTypeLabel(getContext().getResources(), type, customLabel);
+        }
+        view.setLabel(label);
+        view.showData(cursor, EMAIL_ADDRESS_COLUMN_INDEX);
+    }
+
+    protected void bindSectionHeaderAndDivider(final ContactListItemView view, int position) {
+        final int section = getSectionForPosition(position);
+        if (getPositionForSection(section) == position) {
+            String title = (String)getSections()[section];
+            view.setSectionHeader(title);
+        } else {
+            view.setDividerVisible(false);
+            view.setSectionHeader(null);
+        }
+
+        // move the divider for the last item in a section
+        if (getPositionForSection(section + 1) - 1 == position) {
+            view.setDividerVisible(false);
+        } else {
+            view.setDividerVisible(true);
+        }
+    }
+
+    protected void bindName(final ContactListItemView view, Cursor cursor) {
+        view.showDisplayName(cursor, mDisplayNameColumnIndex, mAlternativeDisplayNameColumnIndex,
+                isNameHighlightingEnabled(), getContactNameDisplayOrder());
+//        view.showPhoneticName(cursor, PHONE_PHONETIC_NAME_COLUMN_INDEX);
+    }
+
+    protected void bindPhoto(final ContactListItemView view, Cursor cursor) {
+        long photoId = 0;
+        if (!cursor.isNull(EMAIL_PHOTO_ID_COLUMN_INDEX)) {
+            photoId = cursor.getLong(EMAIL_PHOTO_ID_COLUMN_INDEX);
+        }
+
+        getPhotoLoader().loadPhoto(view.getPhotoView(), photoId);
+    }
+//
+//    protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
+//        view.showSnippet(cursor, SUMMARY_SNIPPET_MIMETYPE_COLUMN_INDEX,
+//                SUMMARY_SNIPPET_DATA1_COLUMN_INDEX, SUMMARY_SNIPPET_DATA4_COLUMN_INDEX);
+//    }
+
+}
diff --git a/src/com/android/contacts/list/EmailAddressPickerFragment.java b/src/com/android/contacts/list/EmailAddressPickerFragment.java
new file mode 100644
index 0000000..168e135
--- /dev/null
+++ b/src/com/android/contacts/list/EmailAddressPickerFragment.java
@@ -0,0 +1,64 @@
+/*
+ * 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.list;
+
+import com.android.contacts.R;
+
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Fragment containing an email list for picking.
+ */
+public class EmailAddressPickerFragment extends ContactEntryListFragment<ContactEntryListAdapter> {
+    private OnEmailAddressPickerActionListener mListener;
+
+    public EmailAddressPickerFragment() {
+        setQuickContactEnabled(false);
+        setPhotoLoaderEnabled(true);
+        setSectionHeaderDisplayEnabled(true);
+        setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DATA_SHORTCUT);
+    }
+
+    public void setOnEmailAddressPickerActionListener(OnEmailAddressPickerActionListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    protected void onItemClick(int position, long id) {
+        EmailAddressListAdapter adapter = (EmailAddressListAdapter)getAdapter();
+        pickEmailAddress(adapter.getDataUri(position));
+    }
+
+    @Override
+    protected ContactEntryListAdapter createListAdapter() {
+        EmailAddressListAdapter adapter = new EmailAddressListAdapter(getActivity());
+        adapter.setSectionHeaderDisplayEnabled(true);
+        adapter.setDisplayPhotos(true);
+        return adapter;
+    }
+
+    @Override
+    protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+        return inflater.inflate(R.layout.contacts_list_content, null);
+    }
+
+    private void pickEmailAddress(Uri uri) {
+        mListener.onPickEmailAddressAction(uri);
+    }
+}
diff --git a/src/com/android/contacts/list/LegacyContactListAdapter.java b/src/com/android/contacts/list/LegacyContactListAdapter.java
index 6747d1f..ffc8fc3 100644
--- a/src/com/android/contacts/list/LegacyContactListAdapter.java
+++ b/src/com/android/contacts/list/LegacyContactListAdapter.java
@@ -85,7 +85,8 @@
     }
 
     protected void bindName(final ContactListItemView view, Cursor cursor) {
-        view.showDisplayName(cursor, PERSON_DISPLAY_NAME_COLUMN_INDEX, false, 0);
+        view.showDisplayName(cursor, PERSON_DISPLAY_NAME_COLUMN_INDEX, 0, false,
+                getContactNameDisplayOrder());
         view.showPhoneticName(cursor, PERSON_PHONETIC_NAME_COLUMN_INDEX);
     }
 
diff --git a/src/com/android/contacts/list/LegacyPhoneNumberListAdapter.java b/src/com/android/contacts/list/LegacyPhoneNumberListAdapter.java
index 47747fb..547650d 100644
--- a/src/com/android/contacts/list/LegacyPhoneNumberListAdapter.java
+++ b/src/com/android/contacts/list/LegacyPhoneNumberListAdapter.java
@@ -89,7 +89,8 @@
     }
 
     protected void bindName(final ContactListItemView view, Cursor cursor) {
-        view.showDisplayName(cursor, PHONE_DISPLAY_NAME_COLUMN_INDEX, false, 0);
+        view.showDisplayName(cursor, PHONE_DISPLAY_NAME_COLUMN_INDEX, 0, false,
+                getContactNameDisplayOrder());
         view.showPhoneticName(cursor, PHONE_PHONETIC_NAME_COLUMN_INDEX);
     }
 
diff --git a/src/com/android/contacts/list/LegacyPostalAddressListAdapter.java b/src/com/android/contacts/list/LegacyPostalAddressListAdapter.java
index 3796c62..48b3f0c 100644
--- a/src/com/android/contacts/list/LegacyPostalAddressListAdapter.java
+++ b/src/com/android/contacts/list/LegacyPostalAddressListAdapter.java
@@ -90,7 +90,8 @@
     }
 
     protected void bindName(final ContactListItemView view, Cursor cursor) {
-        view.showDisplayName(cursor, POSTAL_DISPLAY_NAME_COLUMN_INDEX, false, 0);
+        view.showDisplayName(cursor, POSTAL_DISPLAY_NAME_COLUMN_INDEX, 0, false,
+                getContactNameDisplayOrder());
         view.showPhoneticName(cursor, POSTAL_PHONETIC_NAME_COLUMN_INDEX);
     }
 
diff --git a/src/com/android/contacts/list/OnEmailAddressPickerActionListener.java b/src/com/android/contacts/list/OnEmailAddressPickerActionListener.java
new file mode 100644
index 0000000..e785323
--- /dev/null
+++ b/src/com/android/contacts/list/OnEmailAddressPickerActionListener.java
@@ -0,0 +1,29 @@
+/*
+ * 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.list;
+
+import android.net.Uri;
+
+/**
+ * Action callbacks that can be sent by a email address picker.
+ */
+public interface OnEmailAddressPickerActionListener  {
+
+    /**
+     * Returns the selected phone number to the requester.
+     */
+    void onPickEmailAddressAction(Uri dataUri);
+}
diff --git a/src/com/android/contacts/list/PhoneNumberListAdapter.java b/src/com/android/contacts/list/PhoneNumberListAdapter.java
index 48a6042..9356bb6 100644
--- a/src/com/android/contacts/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/list/PhoneNumberListAdapter.java
@@ -185,8 +185,8 @@
     }
 
     protected void bindName(final ContactListItemView view, Cursor cursor) {
-        view.showDisplayName(cursor, mDisplayNameColumnIndex, isNameHighlightingEnabled(),
-                mAlternativeDisplayNameColumnIndex);
+        view.showDisplayName(cursor, mDisplayNameColumnIndex, mAlternativeDisplayNameColumnIndex,
+                isNameHighlightingEnabled(), getContactNameDisplayOrder());
         view.showPhoneticName(cursor, PHONE_PHONETIC_NAME_COLUMN_INDEX);
     }
 
diff --git a/src/com/android/contacts/list/PostalAddressListAdapter.java b/src/com/android/contacts/list/PostalAddressListAdapter.java
index e622ec6..86d465a 100644
--- a/src/com/android/contacts/list/PostalAddressListAdapter.java
+++ b/src/com/android/contacts/list/PostalAddressListAdapter.java
@@ -131,7 +131,7 @@
             final String customLabel = cursor.getString(POSTAL_LABEL_COLUMN_INDEX);
 
             // TODO cache
-            label = StructuredPostal.getTypeLabel(getContext().getResources(), type, label);
+            label = StructuredPostal.getTypeLabel(getContext().getResources(), type, customLabel);
         }
         view.setLabel(label);
         view.showData(cursor, POSTAL_ADDRESS_COLUMN_INDEX);
@@ -156,8 +156,8 @@
     }
 
     protected void bindName(final ContactListItemView view, Cursor cursor) {
-        view.showDisplayName(cursor, mDisplayNameColumnIndex, isNameHighlightingEnabled(),
-                mAlternativeDisplayNameColumnIndex);
+        view.showDisplayName(cursor, mDisplayNameColumnIndex, mAlternativeDisplayNameColumnIndex,
+                isNameHighlightingEnabled(), getContactNameDisplayOrder());
 //        view.showPhoneticName(cursor, PHONE_PHONETIC_NAME_COLUMN_INDEX);
     }
 
diff --git a/src/com/android/contacts/list/PostalAddressPickerFragment.java b/src/com/android/contacts/list/PostalAddressPickerFragment.java
index f14b718..5f7ca56 100644
--- a/src/com/android/contacts/list/PostalAddressPickerFragment.java
+++ b/src/com/android/contacts/list/PostalAddressPickerFragment.java
@@ -73,7 +73,7 @@
         return inflater.inflate(R.layout.contacts_list_content, null);
     }
 
-    public void pickPostalAddress(Uri uri) {
+    private void pickPostalAddress(Uri uri) {
         mListener.onPickPostalAddressAction(uri);
     }
 }
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index aef5c06..462f7ad 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -30,15 +30,12 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
-import android.view.View;
 import android.widget.EditText;
 
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
-import java.util.List;
 
 /**
  * Internal structure that represents constraints and styles for a specific data
@@ -160,87 +157,6 @@
     }
 
     /**
-     * Description of a specific data type, usually marked by a unique
-     * {@link Data#MIMETYPE}. Includes details about how to view and edit
-     * {@link Data} rows of this kind, including the possible {@link EditType}
-     * labels and editable {@link EditField}.
-     */
-    public static class DataKind {
-
-        public static final String PSEUDO_MIME_TYPE_DISPLAY_NAME = "#displayName";
-        public static final String PSEUDO_MIME_TYPE_PHONETIC_NAME = "#phoneticName";
-        public static final String PSEUDO_COLUMN_PHONETIC_NAME = "#phoneticName";
-
-        public String resPackageName;
-        public String mimeType;
-        public int titleRes;
-        public int iconRes;
-        public int iconAltRes;
-        public int weight;
-        public boolean editable;
-
-        /**
-         * If this is true (default), the user can add and remove values.
-         * If false, the editor will always show a single field (which might be empty).
-         */
-        public boolean isList;
-
-        public StringInflater actionHeader;
-        public StringInflater actionAltHeader;
-        public StringInflater actionBody;
-
-        public boolean actionBodySocial = false;
-
-        public String typeColumn;
-
-        /**
-         * Maximum number of values allowed in the list. -1 represents infinity.
-         * If {@link DataKind#isList} is false, this value is ignored.
-         */
-        public int typeOverallMax;
-
-        public List<EditType> typeList;
-        public List<EditField> fieldList;
-
-        public ContentValues defaultValues;
-
-        public Class<? extends View> editorClass;
-
-        /**
-         * If this is a date field, this specifies the format of the date when saving. The
-         * date includes year, month and day. If this is not a date field or the date field is not
-         * editable, this value should be ignored.
-         */
-        public SimpleDateFormat dateFormatWithoutYear;
-
-        /**
-         * If this is a date field, this specifies the format of the date when saving. The
-         * date includes month and day. If this is not a date field, the field is not editable or
-         * dates without year are not supported, this value should be ignored.
-         */
-        public SimpleDateFormat dateFormatWithYear;
-
-        public DataKind() {
-        }
-
-        public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable) {
-            this(mimeType, titleRes, iconRes, weight, editable, null);
-        }
-
-        public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable,
-                Class<? extends View> editorClass) {
-            this.mimeType = mimeType;
-            this.titleRes = titleRes;
-            this.iconRes = iconRes;
-            this.weight = weight;
-            this.editable = editable;
-            this.isList = true;
-            this.typeOverallMax = -1;
-            this.editorClass = editorClass;
-        }
-    }
-
-    /**
      * Description of a specific "type" or "label" of a {@link DataKind} row,
      * such as {@link Phone#TYPE_WORK}. Includes constraints on total number of
      * rows a {@link Contacts} may have of this type, and details on how
@@ -250,6 +166,10 @@
         public int rawValue;
         public int labelRes;
         public boolean secondary;
+        /**
+         * The number of entries allowed for the type. -1 if not specified.
+         * @see DataKind#typeOverallMax
+         */
         public int specificMax;
         public String customColumn;
 
@@ -329,6 +249,7 @@
         public boolean optional;
         public boolean shortForm;
         public boolean longForm;
+        public boolean isFullName;
 
         public EditField(String column, int titleRes) {
             this.column = column;
@@ -359,6 +280,11 @@
             this.minLines = minLines;
             return this;
         }
+
+        public EditField setIsFullName(boolean isFullName) {
+            this.isFullName = isFullName;
+            return this;
+        }
     }
 
     /**
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 06bb9bd..58195a1 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -16,7 +16,6 @@
 
 package com.android.contacts.model;
 
-import com.android.contacts.model.AccountType.DataKind;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 import com.google.i18n.phonenumbers.PhoneNumberUtil;
diff --git a/src/com/android/contacts/model/BaseAccountType.java b/src/com/android/contacts/model/BaseAccountType.java
index d4a7496..fd8f914 100644
--- a/src/com/android/contacts/model/BaseAccountType.java
+++ b/src/com/android/contacts/model/BaseAccountType.java
@@ -99,7 +99,8 @@
 
     protected DataKind addDataKindStructuredName(Context context) {
         DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
-                R.string.nameLabelsGroup, -1, -1, true));
+                R.string.nameLabelsGroup, -1, -1, true, R.layout.structured_name_editor_view,
+                android.R.style.TextAppearance_Large, -1));
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
         kind.actionBody = new SimpleInflater(Nickname.NAME);
 
@@ -128,13 +129,14 @@
 
     protected DataKind addDataKindDisplayName(Context context) {
         DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
-                R.string.nameLabelsGroup, -1, -1, true));
+                R.string.nameLabelsGroup, -1, -1, true, R.layout.text_fields_editor_view,
+                android.R.style.TextAppearance_Large, -1));
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
         kind.actionBody = new SimpleInflater(Nickname.NAME);
 
         kind.fieldList = Lists.newArrayList();
         kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
-                R.string.full_name, FLAGS_PERSON_NAME).setShortForm(true));
+                R.string.full_name, FLAGS_PERSON_NAME).setShortForm(true).setIsFullName(true));
 
         boolean displayOrderPrimary =
                 context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
@@ -168,7 +170,8 @@
 
     protected DataKind addDataKindPhoneticName(Context context) {
         DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
-                R.string.name_phonetic, -1, -1, true));
+                R.string.name_phonetic, -1, -1, true, R.layout.phonetic_name_editor_view,
+                android.R.style.TextAppearance_Medium, -1));
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
         kind.actionBody = new SimpleInflater(Nickname.NAME);
 
@@ -187,8 +190,8 @@
 
     protected DataKind addDataKindNickname(Context context) {
         DataKind kind = addKind(new DataKind(Nickname.CONTENT_ITEM_TYPE,
-                    R.string.nicknameLabelsGroup, -1, 115, true));
-
+                    R.string.nicknameLabelsGroup, -1, 115, true,
+                    R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium, -1));
         kind.isList = false;
         kind.actionHeader = new SimpleInflater(R.string.nicknameLabelsGroup);
         kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -204,7 +207,9 @@
 
     protected DataKind addDataKindPhone(Context context) {
         DataKind kind = addKind(new DataKind(Phone.CONTENT_ITEM_TYPE, R.string.phoneLabelsGroup,
-                android.R.drawable.sym_action_call, 10, true));
+                android.R.drawable.sym_action_call, 10, true,
+                R.layout.text_fields_editor_view, android.R.style.TextAppearance_Large,
+                R.string.add_phone));
         kind.iconAltRes = R.drawable.sym_action_sms;
         kind.actionHeader = new PhoneActionInflater();
         kind.actionAltHeader = new PhoneActionAltInflater();
@@ -243,7 +248,9 @@
 
     protected DataKind addDataKindEmail(Context context) {
         DataKind kind = addKind(new DataKind(Email.CONTENT_ITEM_TYPE, R.string.emailLabelsGroup,
-                R.drawable.sym_action_email_holo_light, 15, true));
+                R.drawable.sym_action_email_holo_light, 15, true,
+                R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+                R.string.add_email));
         kind.actionHeader = new EmailActionInflater();
         kind.actionBody = new SimpleInflater(Email.DATA);
         kind.typeColumn = Email.TYPE;
@@ -264,7 +271,8 @@
     protected DataKind addDataKindStructuredPostal(Context context) {
         DataKind kind = addKind(new DataKind(StructuredPostal.CONTENT_ITEM_TYPE,
                 R.string.postalLabelsGroup, R.drawable.sym_action_show_map_holo_light, 25,
-                true));
+                true, R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+                R.string.add_address));
         kind.actionHeader = new PostalActionInflater();
         kind.actionBody = new SimpleInflater(StructuredPostal.FORMATTED_ADDRESS);
         kind.typeColumn = StructuredPostal.TYPE;
@@ -285,7 +293,9 @@
 
     protected DataKind addDataKindIm(Context context) {
         DataKind kind = addKind(new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup,
-                    R.drawable.sym_action_talk_holo_light, 20, true));
+                    R.drawable.sym_action_talk_holo_light, 20, true,
+                    R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+                    R.string.add_im));
         kind.actionHeader = new ImActionInflater();
         kind.actionBody = new SimpleInflater(Im.DATA);
 
@@ -316,7 +326,8 @@
 
     protected DataKind addDataKindOrganization(Context context) {
         DataKind kind = addKind(new DataKind(Organization.CONTENT_ITEM_TYPE,
-                    R.string.organizationLabelsGroup, -1, 5, true));
+                    R.string.organizationLabelsGroup, -1, 5, true,
+                    R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium, -1));
         kind.actionHeader = new SimpleInflater(Organization.COMPANY);
         kind.actionBody = new SimpleInflater(Organization.TITLE);
         kind.isList = false;
@@ -331,7 +342,8 @@
     }
 
     protected DataKind addDataKindPhoto(Context context) {
-        DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, -1, true));
+        DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, -1, true, -1,
+                    android.R.style.TextAppearance_Medium, -1));
         kind.fieldList = Lists.newArrayList();
         kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
         return kind;
@@ -339,7 +351,9 @@
 
     protected DataKind addDataKindNote(Context context) {
         DataKind kind = addKind(new DataKind(Note.CONTENT_ITEM_TYPE,
-                    R.string.label_notes, -1, 110, true));
+                    R.string.label_notes, -1, 110, true,
+                    R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+                    R.string.add_note));
         kind.isList = false;
         kind.actionHeader = new SimpleInflater(R.string.label_notes);
         kind.actionBody = new SimpleInflater(Note.NOTE);
@@ -352,7 +366,8 @@
     protected DataKind addDataKindWebsite(Context context) {
         DataKind kind = addKind(new DataKind(Website.CONTENT_ITEM_TYPE,
                 R.string.websiteLabelsGroup, R.drawable.sym_action_goto_website_holo_light, 120,
-                true));
+                true, R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+                R.string.add_website));
         kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup);
         kind.actionBody = new SimpleInflater(Website.URL);
         kind.defaultValues = new ContentValues();
@@ -374,7 +389,9 @@
         // the android:icon attribute of the SIP-related
         // intent-filters in the Phone app's manifest.
         DataKind kind = addKind(new DataKind(SipAddress.CONTENT_ITEM_TYPE,
-                    R.string.label_sip_address, android.R.drawable.sym_action_call, 130, true));
+                    R.string.label_sip_address, android.R.drawable.sym_action_call, 130, true,
+                    R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+                    -1));
 
         kind.isList = false;
         kind.actionHeader = new SimpleInflater(R.string.label_sip_address);
@@ -389,7 +406,8 @@
     protected DataKind addDataKindGroupMembership(Context context) {
         DataKind kind = getKindForMimetype(GroupMembership.CONTENT_ITEM_TYPE);
         kind = addKind(new DataKind(GroupMembership.CONTENT_ITEM_TYPE,
-                R.string.groupsLabel, android.R.drawable.sym_contact_card, 999, true));
+                R.string.groupsLabel, android.R.drawable.sym_contact_card, 999, true, -1,
+                android.R.style.TextAppearance_Medium, -1));
 
         kind.isList = false;
         kind.fieldList = Lists.newArrayList();
diff --git a/src/com/android/contacts/model/DataKind.java b/src/com/android/contacts/model/DataKind.java
new file mode 100644
index 0000000..52769c3
--- /dev/null
+++ b/src/com/android/contacts/model/DataKind.java
@@ -0,0 +1,106 @@
+package com.android.contacts.model;
+
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType.EditField;
+import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.AccountType.StringInflater;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract.Data;
+
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+/**
+ * Description of a specific data type, usually marked by a unique
+ * {@link Data#MIMETYPE}. Includes details about how to view and edit
+ * {@link Data} rows of this kind, including the possible {@link EditType}
+ * labels and editable {@link EditField}.
+ */
+public class DataKind {
+
+    public static final String PSEUDO_MIME_TYPE_DISPLAY_NAME = "#displayName";
+    public static final String PSEUDO_MIME_TYPE_PHONETIC_NAME = "#phoneticName";
+    public static final String PSEUDO_COLUMN_PHONETIC_NAME = "#phoneticName";
+
+    public String resPackageName;
+    public String mimeType;
+    public int titleRes;
+    public int iconRes;
+    public int iconAltRes;
+    public int weight;
+    public boolean editable;
+
+    /**
+     * If this is true (default), the user can add and remove values.
+     * If false, the editor will always show a single field (which might be empty).
+     */
+    public boolean isList;
+
+    public StringInflater actionHeader;
+    public StringInflater actionAltHeader;
+    public StringInflater actionBody;
+
+    public boolean actionBodySocial = false;
+
+    public String typeColumn;
+
+    /**
+     * Maximum number of values allowed in the list. -1 represents infinity.
+     * If {@link DataKind#isList} is false, this value is ignored.
+     */
+    public int typeOverallMax;
+
+    public List<EditType> typeList;
+    public List<EditField> fieldList;
+
+    public ContentValues defaultValues;
+
+    /** Layout resource id for an editor view to edit this {@link DataKind}. */
+    public final int editorLayoutResourceId;
+
+    /** Text appearance resource id for the value of a field in this {@link DataKind}. */
+    public final int textAppearanceResourceId;
+
+    /**
+     * String resource id for the "add field" footer. This is equal to -1 if it
+     * is not applicable to add a new field to this class (i.e. for a structured
+     * name because a user should only have one structured name).
+     */
+    public final int addNewFieldTextResourceId;
+
+    /**
+     * If this is a date field, this specifies the format of the date when saving. The
+     * date includes year, month and day. If this is not a date field or the date field is not
+     * editable, this value should be ignored.
+     */
+    public SimpleDateFormat dateFormatWithoutYear;
+
+    /**
+     * If this is a date field, this specifies the format of the date when saving. The
+     * date includes month and day. If this is not a date field, the field is not editable or
+     * dates without year are not supported, this value should be ignored.
+     */
+    public SimpleDateFormat dateFormatWithYear;
+
+    public DataKind() {
+        editorLayoutResourceId = R.layout.text_fields_editor_view;
+        textAppearanceResourceId = android.R.style.TextAppearance_Medium;
+        addNewFieldTextResourceId = -1;
+    }
+
+    public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable,
+            int editorLayoutResourceId, int textAppearanceResourceId,
+            int addNewFieldTextResourceId) {
+        this.mimeType = mimeType;
+        this.titleRes = titleRes;
+        this.iconRes = iconRes;
+        this.weight = weight;
+        this.editable = editable;
+        this.isList = true;
+        this.typeOverallMax = -1;
+        this.editorLayoutResourceId = editorLayoutResourceId;
+        this.textAppearanceResourceId = textAppearanceResourceId;
+        this.addNewFieldTextResourceId = addNewFieldTextResourceId;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java
index c5d0d9e..fe084f4 100644
--- a/src/com/android/contacts/model/EntityDelta.java
+++ b/src/com/android/contacts/model/EntityDelta.java
@@ -330,7 +330,7 @@
     public String toString() {
         final StringBuilder builder = new StringBuilder();
         builder.append("\n(");
-        builder.append(mValues.toString());
+        builder.append(mValues != null ? mValues.toString() : "null");
         builder.append(") = {");
         for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
             for (ValuesDelta child : mimeEntries) {
diff --git a/src/com/android/contacts/model/EntityModifier.java b/src/com/android/contacts/model/EntityModifier.java
index 1e014ef..9d527df 100644
--- a/src/com/android/contacts/model/EntityModifier.java
+++ b/src/com/android/contacts/model/EntityModifier.java
@@ -17,11 +17,14 @@
 package com.android.contacts.model;
 
 import com.android.contacts.ContactsUtils;
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.editor.EventFieldEditorView;
+import com.android.contacts.util.NameConverter;
+import com.android.contacts.editor.PhoneticNameEditorView;
 import com.android.contacts.model.AccountType.EditField;
 import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.AccountType.EventEditType;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.google.android.collect.Lists;
+import com.android.contacts.util.DateUtils;
 
 import android.content.ContentValues;
 import android.content.Context;
@@ -31,14 +34,19 @@
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
 import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
 import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
 import android.provider.ContactsContract.CommonDataKinds.Note;
 import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 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;
+import android.provider.ContactsContract.CommonDataKinds.Website;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Intents;
 import android.provider.ContactsContract.Intents.Insert;
@@ -47,9 +55,18 @@
 import android.util.Log;
 import android.util.SparseIntArray;
 
+import java.text.ParsePosition;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Helper methods for modifying an {@link EntityDelta}, such as inserting
@@ -137,7 +154,7 @@
      */
     private static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind,
             EditType forceInclude, boolean includeSecondary, SparseIntArray typeCount) {
-        final ArrayList<EditType> validTypes = Lists.newArrayList();
+        final ArrayList<EditType> validTypes = new ArrayList<EditType>();
 
         // Bail early if no types provided
         if (!hasEditTypes(kind)) return validTypes;
@@ -878,4 +895,503 @@
 
         return child;
     }
+
+    /**
+     * Generic mime types with type support (e.g. TYPE_HOME).
+     * Here, "type support" means if the data kind has CommonColumns#TYPE or not. Data kinds which
+     * have their own migrate methods aren't listed here.
+     */
+    private static final Set<String> sGenericMimeTypesWithTypeSupport = new HashSet<String>(
+            Arrays.asList(Phone.CONTENT_ITEM_TYPE,
+                    Email.CONTENT_ITEM_TYPE,
+                    Im.CONTENT_ITEM_TYPE,
+                    Nickname.CONTENT_ITEM_TYPE,
+                    Website.CONTENT_ITEM_TYPE,
+                    Relation.CONTENT_ITEM_TYPE,
+                    SipAddress.CONTENT_ITEM_TYPE));
+    private static final Set<String> sGenericMimeTypesWithoutTypeSupport = new HashSet<String>(
+            Arrays.asList(Organization.CONTENT_ITEM_TYPE,
+                    Note.CONTENT_ITEM_TYPE,
+                    Photo.CONTENT_ITEM_TYPE,
+                    GroupMembership.CONTENT_ITEM_TYPE));
+    // CommonColumns.TYPE cannot be accessed as it is protected interface, so use
+    // Phone.TYPE instead.
+    private static final String COLUMN_FOR_TYPE  = Phone.TYPE;
+    private static final String COLUMN_FOR_LABEL  = Phone.LABEL;
+    private static final int TYPE_CUSTOM = Phone.TYPE_CUSTOM;
+
+    /**
+     * Migrates old EntityDelta to newly created one with a new restriction supplied from
+     * newAccountType.
+     *
+     * This is only for account switch during account creation (which must be insert operation).
+     */
+    public static void migrateStateForNewContact(Context context,
+            EntityDelta oldState, EntityDelta newState,
+            AccountType oldAccountType, AccountType newAccountType) {
+        if (newAccountType == oldAccountType) {
+            // Just copying all data in oldState isn't enough, but we can still rely on a lot of
+            // shortcuts.
+            for (DataKind kind : newAccountType.getSortedDataKinds()) {
+                final String mimeType = kind.mimeType;
+                // The fields with short/long form capability must be treated properly.
+                if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    migrateStructuredName(context, oldState, newState, kind);
+                } else {
+                    List<ValuesDelta> entryList = oldState.getMimeEntries(mimeType);
+                    if (entryList != null && !entryList.isEmpty()) {
+                        for (ValuesDelta entry : entryList) {
+                            ContentValues values = entry.getAfter();
+                            if (values != null) {
+                                newState.addEntry(ValuesDelta.fromAfter(values));
+                            }
+                        }
+                    }
+                }
+            }
+        } else {
+            // Migrate data supported by the new account type.
+            // All the other data inside oldState are silently dropped.
+            for (DataKind kind : newAccountType.getSortedDataKinds()) {
+                final String mimeType = kind.mimeType;
+                final int fieldCount = kind.fieldList.size();
+                final Set<String> allowedColumns = new HashSet<String>();
+                if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
+                        || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
+                    // Ignore pseude data.
+                    continue;
+                } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    migrateStructuredName(context, oldState, newState, kind);
+                } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    migratePostal(oldState, newState, kind);
+                } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    migrateEvent(oldState, newState, kind, null /* default Year */);
+                } else if (sGenericMimeTypesWithoutTypeSupport.contains(mimeType)) {
+                    migrateGenericWithoutTypeColumn(oldState, newState, kind);
+                } else {
+                    migrateGenericWithTypeColumn(oldState, newState, kind);
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks {@link DataKind#isList} and {@link DataKind#typeOverallMax}, and restricts
+     * the number of entries (ValuesDelta) inside newState.
+     */
+    private static ArrayList<ValuesDelta> ensureEntryMaxSize(EntityDelta newState, DataKind kind,
+            ArrayList<ValuesDelta> mimeEntries) {
+        if (mimeEntries == null) {
+            return null;
+        }
+
+        int typeOverallMax = kind.typeOverallMax;
+        if (!kind.isList) {
+            typeOverallMax = 1;
+        }
+        if (typeOverallMax >= 0 && (mimeEntries.size() > typeOverallMax)) {
+            ArrayList<ValuesDelta> newMimeEntries = new ArrayList<ValuesDelta>(typeOverallMax);
+            for (int i = 0; i < typeOverallMax; i++) {
+                newMimeEntries.add(mimeEntries.get(i));
+            }
+            mimeEntries = newMimeEntries;
+        }
+        return mimeEntries;
+    }
+
+    /** @hide Public only for testing. */
+    public static void migrateStructuredName(
+            Context context, EntityDelta oldState, EntityDelta newState, DataKind newDataKind) {
+        final ContentValues values =
+                oldState.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE).getAfter();
+        if (values == null) {
+            return;
+        }
+
+        boolean supportDisplayName = false;
+        boolean supportPhoneticFullName = false;
+        boolean supportPhoneticFamilyName = false;
+        boolean supportPhoneticMiddleName = false;
+        boolean supportPhoneticGivenName = false;
+        for (EditField editField : newDataKind.fieldList) {
+            if (StructuredName.DISPLAY_NAME.equals(editField.column)) {
+                supportDisplayName = true;
+            }
+            if (DataKind.PSEUDO_COLUMN_PHONETIC_NAME.equals(editField.column)) {
+                supportPhoneticFullName = true;
+            }
+            if (StructuredName.PHONETIC_FAMILY_NAME.equals(editField.column)) {
+                supportPhoneticFamilyName = true;
+            }
+            if (StructuredName.PHONETIC_MIDDLE_NAME.equals(editField.column)) {
+                supportPhoneticMiddleName = true;
+            }
+            if (StructuredName.PHONETIC_GIVEN_NAME.equals(editField.column)) {
+                supportPhoneticGivenName = true;
+            }
+        }
+
+        // DISPLAY_NAME <-> PREFIX, GIVEN_NAME, MIDDLE_NAME, FAMILY_NAME, SUFFIX
+        final String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
+        if (!TextUtils.isEmpty(displayName)) {
+            if (!supportDisplayName) {
+                // Old data has a display name, while the new account doesn't allow it.
+                NameConverter.displayNameToStructuredName(context, displayName, values);
+
+                // We don't want to migrate unseen data which may confuse users after the creation.
+                values.remove(StructuredName.DISPLAY_NAME);
+            }
+        } else {
+            if (supportDisplayName) {
+                // Old data does not have display name, while the new account requires it.
+                values.put(StructuredName.DISPLAY_NAME,
+                        NameConverter.structuredNameToDisplayName(context, values));
+                for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
+                    values.remove(field);
+                }
+            }
+        }
+
+        // Phonetic (full) name <-> PHONETIC_FAMILY_NAME, PHONETIC_MIDDLE_NAME, PHONETIC_GIVEN_NAME
+        final String phoneticFullName = values.getAsString(DataKind.PSEUDO_COLUMN_PHONETIC_NAME);
+        if (!TextUtils.isEmpty(phoneticFullName)) {
+            if (!supportPhoneticFullName) {
+                // Old data has a phonetic (full) name, while the new account doesn't allow it.
+                final ContentValues tmpValues =
+                        PhoneticNameEditorView.parsePhoneticName(phoneticFullName, null);
+                values.remove(DataKind.PSEUDO_COLUMN_PHONETIC_NAME);
+                if (supportPhoneticFamilyName) {
+                    values.put(StructuredName.PHONETIC_FAMILY_NAME,
+                            tmpValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME));
+                } else {
+                    values.remove(StructuredName.PHONETIC_FAMILY_NAME);
+                }
+                if (supportPhoneticMiddleName) {
+                    values.put(StructuredName.PHONETIC_MIDDLE_NAME,
+                            tmpValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME));
+                } else {
+                    values.remove(StructuredName.PHONETIC_MIDDLE_NAME);
+                }
+                if (supportPhoneticGivenName) {
+                    values.put(StructuredName.PHONETIC_GIVEN_NAME,
+                            tmpValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
+                } else {
+                    values.remove(StructuredName.PHONETIC_GIVEN_NAME);
+                }
+            }
+        } else {
+            if (supportPhoneticFullName) {
+                // Old data does not have a phonetic (full) name, while the new account requires it.
+                values.put(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
+                        PhoneticNameEditorView.buildPhoneticName(
+                                values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
+                                values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
+                                values.getAsString(StructuredName.PHONETIC_GIVEN_NAME)));
+            }
+            if (!supportPhoneticFamilyName) {
+                values.remove(StructuredName.PHONETIC_FAMILY_NAME);
+            }
+            if (!supportPhoneticMiddleName) {
+                values.remove(StructuredName.PHONETIC_MIDDLE_NAME);
+            }
+            if (!supportPhoneticGivenName) {
+                values.remove(StructuredName.PHONETIC_GIVEN_NAME);
+            }
+        }
+
+        newState.addEntry(ValuesDelta.fromAfter(values));
+    }
+
+    /** @hide Public only for testing. */
+    public static void migratePostal(EntityDelta oldState, EntityDelta newState,
+            DataKind newDataKind) {
+        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
+                oldState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE));
+        if (mimeEntries == null || mimeEntries.isEmpty()) {
+            return;
+        }
+
+        boolean supportFormattedAddress = false;
+        boolean supportStreet = false;
+        final String firstColumn = newDataKind.fieldList.get(0).column;
+        for (EditField editField : newDataKind.fieldList) {
+            if (StructuredPostal.FORMATTED_ADDRESS.equals(editField.column)) {
+                supportFormattedAddress = true;
+            }
+            if (StructuredPostal.STREET.equals(editField.column)) {
+                supportStreet = true;
+            }
+        }
+
+        final Set<Integer> supportedTypes = new HashSet<Integer>();
+        if (newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) {
+            for (EditType editType : newDataKind.typeList) {
+                supportedTypes.add(editType.rawValue);
+            }
+        }
+
+        for (ValuesDelta entry : mimeEntries) {
+            final ContentValues values = entry.getAfter();
+            if (values == null) {
+                continue;
+            }
+            final Integer oldType = values.getAsInteger(StructuredPostal.TYPE);
+            if (!supportedTypes.contains(oldType)) {
+                int defaultType;
+                if (newDataKind.defaultValues != null) {
+                    defaultType = newDataKind.defaultValues.getAsInteger(StructuredPostal.TYPE);
+                } else {
+                    defaultType = newDataKind.typeList.get(0).rawValue;
+                }
+                values.put(StructuredPostal.TYPE, defaultType);
+                if (oldType != null && oldType == StructuredPostal.TYPE_CUSTOM) {
+                    values.remove(StructuredPostal.LABEL);
+                }
+            }
+
+            final String formattedAddress = values.getAsString(StructuredPostal.FORMATTED_ADDRESS);
+            if (!TextUtils.isEmpty(formattedAddress)) {
+                if (!supportFormattedAddress) {
+                    // Old data has a formatted address, while the new account doesn't allow it.
+                    values.remove(StructuredPostal.FORMATTED_ADDRESS);
+
+                    // Unlike StructuredName we don't have logic to split it, so first
+                    // try to use street field and. If the new account doesn't have one,
+                    // then select first one anyway.
+                    if (supportStreet) {
+                        values.put(StructuredPostal.STREET, formattedAddress);
+                    } else {
+                        values.put(firstColumn, formattedAddress);
+                    }
+                }
+            } else {
+                if (supportFormattedAddress) {
+                    // Old data does not have formatted address, while the new account requires it.
+                    // Unlike StructuredName we don't have logic to join multiple address values.
+                    // Use poor join heuristics for now.
+                    String[] structuredData;
+                    final boolean useJapaneseOrder =
+                            Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage());
+                    if (useJapaneseOrder) {
+                        structuredData = new String[] {
+                                values.getAsString(StructuredPostal.COUNTRY),
+                                values.getAsString(StructuredPostal.POSTCODE),
+                                values.getAsString(StructuredPostal.REGION),
+                                values.getAsString(StructuredPostal.CITY),
+                                values.getAsString(StructuredPostal.NEIGHBORHOOD),
+                                values.getAsString(StructuredPostal.STREET),
+                                values.getAsString(StructuredPostal.POBOX) };
+                    } else {
+                        structuredData = new String[] {
+                                values.getAsString(StructuredPostal.POBOX),
+                                values.getAsString(StructuredPostal.STREET),
+                                values.getAsString(StructuredPostal.NEIGHBORHOOD),
+                                values.getAsString(StructuredPostal.CITY),
+                                values.getAsString(StructuredPostal.REGION),
+                                values.getAsString(StructuredPostal.POSTCODE),
+                                values.getAsString(StructuredPostal.COUNTRY) };
+                    }
+                    final StringBuilder builder = new StringBuilder();
+                    for (String elem : structuredData) {
+                        if (!TextUtils.isEmpty(elem)) {
+                            builder.append(elem + "\n");
+                        }
+                    }
+                    values.put(StructuredPostal.FORMATTED_ADDRESS, builder.toString());
+
+                    values.remove(StructuredPostal.POBOX);
+                    values.remove(StructuredPostal.STREET);
+                    values.remove(StructuredPostal.NEIGHBORHOOD);
+                    values.remove(StructuredPostal.CITY);
+                    values.remove(StructuredPostal.REGION);
+                    values.remove(StructuredPostal.POSTCODE);
+                    values.remove(StructuredPostal.COUNTRY);
+                }
+            }
+
+            newState.addEntry(ValuesDelta.fromAfter(values));
+        }
+    }
+
+    /** @hide Public only for testing. */
+    public static void migrateEvent(EntityDelta oldState, EntityDelta newState,
+            DataKind newDataKind, Integer defaultYear) {
+        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
+                oldState.getMimeEntries(Event.CONTENT_ITEM_TYPE));
+        if (mimeEntries == null || mimeEntries.isEmpty()) {
+            return;
+        }
+
+        final Map<Integer, EventEditType> allowedTypes = new HashMap<Integer, EventEditType>();
+        for (EditType editType : newDataKind.typeList) {
+            allowedTypes.put(editType.rawValue, (EventEditType) editType);
+        }
+        for (ValuesDelta entry : mimeEntries) {
+            final ContentValues values = entry.getAfter();
+            if (values == null) {
+                continue;
+            }
+            final String dateString = values.getAsString(Event.START_DATE);
+            final Integer type = values.getAsInteger(Event.TYPE);
+            if (type != null && allowedTypes.containsKey(type) && !TextUtils.isEmpty(dateString)) {
+                EventEditType suitableType = allowedTypes.get(type);
+
+                final ParsePosition position = new ParsePosition(0);
+                boolean yearOptional = false;
+                Date date = DateUtils.DATE_AND_TIME_FORMAT.parse(dateString, position);
+                if (date == null) {
+                    yearOptional = true;
+                    date = DateUtils.NO_YEAR_DATE_FORMAT.parse(dateString, position);
+                }
+                if (date != null) {
+                    if (yearOptional && !suitableType.isYearOptional()) {
+                        // The new EditType doesn't allow optional year. Supply default.
+                        final Calendar calendar = Calendar.getInstance(DateUtils.UTC_TIMEZONE,
+                                Locale.US);
+                        if (defaultYear == null) {
+                            defaultYear = calendar.get(Calendar.YEAR);
+                        }
+                        calendar.setTime(date);
+                        final int month = calendar.get(Calendar.MONTH);
+                        final int day = calendar.get(Calendar.DAY_OF_MONTH);
+                        // Exchange requires 8:00 for birthdays
+                        calendar.set(defaultYear, month, day,
+                                EventFieldEditorView.getDefaultHourForBirthday(), 0, 0);
+                        values.put(Event.START_DATE,
+                                DateUtils.FULL_DATE_FORMAT.format(calendar.getTime()));
+                    }
+                }
+                newState.addEntry(ValuesDelta.fromAfter(values));
+            } else {
+                // Just drop it.
+            }
+        }
+    }
+
+    /** @hide Public only for testing. */
+    public static void migrateGenericWithoutTypeColumn(
+            EntityDelta oldState, EntityDelta newState, DataKind newDataKind) {
+        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
+                oldState.getMimeEntries(newDataKind.mimeType));
+        if (mimeEntries == null || mimeEntries.isEmpty()) {
+            return;
+        }
+
+        for (ValuesDelta entry : mimeEntries) {
+            ContentValues values = entry.getAfter();
+            if (values != null) {
+                newState.addEntry(ValuesDelta.fromAfter(values));
+            }
+        }
+    }
+
+    /** @hide Public only for testing. */
+    public static void migrateGenericWithTypeColumn(
+            EntityDelta oldState, EntityDelta newState, DataKind newDataKind) {
+        if (!sGenericMimeTypesWithTypeSupport.contains(newDataKind.mimeType)) {
+            throw new RuntimeException("not supported: " + newDataKind.mimeType);
+        }
+
+        final ArrayList<ValuesDelta> mimeEntries = oldState.getMimeEntries(newDataKind.mimeType);
+        if (mimeEntries == null || mimeEntries.isEmpty()) {
+            return;
+        }
+
+        // Note that type specified with the old account may be invalid with the new account, while
+        // we want to preserve its data as much as possible. e.g. if a user typed a phone number
+        // with a type which is valid with an old account but not with a new account, the user
+        // probably wants to have the number with default type, rather than seeing complete data
+        // loss.
+        //
+        // Specifically, this method works as follows:
+        // 1. detect defaultType
+        // 2. prepare constants & variables for iteration
+        // 3. iterate over mimeEntries:
+        // 3.1 stop iteration if total number of mimeEntries reached typeOverallMax specified in
+        //     DataKind
+        // 3.2 replace unallowed types with defaultType
+        // 3.3 check if the number of entries is below specificMax specified in AccountType
+
+        // Here, defaultType can be supplied in two ways
+        // - via kind.defaultValues
+        // - via kind.typeList.get(0).rawValue
+        Integer defaultType = null;
+        if (newDataKind.defaultValues != null) {
+            defaultType = newDataKind.defaultValues.getAsInteger(COLUMN_FOR_TYPE);
+        }
+        final Set<Integer> allowedTypes = new HashSet<Integer>();
+        // key: type, value: the number of entries allowed for the type (specificMax)
+        final Map<Integer, Integer> typeSpecificMaxMap = new HashMap<Integer, Integer>();
+        if (defaultType != null) {
+            allowedTypes.add(defaultType);
+            typeSpecificMaxMap.put(defaultType, -1);
+        }
+        // Note: typeList may be used in different purposes when defaultValues are specified.
+        // Especially in IM, typeList contains available protocols (e.g. PROTOCOL_GOOGLE_TALK)
+        // instead of "types" which we want to treate here (e.g. TYPE_HOME). So we don't add
+        // anything other than defaultType into allowedTypes and typeSpecificMapMax.
+        if (!Im.CONTENT_ITEM_TYPE.equals(newDataKind.mimeType) &&
+                newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) {
+            for (EditType editType : newDataKind.typeList) {
+                allowedTypes.add(editType.rawValue);
+                typeSpecificMaxMap.put(editType.rawValue, editType.specificMax);
+            }
+            if (defaultType == null) {
+                defaultType = newDataKind.typeList.get(0).rawValue;
+            }
+        }
+
+        if (defaultType == null) {
+            Log.w(TAG, "Default type isn't available for mimetype " + newDataKind.mimeType);
+        }
+
+        final int typeOverallMax = newDataKind.isList ? newDataKind.typeOverallMax : 1;
+
+        // key: type, value: the number of current entries.
+        final Map<Integer, Integer> currentEntryCount = new HashMap<Integer, Integer>();
+        int totalCount = 0;
+
+        for (ValuesDelta entry : mimeEntries) {
+            if (typeOverallMax != -1 && totalCount >= typeOverallMax) {
+                break;
+            }
+
+            final ContentValues values = entry.getAfter();
+            if (values == null) {
+                continue;
+            }
+
+            final Integer oldType = entry.getAsInteger(COLUMN_FOR_TYPE);
+            final Integer typeForNewAccount;
+            if (!allowedTypes.contains(oldType)) {
+                // The new account doesn't support the type.
+                if (defaultType != null) {
+                    typeForNewAccount = defaultType.intValue();
+                    values.put(COLUMN_FOR_TYPE, defaultType.intValue());
+                    if (oldType != null && oldType == TYPE_CUSTOM) {
+                        values.remove(COLUMN_FOR_LABEL);
+                    }
+                } else {
+                    typeForNewAccount = null;
+                    values.remove(COLUMN_FOR_TYPE);
+                }
+            } else {
+                typeForNewAccount = oldType;
+            }
+            if (typeForNewAccount != null) {
+                final int specificMax = (typeSpecificMaxMap.containsKey(typeForNewAccount) ?
+                        typeSpecificMaxMap.get(typeForNewAccount) : 0);
+                if (specificMax >= 0) {
+                    final int currentCount = (currentEntryCount.get(typeForNewAccount) != null ?
+                            currentEntryCount.get(typeForNewAccount) : 0);
+                    if (currentCount >= specificMax) {
+                        continue;
+                    }
+                    currentEntryCount.put(typeForNewAccount, currentCount + 1);
+                }
+            }
+            newState.addEntry(ValuesDelta.fromAfter(values));
+            totalCount++;
+        }
+    }
 }
diff --git a/src/com/android/contacts/model/ExchangeAccountType.java b/src/com/android/contacts/model/ExchangeAccountType.java
index 2b1a520..22a2ba2 100644
--- a/src/com/android/contacts/model/ExchangeAccountType.java
+++ b/src/com/android/contacts/model/ExchangeAccountType.java
@@ -17,7 +17,6 @@
 package com.android.contacts.model;
 
 import com.android.contacts.R;
-import com.android.contacts.editor.EventFieldEditorView;
 import com.android.contacts.util.DateUtils;
 import com.google.android.collect.Lists;
 
@@ -65,7 +64,8 @@
     @Override
     protected DataKind addDataKindStructuredName(Context context) {
         DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
-                R.string.nameLabelsGroup, -1, -1, true));
+                R.string.nameLabelsGroup, -1, -1, true,
+                R.layout.structured_name_editor_view, android.R.style.TextAppearance_Large, -1));
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
         kind.actionBody = new SimpleInflater(Nickname.NAME);
 
@@ -94,7 +94,8 @@
     @Override
     protected DataKind addDataKindDisplayName(Context context) {
         DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
-                R.string.nameLabelsGroup, -1, -1, true));
+                R.string.nameLabelsGroup, -1, -1, true,
+                R.layout.text_fields_editor_view, android.R.style.TextAppearance_Large, -1));
 
         boolean displayOrderPrimary =
                 context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
@@ -127,7 +128,8 @@
     @Override
     protected DataKind addDataKindPhoneticName(Context context) {
         DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
-                R.string.name_phonetic, -1, -1, true));
+                R.string.name_phonetic, -1, -1, true,
+                R.layout.phonetic_name_editor_view, android.R.style.TextAppearance_Medium, -1));
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
         kind.actionBody = new SimpleInflater(Nickname.NAME);
 
@@ -292,7 +294,9 @@
 
     protected DataKind addDataKindEvent(Context context) {
         DataKind kind = addKind(
-                new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, -1, 150, true));
+                new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, -1, 150, true,
+                R.layout.event_field_editor_view, android.R.style.TextAppearance_Medium,
+                R.string.add_event));
         kind.actionHeader = new EventActionInflater();
         kind.actionBody = new SimpleInflater(Event.START_DATE);
 
@@ -303,7 +307,6 @@
         kind.typeList.add(buildEventType(Event.TYPE_BIRTHDAY, false).setSpecificMax(1));
 
         kind.dateFormatWithYear = DateUtils.DATE_AND_TIME_FORMAT;
-        kind.editorClass = EventFieldEditorView.class;
 
         kind.fieldList = Lists.newArrayList();
         kind.fieldList.add(new EditField(Event.DATA, R.string.eventLabelsGroup, FLAGS_EVENT));
diff --git a/src/com/android/contacts/model/GoogleAccountType.java b/src/com/android/contacts/model/GoogleAccountType.java
index af25691..bd445b1 100644
--- a/src/com/android/contacts/model/GoogleAccountType.java
+++ b/src/com/android/contacts/model/GoogleAccountType.java
@@ -17,7 +17,6 @@
 package com.android.contacts.model;
 
 import com.android.contacts.R;
-import com.android.contacts.editor.EventFieldEditorView;
 import com.android.contacts.util.DateUtils;
 import com.google.android.collect.Lists;
 
@@ -63,9 +62,10 @@
 
         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_WORK));
+        kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
+        kind.typeList.add(buildPhoneType(Phone.TYPE_MAIN));
         kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
         kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
         kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true));
@@ -99,7 +99,9 @@
 
     private DataKind addDataKindRelation(Context context) {
         DataKind kind = addKind(new DataKind(Relation.CONTENT_ITEM_TYPE,
-                R.string.relationLabelsGroup, -1, 160, true));
+                R.string.relationLabelsGroup, -1, 160, true,
+                R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+                R.string.add_relationship));
         kind.actionHeader = new RelationActionInflater();
         kind.actionBody = new SimpleInflater(Relation.NAME);
 
@@ -134,10 +136,11 @@
 
     private DataKind addDataKindEvent(Context context) {
         DataKind kind = addKind(new DataKind(Event.CONTENT_ITEM_TYPE,
-                    R.string.eventLabelsGroup, -1, 150, true));
+                    R.string.eventLabelsGroup, -1, 150, true,
+                    R.layout.event_field_editor_view, android.R.style.TextAppearance_Medium,
+                    R.string.add_event));
         kind.actionHeader = new EventActionInflater();
         kind.actionBody = new SimpleInflater(Event.START_DATE);
-        kind.editorClass = EventFieldEditorView.class;
 
         kind.typeColumn = Event.TYPE;
         kind.typeList = Lists.newArrayList();
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
index bd59856..787d482 100644
--- a/src/com/android/contacts/quickcontact/DataAction.java
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -2,7 +2,7 @@
 
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.util.Constants;
 import com.android.contacts.util.PhoneCapabilityTester;
 
diff --git a/src/com/android/contacts/quickcontact/FloatingChildLayout.java b/src/com/android/contacts/quickcontact/FloatingChildLayout.java
new file mode 100644
index 0000000..ddba609
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/FloatingChildLayout.java
@@ -0,0 +1,231 @@
+/*
+ * 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.quickcontact;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+import android.widget.PopupWindow;
+
+/**
+ * Layout containing single child {@link View} which it attempts to center
+ * around {@link #setChildTargetScreen(Rect)}.
+ * <p>
+ * Updates drawable state to be {@link android.R.attr#state_first} when child is
+ * above target, and {@link android.R.attr#state_last} when child is below
+ * target. Also updates {@link Drawable#setLevel(int)} on child
+ * {@link View#getBackground()} to reflect horizontal center of target.
+ * <p>
+ * The reason for this approach is because target {@link Rect} is in screen
+ * coordinates disregarding decor insets; otherwise something like
+ * {@link PopupWindow} might work better.
+ */
+public class FloatingChildLayout extends FrameLayout {
+    private static final String TAG = "FloatingChild";
+
+    public FloatingChildLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    private View mChild;
+
+    private Rect mTargetScreen = new Rect();
+
+    private int mCalloutState = 0;
+    private int mCalloutLeft;
+
+    @Override
+    protected void onFinishInflate() {
+        mChild = findViewById(android.R.id.content);
+        mChild.setDuplicateParentStateEnabled(true);
+    }
+
+    public View getChild() {
+        return mChild;
+    }
+
+    /**
+     * Set {@link Rect} in screen coordinates that {@link #getChild()} should be
+     * centered around.
+     */
+    public void setChildTargetScreen(Rect targetScreen) {
+        mTargetScreen = targetScreen;
+        requestLayout();
+    }
+
+    /**
+     * Return {@link #mTargetScreen} in local window coordinates, taking any
+     * decor insets into account.
+     */
+    private Rect getTargetInWindow() {
+        final Rect windowScreen = new Rect();
+        getWindowVisibleDisplayFrame(windowScreen);
+
+        final Rect target = new Rect(mTargetScreen);
+        target.offset(-windowScreen.left, -windowScreen.top);
+        return target;
+    }
+
+    private void updateCallout(int calloutState, int calloutLeft) {
+        if (mCalloutState != calloutState) {
+            mCalloutState = calloutState;
+            mChild.refreshDrawableState();
+        }
+
+        final Drawable background = mChild.getBackground();
+        if (background != null && mCalloutLeft != calloutLeft) {
+            mCalloutLeft = calloutLeft;
+            background.setLevel(calloutLeft);
+        }
+    }
+
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+        mergeDrawableStates(drawableState, new int[] { mCalloutState });
+        return drawableState;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+
+        final View child = mChild;
+        final Rect target = getTargetInWindow();
+
+        final int childWidth = child.getMeasuredWidth();
+        final int childHeight = child.getMeasuredHeight();
+
+        // default is no callout, left-aligned, and vertically centered
+        int calloutState = 0;
+        int childLeft = target.left;
+        int childTop = target.centerY() - (childHeight / 2);
+
+        // when target is wide, horizontally center instead of left-align
+        if (target.width() > childWidth / 2) {
+            childLeft = target.centerX() - (childWidth / 2);
+        }
+
+        final int areaAboveTarget = target.top;
+        final int areaBelowTarget = getHeight() - target.bottom;
+
+        if (areaAboveTarget >= childHeight) {
+            // enough room above target, place above and callout down
+            calloutState = android.R.attr.state_first;
+            childTop = target.top - childHeight;
+
+        } else if (areaBelowTarget >= childHeight) {
+            // enough room below target, place below and callout up
+            calloutState = android.R.attr.state_last;
+            childTop = target.bottom;
+        }
+
+        // when child is outside bounds, nudge back inside
+        childLeft = clampDimension(childLeft, childWidth, getWidth());
+        childTop = clampDimension(childTop, childHeight, getHeight());
+
+        final int calloutLeft = target.centerX() - childLeft;
+        updateCallout(calloutState, calloutLeft);
+        layoutChild(child, childLeft, childTop);
+
+    }
+
+    private static int clampDimension(int value, int size, int max) {
+        // when larger than bounds, just center
+        if (size > max) {
+            return (max - size) / 2;
+        }
+
+        // clamp to lower bound
+        value = Math.max(value, 0);
+        // clamp to higher bound
+        value = Math.min(value, max - size);
+
+        return value;
+    }
+
+    private static void layoutChild(View child, int left, int top) {
+        child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
+    }
+
+    /**
+     * Begin animating {@link #getChild()} visible.
+     */
+    public void showChild() {
+        final boolean calloutAbove = mCalloutState == android.R.attr.state_first;
+        final Animation anim = AnimationUtils.loadAnimation(getContext(),
+                calloutAbove ? R.anim.quickcontact_above_enter : R.anim.quickcontact_below_enter);
+        mChild.startAnimation(anim);
+        mChild.setVisibility(View.VISIBLE);
+    }
+
+    /**
+     * Begin animating {@link #getChild()} invisible.
+     */
+    public void hideChild(final Runnable onAnimationEnd) {
+        final boolean calloutAbove = mCalloutState == android.R.attr.state_first;
+        final Animation anim = AnimationUtils.loadAnimation(getContext(),
+                calloutAbove ? R.anim.quickcontact_above_exit : R.anim.quickcontact_below_exit);
+
+        if (onAnimationEnd != null) {
+            anim.setAnimationListener(new AnimationListener() {
+                /** {@inheritDoc} */
+                public void onAnimationStart(Animation animation) {
+                    // ignored
+                }
+
+                /** {@inheritDoc} */
+                public void onAnimationRepeat(Animation animation) {
+                    // ignored
+                }
+
+                /** {@inheritDoc} */
+                public void onAnimationEnd(Animation animation) {
+                    onAnimationEnd.run();
+                }
+            });
+        }
+
+        mChild.startAnimation(anim);
+        mChild.setVisibility(View.INVISIBLE);
+    }
+
+    private View.OnTouchListener mOutsideTouchListener;
+
+    public void setOnOutsideTouchListener(View.OnTouchListener listener) {
+        mOutsideTouchListener = listener;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        // at this point, touch wasn't handled by child view; assume outside
+        if (mOutsideTouchListener != null) {
+            return mOutsideTouchListener.onTouch(this, event);
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
deleted file mode 100644
index 5032386..0000000
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.contacts.quickcontact;
-
-import com.android.contacts.ContactsActivity;
-
-import android.content.ContentUris;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract.QuickContact;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-
-/**
- * Stub translucent activity that just shows {@link QuickContactWindow} floating
- * above the caller. This temporary hack should eventually be replaced with
- * direct framework support.
- */
-public final class QuickContactActivity extends ContactsActivity
-        implements QuickContactWindow.OnDismissListener {
-    private static final String TAG = "QuickContactActivity";
-
-    static final boolean LOGV = false;
-    static final boolean FORCE_CREATE = true;
-
-    private QuickContactWindow mQuickContact;
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        if (LOGV) Log.d(TAG, "onCreate");
-
-        this.onNewIntent(getIntent());
-    }
-
-    @Override
-    public void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        if (LOGV) Log.d(TAG, "onNewIntent");
-
-        if (QuickContactWindow.TRACE_LAUNCH) {
-            android.os.Debug.startMethodTracing(QuickContactWindow.TRACE_TAG);
-        }
-
-        if (mQuickContact == null || FORCE_CREATE) {
-            if (LOGV) Log.d(TAG, "Preparing window");
-            mQuickContact = new QuickContactWindow(this, this);
-        }
-
-        // Use our local window token for now
-        Uri lookupUri = intent.getData();
-        // Check to see whether it comes from the old version.
-        if (android.provider.Contacts.AUTHORITY.equals(lookupUri.getAuthority())) {
-            final long rawContactId = ContentUris.parseId(lookupUri);
-            lookupUri = RawContacts.getContactLookupUri(getContentResolver(),
-                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
-        }
-        final Bundle extras = intent.getExtras();
-
-        // Read requested parameters for displaying
-        final Rect target = intent.getSourceBounds();
-        final int mode = extras.getInt(QuickContact.EXTRA_MODE, QuickContact.MODE_MEDIUM);
-        final String[] excludeMimes = extras.getStringArray(QuickContact.EXTRA_EXCLUDE_MIMES);
-
-        mQuickContact.show(lookupUri, target, mode, excludeMimes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onBackPressed() {
-        if (LOGV) Log.w(TAG, "Unexpected back captured by stub activity");
-        finish();
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        if (LOGV) Log.d(TAG, "onPause");
-
-        // Dismiss any dialog when pausing
-        mQuickContact.dismiss();
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        if (LOGV) Log.d(TAG, "onDestroy");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onDismiss(QuickContactWindow dialog) {
-        if (LOGV) Log.d(TAG, "onDismiss");
-
-        if (isTaskRoot() && !FORCE_CREATE) {
-            // Instead of stopping, simply push this to the back of the stack.
-            // This is only done when running at the top of the stack;
-            // otherwise, we have been launched by someone else so need to
-            // allow the user to go back to the caller.
-            moveTaskToBack(false);
-        } else {
-            finish();
-        }
-    }
-}
diff --git a/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java b/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java
index 9118480..15311f9 100644
--- a/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java
+++ b/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java
@@ -26,37 +26,43 @@
 import android.graphics.drawable.Drawable;
 
 /**
- * Drawable that draws three pictures for the QuickContact-Background. ColorFilter is ignored
+ * Background {@link Drawable} for {@link QuickContactWindow} that draws arrow
+ * centered around requested position.
  */
 public class QuickContactBackgroundDrawable extends Drawable {
     private Drawable mLeftDrawable;
     private Drawable mMiddleDrawable;
     private Drawable mRightDrawable;
-    private int mRequestedX = Integer.MIN_VALUE;
-    private boolean mBoundsSet = false;
-    private int mAlpha = -1;
+
     private int mBottomOverride = Integer.MIN_VALUE;
 
+    public QuickContactBackgroundDrawable(Resources res) {
+        mLeftDrawable = res.getDrawable(R.drawable.quickactions_arrow_left_holo_light);
+        mMiddleDrawable = res.getDrawable(R.drawable.quickactions_arrow_middle_holo_light);
+        mRightDrawable = res.getDrawable(R.drawable.quickactions_arrow_right_holo_light);
+    }
+
     @Override
     public void setAlpha(int alpha) {
-        mAlpha = alpha;
-        setChildAlpha();
+        mLeftDrawable.setAlpha(alpha);
+        mMiddleDrawable.setAlpha(alpha);
+        mRightDrawable.setAlpha(alpha);
     }
 
     /**
-     * Overrides the bottom bounds. This is used for the animation when the QuickContact
-     * expands/collapses options
+     * Overrides the bottom bounds. This is used for the animation when the
+     * QuickContact expands/collapses options
      */
     public void setBottomOverride(int value) {
         mBottomOverride = value;
-        setChildBounds();
+        onBoundsChange(getBounds());
         invalidateSelf();
     }
 
     public void clearBottomOverride() {
         mBottomOverride = Integer.MIN_VALUE;
+        onBoundsChange(getBounds());
         invalidateSelf();
-        setChildBounds();
     }
 
     public float getBottomOverride() {
@@ -64,7 +70,29 @@
     }
 
     @Override
+    public boolean isStateful() {
+        return true;
+    }
+
+    @Override
+    protected boolean onStateChange(int[] state) {
+        super.onStateChange(state);
+        mLeftDrawable.setState(state);
+        mMiddleDrawable.setState(state);
+        mRightDrawable.setState(state);
+        return true;
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        return true;
+    }
+
+    @Override
     public void setColorFilter(ColorFilter cf) {
+        mLeftDrawable.setColorFilter(cf);
+        mMiddleDrawable.setColorFilter(cf);
+        mRightDrawable.setColorFilter(cf);
     }
 
     @Override
@@ -72,54 +100,22 @@
         return PixelFormat.TRANSLUCENT;
     }
 
-    public void configure(Resources resources, boolean arrowUp, int requestedX) {
-        mLeftDrawable = resources.getDrawable(arrowUp
-                ? R.drawable.quickactions_arrowup_left_holo_light
-                : R.drawable.quickactions_arrowdown_left_holo_light);
-        mMiddleDrawable = resources.getDrawable(arrowUp
-                ? R.drawable.quickactions_arrowup_middle_holo_light
-                : R.drawable.quickactions_arrowdown_middle_holo_light);
-        mRightDrawable = resources.getDrawable(arrowUp
-                ? R.drawable.quickactions_arrowup_right_holo_light
-                : R.drawable.quickactions_arrowdown_right_holo_light);
-
-        mRequestedX = requestedX;
-
-        setChildAlpha();
-        setChildBounds();
-    }
-
     @Override
     protected void onBoundsChange(Rect bounds) {
-        mBoundsSet = true;
-        setChildBounds();
-    }
+        final int requestedX = getLevel();
 
-    private void setChildAlpha() {
-        if (mAlpha == -1) return;
-
-        if (mLeftDrawable != null) mLeftDrawable.setAlpha(mAlpha);
-        if (mMiddleDrawable != null) mMiddleDrawable.setAlpha(mAlpha);
-        if (mRightDrawable != null) mRightDrawable.setAlpha(mAlpha);
-    }
-
-    private void setChildBounds() {
-        if (mRequestedX == Integer.MIN_VALUE) return;
-        if (!mBoundsSet) return;
-
-        final Rect bounds = getBounds();
-        int middleLeft = mRequestedX - mMiddleDrawable.getIntrinsicWidth() / 2;
-        int middleRight = mRequestedX + mMiddleDrawable.getIntrinsicWidth() / 2;
+        int middleLeft = requestedX - mMiddleDrawable.getIntrinsicWidth() / 2;
+        int middleRight = requestedX + mMiddleDrawable.getIntrinsicWidth() / 2;
 
         // ensure left drawable is not smaller than its Intrinsic Width
-        final int leftExtra =  (middleLeft - bounds.left) - mLeftDrawable.getIntrinsicWidth();
+        final int leftExtra = (middleLeft - bounds.left) - mLeftDrawable.getIntrinsicWidth();
         if (leftExtra < 0) {
             middleLeft -= leftExtra;
             middleRight -= leftExtra;
         }
 
         // ensure right drawable is not smaller than its Intrinsic Width
-        final int rightExtra =  (bounds.right - middleRight) - mRightDrawable.getIntrinsicWidth();
+        final int rightExtra = (bounds.right - middleRight) - mRightDrawable.getIntrinsicWidth();
         if (rightExtra < 0) {
             middleLeft += rightExtra;
             middleRight += rightExtra;
diff --git a/src/com/android/contacts/quickcontact/QuickContactRootLayout.java b/src/com/android/contacts/quickcontact/QuickContactRootLayout.java
deleted file mode 100644
index 007783a..0000000
--- a/src/com/android/contacts/quickcontact/QuickContactRootLayout.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2010 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.quickcontact;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.widget.LinearLayout;
-
-/**
- * Custom layout for Quick Contact. It intercepts the BACK key and
- * close QC even when the soft keyboard is open.
- */
-public class QuickContactRootLayout extends LinearLayout {
-    private Listener mListener;
-
-    public QuickContactRootLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public void setListener(Listener value) {
-        mListener = value;
-    }
-
-    /**
-     * Intercepts the BACK key event and dismisses QuickContact window.
-     */
-    @Override
-    public boolean dispatchKeyEventPreIme(KeyEvent event) {
-        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
-            if (mListener != null) mListener.onBackPressed();
-            return true;
-        }
-        return super.dispatchKeyEventPreIme(event);
-    }
-
-    public interface Listener {
-        void onBackPressed();
-    }
-}
diff --git a/src/com/android/contacts/quickcontact/QuickContactWindow.java b/src/com/android/contacts/quickcontact/QuickContactWindow.java
index a66f506..5f4bcc9 100644
--- a/src/com/android/contacts/quickcontact/QuickContactWindow.java
+++ b/src/com/android/contacts/quickcontact/QuickContactWindow.java
@@ -20,18 +20,20 @@
 import com.android.contacts.ContactPresenceIconUtil;
 import com.android.contacts.ContactSaveService;
 import com.android.contacts.R;
-import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.util.Constants;
 import com.android.contacts.util.DataStatus;
 import com.android.contacts.util.NotifyingAsyncQueryHandler;
-import com.android.internal.policy.PolicyManager;
 import com.google.android.collect.Lists;
+import com.google.common.base.Preconditions;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.app.Activity;
 import android.content.ActivityNotFoundException;
+import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -41,6 +43,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Im;
@@ -54,31 +57,16 @@
 import android.provider.ContactsContract.QuickContact;
 import android.provider.ContactsContract.RawContacts;
 import android.text.TextUtils;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.ActionMode;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewStub;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.FrameLayout;
-import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
@@ -89,72 +77,44 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
 /**
- * Window that shows QuickContact dialog for a specific {@link Contacts#_ID}.
+ * Mostly translucent {@link Activity} that shows QuickContact dialog. It loads
+ * data asynchronously, and then shows a popup with details centered around
+ * {@link Intent#getSourceBounds()}.
  */
-public class QuickContactWindow implements Window.Callback,
+public class QuickContactWindow extends Activity implements
         NotifyingAsyncQueryHandler.AsyncQueryListener, View.OnClickListener,
-        AbsListView.OnItemClickListener, KeyEvent.Callback, OnGlobalLayoutListener,
-        QuickContactRootLayout.Listener {
+        AbsListView.OnItemClickListener {
+    private static final String TAG = "QuickContact";
 
-    private static final String TAG = "QuickContactWindow";
+    private static final boolean TRACE_LAUNCH = false;
+    private static final String TRACE_TAG = "quickcontact";
 
-    /**
-     * Interface used to allow the person showing a {@link QuickContactWindow} to
-     * know when the window has been dismissed.
-     */
-    public interface OnDismissListener {
-        public void onDismiss(QuickContactWindow dialog);
-    }
-
-    private final static int ANIMATION_FADE_IN_TIME = 100;
-    private final static int ANIMATION_FADE_OUT_TIME = 100;
-    private final static int ANIMATION_EXPAND_TIME = 100;
-    private final static int ANIMATION_COLLAPSE_TIME = 100;
-
-    /**
-     * If the anchor is wider than (quick contact width * this constant) then
-     * center quick contact.  Otherwise, left-align.
-     */
-    private static final double MIN_RELATIVE_ANCHOR_WIDTH_TO_CENTER = 0.5;
-
-    private final Context mContext;
-    private final LayoutInflater mInflater;
-    private final WindowManager mWindowManager;
-    private Window mWindow;
-    private View mDecor;
-    private final Rect mRect = new Rect();
-
-    private boolean mDismissed = false;
-    private boolean mQuerying = false;
-    private boolean mShowing = false;
+    private static final int ANIMATION_FADE_IN_TIME = 100;
+    private static final int ANIMATION_FADE_OUT_TIME = 100;
+    private static final int ANIMATION_EXPAND_TIME = 100;
+    private static final int ANIMATION_COLLAPSE_TIME = 100;
 
     private NotifyingAsyncQueryHandler mHandler;
-    private OnDismissListener mDismissListener;
 
     private Uri mLookupUri;
-    private Rect mAnchor;
-
-    private int mScreenWidth;
-    private int mUseableScreenHeight;
-    private int mRequestedY;
+    private int mMode;
+    private String[] mExcludeMimes;
 
     private boolean mHasValidSocial = false;
 
-    private int mMode;
-    private QuickContactRootLayout mRootView;
+    private FloatingChildLayout mFloatingLayout;
     private QuickContactBackgroundDrawable mBackground;
-    private View mHeader;
-    private HorizontalScrollView mTrackScroll;
-    private ViewGroup mTrack;
 
+    private View mHeader;
+    private ViewGroup mTrack;
     private FrameLayout mFooter;
     private LinearLayout mFooterDisambig;
     private LinearLayout mFooterClearDefaults;
+
     private ListView mResolveList;
     private CheckableImageView mLastAction;
     private CheckBox mSetPrimaryCheckBox;
@@ -166,9 +126,6 @@
      */
     private HashMap<String, Action> mDefaultsMap = new HashMap<String, Action>();
 
-    private int mWindowRecycled = 0;
-    private int mActionRecycled = 0;
-
     /**
      * Set of {@link Action} that are associated with the aggregate currently
      * displayed by this dialog, represented as a map from {@link String}
@@ -177,14 +134,6 @@
     private ActionMultiMap mActions = new ActionMultiMap();
 
     /**
-     * Pool of unused {@link CheckableImageView} that have previously been
-     * inflated, and are ready to be recycled through {@link #obtainView()}.
-     */
-    private LinkedList<CheckableImageView> mActionPool = new LinkedList<CheckableImageView>();
-
-    private String[] mExcludeMimes;
-
-    /**
      * {@link #PRECEDING_MIMETYPES} and {@link #FOLLOWING_MIMETYPES} are used to sort MIME-types.
      *
      * <p>The MIME-types in {@link #PRECEDING_MIMETYPES} appear in the front of the dialog,
@@ -222,80 +171,48 @@
     });
     private static final int TOKEN_DATA = 1;
 
-    static final boolean TRACE_LAUNCH = false;
-    static final String TRACE_TAG = "quickcontact";
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
 
-    /**
-     * Prepare a dialog to show in the given {@link Context}.
-     */
-    public QuickContactWindow(Context context) {
-        mContext = new ContextThemeWrapper(context, R.style.QuickContact);
-        mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+        setContentView(R.layout.quickcontact_activity);
 
-        mWindow = PolicyManager.makeNewWindow(mContext);
-        mWindow.setCallback(this);
-        mWindow.setWindowManager(mWindowManager, null, null);
-        mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED);
+        mBackground = new QuickContactBackgroundDrawable(getResources());
 
-        mWindow.setContentView(R.layout.quickcontact);
+        mFloatingLayout = findTypedViewById(R.id.floating_layout);
+        mFloatingLayout.getChild().setBackgroundDrawable(mBackground);
+        mFloatingLayout.setOnOutsideTouchListener(mOnOutsideTouchListener);
 
-        mRootView = (QuickContactRootLayout)mWindow.findViewById(R.id.root);
-        mRootView.setListener(this);
-        mRootView.setFocusable(true);
-        mRootView.setFocusableInTouchMode(true);
-        mRootView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+        mTrack = findTypedViewById(R.id.quickcontact);
+        mFooter = findTypedViewById(R.id.footer);
+        mFooterDisambig = findTypedViewById(R.id.footer_disambig);
+        mFooterClearDefaults = findTypedViewById(R.id.footer_clear_defaults);
+        mResolveList = findTypedViewById(android.R.id.list);
+        mSetPrimaryCheckBox = findTypedViewById(android.R.id.checkbox);
 
-        mBackground = new QuickContactBackgroundDrawable();
-        mRootView.setBackgroundDrawable(mBackground);
+        mDefaultsListView = findTypedViewById(R.id.defaults_list);
 
-        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
-        mScreenWidth = metrics.widthPixels;
-        mUseableScreenHeight = metrics.heightPixels;
+        mClearDefaultsButton = findTypedViewById(R.id.clear_defaults_button);
+        mClearDefaultsButton.setOnClickListener(mOnClearDefaultsClickListener);
 
-        mTrack = (ViewGroup) mWindow.findViewById(R.id.quickcontact);
-        mTrackScroll = (HorizontalScrollView) mWindow.findViewById(R.id.scroll);
+        mResolveList.setOnItemClickListener(this);
 
-        mFooter = (FrameLayout) mWindow.findViewById(R.id.footer);
-        mFooterDisambig = (LinearLayout) mWindow.findViewById(R.id.footer_disambig);
-        mFooterClearDefaults = (LinearLayout) mWindow.findViewById(R.id.footer_clear_defaults);
-        mResolveList = (ListView) mWindow.findViewById(android.R.id.list);
-        mSetPrimaryCheckBox = (CheckBox) mWindow.findViewById(android.R.id.checkbox);
+        mHandler = new NotifyingAsyncQueryHandler(this, this);
 
-        mDefaultsListView = (ListView) mWindow.findViewById(R.id.defaults_list);
-        mClearDefaultsButton = (Button) mWindow.findViewById(R.id.clear_defaults_button);
-        mClearDefaultsButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                clearDefaults();
-            }
-        });
-
-        mResolveList.setOnItemClickListener(QuickContactWindow.this);
-
-        mHandler = new NotifyingAsyncQueryHandler(mContext, this);
-    }
-
-    /**
-     * Prepare a dialog to show in the given {@link Context}, and notify the
-     * given {@link OnDismissListener} each time this dialog is dismissed.
-     */
-    public QuickContactWindow(Context context, OnDismissListener dismissListener) {
-        this(context);
-        mDismissListener = dismissListener;
+        show();
     }
 
     private View getHeaderView(int mode) {
         View header = null;
         switch (mode) {
             case QuickContact.MODE_SMALL:
-                header = mWindow.findViewById(R.id.header_small);
+                header = findViewById(R.id.header_small);
                 break;
             case QuickContact.MODE_MEDIUM:
-                header = mWindow.findViewById(R.id.header_medium);
+                header = findViewById(R.id.header_medium);
                 break;
             case QuickContact.MODE_LARGE:
-                header = mWindow.findViewById(R.id.header_large);
+                header = findViewById(R.id.header_large);
                 break;
         }
 
@@ -310,60 +227,52 @@
         return header;
     }
 
-    /**
-     * Start showing a dialog for the given {@link Contacts#_ID} pointing
-     * towards the given location.
-     */
-    public synchronized void show(Uri lookupUri, Rect anchor, int mode, String[] excludeMimes) {
-        if (mQuerying || mShowing) {
-            Log.w(TAG, "dismissing before showing");
-            dismissInternal();
-        }
+    private void show() {
 
-        if (TRACE_LAUNCH && !android.os.Debug.isMethodTracingActive()) {
+        if (TRACE_LAUNCH) {
             android.os.Debug.startMethodTracing(TRACE_TAG);
         }
 
-        // Validate incoming parameters
-        final boolean validMode = (mode == QuickContact.MODE_SMALL
-                || mode == QuickContact.MODE_MEDIUM || mode == QuickContact.MODE_LARGE);
-        if (!validMode) {
-            throw new IllegalArgumentException("Invalid mode, expecting MODE_LARGE, "
-                    + "MODE_MEDIUM, or MODE_SMALL");
+        final Intent intent = getIntent();
+
+        Uri lookupUri = intent.getData();
+
+        // Check to see whether it comes from the old version.
+        if (android.provider.Contacts.AUTHORITY.equals(lookupUri.getAuthority())) {
+            final long rawContactId = ContentUris.parseId(lookupUri);
+            lookupUri = RawContacts.getContactLookupUri(getContentResolver(),
+                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
         }
 
-        if (anchor == null) {
-            throw new IllegalArgumentException("Missing anchor rectangle");
+        mLookupUri = Preconditions.checkNotNull(lookupUri, "missing lookupUri");
+
+        // Read requested parameters for displaying
+        final Rect targetScreen = intent.getSourceBounds();
+        Preconditions.checkNotNull(targetScreen, "missing targetScreen");
+        mFloatingLayout.setChildTargetScreen(targetScreen);
+
+        mMode = intent.getIntExtra(QuickContact.EXTRA_MODE, QuickContact.MODE_MEDIUM);
+        mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
+
+        switch (mMode) {
+            case QuickContact.MODE_SMALL:
+            case QuickContact.MODE_MEDIUM:
+            case QuickContact.MODE_LARGE:
+                break;
+            default:
+                throw new IllegalArgumentException("Unexpected mode: " + mMode);
         }
 
-        // Prepare header view for requested mode
-        mLookupUri = lookupUri;
-        mAnchor = new Rect(anchor);
-        mMode = mode;
-        mExcludeMimes = excludeMimes;
-
-        mHeader = getHeaderView(mode);
-
+        // find and prepare correct header view
+        mHeader = getHeaderView(mMode);
         setHeaderText(R.id.name, R.string.quickcontact_missing_name);
-
         setHeaderText(R.id.status, null);
         setHeaderText(R.id.timestamp, null);
-
         setHeaderImage(R.id.presence, null);
 
-        resetTrack();
-
-        // We need to have a focused view inside the QuickContact window so
-        // that the BACK key event can be intercepted
-        mRootView.requestFocus();
-
-        mHasValidSocial = false;
-        mDismissed = false;
-        mQuerying = true;
-
         // Start background query for data, but only select photo rows when they
         // directly match the super-primary PHOTO_ID.
-        final Uri dataUri = getDataUri(lookupUri);
+        final Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
         mHandler.cancelOperation(TOKEN_DATA);
 
         // Only request photo data when required by mode
@@ -379,222 +288,85 @@
         }
     }
 
-    /**
-     * Build a {@link Uri} into the {@link Data} table for the requested
-     * {@link Contacts#CONTENT_LOOKUP_URI} style {@link Uri}.
-     */
-    private Uri getDataUri(Uri lookupUri) {
-        return Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
+    @SuppressWarnings("unchecked")
+    private <T> T findTypedViewById(int id) {
+        return (T) super.findViewById(id);
     }
 
-    /**
-     * Creates and configures the background resource
-     */
-    private void configureBackground(boolean arrowUp, int requestedX) {
-        mBackground.configure(mContext.getResources(), arrowUp, requestedX);
+    private View.OnTouchListener mOnOutsideTouchListener = new View.OnTouchListener() {
+        /** {@inheritDoc} */
+        public boolean onTouch(View v, MotionEvent event) {
+            hide(true);
+            return true;
+        }
+    };
+
+    private View.OnClickListener mOnClearDefaultsClickListener = new View.OnClickListener() {
+        /** {@inheritDoc} */
+        public void onClick(View v) {
+            clearDefaults();
+        }
+    };
+
+    private void hide(boolean withAnimation) {
+        // cancel any pending queries
+        mHandler.cancelOperation(TOKEN_DATA);
+
+        if (withAnimation) {
+            mFloatingLayout.hideChild(new Runnable() {
+                /** {@inheritDoc} */
+                public void run() {
+                    finish();
+                }
+            });
+        } else {
+            mFloatingLayout.hideChild(null);
+            finish();
+        }
     }
 
-    /**
-     * Actual internal method to show this dialog. Called only by
-     * {@link #considerShowing()} when all data requirements have been met.
-     */
-    private void showInternal() {
-        mDecor = mWindow.getDecorView();
-        mDecor.getViewTreeObserver().addOnGlobalLayoutListener(this);
-        WindowManager.LayoutParams layoutParams = mWindow.getAttributes();
-
-        layoutParams.width = mContext.getResources().getDimensionPixelSize(
-                R.dimen.quick_contact_width);
-        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
-
-        // Try to left align with the anchor control or center if the anchor is wide
-        if (mAnchor.left + layoutParams.width <= mScreenWidth) {
-            if (mAnchor.width() > layoutParams.width * MIN_RELATIVE_ANCHOR_WIDTH_TO_CENTER) {
-                layoutParams.x = mAnchor.left + (mAnchor.width() - layoutParams.width) / 2;
-            } else {
-                layoutParams.x = mAnchor.left;
-            }
-        } else {
-            // Not enough space. Try to right align to the anchor
-            if (mAnchor.right - layoutParams.width >= 0) {
-                layoutParams.x = mAnchor.right - layoutParams.width;
-            } else {
-                // Also not enough space. Use the whole screen width available
-                layoutParams.x = 0;
-                layoutParams.width = mScreenWidth;
-            }
-        }
-
-        // Force layout measuring pass so we have baseline numbers
-        mDecor.measure(layoutParams.width, layoutParams.height);
-        final int blockHeight = mDecor.getMeasuredHeight();
-
-        layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
-
-        if (mUseableScreenHeight - mAnchor.bottom > blockHeight) {
-            // Show downwards callout when enough room, aligning block top with bottom of
-            // anchor area, and adjusting to inset arrow.
-            configureBackground(true, mAnchor.centerX() - layoutParams.x);
-            layoutParams.y = mAnchor.bottom;
-            layoutParams.windowAnimations = R.style.QuickContactBelowAnimation;
-        } else {
-            // Show upwards callout, aligning bottom block
-            // edge with top of anchor area, and adjusting to inset arrow.
-            configureBackground(false, mAnchor.centerX() - layoutParams.x);
-            layoutParams.y = mAnchor.top - blockHeight;
-            layoutParams.windowAnimations = R.style.QuickContactAboveAnimation;
-        }
-
-        layoutParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-
-        mRequestedY = layoutParams.y;
-        mWindowManager.addView(mDecor, layoutParams);
-        mShowing = true;
-        mQuerying = false;
-        mDismissed = false;
-
-        if (TRACE_LAUNCH) {
-            android.os.Debug.stopMethodTracing();
-            Log.d(TAG, "Window recycled " + mWindowRecycled + " times, chiclets "
-                    + mActionRecycled + " times");
-        }
+    @Override
+    public void onBackPressed() {
+        hide(true);
     }
 
     /** {@inheritDoc} */
-    @Override
-    public void onGlobalLayout() {
-        layoutInScreen();
-    }
+    public synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
+        try {
+            if (isFinishing()) {
+                hide(false);
+                return;
+            } else if (cursor == null || cursor.getCount() == 0) {
+                Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_LONG).show();
+                hide(false);
+                return;
+            }
 
-    /**
-     * Adjust vertical {@link WindowManager.LayoutParams} to fit window as best
-     * as possible, shifting up to display content as needed.
-     */
-    private void layoutInScreen() {
-        if (!mShowing) return;
+            bindData(cursor);
 
-        final WindowManager.LayoutParams l = mWindow.getAttributes();
-        final int originalY = l.y;
-
-        final int blockHeight = mDecor.getHeight();
-
-        l.y = mRequestedY;
-        if (mRequestedY + blockHeight > mUseableScreenHeight) {
-            // Shift up from bottom when overflowing
-            l.y = mUseableScreenHeight - blockHeight;
-        }
-
-        if (originalY != l.y) {
-            // Only update when value is changed
-            mWindow.setAttributes(l);
-        }
-    }
-
-    /**
-     * Dismiss this dialog if showing.
-     */
-    public synchronized void dismiss() {
-        // Notify any listeners that we've been dismissed
-        if (mDismissListener != null) {
-            mDismissListener.onDismiss(this);
-        }
-
-        dismissInternal();
-    }
-
-    private void dismissInternal() {
-        // Remove any attached window decor for recycling
-        boolean hadDecor = mDecor != null;
-        if (hadDecor) {
-            mWindowManager.removeView(mDecor);
-            mWindowRecycled++;
-            mDecor.getViewTreeObserver().removeGlobalOnLayoutListener(this);
-            mDecor = null;
-            mWindow.closeAllPanels();
-        }
-        mShowing = false;
-        mDismissed = true;
-
-        // Cancel any pending queries
-        mHandler.cancelOperation(TOKEN_DATA);
-        mQuerying = false;
-
-        // Completely hide header and reset track
-        mHeader.setVisibility(View.GONE);
-        resetTrack();
-    }
-
-    /**
-     * Reset track to initial state, recycling any chiclets.
-     */
-    private void resetTrack() {
-        // Clear background height-animation override
-        mBackground.clearBottomOverride();
-
-        // Release reference to last chiclet
-        mLastAction = null;
-
-        // Clear track actions and scroll to hard left
-        mActions.clear();
-
-        // Recycle any chiclets in use
-        for (int i = mTrack.getChildCount() - 1; i >= 0; i--) {
-            releaseView((CheckableImageView)mTrack.getChildAt(i));
-            mTrack.removeViewAt(i);
-        }
-
-        mTrackScroll.fullScroll(View.FOCUS_LEFT);
-
-        // Clear any primary requests
-        mSetPrimaryCheckBox.setChecked(false);
-
-        setNewActionViewChecked(null);
-        mFooter.setVisibility(View.GONE);
-    }
-
-    /**
-     * Consider showing this window, which will only call through to
-     * {@link #showInternal()} when all data items are present.
-     */
-    private void considerShowing() {
-        if (!mShowing && !mDismissed) {
             if (mMode == QuickContact.MODE_MEDIUM && !mHasValidSocial) {
                 // Missing valid social, swap medium for small header
                 mHeader.setVisibility(View.GONE);
                 mHeader = getHeaderView(QuickContact.MODE_SMALL);
             }
 
-            // All queries have returned, pull curtain
-            showInternal();
+            if (TRACE_LAUNCH) {
+                android.os.Debug.stopMethodTracing();
+            }
+
+            // data bound and ready, pull curtain to show
+            mFloatingLayout.showChild();
+
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
         }
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
-        // Bail early when query is stale
-        if (cookie != mLookupUri) return;
-
-        if (cursor == null) {
-            // Problem while running query, so bail without showing
-            Log.w(TAG, "Missing cursor for token=" + token);
-            this.dismiss();
-            return;
-        }
-
-        handleData(cursor);
-
-        if (!cursor.isClosed()) {
-            cursor.close();
-        }
-
-        considerShowing();
-    }
-
     /** Assign this string to the view, if found in {@link #mHeader}. */
     private void setHeaderText(int id, int resId) {
-        setHeaderText(id, mContext.getResources().getText(resId));
+        setHeaderText(id, getText(resId));
     }
 
     /** Assign this string to the view, if found in {@link #mHeader}. */
@@ -632,26 +404,22 @@
     /**
      * Handle the result from the {@link #TOKEN_DATA} query.
      */
-    private void handleData(Cursor cursor) {
-        final ResolveCache cache = ResolveCache.getInstance(mContext);
-        if (cursor == null) return;
-        if (cursor.getCount() == 0) {
-            Toast.makeText(mContext, R.string.invalidContactMessage, Toast.LENGTH_LONG).show();
-            dismiss();
-            return;
-        }
+    private void bindData(Cursor cursor) {
+        final ResolveCache cache = ResolveCache.getInstance(this);
+        final Context context = this;
 
         if (!isMimeExcluded(Contacts.CONTENT_ITEM_TYPE)) {
             // Add the profile shortcut action
-            final Action action = new ProfileAction(mContext, mLookupUri);
+            final Action action = new ProfileAction(context, mLookupUri);
             mActions.put(Contacts.CONTENT_ITEM_TYPE, action);
         }
 
         mDefaultsMap.clear();
 
         final DataStatus status = new DataStatus();
-        final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
-        final ImageView photoView = (ImageView)mHeader.findViewById(R.id.photo);
+        final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
+                context.getApplicationContext());
+        final ImageView photoView = (ImageView) mHeader.findViewById(R.id.photo);
 
         Bitmap photoBitmap = null;
         while (cursor.moveToNext()) {
@@ -683,7 +451,7 @@
                 // Build an action for this data entry, find a mapping to a UI
                 // element, build its summary from the cursor, and collect it
                 // along with all others of this MIME-type.
-                final Action action = new DataAction(mContext, mimeType, kind, dataId, cursor);
+                final Action action = new DataAction(context, mimeType, kind, dataId, cursor);
                 final boolean wasAdded = considerAdd(action, cache);
                 if (wasAdded) {
                     // Remember the default
@@ -695,7 +463,7 @@
 
             // If phone number, also insert as text message action
             if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && kind != null) {
-                final DataAction action = new DataAction(mContext, Constants.MIME_TYPE_SMS_ADDRESS,
+                final DataAction action = new DataAction(context, Constants.MIME_TYPE_SMS_ADDRESS,
                         kind, dataId, cursor);
                 considerAdd(action, cache);
             }
@@ -708,7 +476,7 @@
                 final DataKind imKind = accountTypes.getKindOrFallback(accountType,
                         Im.CONTENT_ITEM_TYPE);
                 if (imKind != null) {
-                    final DataAction action = new DataAction(mContext, Im.CONTENT_ITEM_TYPE, imKind,
+                    final DataAction action = new DataAction(context, Im.CONTENT_ITEM_TYPE, imKind,
                             dataId, cursor);
                     considerAdd(action, cache);
                     isIm = true;
@@ -721,7 +489,7 @@
                     final DataKind imKind = accountTypes.getKindOrFallback(accountType,
                             Im.CONTENT_ITEM_TYPE);
                     if (imKind != null) {
-                        final DataAction chatAction = new DataAction(mContext,
+                        final DataAction chatAction = new DataAction(context,
                                 Constants.MIME_TYPE_VIDEO_CHAT, imKind, dataId, cursor);
                         considerAdd(chatAction, cache);
                     }
@@ -729,6 +497,11 @@
             }
         }
 
+        // Collapse Action Lists (remove e.g. duplicate e-mail addresses from different sources)
+        for (ArrayList<Action> actionChildren : mActions.values()) {
+            Collapser.collapseList(actionChildren);
+        }
+
         // Make sure that we only display the "clear default" action if there
         // are actually several items to chose from
         boolean shouldDisplayClearDefaults = false;
@@ -750,7 +523,7 @@
             final int presence = cursor.getInt(DataQuery.CONTACT_PRESENCE);
             final int chatCapability = cursor.getInt(DataQuery.CONTACT_CHAT_CAPABILITY);
             final Drawable statusIcon = ContactPresenceIconUtil.getChatCapabilityIcon(
-                    mContext, presence, chatCapability);
+                    context, presence, chatCapability);
 
             setHeaderText(R.id.name, name);
             setHeaderImage(R.id.presence, statusIcon);
@@ -766,7 +539,7 @@
         if (mHasValidSocial && mMode != QuickContact.MODE_SMALL) {
             // Update status when valid was found
             setHeaderText(R.id.status, status.getStatus());
-            setHeaderText(R.id.timestamp, status.getTimestampLabel(mContext));
+            setHeaderText(R.id.timestamp, status.getTimestampLabel(context));
         }
 
         // Turn our list of actions into UI elements
@@ -780,7 +553,7 @@
         for (String mimeType : PRECEDING_MIMETYPES) {
             if (containedTypes.contains(mimeType)) {
                 hasData = true;
-                mTrack.addView(inflateAction(mimeType, cache));
+                mTrack.addView(inflateAction(mimeType, cache, mTrack));
                 containedTypes.remove(mimeType);
             }
         }
@@ -792,7 +565,7 @@
         for (String mimeType : FOLLOWING_MIMETYPES) {
             if (containedTypes.contains(mimeType)) {
                 hasData = true;
-                mTrack.addView(inflateAction(mimeType, cache));
+                mTrack.addView(inflateAction(mimeType, cache, mTrack));
                 containedTypes.remove(mimeType);
             }
         }
@@ -801,9 +574,11 @@
         if (containedTypes.contains(ClearDefaultsAction.PSEUDO_MIME_TYPE)) {
             final ClearDefaultsAction action = (ClearDefaultsAction) mActions.get(
                     ClearDefaultsAction.PSEUDO_MIME_TYPE).get(0);
-            final CheckableImageView view = obtainView();
+            final CheckableImageView view = (CheckableImageView) getLayoutInflater().inflate(
+                    R.layout.quickcontact_item, mTrack, false);
+
             view.setChecked(false);
-            final String description = mContext.getResources().getString(
+            final String description = context.getResources().getString(
                     R.string.quickcontact_clear_defaults_description);
             view.setContentDescription(description);
             view.setImageResource(R.drawable.ic_menu_settings_holo_light);
@@ -819,12 +594,13 @@
         if (remainingTypes.length > 0) hasData = true;
         Arrays.sort(remainingTypes);
         for (String mimeType : remainingTypes) {
-            mTrack.addView(inflateAction(mimeType, cache), index++);
+            mTrack.addView(inflateAction(mimeType, cache, mTrack), index++);
         }
 
         if (!hasData) {
             // When there is no data to display, add a TextView to show the user there's no data
-            View view = mInflater.inflate(R.layout.quickcontact_item_nodata, mTrack, false);
+            View view = getLayoutInflater().inflate(
+                    R.layout.quickcontact_item_nodata, mTrack, false);
             mTrack.addView(view, index++);
         }
     }
@@ -833,7 +609,9 @@
      * Clears the defaults currently set on the Contact
      */
     private void clearDefaults() {
+        final Context context = this;
         final Set<String> mimeTypesKeySet = mDefaultsMap.keySet();
+
         // Copy to array so that we can modify the HashMap below
         final String[] mimeTypes = new String[mimeTypesKeySet.size()];
         mimeTypesKeySet.toArray(mimeTypes);
@@ -841,9 +619,9 @@
         // Send clear default Intents, one by one
         for (String mimeType : mimeTypes) {
             final Action action = mDefaultsMap.get(mimeType);
-            final Intent intent =
-                    ContactSaveService.createClearPrimaryIntent(mContext, action.getDataId());
-            mContext.startService(intent);
+            final Intent intent = ContactSaveService.createClearPrimaryIntent(
+                    context, action.getDataId());
+            context.startService(intent);
             mDefaultsMap.remove(mimeType);
         }
 
@@ -854,7 +632,6 @@
                 for (int i = mTrack.getChildCount() - 1; i >= 0; i--) {
                     final CheckableImageView button = (CheckableImageView) mTrack.getChildAt(i);
                     if (button.getTag() instanceof ClearDefaultsAction) {
-                        releaseView(button);
                         mTrack.removeViewAt(i);
                         break;
                     }
@@ -878,41 +655,15 @@
     }
 
     /**
-     * Obtain a new {@link CheckableImageView} for a new chiclet, either by
-     * recycling one from {@link #mActionPool}, or by inflating a new one. When
-     * finished, use {@link #releaseView(CheckableImageView)} to return back into the pool for
-     * later recycling.
-     */
-    private synchronized CheckableImageView obtainView() {
-        CheckableImageView view = mActionPool.poll();
-        if (view == null || QuickContactActivity.FORCE_CREATE) {
-            view = (CheckableImageView) mInflater.inflate(R.layout.quickcontact_item, mTrack,
-                    false);
-        }
-        return view;
-    }
-
-    /**
-     * Return the given {@link CheckableImageView} into our internal pool for
-     * possible recycling during another pass.
-     */
-    private synchronized void releaseView(CheckableImageView view) {
-        mActionPool.offer(view);
-        mActionRecycled++;
-    }
-
-    /**
      * Inflate the in-track view for the action of the given MIME-type, collapsing duplicate values.
      * Will use the icon provided by the {@link DataKind}.
      */
-    private View inflateAction(String mimeType, ResolveCache resolveCache) {
-        final CheckableImageView view = obtainView();
+    private View inflateAction(String mimeType, ResolveCache resolveCache, ViewGroup root) {
+        final CheckableImageView view = (CheckableImageView) getLayoutInflater().inflate(
+                R.layout.quickcontact_item, root, false);
 
         // Add direct intent if single child, otherwise flag for multiple
         List<Action> children = mActions.get(mimeType);
-        if (children.size() > 1) {
-            Collapser.collapseList(children);
-        }
         view.setTag(mimeType);
         final Action firstInfo = children.get(0);
 
@@ -989,6 +740,13 @@
                 expandAnimator.setDuration(ANIMATION_EXPAND_TIME);
                 expandAnimator.start();
 
+                expandAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mBackground.clearBottomOverride();
+                    }
+                });
+
                 final ObjectAnimator fadeInAnimator = ObjectAnimator.ofFloat(mFooter,
                         "alpha", 0.0f, 1.0f);
                 fadeInAnimator.setDuration(ANIMATION_FADE_IN_TIME);
@@ -1001,9 +759,12 @@
     /** {@inheritDoc} */
     @Override
     public void onClick(View view) {
+        final Context context = this;
+
         final boolean isActionView = (view instanceof CheckableImageView);
         final CheckableImageView actionView = isActionView ? (CheckableImageView)view : null;
         final Object tag = view.getTag();
+
         if (tag instanceof ClearDefaultsAction) {
             // Do nothing if already open
             if (actionView == mLastAction) return;
@@ -1044,29 +805,28 @@
 
                         @Override
                         public View getView(int position, View convertView, ViewGroup parent) {
-                            final View result = convertView != null ? convertView :
-                                    mInflater.inflate(R.layout.quickcontact_default_item,
-                                    parent, false);
+                            if (convertView == null) {
+                                convertView = getLayoutInflater().inflate(
+                                        R.layout.quickcontact_default_item, parent, false);
+                            }
+
                             // Set action title based on summary value
                             final Action defaultAction = actions[position];
 
-                            TextView text1 = (TextView)result.findViewById(android.R.id.text1);
-                            TextView text2 = (TextView)result.findViewById(android.R.id.text2);
+                            final TextView text1 = (TextView) convertView.findViewById(
+                                    android.R.id.text1);
+                            final TextView text2 = (TextView) convertView.findViewById(
+                                    android.R.id.text2);
 
                             text1.setText(defaultAction.getHeader());
                             text2.setText(defaultAction.getBody());
 
-                            result.setTag(defaultAction);
-                            return result;
+                            convertView.setTag(defaultAction);
+                            return convertView;
                         }
                     });
 
                     animateExpand(true);
-                    // Make sure we resize to make room for ListView
-                    if (mDecor != null) {
-                        mDecor.forceLayout();
-                        mDecor.invalidate();
-                    }
                 }
             };
             if (mFooter.getVisibility() == View.VISIBLE) {
@@ -1115,24 +875,24 @@
                 public void run() {
                     // Incoming tag is concrete intent, so try launching
                     try {
-                        mContext.startActivity(action.getIntent());
+                        context.startActivity(action.getIntent());
                     } catch (ActivityNotFoundException e) {
-                        Toast.makeText(mContext, R.string.quickcontact_missing_app,
+                        Toast.makeText(context, R.string.quickcontact_missing_app,
                                 Toast.LENGTH_SHORT).show();
                     }
 
                     // Hide the resolution list, if present
                     setNewActionViewChecked(null);
-                    dismiss();
-                    mFooter.setVisibility(View.GONE);
 
                     // Set default?
                     final long dataId = action.getDataId();
                     if (makePrimary && dataId != -1) {
                         Intent serviceIntent = ContactSaveService.createSetSuperPrimaryIntent(
-                                mContext, dataId);
-                        mContext.startService(serviceIntent);
+                                context, dataId);
+                        context.startService(serviceIntent);
                     }
+
+                    hide(false);
                 }
             };
             if (isActionView && mFooter.getVisibility() == View.VISIBLE) {
@@ -1177,31 +937,31 @@
 
                     @Override
                     public View getView(int position, View convertView, ViewGroup parent) {
-                        final View result = convertView != null ? convertView :
-                                mInflater.inflate(R.layout.quickcontact_resolve_item,
-                                parent, false);
+                        if (convertView == null) {
+                            convertView = getLayoutInflater().inflate(
+                                    R.layout.quickcontact_resolve_item, parent, false);
+                        }
+
                         // Set action title based on summary value
                         final Action listAction = actionList.get(position);
 
-                        TextView text1 = (TextView)result.findViewById(android.R.id.text1);
-                        TextView text2 = (TextView)result.findViewById(android.R.id.text2);
+                        final TextView text1 = (TextView) convertView.findViewById(
+                                android.R.id.text1);
+                        final TextView text2 = (TextView) convertView.findViewById(
+                                android.R.id.text2);
 
                         text1.setText(listAction.getHeader());
                         text2.setText(listAction.getBody());
 
-                        result.setTag(listAction);
-                        return result;
+                        convertView.setTag(listAction);
+                        return convertView;
                     }
                 });
 
                 animateExpand(false);
-                // Make sure we resize to make room for ListView
-                if (mDecor != null) {
-                    mDecor.forceLayout();
-                    mDecor.invalidate();
-                }
             }
         };
+
         if (mFooter.getVisibility() == View.VISIBLE) {
             // If the expansion list is currently opened, animate its collapse and then
             // execute the target app
@@ -1212,191 +972,6 @@
         }
     }
 
-    @Override
-    public void onBackPressed() {
-        dismiss();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if (mWindow.superDispatchKeyEvent(event)) {
-            return true;
-        }
-        return event.dispatch(this, mDecor != null
-                ? mDecor.getKeyDispatcherState() : null, this);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
-            event.startTracking();
-            return true;
-        }
-
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
-                && !event.isCanceled()) {
-            onBackPressed();
-            return true;
-        }
-
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
-        return mWindow.superDispatchKeyShortcutEvent(event);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        // TODO: make this window accessible
-        return false;
-    }
-
-    /**
-     * Detect if the given {@link MotionEvent} is outside the boundaries of this
-     * window, which usually means we should dismiss.
-     */
-    protected void detectEventOutside(MotionEvent event) {
-        if (event.getAction() == MotionEvent.ACTION_DOWN && mDecor != null) {
-            // Only try detecting outside events on down-press
-            mDecor.getHitRect(mRect);
-            final int x = (int)event.getX();
-            final int y = (int)event.getY();
-            if (!mRect.contains(x, y)) {
-                event.setAction(MotionEvent.ACTION_OUTSIDE);
-            }
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent event) {
-        detectEventOutside(event);
-        if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-            dismiss();
-            return true;
-        }
-        return mWindow.superDispatchTouchEvent(event);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean dispatchTrackballEvent(MotionEvent event) {
-        return mWindow.superDispatchTrackballEvent(event);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean dispatchGenericMotionEvent(MotionEvent event) {
-        return mWindow.superDispatchGenericMotionEvent(event);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onContentChanged() {
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean onCreatePanelMenu(int featureId, Menu menu) {
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public View onCreatePanelView(int featureId) {
-        return null;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean onMenuItemSelected(int featureId, MenuItem item) {
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean onMenuOpened(int featureId, Menu menu) {
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onPanelClosed(int featureId, Menu menu) {
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean onPreparePanel(int featureId, View view, Menu menu) {
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean onSearchRequested() {
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams attrs) {
-        if (mDecor != null) {
-            mWindowManager.updateViewLayout(mDecor, attrs);
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onWindowFocusChanged(boolean hasFocus) {
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onAttachedToWindow() {
-        // No actions
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onDetachedFromWindow() {
-        // No actions
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
-        return null;
-    }
-
-    @Override
-    public void onActionModeStarted(ActionMode mode) {
-    }
-
-    @Override
-    public void onActionModeFinished(ActionMode mode) {
-    }
 
     private interface DataQuery {
         final String[] PROJECTION = new String[] {
diff --git a/src/com/android/contacts/util/AccountsListAdapter.java b/src/com/android/contacts/util/AccountsListAdapter.java
index 8dbfc8d..97a9f84 100644
--- a/src/com/android/contacts/util/AccountsListAdapter.java
+++ b/src/com/android/contacts/util/AccountsListAdapter.java
@@ -60,7 +60,9 @@
         final AccountType accountType = mAccountTypes.getAccountType(account.type);
 
         text1.setText(account.name);
-        text2.setText(accountType.getDisplayLabel(mContext));
+        if (text2 != null) {
+            text2.setText(accountType.getDisplayLabel(mContext));
+        }
         icon.setImageDrawable(accountType.getDisplayIcon(mContext));
 
         return resultView;
diff --git a/src/com/android/contacts/util/NameConverter.java b/src/com/android/contacts/util/NameConverter.java
new file mode 100644
index 0000000..9853821
--- /dev/null
+++ b/src/com/android/contacts/util/NameConverter.java
@@ -0,0 +1,166 @@
+/*
+ * 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 android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.text.TextUtils;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Utility class for converting between a display name and structured name (and vice-versa), via
+ * calls to the contact provider.
+ */
+public class NameConverter {
+
+    /**
+     * The array of fields that comprise a structured name.
+     */
+    public static final String[] STRUCTURED_NAME_FIELDS = new String[] {
+            StructuredName.PREFIX,
+            StructuredName.GIVEN_NAME,
+            StructuredName.MIDDLE_NAME,
+            StructuredName.FAMILY_NAME,
+            StructuredName.SUFFIX
+    };
+
+    /**
+     * Converts the given structured name (provided as a map from {@link StructuredName} fields to
+     * corresponding values) into a display name string.
+     * <p>
+     * Note that this operates via a call back to the ContactProvider, but it does not access the
+     * database, so it should be safe to call from the UI thread.  See
+     * ContactsProvider2.completeName() for the underlying method call.
+     * @param context Activity context.
+     * @param structuredName The structured name map to convert.
+     * @return The display name computed from the structured name map.
+     */
+    public static String structuredNameToDisplayName(Context context,
+            Map<String, String> structuredName) {
+        Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name");
+        for (String key : STRUCTURED_NAME_FIELDS) {
+            if (structuredName.containsKey(key)) {
+                appendQueryParameter(builder, key, structuredName.get(key));
+            }
+        }
+        return fetchDisplayName(context, builder.build());
+    }
+
+    /**
+     * Converts the given structured name (provided as ContentValues) into a display name string.
+     * @param context Activity context.
+     * @param values The content values containing values comprising the structured name.
+     * @return
+     */
+    public static String structuredNameToDisplayName(Context context, ContentValues values) {
+        Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name");
+        for (String key : STRUCTURED_NAME_FIELDS) {
+            if (values.containsKey(key)) {
+                appendQueryParameter(builder, key, values.getAsString(key));
+            }
+        }
+        return fetchDisplayName(context, builder.build());
+    }
+
+    /**
+     * Helper method for fetching the display name via the given URI.
+     */
+    private static String fetchDisplayName(Context context, Uri uri) {
+        String displayName = null;
+        Cursor cursor = context.getContentResolver().query(uri, new String[]{
+                StructuredName.DISPLAY_NAME,
+        }, null, null, null);
+
+        try {
+            if (cursor.moveToFirst()) {
+                displayName = cursor.getString(0);
+            }
+        } finally {
+            cursor.close();
+        }
+        return displayName;
+    }
+
+    /**
+     * Converts the given display name string into a structured name (as a map from
+     * {@link StructuredName} fields to corresponding values).
+     * <p>
+     * Note that this operates via a call back to the ContactProvider, but it does not access the
+     * database, so it should be safe to call from the UI thread.
+     * @param context Activity context.
+     * @param displayName The display name to convert.
+     * @return The structured name map computed from the display name.
+     */
+    public static Map<String, String> displayNameToStructuredName(Context context,
+            String displayName) {
+        Map<String, String> structuredName = new TreeMap<String, String>();
+        Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name");
+
+        appendQueryParameter(builder, StructuredName.DISPLAY_NAME, displayName);
+        Cursor cursor = context.getContentResolver().query(builder.build(), STRUCTURED_NAME_FIELDS,
+                null, null, null);
+
+        try {
+            if (cursor.moveToFirst()) {
+                for (int i = 0; i < STRUCTURED_NAME_FIELDS.length; i++) {
+                    structuredName.put(STRUCTURED_NAME_FIELDS[i], cursor.getString(i));
+                }
+            }
+        } finally {
+            cursor.close();
+        }
+        return structuredName;
+    }
+
+    /**
+     * Converts the given display name string into a structured name (inserting the structured
+     * values into a new or existing ContentValues object).
+     * <p>
+     * Note that this operates via a call back to the ContactProvider, but it does not access the
+     * database, so it should be safe to call from the UI thread.
+     * @param context Activity context.
+     * @param displayName The display name to convert.
+     * @param contentValues The content values object to place the structured name values into.  If
+     *     null, a new one will be created and returned.
+     * @return The ContentValues object containing the structured name fields derived from the
+     *     display name.
+     */
+    public static ContentValues displayNameToStructuredName(Context context, String displayName,
+            ContentValues contentValues) {
+        if (contentValues == null) {
+            contentValues = new ContentValues();
+        }
+        Map<String, String> mapValues = displayNameToStructuredName(context, displayName);
+        for (String key : mapValues.keySet()) {
+            contentValues.put(key, mapValues.get(key));
+        }
+        return contentValues;
+    }
+
+    private static void appendQueryParameter(Builder builder, String field, String value) {
+        if (!TextUtils.isEmpty(value)) {
+            builder.appendQueryParameter(field, value);
+        }
+    }
+}
diff --git a/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java b/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java
index 83fae29..c8cfc8d 100644
--- a/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java
+++ b/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java
@@ -29,8 +29,6 @@
  * <p>
  * This pattern can be used to perform background queries without leaking
  * {@link Context} objects.
- *
- * @hide pending API council review
  */
 public class NotifyingAsyncQueryHandler extends AsyncQueryHandler {
     private WeakReference<AsyncQueryListener> mListener;
diff --git a/src/com/android/contacts/util/PhoneCapabilityTester.java b/src/com/android/contacts/util/PhoneCapabilityTester.java
index 23319aa..d3a8060 100644
--- a/src/com/android/contacts/util/PhoneCapabilityTester.java
+++ b/src/com/android/contacts/util/PhoneCapabilityTester.java
@@ -16,6 +16,8 @@
 
 package com.android.contacts.util;
 
+import com.android.contacts.R;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -79,4 +81,12 @@
                 Uri.fromParts(Constants.SCHEME_SMSTO, "", null));
         return isIntentRegistered(context, intent);
     }
+
+    /**
+     * True if we are using two-pane layouts ("tablet mode"), false if we are using single views
+     * ("phone mode")
+     */
+    public static boolean isUsingTwoPanes(Context context) {
+        return context.getResources().getBoolean(R.bool.config_use_two_panes);
+    }
 }
diff --git a/src/com/android/contacts/vcard/ExportProcessor.java b/src/com/android/contacts/vcard/ExportProcessor.java
index 67c9c4a..7b5e001 100644
--- a/src/com/android/contacts/vcard/ExportProcessor.java
+++ b/src/com/android/contacts/vcard/ExportProcessor.java
@@ -22,18 +22,22 @@
 
 import android.app.Notification;
 import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContactsEntity;
 import android.text.TextUtils;
 import android.util.Log;
-import android.widget.RemoteViews;
 
+import java.io.BufferedWriter;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
 
 /**
  * Class for processing one export request from a user. Dropped after exporting requested Uri(s).
@@ -92,6 +96,7 @@
         if (DEBUG) Log.d(LOG_TAG, String.format("vCard export (id: %d) has started.", mJobId));
         final ExportRequest request = mExportRequest;
         VCardComposer composer = null;
+        Writer writer = null;
         boolean successful = false;
         try {
             if (isCancelled()) {
@@ -129,9 +134,14 @@
             //     VCardConfig.FLAG_USE_QP_TO_PRIMARY_PROPERTIES);
             // composer = new VCardComposer(ExportVCardActivity.this, vcardType, true);
 
-            composer.addHandler(composer.new HandlerForOutputStream(outputStream));
-
-            if (!composer.init()) {
+            writer = new BufferedWriter(new OutputStreamWriter(outputStream));
+            final Uri contentUriForRawContactsEntity = RawContactsEntity.CONTENT_URI.buildUpon()
+                    .appendQueryParameter(RawContactsEntity.FOR_EXPORT_ONLY, "1")
+                    .build();
+            // TODO: should provide better selection.
+            if (!composer.init(Contacts.CONTENT_URI, new String[] {Contacts._ID},
+                    null, null,
+                    null, contentUriForRawContactsEntity)) {
                 final String errorReason = composer.getErrorReason();
                 Log.e(LOG_TAG, "initialization of vCard composer failed: " + errorReason);
                 final String translatedErrorReason =
@@ -157,7 +167,9 @@
                     Log.i(LOG_TAG, "Export request is cancelled during composing vCard");
                     return;
                 }
-                if (!composer.createOneEntry()) {
+                try {
+                    writer.write(composer.createOneEntry());
+                } catch (IOException e) {
                     final String errorReason = composer.getErrorReason();
                     Log.e(LOG_TAG, "Failed to read a contact: " + errorReason);
                     final String translatedErrorReason =
@@ -192,7 +204,13 @@
             if (composer != null) {
                 composer.terminate();
             }
-
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (IOException e) {
+                    Log.w(LOG_TAG, "IOException is thrown during close(). Ignored. " + e);
+                }
+            }
             mService.handleFinishExportNotification(mJobId, successful);
         }
     }
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index b3376e9..17b89c3 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -21,7 +21,6 @@
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.util.AccountSelectionUtil;
 import com.android.vcard.VCardEntryCounter;
-import com.android.vcard.VCardInterpreterCollection;
 import com.android.vcard.VCardParser;
 import com.android.vcard.VCardParser_V21;
 import com.android.vcard.VCardParser_V30;
@@ -399,7 +398,6 @@
             final ContentResolver resolver = ImportVCardActivity.this.getContentResolver();
             VCardEntryCounter counter = null;
             VCardSourceDetector detector = null;
-            VCardInterpreterCollection interpreter = null;
             int vcardVersion = VCARD_VERSION_V21;
             try {
                 boolean shouldUseV30 = false;
@@ -408,9 +406,9 @@
                 try {
                     counter = new VCardEntryCounter();
                     detector = new VCardSourceDetector();
-                    interpreter = new VCardInterpreterCollection(
-                            Arrays.asList(counter, detector));
-                    mVCardParser.parse(is, interpreter);
+                    mVCardParser.addInterpreter(counter);
+                    mVCardParser.addInterpreter(detector);
+                    mVCardParser.parse(is);
                 } catch (VCardVersionException e1) {
                     try {
                         is.close();
@@ -423,9 +421,9 @@
                     try {
                         counter = new VCardEntryCounter();
                         detector = new VCardSourceDetector();
-                        interpreter = new VCardInterpreterCollection(
-                                Arrays.asList(counter, detector));
-                        mVCardParser.parse(is, interpreter);
+                        mVCardParser.addInterpreter(counter);
+                        mVCardParser.addInterpreter(detector);
+                        mVCardParser.parse(is);
                     } catch (VCardVersionException e2) {
                         throw new VCardException("vCard with unspported version.");
                     }
diff --git a/src/com/android/contacts/widget/TextHighlightingAnimation.java b/src/com/android/contacts/widget/TextHighlightingAnimation.java
index 21bbc63..882dd48 100644
--- a/src/com/android/contacts/widget/TextHighlightingAnimation.java
+++ b/src/com/android/contacts/widget/TextHighlightingAnimation.java
@@ -15,6 +15,7 @@
  */
 package com.android.contacts.widget;
 
+import com.android.contacts.format.FormatUtils;
 import com.android.internal.R;
 
 import android.database.CharArrayBuffer;
@@ -72,7 +73,7 @@
             // TODO figure out a way to avoid string allocation
             mString = new String(mText.data, 0, mText.sizeCopied);
 
-            int index = indexOf(baseText, highlightedText);
+            int index = FormatUtils.overlapPoint(baseText, highlightedText);
 
             if (index == 0 || index == -1) {
                 mDimmingEnabled = false;
@@ -83,42 +84,6 @@
             }
         }
 
-        /**
-         * An implementation of indexOf on CharArrayBuffers that finds the first match of
-         * the start of buffer2 in buffer1.  For example, indexOf("abcd", "cdef") == 2
-         */
-        private int indexOf(CharArrayBuffer buffer1, CharArrayBuffer buffer2) {
-            char[] string1 = buffer1.data;
-            char[] string2 = buffer2.data;
-            int count1 = buffer1.sizeCopied;
-            int count2 = buffer2.sizeCopied;
-
-            // Ignore matching tails of the two buffers
-            while (count1 > 0 && count2 > 0 && string1[count1 - 1] == string2[count2 - 1]) {
-                count1--;
-                count2--;
-            }
-
-            int size = count2;
-            for (int i = 0; i < count1; i++) {
-                if (i + size > count1) {
-                    size = count1 - i;
-                }
-                int j;
-                for (j = 0; j < size; j++) {
-                    if (string1[i+j] != string2[j]) {
-                        break;
-                    }
-                }
-                if (j == size) {
-                    return i;
-                }
-            }
-
-            return -1;
-        }
-
-
         @SuppressWarnings("unchecked")
         public <T> T[] getSpans(int start, int end, Class<T> type) {
             if (mDimmingEnabled) {
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
index 8557fd6..bd272c3 100644
--- a/tests/res/values/donottranslate_strings.xml
+++ b/tests/res/values/donottranslate_strings.xml
@@ -32,6 +32,7 @@
         <item>ACTION_PICK: phone (legacy)</item>
         <item>ACTION_PICK: postal</item>
         <item>ACTION_PICK: postal (legacy)</item>
+        <item>ACTION_PICK: e-mail</item>
         <item>ACTION_CREATE_SHORTCUT: contact</item>
         <item>ACTION_CREATE_SHORTCUT: dial</item>
         <item>ACTION_CREATE_SHORTCUT: message</item>
diff --git a/tests/src/com/android/contacts/ContactLoaderTest.java b/tests/src/com/android/contacts/ContactLoaderTest.java
index 503fc64..0e0e2b0 100644
--- a/tests/src/com/android/contacts/ContactLoaderTest.java
+++ b/tests/src/com/android/contacts/ContactLoaderTest.java
@@ -29,10 +29,12 @@
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.StatusUpdates;
 import android.test.LoaderTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
 
 /**
  * Runs ContactLoader tests for the the contact-detail and editor view.
  */
+@LargeTest
 public class ContactLoaderTest extends LoaderTestCase {
     ContactsMockContext mMockContext;
     MockContentProvider mContactsProvider;
@@ -265,7 +267,8 @@
             mContactsProvider.expectQuery(baseUri)
                     .withProjection(new String[] {
                         Contacts.NAME_RAW_CONTACT_ID, Contacts.DISPLAY_NAME_SOURCE,
-                        Contacts.LOOKUP_KEY, Contacts.DISPLAY_NAME, Contacts.PHONETIC_NAME,
+                        Contacts.LOOKUP_KEY, Contacts.DISPLAY_NAME,
+                        Contacts.DISPLAY_NAME_ALTERNATIVE, Contacts.PHONETIC_NAME,
                         Contacts.PHOTO_ID, Contacts.STARRED, Contacts.CONTACT_PRESENCE,
                         Contacts.CONTACT_STATUS, Contacts.CONTACT_STATUS_TIMESTAMP,
                         Contacts.CONTACT_STATUS_RES_PACKAGE, Contacts.CONTACT_STATUS_LABEL,
@@ -299,7 +302,7 @@
                     .withSortOrder(Contacts.Entity.RAW_CONTACT_ID)
                     .returnRow(
                         rawContactId, 40,
-                        "aa%12%@!", "John Doe", "jdo",
+                        "aa%12%@!", "John Doe", "Doe, John", "jdo",
                         0, 0, StatusUpdates.AVAILABLE,
                         "Having lunch", 0,
                         "mockPkg1", 10,
diff --git a/tests/src/com/android/contacts/DialerLaunchPerformance.java b/tests/src/com/android/contacts/DialerLaunchPerformance.java
index ae78082..0803c6b 100644
--- a/tests/src/com/android/contacts/DialerLaunchPerformance.java
+++ b/tests/src/com/android/contacts/DialerLaunchPerformance.java
@@ -32,7 +32,7 @@
         mIntent.setAction(Intent.ACTION_MAIN);
         mIntent.addCategory(Intent.CATEGORY_LAUNCHER);
         mIntent.setComponent(new ComponentName("com.android.contacts",
-                "com.android.contacts.DialtactsActivity"));
+                "com.android.contacts.activities.DialtactsActivity"));
 
         start();
     }
diff --git a/tests/src/com/android/contacts/EntityModifierTests.java b/tests/src/com/android/contacts/EntityModifierTests.java
index 4acaa92..63dd84d 100644
--- a/tests/src/com/android/contacts/EntityModifierTests.java
+++ b/tests/src/com/android/contacts/EntityModifierTests.java
@@ -20,23 +20,31 @@
 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.DataKind;
 import com.android.contacts.model.AccountType.EditType;
 import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.android.contacts.model.EntityDeltaList;
 import com.android.contacts.model.EntityModifier;
+import com.android.contacts.model.ExchangeAccountType;
+import com.android.contacts.model.GoogleAccountType;
+import com.android.contacts.tests.mocks.ContactsMockContext;
 import com.android.contacts.tests.mocks.MockAccountTypeManager;
-import com.google.android.collect.Lists;
+import com.android.contacts.tests.mocks.MockContentProvider;
 
 import android.content.ContentProviderOperation;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Entity;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
 import android.provider.ContactsContract.CommonDataKinds.Im;
 import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
@@ -81,45 +89,54 @@
         MockContactsSource() {
             this.accountType = TEST_ACCOUNT_TYPE;
 
+            final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE,
+                    R.string.nameLabelsGroup, -1, -1, true, -1, -1, -1);
+            nameKind.typeOverallMax = 1;
+            addKind(nameKind);
+
             // Phone allows maximum 2 home, 1 work, and unlimited other, with
             // constraint of 5 numbers maximum.
-            DataKind kind = new DataKind(Phone.CONTENT_ITEM_TYPE, -1, -1, 10, true);
+            final DataKind phoneKind = new DataKind(
+                    Phone.CONTENT_ITEM_TYPE, -1, -1, 10, true, -1, -1, -1);
 
-            kind.typeOverallMax = 5;
-            kind.typeColumn = Phone.TYPE;
-            kind.typeList = Lists.newArrayList();
-            kind.typeList.add(new EditType(Phone.TYPE_HOME, -1).setSpecificMax(2));
-            kind.typeList.add(new EditType(Phone.TYPE_WORK, -1).setSpecificMax(1));
-            kind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, -1).setSecondary(true));
-            kind.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));
 
-            kind.fieldList = Lists.newArrayList();
-            kind.fieldList.add(new EditField(Phone.NUMBER, -1, -1));
-            kind.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(kind);
+            addKind(phoneKind);
 
             // Email is unlimited
-            kind = new DataKind(Email.CONTENT_ITEM_TYPE, -1, -1, 10, true);
-            kind.typeOverallMax = -1;
-            kind.fieldList = Lists.newArrayList();
-            kind.fieldList.add(new EditField(Email.DATA, -1, -1));
-            addKind(kind);
+            final DataKind emailKind = new DataKind(
+                    Email.CONTENT_ITEM_TYPE, -1, -1, 10, true, -1, -1, -1);
+            emailKind.typeOverallMax = -1;
+            emailKind.fieldList = Lists.newArrayList();
+            emailKind.fieldList.add(new EditField(Email.DATA, -1, -1));
+            addKind(emailKind);
 
             // IM is only one
-            kind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, -1, 10, true);
-            kind.typeOverallMax = 1;
-            kind.fieldList = Lists.newArrayList();
-            kind.fieldList.add(new EditField(Im.DATA, -1, -1));
-            addKind(kind);
+            final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, -1, 10,
+                    true, -1, -1, -1);
+            imKind.typeOverallMax = 1;
+            imKind.fieldList = Lists.newArrayList();
+            imKind.fieldList.add(new EditField(Im.DATA, -1, -1));
+            addKind(imKind);
 
             // Organization is only one
-            kind = new DataKind(Organization.CONTENT_ITEM_TYPE, -1, -1, 10, true);
-            kind.typeOverallMax = 1;
-            kind.fieldList = Lists.newArrayList();
-            kind.fieldList.add(new EditField(Organization.COMPANY, -1, -1));
-            kind.fieldList.add(new EditField(Organization.TITLE, -1, -1));
-            addKind(kind);
+            final DataKind orgKind = new DataKind(
+                    Organization.CONTENT_ITEM_TYPE, -1, -1, 10, true, -1, -1, -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);
         }
 
         @Override
@@ -330,7 +347,7 @@
 
         // Test row that has type values, but core fields are empty
         final EntityDelta state = getEntity(TEST_ID);
-        final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
+        EntityModifier.insertChild(state, kindPhone, typeHome);
 
         // Build diff, expecting insert for data row and update enforcement
         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
@@ -420,7 +437,7 @@
     public void testTrimEmptyUntouched() {
         final AccountType source = getAccountType();
         final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
-        final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
+        EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
 
         // Build "before" that has empty row
         final EntityDelta state = getEntity(TEST_ID);
@@ -497,7 +514,7 @@
         final AccountType accountType = getAccountType();
         final AccountTypeManager accountTypes = getAccountTypes(accountType);
         final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
-        final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
+        EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
 
         // Try creating a contact without any child entries
         final EntityDelta state = getEntity(null);
@@ -528,7 +545,7 @@
 
         // Try creating a contact with single empty entry
         final EntityDelta state = getEntity(null);
-        final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
+        EntityModifier.insertChild(state, kindPhone, typeHome);
         final EntityDeltaList set = EntityDeltaList.fromSingle(state);
 
         // Build diff, expecting two insert operations
@@ -746,4 +763,448 @@
         final int count = state.getMimeEntries(Organization.CONTENT_ITEM_TYPE).size();
         assertEquals("Expected to create organization", 1, count);
     }
+
+    public void testMigrateWithDisplayNameFromGoogleToExchange1() {
+        AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+        AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+        DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
+
+        ContactsMockContext context = new ContactsMockContext(getContext());
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+        mockNameValues.put(StructuredName.PREFIX, "prefix");
+        mockNameValues.put(StructuredName.GIVEN_NAME, "given");
+        mockNameValues.put(StructuredName.MIDDLE_NAME, "middle");
+        mockNameValues.put(StructuredName.FAMILY_NAME, "family");
+        mockNameValues.put(StructuredName.SUFFIX, "suffix");
+        mockNameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "PHONETIC_FAMILY");
+        mockNameValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "PHONETIC_MIDDLE");
+        mockNameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "PHONETIC_GIVEN");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migrateStructuredName(context, oldState, newState, kind);
+        List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE);
+        assertEquals(1, list.size());
+
+        ContentValues output = list.get(0).getAfter();
+        assertEquals("prefix", output.getAsString(StructuredName.PREFIX));
+        assertEquals("given", output.getAsString(StructuredName.GIVEN_NAME));
+        assertEquals("middle", output.getAsString(StructuredName.MIDDLE_NAME));
+        assertEquals("family", output.getAsString(StructuredName.FAMILY_NAME));
+        assertEquals("suffix", output.getAsString(StructuredName.SUFFIX));
+        // Phonetic middle name isn't supported by Exchange.
+        assertEquals("PHONETIC_FAMILY", output.getAsString(StructuredName.PHONETIC_FAMILY_NAME));
+        assertEquals("PHONETIC_GIVEN", output.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
+    }
+
+    public void testMigrateWithDisplayNameFromGoogleToExchange2() {
+        AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+        AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+        DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
+
+        ContactsMockContext context = new ContactsMockContext(getContext());
+        MockContentProvider provider = context.getContactsProvider();
+
+        String inputDisplayName = "prefix given middle family suffix";
+        // The method will ask the provider to split/join StructuredName.
+        Uri uriForBuildDisplayName =
+                ContactsContract.AUTHORITY_URI
+                        .buildUpon()
+                        .appendPath("complete_name")
+                        .appendQueryParameter(StructuredName.DISPLAY_NAME, inputDisplayName)
+                        .build();
+        provider.expectQuery(uriForBuildDisplayName)
+                .returnRow("prefix", "given", "middle", "family", "suffix")
+                .withProjection(StructuredName.PREFIX, StructuredName.GIVEN_NAME,
+                        StructuredName.MIDDLE_NAME, StructuredName.FAMILY_NAME,
+                        StructuredName.SUFFIX);
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+        mockNameValues.put(StructuredName.DISPLAY_NAME, inputDisplayName);
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migrateStructuredName(context, oldState, newState, kind);
+        List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE);
+        assertEquals(1, list.size());
+
+        ContentValues outputValues = list.get(0).getAfter();
+        assertEquals("prefix", outputValues.getAsString(StructuredName.PREFIX));
+        assertEquals("given", outputValues.getAsString(StructuredName.GIVEN_NAME));
+        assertEquals("middle", outputValues.getAsString(StructuredName.MIDDLE_NAME));
+        assertEquals("family", outputValues.getAsString(StructuredName.FAMILY_NAME));
+        assertEquals("suffix", outputValues.getAsString(StructuredName.SUFFIX));
+    }
+
+    public void testMigrateWithStructuredNameFromExchangeToGoogle() {
+        AccountType oldAccountType = new ExchangeAccountType(getContext(), "");
+        AccountType newAccountType = new GoogleAccountType(getContext(), "");
+        DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
+
+        ContactsMockContext context = new ContactsMockContext(getContext());
+        MockContentProvider provider = context.getContactsProvider();
+
+        // The method will ask the provider to split/join StructuredName.
+        Uri uriForBuildDisplayName =
+                ContactsContract.AUTHORITY_URI
+                        .buildUpon()
+                        .appendPath("complete_name")
+                        .appendQueryParameter(StructuredName.PREFIX, "prefix")
+                        .appendQueryParameter(StructuredName.GIVEN_NAME, "given")
+                        .appendQueryParameter(StructuredName.MIDDLE_NAME, "middle")
+                        .appendQueryParameter(StructuredName.FAMILY_NAME, "family")
+                        .appendQueryParameter(StructuredName.SUFFIX, "suffix")
+                        .build();
+        provider.expectQuery(uriForBuildDisplayName)
+                .returnRow("prefix given middle family suffix")
+                .withProjection(StructuredName.DISPLAY_NAME);
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+        mockNameValues.put(StructuredName.PREFIX, "prefix");
+        mockNameValues.put(StructuredName.GIVEN_NAME, "given");
+        mockNameValues.put(StructuredName.MIDDLE_NAME, "middle");
+        mockNameValues.put(StructuredName.FAMILY_NAME, "family");
+        mockNameValues.put(StructuredName.SUFFIX, "suffix");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migrateStructuredName(context, oldState, newState, kind);
+
+        List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE);
+        assertNotNull(list);
+        assertEquals(1, list.size());
+        ContentValues outputValues = list.get(0).getAfter();
+        assertEquals("prefix given middle family suffix",
+                outputValues.getAsString(StructuredName.DISPLAY_NAME));
+    }
+
+    public void testMigratePostalFromGoogleToExchange() {
+        AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+        AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+        DataKind kind = newAccountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
+        mockNameValues.put(StructuredPostal.FORMATTED_ADDRESS, "formatted_address");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migratePostal(oldState, newState, kind);
+
+        List<ValuesDelta> list = newState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE);
+        assertNotNull(list);
+        assertEquals(1, list.size());
+        ContentValues outputValues = list.get(0).getAfter();
+        // FORMATTED_ADDRESS isn't supported by Exchange.
+        assertNull(outputValues.getAsString(StructuredPostal.FORMATTED_ADDRESS));
+        assertEquals("formatted_address", outputValues.getAsString(StructuredPostal.STREET));
+    }
+
+    public void testMigratePostalFromExchangeToGoogle() {
+        AccountType oldAccountType = new ExchangeAccountType(getContext(), "");
+        AccountType newAccountType = new GoogleAccountType(getContext(), "");
+        DataKind kind = newAccountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
+        mockNameValues.put(StructuredPostal.COUNTRY, "country");
+        mockNameValues.put(StructuredPostal.POSTCODE, "postcode");
+        mockNameValues.put(StructuredPostal.REGION, "region");
+        mockNameValues.put(StructuredPostal.CITY, "city");
+        mockNameValues.put(StructuredPostal.STREET, "street");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migratePostal(oldState, newState, kind);
+
+        List<ValuesDelta> list = newState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE);
+        assertNotNull(list);
+        assertEquals(1, list.size());
+        ContentValues outputValues = list.get(0).getAfter();
+
+        // Check FORMATTED_ADDRESS contains all info.
+        String formattedAddress = outputValues.getAsString(StructuredPostal.FORMATTED_ADDRESS);
+        assertNotNull(formattedAddress);
+        assertTrue(formattedAddress.contains("country"));
+        assertTrue(formattedAddress.contains("postcode"));
+        assertTrue(formattedAddress.contains("region"));
+        assertTrue(formattedAddress.contains("postcode"));
+        assertTrue(formattedAddress.contains("city"));
+        assertTrue(formattedAddress.contains("street"));
+    }
+
+    public void testMigrateEventFromGoogleToExchange1() {
+        testMigrateEventCommon(new GoogleAccountType(getContext(), ""),
+                new ExchangeAccountType(getContext(), ""));
+    }
+
+    public void testMigrateEventFromExchangeToGoogle() {
+        testMigrateEventCommon(new ExchangeAccountType(getContext(), ""),
+                new GoogleAccountType(getContext(), ""));
+    }
+
+    private void testMigrateEventCommon(AccountType oldAccountType, AccountType newAccountType) {
+        DataKind kind = newAccountType.getKindForMimetype(Event.CONTENT_ITEM_TYPE);
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Event.START_DATE, "1972-02-08");
+        mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migrateEvent(oldState, newState, kind, 1990);
+
+        List<ValuesDelta> list = newState.getMimeEntries(Event.CONTENT_ITEM_TYPE);
+        assertNotNull(list);
+        assertEquals(1, list.size());  // Anniversary should be dropped.
+        ContentValues outputValues = list.get(0).getAfter();
+
+        assertEquals("1972-02-08", outputValues.getAsString(Event.START_DATE));
+        assertEquals(Event.TYPE_BIRTHDAY, outputValues.getAsInteger(Event.TYPE).intValue());
+    }
+
+    public void testMigrateEventFromGoogleToExchange2() {
+        AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+        AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+        DataKind kind = newAccountType.getKindForMimetype(Event.CONTENT_ITEM_TYPE);
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
+        // No year format is not supported by Exchange.
+        mockNameValues.put(Event.START_DATE, "--06-01");
+        mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+        mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Event.START_DATE, "1980-08-02");
+        // Anniversary is not supported by Exchange
+        mockNameValues.put(Event.TYPE, Event.TYPE_ANNIVERSARY);
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migrateEvent(oldState, newState, kind, 1990);
+
+        List<ValuesDelta> list = newState.getMimeEntries(Event.CONTENT_ITEM_TYPE);
+        assertNotNull(list);
+        assertEquals(1, list.size());  // Anniversary should be dropped.
+        ContentValues outputValues = list.get(0).getAfter();
+
+        // Default year should be used.
+        assertEquals("1990-06-01", outputValues.getAsString(Event.START_DATE));
+        assertEquals(Event.TYPE_BIRTHDAY, outputValues.getAsInteger(Event.TYPE).intValue());
+    }
+
+    public void testMigrateEmailFromGoogleToExchange() {
+        AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+        AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+        DataKind kind = newAccountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE);
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Email.TYPE, Email.TYPE_CUSTOM);
+        mockNameValues.put(Email.LABEL, "custom_type");
+        mockNameValues.put(Email.ADDRESS, "address1");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+        mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Email.TYPE, Email.TYPE_HOME);
+        mockNameValues.put(Email.ADDRESS, "address2");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+        mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Email.TYPE, Email.TYPE_WORK);
+        mockNameValues.put(Email.ADDRESS, "address3");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+        // Exchange can have up to 3 email entries. This 4th entry should be dropped.
+        mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Email.TYPE, Email.TYPE_OTHER);
+        mockNameValues.put(Email.ADDRESS, "address4");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migrateGenericWithTypeColumn(oldState, newState, kind);
+
+        List<ValuesDelta> list = newState.getMimeEntries(Email.CONTENT_ITEM_TYPE);
+        assertNotNull(list);
+        assertEquals(3, list.size());
+
+        ContentValues outputValues = list.get(0).getAfter();
+        assertEquals(Email.TYPE_CUSTOM, outputValues.getAsInteger(Email.TYPE).intValue());
+        assertEquals("custom_type", outputValues.getAsString(Email.LABEL));
+        assertEquals("address1", outputValues.getAsString(Email.ADDRESS));
+
+        outputValues = list.get(1).getAfter();
+        assertEquals(Email.TYPE_HOME, outputValues.getAsInteger(Email.TYPE).intValue());
+        assertEquals("address2", outputValues.getAsString(Email.ADDRESS));
+
+        outputValues = list.get(2).getAfter();
+        assertEquals(Email.TYPE_WORK, outputValues.getAsInteger(Email.TYPE).intValue());
+        assertEquals("address3", outputValues.getAsString(Email.ADDRESS));
+    }
+
+    public void testMigrateImFromGoogleToExchange() {
+        AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+        AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+        DataKind kind = newAccountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE);
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+        // Exchange doesn't support TYPE_HOME
+        mockNameValues.put(Im.TYPE, Im.TYPE_HOME);
+        mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_JABBER);
+        mockNameValues.put(Im.DATA, "im1");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+        // Exchange doesn't support TYPE_WORK
+        mockNameValues.put(Im.TYPE, Im.TYPE_WORK);
+        mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_YAHOO);
+        mockNameValues.put(Im.DATA, "im2");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Im.TYPE, Im.TYPE_OTHER);
+        mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
+        mockNameValues.put(Im.CUSTOM_PROTOCOL, "custom_protocol");
+        mockNameValues.put(Im.DATA, "im3");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        // Exchange can have up to 3 IM entries. This 4th entry should be dropped.
+        mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Im.TYPE, Im.TYPE_OTHER);
+        mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
+        mockNameValues.put(Im.DATA, "im4");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migrateGenericWithTypeColumn(oldState, newState, kind);
+
+        List<ValuesDelta> list = newState.getMimeEntries(Im.CONTENT_ITEM_TYPE);
+        assertNotNull(list);
+        assertEquals(3, list.size());
+
+        assertNotNull(kind.defaultValues.getAsInteger(Im.TYPE));
+
+        int defaultType = kind.defaultValues.getAsInteger(Im.TYPE);
+
+        ContentValues outputValues = list.get(0).getAfter();
+        // HOME should become default type.
+        assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue());
+        assertEquals(Im.PROTOCOL_JABBER, outputValues.getAsInteger(Im.PROTOCOL).intValue());
+        assertEquals("im1", outputValues.getAsString(Im.DATA));
+
+        outputValues = list.get(1).getAfter();
+        assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue());
+        assertEquals(Im.PROTOCOL_YAHOO, outputValues.getAsInteger(Im.PROTOCOL).intValue());
+        assertEquals("im2", outputValues.getAsString(Im.DATA));
+
+        outputValues = list.get(2).getAfter();
+        assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue());
+        assertEquals(Im.PROTOCOL_CUSTOM, outputValues.getAsInteger(Im.PROTOCOL).intValue());
+        assertEquals("custom_protocol", outputValues.getAsString(Im.CUSTOM_PROTOCOL));
+        assertEquals("im3", outputValues.getAsString(Im.DATA));
+    }
+
+    public void testMigratePhoneFromGoogleToExchange() {
+        AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+        AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+        DataKind kind = newAccountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Phone.TYPE, Phone.TYPE_HOME);
+        mockNameValues.put(Phone.NUMBER, "1");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+        mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Phone.TYPE, Phone.TYPE_MOBILE);
+        mockNameValues.put(Phone.NUMBER, "2");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+        mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        // Exchange doesn't support this type. Default to HOME
+        mockNameValues.put(Phone.TYPE, Phone.TYPE_CUSTOM);
+        mockNameValues.put(Phone.LABEL, "custom_type");
+        mockNameValues.put(Phone.NUMBER, "3");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+        mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK);
+        mockNameValues.put(Phone.NUMBER, "4");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+        mockNameValues = new ContentValues();
+
+        // This field should be ignored, as Exchange only allows 2 HOME phone numbers while we
+        // already have that number of HOME phones.
+        mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK_MOBILE);
+        mockNameValues.put(Phone.NUMBER, "5");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migrateGenericWithTypeColumn(oldState, newState, kind);
+
+        List<ValuesDelta> list = newState.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
+        assertNotNull(list);
+        assertEquals(4, list.size());
+
+        int defaultType = kind.typeList.get(0).rawValue;
+
+        ContentValues outputValues = list.get(0).getAfter();
+        assertEquals(Phone.TYPE_HOME, outputValues.getAsInteger(Phone.TYPE).intValue());
+        assertEquals("1", outputValues.getAsString(Phone.NUMBER));
+        outputValues = list.get(1).getAfter();
+        assertEquals(Phone.TYPE_MOBILE, outputValues.getAsInteger(Phone.TYPE).intValue());
+        assertEquals("2", outputValues.getAsString(Phone.NUMBER));
+        outputValues = list.get(2).getAfter();
+        assertEquals(defaultType, outputValues.getAsInteger(Phone.TYPE).intValue());
+        assertNull(outputValues.getAsInteger(Phone.LABEL));
+        assertEquals("3", outputValues.getAsString(Phone.NUMBER));
+        outputValues = list.get(3).getAfter();
+        assertEquals(Phone.TYPE_WORK, outputValues.getAsInteger(Phone.TYPE).intValue());
+        assertEquals("4", outputValues.getAsString(Phone.NUMBER));
+    }
+
+    public void testMigrateOrganizationFromGoogleToExchange() {
+        AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+        AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+        DataKind kind = newAccountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE);
+
+        EntityDelta oldState = new EntityDelta();
+        ContentValues mockNameValues = new ContentValues();
+        mockNameValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
+        mockNameValues.put(Organization.COMPANY, "company1");
+        mockNameValues.put(Organization.DEPARTMENT, "department1");
+        oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+        EntityDelta newState = new EntityDelta();
+        EntityModifier.migrateGenericWithoutTypeColumn(oldState, newState, kind);
+
+        List<ValuesDelta> list = newState.getMimeEntries(Organization.CONTENT_ITEM_TYPE);
+        assertNotNull(list);
+        assertEquals(1, list.size());
+
+        ContentValues outputValues = list.get(0).getAfter();
+        assertEquals("company1", outputValues.getAsString(Organization.COMPANY));
+        assertEquals("department1", outputValues.getAsString(Organization.DEPARTMENT));
+    }
 }
diff --git a/tests/src/com/android/contacts/RecentCallsListActivityTests.java b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
similarity index 92%
rename from tests/src/com/android/contacts/RecentCallsListActivityTests.java
rename to tests/src/com/android/contacts/activities/CallLogActivityTests.java
index 3c2a9fe..ef1afd0 100644
--- a/tests/src/com/android/contacts/RecentCallsListActivityTests.java
+++ b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
@@ -14,23 +14,22 @@
  * limitations under the License.
  */
 
-package com.android.contacts;
+package com.android.contacts.activities;
+
+import com.android.contacts.calllog.CallLogFragment;
+import com.android.internal.telephony.CallerInfo;
 
 import android.content.res.Resources;
-import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
 import android.provider.CallLog.Calls;
 import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
 import android.view.View;
 import android.widget.FrameLayout;
-import com.android.contacts.RecentCallsListActivity;
-import com.android.internal.telephony.CallerInfo;
+
 import java.util.Date;
 import java.util.Formatter;
 import java.util.HashMap;
@@ -46,10 +45,10 @@
  *   adb shell am instrument \
  *     -w com.android.contacts.tests/android.test.InstrumentationTestRunner
  */
-
-public class RecentCallsListActivityTests
-        extends ActivityInstrumentationTestCase2<RecentCallsListActivity> {
-    static private final String TAG = "RecentCallsListActivityTests";
+@LargeTest
+public class CallLogActivityTests
+        extends ActivityInstrumentationTestCase2<CallLogActivity> {
+    static private final String TAG = "CallLogActivityTests";
     static private final String[] CALL_LOG_PROJECTION = new String[] {
             Calls._ID,
             Calls.NUMBER,
@@ -66,11 +65,12 @@
 
     // We get the call list activity and assign is a frame to build
     // its list.  mAdapter is an inner class of
-    // RecentCallsListActivity to build the rows (view) in the call
+    // CallLogActivity to build the rows (view) in the call
     // list. We reuse it with our own in-mem DB.
-    private RecentCallsListActivity mActivity;
+    private CallLogActivity mActivity;
+    private CallLogFragment mFragment;
     private FrameLayout mParentView;
-    private RecentCallsListActivity.RecentCallsAdapter mAdapter;
+    private CallLogFragment.CallLogAdapter mAdapter;
     private String mVoicemail;
 
     // In memory array to hold the rows corresponding to the 'calls' table.
@@ -85,13 +85,13 @@
     private HashMap<Integer, Bitmap> mCallTypeIcons;
 
     // An item in the call list. All the methods performing checks use it.
-    private RecentCallsListActivity.RecentCallsListItemViews mItem;
+    private CallLogFragment.CallLogListItemViews mItem;
     // The list of views representing the data in the DB. View are in
     // reverse order compare to the DB.
     private View[] mList;
 
-    public RecentCallsListActivityTests() {
-        super("com.android.contacts", RecentCallsListActivity.class);
+    public CallLogActivityTests() {
+        super("com.android.contacts", CallLogActivity.class);
         mIndex = 1;
         mRnd = new Random();
     }
@@ -99,8 +99,9 @@
     @Override
     public void setUp() {
         mActivity = getActivity();
-        mVoicemail = mActivity.mVoiceMailNumber;
-        mAdapter = mActivity.mAdapter;
+        mFragment = mActivity.getFragment();
+        mVoicemail = mFragment.getVoiceMailNumber();
+        mAdapter = mFragment.getAdapter();
         mParentView = new FrameLayout(mActivity);
         mCursor = new MatrixCursor(CALL_LOG_PROJECTION);
         buildIconMap();
@@ -170,7 +171,7 @@
             if (null == mList[i]) {
                 break;
             }
-            mItem = (RecentCallsListActivity.RecentCallsListItemViews) mList[i].getTag();
+            mItem = (CallLogFragment.CallLogListItemViews) mList[i].getTag();
 
             // callView tag is the phone number.
             String number = (String) mItem.callView.getTag();
diff --git a/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java b/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
index 912950f..d165963 100644
--- a/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
+++ b/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts.activities;
 
+import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.ContactsApplication;
 import com.android.contacts.R;
 import com.android.contacts.detail.ContactDetailFragment;
@@ -26,9 +27,11 @@
 import com.android.contacts.test.InjectedServices;
 import com.android.contacts.tests.mocks.ContactsMockContext;
 import com.android.contacts.tests.mocks.MockAccountTypeManager;
+import com.android.contacts.tests.mocks.MockContactPhotoManager;
 import com.android.contacts.tests.mocks.MockContentProvider;
 import com.android.contacts.tests.mocks.MockContentProvider.Query;
 import com.android.contacts.tests.mocks.MockSharedPreferences;
+import com.android.contacts.util.PhoneCapabilityTester;
 
 import android.accounts.Account;
 import android.content.ContentValues;
@@ -85,7 +88,8 @@
         InjectedServices services = new InjectedServices();
         services.setContentResolver(mContext.getContentResolver());
         services.setSharedPreferences(new MockSharedPreferences());
-
+        services.setSystemService(ContactPhotoManager.CONTACT_PHOTO_SERVICE,
+                new MockContactPhotoManager());
         AccountType accountType = new BaseAccountType();
         accountType.accountType = TEST_ACCOUNT_TYPE;
 
@@ -98,6 +102,10 @@
     }
 
     public void testSingleAccountNoGroups() {
+        // This two-pane UI test only makes sense if we run with two panes.
+        // Let's ignore this in the single pane case
+        if (!PhoneCapabilityTester.isUsingTwoPanes(mContext)) return;
+
         expectSettingsQueriesAndReturnDefault();
         expectProviderStatusQueryAndReturnNormal();
         expectGroupsQueryAndReturnEmpty();
diff --git a/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java b/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
index 4cee32c..cfab94a 100644
--- a/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
+++ b/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
@@ -16,7 +16,7 @@
 
 package com.android.contacts.detail;
 
-import com.android.contacts.detail.ContactDetailFragment.ViewEntry;
+import com.android.contacts.detail.ContactDetailFragment.DetailViewEntry;
 
 import android.content.ContentValues;
 import android.content.Intent;
@@ -42,7 +42,7 @@
         values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
         values.put(Im.DATA, TEST_ADDRESS);
 
-        ViewEntry entry = new ContactDetailFragment.ViewEntry();
+        DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
         ContactDetailFragment.buildImActions(entry, values);
         assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
         assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
@@ -59,7 +59,7 @@
         values.put(Im.DATA, TEST_ADDRESS);
         values.put(Im.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO);
 
-        ViewEntry entry = new ContactDetailFragment.ViewEntry();
+        DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
         ContactDetailFragment.buildImActions(entry, values);
         assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
         assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
@@ -78,7 +78,7 @@
         values.put(Im.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO |
                 Im.CAPABILITY_HAS_VOICE);
 
-        ViewEntry entry = new ContactDetailFragment.ViewEntry();
+        DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
         ContactDetailFragment.buildImActions(entry, values);
         assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
         assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
@@ -97,7 +97,7 @@
         values.put(Im.CUSTOM_PROTOCOL, TEST_PROTOCOL);
         values.put(Im.DATA, TEST_ADDRESS);
 
-        ViewEntry entry = new ContactDetailFragment.ViewEntry();
+        DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
         ContactDetailFragment.buildImActions(entry, values);
         assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
 
@@ -120,7 +120,7 @@
         values.put(Email.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO |
                 Im.CAPABILITY_HAS_VOICE);
 
-        ViewEntry entry = new ContactDetailFragment.ViewEntry();
+        DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
         ContactDetailFragment.buildImActions(entry, values);
         assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
         assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
diff --git a/tests/src/com/android/contacts/format/FormatUtilsTests.java b/tests/src/com/android/contacts/format/FormatUtilsTests.java
new file mode 100644
index 0000000..0464adb
--- /dev/null
+++ b/tests/src/com/android/contacts/format/FormatUtilsTests.java
@@ -0,0 +1,35 @@
+/*
+ * 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.format;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Test cases for format utility methods.
+ */
+@SmallTest
+public class FormatUtilsTests extends AndroidTestCase {
+
+    public void testOverlapPoint() throws Exception {
+        assertEquals(2, FormatUtils.overlapPoint("abcde", "cdefg"));
+        assertEquals(-1, FormatUtils.overlapPoint("John Doe", "John Doe"));
+        assertEquals(5, FormatUtils.overlapPoint("John Doe", "Doe, John"));
+        assertEquals(-1, FormatUtils.overlapPoint("Mr. John Doe", "Mr. Doe, John"));
+        assertEquals(13, FormatUtils.overlapPoint("John Herbert Doe", "Doe, John Herbert"));
+    }
+}
diff --git a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
index 43e844e..c401a8c 100644
--- a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
+++ b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
@@ -124,6 +124,26 @@
         assertEquals("sms:456", interaction.startedIntent.getDataString());
     }
 
+    public void testShouldCollapseWith() {
+        PhoneNumberInteraction.PhoneItem phoneItem1 = new PhoneNumberInteraction.PhoneItem();
+        PhoneNumberInteraction.PhoneItem phoneItem2 = new PhoneNumberInteraction.PhoneItem();
+
+        phoneItem1.phoneNumber = "123";
+        phoneItem2.phoneNumber = "123";
+
+        assertTrue(phoneItem1.shouldCollapseWith(phoneItem2));
+
+        phoneItem1.phoneNumber = "123";
+        phoneItem2.phoneNumber = "456";
+
+        assertFalse(phoneItem1.shouldCollapseWith(phoneItem2));
+
+        phoneItem1.phoneNumber = "123#,123";
+        phoneItem2.phoneNumber = "123#,456";
+
+        assertFalse(phoneItem1.shouldCollapseWith(phoneItem2));
+    }
+
     public void testCallNumberWhenThereAreDuplicates() {
         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
         expectQuery(contactUri)
diff --git a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
index 50a1f1f..03b6302 100644
--- a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
+++ b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
@@ -73,6 +73,7 @@
         ACTION_PICK_PHONE_LEGACY,
         ACTION_PICK_POSTAL,
         ACTION_PICK_POSTAL_LEGACY,
+        ACTION_PICK_EMAIL,
         ACTION_CREATE_SHORTCUT_CONTACT,
         ACTION_CREATE_SHORTCUT_DIAL,
         ACTION_CREATE_SHORTCUT_MESSAGE,
@@ -195,6 +196,11 @@
                 startContactSelectionActivityForResult(intent);
                 break;
             }
+            case ACTION_PICK_EMAIL: {
+                startContactSelectionActivityForResult(
+                        new Intent(Intent.ACTION_PICK, Email.CONTENT_URI));
+                break;
+            }
             case ACTION_CREATE_SHORTCUT_CONTACT: {
                 Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
                 startContactSelectionActivityForResult(intent);
diff --git a/tests/src/com/android/contacts/tests/mocks/MockContactPhotoManager.java b/tests/src/com/android/contacts/tests/mocks/MockContactPhotoManager.java
new file mode 100644
index 0000000..6da205b
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/mocks/MockContactPhotoManager.java
@@ -0,0 +1,54 @@
+/*
+ * 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.tests.mocks;
+
+import com.android.contacts.ContactPhotoManager;
+
+import android.net.Uri;
+import android.widget.ImageView;
+
+/**
+ * A photo preloader that always uses the "no contact" picture and never executes any real
+ * db queries
+ */
+public class MockContactPhotoManager extends ContactPhotoManager {
+    @Override
+    public void loadPhoto(ImageView view, long photoId) {
+        view.setImageResource(mDefaultResourceId);
+    }
+
+    @Override
+    public void loadPhoto(ImageView view, Uri photoUri) {
+        view.setImageResource(mDefaultResourceId);
+    }
+
+    @Override
+    public void pause() {
+    }
+
+    @Override
+    public void resume() {
+    }
+
+    @Override
+    public void refreshCache() {
+    }
+
+    @Override
+    public void preloadPhotosInBackground() {
+    }
+}
diff --git a/tests/src/com/android/contacts/util/NameConverterTests.java b/tests/src/com/android/contacts/util/NameConverterTests.java
new file mode 100644
index 0000000..e1773a7
--- /dev/null
+++ b/tests/src/com/android/contacts/util/NameConverterTests.java
@@ -0,0 +1,99 @@
+/*
+ * 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 android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tests for {@link NameConverter}.
+ */
+@SmallTest
+public class NameConverterTests extends AndroidTestCase {
+
+    public void testStructuredNameToDisplayName() {
+        Map<String, String> structuredName = new HashMap<String, String>();
+        structuredName.put(StructuredName.PREFIX, "Mr.");
+        structuredName.put(StructuredName.GIVEN_NAME, "John");
+        structuredName.put(StructuredName.MIDDLE_NAME, "Quincy");
+        structuredName.put(StructuredName.FAMILY_NAME, "Adams");
+        structuredName.put(StructuredName.SUFFIX, "Esquire");
+
+        assertEquals("Mr. John Quincy Adams, Esquire",
+                NameConverter.structuredNameToDisplayName(mContext, structuredName));
+
+        structuredName.remove(StructuredName.SUFFIX);
+        assertEquals("Mr. John Quincy Adams",
+                NameConverter.structuredNameToDisplayName(mContext, structuredName));
+
+        structuredName.remove(StructuredName.MIDDLE_NAME);
+        assertEquals("Mr. John Adams",
+                NameConverter.structuredNameToDisplayName(mContext, structuredName));
+    }
+
+    public void testDisplayNameToStructuredName() {
+        assertStructuredName("Mr. John Quincy Adams, Esquire",
+                "Mr.", "John", "Quincy", "Adams", "Esquire");
+        assertStructuredName("John Doe", null, "John", null, "Doe", null);
+        assertStructuredName("Ms. Jane Eyre", "Ms.", "Jane", null, "Eyre", null);
+        assertStructuredName("Dr Leo Spaceman, PhD", "Dr", "Leo", null, "Spaceman", "PhD");
+    }
+
+    /**
+     * Helper method to check whether a given display name parses out to the other parameters.
+     * @param displayName Display name to break into a structured name.
+     * @param prefix Expected prefix (null if not expected).
+     * @param givenName Expected given name (null if not expected).
+     * @param middleName Expected middle name (null if not expected).
+     * @param familyName Expected family name (null if not expected).
+     * @param suffix Expected suffix (null if not expected).
+     */
+    private void assertStructuredName(String displayName, String prefix,
+            String givenName, String middleName, String familyName, String suffix) {
+        Map<String, String> structuredName = NameConverter.displayNameToStructuredName(mContext,
+                displayName);
+        checkNameComponent(StructuredName.PREFIX, prefix, structuredName);
+        checkNameComponent(StructuredName.GIVEN_NAME, givenName, structuredName);
+        checkNameComponent(StructuredName.MIDDLE_NAME, middleName, structuredName);
+        checkNameComponent(StructuredName.FAMILY_NAME, familyName, structuredName);
+        checkNameComponent(StructuredName.SUFFIX, suffix, structuredName);
+        assertEquals(0, structuredName.size());
+    }
+
+    /**
+     * Checks that the given field and value are present in the structured name map (or not present
+     * if the given value is null).  If the value is present and matches, the key is removed from
+     * the map - once all components of the name are checked, the map should be empty.
+     * @param field Field to check.
+     * @param value Expected value for the field (null if it is not expected to be populated).
+     * @param structuredName The map of structured field names to values.
+     */
+    private void checkNameComponent(String field, String value,
+            Map<String, String> structuredName) {
+        if (TextUtils.isEmpty(value)) {
+            assertNull(structuredName.get(field));
+        } else {
+            assertEquals(value, structuredName.get(field));
+        }
+        structuredName.remove(field);
+    }
+}