Merge "Clear missed call notification."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d18515e..95edf0f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -599,17 +599,23 @@
             android:configChanges="orientation|screenSize|keyboardHidden"
             android:theme="@style/BackgroundOnly">
             <intent-filter>
-                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
-                <data android:mimeType="text/x-vcard" />
-                <data android:mimeType="text/x-vCard" />
-                <data android:mimeType="text/vcard" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-            <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <data android:mimeType="text/directory" />
                 <data android:mimeType="text/vcard" />
                 <data android:mimeType="text/x-vcard" />
+                <data android:mimeType="text/x-vCard" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".vcard.NfcImportVCardActivity"
+            android:configChanges="orientation|screenSize|keyboardHidden"
+            android:theme="@style/BackgroundOnly">
+            <intent-filter>
+                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
+                <data android:mimeType="text/vcard" />
+                <data android:mimeType="text/x-vcard" />
+                <data android:mimeType="text/x-vCard" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
diff --git a/res/drawable-hdpi/list_activated_holo.9.png b/res/drawable-hdpi/list_activated_holo.9.png
index 7b89319..046b24a 100644
--- a/res/drawable-hdpi/list_activated_holo.9.png
+++ b/res/drawable-hdpi/list_activated_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_background_holo.9.png b/res/drawable-hdpi/list_background_holo.9.png
index e382b2d..cddf9be 100644
--- a/res/drawable-hdpi/list_background_holo.9.png
+++ b/res/drawable-hdpi/list_background_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_selected_focused_holo.9.png b/res/drawable-hdpi/tab_selected_focused_holo.9.png
new file mode 100644
index 0000000..feaaa2d
--- /dev/null
+++ b/res/drawable-hdpi/tab_selected_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_selected_holo.9.png b/res/drawable-hdpi/tab_selected_holo.9.png
new file mode 100644
index 0000000..b939b5d
--- /dev/null
+++ b/res/drawable-hdpi/tab_selected_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_selected_pressed_holo.9.png b/res/drawable-hdpi/tab_selected_pressed_holo.9.png
new file mode 100644
index 0000000..ccb7342
--- /dev/null
+++ b/res/drawable-hdpi/tab_selected_pressed_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_unselected_focused_holo.9.png b/res/drawable-hdpi/tab_unselected_focused_holo.9.png
new file mode 100644
index 0000000..4f59d32
--- /dev/null
+++ b/res/drawable-hdpi/tab_unselected_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_unselected_holo.9.png b/res/drawable-hdpi/tab_unselected_holo.9.png
new file mode 100644
index 0000000..de9f4f2
--- /dev/null
+++ b/res/drawable-hdpi/tab_unselected_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_unselected_pressed_holo.9.png b/res/drawable-hdpi/tab_unselected_pressed_holo.9.png
new file mode 100644
index 0000000..8e706b0
--- /dev/null
+++ b/res/drawable-hdpi/tab_unselected_pressed_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_activated_holo.9.png b/res/drawable-mdpi/list_activated_holo.9.png
index 4d98919..1ff3373 100644
--- a/res/drawable-mdpi/list_activated_holo.9.png
+++ b/res/drawable-mdpi/list_activated_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_background_holo.9.png b/res/drawable-mdpi/list_background_holo.9.png
index ca2f750..7d5d66d 100644
--- a/res/drawable-mdpi/list_background_holo.9.png
+++ b/res/drawable-mdpi/list_background_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_selected_focused_holo.9.png b/res/drawable-mdpi/tab_selected_focused_holo.9.png
new file mode 100644
index 0000000..ffe76c7
--- /dev/null
+++ b/res/drawable-mdpi/tab_selected_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_selected_holo.9.png b/res/drawable-mdpi/tab_selected_holo.9.png
new file mode 100644
index 0000000..6ccbd47
--- /dev/null
+++ b/res/drawable-mdpi/tab_selected_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_selected_pressed_holo.9.png b/res/drawable-mdpi/tab_selected_pressed_holo.9.png
new file mode 100644
index 0000000..834b1f5
--- /dev/null
+++ b/res/drawable-mdpi/tab_selected_pressed_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_unselected_focused_holo.9.png b/res/drawable-mdpi/tab_unselected_focused_holo.9.png
new file mode 100644
index 0000000..9cf9398
--- /dev/null
+++ b/res/drawable-mdpi/tab_unselected_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_unselected_holo.9.png b/res/drawable-mdpi/tab_unselected_holo.9.png
new file mode 100644
index 0000000..5834c1f
--- /dev/null
+++ b/res/drawable-mdpi/tab_unselected_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_unselected_pressed_holo.9.png b/res/drawable-mdpi/tab_unselected_pressed_holo.9.png
new file mode 100644
index 0000000..b205bc7
--- /dev/null
+++ b/res/drawable-mdpi/tab_unselected_pressed_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_activated_holo.9.png b/res/drawable-xhdpi/list_activated_holo.9.png
index 4e13add..2eb7c7e 100644
--- a/res/drawable-xhdpi/list_activated_holo.9.png
+++ b/res/drawable-xhdpi/list_activated_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_background_holo.9.png b/res/drawable-xhdpi/list_background_holo.9.png
index adba6a8..b652725 100644
--- a/res/drawable-xhdpi/list_background_holo.9.png
+++ b/res/drawable-xhdpi/list_background_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_focused_holo.9.png b/res/drawable-xhdpi/tab_selected_focused_holo.9.png
new file mode 100644
index 0000000..8bb6ec5
--- /dev/null
+++ b/res/drawable-xhdpi/tab_selected_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_holo.9.png b/res/drawable-xhdpi/tab_selected_holo.9.png
new file mode 100644
index 0000000..81d5b34
--- /dev/null
+++ b/res/drawable-xhdpi/tab_selected_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_pressed_holo.9.png b/res/drawable-xhdpi/tab_selected_pressed_holo.9.png
new file mode 100644
index 0000000..9ae1441
--- /dev/null
+++ b/res/drawable-xhdpi/tab_selected_pressed_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_focused_holo.9.png b/res/drawable-xhdpi/tab_unselected_focused_holo.9.png
new file mode 100644
index 0000000..5973936
--- /dev/null
+++ b/res/drawable-xhdpi/tab_unselected_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_holo.9.png b/res/drawable-xhdpi/tab_unselected_holo.9.png
new file mode 100644
index 0000000..1590ba8
--- /dev/null
+++ b/res/drawable-xhdpi/tab_unselected_holo.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png b/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png
new file mode 100644
index 0000000..b781355
--- /dev/null
+++ b/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png
Binary files differ
diff --git a/res/drawable/action_bar_tab.xml b/res/drawable/action_bar_tab.xml
new file mode 100644
index 0000000..7676549
--- /dev/null
+++ b/res/drawable/action_bar_tab.xml
@@ -0,0 +1,44 @@
+<?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_pressed="true"
+        android:state_selected="true"
+        android:drawable="@drawable/tab_selected_pressed_holo"/>
+    <item
+        android:state_focused="true"
+        android:state_selected="true"
+        android:drawable="@drawable/tab_selected_focused_holo"/>
+    <item
+        android:state_focused="true"
+        android:state_selected="false"
+        android:drawable="@drawable/tab_unselected_focused_holo"/>
+    <item
+        android:state_pressed="true"
+        android:state_selected="false"
+        android:drawable="@drawable/tab_unselected_pressed_holo"/>
+    <item
+        android:state_focused="false"
+        android:state_selected="true"
+        android:state_pressed="false"
+        android:drawable="@drawable/tab_selected_holo"/>
+    <item
+        android:state_focused="false"
+        android:state_pressed="false"
+        android:state_selected="false"
+        android:drawable="@drawable/tab_unselected_holo"/>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/list_item_activated_background.xml b/res/drawable/list_item_activated_background.xml
index 334e8ec..6ea21d3 100644
--- a/res/drawable/list_item_activated_background.xml
+++ b/res/drawable/list_item_activated_background.xml
@@ -17,5 +17,5 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
         android:exitFadeDuration="@android:integer/config_mediumAnimTime">
     <item android:state_activated="true" android:drawable="@drawable/list_activated_holo" />
-    <item android:drawable="@android:color/transparent" />
-</selector>
+    <item android:drawable="@drawable/list_background_holo" />
+</selector>
\ No newline at end of file
diff --git a/res/layout-sw580dp-w1000dp/contact_detail_container.xml b/res/layout-sw580dp-w1000dp/contact_detail_container.xml
index 1d4ea35..6b1d094 100644
--- a/res/layout-sw580dp-w1000dp/contact_detail_container.xml
+++ b/res/layout-sw580dp-w1000dp/contact_detail_container.xml
@@ -35,7 +35,14 @@
         android:id="@+id/about_fragment_container"
         android:layout_width="0dip"
         android:layout_height="match_parent"
-        android:layout_weight="1" />
+        android:layout_weight="3" />
+
+    <!-- Vertical divider -->
+    <View
+        android:layout_width="2dip"
+        android:layout_height="match_parent"
+        android:background="?android:attr/listDivider"
+        />
 
     <!--
       Container for the "Updates" fragment on the contact card for a contact
@@ -44,7 +51,8 @@
     -->
     <FrameLayout
         android:id="@+id/updates_fragment_container"
-        android:layout_width="306dip"
+        android:layout_width="0dip"
+        android:layout_weight="2"
         android:layout_height="match_parent" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp-w1000dp/contact_editor_activity.xml b/res/layout-sw580dp-w1000dp/contact_editor_activity.xml
new file mode 100644
index 0000000..d2e0d22
--- /dev/null
+++ b/res/layout-sw580dp-w1000dp/contact_editor_activity.xml
@@ -0,0 +1,48 @@
+<?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.
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fillViewport="true">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="horizontal">
+
+        <!-- Empty view to represent the left margin -->
+        <View
+            android:layout_height="match_parent"
+            android:layout_width="0dip"
+            android:layout_weight="2" />
+
+        <fragment class="com.android.contacts.editor.ContactEditorFragment"
+            android:id="@+id/contact_editor_fragment"
+            android:layout_width="0dip"
+            android:layout_weight="6"
+            android:layout_height="match_parent"/>
+
+        <!-- Empty view to represent the right margin -->
+        <View
+            android:layout_height="match_parent"
+            android:layout_width="0dip"
+            android:layout_weight="2" />
+
+    </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/res/layout-sw580dp-w1000dp/group_detail_fragment.xml b/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
index 11779cb..31d3ce1 100644
--- a/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
+++ b/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
@@ -27,21 +27,22 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingBottom="@dimen/group_detail_vertical_padding"
+        android:paddingTop="12dip"
+        android:paddingBottom="12dip"
         android:orientation="horizontal" >
 
         <LinearLayout
             android:layout_width="0dip"
             android:layout_height="wrap_content"
             android:layout_weight="1"
+            android:paddingLeft="@dimen/group_detail_border_padding"
             android:orientation="vertical" >
 
             <TextView
                 android:id="@+id/group_title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:paddingLeft="@dimen/group_detail_border_padding"
-                android:paddingTop="@dimen/group_detail_vertical_padding"
+                android:paddingLeft="8dip"
                 android:textAppearance="?android:attr/textAppearanceLarge"
                 android:textStyle="bold" />
 
@@ -49,7 +50,7 @@
                 android:id="@+id/group_size"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:paddingLeft="@dimen/group_detail_border_padding"
+                android:paddingLeft="8dip"
                 android:textAppearance="?android:attr/textAppearanceMedium"
                 android:textColor="?android:attr/textColorSecondary" />
 
@@ -67,10 +68,9 @@
     <View
         android:layout_width="match_parent"
         android:layout_height="1dip"
-        android:background="?android:attr/listDivider"
-        android:layout_marginBottom="@dimen/group_detail_vertical_padding"
-        android:layout_marginLeft="@dimen/group_detail_divider_margin"
-        android:layout_marginRight="@dimen/group_detail_divider_margin" />
+        android:background="@color/people_app_theme_color"
+        android:layout_marginLeft="@dimen/group_detail_border_padding"
+        android:layout_marginRight="@dimen/group_detail_border_padding" />
 
     <!-- List of group members -->
     <ListView android:id="@android:id/list"
diff --git a/res/layout-sw580dp-w1000dp/people_activity.xml b/res/layout-sw580dp-w1000dp/people_activity.xml
new file mode 100644
index 0000000..7d8d064
--- /dev/null
+++ b/res/layout-sw580dp-w1000dp/people_activity.xml
@@ -0,0 +1,162 @@
+<?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.
+-->
+
+<FrameLayout
+    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="match_parent">
+
+    <com.android.contacts.widget.InterpolatingLayout
+        android:id="@+id/main_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:splitMotionEvents="true">
+
+        <LinearLayout
+            android:id="@+id/browse_view"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            android:minWidth="100dip"
+            ex:layout_narrowParentWidth="1000dip"
+            ex:layout_narrowWidth="276dip"
+            ex:layout_wideParentWidth="1280dip"
+            ex:layout_wideWidth="376dip"
+            android:background="@drawable/list_background_holo"
+            android:visibility="gone">
+
+            <!-- All -->
+            <fragment
+                android:id="@+id/all_fragment"
+                class="com.android.contacts.list.DefaultContactBrowseListFragment"
+                android:layout_height="0dip"
+                android:layout_width="match_parent"
+                android:layout_weight="1" />
+
+            <!-- Groups -->
+            <fragment
+                android:id="@+id/groups_fragment"
+                class="com.android.contacts.group.GroupBrowseListFragment"
+                android:layout_height="match_parent"
+                android:layout_width="match_parent" />
+        </LinearLayout>
+
+        <view
+            class="com.android.contacts.widget.TransitionAnimationView"
+            android:id="@+id/details_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            ex:layout_narrowParentWidth="800dip"
+            ex:layout_narrowMarginLeft="0dip"
+            ex:layout_narrowMarginRight="0dip"
+            ex:layout_wideParentWidth="1280dip"
+            ex:layout_wideMarginLeft="0dip"
+            ex:layout_wideMarginRight="0dip"
+            ex:clipMarginLeft="0dip"
+            ex:clipMarginTop="3dip"
+            ex:clipMarginRight="3dip"
+            ex:clipMarginBottom="9dip"
+            ex:enterAnimation="@android:animator/fade_in"
+            ex:exitAnimation="@android:animator/fade_out"
+            ex:animationDuration="200"
+            android:visibility="gone">
+
+            <!-- This layout includes all possible views needed for a contact detail page -->
+            <include
+                android:id="@+id/contact_detail_container"
+                layout="@layout/contact_detail_container"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+
+            <!-- This invisible worker fragment loads the contact's details -->
+            <fragment
+                android:id="@+id/contact_detail_loader_fragment"
+                class="com.android.contacts.detail.ContactLoaderFragment"
+                android:layout_height="0dip"
+                android:layout_width="0dip"
+                android:visibility="gone"/>
+
+            <!-- This is the group detail page -->
+            <fragment
+                android:id="@+id/group_detail_fragment"
+                class="com.android.contacts.group.GroupDetailFragment"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="gone" />
+        </view>
+
+        <view
+            class="com.android.contacts.widget.TransitionAnimationView"
+            android:id="@+id/favorites_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            ex:clipMarginLeft="0dip"
+            ex:clipMarginTop="3dip"
+            ex:clipMarginRight="3dip"
+            ex:clipMarginBottom="9dip"
+            ex:enterAnimation="@android:animator/fade_in"
+            ex:exitAnimation="@android:animator/fade_out"
+            ex:animationDuration="200">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@drawable/list_background_holo">
+
+                <!-- Starred -->
+                <fragment
+                    android:id="@+id/favorites_fragment"
+                    class="com.android.contacts.list.ContactTileListFragment"
+                    android:layout_height="match_parent"
+                    style="@style/FavoritesFragmentStyle"
+                    android:layout_marginTop="32dip"
+                    android:layout_marginLeft="32dip"
+                    android:layout_marginRight="32dip" />
+
+                <!-- Most Frequent -->
+                <fragment
+                    android:id="@+id/frequent_fragment"
+                    class="com.android.contacts.list.ContactTileListFragment"
+                    style="@style/FrequentFragmentStyle"
+                    android:layout_height="match_parent"
+                    android:layout_marginTop="32dip"
+                    android:layout_marginRight="32dip"/>
+            </LinearLayout>
+        </view>
+
+    </com.android.contacts.widget.InterpolatingLayout>
+
+    <com.android.contacts.widget.InterpolatingLayout
+        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"
+            ex:layout_narrowParentWidth="800dip"
+            ex:layout_narrowMarginLeft="80dip"
+            ex:layout_narrowMarginRight="80dip"
+            ex:layout_wideParentWidth="1280dip"
+            ex:layout_wideMarginLeft="200dip"
+            ex:layout_wideMarginRight="200dip"
+            android:paddingBottom="20dip" />
+
+    </com.android.contacts.widget.InterpolatingLayout>
+</FrameLayout>
diff --git a/res/layout-sw580dp/contact_editor_activity.xml b/res/layout-sw580dp/contact_editor_activity.xml
index b9f98a3..c33a3c0 100644
--- a/res/layout-sw580dp/contact_editor_activity.xml
+++ b/res/layout-sw580dp/contact_editor_activity.xml
@@ -16,23 +16,13 @@
 
 <ScrollView
     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="match_parent"
     android:fillViewport="true">
-    <com.android.contacts.widget.InterpolatingLayout
+
+    <fragment class="com.android.contacts.editor.ContactEditorFragment"
+        android:id="@+id/contact_editor_fragment"
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
-        <fragment class="com.android.contacts.editor.ContactEditorFragment"
-            android:id="@+id/contact_editor_fragment"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            ex:layout_wideParentWidth="1280dip"
-            ex:layout_wideMarginLeft="160dip"
-            ex:layout_wideMarginRight="160dip"
-            ex:layout_narrowParentWidth="800dip"
-            ex:layout_narrowMarginLeft="0dip"
-            ex:layout_narrowMarginRight="0dip"
-         />
-    </com.android.contacts.widget.InterpolatingLayout>
+        android:layout_height="match_parent"/>
+
 </ScrollView>
diff --git a/res/layout-sw580dp/contact_editor_fragment.xml b/res/layout-sw580dp/contact_editor_fragment.xml
index 0d01214..988be2a 100644
--- a/res/layout-sw580dp/contact_editor_fragment.xml
+++ b/res/layout-sw580dp/contact_editor_fragment.xml
@@ -14,12 +14,23 @@
      limitations under the License.
 -->
 
-<LinearLayout
+<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:id="@+id/editors"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/panel_message"
-    android:orientation="vertical"
-/>
+    android:background="@drawable/panel_message">
+
+    <LinearLayout
+        android:id="@+id/editors"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        ex:layout_wideParentWidth="800dip"
+        ex:layout_wideMarginLeft="128dip"
+        ex:layout_wideMarginRight="128dip"
+        ex:layout_narrowParentWidth="600dip"
+        ex:layout_narrowMarginLeft="50dip"
+        ex:layout_narrowMarginRight="50dip"/>
+
+</com.android.contacts.widget.InterpolatingLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp/event_field_editor_view.xml b/res/layout-sw580dp/event_field_editor_view.xml
deleted file mode 100644
index 822de37..0000000
--- a/res/layout-sw580dp/event_field_editor_view.xml
+++ /dev/null
@@ -1,51 +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.
--->
-
-<!-- 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">
-
-    <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: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-sw580dp/external_raw_contact_editor_view.xml
deleted file mode 100644
index fba5220..0000000
--- a/res/layout-sw580dp/external_raw_contact_editor_view.xml
+++ /dev/null
@@ -1,141 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<com.android.contacts.editor.ExternalRawContactEditorView
-    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"
-    android:orientation="vertical">
-
-    <include
-        layout="@layout/editor_account_header" />
-
-    <LinearLayout
-        android:id="@+id/body"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:paddingTop="10dip">
-
-        <com.android.contacts.widget.InterpolatingLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-
-            <FrameLayout
-                android:id="@+id/stub_photo"
-                android:layout_width="96dip"
-                android:layout_height="96dip"
-                android:layout_gravity="top|left"
-                ex:layout_wideParentWidth="960dip"
-                ex:layout_wideMarginLeft="96dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
-                ex:layout_narrowMarginLeft="15dip">
-
-                <include
-                    android:id="@+id/edit_photo"
-                    layout="@layout/item_photo_editor" />
-
-            </FrameLayout>
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_gravity="top"
-                ex:layout_wideParentWidth="960dip"
-                ex:layout_wideMarginRight="48dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
-                ex:layout_narrowMarginRight="15dip"
-                android:orientation="vertical">
-
-                <View
-                    android:layout_width="match_parent"
-                    android:layout_height="1px"
-                    android:background="@color/contact_detail_header_divider_color" />
-
-                <TextView
-                    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_marginRight="48dip"
-                    android:layout_marginTop="6dip"
-                    android:layout_marginBottom="4dip"
-                    android:paddingRight="?android:attr/scrollbarSize"
-                    android:textSize="@dimen/contact_name_text_size" />
-            </LinearLayout>
-        </com.android.contacts.widget.InterpolatingLayout>
-
-        <com.android.contacts.widget.InterpolatingLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="vertical"
-                ex:layout_wideParentWidth="960dip"
-                ex:layout_wideMarginLeft="96dip"
-                ex:layout_wideMarginRight="96dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
-                ex:layout_narrowMarginLeft="15dip"
-                ex:layout_narrowMarginRight="15dip">
-
-                <TextView
-                    android:id="@+id/read_only_warning"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_marginTop="13dip"
-                    android:layout_marginBottom="13dip"
-                    android:layout_marginLeft="13dip"
-                    android:textAppearance="?android:attr/textAppearanceSmall"
-                    android:textColor="?android:attr/textColorPrimary"
-                    android:drawableLeft="?android:attr/alertDialogIcon"
-                    android:drawablePadding="10dip"
-                    android:gravity="center_vertical" />
-
-                <View
-                    android:layout_width="match_parent"
-                    android:layout_height="1px"
-                    android:background="?android:attr/listDivider" />
-
-                <LinearLayout
-                    android:id="@+id/sect_general"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:orientation="vertical" />
-            </LinearLayout>
-        </com.android.contacts.widget.InterpolatingLayout>
-
-        <com.android.contacts.widget.InterpolatingLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="10dip"
-            android:layout_marginBottom="42dip">
-            <Button
-                android:id="@+id/button_edit_externally"
-                android:text="@string/edit_contact"
-                ex:layout_wideParentWidth="960dip"
-                ex:layout_wideMarginLeft="246dip"
-                ex:layout_wideMarginRight="156dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
-                ex:layout_narrowMarginLeft="165dip"
-                ex:layout_narrowMarginRight="121dip"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
-        </com.android.contacts.widget.InterpolatingLayout>
-    </LinearLayout>
-</com.android.contacts.editor.ExternalRawContactEditorView>
diff --git a/res/layout-sw580dp/group_detail_fragment.xml b/res/layout-sw580dp/group_detail_fragment.xml
index a7db154..c11337f 100644
--- a/res/layout-sw580dp/group_detail_fragment.xml
+++ b/res/layout-sw580dp/group_detail_fragment.xml
@@ -21,14 +21,16 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:paddingLeft="@dimen/group_detail_border_padding"
+    android:paddingRight="@dimen/group_detail_border_padding"
     android:background="@drawable/panel_content">
 
     <TextView
         android:id="@+id/group_title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingLeft="@dimen/group_detail_border_padding"
-        android:paddingTop="@dimen/group_detail_vertical_padding"
+        android:paddingLeft="8dip"
+        android:paddingTop="12dip"
         android:textAppearance="?android:attr/textAppearanceLarge"
         android:textStyle="bold" />
 
@@ -36,31 +38,25 @@
         android:id="@+id/group_size"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingLeft="@dimen/group_detail_border_padding"
+        android:paddingLeft="8dip"
         android:textAppearance="?android:attr/textAppearanceSmall"
         android:textColor="?android:attr/textColorTertiary" />
 
     <View
         android:layout_width="match_parent"
         android:layout_height="1dip"
-        android:background="?android:attr/listDivider"
-        android:layout_marginTop="@dimen/group_detail_vertical_padding"
-        android:layout_marginBottom="@dimen/group_detail_vertical_padding"
-        android:layout_marginLeft="@dimen/group_detail_divider_margin"
-        android:layout_marginRight="@dimen/group_detail_divider_margin" />
+        android:background="@color/people_app_theme_color"
+        android:layout_marginTop="12dip" />
 
     <FrameLayout
         android:id="@+id/group_source_view_container"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:paddingLeft="@dimen/group_detail_border_padding" />
+        android:layout_gravity="center_vertical" />
 
     <ListView android:id="@android:id/list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginLeft="@dimen/group_detail_border_padding"
-        android:layout_marginRight="@dimen/group_detail_border_padding"
         android:cacheColorHint="#00000000"
         android:divider="@null" />
 
diff --git a/res/layout-sw580dp/item_group_membership.xml b/res/layout-sw580dp/item_group_membership.xml
deleted file mode 100644
index 985b7ae..0000000
--- a/res/layout-sw580dp/item_group_membership.xml
+++ /dev/null
@@ -1,69 +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.GroupMembershipView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/editor_min_line_item_height"
-    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:textAppearance="?android:attr/textAppearanceSmall"
-        android:singleLine="true"
-        android:textColor="@color/editor_label_text_color"
-        android:ellipsize="marquee" />
-
-    <Button
-        style="?android:attr/spinnerStyle"
-        android:id="@+id/group_list"
-        android:layout_width="0dip"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:gravity="left|center_vertical"
-        android:ellipsize="end"
-        android:focusable="true"
-        android:paddingLeft="18dip"
-    />
-    
-    <!-- Plus/Minus button only for layout. This makes the editor lay out nicely with the other fields -->
-    <ImageButton
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        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:src="@drawable/ic_menu_add_field_holo_light"
-        android:background="@null"
-        android:visibility="invisible" />
-    <ImageButton
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        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:src="@drawable/ic_menu_remove_field_holo_light"
-        android:background="@null"
-        android:visibility="invisible" />
-</com.android.contacts.editor.GroupMembershipView>
diff --git a/res/layout-sw580dp/people_activity.xml b/res/layout-sw580dp/people_activity.xml
index 8bf627f..35c11b1 100644
--- a/res/layout-sw580dp/people_activity.xml
+++ b/res/layout-sw580dp/people_activity.xml
@@ -20,23 +20,19 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <com.android.contacts.widget.InterpolatingLayout
+    <LinearLayout
         android:id="@+id/main_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginLeft="8dip"
+        android:orientation="horizontal"
         android:splitMotionEvents="true">
 
         <LinearLayout
             android:id="@+id/browse_view"
-            android:layout_width="wrap_content"
+            android:layout_width="0dip"
             android:layout_height="match_parent"
+            android:layout_weight="1"
             android:orientation="vertical"
-            android:minWidth="100dip"
-            ex:layout_narrowParentWidth="800dip"
-            ex:layout_narrowWidth="310dip"
-            ex:layout_wideParentWidth="1280dip"
-            ex:layout_wideWidth="430dip"
             android:background="@drawable/list_background_holo"
             android:visibility="gone">
 
@@ -59,14 +55,9 @@
         <view
             class="com.android.contacts.widget.TransitionAnimationView"
             android:id="@+id/details_view"
-            android:layout_width="match_parent"
+            android:layout_width="0dip"
             android:layout_height="match_parent"
-            ex:layout_narrowParentWidth="800dip"
-            ex:layout_narrowMarginLeft="0dip"
-            ex:layout_narrowMarginRight="0dip"
-            ex:layout_wideParentWidth="1280dip"
-            ex:layout_wideMarginLeft="0dip"
-            ex:layout_wideMarginRight="0dip"
+            android:layout_weight="1"
             ex:clipMarginLeft="0dip"
             ex:clipMarginTop="3dip"
             ex:clipMarginRight="3dip"
@@ -105,19 +96,13 @@
             android:id="@+id/favorites_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            ex:layout_narrowParentWidth="800dip"
-            ex:layout_narrowMarginRight="0dip"
-            ex:layout_wideParentWidth="1280dip"
-            ex:layout_wideMarginRight="48dip"
             ex:clipMarginLeft="0dip"
             ex:clipMarginTop="3dip"
             ex:clipMarginRight="3dip"
             ex:clipMarginBottom="9dip"
             ex:enterAnimation="@android:animator/fade_in"
             ex:exitAnimation="@android:animator/fade_out"
-            ex:animationDuration="200"
-            android:paddingTop="32dip"
-            android:paddingRight="32dip">
+            ex:animationDuration="200">
 
             <LinearLayout
                 android:layout_width="match_parent"
@@ -129,20 +114,23 @@
                     android:id="@+id/favorites_fragment"
                     class="com.android.contacts.list.ContactTileListFragment"
                     android:layout_height="match_parent"
-                    android:layout_width="0dip"
-                    android:layout_weight="2" />
+                    style="@style/FavoritesFragmentStyle"
+                    android:layout_marginTop="32dip"
+                    android:layout_marginLeft="32dip"
+                    android:layout_marginRight="32dip" />
 
                 <!-- Most Frequent -->
                 <fragment
                     android:id="@+id/frequent_fragment"
                     class="com.android.contacts.list.ContactTileListFragment"
+                    style="@style/FrequentFragmentStyle"
                     android:layout_height="match_parent"
-                    android:layout_width="0dip"
-                    android:layout_weight="1" />
+                    android:layout_marginTop="32dip"
+                    android:layout_marginRight="32dip"/>
             </LinearLayout>
         </view>
 
-    </com.android.contacts.widget.InterpolatingLayout>
+    </LinearLayout>
 
     <com.android.contacts.widget.InterpolatingLayout
         android:id="@+id/contacts_unavailable_view"
diff --git a/res/layout-sw580dp/phonetic_name_editor_view.xml b/res/layout-sw580dp/phonetic_name_editor_view.xml
deleted file mode 100644
index f27c1fc..0000000
--- a/res/layout-sw580dp/phonetic_name_editor_view.xml
+++ /dev/null
@@ -1,59 +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.
--->
-
-<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="@dimen/editor_name_text_field_left_margin"
-    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-sw580dp/raw_contact_editor_view.xml b/res/layout-sw580dp/raw_contact_editor_view.xml
deleted file mode 100644
index 11fc161..0000000
--- a/res/layout-sw580dp/raw_contact_editor_view.xml
+++ /dev/null
@@ -1,142 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<com.android.contacts.editor.RawContactEditorView
-    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"
-    android:orientation="vertical">
-
-    <include
-        layout="@layout/editor_account_header" />
-
-    <LinearLayout
-        android:id="@+id/body"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:paddingTop="10dip">
-
-        <com.android.contacts.widget.InterpolatingLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-
-            <FrameLayout
-                android:id="@+id/stub_photo"
-                android:layout_width="96dip"
-                android:layout_height="96dip"
-                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">
-
-                <include
-                    android:id="@+id/edit_photo"
-                    layout="@layout/item_photo_editor" />
-
-            </FrameLayout>
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                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_narrowMarginRight="15dip"
-                ex:layout_narrowMarginLeft="5dip"
-                android:orientation="vertical">
-
-                <View
-                    android:layout_width="match_parent"
-                    android:layout_height="1px"
-                    android:background="@color/contact_detail_header_divider_color" />
-
-                <include
-                    android:id="@+id/edit_name"
-                    layout="@layout/structured_name_editor_view" />
-
-                <include
-                    android:id="@+id/edit_phonetic_name"
-                    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_marginRight="48dip">
-                    <ViewStub
-                        android:id="@+id/aggregation_suggestion_stub"
-                        android:inflatedId="@+id/aggregation_suggestion"
-                        android:layout="@layout/aggregation_suggestions"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:visibility="visible" />
-                </FrameLayout>
-            </LinearLayout>
-        </com.android.contacts.widget.InterpolatingLayout>
-
-        <com.android.contacts.widget.InterpolatingLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="vertical"
-                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">
-
-                <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" />
-            </LinearLayout>
-        </com.android.contacts.widget.InterpolatingLayout>
-
-        <com.android.contacts.widget.InterpolatingLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dip"
-            android:layout_marginBottom="32dip">
-            <Button
-                android:id="@+id/button_add_field"
-                android:text="@string/add_field"
-                ex:layout_wideParentWidth="960dip"
-                ex:layout_wideMarginLeft="246dip"
-                ex:layout_wideMarginRight="156dip"
-                ex:layout_narrowParentWidth="@dimen/editor_interpolator_narrow_width"
-                ex:layout_narrowMarginLeft="165dip"
-                ex:layout_narrowMarginRight="121dip"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
-        </com.android.contacts.widget.InterpolatingLayout>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1px"
-            android:layout_alignParentBottom="true"
-            android:background="?android:attr/listDivider" />
-    </LinearLayout>
-</com.android.contacts.editor.RawContactEditorView>
diff --git a/res/layout-sw580dp/structured_name_editor_view.xml b/res/layout-sw580dp/structured_name_editor_view.xml
deleted file mode 100644
index 97e222e..0000000
--- a/res/layout-sw580dp/structured_name_editor_view.xml
+++ /dev/null
@@ -1,58 +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.
--->
-
-<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="@dimen/editor_name_text_field_left_margin"
-    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-sw580dp/text_fields_editor_view.xml b/res/layout-sw580dp/text_fields_editor_view.xml
index be0ea67..ecc9dd2 100644
--- a/res/layout-sw580dp/text_fields_editor_view.xml
+++ b/res/layout-sw580dp/text_fields_editor_view.xml
@@ -18,7 +18,7 @@
     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:layout_marginTop="-4dip"
     android:orientation="vertical">
 
     <LinearLayout
@@ -26,7 +26,7 @@
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:orientation="horizontal"
-        android:gravity="top"
+        android:gravity="center_vertical"
         android:focusable="true"
         android:clickable="true">
 
@@ -35,14 +35,13 @@
             layout="@layout/edit_field_list" />
 
         <include
-            android:id="@+id/spinner"
-            layout="@layout/edit_spinner"
-            android:paddingTop="15dip"
+            android:id="@+id/expansion_view_container"
+            layout="@layout/edit_expansion_view"
             android:visibility="gone" />
 
         <include
-            android:id="@+id/expansion_view_container"
-            layout="@layout/edit_expansion_view"
+            android:id="@+id/spinner"
+            layout="@layout/edit_spinner"
             android:visibility="gone" />
 
         <include
diff --git a/res/layout-w470dp/contact_detail_fragment.xml b/res/layout-w470dp/contact_detail_fragment.xml
index 56c9f20..d63236d 100644
--- a/res/layout-w470dp/contact_detail_fragment.xml
+++ b/res/layout-w470dp/contact_detail_fragment.xml
@@ -77,7 +77,6 @@
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
         android:background="@android:color/black"
-        android:alpha=".50"
         android:visibility="gone"/>
 
     <View
diff --git a/res/layout-w470dp/contact_detail_updates_fragment.xml b/res/layout-w470dp/contact_detail_updates_fragment.xml
index 92ddd8c..dd7cfbd 100644
--- a/res/layout-w470dp/contact_detail_updates_fragment.xml
+++ b/res/layout-w470dp/contact_detail_updates_fragment.xml
@@ -14,21 +14,45 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
     android:id="@+id/contact_detail_updates_fragment"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:layout_height="match_parent">
 
-    <include
-        android:id="@+id/title"
-        layout="@layout/contact_detail_kind_title_entry_view" />
-
-    <ListView android:id="@android:id/list"
+    <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@color/background_social_updates"
-        android:divider="@null"/>
+        android:layout_height="match_parent"
+        android:orientation="vertical">
 
-</LinearLayout>
+        <include
+            android:id="@+id/title"
+            layout="@layout/contact_detail_kind_title_entry_view" />
+
+        <ListView android:id="@android:id/list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@color/background_social_updates"
+            android:divider="@null"/>
+
+    </LinearLayout>
+
+    <View
+        android:id="@+id/alpha_overlay"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentTop="true"
+        android:background="@android:color/black"
+        android:visibility="gone"/>
+
+    <View
+        android:id="@+id/touch_intercept_overlay"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentTop="true"
+        android:background="?android:attr/selectableItemBackground"
+        android:visibility="gone"/>
+
+</FrameLayout>
diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml
index f28bda1..8bd6a19 100644
--- a/res/layout/call_log_list_item.xml
+++ b/res/layout/call_log_list_item.xml
@@ -19,7 +19,6 @@
     class="com.android.contacts.calllog.CallLogListItemView"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:padding="@dimen/call_log_inner_margin"
 >
     <!--
         This layout may represent either a call log item or one of the
@@ -45,22 +44,25 @@
             android:layout_height="@dimen/call_log_list_contact_photo_size"
             android:layout_alignParentLeft="true"
             android:layout_centerVertical="true"
-            android:layout_marginLeft="@dimen/call_log_inner_margin"
+            android:layout_marginLeft="@dimen/call_log_outer_margin"
+            android:layout_marginTop="@dimen/call_log_inner_margin"
+            android:layout_marginBottom="@dimen/call_log_inner_margin"
         />
         <LinearLayout
             android:id="@+id/divider"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:layout_alignParentRight="true"
-            android:layout_centerInParent="true"
+            android:layout_centerVertical="true"
+            android:layout_alignTop="@+id/primary_action_view"
+            android:layout_alignBottom="@+id/primary_action_view"
             android:orientation="horizontal"
         >
             <ImageView
                 android:id="@+id/unheard_icon"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginLeft="@dimen/call_log_inner_margin"
-                android:layout_gravity="center_vertical"
+                android:layout_height="match_parent"
+                android:paddingRight="@dimen/call_log_inner_margin"
                 android:scaleType="center"
                 android:src="@drawable/ic_unheard_voicemail_holo_dark"
                 android:visibility="gone"
@@ -69,39 +71,32 @@
             <View
                 android:layout_width="1px"
                 android:layout_height="@dimen/call_log_call_action_size"
-                android:layout_gravity="center_vertical"
-                android:layout_marginLeft="@dimen/call_log_inner_margin"
                 android:background="@drawable/ic_divider_dashed_holo_dark"
-            />
-            <ImageView
-                android:id="@+id/call_icon"
-                android:layout_width="@dimen/call_log_call_action_width"
-                android:layout_height="@dimen/call_log_call_action_height"
                 android:layout_gravity="center_vertical"
-                android:scaleType="center"
-                android:src="@drawable/ic_ab_dialer_holo_dark"
-                android:background="@drawable/list_selector"
-                android:contentDescription="@string/description_call_log_call_button"
             />
-            <ImageView
-                android:id="@+id/play_icon"
+            <ImageButton
+                android:id="@+id/secondary_action_icon"
                 android:layout_width="@dimen/call_log_call_action_width"
-                android:layout_height="@dimen/call_log_call_action_height"
-                android:layout_gravity="center_vertical"
+                android:layout_height="match_parent"
+                android:paddingLeft="@dimen/call_log_inner_margin"
+                android:paddingTop="@dimen/call_log_inner_margin"
+                android:paddingBottom="@dimen/call_log_inner_margin"
+                android:paddingRight="@dimen/call_log_outer_margin"
                 android:scaleType="center"
-                android:src="@drawable/ic_play_holo_dark"
                 android:background="@drawable/list_selector"
-                android:contentDescription="@string/description_call_log_play_button"
             />
         </LinearLayout>
         <LinearLayout
+            android:id="@+id/primary_action_view"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:layout_toRightOf="@id/quick_contact_photo"
             android:layout_toLeftOf="@id/divider"
             android:layout_centerVertical="true"
-            android:layout_marginLeft="@dimen/call_log_inner_margin"
+            android:padding="@dimen/call_log_inner_margin"
             android:orientation="vertical"
+            android:background="@drawable/list_selector"
+            android:focusable="true"
         >
             <TextView
                 android:id="@+id/name"
@@ -151,8 +146,12 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginLeft="@dimen/call_log_inner_margin"
+        android:layout_marginRight="@dimen/call_log_inner_margin"
         android:textSize="14sp"
         android:textStyle="bold"
         android:textColor="?attr/call_log_header_color"
+        android:padding="@dimen/call_log_inner_margin"
+        android:focusable="true"
+        android:drawableBottom="@android:color/holo_blue_light"
     />
 </view>
diff --git a/res/layout/contact_detail_tab_carousel.xml b/res/layout/contact_detail_tab_carousel.xml
index fe167d1..fb60f17 100644
--- a/res/layout/contact_detail_tab_carousel.xml
+++ b/res/layout/contact_detail_tab_carousel.xml
@@ -28,10 +28,18 @@
         android:layout_height="match_parent"
         android:orientation="horizontal">
 
+        <!-- "About" tab -->
         <include
             android:id="@+id/tab_about"
             layout="@layout/carousel_about_tab" />
 
+        <!-- Vertical divider -->
+        <View
+            android:layout_width="2dip"
+            android:layout_height="match_parent"
+            android:background="@android:color/white"/>
+
+        <!-- "Updates" tab -->
         <include
             android:id="@+id/tab_update"
             layout="@layout/carousel_updates_tab" />
diff --git a/res/layout/contact_tile_frequent.xml b/res/layout/contact_tile_frequent.xml
index fc9c04c..14cb04e 100644
--- a/res/layout/contact_tile_frequent.xml
+++ b/res/layout/contact_tile_frequent.xml
@@ -52,7 +52,7 @@
             android:id="@+id/contact_tile_status"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textSize="12sp"
+            android:textSize="14sp"
             android:ellipsize="end"
             android:singleLine="true"
             android:textColor="#cccccc"
diff --git a/res/layout/contact_tile_frequent_phone.xml b/res/layout/contact_tile_frequent_phone.xml
index e0ea0fb..50c1fc2 100644
--- a/res/layout/contact_tile_frequent_phone.xml
+++ b/res/layout/contact_tile_frequent_phone.xml
@@ -47,11 +47,11 @@
             android:id="@+id/contact_tile_phone_number"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textSize="12sp"
+            android:textSize="14sp"
             android:ellipsize="end"
             android:singleLine="true"
-            android:textColor="#cccccc"
-            android:layout_marginLeft="12dip"
+            android:textColor="@color/dialtacts_secondary_text_color"
+            android:layout_marginLeft="8dip"
             android:layout_toRightOf="@id/contact_tile_quick"
             android:layout_below="@id/contact_tile_name" />
 
@@ -59,14 +59,30 @@
             android:id="@+id/contact_tile_phone_type"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textSize="12sp"
+            android:textSize="14sp"
             android:ellipsize="end"
             android:singleLine="true"
-            android:textColor="#cccccc"
+            android:textAllCaps="true"
+            android:textColor="@color/dialtacts_secondary_text_color"
             android:layout_marginLeft="12dip"
             android:layout_alignParentRight="true"
             android:layout_alignTop="@id/contact_tile_phone_number" />
 
+        <View
+            android:id="@+id/contact_tile_horizontal_divider"
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider"
+            android:layout_alignParentBottom="true" />
+
+        <View
+            android:id="@+id/contact_tile_horizontal_picture_divider"
+            android:layout_width="64dip"
+            android:layout_height="1dip"
+            android:background="@android:color/black"
+            android:layout_alignParentBottom="true"
+            android:layout_alignParentLeft="true" />
+
     </RelativeLayout>
 
 </view>
diff --git a/res/layout/contact_tile_starred.xml b/res/layout/contact_tile_starred.xml
index d667a34..3b74946 100644
--- a/res/layout/contact_tile_starred.xml
+++ b/res/layout/contact_tile_starred.xml
@@ -61,7 +61,7 @@
             android:layout_marginLeft="4dip"
             style="@style/ContactTileStatusText" />
 
-        <ImageButton
+       <ImageButton
             android:id="@+id/contact_tile_push_state"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/res/layout/contact_tile_starred_secondary_target.xml b/res/layout/contact_tile_starred_secondary_target.xml
index 887fa5d..3558166 100644
--- a/res/layout/contact_tile_starred_secondary_target.xml
+++ b/res/layout/contact_tile_starred_secondary_target.xml
@@ -61,14 +61,14 @@
         </FrameLayout>
 
         <View
-            android:id="@+id/vertical_divider"
+            android:id="@+id/secondary_vertical_divider"
             android:layout_width="1dip"
-            android:layout_height="24dip"
+            android:layout_height="32dip"
             android:layout_alignParentBottom="true"
             android:layout_alignParentRight="true"
-            android:layout_marginBottom="12dip"
             android:layout_marginRight="44dip"
-            android:background="?android:attr/dividerVertical" />
+            android:layout_marginBottom="8dip"
+            android:background="@drawable/ic_divider_dashed_holo_dark" />
 
         <ImageButton
             android:id="@+id/contact_tile_push_state"
@@ -78,14 +78,28 @@
 
         <ImageButton
             android:id="@+id/contact_tile_secondary_button"
-            android:src="@drawable/ic_tab_unselected_contacts"
+            android:src="@drawable/ic_contacts_holo_dark"
             android:background="?android:attr/selectableItemBackground"
             android:layout_height="@dimen/contact_tile_shadowbox_height"
-            android:layout_width="32dip"
+            android:layout_width="48dip"
             android:layout_alignParentBottom="true"
             android:layout_alignParentRight="true"
-            android:layout_marginLeft="8dip"
-            android:layout_marginRight="8dip" />
+            android:paddingRight="8dip"
+            android:paddingLeft="8dip" />
+
+        <View
+            android:id="@+id/contact_tile_vertical_divider"
+            android:layout_height="match_parent"
+            android:layout_width="2dip"
+            android:background="@color/phone_contact_tile_divider_color"
+            android:layout_alignParentRight="true" />
+
+        <View
+            android:id="@+id/contact_tile_horizontal_divider"
+            android:layout_width="match_parent"
+            android:layout_height="2dip"
+            android:background="@color/phone_contact_tile_divider_color"
+            android:layout_alignParentBottom="true" />
 
     </RelativeLayout>
 
diff --git a/res/layout/edit_add_field.xml b/res/layout/edit_add_field.xml
index b17185d..0b62ac1 100644
--- a/res/layout/edit_add_field.xml
+++ b/res/layout/edit_add_field.xml
@@ -17,8 +17,8 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="48dip"
-    android:paddingLeft="16dip"
+    android:minHeight="40dip"
+    android:paddingLeft="@dimen/editor_add_field_label_left_padding"
     android:background="?android:attr/selectableItemBackground">
     <TextView
         android:id="@+id/add_text"
diff --git a/res/layout/edit_delete_button.xml b/res/layout/edit_delete_button.xml
index f05b0e4..ed3f4d6 100644
--- a/res/layout/edit_delete_button.xml
+++ b/res/layout/edit_delete_button.xml
@@ -19,12 +19,13 @@
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
+    android:layout_height="@dimen/editor_min_line_item_height"
     android:layout_gravity="top">
     <ImageView
         android:id="@+id/delete_button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_gravity="center"
         android:duplicateParentState="true"
         android:background="?android:attr/selectableItemBackground"
         android:src="@drawable/ic_menu_remove_field_holo_light"
diff --git a/res/layout/edit_kind_title.xml b/res/layout/edit_kind_title.xml
index 0f755ee..8901d5b 100644
--- a/res/layout/edit_kind_title.xml
+++ b/res/layout/edit_kind_title.xml
@@ -24,14 +24,15 @@
     android:minHeight="24dip"
     android:paddingLeft="8dip"
     android:paddingRight="8dip"
+    android:paddingBottom="4dip"
     android:orientation="vertical">
 
     <TextView
         android:id="@+id/kind_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:paddingLeft="8dip"
-        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:paddingLeft="@dimen/editor_kind_title_left_padding"
+        android:textAppearance="?android:attr/textAppearanceSmall"
         android:textColor="@color/people_app_theme_color"
         android:textStyle="bold"
         android:textAllCaps="true"
diff --git a/res/layout/edit_spinner.xml b/res/layout/edit_spinner.xml
index 37edcbd..0f3ed23 100644
--- a/res/layout/edit_spinner.xml
+++ b/res/layout/edit_spinner.xml
@@ -22,6 +22,6 @@
     android:id="@+id/spinner"
     android:layout_gravity="top"
     android:layout_width="@dimen/editor_type_label_width"
-    android:layout_height="wrap_content"
-    android:paddingLeft="5dip"
+    android:layout_height="@dimen/editor_min_line_item_height"
+    android:paddingLeft="8dip"
     android:paddingRight="20dip"/>
\ No newline at end of file
diff --git a/res/layout/external_raw_contact_editor_view.xml b/res/layout/external_raw_contact_editor_view.xml
index 6f39b34..24e8322 100644
--- a/res/layout/external_raw_contact_editor_view.xml
+++ b/res/layout/external_raw_contact_editor_view.xml
@@ -19,7 +19,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:paddingBottom="32dip">
 
     <include
         layout="@layout/editor_account_header" />
@@ -28,8 +29,8 @@
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
         android:orientation="horizontal"
-        android:paddingLeft="16dip"
-        android:paddingRight="16dip">
+        android:paddingTop="8dip"
+        android:paddingLeft="16dip">
 
         <TextView android:id="@+id/read_only_name"
             android:layout_width="0dip"
@@ -45,7 +46,8 @@
         <FrameLayout
             android:id="@+id/stub_photo"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content">
+            android:layout_height="wrap_content"
+            android:layout_marginRight="8dip">
 
             <include
                 android:id="@+id/edit_photo"
diff --git a/res/layout/group_browse_list_account_header.xml b/res/layout/group_browse_list_account_header.xml
index 7c07497..f739ea2 100644
--- a/res/layout/group_browse_list_account_header.xml
+++ b/res/layout/group_browse_list_account_header.xml
@@ -18,7 +18,10 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:padding="@dimen/group_list_header_padding"
+    android:background="@drawable/list_background_holo"
+    android:paddingLeft="@dimen/group_list_header_padding"
+    android:paddingRight="@dimen/group_list_header_padding"
+    android:paddingTop="@dimen/group_list_header_padding"
     android:orientation="vertical">
 
     <LinearLayout
diff --git a/res/layout/group_browse_list_item.xml b/res/layout/group_browse_list_item.xml
index 6f5fcef..326b413 100644
--- a/res/layout/group_browse_list_item.xml
+++ b/res/layout/group_browse_list_item.xml
@@ -19,16 +19,12 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="@dimen/detail_min_line_item_height"
-    android:paddingRight="20dip"
-    android:paddingBottom="10dip"
-    style="@style/GroupBrowseListItem">
+    android:minHeight="@dimen/detail_min_line_item_height">
 
     <ImageView
         android:id="@+id/divider"
         android:layout_width="match_parent"
         android:layout_height="1dip"
-        android:layout_marginBottom="10dip"
         android:paddingLeft="10dip"
         android:paddingRight="10dip"
         android:scaleType="fitXY"
@@ -37,11 +33,17 @@
     <include
         android:id="@+id/group_list_header"
         layout="@layout/group_browse_list_account_header"
+        android:paddingRight="20dip"
+        android:paddingBottom="10dip"
         android:visibility="gone" />
 
     <RelativeLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content">
+        android:layout_height="wrap_content"
+        android:paddingTop="10dip"
+        android:paddingRight="20dip"
+        android:paddingBottom="10dip"
+        style="@style/GroupBrowseListItem">
 
         <LinearLayout
             android:layout_width="match_parent"
diff --git a/res/layout/item_photo_editor.xml b/res/layout/item_photo_editor.xml
index 2e73cb2..a41b007 100644
--- a/res/layout/item_photo_editor.xml
+++ b/res/layout/item_photo_editor.xml
@@ -17,13 +17,13 @@
 <view
     class="com.android.contacts.editor.PhotoEditorView"
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/edit_photo_size"
-    android:layout_height="@dimen/edit_photo_size"
+    android:layout_width="42dip"
+    android:layout_height="42dip"
     >
     <ImageView
         android:id="@+id/photo"
-        android:layout_width="@dimen/edit_photo_size"
-        android:layout_height="@dimen/edit_photo_size"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
         android:src="@drawable/ic_contact_picture"
         android:cropToPadding="true"
         android:scaleType="centerCrop"
@@ -31,8 +31,8 @@
     />
     <View
         android:id="@+id/frame"
-        android:layout_width="@dimen/edit_photo_size"
-        android:layout_height="@dimen/edit_photo_size"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
         android:clickable="true"
         android:focusable="true"
         android:contentDescription="@string/description_contact_photo"
diff --git a/res/layout/list_separator.xml b/res/layout/list_separator.xml
index af83584..7d57ef7 100644
--- a/res/layout/list_separator.xml
+++ b/res/layout/list_separator.xml
@@ -15,8 +15,30 @@
 -->
 
 <!-- Layout used for list separators. -->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    style="?android:attr/listSeparatorTextViewStyle"
-    android:gravity="left|center_vertical"
-    android:id="@+id/header_text"
-/>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:orientation="vertical"
+    android:layout_height="32dip"
+    android:divider="?android:attr/dividerVertical"
+    android:paddingLeft="16dip"
+    android:paddingRight="16dip">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dip"
+            android:paddingLeft="8dip"
+            android:textStyle="bold"
+            android:textAllCaps="true"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="@color/people_app_theme_color"
+            android:gravity="left|center_vertical"
+            android:id="@+id/header_text" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:background="@color/people_app_theme_color" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/organization_editor_view_switcher.xml b/res/layout/organization_editor_view_switcher.xml
index fb21626..810634c 100644
--- a/res/layout/organization_editor_view_switcher.xml
+++ b/res/layout/organization_editor_view_switcher.xml
@@ -23,14 +23,14 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="48dip"
+    android:minHeight="40dip"
     android:background="?android:attr/selectableItemBackground">
 
     <TextView
         android:id="@+id/add_organization_button"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingLeft="16dip"
+        android:paddingLeft="@dimen/editor_add_field_label_left_padding"
         android:paddingRight="16dip"
         android:layout_gravity="center_vertical"
         android:duplicateParentState="true"
diff --git a/res/layout/phonetic_name_editor_view.xml b/res/layout/phonetic_name_editor_view.xml
index 6084d3f..5846998 100644
--- a/res/layout/phonetic_name_editor_view.xml
+++ b/res/layout/phonetic_name_editor_view.xml
@@ -18,6 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:minHeight="@dimen/editor_min_line_item_height"
     android:orientation="vertical">
 
     <include
diff --git a/res/layout/raw_contact_editor_view.xml b/res/layout/raw_contact_editor_view.xml
index b4aa766..b98f4fb 100644
--- a/res/layout/raw_contact_editor_view.xml
+++ b/res/layout/raw_contact_editor_view.xml
@@ -18,7 +18,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:paddingTop="@dimen/editor_padding_top">
 
     <include
         layout="@layout/editor_account_header" />
diff --git a/res/layout/structured_name_editor_view.xml b/res/layout/structured_name_editor_view.xml
index 979a0f3..196b079 100644
--- a/res/layout/structured_name_editor_view.xml
+++ b/res/layout/structured_name_editor_view.xml
@@ -18,6 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:minHeight="@dimen/editor_min_line_item_height"
     android:orientation="vertical">
 
     <include
diff --git a/res/layout/text_fields_editor_view.xml b/res/layout/text_fields_editor_view.xml
index 89970c6..e63b7da 100644
--- a/res/layout/text_fields_editor_view.xml
+++ b/res/layout/text_fields_editor_view.xml
@@ -18,6 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:layout_marginTop="-2dip"
     android:orientation="vertical">
 
     <LinearLayout
diff --git a/res/menu-sw580dp-w720dp/actions.xml b/res/menu-sw580dp-w720dp/actions.xml
index 4f0da2c..0b8303a 100644
--- a/res/menu-sw580dp-w720dp/actions.xml
+++ b/res/menu-sw580dp-w720dp/actions.xml
@@ -16,7 +16,7 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <item
         android:id="@+id/menu_search"
-        android:icon="@android:drawable/ic_menu_search"
+        android:icon="@drawable/ic_ab_search_holo_dark"
         android:title="@string/menu_search"
         android:showAsAction="always" />
 
diff --git a/res/menu-sw580dp/actions.xml b/res/menu-sw580dp/actions.xml
index b6cdfbf..8732927 100644
--- a/res/menu-sw580dp/actions.xml
+++ b/res/menu-sw580dp/actions.xml
@@ -16,7 +16,7 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <item
         android:id="@+id/menu_search"
-        android:icon="@android:drawable/ic_menu_search"
+        android:icon="@drawable/ic_ab_search_holo_dark"
         android:title="@string/menu_search"
         android:showAsAction="ifRoom" />
 
diff --git a/res/layout-sw580dp/edit_spinner.xml b/res/values-sw580dp-w1000dp/styles.xml
similarity index 60%
rename from res/layout-sw580dp/edit_spinner.xml
rename to res/values-sw580dp-w1000dp/styles.xml
index f1909fe..85f4733 100644
--- a/res/layout-sw580dp/edit_spinner.xml
+++ b/res/values-sw580dp-w1000dp/styles.xml
@@ -13,15 +13,14 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<resources>
+    <style name="FavoritesFragmentStyle">
+        <item name="android:layout_width">0dip</item>
+        <item name="android:layout_weight">7</item>
+    </style>
 
-<!-- Spinner for a field in the contact editor. -->
-
-<Spinner
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/spinner"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    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
+    <style name="FrequentFragmentStyle">
+        <item name="android:layout_width">0dip</item>
+        <item name="android:layout_weight">3</item>
+    </style>
+</resources>
diff --git a/res/values-sw580dp-w720dp/styles.xml b/res/values-sw580dp-w720dp/styles.xml
index a6cbb9b..ff50d45 100644
--- a/res/values-sw580dp-w720dp/styles.xml
+++ b/res/values-sw580dp-w720dp/styles.xml
@@ -16,11 +16,11 @@
 <resources>
     <style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
+        <item name="android:actionBarTabStyle">@style/ContactsActionBarTabView</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
         <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="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>
diff --git a/res/values-sw580dp/dimens.xml b/res/values-sw580dp/dimens.xml
index 7e3fb80..f7c0f05 100644
--- a/res/values-sw580dp/dimens.xml
+++ b/res/values-sw580dp/dimens.xml
@@ -14,14 +14,14 @@
      limitations under the License.
 -->
 <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_padding_top">32dip</dimen>
+    <dimen name="editor_type_label_width">122dip</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="editor_round_button_padding_left">16dip</dimen>
+    <dimen name="editor_round_button_padding_right">16dip</dimen>
+    <dimen name="editor_kind_title_left_padding">16dip</dimen>
+    <dimen name="editor_add_field_label_left_padding">24dip</dimen>
     <dimen name="detail_item_side_margin">0dip</dimen>
     <dimen name="detail_item_vertical_margin">16dip</dimen>
     <dimen name="detail_item_icon_margin">8dip</dimen>
diff --git a/res/values-sw580dp/styles.xml b/res/values-sw580dp/styles.xml
index b9b87cf..a386e57 100644
--- a/res/values-sw580dp/styles.xml
+++ b/res/values-sw580dp/styles.xml
@@ -16,11 +16,11 @@
 <resources>
     <style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
+        <item name="android:actionBarTabStyle">@style/ContactsActionBarTabView</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
         <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="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>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 0005484..6e6cadb 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -95,4 +95,8 @@
 
     <!-- Color of the text for buttons in the action bar  -->
     <color name="action_bar_button_text_color">#FFFFFF</color>
+
+    <!-- Color of the vertical and horizontal dividers for starred contacts in the Phone app -->
+    <color name="phone_contact_tile_divider_color">#D8000000</color>
+
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 459d0f8..5bc8ea0 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -14,8 +14,6 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="edit_photo_size">42dip</dimen>
-
     <!-- The height of the ScrollingTabWidget -->
     <dimen name="tab_height">40dip</dimen>
     <dimen name="account_name_height">25dip</dimen>
@@ -33,12 +31,21 @@
     based on the source of the request -->
     <dimen name="quick_contact_top_position">48dip</dimen>
 
+    <!-- Top padding of the entire contact editor  -->
+    <dimen name="editor_padding_top">0dip</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_top">4dip</dimen>
     <dimen name="editor_round_button_padding_bottom">4dip</dimen>
 
+    <!-- Left padding of the kind title in the contact editor -->
+    <dimen name="editor_kind_title_left_padding">8dip</dimen>
+
+    <!-- Left padding of the label in the add field button for the contact editor -->
+    <dimen name="editor_add_field_label_left_padding">16dip</dimen>
+
     <!-- Width of the Type-Label in the Editor -->
     <dimen name="editor_type_label_width">100dip</dimen>
 
@@ -48,12 +55,6 @@
     <!-- Right padding of a field in the Editor -->
     <dimen name="editor_field_right_padding">4dip</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>
 
@@ -75,9 +76,6 @@
     <!-- Width of the title labels in the contact editor -->
     <dimen name="editor_title_label_width">120dip</dimen>
 
-    <!-- Interpolator layout narrow width value of the contact editor -->
-    <dimen name="editor_interpolator_narrow_width">600dip</dimen>
-
     <!-- Height of the attribution text view in the contact detail header view -->
     <dimen name="detail_header_attribution_height">40dip</dimen>
 
@@ -191,14 +189,8 @@
     <!-- Size of group list icons -->
     <dimen name="group_list_icon_size">32dip</dimen>
 
-    <!-- Border padding for the group detail fragment header -->
-    <dimen name="group_detail_border_padding">20dip</dimen>
-
-    <!-- Vertical padding for the group detail fragment header -->
-    <dimen name="group_detail_vertical_padding">15dip</dimen>
-
-    <!-- Margins for the group detail fragment divider in the header -->
-    <dimen name="group_detail_divider_margin">15dip</dimen>
+    <!-- Border padding for the group detail fragment -->
+    <dimen name="group_detail_border_padding">32dip</dimen>
 
     <!-- Height of the member list in the group editor -->
     <dimen name="group_editor_member_list_height">550dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a49ab77..2e8983f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -110,10 +110,10 @@
     <string name="menu_removeStar">Remove from favorites</string>
 
     <!-- Menu item used to edit a specific contact -->
-    <string name="menu_editContact">Edit contact</string>
+    <string name="menu_editContact">Edit</string>
 
     <!-- Menu item used to delete a specific contact -->
-    <string name="menu_deleteContact">Delete contact</string>
+    <string name="menu_deleteContact">Delete</string>
 
     <!-- Menu item used to call a specific contact when viewing the details of that contact. -->
     <string name="menu_call">Call contact</string>
@@ -129,13 +129,13 @@
     <string name="menu_splitAggregate">Separate</string>
 
     <!-- Menu item that edits the currently selected group [CHAR LIMIT=30] -->
-    <string name="menu_editGroup">Edit group</string>
+    <string name="menu_editGroup">Edit</string>
 
     <!-- Menu item that renames the currently selected group [CHAR LIMIT=30] -->
     <string name="menu_renameGroup">Rename group</string>
 
     <!-- Menu item that deletes the currently selected group [CHAR LIMIT=30] -->
-    <string name="menu_deleteGroup">Delete group</string>
+    <string name="menu_deleteGroup">Delete</string>
 
     <!-- Menu item (in the action bar) that creates a new contacts [CHAR LIMIT=12] -->
     <string name="menu_new_contact_action_bar">New</string>
@@ -182,7 +182,7 @@
     <string name="menu_set_ring_tone">Set ringtone</string>
 
     <!-- Menu item that opens the Options activity for a given contact [CHAR LIMIT=30] -->
-    <string name="menu_redirect_calls_to_vm">Redirect calls to voicemail</string>
+    <string name="menu_redirect_calls_to_vm">All calls to voicemail</string>
 
     <!-- Warning dialog contents after users selects to delete a ReadOnly contact. -->
     <string name="readOnlyContactWarning">You cannot delete contacts from read-only accounts, but you can hide them in your contacts lists.</string>
@@ -205,12 +205,6 @@
     <!-- Menu item to indicate you want to stop editing a contact and NOT save the changes you've made [CHAR LIMIT=12] -->
     <string name="menu_discard">Discard</string>
 
-    <!-- The title of the activity that edits and existing contact -->
-    <string name="editContact_title_edit">Edit contact</string>
-
-    <!-- The title of the activity that creates a new contact -->
-    <string name="editContact_title_insert">New contact</string>
-
     <!-- The label describing the phonetic pronunciation/reading of a contact name [CHAR LIMIT=20] -->
     <string name="label_phonetic_name">Phonetic</string>
 
@@ -1040,7 +1034,7 @@
     <string name="dialog_import_export">Import/Export contacts</string>
 
     <!-- The menu item to share the currently viewed contact [CHAR LIMIT=30] -->
-    <string name="menu_share">Share contact</string>
+    <string name="menu_share">Share</string>
 
     <!-- Dialog title when picking the application to share a contact with. -->
     <string name="share_via">Share contact via</string>
@@ -1603,12 +1597,6 @@
     <!-- Label to instruct the user to type in a contact's name to add the contact as a member of the current group. [CHAR LIMIT=64] -->
     <string name="enter_contact_name">Enter contact\'s name</string>
 
-    <!-- The title of the activity that creates a new group [CHAR LIMIT=NONE] -->
-    <string name="editGroup_title_insert">New group</string>
-
-    <!-- The title of the activity that edits an existing group [CHAR LIMIT=NONE] -->
-    <string name="editGroup_title_edit">Edit group</string>
-
     <!-- Button to view the updates from the current group on the group detail page [CHAR LIMIT=20] -->
     <string name="view_updates_from_group">View updates</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 423466f..763a600 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -76,7 +76,6 @@
 
     <style name="ContactDetailActivityTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
-        <item name="android:windowContentOverlay">@null</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
     </style>
@@ -123,6 +122,7 @@
 
     <style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
+        <item name="android:actionBarTabStyle">@style/ContactsActionBarTabView</item>
         <item name="android:textColorPrimary">@color/primary_text_color</item>
         <item name="android:textColorSecondary">@color/secondary_text_color</item>
         <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
@@ -160,6 +160,10 @@
         <item name="android:backgroundStacked">@drawable/ab_solid_custom_blue_inverse_holo</item>
     </style>
 
+    <style name="ContactsActionBarTabView" parent="@android:style/Widget.Holo.ActionBar.TabView">
+        <item name="android:background">@drawable/action_bar_tab</item>
+    </style>
+
     <!-- TODO: Clean up this file so themes aren't copied. -->
     <style name="GroupDetailTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
@@ -280,8 +284,6 @@
 
     <style name="ContactTileStarred">
         <item name="android:focusable">true</item>
-        <item name="android:padding">1px</item>
-        <item name="android:background">@drawable/list_selector</item>
     </style>
 
     <style name="ContactTileStarredName">
@@ -301,6 +303,16 @@
         <item name="android:background">#7F000000</item>
     </style>
 
+    <style name="FavoritesFragmentStyle">
+        <item name="android:layout_width">0dip</item>
+        <item name="android:layout_weight">1</item>
+    </style>
+
+    <style name="FrequentFragmentStyle">
+        <item name="android:layout_width">0dip</item>
+        <item name="android:layout_weight">1</item>
+    </style>
+
     <style name="DialtactsActionBarStyle" parent="android:Widget.Holo.ActionBar">
         <item name="android:backgroundSplit">@drawable/ab_bottom_opaque_dark_holo</item>
         <item name="android:backgroundStacked">@drawable/ab_stacked_opaque_dark_holo</item>
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 43e6fc7..80219d9 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -116,6 +116,7 @@
         CallLog.Calls.NUMBER,
         CallLog.Calls.TYPE,
         CallLog.Calls.COUNTRY_ISO,
+        CallLog.Calls.GEOCODED_LOCATION,
     };
 
     static final int DATE_COLUMN_INDEX = 0;
@@ -123,6 +124,7 @@
     static final int NUMBER_COLUMN_INDEX = 2;
     static final int CALL_TYPE_COLUMN_INDEX = 3;
     static final int COUNTRY_ISO_COLUMN_INDEX = 4;
+    static final int GEOCODED_LOCATION_COLUMN_INDEX = 5;
 
     static final String[] PHONES_PROJECTION = new String[] {
         PhoneLookup._ID,
@@ -403,6 +405,8 @@
             long duration = callCursor.getLong(DURATION_COLUMN_INDEX);
             int callType = callCursor.getInt(CALL_TYPE_COLUMN_INDEX);
             String countryIso = callCursor.getString(COUNTRY_ISO_COLUMN_INDEX);
+            final String geocode = callCursor.getString(GEOCODED_LOCATION_COLUMN_INDEX);
+
             if (TextUtils.isEmpty(countryIso)) {
                 countryIso = mDefaultCountryIso;
             }
@@ -448,7 +452,7 @@
                     numberText = candidateNumberText;
                 }
             }
-            return new PhoneCallDetails(number, numberText, countryIso,
+            return new PhoneCallDetails(number, numberText, countryIso, geocode,
                     new int[]{ callType }, date, duration,
                     nameText, numberType, numberLabel, personId, photoUri);
         } finally {
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index 8416721..064deb5 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.content.Entity;
 import android.content.Entity.NamedContentValues;
+import android.content.Intent;
 import android.content.Loader;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -784,7 +785,7 @@
 
             HashMap<String, AccountType> result = new HashMap<String, AccountType>(allInvitables);
 
-            // Remove the ones that already has a raw contact in the current contact
+            // Remove the ones that already have a raw contact in the current contact
             for (Entity entity : contactData.getEntities()) {
                 final String type = entity.getEntityValues().getAsString(RawContacts.ACCOUNT_TYPE);
                 if (!TextUtils.isEmpty(type)) {
@@ -1095,12 +1096,45 @@
                     mContact.setLoadingPhoto(true);
                     new AsyncPhotoLoader().execute(mContact.getPhotoUri());
                 }
+
+                // inform the source of the data that this contact is being looked at
+                postViewNotificationToSyncAdapter();
             }
 
             deliverResult(mContact);
         }
     }
 
+    /**
+     * Posts a message to the contributing sync adapters that have opted-in, notifying them
+     * that the contact has just been loaded
+     */
+    private void postViewNotificationToSyncAdapter() {
+        Context context = getContext();
+        for (Entity entity : mContact.getEntities()) {
+            final ContentValues entityValues = entity.getEntityValues();
+            final String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
+            final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
+            final AccountType accountType = AccountTypeManager.getInstance(context ).getAccountType(
+                    type, dataSet);
+            final String serviceName = accountType.getViewContactNotifyServiceClassName();
+            final String resPackageName = accountType.resPackageName;
+            if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
+                final long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
+                final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
+                final Intent intent = new Intent();
+                intent.setClassName(resPackageName, serviceName);
+                intent.setAction(Intent.ACTION_VIEW);
+                intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
+                try {
+                    context.startService(intent);
+                } catch (Exception e) {
+                    Log.e(TAG, "Error sending message to source-app", e);
+                }
+            }
+        }
+    }
+
     private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
 
         private static final int BUFFER_SIZE = 1024*16;
diff --git a/src/com/android/contacts/PhoneCallDetails.java b/src/com/android/contacts/PhoneCallDetails.java
index 347a303..5718091 100644
--- a/src/com/android/contacts/PhoneCallDetails.java
+++ b/src/com/android/contacts/PhoneCallDetails.java
@@ -30,6 +30,8 @@
     public final CharSequence formattedNumber;
     /** The country corresponding with the phone number. */
     public final String countryIso;
+    /** The geocoded location for the phone number. */
+    public final String geocode;
     /**
      * The type of calls, as defined in the call log table, e.g., {@link Calls#INCOMING_TYPE}.
      * <p>
@@ -56,18 +58,20 @@
 
     /** Create the details for a call with a number not associated with a contact. */
     public PhoneCallDetails(CharSequence number, CharSequence formattedNumber,
-            String countryIso, int[] callTypes, long date, long duration) {
-        this(number, formattedNumber, countryIso, callTypes, date, duration, "", 0, "", -1L, null);
+            String countryIso, String geocode, int[] callTypes, long date, long duration) {
+        this(number, formattedNumber, countryIso, geocode, callTypes, date, duration, "", 0, "",
+                -1L, null);
     }
 
     /** Create the details for a call with a number associated with a contact. */
     public PhoneCallDetails(CharSequence number, CharSequence formattedNumber,
-            String countryIso, int[] callTypes, long date, long duration,
+            String countryIso, String geocode, int[] callTypes, long date, long duration,
             CharSequence name, int numberType, CharSequence numberLabel, long personId,
             Uri photoUri) {
         this.number = number;
         this.formattedNumber = formattedNumber;
         this.countryIso = countryIso;
+        this.geocode = geocode;
         this.callTypes = callTypes;
         this.date = date;
         this.duration = duration;
diff --git a/src/com/android/contacts/PhoneCallDetailsHelper.java b/src/com/android/contacts/PhoneCallDetailsHelper.java
index 3101aee..e970fcc 100644
--- a/src/com/android/contacts/PhoneCallDetailsHelper.java
+++ b/src/com/android/contacts/PhoneCallDetailsHelper.java
@@ -107,12 +107,10 @@
             mPhoneNumberHelper.getDisplayNumber(details.number, details.formattedNumber);
         if (TextUtils.isEmpty(details.name)) {
             nameText = displayNumber;
-            String geocode = mPhoneNumberHelper.getGeocodeForNumber(
-                    details.number.toString(), details.countryIso);
-            if (TextUtils.isEmpty(geocode)) {
+            if (TextUtils.isEmpty(details.geocode)) {
                 numberText = mResources.getString(R.string.call_log_empty_gecode);
             } else {
-                numberText = geocode;
+                numberText = details.geocode;
             }
         } else {
             nameText = details.name;
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index 3cd07ca..c863b23 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -152,7 +152,6 @@
         }
 
         mDetailFragment.setListener(mFragmentListener);
-        TabCarouselScrollManager.bind(mTabCarousel, mDetailFragment, mUpdatesFragment);
         mDetailFragment.setData(mLookupUri, mContactData);
         mUpdatesFragment.setData(mLookupUri, mContactData);
 
@@ -348,6 +347,7 @@
             mTabCarousel = (ContactDetailTabCarousel) findViewById(R.id.tab_carousel);
             if (mTabCarousel != null) {
                 mTabCarousel.setListener(mTabCarouselListener);
+                TabCarouselScrollManager.bind(mTabCarousel, mDetailFragment, mUpdatesFragment);
             }
 
             mViewPager = (ViewPager) findViewById(R.id.pager);
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index 54ea05f..2936110 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -171,11 +171,6 @@
         }
 
         @Override
-        public void setTitleTo(int resourceId) {
-            setTitle(resourceId);
-        }
-
-        @Override
         public void onEditOtherContactRequested(
                 Uri contactLookupUri, ArrayList<ContentValues> values) {
             Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
diff --git a/src/com/android/contacts/activities/GroupEditorActivity.java b/src/com/android/contacts/activities/GroupEditorActivity.java
index f0d2aa1..b397c19 100644
--- a/src/com/android/contacts/activities/GroupEditorActivity.java
+++ b/src/com/android/contacts/activities/GroupEditorActivity.java
@@ -71,7 +71,7 @@
             saveMenuItem.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
-                    mFragment.doSaveAction();
+                    mFragment.onDoneClicked();
                 }
             });
             // Show the custom action bar but hide the home icon and title
@@ -85,8 +85,13 @@
                 R.id.group_editor_fragment);
         mFragment.setListener(mFragmentListener);
         mFragment.setContentResolver(getContentResolver());
-        Uri uri = Intent.ACTION_EDIT.equals(action) ? getIntent().getData() : null;
-        mFragment.load(action, uri, getIntent().getExtras());
+
+        // NOTE The fragment will restore its state by itself after orientation changes, so
+        // we need to do this only for a new instance.
+        if (savedState == null) {
+            Uri uri = Intent.ACTION_EDIT.equals(action) ? getIntent().getData() : null;
+            mFragment.load(action, uri, getIntent().getExtras());
+        }
     }
 
     @Override
@@ -158,11 +163,6 @@
             }
             finish();
         }
-
-        @Override
-        public void onTitleLoaded(int resourceId) {
-            setTitle(resourceId);
-        }
     };
 
     @Override
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index 490c938..0713e0e 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -17,7 +17,6 @@
 package com.android.contacts.calllog;
 
 import com.android.common.widget.GroupingListAdapter;
-import com.android.contacts.CallDetailActivity;
 import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.PhoneCallDetails;
@@ -35,7 +34,6 @@
 
 import android.app.KeyguardManager;
 import android.app.ListFragment;
-import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -90,6 +88,7 @@
                 Calls.TYPE,
                 Calls.COUNTRY_ISO,
                 Calls.VOICEMAIL_URI,
+                Calls.GEOCODED_LOCATION,
         };
 
         public static final int ID = 0;
@@ -99,6 +98,7 @@
         public static final int CALL_TYPE = 4;
         public static final int COUNTRY_ISO = 5;
         public static final int VOICEMAIL_URI = 6;
+        public static final int GEOCODED_LOCATION = 7;
 
         /**
          * The name of the synthetic "section" column.
@@ -108,7 +108,7 @@
          */
         public static final String SECTION_NAME = "section";
         /** The index of the "section" column in the projection. */
-        public static final int SECTION = 7;
+        public static final int SECTION = 8;
         /** The value of the "section" column for the header of the new section. */
         public static final int SECTION_NEW_HEADER = 0;
         /** The value of the "section" column for the items of the new section. */
@@ -275,7 +275,18 @@
         /** Can be set to true by tests to disable processing of requests. */
         private volatile boolean mRequestProcessingDisabled = false;
 
-        private final View.OnClickListener mCallPlayOnClickListener = new View.OnClickListener() {
+        /** Listener for the primary action in the list, opens the call details. */
+        private final View.OnClickListener mPrimaryActionListener = new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                IntentProvider intentProvider = (IntentProvider) view.getTag();
+                if (intentProvider != null) {
+                    mContext.startActivity(intentProvider.getIntent(mContext));
+                }
+            }
+        };
+        /** Listener for the secondary action in the list, either call or play. */
+        private final View.OnClickListener mSecondaryActionListener = new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                 IntentProvider intentProvider = (IntentProvider) view.getTag();
@@ -329,7 +340,8 @@
             PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
                     resources, callTypeHelper, mPhoneNumberHelper);
             mCallLogViewsHelper =
-                    new CallLogListItemHelper(phoneCallDetailsHelper, mPhoneNumberHelper);
+                    new CallLogListItemHelper(
+                            phoneCallDetailsHelper, mPhoneNumberHelper, resources);
             mCallLogGroupBuilder = new CallLogGroupBuilder(this);
         }
 
@@ -662,8 +674,8 @@
         private void findAndCacheViews(View view) {
             // Get the views to bind to.
             CallLogListItemViews views = CallLogListItemViews.fromView(view);
-            views.callView.setOnClickListener(mCallPlayOnClickListener);
-            views.playView.setOnClickListener(mCallPlayOnClickListener);
+            views.primaryActionView.setOnClickListener(mPrimaryActionListener);
+            views.secondaryActionView.setOnClickListener(mSecondaryActionListener);
             view.setTag(views);
         }
 
@@ -701,21 +713,22 @@
             final String formattedNumber;
             final String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
 
+            views.primaryActionView.setTag(
+                    IntentProvider.getCallDetailIntentProvider(
+                            this, c.getPosition(), c.getLong(CallLogQuery.ID), count));
             // Store away the voicemail information so we can play it directly.
             if (callType == Calls.VOICEMAIL_TYPE) {
                 String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
                 final long rowId = c.getLong(CallLogQuery.ID);
-                views.playView.setTag(
+                views.secondaryActionView.setTag(
                         IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri));
-                views.callView.setTag(null);
             } else if (!TextUtils.isEmpty(number)) {
                 // Store away the number so we can call it directly if you click on the call icon.
-                views.callView.setTag(IntentProvider.getReturnCallIntentProvider(number));
-                views.playView.setTag(null);
+                views.secondaryActionView.setTag(
+                        IntentProvider.getReturnCallIntentProvider(number));
             } else {
                 // No action enabled.
-                views.callView.setTag(null);
-                views.playView.setTag(null);
+                views.secondaryActionView.setTag(null);
             }
 
             // Lookup contacts with this number
@@ -758,12 +771,13 @@
             final Uri thumbnailUri = info.thumbnailUri;
             final String lookupKey = info.lookupKey;
             final int[] callTypes = getCallTypes(c, count);
+            final String geocode = c.getString(CallLogQuery.GEOCODED_LOCATION);
             final PhoneCallDetails details;
             if (TextUtils.isEmpty(name)) {
-                details = new PhoneCallDetails(number, formattedNumber, countryIso,
+                details = new PhoneCallDetails(number, formattedNumber, countryIso, geocode,
                         callTypes, date, duration);
             } else {
-                details = new PhoneCallDetails(number, formattedNumber, countryIso,
+                details = new PhoneCallDetails(number, formattedNumber, countryIso, geocode,
                         callTypes, date, duration, name, ntype, label, personId, thumbnailUri);
             }
 
@@ -910,6 +924,7 @@
         mAdapter = new CallLogAdapter(getActivity(), mCallLogQueryHandler, currentCountryIso,
                 getVoiceMailNumber());
         setListAdapter(mAdapter);
+        getListView().setItemsCanFocus(true);
     }
 
     @Override
@@ -1099,38 +1114,6 @@
         }
     }
 
-    @Override
-    public void onListItemClick(ListView l, View v, int position, long id) {
-        Cursor cursor = (Cursor) mAdapter.getItem(position);
-        if (CallLogQuery.isSectionHeader(cursor)) {
-            // Do nothing when a header is clicked.
-            return;
-        }
-        Intent intent = new Intent(getActivity(), CallDetailActivity.class);
-        if (mAdapter.isGroupHeader(position)) {
-            // We want to restore the position in the cursor at the end.
-            int currentPosition = cursor.getPosition();
-            int groupSize = mAdapter.getGroupSize(position);
-            long[] ids = new long[groupSize];
-            // Copy the ids of the rows in the group.
-            for (int index = 0; index < groupSize; ++index) {
-                ids[index] = cursor.getLong(CallLogQuery.ID);
-                cursor.moveToNext();
-            }
-            intent.putExtra(CallDetailActivity.EXTRA_CALL_LOG_IDS, ids);
-            cursor.moveToPosition(currentPosition);
-        } else {
-            // If there is a single item, use the direct URI for it.
-            intent.setData(ContentUris.withAppendedId(Calls.CONTENT_URI_WITH_VOICEMAIL, id));
-            String voicemailUri = cursor.getString(CallLogQuery.VOICEMAIL_URI);
-            if (voicemailUri != null) {
-                intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI, Uri.parse(voicemailUri));
-            }
-            intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_START_PLAYBACK, false);
-        }
-        startActivity(intent);
-    }
-
     @VisibleForTesting
     public CallLogAdapter getAdapter() {
         return mAdapter;
diff --git a/src/com/android/contacts/calllog/CallLogListItemHelper.java b/src/com/android/contacts/calllog/CallLogListItemHelper.java
index a973d49..5951ea3 100644
--- a/src/com/android/contacts/calllog/CallLogListItemHelper.java
+++ b/src/com/android/contacts/calllog/CallLogListItemHelper.java
@@ -18,7 +18,9 @@
 
 import com.android.contacts.PhoneCallDetails;
 import com.android.contacts.PhoneCallDetailsHelper;
+import com.android.contacts.R;
 
+import android.content.res.Resources;
 import android.provider.CallLog.Calls;
 import android.view.View;
 
@@ -30,6 +32,8 @@
     private final PhoneCallDetailsHelper mPhoneCallDetailsHelper;
     /** Helper for handling phone numbers. */
     private final PhoneNumberHelper mPhoneNumberHelper;
+    /** Resources to look up strings. */
+    private final Resources mResources;
 
     /**
      * Creates a new helper instance.
@@ -38,9 +42,10 @@
      * @param phoneNumberHelper used to process phone number
      */
     public CallLogListItemHelper(PhoneCallDetailsHelper phoneCallDetailsHelper,
-            PhoneNumberHelper phoneNumberHelper) {
+            PhoneNumberHelper phoneNumberHelper, Resources resources) {
         mPhoneCallDetailsHelper = phoneCallDetailsHelper;
         mPhoneNumberHelper= phoneNumberHelper;
+        mResources = resources;
     }
 
     /**
@@ -59,22 +64,35 @@
 
         if (canPlay) {
             // Playback action takes preference.
-            views.callView.setVisibility(View.GONE);
-            views.playView.setVisibility(View.VISIBLE);
+            configurePlaySecondaryAction(views);
             views.unheardView.setVisibility(isHighlighted ? View.VISIBLE : View.GONE);
             views.dividerView.setVisibility(View.VISIBLE);
         } else if (canCall) {
-            // Call is the main action.
-            views.callView.setVisibility(View.VISIBLE);
-            views.playView.setVisibility(View.GONE);
+            // Call is the secondary action.
+            configureCallSecondaryAction(views);
             views.unheardView.setVisibility(View.GONE);
             views.dividerView.setVisibility(View.VISIBLE);
         } else {
             // No action available.
-            views.callView.setVisibility(View.GONE);
-            views.playView.setVisibility(View.GONE);
+            views.secondaryActionView.setVisibility(View.GONE);
             views.unheardView.setVisibility(View.GONE);
             views.dividerView.setVisibility(View.GONE);
         }
     }
+
+    /** Sets the secondary action to correspond to the call button. */
+    private void configureCallSecondaryAction(CallLogListItemViews views) {
+        views.secondaryActionView.setVisibility(View.VISIBLE);
+        views.secondaryActionView.setImageResource(R.drawable.ic_ab_dialer_holo_dark);
+        views.secondaryActionView.setContentDescription(
+                mResources.getString(R.string.description_call_log_call_button));
+    }
+
+    /** Sets the secondary action to correspond to the play button. */
+    private void configurePlaySecondaryAction(CallLogListItemViews views) {
+        views.secondaryActionView.setVisibility(View.VISIBLE);
+        views.secondaryActionView.setImageResource(R.drawable.ic_play_holo_dark);
+        views.secondaryActionView.setContentDescription(
+                mResources.getString(R.string.description_call_log_play_button));
+    }
 }
diff --git a/src/com/android/contacts/calllog/CallLogListItemViews.java b/src/com/android/contacts/calllog/CallLogListItemViews.java
index 21f2dc5..fbf91cc 100644
--- a/src/com/android/contacts/calllog/CallLogListItemViews.java
+++ b/src/com/android/contacts/calllog/CallLogListItemViews.java
@@ -21,6 +21,7 @@
 
 import android.content.Context;
 import android.view.View;
+import android.widget.ImageView;
 import android.widget.QuickContactBadge;
 import android.widget.TextView;
 
@@ -30,13 +31,13 @@
 public final class CallLogListItemViews {
     /** The quick contact badge for the contact. */
     public final QuickContactBadge quickContactView;
-    /** The main action button on the entry. */
-    public final View callView;
-    /** The play action button used for voicemail. */
-    public final View playView;
+    /** The primary action view of the entry. */
+    public final View primaryActionView;
+    /** The secondary action button on the entry. */
+    public final ImageView secondaryActionView;
     /** The icon used for unheard voicemail. */
     public final View unheardView;
-    /** The divider between callView and playView. */
+    /** The divider between the primary and secondary actions. */
     public final View dividerView;
     /** The details of the phone call. */
     public final PhoneCallDetailsViews phoneCallDetailsViews;
@@ -45,13 +46,13 @@
     /** The text of the header in a stand-alone row, or null for other types of rows. */
     public final TextView listHeaderTextView;
 
-    private CallLogListItemViews(QuickContactBadge quickContactView,
-            View callView, View playView, View unheardView, View dividerView,
+    private CallLogListItemViews(QuickContactBadge quickContactView, View primaryActionView,
+            ImageView secondaryActionView, View unheardView, View dividerView,
             PhoneCallDetailsViews phoneCallDetailsViews, View listItemView,
             TextView listHeaderTextView) {
         this.quickContactView = quickContactView;
-        this.callView = callView;
-        this.playView = playView;
+        this.primaryActionView = primaryActionView;
+        this.secondaryActionView = secondaryActionView;
         this.unheardView = unheardView;
         this.dividerView = dividerView;
         this.phoneCallDetailsViews = phoneCallDetailsViews;
@@ -62,8 +63,8 @@
     public static CallLogListItemViews fromView(View view) {
         return new CallLogListItemViews(
                 (QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
-                view.findViewById(R.id.call_icon),
-                view.findViewById(R.id.play_icon),
+                view.findViewById(R.id.primary_action_view),
+                (ImageView) view.findViewById(R.id.secondary_action_icon),
                 view.findViewById(R.id.unheard_icon),
                 view.findViewById(R.id.divider),
                 PhoneCallDetailsViews.fromView(view),
@@ -75,7 +76,7 @@
         return new CallLogListItemViews(
                 new QuickContactBadge(context),
                 new View(context),
-                new View(context),
+                new ImageView(context),
                 new View(context),
                 new View(context),
                 PhoneCallDetailsViews.createForTest(context),
diff --git a/src/com/android/contacts/calllog/CallLogQueryHandler.java b/src/com/android/contacts/calllog/CallLogQueryHandler.java
index 394599f..68ac63a 100644
--- a/src/com/android/contacts/calllog/CallLogQueryHandler.java
+++ b/src/com/android/contacts/calllog/CallLogQueryHandler.java
@@ -103,7 +103,7 @@
                 new MatrixCursor(CallLogFragment.CallLogQuery.EXTENDED_PROJECTION);
         // The values in this row correspond to default values for _PROJECTION from CallLogQuery
         // plus the section value.
-        matrixCursor.addRow(new Object[]{ -1L, "", 0L, 0L, 0, "", "", section });
+        matrixCursor.addRow(new Object[]{ -1L, "", 0L, 0L, 0, "", "", "", section });
         return matrixCursor;
     }
 
diff --git a/src/com/android/contacts/calllog/IntentProvider.java b/src/com/android/contacts/calllog/IntentProvider.java
index 035d90a..8173238 100644
--- a/src/com/android/contacts/calllog/IntentProvider.java
+++ b/src/com/android/contacts/calllog/IntentProvider.java
@@ -17,10 +17,13 @@
 package com.android.contacts.calllog;
 
 import com.android.contacts.CallDetailActivity;
+import com.android.contacts.calllog.CallLogFragment.CallLogAdapter;
+import com.android.contacts.calllog.CallLogFragment.CallLogQuery;
 
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
 import android.net.Uri;
 import android.provider.CallLog.Calls;
 import android.telephony.PhoneNumberUtils;
@@ -68,4 +71,41 @@
             }
         };
     }
+
+    public static IntentProvider getCallDetailIntentProvider(
+            final CallLogAdapter adapter, final int position, final long id, final int groupSize) {
+        return new IntentProvider() {
+            @Override
+            public Intent getIntent(Context context) {
+                Cursor cursor = adapter.getCursor();
+                cursor.moveToPosition(position);
+                if (CallLogQuery.isSectionHeader(cursor)) {
+                    // Do nothing when a header is clicked.
+                    return null;
+                }
+                Intent intent = new Intent(context, CallDetailActivity.class);
+                if (adapter.isGroupHeader(position)) {
+                    // We want to restore the position in the cursor at the end.
+                    long[] ids = new long[groupSize];
+                    // Copy the ids of the rows in the group.
+                    for (int index = 0; index < groupSize; ++index) {
+                        ids[index] = cursor.getLong(CallLogQuery.ID);
+                        cursor.moveToNext();
+                    }
+                    intent.putExtra(CallDetailActivity.EXTRA_CALL_LOG_IDS, ids);
+                } else {
+                    // If there is a single item, use the direct URI for it.
+                    intent.setData(ContentUris.withAppendedId(
+                            Calls.CONTENT_URI_WITH_VOICEMAIL, id));
+                    String voicemailUri = cursor.getString(CallLogQuery.VOICEMAIL_URI);
+                    if (voicemailUri != null) {
+                        intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI,
+                                Uri.parse(voicemailUri));
+                    }
+                    intent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_START_PLAYBACK, false);
+                }
+                return intent;
+            }
+        };
+    }
 }
diff --git a/src/com/android/contacts/calllog/PhoneNumberHelper.java b/src/com/android/contacts/calllog/PhoneNumberHelper.java
index bf493f5..4d96f4f 100644
--- a/src/com/android/contacts/calllog/PhoneNumberHelper.java
+++ b/src/com/android/contacts/calllog/PhoneNumberHelper.java
@@ -18,10 +18,6 @@
 
 import com.android.contacts.R;
 import com.android.internal.telephony.CallerInfo;
-import com.google.i18n.phonenumbers.NumberParseException;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
-import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
-import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
 
 import android.content.res.Resources;
 import android.net.Uri;
@@ -34,14 +30,10 @@
 public class PhoneNumberHelper {
     private final Resources mResources;
     private final String mVoicemailNumber;
-    private final PhoneNumberUtil mPhoneNumberUtil;
-    private final PhoneNumberOfflineGeocoder mPhoneNumberOfflineGeocoder;
 
     public PhoneNumberHelper(Resources resources, String voicemailNumber) {
         mResources = resources;
         mVoicemailNumber = voicemailNumber;
-        mPhoneNumberUtil = PhoneNumberUtil.getInstance();
-        mPhoneNumberOfflineGeocoder = PhoneNumberOfflineGeocoder.getInstance();
     }
 
     /** Returns true if it is possible to place a call to the given number. */
@@ -106,35 +98,4 @@
     public boolean isSipNumber(CharSequence number) {
         return PhoneNumberUtils.isUriNumber(number.toString());
     }
-
-    /**
-     * Returns a structured phone number from the given text representation, or null if the number
-     * cannot be parsed.
-     */
-    private PhoneNumber parsePhoneNumber(String number, String countryIso) {
-        try {
-            return mPhoneNumberUtil.parse(number, countryIso);
-        } catch (NumberParseException e) {
-            return null;
-        }
-    }
-
-    /** Returns the geocode associated with a phone number or the empty string if not available. */
-    public String getGeocodeForNumber(String number, String countryIso) {
-        if (!canGeocode(number)) {
-            return "";
-        }
-        PhoneNumber structuredPhoneNumber = parsePhoneNumber(number, countryIso);
-        if (structuredPhoneNumber != null) {
-            return mPhoneNumberOfflineGeocoder.getDescriptionForNumber(
-                    structuredPhoneNumber, mResources.getConfiguration().locale);
-        } else {
-            return "";
-        }
-    }
-
-    /** Returns true if it is possible to compute a geocode for the given number. */
-    private boolean canGeocode(CharSequence number) {
-        return canPlaceCallsTo(number) && !isVoicemailNumber(number);
-    }
 }
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index 94150b3..5b51d57 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -31,17 +31,24 @@
 import android.content.Context;
 import android.content.Entity;
 import android.content.Entity.NamedContentValues;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
 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.Html;
+import android.text.Html.ImageGetter;
 import android.text.Spanned;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -60,6 +67,8 @@
  * {@link ContactLoader.Result} data object to appropriate {@link View}s.
  */
 public class ContactDetailDisplayUtils {
+    private static final String TAG = "ContactDetailDisplayUtils";
+
     private static final int PHOTO_FADE_IN_ANIMATION_DURATION_MILLIS = 100;
 
     private ContactDetailDisplayUtils() {
@@ -67,9 +76,8 @@
     }
 
     /**
-     * Returns the display name of the contact. Depending on the preference for
-     * display name ordering, the contact's first name may be bolded if
-     * possible. Returns empty string if there is no display name.
+     * Returns the display name of the contact, using the current display order setting.
+     * Returns res/string/missing_name if there is no display name.
      */
     public static CharSequence getDisplayName(Context context, Result contactData) {
         CharSequence displayName = contactData.getDisplayName();
@@ -78,25 +86,9 @@
         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;
-                }
+                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;
-                }
+                styledName = altDisplayName;
             }
         } else {
             styledName = context.getResources().getString(R.string.missing_name);
@@ -313,10 +305,11 @@
         TextView attributionView = (TextView) rootView.findViewById(
                 R.id.stream_item_attribution);
         TextView commentsView = (TextView) rootView.findViewById(R.id.stream_item_comments);
-        htmlView.setText(Html.fromHtml(streamItem.getText()));
+        ImageGetter imageGetter = new DefaultImageGetter(context.getPackageManager());
+        htmlView.setText(Html.fromHtml(streamItem.getText(), imageGetter, null));
         attributionView.setText(ContactBadgeUtil.getSocialDate(streamItem, context));
         if (streamItem.getComments() != null) {
-            commentsView.setText(Html.fromHtml(streamItem.getComments()));
+            commentsView.setText(Html.fromHtml(streamItem.getComments(), imageGetter, null));
             commentsView.setVisibility(View.VISIBLE);
         } else {
             commentsView.setVisibility(View.GONE);
@@ -382,4 +375,79 @@
         }
     }
 
+    /** Fetcher for images from resources to be included in HTML text. */
+    private static class DefaultImageGetter implements Html.ImageGetter {
+        /** The scheme used to load resources. */
+        private static final String RES_SCHEME = "res";
+
+        private final PackageManager mPackageManager;
+
+        public DefaultImageGetter(PackageManager packageManager) {
+            mPackageManager = packageManager;
+        }
+
+        @Override
+        public Drawable getDrawable(String source) {
+            // Returning null means that a default image will be used.
+            Uri uri;
+            try {
+                uri = Uri.parse(source);
+            } catch (Throwable e) {
+                Log.d(TAG, "Could not parse image source: " + source);
+                return null;
+            }
+            if (!RES_SCHEME.equals(uri.getScheme())) {
+                Log.d(TAG, "Image source does not correspond to a resource: " + source);
+                return null;
+            }
+            // The URI authority represents the package name.
+            String packageName = uri.getAuthority();
+
+            Resources resources = getResourcesForResourceName(packageName);
+            if (resources == null) {
+                Log.d(TAG, "Could not parse image source: " + source);
+                return null;
+            }
+
+            List<String> pathSegments = uri.getPathSegments();
+            if (pathSegments.size() != 1) {
+                Log.d(TAG, "Could not parse image source: " + source);
+                return null;
+            }
+
+            final String name = pathSegments.get(0);
+            final int resId = resources.getIdentifier(name, "drawable", packageName);
+
+            if (resId == 0) {
+                // Use the default image icon in this case.
+                Log.d(TAG, "Cannot resolve resource identifier: " + source);
+                return null;
+            }
+
+            try {
+                return getResourceDrawable(resources, resId);
+            } catch (NotFoundException e) {
+                Log.d(TAG, "Resource not found: " + source, e);
+                return null;
+            }
+        }
+
+        /** Returns the drawable associated with the given id. */
+        private Drawable getResourceDrawable(Resources resources, int resId)
+                throws NotFoundException {
+            Drawable drawable = resources.getDrawable(resId);
+            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+            return drawable;
+        }
+
+        /** Returns the {@link Resources} of the package of the given resource name. */
+        private Resources getResourcesForResourceName(String packageName) {
+            try {
+                return mPackageManager.getResourcesForApplication(packageName);
+            } catch (NameNotFoundException e) {
+                Log.d(TAG, "Could not find package: " + packageName);
+                return null;
+            }
+        }
+    }
 }
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 383f23b..47617d3 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -118,7 +118,7 @@
 import java.util.Map;
 
 public class ContactDetailFragment extends Fragment implements FragmentKeyListener, ViewOverlay,
-        SelectAccountDialogFragment.Listener {
+        SelectAccountDialogFragment.Listener, OnItemClickListener {
 
     private static final String TAG = "ContactDetailFragment";
 
@@ -279,6 +279,7 @@
 
         mListView = (ListView) mView.findViewById(android.R.id.list);
         mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+        mListView.setOnItemClickListener(this);
         mListView.setItemsCanFocus(true);
         mListView.setOnScrollListener(mVerticalScrollListener);
 
@@ -1709,6 +1710,21 @@
         }
     }
 
+    /**
+     * Default (fallback) list item click listener.  Note the click event for DetailViewEntry is
+     * caught by individual views in the list item view to distinguish the primary action and the
+     * secondary action, so this method won't be invoked for that.  (The listener is set in the
+     * bindview in the adapter)
+     * This listener is used for other kind of entries.
+     */
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        if (mListener == null) return;
+        final ViewEntry entry = mAdapter.getItem(position);
+        if (entry == null) return;
+        entry.click(view, mListener);
+    }
+
     @Override
     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
         super.onCreateContextMenu(menu, view, menuInfo);
diff --git a/src/com/android/contacts/detail/ContactLoaderFragment.java b/src/com/android/contacts/detail/ContactLoaderFragment.java
index 8d3cd75..265550b 100644
--- a/src/com/android/contacts/detail/ContactLoaderFragment.java
+++ b/src/com/android/contacts/detail/ContactLoaderFragment.java
@@ -19,7 +19,6 @@
 import com.android.contacts.ContactLoader;
 import com.android.contacts.ContactSaveService;
 import com.android.contacts.R;
-import com.android.contacts.activities.ContactDetailActivity;
 import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
 import com.android.contacts.util.PhoneCapabilityTester;
 import com.android.internal.util.Objects;
@@ -29,7 +28,6 @@
 import android.app.LoaderManager;
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.ActivityNotFoundException;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.Loader;
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 4126425..412efab 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -317,11 +317,8 @@
 
         if (!hasIncomingState) {
             if (Intent.ACTION_EDIT.equals(mAction)) {
-                if (mListener != null) mListener.setTitleTo(R.string.editContact_title_edit);
                 getLoaderManager().initLoader(LOADER_DATA, null, mDataLoaderListener);
             } else if (Intent.ACTION_INSERT.equals(mAction)) {
-                if (mListener != null) mListener.setTitleTo(R.string.editContact_title_insert);
-
                 final Account account = mIntentExtras == null ? null :
                         (Account) mIntentExtras.getParcelable(Intents.Insert.ACCOUNT);
                 final String dataSet = mIntentExtras == null ? null :
@@ -1119,11 +1116,6 @@
         void onReverted();
 
         /**
-         * Set the Title (e.g. of the Activity)
-         */
-        void setTitleTo(int resourceId);
-
-        /**
          * Contact was saved and the Fragment can now be closed safely.
          */
         void onSaveFinished(Intent resultIntent);
diff --git a/src/com/android/contacts/editor/ExternalRawContactEditorView.java b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
index 04f4140..cabf639 100644
--- a/src/com/android/contacts/editor/ExternalRawContactEditorView.java
+++ b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
@@ -138,6 +138,8 @@
                     mContext.getString(R.string.from_account_format, mAccountName));
         }
         mAccountTypeTextView.setText(mContext.getString(R.string.account_type_format, accountType));
+        mAccountTypeTextView.setTextColor(mContext.getResources().getColor(
+                R.color.secondary_text_color));
 
         // TODO: Expose data set in the UI somehow?
 
diff --git a/src/com/android/contacts/editor/LabeledEditorView.java b/src/com/android/contacts/editor/LabeledEditorView.java
index bb25ea0..6452c80 100644
--- a/src/com/android/contacts/editor/LabeledEditorView.java
+++ b/src/com/android/contacts/editor/LabeledEditorView.java
@@ -508,7 +508,7 @@
             if (convertView == null) {
                 textView = (TextView) mInflater.inflate(resource, parent, false);
                 textView.setAllCaps(true);
-                textView.setGravity(Gravity.RIGHT);
+                textView.setGravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL);
                 textView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
                 textView.setTextColor(mTextColor);
             } else {
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 6335cb6..dd3c15a 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -129,12 +129,11 @@
 
         mInflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
-        final int photoSize = getResources().getDimensionPixelSize(R.dimen.edit_photo_size);
-
         mName = (StructuredNameEditorView)findViewById(R.id.edit_name);
         mName.setDeletable(false);
 
         mPhoneticName = (PhoneticNameEditorView)findViewById(R.id.edit_phonetic_name);
+        mPhoneticName.setDeletable(false);
 
         mFields = (ViewGroup)findViewById(R.id.sect_fields);
 
diff --git a/src/com/android/contacts/editor/TextFieldsEditorView.java b/src/com/android/contacts/editor/TextFieldsEditorView.java
index d3fa3b7..a448f4e 100644
--- a/src/com/android/contacts/editor/TextFieldsEditorView.java
+++ b/src/com/android/contacts/editor/TextFieldsEditorView.java
@@ -60,7 +60,7 @@
     private ImageView mExpansionView;
     private boolean mHideOptional = true;
     private boolean mHasShortAndLongForms;
-    private int mHintTextColor;
+    private int mMinFieldHeight;
 
     public TextFieldsEditorView(Context context) {
         super(context);
@@ -82,7 +82,8 @@
         setDrawingCacheEnabled(true);
         setAlwaysDrawnWithCacheEnabled(true);
 
-        mHintTextColor = getContext().getResources().getColor(R.color.secondary_text_color);
+        mMinFieldHeight = mContext.getResources().getDimensionPixelSize(
+                R.dimen.editor_min_line_item_height);
         mFields = (ViewGroup) findViewById(R.id.editors);
         mExpansionView = (ImageView) findViewById(R.id.expansion_view);
         mExpansionViewContainer = findViewById(R.id.expansion_view_container);
@@ -173,9 +174,8 @@
             final EditField field = kind.fieldList.get(index);
             final EditText fieldView = new EditText(mContext);
             fieldView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
-                    LayoutParams.WRAP_CONTENT));
+                    field.isMultiLine() ? LayoutParams.WRAP_CONTENT : mMinFieldHeight));
             fieldView.setTextAppearance(getContext(), kind.textAppearanceResourceId);
-            fieldView.setHintTextColor(mHintTextColor);
             fieldView.setGravity(Gravity.TOP);
             mFieldEditTexts[index] = fieldView;
             fieldView.setId(vig.getId(state, kind, entry, index));
@@ -184,9 +184,6 @@
             }
             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)));
@@ -353,62 +350,6 @@
         };
     }
 
-    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);
-                }
-            }
-        }
-    }
-
     @Override
     public void clearAllFields() {
         if (mFieldEditTexts != null) {
diff --git a/src/com/android/contacts/format/DisplayNameFormatter.java b/src/com/android/contacts/format/DisplayNameFormatter.java
index 07577f2..e525217 100644
--- a/src/com/android/contacts/format/DisplayNameFormatter.java
+++ b/src/com/android/contacts/format/DisplayNameFormatter.java
@@ -65,15 +65,6 @@
 
     public CharSequence getDisplayName(int displayOrder, boolean highlightingEnabled,
             char[] highlightedPrefix) {
-        // Compute the point at which name and alternate name overlap (for bolding).
-        int overlapPoint = FormatUtils.overlapPoint(mNameBuffer, mAlternateNameBuffer);
-        int boldStart = 0;
-        int boldEnd = overlapPoint;
-        if (displayOrder == ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE) {
-            boldStart = overlapPoint;
-            boldEnd = mNameBuffer.sizeCopied;
-        }
-
         int size = mNameBuffer.sizeCopied;
         if (size == 0) {
             return mUnknownNameText;
@@ -93,11 +84,6 @@
         if (highlightedPrefix != null) {
             text = mPrefixHighlighter.apply(text, highlightedPrefix);
         }
-        if (overlapPoint > 0) {
-            // Bold the first or last name.
-            text = FormatUtils.applyStyleToSpan(Typeface.BOLD, text, boldStart, boldEnd,
-                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-        }
         return text;
     }
 }
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index ccc84a2..b060541 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -48,6 +48,8 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Intents;
 import android.text.TextUtils;
@@ -63,7 +65,6 @@
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AutoCompleteTextView;
 import android.widget.BaseAdapter;
-import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.QuickContactBadge;
@@ -73,13 +74,24 @@
 import java.util.ArrayList;
 import java.util.List;
 
-// TODO: Use savedInstanceState
 public class GroupEditorFragment extends Fragment implements SelectAccountDialogFragment.Listener {
-
     private static final String TAG = "GroupEditorFragment";
 
     private static final String LEGACY_CONTACTS_AUTHORITY = "contacts";
 
+    private static final String KEY_ACTION = "action";
+    private static final String KEY_GROUP_URI = "groupUri";
+    private static final String KEY_GROUP_ID = "groupId";
+    private static final String KEY_STATUS = "status";
+    private static final String KEY_ACCOUNT_NAME = "accountName";
+    private static final String KEY_ACCOUNT_TYPE = "accountType";
+    private static final String KEY_DATA_SET = "dataSet";
+    private static final String KEY_GROUP_NAME_IS_READ_ONLY = "groupNameIsReadOnly";
+    private static final String KEY_ORIGINAL_GROUP_NAME = "originalGroupName";
+    private static final String KEY_MEMBERS_TO_ADD = "membersToAdd";
+    private static final String KEY_MEMBERS_TO_REMOVE = "membersToRemove";
+    private static final String KEY_MEMBERS_TO_DISPLAY = "membersToDisplay";
+
     public static interface Listener {
         /**
          * Group metadata was not found, close the fragment now.
@@ -92,11 +104,6 @@
         void onReverted();
 
         /**
-         * Title has been determined.
-         */
-        void onTitleLoaded(int resourceId);
-
-        /**
          * Contact was saved and the Fragment can now be closed safely.
          */
         void onSaveFinished(int resultCode, Intent resultIntent);
@@ -145,7 +152,8 @@
      * Modes that specify the status of the editor
      */
     public enum Status {
-        LOADING,    // Loader is fetching the data
+        SELECTING_ACCOUNT, // Account select dialog is showing
+        LOADING,    // Loader is fetching the group metadata
         EDITING,    // Not currently busy. We are waiting forthe user to enter data.
         SAVING,     // Data is currently being saved
         CLOSING     // Prevents any more saves
@@ -170,10 +178,11 @@
     private TextView mAccountNameTextView;
     private AutoCompleteTextView mAutoCompleteTextView;
 
-    private boolean mGroupNameIsReadOnly;
     private String mAccountName;
     private String mAccountType;
     private String mDataSet;
+
+    private boolean mGroupNameIsReadOnly;
     private String mOriginalGroupName = "";
 
     private MemberListAdapter mMemberListAdapter;
@@ -182,9 +191,9 @@
     private ContentResolver mContentResolver;
     private SuggestedMemberListAdapter mAutoCompleteAdapter;
 
-    private List<Member> mListMembersToAdd = new ArrayList<Member>();
-    private List<Member> mListMembersToRemove = new ArrayList<Member>();
-    private List<Member> mListToDisplay = new ArrayList<Member>();
+    private final List<Member> mListMembersToAdd = new ArrayList<Member>();
+    private final List<Member> mListMembersToRemove = new ArrayList<Member>();
+    private final List<Member> mListToDisplay = new ArrayList<Member>();
 
     public GroupEditorFragment() {
     }
@@ -209,18 +218,19 @@
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
-        // Edit an existing group
-        if (Intent.ACTION_EDIT.equals(mAction)) {
-            if (mListener != null) {
-                mListener.onTitleLoaded(R.string.editGroup_title_edit);
+        if (savedInstanceState != null) {
+            // Just restore from the saved state.  No loading.
+            onRestoreInstanceState(savedInstanceState);
+            if (mStatus == Status.SELECTING_ACCOUNT) {
+                // Account select dialog is showing.  Don't setup the editor yet.
+            } else if (mStatus == Status.LOADING) {
+                startGroupMetaDataLoader();
+            } else {
+                setupEditorForAccount();
             }
-            getLoaderManager().initLoader(LOADER_GROUP_METADATA, null,
-                    mGroupMetaDataLoaderListener);
+        } else if (Intent.ACTION_EDIT.equals(mAction)) {
+            startGroupMetaDataLoader();
         } else if (Intent.ACTION_INSERT.equals(mAction)) {
-            if (mListener != null) {
-                mListener.onTitleLoaded(R.string.editGroup_title_insert);
-            }
-
             final Account account = mIntentExtras == null ? null :
                     (Account) mIntentExtras.getParcelable(Intents.Insert.ACCOUNT);
             final String dataSet = mIntentExtras == null ? null :
@@ -242,6 +252,51 @@
         }
     }
 
+    private void startGroupMetaDataLoader() {
+        mStatus = Status.LOADING;
+        getLoaderManager().initLoader(LOADER_GROUP_METADATA, null,
+                mGroupMetaDataLoaderListener);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putString(KEY_ACTION, mAction);
+        outState.putParcelable(KEY_GROUP_URI, mGroupUri);
+        outState.putLong(KEY_GROUP_ID, mGroupId);
+
+        outState.putSerializable(KEY_STATUS, mStatus);
+        outState.putString(KEY_ACCOUNT_NAME, mAccountName);
+        outState.putString(KEY_ACCOUNT_TYPE, mAccountType);
+        outState.putString(KEY_DATA_SET, mDataSet);
+
+        outState.putBoolean(KEY_GROUP_NAME_IS_READ_ONLY, mGroupNameIsReadOnly);
+        outState.putString(KEY_ORIGINAL_GROUP_NAME, mOriginalGroupName);
+
+        outState.putParcelableArray(KEY_MEMBERS_TO_ADD, Member.toArray(mListMembersToAdd));
+        outState.putParcelableArray(KEY_MEMBERS_TO_REMOVE, Member.toArray(mListMembersToRemove));
+        outState.putParcelableArray(KEY_MEMBERS_TO_DISPLAY, Member.toArray(mListToDisplay));
+    }
+
+    private void onRestoreInstanceState(Bundle state) {
+        mAction = state.getString(KEY_ACTION);
+        mGroupUri = state.getParcelable(KEY_GROUP_URI);
+        mGroupId = state.getLong(KEY_GROUP_ID);
+
+        mStatus = (Status) state.getSerializable(KEY_STATUS);
+        mAccountName = state.getString(KEY_ACCOUNT_NAME);
+        mAccountType = state.getString(KEY_ACCOUNT_TYPE);
+        mDataSet = state.getString(KEY_DATA_SET);
+
+        mGroupNameIsReadOnly = state.getBoolean(KEY_GROUP_NAME_IS_READ_ONLY);
+        mOriginalGroupName = state.getString(KEY_ORIGINAL_GROUP_NAME);
+
+        Member.toList((Member[]) state.getParcelableArray(KEY_MEMBERS_TO_ADD), mListMembersToAdd);
+        Member.toList((Member[]) state.getParcelableArray(KEY_MEMBERS_TO_REMOVE),
+                mListMembersToRemove);
+        Member.toList((Member[]) state.getParcelableArray(KEY_MEMBERS_TO_DISPLAY), mListToDisplay);
+    }
+
     public void setContentResolver(ContentResolver resolver) {
         mContentResolver = resolver;
         if (mAutoCompleteAdapter != null) {
@@ -267,6 +322,7 @@
             return;  // Don't show a dialog.
         }
 
+        mStatus = Status.SELECTING_ACCOUNT;
         final SelectAccountDialogFragment dialog = new SelectAccountDialogFragment(
                 R.string.dialog_new_group_account);
         dialog.setTargetFragment(this, 0);
@@ -289,13 +345,27 @@
         }
     }
 
+    private AccountType getAccountType() {
+        return AccountTypeManager.getInstance(mContext).getAccountType(mAccountType, mDataSet);
+    }
+
+    /**
+     * @return true if the group membership is editable on this account type.  false otherwise,
+     *         or account is not set yet.
+     */
+    private boolean isGroupMembershipEditable() {
+        if (mAccountType == null) {
+            return false;
+        }
+        return getAccountType().isGroupMembershipEditable();
+    }
+
     /**
      * Sets up the editor based on the group's account name and type.
      */
     private void setupEditorForAccount() {
-        final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext);
-        final AccountType accountType = accountTypeManager.getAccountType(mAccountType, mDataSet);
-        final boolean editable = accountType.isGroupMembershipEditable();
+        final AccountType accountType = getAccountType();
+        final boolean editable = isGroupMembershipEditable();
         mMemberListAdapter.setIsGroupMembershipEditable(editable);
 
         View editorView = mLayoutInflater.inflate(editable ?
@@ -345,8 +415,14 @@
                     mAutoCompleteTextView.setText("");
                 }
             });
+            // Update the exempt list.  (mListToDisplay might have been restored from the saved
+            // state.)
+            mAutoCompleteAdapter.updateExistingMembersList(mListToDisplay);
         }
 
+        // If the group name is ready only, don't let the user focus on the field.
+        mGroupNameView.setFocusable(!mGroupNameIsReadOnly);
+
         mRootView.addView(editorView);
         mStatus = Status.EDITING;
     }
@@ -359,30 +435,21 @@
     }
 
     private void bindGroupMetaData(Cursor cursor) {
-        if (cursor.getCount() == 0) {
-            if (mListener != null) {
-                mListener.onGroupNotFound();
-            }
-        }
-        try {
-            cursor.moveToFirst();
-            mOriginalGroupName = cursor.getString(GroupMetaDataLoader.TITLE);
-            mAccountName = cursor.getString(GroupMetaDataLoader.ACCOUNT_NAME);
-            mAccountType = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
-            mGroupNameIsReadOnly = (cursor.getInt(GroupMetaDataLoader.IS_READ_ONLY) == 1);
-        } catch (Exception e) {
+        if (!cursor.moveToFirst()) {
             Log.i(TAG, "Group not found with URI: " + mGroupUri + " Closing activity now.");
             if (mListener != null) {
                 mListener.onGroupNotFound();
             }
-        } finally {
-            cursor.close();
+            return;
         }
+        mOriginalGroupName = cursor.getString(GroupMetaDataLoader.TITLE);
+        mAccountName = cursor.getString(GroupMetaDataLoader.ACCOUNT_NAME);
+        mAccountType = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+        mGroupNameIsReadOnly = (cursor.getInt(GroupMetaDataLoader.IS_READ_ONLY) == 1);
         setupEditorForAccount();
-        // Setup the group metadata display (If the group name is ready only, don't let the user
-        // focus on the field).
+
+        // Setup the group metadata display
         mGroupNameView.setText(mOriginalGroupName);
-        mGroupNameView.setFocusable(!mGroupNameIsReadOnly);
     }
 
     public void loadMemberToAddToGroup(long rawContactId, String contactId) {
@@ -396,8 +463,13 @@
         mListener = value;
     }
 
-    public void doSaveAction() {
-        save(SaveMode.CLOSE);
+    public void onDoneClicked() {
+        if (isGroupMembershipEditable()) {
+            save(SaveMode.CLOSE);
+        } else {
+            // Just revert it.
+            doRevertAction();
+        }
     }
 
     @Override
@@ -596,13 +668,13 @@
         return membersArray;
     }
 
-    private void addExistingMembers(List<Member> members, List<Long> listContactIds) {
+    private void addExistingMembers(List<Member> members) {
         mListToDisplay.addAll(members);
         mMemberListAdapter.notifyDataSetChanged();
 
         // Update the autocomplete adapter (if there is one) so these contacts don't get suggested
         if (mAutoCompleteAdapter != null) {
-            mAutoCompleteAdapter.updateExistingMembersList(listContactIds);
+            mAutoCompleteAdapter.updateExistingMembersList(members);
         }
     }
 
@@ -672,29 +744,23 @@
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-            List<Long> listContactIds = new ArrayList<Long>();
             List<Member> listExistingMembers = new ArrayList<Member>();
-            try {
-                data.moveToPosition(-1);
-                while (data.moveToNext()) {
-                    long contactId = data.getLong(GroupMemberLoader.CONTACT_ID_COLUMN_INDEX);
-                    long rawContactId = data.getLong(GroupMemberLoader.RAW_CONTACT_ID_COLUMN_INDEX);
-                    String lookupKey = data.getString(
-                            GroupMemberLoader.CONTACT_LOOKUP_KEY_COLUMN_INDEX);
-                    String displayName = data.getString(
-                            GroupMemberLoader.CONTACT_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
-                    String photoUri = data.getString(
-                            GroupMemberLoader.CONTACT_PHOTO_URI_COLUMN_INDEX);
-                    listExistingMembers.add(new Member(rawContactId, lookupKey, contactId,
-                            displayName, photoUri));
-                    listContactIds.add(contactId);
-                }
-            } finally {
-                data.close();
+            data.moveToPosition(-1);
+            while (data.moveToNext()) {
+                long contactId = data.getLong(GroupMemberLoader.CONTACT_ID_COLUMN_INDEX);
+                long rawContactId = data.getLong(GroupMemberLoader.RAW_CONTACT_ID_COLUMN_INDEX);
+                String lookupKey = data.getString(
+                        GroupMemberLoader.CONTACT_LOOKUP_KEY_COLUMN_INDEX);
+                String displayName = data.getString(
+                        GroupMemberLoader.CONTACT_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
+                String photoUri = data.getString(
+                        GroupMemberLoader.CONTACT_PHOTO_URI_COLUMN_INDEX);
+                listExistingMembers.add(new Member(rawContactId, lookupKey, contactId,
+                        displayName, photoUri));
             }
 
             // Update the display list
-            addExistingMembers(listExistingMembers, listContactIds);
+            addExistingMembers(listExistingMembers);
 
             // No more updates
             // TODO: move to a runnable
@@ -725,26 +791,17 @@
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
-            // Retrieve the contact data fields that will be sufficient to update the adapter with
-            // a new entry for this contact
-            Member member = null;
-            try {
-                cursor.moveToFirst();
-                long contactId = cursor.getLong(CONTACT_ID_COLUMN_INDEX);
-                String displayName = cursor.getString(CONTACT_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
-                String lookupKey = cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX);
-                String photoUri = cursor.getString(CONTACT_PHOTO_URI_COLUMN_INDEX);
-                getLoaderManager().destroyLoader(LOADER_NEW_GROUP_MEMBER);
-                member = new Member(mRawContactId, lookupKey, contactId, displayName, photoUri);
-            } finally {
-                cursor.close();
-            }
-
-            if (member == null) {
+            if (!cursor.moveToFirst()) {
                 return;
             }
-
-            // Otherwise continue adding the member to list of members
+            // Retrieve the contact data fields that will be sufficient to update the adapter with
+            // a new entry for this contact
+            long contactId = cursor.getLong(CONTACT_ID_COLUMN_INDEX);
+            String displayName = cursor.getString(CONTACT_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
+            String lookupKey = cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX);
+            String photoUri = cursor.getString(CONTACT_PHOTO_URI_COLUMN_INDEX);
+            getLoaderManager().destroyLoader(LOADER_NEW_GROUP_MEMBER);
+            Member member = new Member(mRawContactId, lookupKey, contactId, displayName, photoUri);
             addMember(member);
         }
 
@@ -755,7 +812,9 @@
     /**
      * This represents a single member of the current group.
      */
-    public static class Member {
+    public static class Member implements Parcelable {
+        private static final Member[] EMPTY_ARRAY = new Member[0];
+
         // TODO: Switch to just dealing with raw contact IDs everywhere if possible
         private final long mRawContactId;
         private final long mContactId;
@@ -800,6 +859,55 @@
             }
             return false;
         }
+
+        // Parcelable
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeLong(mRawContactId);
+            dest.writeLong(mContactId);
+            dest.writeParcelable(mLookupUri, flags);
+            dest.writeString(mDisplayName);
+            dest.writeParcelable(mPhotoUri, flags);
+        }
+
+        private Member(Parcel in) {
+            mRawContactId = in.readLong();
+            mContactId = in.readLong();
+            mLookupUri = in.readParcelable(getClass().getClassLoader());
+            mDisplayName = in.readString();
+            mPhotoUri = in.readParcelable(getClass().getClassLoader());
+        }
+
+        public static final Parcelable.Creator<Member> CREATOR = new Parcelable.Creator<Member>() {
+            public Member createFromParcel(Parcel in) {
+                return new Member(in);
+            }
+
+            public Member[] newArray(int size) {
+                return new Member[size];
+            }
+        };
+
+        /** Convert to an array */
+        public static Member[] toArray(List<Member> list) {
+            return list.toArray(EMPTY_ARRAY);
+        }
+
+        /**
+         * Convert to a list.  Instead of creating a new one, this method clears the passed list
+         * and adds elements to it.
+         */
+        public static void toList(Member[] array, List<Member> list) {
+            list.clear();
+            for (Member member : array) {
+                list.add(member);
+            }
+        }
     }
 
     /**
diff --git a/src/com/android/contacts/group/SuggestedMemberListAdapter.java b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
index 653cc25..f671b65 100644
--- a/src/com/android/contacts/group/SuggestedMemberListAdapter.java
+++ b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
@@ -82,7 +82,7 @@
 
     // TODO: Make this a Map for better performance when we check if a new contact is in the list
     // or not
-    private List<Long> mExistingMemberContactIds = new ArrayList<Long>();
+    private final List<Long> mExistingMemberContactIds = new ArrayList<Long>();
 
     private static final int SUGGESTIONS_LIMIT = 5;
 
@@ -107,8 +107,11 @@
         mContentResolver = resolver;
     }
 
-    public void updateExistingMembersList(List<Long> listContactIds) {
-        mExistingMemberContactIds = listContactIds;
+    public void updateExistingMembersList(List<GroupEditorFragment.Member> list) {
+        mExistingMemberContactIds.clear();
+        for (GroupEditorFragment.Member member : list) {
+            mExistingMemberContactIds.add(member.getContactId());
+        }
     }
 
     public void addNewMember(long contactId) {
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 0d4eb8c..cc0a184 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -341,6 +341,7 @@
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         int itemViewType = getItemViewType(position);
+
         if (itemViewType == ViewTypes.DIVIDER) {
             // Checking For Divider First so not to cast convertView
             return convertView == null ? getDivider() : convertView;
@@ -353,7 +354,7 @@
             // Creating new row if needed
             contactTileRowView = new ContactTileRow(mContext, itemViewType);
         }
-        contactTileRowView.configureRow(contactList);
+        contactTileRowView.configureRow(contactList, position == getCount() - 1);
         return contactTileRowView;
     }
 
@@ -455,21 +456,21 @@
         /**
          * Configures the row to add {@link ContactEntry}s information to the views
          */
-        public void configureRow(ArrayList<ContactEntry> list) {
+        public void configureRow(ArrayList<ContactEntry> list, boolean isLastRow) {
             int columnCount = mItemViewType == ViewTypes.FREQUENT ? 1 : mColumnCount;
 
             // Adding tiles to row and filling in contact information
             for (int columnCounter = 0; columnCounter < columnCount; columnCounter++) {
                 ContactEntry entry =
                         columnCounter < list.size() ? list.get(columnCounter) : null;
-                addTileFromEntry(entry, columnCounter);
+                addTileFromEntry(entry, columnCounter, isLastRow);
             }
         }
 
-        private void addTileFromEntry(ContactEntry entry, int tileIndex) {
+        private void addTileFromEntry(ContactEntry entry, int childIndex, boolean isLastRow) {
             final ContactTileView contactTile;
 
-            if (getChildCount() <= tileIndex) {
+            if (getChildCount() <= childIndex) {
                 contactTile = (ContactTileView) inflate(mContext, mLayoutResId, null);
                 contactTile.setLayoutParams(new LinearLayout.LayoutParams(0,
                         LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f));
@@ -477,10 +478,15 @@
                 contactTile.setListener(mContactTileListener);
                 addView(contactTile);
             } else {
-                contactTile = (ContactTileView) getChildAt(tileIndex);
+                contactTile = (ContactTileView) getChildAt(childIndex);
             }
             contactTile.setClickable(entry != null);
             contactTile.loadFromContact(entry);
+
+            contactTile.setVerticalDividerVisibility(
+                    childIndex >= mColumnCount - 1 ? View.GONE : View.VISIBLE);
+            contactTile.setHorizontalDividerVisibility(
+                    isLastRow ? View.GONE : View.VISIBLE);
         }
     }
 
diff --git a/src/com/android/contacts/list/ContactTileView.java b/src/com/android/contacts/list/ContactTileView.java
index 4dfa876..ef9c2fb 100644
--- a/src/com/android/contacts/list/ContactTileView.java
+++ b/src/com/android/contacts/list/ContactTileView.java
@@ -49,6 +49,8 @@
     private ContactPhotoManager mPhotoManager = null;
     private ImageButton mPushState;
     private Listener mListener;
+    private View mVerticalDivider;
+    private View mHorizontalDivider;
 
     public ContactTileView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -66,6 +68,8 @@
         mPhoneLabel = (TextView) findViewById(R.id.contact_tile_phone_type);
         mPhoneNumber = (TextView) findViewById(R.id.contact_tile_phone_number);
         mPushState = (ImageButton) findViewById(R.id.contact_tile_push_state);
+        mVerticalDivider = findViewById(R.id.contact_tile_vertical_divider);
+        mHorizontalDivider = findViewById(R.id.contact_tile_horizontal_divider);
 
         OnClickListener listener = new OnClickListener() {
             @Override
@@ -148,6 +152,14 @@
         }
     }
 
+    public void setVerticalDividerVisibility(int visibility) {
+        if (mVerticalDivider != null) mVerticalDivider.setVisibility(visibility);
+    }
+
+    public void setHorizontalDividerVisibility(int visibility) {
+        if (mHorizontalDivider != null) mHorizontalDivider.setVisibility(visibility);
+    }
+
     public void setListener(Listener listener) {
         mListener = listener;
     }
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index 59b9f45..a78185f 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -31,6 +31,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
+import android.view.inputmethod.EditorInfo;
 import android.widget.EditText;
 
 import java.text.Collator;
@@ -112,6 +113,16 @@
         return null;
     }
 
+    /**
+     * Returns an optional service that can be launched whenever a contact is being looked at.
+     * This allows the sync adapter to provide more up-to-date information.
+     * The service class should reside in the sync adapter package as determined by
+     * {@link #resPackageName}.
+     */
+    public String getViewContactNotifyServiceClassName() {
+        return null;
+    }
+
     public CharSequence getDisplayLabel(Context context) {
         return getResourceText(context, summaryResPackageName, titleRes, accountType);
     }
@@ -357,6 +368,10 @@
             this.isFullName = isFullName;
             return this;
         }
+
+        public boolean isMultiLine() {
+            return (inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
+        }
     }
 
     /**
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index eccf44b..b517c2c 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -69,6 +69,7 @@
      * the available authenticators. This method can safely be called from the UI thread.
      */
     public static AccountTypeManager getInstance(Context context) {
+        context = context.getApplicationContext();
         AccountTypeManager service =
                 (AccountTypeManager) context.getSystemService(ACCOUNT_TYPE_SERVICE);
         if (service == null) {
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index e5af8f8..5dd4d3b 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -57,6 +57,7 @@
     private static final String ATTR_CREATE_CONTACT_ACTIVITY = "createContactActivity";
     private static final String ATTR_INVITE_CONTACT_ACTIVITY = "inviteContactActivity";
     private static final String ATTR_INVITE_CONTACT_ACTION_LABEL = "inviteContactActionLabel";
+    private static final String ATTR_VIEW_CONTACT_NOTIFY_SERVICE = "viewContactNotifyService";
     private static final String ATTR_DATA_SET = "dataSet";
     private static final String ATTR_EXTENSION_PACKAGE_NAMES = "extensionPackageNames";
 
@@ -71,6 +72,7 @@
     private String mCreateContactActivityClassName;
     private String mInviteContactActivity;
     private String mInviteActionLabelAttribute;
+    private String mViewContactNotifyService;
     private List<String> mExtensionPackageNames;
     private int mInviteActionLabelResId;
     private String mAccountTypeLabelAttribute;
@@ -149,6 +151,11 @@
     }
 
     @Override
+    public String getViewContactNotifyServiceClassName() {
+        return mViewContactNotifyService;
+    }
+
+    @Override
     public List<String> getExtensionPackageNames() {
         return mExtensionPackageNames;
     }
@@ -193,6 +200,8 @@
                     mInviteContactActivity = value;
                 } else if (ATTR_INVITE_CONTACT_ACTION_LABEL.equals(attr)) {
                     mInviteActionLabelAttribute = value;
+                } else if (ATTR_VIEW_CONTACT_NOTIFY_SERVICE.equals(attr)) {
+                    mViewContactNotifyService = value;
                 } else if (ATTR_DATA_SET.equals(attr)) {
                     dataSet = value;
                 } else if (ATTR_EXTENSION_PACKAGE_NAMES.equals(attr)) {
diff --git a/src/com/android/contacts/vcard/CancelActivity.java b/src/com/android/contacts/vcard/CancelActivity.java
index b1428bb..c890607 100644
--- a/src/com/android/contacts/vcard/CancelActivity.java
+++ b/src/com/android/contacts/vcard/CancelActivity.java
@@ -72,7 +72,6 @@
     private int mJobId;
     private String mDisplayName;
     private int mType;
-    private Messenger mMessenger;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -121,29 +120,21 @@
     }
 
     @Override
-    public void onServiceConnected(ComponentName name, IBinder service) {
-        mMessenger = new Messenger(service);
+    public void onServiceConnected(ComponentName name, IBinder binder) {
+        VCardService service = ((VCardService.MyBinder) binder).getService();
 
-        boolean callFinish = false;
         try {
             final CancelRequest request = new CancelRequest(mJobId, mDisplayName);
-            mMessenger.send(Message.obtain(null, VCardService.MSG_CANCEL_REQUEST, request));
-            callFinish = true;
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, "RemoteException is thrown when trying to send request");
-            showDialog(R.id.dialog_cancel_failed);
-            // finish() should be called from the Dialog
+            service.handleCancelRequest(request, null);
         } finally {
             unbindService(this);
         }
 
-        if (callFinish) {
-            finish();
-        }
+        finish();
     }
 
     @Override
     public void onServiceDisconnected(ComponentName name) {
-        mMessenger = null;
+        // do nothing
     }
 }
diff --git a/src/com/android/contacts/vcard/ExportProcessor.java b/src/com/android/contacts/vcard/ExportProcessor.java
index 6dc2c34..b52839b 100644
--- a/src/com/android/contacts/vcard/ExportProcessor.java
+++ b/src/com/android/contacts/vcard/ExportProcessor.java
@@ -235,9 +235,11 @@
         final String tickerText =
                 mService.getString(R.string.exporting_contact_list_title);
         final Notification notification =
-                VCardService.constructProgressNotification(mService, VCardService.TYPE_EXPORT,
-                        description, tickerText, mJobId, displayName, totalCount, currentCount);
-        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
+                NotificationImportExportListener.constructProgressNotification(mService,
+                        VCardService.TYPE_EXPORT, description, tickerText, mJobId, displayName,
+                        totalCount, currentCount);
+        mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG,
+                mJobId, notification);
     }
 
     private void doCancelNotification() {
@@ -245,16 +247,19 @@
         final String description = mService.getString(R.string.exporting_vcard_canceled_title,
                 mExportRequest.destUri.getLastPathSegment());
         final Notification notification =
-                VCardService.constructCancelNotification(mService, description);
-        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
+                NotificationImportExportListener.constructCancelNotification(mService, description);
+        mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG,
+                mJobId, notification);
     }
 
     private void doFinishNotification(final String title, final String description) {
         if (DEBUG) Log.d(LOG_TAG, "send finish notification: " + title + ", " + description);
         final Intent intent = new Intent(mService, PeopleActivity.class);
         final Notification notification =
-                VCardService.constructFinishNotification(mService, title, description, intent);
-        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
+                NotificationImportExportListener.constructFinishNotification(mService, title,
+                        description, intent);
+        mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG,
+                mJobId, notification);
     }
 
     @Override
diff --git a/src/com/android/contacts/vcard/ExportVCardActivity.java b/src/com/android/contacts/vcard/ExportVCardActivity.java
index 8bd9899..a0fd4d8 100644
--- a/src/com/android/contacts/vcard/ExportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ExportVCardActivity.java
@@ -110,7 +110,7 @@
      */
     private volatile boolean mProcessOngoing = true;
 
-    private Messenger mOutgoingMessenger;
+    private VCardService mService;
     private final Messenger mIncomingMessenger = new Messenger(new IncomingHandler());
 
     // Used temporarily when asking users to confirm the file name
@@ -138,10 +138,8 @@
                 }
                 final ExportRequest request = new ExportRequest(mDestinationUri);
                 // The connection object will call finish().
-                if (trySend(Message.obtain(null, VCardService.MSG_EXPORT_REQUEST, request))) {
-                    Log.i(LOG_TAG, "Successfully sent export request. Finish itself");
-                    unbindAndFinish();
-                }
+                mService.handleExportRequest(request, new NotificationImportExportListener(
+                        ExportVCardActivity.this));
             }
         }
     }
@@ -160,14 +158,16 @@
             return;
         }
 
-        if (startService(new Intent(this, VCardService.class)) == null) {
+        Intent intent = new Intent(this, VCardService.class);
+
+        if (startService(intent) == null) {
             Log.e(LOG_TAG, "Failed to start vCard service");
             mErrorReason = getString(R.string.fail_reason_unknown);
             showDialog(R.id.dialog_fail_to_export_with_reason);
             return;
         }
 
-        if (!bindService(new Intent(this, VCardService.class), this, Context.BIND_AUTO_CREATE)) {
+        if (!bindService(intent, this, Context.BIND_AUTO_CREATE)) {
             Log.e(LOG_TAG, "Failed to connect to vCard service.");
             mErrorReason = getString(R.string.fail_reason_unknown);
             showDialog(R.id.dialog_fail_to_export_with_reason);
@@ -176,14 +176,11 @@
     }
 
     @Override
-    public synchronized void onServiceConnected(ComponentName name, IBinder service) {
+    public synchronized void onServiceConnected(ComponentName name, IBinder binder) {
         if (DEBUG) Log.d(LOG_TAG, "connected to service, requesting a destination file name");
         mConnected = true;
-        mOutgoingMessenger = new Messenger(service);
-        final Message message =
-                Message.obtain(null, VCardService.MSG_REQUEST_AVAILABLE_EXPORT_DESTINATION);
-        message.replyTo = mIncomingMessenger;
-        trySend(message);
+        mService = ((VCardService.MyBinder) binder).getService();
+        mService.handleRequestAvailableExportDestination(mIncomingMessenger);
         // Wait until MSG_SET_AVAILABLE_EXPORT_DESTINATION message is available.
     }
 
@@ -191,7 +188,7 @@
     @Override
     public synchronized void onServiceDisconnected(ComponentName name) {
         if (DEBUG) Log.d(LOG_TAG, "onServiceDisconnected()");
-        mOutgoingMessenger = null;
+        mService = null;
         mConnected = false;
         if (mProcessOngoing) {
             // Unexpected disconnect event.
@@ -267,19 +264,6 @@
         }
     }
 
-    private boolean trySend(Message message) {
-        try {
-            mOutgoingMessenger.send(message);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, "RemoteException is thrown when trying to send request");
-            unbindService(this);
-            mErrorReason = getString(R.string.fail_reason_unknown);
-            showDialog(R.id.dialog_fail_to_export_with_reason);
-            return false;
-        }
-    }
-
     @Override
     public void onClick(DialogInterface dialog, int which) {
         if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onClick() is called");
diff --git a/src/com/android/contacts/vcard/ImportProcessor.java b/src/com/android/contacts/vcard/ImportProcessor.java
index 3092087..e8a9193 100644
--- a/src/com/android/contacts/vcard/ImportProcessor.java
+++ b/src/com/android/contacts/vcard/ImportProcessor.java
@@ -15,9 +15,10 @@
  */
 package com.android.contacts.vcard;
 
-import com.android.contacts.R;
+import com.android.vcard.VCardEntry;
 import com.android.vcard.VCardEntryCommitter;
 import com.android.vcard.VCardEntryConstructor;
+import com.android.vcard.VCardEntryHandler;
 import com.android.vcard.VCardInterpreter;
 import com.android.vcard.VCardParser;
 import com.android.vcard.VCardParser_V21;
@@ -28,14 +29,8 @@
 import com.android.vcard.exception.VCardVersionException;
 
 import android.accounts.Account;
-import android.app.Notification;
-import android.app.NotificationManager;
 import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
 import android.net.Uri;
-import android.provider.ContactsContract.RawContacts;
 import android.util.Log;
 
 import java.io.ByteArrayInputStream;
@@ -48,17 +43,15 @@
  * Class for processing one import request from a user. Dropped after importing requested Uri(s).
  * {@link VCardService} will create another object when there is another import request.
  */
-public class ImportProcessor extends ProcessorBase {
+public class ImportProcessor extends ProcessorBase implements VCardEntryHandler {
     private static final String LOG_TAG = "VCardImport";
     private static final boolean DEBUG = VCardService.DEBUG;
 
     private final VCardService mService;
     private final ContentResolver mResolver;
-    private final NotificationManager mNotificationManager;
     private final ImportRequest mImportRequest;
     private final int mJobId;
-
-    private final ImportProgressNotifier mNotifier;
+    private final VCardImportExportListener mListener;
 
     // TODO: remove and show appropriate message instead.
     private final List<Uri> mFailedUris = new ArrayList<Uri>();
@@ -68,17 +61,35 @@
     private volatile boolean mCanceled;
     private volatile boolean mDone;
 
-    public ImportProcessor(final VCardService service, final ImportRequest request,
-            final int jobId) {
+    private int mCurrentCount = 0;
+    private int mTotalCount = 0;
+
+    public ImportProcessor(final VCardService service, final VCardImportExportListener listener,
+            final ImportRequest request, final int jobId) {
         mService = service;
         mResolver = mService.getContentResolver();
-        mNotificationManager = (NotificationManager)
-                mService.getSystemService(Context.NOTIFICATION_SERVICE);
+        mListener = listener;
 
         mImportRequest = request;
         mJobId = jobId;
-        mNotifier = new ImportProgressNotifier(service, mNotificationManager, jobId,
-                request.displayName);
+    }
+
+    @Override
+    public void onStart() {
+        // do nothing
+    }
+
+    @Override
+    public void onEnd() {
+        // do nothing
+    }
+
+    @Override
+    public void onEntryCreated(VCardEntry entry) {
+        mCurrentCount++;
+        if (mListener != null) {
+            mListener.onImportParsed(mImportRequest, mJobId, entry, mCurrentCount, mTotalCount);
+        }
     }
 
     @Override
@@ -92,8 +103,8 @@
         try {
             runInternal();
 
-            if (isCancelled()) {
-                doCancelNotification();
+            if (isCancelled() && mListener != null) {
+                mListener.onImportCanceled(mImportRequest, mJobId);
             }
         } catch (OutOfMemoryError e) {
             Log.e(LOG_TAG, "OutOfMemoryError thrown during import", e);
@@ -136,15 +147,13 @@
         final int estimatedVCardType = request.estimatedVCardType;
         final String estimatedCharset = request.estimatedCharset;
         final int entryCount = request.entryCount;
-        mNotifier.addTotalCount(entryCount);
+        mTotalCount += entryCount;
 
         final VCardEntryConstructor constructor =
                 new VCardEntryConstructor(estimatedVCardType, account, estimatedCharset);
         final VCardEntryCommitter committer = new VCardEntryCommitter(mResolver);
         constructor.addEntryHandler(committer);
-        if (!request.showImmediately) {
-            constructor.addEntryHandler(mNotifier);
-        }
+        constructor.addEntryHandler(this);
 
         InputStream is = null;
         boolean successful = false;
@@ -184,15 +193,17 @@
             } else {
                 Log.i(LOG_TAG, "Successfully finished importing one vCard file: " + uri);
                 List<Uri> uris = committer.getCreatedUris();
-                if (uris != null && uris.size() > 0) {
-                    // TODO: construct intent showing a list of imported contact list.
-                    doFinishNotification(uris.get(0));
-                } else {
-                    // Not critical, but suspicious.
-                    Log.w(LOG_TAG,
-                            "Created Uris is null or 0 length " +
-                            "though the creation itself is successful.");
-                    doFinishNotification(null);
+                if (mListener != null) {
+                    if (uris != null && uris.size() > 0) {
+                        // TODO: construct intent showing a list of imported contact list.
+                        mListener.onImportFinished(mImportRequest, mJobId, uris.get(0));
+                    } else {
+                        // Not critical, but suspicious.
+                        Log.w(LOG_TAG,
+                                "Created Uris is null or 0 length " +
+                                "though the creation itself is successful.");
+                        mListener.onImportFinished(mImportRequest, mJobId, null);
+                    }
                 }
             }
         } else {
@@ -201,39 +212,6 @@
         }
     }
 
-    private void doCancelNotification() {
-        final String description = mService.getString(R.string.importing_vcard_canceled_title,
-                mImportRequest.displayName);
-        final Notification notification =
-                VCardService.constructCancelNotification(mService, description);
-        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
-    }
-
-    private void doFinishNotification(final Uri createdUri) {
-        final String description = mService.getString(R.string.importing_vcard_finished_title,
-                mImportRequest.displayName);
-        final Intent intent;
-        if (createdUri != null) {
-            final long rawContactId = ContentUris.parseId(createdUri);
-            final Uri contactUri = RawContacts.getContactLookupUri(
-                    mResolver, ContentUris.withAppendedId(
-                            RawContacts.CONTENT_URI, rawContactId));
-            intent = new Intent(Intent.ACTION_VIEW, contactUri);
-        } else {
-            intent = null;
-        }
-        if (mImportRequest.showImmediately && (intent != null)) {
-            mNotificationManager.cancel(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            mService.startActivity(intent);
-        } else {
-            final Notification notification = VCardService.constructFinishNotification(mService,
-                    description, null, intent);
-            mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId,
-                    notification);
-        }
-    }
-
     private boolean readOneVCard(InputStream is, int vcardType, String charset,
             final VCardInterpreter interpreter,
             final int[] possibleVCardVersions) {
diff --git a/src/com/android/contacts/vcard/ImportProgressNotifier.java b/src/com/android/contacts/vcard/ImportProgressNotifier.java
deleted file mode 100644
index 698487d..0000000
--- a/src/com/android/contacts/vcard/ImportProgressNotifier.java
+++ /dev/null
@@ -1,83 +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.vcard;
-
-import com.android.contacts.R;
-import com.android.vcard.VCardEntry;
-import com.android.vcard.VCardEntryHandler;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
-
-/**
- * {@link VCardEntryHandler} implementation letting the system update the current status of
- * vCard import.
- */
-public class ImportProgressNotifier implements VCardEntryHandler {
-    private static final String LOG_TAG = "VCardImport";
-
-    private final Context mContext;
-    private final NotificationManager mNotificationManager;
-    private final int mJobId;
-    private final String mDisplayName;
-
-    private int mCurrentCount;
-    private int mTotalCount;
-
-    public ImportProgressNotifier(
-            Context context, NotificationManager notificationManager,
-            int jobId, String displayName) {
-        mContext = context;
-        mNotificationManager = notificationManager;
-        mJobId = jobId;
-        mDisplayName = displayName;
-    }
-
-    public void onStart() {
-    }
-
-    public void onEntryCreated(VCardEntry contactStruct) {
-        mCurrentCount++;  // 1 origin.
-        if (contactStruct.isIgnorable()) {
-            return;
-        }
-
-        final String totalCountString;
-        synchronized (this) {
-            totalCountString = String.valueOf(mTotalCount);
-        }
-        final String tickerText =
-                mContext.getString(R.string.progress_notifier_message,
-                        String.valueOf(mCurrentCount),
-                        totalCountString,
-                        contactStruct.getDisplayName());
-        final String description = mContext.getString(R.string.importing_vcard_description,
-                contactStruct.getDisplayName());
-
-        final Notification notification = VCardService.constructProgressNotification(
-                mContext.getApplicationContext(), VCardService.TYPE_IMPORT, description, tickerText,
-                mJobId, mDisplayName, mTotalCount, mCurrentCount);
-        mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mJobId, notification);
-    }
-
-    public synchronized void addTotalCount(int additionalCount) {
-        mTotalCount += additionalCount;
-    }
-
-    public void onEnd() {
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/contacts/vcard/ImportRequest.java b/src/com/android/contacts/vcard/ImportRequest.java
index 84fbb0e..68b5424 100644
--- a/src/com/android/contacts/vcard/ImportRequest.java
+++ b/src/com/android/contacts/vcard/ImportRequest.java
@@ -56,12 +56,6 @@
     public final String displayName;
 
     /**
-     * Whether to show the imported vcard immediately after the import is done.
-     * If set to false, just a notification will be shown.
-     */
-    public final boolean showImmediately;
-
-    /**
      * Can be {@link VCardSourceDetector#PARSE_TYPE_UNKNOWN}.
      */
     public final int estimatedVCardType;
@@ -103,7 +97,7 @@
 
     public ImportRequest(Account account,
             byte[] data, Uri uri, String displayName, int estimatedType, String estimatedCharset,
-            int vcardVersion, int entryCount, boolean showImmediately) {
+            int vcardVersion, int entryCount) {
         this.account = account;
         this.data = data;
         this.uri = uri;
@@ -112,6 +106,5 @@
         this.estimatedCharset = estimatedCharset;
         this.vcardVersion = vcardVersion;
         this.entryCount = entryCount;
-        this.showImmediately = showImmediately;
     }
 }
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index 28b8540..2baa9cb 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -128,6 +128,7 @@
 
     private VCardCacheThread mVCardCacheThread;
     private ImportRequestConnection mConnection;
+    /* package */ VCardImportExportListener mListener;
 
     private String mErrorMessage;
 
@@ -188,23 +189,16 @@
     private CancelListener mCancelListener = new CancelListener();
 
     private class ImportRequestConnection implements ServiceConnection {
-        private Messenger mMessenger;
+        private VCardService mService;
 
         public void sendImportRequest(final List<ImportRequest> requests) {
             Log.i(LOG_TAG, "Send an import request");
-            try {
-                mMessenger.send(Message.obtain(null,
-                        VCardService.MSG_IMPORT_REQUEST,
-                        requests));
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "RemoteException is thrown when trying to send request");
-                runOnUiThread(new DialogDisplayer(getString(R.string.fail_reason_unknown)));
-            }
+            mService.handleImportRequest(requests, mListener);
         }
 
         @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            mMessenger = new Messenger(service);
+        public void onServiceConnected(ComponentName name, IBinder binder) {
+            mService = ((VCardService.MyBinder) binder).getService();
             Log.i(LOG_TAG,
                     String.format("Connected to VCardService. Kick a vCard cache thread (uri: %s)",
                             Arrays.toString(mVCardCacheThread.getSourceUris())));
@@ -233,7 +227,6 @@
         private final Uri[] mSourceUris;  // Given from a caller.
         private final byte[] mSource;
         private final String mDisplayName;
-        private final boolean mShowImmediately;
 
         public VCardCacheThread(final Uri[] sourceUris) {
             mSourceUris = sourceUris;
@@ -245,24 +238,6 @@
                     PowerManager.SCREEN_DIM_WAKE_LOCK |
                     PowerManager.ON_AFTER_RELEASE, LOG_TAG);
             mDisplayName = null;
-            // Showing immediately could make sense here if we restrict
-            // it to cases where we import a single vcard. For now disable
-            // this feature though.
-            mShowImmediately = false;
-        }
-
-        public VCardCacheThread(final byte[] data, String displayName,
-                final boolean showImmediately) {
-            mSource = data;
-            mSourceUris = null;
-            final Context context = ImportVCardActivity.this;
-            final PowerManager powerManager =
-                    (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-            mWakeLock = powerManager.newWakeLock(
-                    PowerManager.SCREEN_DIM_WAKE_LOCK |
-                    PowerManager.ON_AFTER_RELEASE, LOG_TAG);
-            mDisplayName = displayName;
-            mShowImmediately = showImmediately;
         }
 
         @Override
@@ -301,8 +276,7 @@
                 ArrayList<ImportRequest> requests = new ArrayList<ImportRequest>();
                 if (mSource != null) {
                     try {
-                        requests.add(constructImportRequest(mSource, null, mDisplayName,
-                                mShowImmediately));
+                        requests.add(constructImportRequest(mSource, null, mDisplayName));
                     } catch (VCardException e) {
                         Log.e(LOG_TAG, "Maybe the file is in wrong format", e);
                         showFailureNotification(R.string.fail_reason_not_supported);
@@ -336,7 +310,7 @@
                         final ImportRequest request;
                         try {
                             request = constructImportRequest(null, localDataUri,
-                                    sourceUri.getLastPathSegment(), mShowImmediately);
+                                    sourceUri.getLastPathSegment());
                         } catch (VCardException e) {
                             Log.e(LOG_TAG, "Maybe the file is in wrong format", e);
                             showFailureNotification(R.string.fail_reason_not_supported);
@@ -436,8 +410,7 @@
          * {@link ImportRequest#displayName}.
          */
         private ImportRequest constructImportRequest(final byte[] data,
-                final Uri localDataUri, final String displayName,
-                final boolean showImmediately)
+                final Uri localDataUri, final String displayName)
                 throws IOException, VCardException {
             final ContentResolver resolver = ImportVCardActivity.this.getContentResolver();
             VCardEntryCounter counter = null;
@@ -499,8 +472,7 @@
                     data, localDataUri, displayName,
                     detector.getEstimatedType(),
                     detector.getEstimatedCharset(),
-                    vcardVersion, counter.getCount(),
-                    showImmediately);
+                    vcardVersion, counter.getCount());
         }
 
         public Uri[] getSourceUris() {
@@ -760,19 +732,10 @@
 
     private void importVCard(final Uri[] uris) {
         runOnUiThread(new Runnable() {
+            @Override
             public void run() {
                 mVCardCacheThread = new VCardCacheThread(uris);
-                showDialog(R.id.dialog_cache_vcard);
-            }
-        });
-    }
-
-    private void importVCard(final byte[] data, final String displayName,
-            final boolean showImmediately) {
-        runOnUiThread(new Runnable() {
-            public void run() {
-                mVCardCacheThread = new VCardCacheThread(data, displayName,
-                        showImmediately);
+                mListener = new NotificationImportExportListener(ImportVCardActivity.this);
                 showDialog(R.id.dialog_cache_vcard);
             }
         });
@@ -888,32 +851,14 @@
 
     private void startImport() {
         Intent intent = getIntent();
-        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
-            // Handle inbound NDEF
-            NdefMessage msg = (NdefMessage) intent.getParcelableArrayExtra(
-                    NfcAdapter.EXTRA_NDEF_MESSAGES)[0];
-            NdefRecord record = msg.getRecords()[0];
-            String type = new String(record.getType(), Charset.forName("UTF8"));
-            if (record.getTnf() != NdefRecord.TNF_MIME_MEDIA ||
-                    (!"text/x-vcard".equalsIgnoreCase(type) && !"text/vcard".equals(type))) {
-                Log.d(LOG_TAG, "Not a vcard");
-                showFailureNotification(R.string.fail_reason_not_supported);
-                finish();
-                return;
-            }
-            // For NFC imports, we always show the contact once import is
-            // complete.
-            importVCard(record.getPayload(), getString(R.string.nfc_vcard_file_name), true);
+        // Handle inbound files
+        Uri uri = intent.getData();
+        if (uri != null) {
+            Log.i(LOG_TAG, "Starting vCard import using Uri " + uri);
+            importVCard(uri);
         } else {
-            // Handle inbound files
-            Uri uri = intent.getData();
-            if (uri != null) {
-                Log.i(LOG_TAG, "Starting vCard import using Uri " + uri);
-                importVCard(uri);
-            } else {
-                Log.i(LOG_TAG, "Start vCard without Uri. The user will select vCard manually.");
-                doScanExternalStorageAndImportVCard();
-            }
+            Log.i(LOG_TAG, "Start vCard without Uri. The user will select vCard manually.");
+            doScanExternalStorageAndImportVCard();
         }
     }
 
@@ -975,13 +920,7 @@
                     mProgressDialogForCachingVCard.setMessage(message);
                     mProgressDialogForCachingVCard.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                     mProgressDialogForCachingVCard.setOnCancelListener(mVCardCacheThread);
-                    mConnection = new ImportRequestConnection();
-
-                    Log.i(LOG_TAG, "Bind to VCardService.");
-                    // We don't want the service finishes itself just after this connection.
-                    startService(new Intent(this, VCardService.class));
-                    bindService(new Intent(this, VCardService.class),
-                            mConnection, Context.BIND_AUTO_CREATE);
+                    startVCardService();
                 }
                 return mProgressDialogForCachingVCard;
             }
@@ -1015,6 +954,17 @@
         return super.onCreateDialog(resId, bundle);
     }
 
+    /* package */ void startVCardService() {
+        mConnection = new ImportRequestConnection();
+
+        Log.i(LOG_TAG, "Bind to VCardService.");
+        // We don't want the service finishes itself just after this connection.
+        Intent intent = new Intent(this, VCardService.class);
+        startService(intent);
+        bindService(new Intent(this, VCardService.class),
+                mConnection, Context.BIND_AUTO_CREATE);
+    }
+
     @Override
     protected void onRestoreInstanceState(Bundle savedInstanceState) {
         super.onRestoreInstanceState(savedInstanceState);
@@ -1052,11 +1002,11 @@
         final NotificationManager notificationManager =
                 (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
         final Notification notification =
-                VCardService.constructImportFailureNotification(
+                NotificationImportExportListener.constructImportFailureNotification(
                         ImportVCardActivity.this,
                         getString(reasonId));
-        notificationManager.notify(VCardService.FAILURE_NOTIFICATION_TAG, FAILURE_NOTIFICATION_ID,
-                notification);
+        notificationManager.notify(NotificationImportExportListener.FAILURE_NOTIFICATION_TAG,
+                FAILURE_NOTIFICATION_ID, notification);
         mHandler.post(new Runnable() {
             @Override
             public void run() {
diff --git a/src/com/android/contacts/vcard/NfcImportVCardActivity.java b/src/com/android/contacts/vcard/NfcImportVCardActivity.java
new file mode 100644
index 0000000..d182cfd
--- /dev/null
+++ b/src/com/android/contacts/vcard/NfcImportVCardActivity.java
@@ -0,0 +1,272 @@
+/*
+ * 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.vcard;
+
+import com.android.contacts.R;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
+import com.android.vcard.VCardEntry;
+import com.android.vcard.VCardEntryCounter;
+import com.android.vcard.VCardParser;
+import com.android.vcard.VCardParser_V21;
+import com.android.vcard.VCardParser_V30;
+import com.android.vcard.VCardSourceDetector;
+import com.android.vcard.exception.VCardException;
+import com.android.vcard.exception.VCardNestedException;
+import com.android.vcard.exception.VCardVersionException;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+public class NfcImportVCardActivity extends Activity implements ServiceConnection,
+        VCardImportExportListener {
+    private static final String TAG = "NfcImportVCardActivity";
+
+    private static final int SELECT_ACCOUNT = 1;
+
+    private NdefRecord mRecord;
+    private AccountWithDataSet mAccount;
+
+    /* package */ class ImportTask extends AsyncTask<VCardService, Void, ImportRequest> {
+        @Override
+        public ImportRequest doInBackground(VCardService... services) {
+            ImportRequest request = createImportRequest();
+            if (request == null) {
+                return null;
+            }
+
+            ArrayList<ImportRequest> requests = new ArrayList<ImportRequest>();
+            requests.add(request);
+            services[0].handleImportRequest(requests, NfcImportVCardActivity.this);
+            return request;
+        }
+
+        @Override
+        public void onCancelled() {
+            unbindService(NfcImportVCardActivity.this);
+        }
+
+        @Override
+        public void onPostExecute(ImportRequest request) {
+            unbindService(NfcImportVCardActivity.this);
+        }
+    }
+
+    /* package */ ImportRequest createImportRequest() {
+        VCardParser parser;
+        VCardEntryCounter counter = null;
+        VCardSourceDetector detector = null;
+        int vcardVersion = ImportVCardActivity.VCARD_VERSION_V21;
+        try {
+            ByteArrayInputStream is = new ByteArrayInputStream(mRecord.getPayload());
+            is.mark(0);
+            parser = new VCardParser_V21();
+            try {
+                counter = new VCardEntryCounter();
+                detector = new VCardSourceDetector();
+                parser.addInterpreter(counter);
+                parser.addInterpreter(detector);
+                parser.parse(is);
+            } catch (VCardVersionException e1) {
+                is.reset();
+                vcardVersion = ImportVCardActivity.VCARD_VERSION_V30;
+                parser = new VCardParser_V30();
+                try {
+                    counter = new VCardEntryCounter();
+                    detector = new VCardSourceDetector();
+                    parser.addInterpreter(counter);
+                    parser.addInterpreter(detector);
+                    parser.parse(is);
+                } catch (VCardVersionException e2) {
+                    return null;
+                }
+            } finally {
+                try {
+                    if (is != null) is.close();
+                } catch (IOException e) {
+                }
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Failed reading vcard data", e);
+            return null;
+        } catch (VCardNestedException e) {
+            Log.w(TAG, "Nested Exception is found (it may be false-positive).");
+            // Go through without throwing the Exception, as we may be able to detect the
+            // version before it
+        } catch (VCardException e) {
+            Log.e(TAG, "Error parsing vcard", e);
+            return null;
+        }
+
+        return new ImportRequest(mAccount, mRecord.getPayload(), null,
+                getString(R.string.nfc_vcard_file_name), detector.getEstimatedType(),
+                detector.getEstimatedCharset(), vcardVersion, counter.getCount());
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName name, IBinder binder) {
+        VCardService service = ((VCardService.MyBinder) binder).getService();
+        new ImportTask().execute(service);
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName name) {
+        // Do nothing
+    }
+
+    @Override
+    protected void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+
+        Intent intent = getIntent();
+        if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
+            Log.w(TAG, "Unknowon intent " + intent);
+            finish();
+        }
+
+        NdefMessage msg = (NdefMessage) intent.getParcelableArrayExtra(
+                NfcAdapter.EXTRA_NDEF_MESSAGES)[0];
+        NdefRecord records[] = msg.getRecords();
+        if (records == null || records.length == 0) {
+            Log.w(TAG, "No records " + intent);
+            finish();
+        }
+
+        NdefRecord record = records[0];
+        String type = new String(record.getType(), Charset.forName("UTF8"));
+        if (record.getTnf() != NdefRecord.TNF_MIME_MEDIA ||
+                (!"text/x-vcard".equalsIgnoreCase(type) && !"text/vcard".equals(type))) {
+            Log.w(TAG, "Not a vcard");
+            //setStatus(getString(R.string.fail_reason_not_supported));
+            return;
+        }
+        mRecord = record;
+
+        final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
+        final List<AccountWithDataSet> accountList = accountTypes.getAccounts(true);
+        if (accountList.size() == 0) {
+            mAccount = null;
+        } else if (accountList.size() == 1) {
+            mAccount = accountList.get(0);
+        } else {
+            startActivityForResult(new Intent(this, SelectAccountActivity.class), SELECT_ACCOUNT);
+            return;
+        }
+
+        startImport();
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+        if (requestCode == SELECT_ACCOUNT) {
+            if (resultCode == RESULT_OK) {
+                mAccount = new AccountWithDataSet(
+                        intent.getStringExtra(SelectAccountActivity.ACCOUNT_NAME),
+                        intent.getStringExtra(SelectAccountActivity.ACCOUNT_TYPE),
+                        intent.getStringExtra(SelectAccountActivity.DATA_SET));
+                startImport();
+            } else {
+                finish();
+            }
+        }
+    }
+
+    private void startImport() {
+        // We don't want the service finishes itself just after this connection.
+        Intent intent = new Intent(this, VCardService.class);
+        startService(intent);
+        bindService(intent, this, Context.BIND_AUTO_CREATE);
+    }
+
+    @Override
+    public void onImportProcessed(ImportRequest request, int jobId, int sequence) {
+        // do nothing
+    }
+
+    @Override
+    public void onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
+            int totalCount) {
+        // do nothing
+    }
+
+    @Override
+    public void onImportFinished(ImportRequest request, int jobId, Uri uri) {
+        if (isFinishing()) {
+            Log.i(TAG, "Late import -- ignoring");
+            return;
+        }
+
+        if (uri != null) {
+            Uri contactUri = RawContacts.getContactLookupUri(getContentResolver(), uri);
+            Intent intent = new Intent(Intent.ACTION_VIEW, contactUri);
+            startActivity(intent);
+            finish();
+        }
+    }
+
+    @Override
+    public void onImportFailed(ImportRequest request) {
+        if (isFinishing()) {
+            Log.i(TAG, "Late import failure -- ignoring");
+            return;
+        }
+        // TODO: report failure
+    }
+
+    @Override
+    public void onImportCanceled(ImportRequest request, int jobId) {
+        // do nothing
+    }
+
+    @Override
+    public void onExportProcessed(ExportRequest request, int jobId) {
+        // do nothing
+    }
+
+    @Override
+    public void onExportFailed(ExportRequest request) {
+        // do nothing
+    }
+
+    @Override
+    public void onCancelRequest(CancelRequest request, int type) {
+        // do nothing
+    }
+
+    @Override
+    public void onComplete() {
+        // do nothing
+    }
+}
diff --git a/src/com/android/contacts/vcard/NotificationImportExportListener.java b/src/com/android/contacts/vcard/NotificationImportExportListener.java
new file mode 100644
index 0000000..fb83cec
--- /dev/null
+++ b/src/com/android/contacts/vcard/NotificationImportExportListener.java
@@ -0,0 +1,302 @@
+/*
+ * 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.vcard;
+
+import com.android.contacts.R;
+import com.android.vcard.VCardEntry;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.ContactsContract.RawContacts;
+import android.widget.RemoteViews;
+import android.widget.Toast;
+
+public class NotificationImportExportListener implements VCardImportExportListener,
+        Handler.Callback {
+    /** The tag used by vCard-related notifications. */
+    /* package */ static final String DEFAULT_NOTIFICATION_TAG = "VCardServiceProgress";
+    /**
+     * The tag used by vCard-related failure notifications.
+     * <p>
+     * Use a different tag from {@link #DEFAULT_NOTIFICATION_TAG} so that failures do not get
+     * replaced by other notifications and vice-versa.
+     */
+    /* package */ static final String FAILURE_NOTIFICATION_TAG = "VCardServiceFailure";
+
+    private final NotificationManager mNotificationManager;
+    private final Activity mContext;
+    private final Handler mHandler;
+
+    public NotificationImportExportListener(Activity activity) {
+        mContext = activity;
+        mNotificationManager = (NotificationManager) activity.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+        mHandler = new Handler(this);
+    }
+
+    @Override
+    public boolean handleMessage(Message msg) {
+        String text = (String) msg.obj;
+        Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
+        return true;
+    }
+
+    @Override
+    public void onImportProcessed(ImportRequest request, int jobId, int sequence) {
+        // Show a notification about the status
+        final String displayName;
+        final String message;
+        if (request.displayName != null) {
+            displayName = request.displayName;
+            message = mContext.getString(R.string.vcard_import_will_start_message, displayName);
+        } else {
+            displayName = mContext.getString(R.string.vcard_unknown_filename);
+            message = mContext.getString(
+                    R.string.vcard_import_will_start_message_with_default_name);
+        }
+
+        // We just want to show notification for the first vCard.
+        if (sequence == 0) {
+            // TODO: Ideally we should detect the current status of import/export and
+            // show "started" when we can import right now and show "will start" when
+            // we cannot.
+            mHandler.obtainMessage(0, message).sendToTarget();
+        }
+
+        final Notification notification = constructProgressNotification(mContext,
+                VCardService.TYPE_IMPORT, message, message, jobId, displayName, -1, 0);
+        mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
+    }
+
+    @Override
+    public void onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
+            int totalCount) {
+        if (entry.isIgnorable()) {
+            return;
+        }
+
+        final String totalCountString = String.valueOf(totalCount);
+        final String tickerText =
+                mContext.getString(R.string.progress_notifier_message,
+                        String.valueOf(currentCount),
+                        totalCountString,
+                        entry.getDisplayName());
+        final String description = mContext.getString(R.string.importing_vcard_description,
+                entry.getDisplayName());
+
+        final Notification notification = constructProgressNotification(
+                mContext.getApplicationContext(), VCardService.TYPE_IMPORT, description, tickerText,
+                jobId, request.displayName, totalCount, currentCount);
+        mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
+    }
+
+    @Override
+    public void onImportFinished(ImportRequest request, int jobId, Uri createdUri) {
+        final String description = mContext.getString(R.string.importing_vcard_finished_title,
+                request.displayName);
+        final Intent intent;
+        if (createdUri != null) {
+            final long rawContactId = ContentUris.parseId(createdUri);
+            final Uri contactUri = RawContacts.getContactLookupUri(
+                    mContext.getContentResolver(), ContentUris.withAppendedId(
+                            RawContacts.CONTENT_URI, rawContactId));
+            intent = new Intent(Intent.ACTION_VIEW, contactUri);
+        } else {
+            intent = null;
+        }
+        final Notification notification =
+                NotificationImportExportListener.constructFinishNotification(mContext,
+                description, null, intent);
+        mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG,
+                jobId, notification);
+    }
+
+    @Override
+    public void onImportFailed(ImportRequest request) {
+        // TODO: a little unkind to show Toast in this case, which is shown just a moment.
+        // Ideally we should show some persistent something users can notice more easily.
+        mHandler.obtainMessage(0,
+                mContext.getString(R.string.vcard_import_request_rejected_message)).sendToTarget();
+    }
+
+    @Override
+    public void onImportCanceled(ImportRequest request, int jobId) {
+        final String description = mContext.getString(R.string.importing_vcard_canceled_title,
+                request.displayName);
+        final Notification notification =
+                NotificationImportExportListener.constructCancelNotification(mContext, description);
+        mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG,
+                jobId, notification);
+    }
+
+    @Override
+    public void onExportProcessed(ExportRequest request, int jobId) {
+        final String displayName = request.destUri.getLastPathSegment();
+        final String message = mContext.getString(R.string.vcard_export_will_start_message,
+                displayName);
+
+        mHandler.obtainMessage(0, message).sendToTarget();
+        final Notification notification =
+                NotificationImportExportListener.constructProgressNotification(mContext,
+                        VCardService.TYPE_EXPORT, message, message, jobId, displayName, -1, 0);
+        mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
+    }
+
+    @Override
+    public void onExportFailed(ExportRequest request) {
+        mHandler.obtainMessage(0,
+                mContext.getString(R.string.vcard_export_request_rejected_message)).sendToTarget();
+    }
+
+    @Override
+    public void onCancelRequest(CancelRequest request, int type) {
+        final String description = type == VCardService.TYPE_IMPORT ?
+                mContext.getString(R.string.importing_vcard_canceled_title, request.displayName) :
+                mContext.getString(R.string.exporting_vcard_canceled_title, request.displayName);
+        final Notification notification = constructCancelNotification(mContext, description);
+        mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, request.jobId, notification);
+    }
+
+    /**
+     * Constructs a {@link Notification} showing the current status of import/export.
+     * Users can cancel the process with the Notification.
+     *
+     * @param context
+     * @param type import/export
+     * @param description Content of the Notification.
+     * @param tickerText
+     * @param jobId
+     * @param displayName Name to be shown to the Notification (e.g. "finished importing XXXX").
+     * Typycally a file name.
+     * @param totalCount The number of vCard entries to be imported. Used to show progress bar.
+     * -1 lets the system show the progress bar with "indeterminate" state.
+     * @param currentCount The index of current vCard. Used to show progress bar.
+     */
+    /* package */ static Notification constructProgressNotification(
+            Context context, int type, String description, String tickerText,
+            int jobId, String displayName, int totalCount, int currentCount) {
+        final RemoteViews remoteViews =
+                new RemoteViews(context.getPackageName(),
+                        R.layout.status_bar_ongoing_event_progress_bar);
+        remoteViews.setTextViewText(R.id.status_description, description);
+        remoteViews.setProgressBar(R.id.status_progress_bar, totalCount, currentCount,
+                totalCount == -1);
+        final String percentage;
+        if (totalCount > 0) {
+            percentage = context.getString(R.string.percentage,
+                    String.valueOf(currentCount * 100/totalCount));
+        } else {
+            percentage = "";
+        }
+        remoteViews.setTextViewText(R.id.status_progress_text, percentage);
+        final int icon = (type == VCardService.TYPE_IMPORT ? android.R.drawable.stat_sys_download :
+                android.R.drawable.stat_sys_upload);
+        remoteViews.setImageViewResource(R.id.status_icon, icon);
+
+        final Notification notification = new Notification();
+        notification.icon = icon;
+        notification.tickerText = tickerText;
+        notification.contentView = remoteViews;
+        notification.flags |= Notification.FLAG_ONGOING_EVENT;
+
+        // Note: We cannot use extra values here (like setIntExtra()), as PendingIntent doesn't
+        // preserve them across multiple Notifications. PendingIntent preserves the first extras
+        // (when flag is not set), or update them when PendingIntent#getActivity() is called
+        // (See PendingIntent#FLAG_UPDATE_CURRENT). In either case, we cannot preserve extras as we
+        // expect (for each vCard import/export request).
+        //
+        // We use query parameter in Uri instead.
+        // Scheme and Authority is arbitorary, assuming CancelActivity never refers them.
+        final Intent intent = new Intent(context, CancelActivity.class);
+        final Uri uri = (new Uri.Builder())
+                .scheme("invalidscheme")
+                .authority("invalidauthority")
+                .appendQueryParameter(CancelActivity.JOB_ID, String.valueOf(jobId))
+                .appendQueryParameter(CancelActivity.DISPLAY_NAME, displayName)
+                .appendQueryParameter(CancelActivity.TYPE, String.valueOf(type)).build();
+        intent.setData(uri);
+
+        notification.contentIntent = PendingIntent.getActivity(context, 0, intent, 0);
+        return notification;
+    }
+
+    /**
+     * Constructs a Notification telling users the process is canceled.
+     *
+     * @param context
+     * @param description Content of the Notification
+     */
+    /* package */ static Notification constructCancelNotification(
+            Context context, String description) {
+        return new Notification.Builder(context)
+                .setAutoCancel(true)
+                .setSmallIcon(android.R.drawable.stat_notify_error)
+                .setContentTitle(description)
+                .setContentText(description)
+                .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
+                .getNotification();
+    }
+
+    /**
+     * Constructs a Notification telling users the process is finished.
+     *
+     * @param context
+     * @param description Content of the Notification
+     * @param intent Intent to be launched when the Notification is clicked. Can be null.
+     */
+    /* package */ static Notification constructFinishNotification(
+            Context context, String title, String description, Intent intent) {
+        return new Notification.Builder(context)
+                .setAutoCancel(true)
+                .setSmallIcon(android.R.drawable.stat_sys_download_done)
+                .setContentTitle(title)
+                .setContentText(description)
+                .setContentIntent(PendingIntent.getActivity(context, 0,
+                        (intent != null ? intent : new Intent()), 0))
+                .getNotification();
+    }
+
+    /**
+     * Constructs a Notification telling the vCard import has failed.
+     *
+     * @param context
+     * @param reason The reason why the import has failed. Shown in description field.
+     */
+    /* package */ static Notification constructImportFailureNotification(
+            Context context, String reason) {
+        return new Notification.Builder(context)
+                .setAutoCancel(true)
+                .setSmallIcon(android.R.drawable.stat_notify_error)
+                .setContentTitle(context.getString(R.string.vcard_import_failed))
+                .setContentText(reason)
+                .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
+                .getNotification();
+    }
+
+    @Override
+    public void onComplete() {
+        mContext.finish();
+    }
+}
diff --git a/src/com/android/contacts/vcard/VCardImportExportListener.java b/src/com/android/contacts/vcard/VCardImportExportListener.java
new file mode 100644
index 0000000..d3ef198
--- /dev/null
+++ b/src/com/android/contacts/vcard/VCardImportExportListener.java
@@ -0,0 +1,36 @@
+/*
+ * 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.vcard;
+
+import com.android.vcard.VCardEntry;
+
+import android.net.Uri;
+
+interface VCardImportExportListener {
+    void onImportProcessed(ImportRequest request, int jobId, int sequence);
+    void onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
+            int totalCount);
+    void onImportFinished(ImportRequest request, int jobId, Uri uri);
+    void onImportFailed(ImportRequest request);
+    void onImportCanceled(ImportRequest request, int jobId);
+
+    void onExportProcessed(ExportRequest request, int jobId);
+    void onExportFailed(ExportRequest request);
+
+    void onCancelRequest(CancelRequest request, int type);
+    void onComplete();
+}
diff --git a/src/com/android/contacts/vcard/VCardService.java b/src/com/android/contacts/vcard/VCardService.java
index 0358e22..71b0214 100644
--- a/src/com/android/contacts/vcard/VCardService.java
+++ b/src/com/android/contacts/vcard/VCardService.java
@@ -17,16 +17,13 @@
 
 import com.android.contacts.R;
 
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.app.Service;
-import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.media.MediaScannerConnection;
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -34,8 +31,6 @@
 import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.Log;
-import android.widget.RemoteViews;
-import android.widget.Toast;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -60,16 +55,6 @@
 public class VCardService extends Service {
     private final static String LOG_TAG = "VCardService";
 
-    /** The tag used by vCard-related notifications. */
-    /* package */ static final String DEFAULT_NOTIFICATION_TAG = "VCardServiceProgress";
-    /**
-     * The tag used by vCard-related failure notifications.
-     * <p>
-     * Use a different tag from {@link #DEFAULT_NOTIFICATION_TAG} so that failures do not get
-     * replaced by other notifications and vice-versa.
-     */
-    /* package */ static final String FAILURE_NOTIFICATION_TAG = "VCardServiceFailure";
-
     /* package */ final static boolean DEBUG = false;
 
     /* package */ static final int MSG_IMPORT_REQUEST = 1;
@@ -79,7 +64,7 @@
     /* package */ static final int MSG_SET_AVAILABLE_EXPORT_DESTINATION = 5;
 
     /**
-     * Specifies the type of operation. Used when constructing a {@link Notification}, canceling
+     * Specifies the type of operation. Used when constructing a notification, canceling
      * some operation, etc.
      */
     /* package */ static final int TYPE_IMPORT = 1;
@@ -87,34 +72,6 @@
 
     /* package */ static final String CACHE_FILE_PREFIX = "import_tmp_";
 
-    private final Messenger mMessenger = new Messenger(new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_IMPORT_REQUEST: {
-                    handleImportRequest((List<ImportRequest>)msg.obj);
-                    break;
-                }
-                case MSG_EXPORT_REQUEST: {
-                    handleExportRequest((ExportRequest)msg.obj);
-                    break;
-                }
-                case MSG_CANCEL_REQUEST: {
-                    handleCancelRequest((CancelRequest)msg.obj);
-                    break;
-                }
-                case MSG_REQUEST_AVAILABLE_EXPORT_DESTINATION: {
-                    handleRequestAvailableExportDestination(msg);
-                    break;
-                }
-                // TODO: add cancel capability for export..
-                default: {
-                    Log.w(LOG_TAG, "Received unknown request, ignoring it.");
-                    super.hasMessages(msg.what);
-                }
-            }
-        }
-    });
 
     private class CustomMediaScannerConnectionClient implements MediaScannerConnectionClient {
         final MediaScannerConnection mConnection;
@@ -143,8 +100,6 @@
         }
     }
 
-    private NotificationManager mNotificationManager;
-
     // Should be single thread, as we don't want to simultaneously handle import and export
     // requests.
     private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
@@ -173,16 +128,23 @@
     private String mFileNameExtension;
     private Set<String> mExtensionsToConsider;
     private String mErrorReason;
+    private MyBinder mBinder;
 
     // File names currently reserved by some export job.
     private final Set<String> mReservedDestination = new HashSet<String>();
     /* ** end of vCard exporter params ** */
 
-    @Override
+    public class MyBinder extends Binder {
+        public VCardService getService() {
+            return VCardService.this;
+        }
+    }
+
+   @Override
     public void onCreate() {
         super.onCreate();
+        mBinder = new MyBinder();
         if (DEBUG) Log.d(LOG_TAG, "vCard Service is being created.");
-        mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
         initExporterParams();
     }
 
@@ -218,7 +180,7 @@
 
     @Override
     public IBinder onBind(Intent intent) {
-        return mMessenger.getBinder();
+        return mBinder;
     }
 
     @Override
@@ -229,7 +191,8 @@
         super.onDestroy();
     }
 
-    private synchronized void handleImportRequest(List<ImportRequest> requests) {
+    public synchronized void handleImportRequest(List<ImportRequest> requests,
+            VCardImportExportListener listener) {
         if (DEBUG) {
             final ArrayList<String> uris = new ArrayList<String>();
             final ArrayList<String> displayNames = new ArrayList<String>();
@@ -245,71 +208,44 @@
         for (int i = 0; i < size; i++) {
             ImportRequest request = requests.get(i);
 
-            if (tryExecute(new ImportProcessor(this, request, mCurrentJobId))) {
-                if (!request.showImmediately) {
-                    // Show a notification about the status
-                    final String displayName;
-                    final String message;
-                    if (request.displayName != null) {
-                        displayName = request.displayName;
-                        message = getString(R.string.vcard_import_will_start_message, displayName);
-                    } else {
-                        displayName = getString(R.string.vcard_unknown_filename);
-                        message = getString(
-                                R.string.vcard_import_will_start_message_with_default_name);
-                    }
-
-                    // We just want to show notification for the first vCard.
-                    if (i == 0) {
-                        // TODO: Ideally we should detect the current status of import/export and
-                        // show "started" when we can import right now and show "will start" when
-                        // we cannot.
-                        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
-                    }
-
-                    final Notification notification = constructProgressNotification(this,
-                            TYPE_IMPORT, message, message, mCurrentJobId, displayName, -1, 0);
-                    mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG,
-                            mCurrentJobId, notification);
+            if (tryExecute(new ImportProcessor(this, listener, request, mCurrentJobId))) {
+                if (listener != null) {
+                    listener.onImportProcessed(request, mCurrentJobId, i);
                 }
                 mCurrentJobId++;
             } else {
-                // TODO: a little unkind to show Toast in this case, which is shown just a moment.
-                // Ideally we should show some persistent something users can notice more easily.
-                Toast.makeText(this, getString(R.string.vcard_import_request_rejected_message),
-                        Toast.LENGTH_LONG).show();
+                if (listener != null) {
+                    listener.onImportFailed(request);
+                }
                 // A rejection means executor doesn't run any more. Exit.
                 break;
             }
         }
     }
 
-    private synchronized void handleExportRequest(ExportRequest request) {
+    public synchronized void handleExportRequest(ExportRequest request,
+            VCardImportExportListener listener) {
         if (tryExecute(new ExportProcessor(this, request, mCurrentJobId))) {
-            final String displayName = request.destUri.getLastPathSegment();
-            final String message = getString(R.string.vcard_export_will_start_message,
-                    displayName);
-
             final String path = request.destUri.getEncodedPath();
             if (DEBUG) Log.d(LOG_TAG, "Reserve the path " + path);
             if (!mReservedDestination.add(path)) {
                 Log.w(LOG_TAG,
                         String.format("The path %s is already reserved. Reject export request",
                                 path));
-                Toast.makeText(this, getString(R.string.vcard_export_request_rejected_message),
-                        Toast.LENGTH_LONG).show();
+                if (listener != null) {
+                    listener.onExportFailed(request);
+                }
                 return;
             }
 
-            Toast.makeText(this, message, Toast.LENGTH_LONG).show();
-            final Notification notification =
-                    constructProgressNotification(this, TYPE_EXPORT, message, message,
-                            mCurrentJobId, displayName, -1, 0);
-            mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, mCurrentJobId, notification);
+            if (listener != null) {
+                listener.onExportProcessed(request, mCurrentJobId);
+            }
             mCurrentJobId++;
         } else {
-            Toast.makeText(this, getString(R.string.vcard_export_request_rejected_message),
-                    Toast.LENGTH_LONG).show();
+            if (listener != null) {
+                listener.onExportFailed(request);
+            }
         }
     }
 
@@ -332,19 +268,19 @@
         }
     }
 
-    private synchronized void handleCancelRequest(CancelRequest request) {
+    public synchronized void handleCancelRequest(CancelRequest request,
+            VCardImportExportListener listener) {
         final int jobId = request.jobId;
         if (DEBUG) Log.d(LOG_TAG, String.format("Received cancel request. (id: %d)", jobId));
         final ProcessorBase processor = mRunningJobMap.remove(jobId);
 
         if (processor != null) {
             processor.cancel(true);
-            final String description = processor.getType() == TYPE_IMPORT ?
-                    getString(R.string.importing_vcard_canceled_title, request.displayName) :
-                            getString(R.string.exporting_vcard_canceled_title, request.displayName);
-            final Notification notification = constructCancelNotification(this, description);
-            mNotificationManager.notify(VCardService.DEFAULT_NOTIFICATION_TAG, jobId, notification);
-            if (processor.getType() == TYPE_EXPORT) {
+            final int type = processor.getType();
+            if (listener != null) {
+                listener.onCancelRequest(request, type);
+            }
+            if (type == TYPE_EXPORT) {
                 final String path =
                         ((ExportProcessor)processor).getRequest().destUri.getEncodedPath();
                 Log.i(LOG_TAG,
@@ -359,9 +295,8 @@
         stopServiceIfAppropriate();
     }
 
-    private synchronized void handleRequestAvailableExportDestination(Message msg) {
+    public synchronized void handleRequestAvailableExportDestination(final Messenger messenger) {
         if (DEBUG) Log.d(LOG_TAG, "Received available export destination request.");
-        final Messenger messenger = msg.replyTo;
         final String path = getAppropriateDestination(mTargetDirectory);
         final Message message;
         if (path != null) {
@@ -494,122 +429,6 @@
     }
 
     /**
-     * Constructs a {@link Notification} showing the current status of import/export.
-     * Users can cancel the process with the Notification.
-     *
-     * @param context
-     * @param type import/export
-     * @param description Content of the Notification.
-     * @param tickerText
-     * @param jobId
-     * @param displayName Name to be shown to the Notification (e.g. "finished importing XXXX").
-     * Typycally a file name.
-     * @param totalCount The number of vCard entries to be imported. Used to show progress bar.
-     * -1 lets the system show the progress bar with "indeterminate" state.
-     * @param currentCount The index of current vCard. Used to show progress bar.
-     */
-    /* package */ static Notification constructProgressNotification(
-            Context context, int type, String description, String tickerText,
-            int jobId, String displayName, int totalCount, int currentCount) {
-        final RemoteViews remoteViews =
-                new RemoteViews(context.getPackageName(),
-                        R.layout.status_bar_ongoing_event_progress_bar);
-        remoteViews.setTextViewText(R.id.status_description, description);
-        remoteViews.setProgressBar(R.id.status_progress_bar, totalCount, currentCount,
-                totalCount == -1);
-        final String percentage;
-        if (totalCount > 0) {
-            percentage = context.getString(R.string.percentage,
-                    String.valueOf(currentCount * 100/totalCount));
-        } else {
-            percentage = "";
-        }
-        remoteViews.setTextViewText(R.id.status_progress_text, percentage);
-        final int icon = (type == TYPE_IMPORT ? android.R.drawable.stat_sys_download :
-                android.R.drawable.stat_sys_upload);
-        remoteViews.setImageViewResource(R.id.status_icon, icon);
-
-        final Notification notification = new Notification();
-        notification.icon = icon;
-        notification.tickerText = tickerText;
-        notification.contentView = remoteViews;
-        notification.flags |= Notification.FLAG_ONGOING_EVENT;
-
-        // Note: We cannot use extra values here (like setIntExtra()), as PendingIntent doesn't
-        // preserve them across multiple Notifications. PendingIntent preserves the first extras
-        // (when flag is not set), or update them when PendingIntent#getActivity() is called
-        // (See PendingIntent#FLAG_UPDATE_CURRENT). In either case, we cannot preserve extras as we
-        // expect (for each vCard import/export request).
-        //
-        // We use query parameter in Uri instead.
-        // Scheme and Authority is arbitorary, assuming CancelActivity never refers them.
-        final Intent intent = new Intent(context, CancelActivity.class);
-        final Uri uri = (new Uri.Builder())
-                .scheme("invalidscheme")
-                .authority("invalidauthority")
-                .appendQueryParameter(CancelActivity.JOB_ID, String.valueOf(jobId))
-                .appendQueryParameter(CancelActivity.DISPLAY_NAME, displayName)
-                .appendQueryParameter(CancelActivity.TYPE, String.valueOf(type)).build();
-        intent.setData(uri);
-
-        notification.contentIntent = PendingIntent.getActivity(context, 0, intent, 0);
-        return notification;
-    }
-
-    /**
-     * Constructs a Notification telling users the process is canceled.
-     *
-     * @param context
-     * @param description Content of the Notification
-     */
-    /* package */ static Notification constructCancelNotification(
-            Context context, String description) {
-        return new Notification.Builder(context)
-                .setAutoCancel(true)
-                .setSmallIcon(android.R.drawable.stat_notify_error)
-                .setContentTitle(description)
-                .setContentText(description)
-                .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
-                .getNotification();
-    }
-
-    /**
-     * Constructs a Notification telling users the process is finished.
-     *
-     * @param context
-     * @param description Content of the Notification
-     * @param intent Intent to be launched when the Notification is clicked. Can be null.
-     */
-    /* package */ static Notification constructFinishNotification(
-            Context context, String title, String description, Intent intent) {
-        return new Notification.Builder(context)
-                .setAutoCancel(true)
-                .setSmallIcon(android.R.drawable.stat_sys_download_done)
-                .setContentTitle(title)
-                .setContentText(description)
-                .setContentIntent(PendingIntent.getActivity(context, 0,
-                        (intent != null ? intent : new Intent()), 0))
-                .getNotification();
-    }
-
-    /**
-     * Constructs a Notification telling the vCard import has failed.
-     *
-     * @param context
-     * @param reason The reason why the import has failed. Shown in description field.
-     */
-    /* package */ static Notification constructImportFailureNotification(
-            Context context, String reason) {
-        return new Notification.Builder(context)
-                .setAutoCancel(true)
-                .setSmallIcon(android.R.drawable.stat_notify_error)
-                .setContentTitle(context.getString(R.string.vcard_import_failed))
-                .setContentText(reason)
-                .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
-                .getNotification();
-    }
-
-    /**
      * Returns an appropriate file name for vCard export. Returns null when impossible.
      *
      * @return destination path for a vCard file to be exported. null on error and mErrorReason
diff --git a/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java b/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
index 487a13f..40a33b6 100644
--- a/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
+++ b/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
@@ -50,6 +50,8 @@
     private static final String TEST_FORMATTED_NUMBER = "1-412-255-5555";
     /** The country ISO name used in the tests. */
     private static final String TEST_COUNTRY_ISO = "US";
+    /** The geocoded location used in the tests. */
+    private static final String TEST_GEOCODE = "United States";
 
     /** The object under test. */
     private PhoneCallDetailsHelper mHelper;
@@ -268,7 +270,7 @@
     /** Sets the phone call details with default values and the given number. */
     private void setPhoneCallDetailsWithNumber(String number, String formattedNumber) {
         mHelper.setPhoneCallDetails(mViews,
-                new PhoneCallDetails(number, formattedNumber, TEST_COUNTRY_ISO,
+                new PhoneCallDetails(number, formattedNumber, TEST_COUNTRY_ISO, TEST_GEOCODE,
                         new int[]{ Calls.VOICEMAIL_TYPE }, TEST_DATE, TEST_DURATION),
                 true);
     }
@@ -277,7 +279,7 @@
     private void setPhoneCallDetailsWithDate(long date) {
         mHelper.setPhoneCallDetails(mViews,
                 new PhoneCallDetails(TEST_NUMBER, TEST_FORMATTED_NUMBER, TEST_COUNTRY_ISO,
-                        new int[]{ Calls.INCOMING_TYPE }, date, TEST_DURATION),
+                        TEST_GEOCODE, new int[]{ Calls.INCOMING_TYPE }, date, TEST_DURATION),
                 false);
     }
 
@@ -285,20 +287,20 @@
     private void setPhoneCallDetailsWithCallTypeIcons(int... callTypes) {
         mHelper.setPhoneCallDetails(mViews,
                 new PhoneCallDetails(TEST_NUMBER, TEST_FORMATTED_NUMBER, TEST_COUNTRY_ISO,
-                        callTypes, TEST_DATE, TEST_DURATION),
+                        TEST_GEOCODE, callTypes, TEST_DATE, TEST_DURATION),
                 false);
     }
 
     private void setPhoneCallNameWithNumberOnly() {
         mHelper.setPhoneCallName(mNameView,
                 new PhoneCallDetails(TEST_NUMBER, TEST_FORMATTED_NUMBER, TEST_COUNTRY_ISO,
-                        new int[]{ Calls.INCOMING_TYPE }, TEST_DATE, TEST_DURATION));
+                        TEST_GEOCODE, new int[]{ Calls.INCOMING_TYPE }, TEST_DATE, TEST_DURATION));
     }
 
     private void setPhoneCallName(String name) {
         mHelper.setPhoneCallName(mNameView,
                 new PhoneCallDetails(TEST_NUMBER, TEST_FORMATTED_NUMBER, TEST_COUNTRY_ISO,
-                        new int[]{ Calls.INCOMING_TYPE }, TEST_DATE, TEST_DURATION,
+                        TEST_GEOCODE, new int[]{ Calls.INCOMING_TYPE }, TEST_DATE, TEST_DURATION,
                         name, 0, "", 1, null));
     }
 }
diff --git a/tests/src/com/android/contacts/activities/CallLogActivityTests.java b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
index 7043eb0..ce2418f 100644
--- a/tests/src/com/android/contacts/activities/CallLogActivityTests.java
+++ b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
@@ -174,7 +174,7 @@
         insert(CallerInfo.PRIVATE_NUMBER, NOW, 0, Calls.INCOMING_TYPE);
         View view = mAdapter.newGroupView(getActivity(), mParentView);
         mAdapter.bindGroupView(view, getActivity(), mCursor, 3, false);
-        assertNotNull(view.findViewById(R.id.call_icon));
+        assertNotNull(view.findViewById(R.id.secondary_action_icon));
     }
 
     @MediumTest
@@ -183,7 +183,7 @@
         insert(CallerInfo.PRIVATE_NUMBER, NOW, 0, Calls.INCOMING_TYPE);
         View view = mAdapter.newStandAloneView(getActivity(), mParentView);
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
-        assertNotNull(view.findViewById(R.id.call_icon));
+        assertNotNull(view.findViewById(R.id.secondary_action_icon));
     }
 
     @MediumTest
@@ -192,7 +192,7 @@
         insert(CallerInfo.PRIVATE_NUMBER, NOW, 0, Calls.INCOMING_TYPE);
         View view = mAdapter.newChildView(getActivity(), mParentView);
         mAdapter.bindChildView(view, getActivity(), mCursor);
-        assertNotNull(view.findViewById(R.id.call_icon));
+        assertNotNull(view.findViewById(R.id.secondary_action_icon));
     }
 
     @MediumTest
@@ -303,7 +303,7 @@
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
 
         CallLogListItemViews views = (CallLogListItemViews) view.getTag();
-        IntentProvider intentProvider = (IntentProvider) views.callView.getTag();
+        IntentProvider intentProvider = (IntentProvider) views.secondaryActionView.getTag();
         Intent intent = intentProvider.getIntent(mActivity);
         // Starts a call.
         assertEquals(Intent.ACTION_CALL_PRIVILEGED, intent.getAction());
@@ -319,7 +319,7 @@
         mAdapter.bindStandAloneView(view, getActivity(), mCursor);
 
         CallLogListItemViews views = (CallLogListItemViews) view.getTag();
-        IntentProvider intentProvider = (IntentProvider) views.playView.getTag();
+        IntentProvider intentProvider = (IntentProvider) views.secondaryActionView.getTag();
         Intent intent = intentProvider.getIntent(mActivity);
         // Starts the call detail activity.
         assertEquals(new ComponentName(mActivity, CallDetailActivity.class),
@@ -357,9 +357,9 @@
             String number = getPhoneNumberForListEntry(i);
             if (CallerInfo.PRIVATE_NUMBER.equals(number) ||
                 CallerInfo.UNKNOWN_NUMBER.equals(number)) {
-                assertFalse(View.VISIBLE == mItem.callView.getVisibility());
+                assertFalse(View.VISIBLE == mItem.secondaryActionView.getVisibility());
             } else {
-                assertEquals(View.VISIBLE, mItem.callView.getVisibility());
+                assertEquals(View.VISIBLE, mItem.secondaryActionView.getVisibility());
             }
         }
     }
diff --git a/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java
index 1cf7da9..2c29608 100644
--- a/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java
+++ b/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java
@@ -42,6 +42,8 @@
     private static final String TEST_VOICEMAIL_NUMBER = "123";
     /** The country ISO name used in the tests. */
     private static final String TEST_COUNTRY_ISO = "US";
+    /** The geocoded location used in the tests. */
+    private static final String TEST_GEOCODE = "United States";
 
     /** The object under test. */
     private CallLogListItemHelper mHelper;
@@ -59,7 +61,7 @@
         mPhoneNumberHelper = new PhoneNumberHelper(resources, TEST_VOICEMAIL_NUMBER);
         PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
                 resources, callTypeHelper, mPhoneNumberHelper);
-        mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, mPhoneNumberHelper);
+        mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, mPhoneNumberHelper, resources);
         mViews = CallLogListItemViews.createForTest(context);
     }
 
@@ -72,8 +74,7 @@
 
     public void testSetPhoneCallDetails() {
         setPhoneCallDetailsWithNumber("12125551234", "1-212-555-1234");
-        assertEquals(View.VISIBLE, mViews.callView.getVisibility());
-        assertEquals(View.GONE, mViews.playView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
         assertEquals(View.GONE, mViews.unheardView.getVisibility());
     }
 
@@ -94,37 +95,32 @@
 
     public void testSetPhoneCallDetails_VoicemailNumber() {
         setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER, TEST_VOICEMAIL_NUMBER);
-        assertEquals(View.VISIBLE, mViews.callView.getVisibility());
-        assertEquals(View.GONE, mViews.playView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
         assertEquals(View.GONE, mViews.unheardView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_ReadVoicemail() {
         setPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE);
-        assertEquals(View.GONE, mViews.callView.getVisibility());
-        assertEquals(View.VISIBLE, mViews.playView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
         assertEquals(View.GONE, mViews.unheardView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_UnreadVoicemail() {
         setUnreadPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE);
-        assertEquals(View.GONE, mViews.callView.getVisibility());
-        assertEquals(View.VISIBLE, mViews.playView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
         assertEquals(View.VISIBLE, mViews.unheardView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_VoicemailFromUnknown() {
         setPhoneCallDetailsWithNumberAndType(CallerInfo.UNKNOWN_NUMBER, CallerInfo.UNKNOWN_NUMBER,
                 Calls.VOICEMAIL_TYPE);
-        assertEquals(View.GONE, mViews.callView.getVisibility());
-        assertEquals(View.VISIBLE, mViews.playView.getVisibility());
+        assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
         assertEquals(View.GONE, mViews.unheardView.getVisibility());
     }
 
     /** Asserts that the whole call area is gone. */
     private void assertNoCallButton() {
-        assertEquals(View.GONE, mViews.callView.getVisibility());
-        assertEquals(View.GONE, mViews.playView.getVisibility());
+        assertEquals(View.GONE, mViews.secondaryActionView.getVisibility());
         assertEquals(View.GONE, mViews.unheardView.getVisibility());
         assertEquals(View.GONE, mViews.dividerView.getVisibility());
     }
@@ -138,7 +134,7 @@
     private void setPhoneCallDetailsWithNumberAndType(String number, String formattedNumber,
             int callType) {
         mHelper.setPhoneCallDetails(mViews,
-                new PhoneCallDetails(number, formattedNumber, TEST_COUNTRY_ISO,
+                new PhoneCallDetails(number, formattedNumber, TEST_COUNTRY_ISO, TEST_GEOCODE,
                         new int[]{ callType }, TEST_DATE, TEST_DURATION),
                 false);
     }
@@ -147,7 +143,7 @@
     private void setPhoneCallDetailsWithTypes(int... types) {
         mHelper.setPhoneCallDetails(mViews,
                 new PhoneCallDetails(TEST_NUMBER, TEST_FORMATTED_NUMBER, TEST_COUNTRY_ISO,
-                        types, TEST_DATE, TEST_DURATION),
+                        TEST_GEOCODE, types, TEST_DATE, TEST_DURATION),
                 false);
     }
 
@@ -155,7 +151,7 @@
     private void setUnreadPhoneCallDetailsWithTypes(int... types) {
         mHelper.setPhoneCallDetails(mViews,
                 new PhoneCallDetails(TEST_NUMBER, TEST_FORMATTED_NUMBER, TEST_COUNTRY_ISO,
-                        types, TEST_DATE, TEST_DURATION),
+                        TEST_GEOCODE, types, TEST_DATE, TEST_DURATION),
                 true);
     }
 }