Merge "Add a test activity to add entries to the call log."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 47340a0..d298fbe 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -25,6 +25,8 @@
     <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+    <uses-permission android:name="android.permission.READ_PROFILE" />
+    <uses-permission android:name="android.permission.WRITE_PROFILE" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
diff --git a/res/layout/custom_action_bar.xml b/res/layout/custom_action_bar.xml
new file mode 100644
index 0000000..2357756
--- /dev/null
+++ b/res/layout/custom_action_bar.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <SearchView
+        android:id="@+id/search_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:iconifiedByDefault="false" />
+
+</FrameLayout>
diff --git a/res/layout/group_browse_list_fragment.xml b/res/layout/group_browse_list_fragment.xml
index 50c02c8..d41772d 100644
--- a/res/layout/group_browse_list_fragment.xml
+++ b/res/layout/group_browse_list_fragment.xml
@@ -26,7 +26,9 @@
       android:layout_height="0dip"
       android:fastScrollEnabled="true"
       android:scrollbarStyle="outsideOverlay"
-      android:layout_weight="1" />
+      android:layout_weight="1"
+      android:cacheColorHint="@android:color/transparent"
+      android:divider="@null" />
 
    <TextView
      android:id="@+id/empty"
diff --git a/res/layout/group_browse_list_item.xml b/res/layout/group_browse_list_item.xml
index 28f4e17..d94d444 100644
--- a/res/layout/group_browse_list_item.xml
+++ b/res/layout/group_browse_list_item.xml
@@ -19,24 +19,15 @@
     class="com.android.contacts.group.GroupBrowseListAdapter$GroupListItem"
     android:orientation="horizontal"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-
-    <ImageView
-        android:id="@+id/icon"
-        android:scaleType="center"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_marginLeft="10dip"
-        android:layout_marginRight="10dip"
-        android:layout_gravity="center_vertical"
-        android:src="@drawable/ic_menu_display_all_holo_light" />
+    android:layout_height="wrap_content"
+    style="@style/GroupBrowseListItem">
 
     <LinearLayout
         android:orientation="vertical"
-        android:layout_width="wrap_content"
+        android:layout_width="0dip"
         android:layout_height="match_parent"
-        android:paddingTop="5dip"
-        android:paddingBottom="5dip">
+        android:layout_weight="1"
+        android:padding="5dip">
 
         <TextView
             android:id="@+id/label"
@@ -60,4 +51,13 @@
 
     </LinearLayout>
 
+    <ImageView
+        android:id="@+id/icon"
+        android:scaleType="center"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginRight="20dip"
+        android:layout_gravity="center_vertical"
+        android:src="@drawable/ic_menu_display_all_holo_light" />
+
 </view>
diff --git a/res/layout/navigation_bar.xml b/res/layout/navigation_bar.xml
deleted file mode 100644
index 803705c..0000000
--- a/res/layout/navigation_bar.xml
+++ /dev/null
@@ -1,73 +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.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    style="?android:attr/actionBarTabBarStyle"
-    android:id="@+id/navigation_bar"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="horizontal" >
-
-    <TextView
-        android:id="@+id/search_label"
-        style="?android:attr/actionButtonStyle"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:gravity="center_vertical"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:text="@string/search_label"
-        android:minWidth="@dimen/action_bar_filter_min_width" />
-
-    <view
-        class="com.android.contacts.list.ContactListFilterView"
-        style="?android:attr/actionDropDownStyle"
-        android:id="@+id/filter_view"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="left|center_vertical"
-        android:focusable="true"
-        android:minWidth="@dimen/action_bar_filter_min_width"
-         >
-        <ImageView
-            android:id="@+id/icon"
-            android:scaleType="fitCenter"
-            android:layout_width="24dip"
-            android:layout_height="24dip"
-            android:layout_marginRight="7dip"
-            android:layout_gravity="center_vertical" />
-
-        <TextView
-            android:id="@+id/label"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:gravity="center_vertical"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:ellipsize="end"
-            android:singleLine="true" 
-            android:maxWidth="@dimen/action_bar_filter_max_width" />
-    </view>
-
-    <SearchView
-        android:id="@+id/search_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_gravity="center_vertical"
-        android:layout_marginLeft="@dimen/action_bar_search_spacing"  
-        android:maxWidth="@dimen/action_bar_search_max_width"
-        android:iconifiedByDefault="false" />
-
-</LinearLayout>
diff --git a/res/menu-xlarge/actions.xml b/res/menu-xlarge/actions.xml
new file mode 100644
index 0000000..60788e0
--- /dev/null
+++ b/res/menu-xlarge/actions.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/menu_search"
+        android:showAsAction="always"
+        android:actionViewClass="android.widget.SearchView" />
+
+    <item
+        android:id="@+id/menu_add_contact"
+        android:icon="@drawable/ic_menu_add_contact_holo_light"
+        android:title="@string/menu_new_contact_action_bar"
+        android:showAsAction="withText|always" />
+
+    <item
+        android:id="@+id/menu_add_group"
+        android:icon="@drawable/ic_menu_display_all_holo_light"
+        android:title="@string/menu_new_contact_action_bar"
+        android:showAsAction="withText|always" />
+
+    <item
+        android:id="@+id/menu_contacts_filter"
+        android:icon="@drawable/ic_menu_settings_holo_light"
+        android:title="@string/menu_contacts_filter" />
+
+    <item
+        android:id="@+id/menu_settings"
+        android:icon="@drawable/ic_menu_settings_holo_light"
+        android:title="@string/menu_settings" />
+
+    <item
+        android:id="@+id/menu_accounts"
+        android:icon="@drawable/ic_menu_accounts_holo_light"
+        android:title="@string/menu_accounts" />
+
+    <item
+        android:id="@+id/menu_import_export"
+        android:icon="@drawable/ic_menu_import_export_holo_light"
+        android:title="@string/menu_import_export" />
+
+</menu>
diff --git a/res/menu/view.xml b/res/menu-xlarge/view_contact.xml
similarity index 100%
rename from res/menu/view.xml
rename to res/menu-xlarge/view_contact.xml
diff --git a/res/menu-xlarge/view_group.xml b/res/menu-xlarge/view_group.xml
new file mode 100644
index 0000000..8b0867b
--- /dev/null
+++ b/res/menu-xlarge/view_group.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/menu_edit_group"
+        android:icon="@drawable/ic_menu_compose_holo_light"
+        android:title="@string/menu_editGroup"
+        android:alphabeticShortcut="e"
+        android:showAsAction="always" />
+
+    <item
+        android:id="@+id/menu_rename_group"
+        android:icon="@drawable/ic_menu_settings_holo_light"
+        android:title="@string/menu_renameGroup" />
+
+    <item
+        android:id="@+id/menu_delete_group"
+        android:icon="@drawable/ic_menu_trash_holo_light"
+        android:title="@string/menu_deleteGroup" />
+</menu>
diff --git a/res/menu/actions.xml b/res/menu/actions.xml
index 083d352..996cfef 100644
--- a/res/menu/actions.xml
+++ b/res/menu/actions.xml
@@ -16,12 +16,18 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <item
         android:id="@+id/menu_search"
-        android:showAsAction="always"
-        android:actionViewClass="android.widget.SearchView" />
+        android:icon="@android:drawable/ic_menu_search"
+        android:title="@string/menu_search" />
 
     <item
-        android:id="@+id/menu_add"
-        android:showAsAction="always" />
+        android:id="@+id/menu_add_contact"
+        android:icon="@drawable/ic_menu_add_contact_holo_light"
+        android:title="@string/menu_new_contact_action_bar" />
+
+    <item
+        android:id="@+id/menu_add_group"
+        android:icon="@drawable/ic_menu_display_all_holo_light"
+        android:title="@string/menu_new_contact_action_bar" />
 
     <item
         android:id="@+id/menu_contacts_filter"
@@ -43,13 +49,4 @@
         android:icon="@drawable/ic_menu_import_export_holo_light"
         android:title="@string/menu_import_export" />
 
-    <item
-        android:id="@+id/menu_rename_group"
-        android:icon="@drawable/ic_menu_settings_holo_light"
-        android:title="@string/menu_renameGroup" />
-
-    <item
-        android:id="@+id/menu_delete_group"
-        android:icon="@drawable/ic_menu_trash_holo_light"
-        android:title="@string/menu_deleteGroup" />
 </menu>
diff --git a/res/menu/view_contact.xml b/res/menu/view_contact.xml
new file mode 100644
index 0000000..7cf17d6
--- /dev/null
+++ b/res/menu/view_contact.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/menu_edit"
+        android:icon="@drawable/ic_menu_compose_holo_light"
+        android:title="@string/menu_editContact"
+        android:alphabeticShortcut="e" />
+
+    <item
+        android:id="@+id/menu_share"
+        android:icon="@drawable/ic_menu_share_holo_light"
+        android:title="@string/menu_share"
+        android:alphabeticShortcut="s" />
+
+    <item
+        android:id="@+id/menu_options"
+        android:icon="@drawable/ic_menu_mark"
+        android:title="@string/menu_contactOptions" />
+
+    <item
+        android:id="@+id/menu_delete"
+        android:icon="@drawable/ic_menu_trash_holo_light"
+        android:title="@string/menu_deleteContact" />
+</menu>
diff --git a/res/menu/view_group.xml b/res/menu/view_group.xml
new file mode 100644
index 0000000..3ff6ac6
--- /dev/null
+++ b/res/menu/view_group.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/menu_edit_group"
+        android:icon="@drawable/ic_menu_compose_holo_light"
+        android:title="@string/menu_editGroup"
+        android:alphabeticShortcut="e" />
+
+    <item
+        android:id="@+id/menu_rename_group"
+        android:icon="@drawable/ic_menu_settings_holo_light"
+        android:title="@string/menu_renameGroup" />
+
+    <item
+        android:id="@+id/menu_delete_group"
+        android:icon="@drawable/ic_menu_trash_holo_light"
+        android:title="@string/menu_deleteGroup" />
+</menu>
diff --git a/res/values-xlarge/styles.xml b/res/values-xlarge/styles.xml
index 0179f6a..cbef91a 100644
--- a/res/values-xlarge/styles.xml
+++ b/res/values-xlarge/styles.xml
@@ -31,6 +31,7 @@
         <item name="list_item_vertical_divider_margin">5dip</item>
         <item name="list_item_presence_icon_margin">30dip</item>
         <item name="list_item_photo_size">64dip</item>
+        <item name="list_item_profile_photo_size">80dip</item>
         <item name="list_item_prefix_highlight_color">#729a27</item>
         <item name="list_item_header_text_indent">77dip</item>
         <item name="list_item_header_text_color">?color/section_header_text_color</item>
@@ -53,6 +54,7 @@
         <item name="list_item_vertical_divider_margin">5dip</item>
         <item name="list_item_presence_icon_margin">30dip</item>
         <item name="list_item_photo_size">64dip</item>
+        <item name="list_item_profile_photo_size">80dip</item>
         <item name="list_item_header_text_indent">77dip</item>
         <item name="list_item_header_text_color">?color/section_header_text_color</item>
         <item name="list_item_header_text_size">14sp</item>
@@ -116,4 +118,8 @@
         <item name="android:gravity">center_vertical</item>
         <item name="android:paddingTop">5dip</item>
     </style>
+
+    <style name="GroupBrowseListItem">
+        <item name="android:background">@drawable/list_item_activated_background</item>
+    </style>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 77863d5..f259ea7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -118,6 +118,9 @@
     <!-- Menu item that splits an item from the contact detail into a separate aggregate -->
     <string name="menu_splitAggregate">Separate</string>
 
+    <!-- Menu item that edits the currently selected group [CHAR LIMIT=30] -->
+    <string name="menu_editGroup">Edit group</string>
+
     <!-- Menu item that renames the currently selected group [CHAR LIMIT=30] -->
     <string name="menu_renameGroup">Rename group</string>
 
@@ -1497,6 +1500,12 @@
     <!-- Title of the dialog that allows deletion of a contact group [CHAR LIMIT=128] -->
     <string name="delete_group_dialog_title">Delete group</string>
 
+    <!-- Shows how many groups are from the specified account [CHAR LIMIT=15] -->
+    <plurals name="num_groups_in_account">
+        <item quantity="one">1 group</item>
+        <item quantity="other"><xliff:g id="count">%0$d</xliff:g> groups</item>
+    </plurals>
+
     <!-- Confirmation message of the dialog that allows deletion of a contact group  [CHAR LIMIT=256] -->
     <string name="delete_group_dialog_message">Are you sure you want to delete the group 
       \'<xliff:g id="group_label" example="Friends">%1$s</xliff:g>\'?
@@ -1528,4 +1537,8 @@
     <string name="call_type_and_date">
         <xliff:g id="call_type" example="Friends">%1$s</xliff:g>  <xliff:g id="call_short_date" example="Friends">%2$s</xliff:g>
     </string>
+
+    <!-- Text displayed in place of the display name for the contact that represents the user's
+      personal profile entry [CHAR LIMIT=64] -->
+    <string name="profile_display_name">My profile</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index f18a340..36f7ca8 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -31,6 +31,7 @@
         <item name="list_item_vertical_divider_margin">5dip</item>
         <item name="list_item_presence_icon_margin">5dip</item>
         <item name="list_item_photo_size">56dip</item>
+        <item name="list_item_profile_photo_size">70dip</item>
         <item name="list_item_prefix_highlight_color">#729a27</item>
         <item name="list_item_header_text_indent">56dip</item>
         <item name="list_item_header_text_color">?color/section_header_text_color</item>
@@ -114,6 +115,7 @@
         <attr name="list_item_vertical_divider_margin" format="dimension"/>
         <attr name="list_item_presence_icon_margin" format="dimension"/>
         <attr name="list_item_photo_size" format="dimension"/>
+        <attr name="list_item_profile_photo_size" format="dimension"/>
         <attr name="list_item_prefix_highlight_color" format="color"/>
         <attr name="list_item_header_text_indent" format="dimension" />
         <attr name="list_item_header_text_color" format="color" />
@@ -153,6 +155,7 @@
         <item name="list_item_vertical_divider_margin">5dip</item>
         <item name="list_item_presence_icon_margin">5dip</item>
         <item name="list_item_photo_size">56dip</item>
+        <item name="list_item_profile_photo_size">70dip</item>
         <item name="list_item_prefix_highlight_color">#729a27</item>
         <item name="list_item_header_text_indent">56dip</item>
         <item name="list_item_header_text_color">?color/section_header_text_color</item>
@@ -178,6 +181,7 @@
         <item name="list_item_vertical_divider_margin">5dip</item>
         <item name="list_item_presence_icon_margin">5dip</item>
         <item name="list_item_photo_size">56dip</item>
+        <item name="list_item_profile_photo_size">70dip</item>
         <item name="list_item_prefix_highlight_color">#729a27</item>
         <item name="list_item_header_text_indent">56dip</item>
         <item name="list_item_header_text_color">?color/section_header_text_color</item>
@@ -198,6 +202,7 @@
         <item name="list_item_vertical_divider_margin">5dip</item>
         <item name="list_item_presence_icon_margin">5dip</item>
         <item name="list_item_photo_size">56dip</item>
+        <item name="list_item_profile_photo_size">70dip</item>
         <item name="list_item_prefix_highlight_color">#729a27</item>
         <item name="list_item_header_text_indent">56dip</item>
         <item name="list_item_header_text_color">?color/section_header_text_color</item>
@@ -322,4 +327,8 @@
         <item name="android:padding">5dip</item>
         <item name="android:background">@drawable/list_selector</item>
     </style>
+
+    <style name="GroupBrowseListItem">
+        <item name="android:paddingRight">20dip</item>
+    </style>
 </resources>
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 0931e3d..f3f7073 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.contacts;
 
-import com.android.contacts.format.FormatUtils;
 import com.android.internal.telephony.CallerInfo;
 
 import android.app.ListActivity;
@@ -26,18 +25,15 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.database.Cursor;
-import android.graphics.Typeface;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.provider.Contacts.Intents.Insert;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.PhoneLookup;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
-import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.view.KeyEvent;
@@ -60,9 +56,8 @@
         AdapterView.OnItemClickListener {
     private static final String TAG = "CallDetail";
 
-    private TextView mNameView;
-    private TextView mCallTypeView;
-    private TextView mNumberView;
+    /** The views representing the details of a phone call. */
+    PhoneCallDetailsViews mPhoneCallDetailsViews;
     private TextView mCallTimeView;
     private TextView mCallDurationView;
     private View mCallActionView;
@@ -118,9 +113,7 @@
         mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
         mResources = getResources();
 
-        mNameView = (TextView) findViewById(R.id.name);
-        mCallTypeView = (TextView) findViewById(R.id.call_type);
-        mNumberView = (TextView) findViewById(R.id.number);
+        mPhoneCallDetailsViews = new PhoneCallDetailsViews(getWindow().getDecorView());
         mCallActionView = findViewById(R.id.call);
         mContactPhotoView = (ImageView) findViewById(R.id.contact_photo);
         mContactBackgroundView = (ImageView) findViewById(R.id.contact_background);
@@ -189,46 +182,18 @@
                     mCallDurationView.setText(formatDuration(duration));
                 }
 
-                CharSequence shortDateText =
-                        DateUtils.getRelativeTimeSpanString(date,
-                                System.currentTimeMillis(),
-                                DateUtils.MINUTE_IN_MILLIS,
-                                DateUtils.FORMAT_ABBREV_RELATIVE);
-
-                CharSequence callTypeText = "";
-                switch (callType) {
-                    case Calls.INCOMING_TYPE:
-                        callTypeText = getString(R.string.type_incoming);
-                        break;
-
-                    case Calls.OUTGOING_TYPE:
-                        callTypeText = getString(R.string.type_outgoing);
-                        break;
-
-                    case Calls.MISSED_TYPE:
-                        callTypeText = getString(R.string.type_missed);
-                        break;
-                }
-
-                mCallTypeView.setText(
-                        getString(R.string.call_type_and_date,
-                                FormatUtils.applyStyleToSpan(Typeface.BOLD,
-                                        callTypeText, 0, callTypeText.length(),
-                                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE),
-                                shortDateText));
-
                 long photoId = 0L;
                 CharSequence nameText = "";
-                CharSequence numberText = "";
+                final CharSequence numberText;
+                int numberType = 0;
+                CharSequence numberLabel = "";
                 if (mNumber.equals(CallerInfo.UNKNOWN_NUMBER) ||
                         mNumber.equals(CallerInfo.PRIVATE_NUMBER)) {
-                    nameText = getString(mNumber.equals(CallerInfo.PRIVATE_NUMBER)
+                    numberText = getString(mNumber.equals(CallerInfo.PRIVATE_NUMBER)
                             ? R.string.private_num : R.string.unknown);
-                    numberText = "";
                     mCallActionView.setVisibility(View.GONE);
                 } else {
                     // Perform a reverse-phonebook lookup to find the PERSON_ID
-                    CharSequence callLabel = null;
                     Uri personUri = null;
                     Uri phoneUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
                             Uri.encode(mNumber));
@@ -245,15 +210,15 @@
                                     phonesCursor.getString(COLUMN_INDEX_NUMBER),
                                     phonesCursor.getString(COLUMN_INDEX_NORMALIZED_NUMBER),
                                     countryIso);
-                            callLabel = Phone.getTypeLabel(getResources(),
-                                    phonesCursor.getInt(COLUMN_INDEX_TYPE),
-                                    phonesCursor.getString(COLUMN_INDEX_LABEL));
+                            numberType = phonesCursor.getInt(COLUMN_INDEX_TYPE);
+                            numberLabel = phonesCursor.getString(COLUMN_INDEX_LABEL);
                         } else {
                             mNumber = PhoneNumberUtils.formatNumber(mNumber, countryIso);
                         }
                     } finally {
                       if (phonesCursor != null) phonesCursor.close();
                     }
+                    numberText = mNumber;
 
                     mCallActionView.setVisibility(View.VISIBLE);
                     mCallActionView.setOnClickListener(new View.OnClickListener() {
@@ -265,15 +230,6 @@
                         }
                     });
 
-                    if (callLabel != null) {
-                        numberText = FormatUtils.applyStyleToSpan(Typeface.BOLD,
-                                callLabel + " " + mNumber, 0,
-                                callLabel.length(),
-                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-                    } else {
-                        numberText = mNumber;
-                    }
-
                     // Build list of various available actions
                     List<ViewEntry> actions = new ArrayList<ViewEntry>();
 
@@ -299,8 +255,8 @@
                     ViewAdapter adapter = new ViewAdapter(this, actions);
                     setListAdapter(adapter);
                 }
-                mNameView.setText(nameText);
-                mNumberView.setText(numberText);
+                mPhoneCallDetailsViews.setPhoneCallDetails(getResources(), date, callType, nameText,
+                        numberText, numberType, numberLabel);
 
                 loadContactPhotos(photoId);
             } else {
diff --git a/src/com/android/contacts/PhoneCallDetailsViews.java b/src/com/android/contacts/PhoneCallDetailsViews.java
new file mode 100644
index 0000000..713a667
--- /dev/null
+++ b/src/com/android/contacts/PhoneCallDetailsViews.java
@@ -0,0 +1,124 @@
+/*
+ * 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;
+
+import com.android.contacts.format.FormatUtils;
+
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.telephony.PhoneNumberUtils;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * Encapsulates the views that are used to display the details of a phone call in the call log.
+ */
+public final class PhoneCallDetailsViews {
+    public final TextView mNameView;
+    public final TextView mCallTypeAndDateView;
+    public final TextView mNumberView;
+
+    /**
+     * Creates a new instance and caches its views.
+     *
+     * @param view the view which contains the elements to fill
+     */
+    public PhoneCallDetailsViews(View view) {
+        mNameView = (TextView) view.findViewById(R.id.name);
+        mCallTypeAndDateView = (TextView) view.findViewById(R.id.call_type);
+        mNumberView = (TextView) view.findViewById(R.id.number);
+    }
+
+    /**
+     * Fills the call details views with content.
+     *
+     * @param resources used to look up strings
+     * @param date the date of the call, in milliseconds since the epoch
+     * @param callType the type of call, as defined in the call log table
+     * @param name the name of the contact, if available
+     * @param number the number of the other party involved in the call
+     * @param numberType the type of phone, e.g., {@link Phone#TYPE_HOME}, 0 if not available
+     * @param numberLabel the custom label associated with the phone number in the contact
+     */
+    public void setPhoneCallDetails(Resources resources, long date, int callType,
+            CharSequence name, CharSequence number, int numberType, CharSequence numberLabel) {
+        CharSequence callTypeText = "";
+        switch (callType) {
+            case Calls.INCOMING_TYPE:
+                callTypeText = resources.getString(R.string.type_incoming);
+                break;
+
+            case Calls.OUTGOING_TYPE:
+                callTypeText = resources.getString(R.string.type_outgoing);
+                break;
+
+            case Calls.MISSED_TYPE:
+                callTypeText = resources.getString(R.string.type_missed);
+                break;
+        }
+
+        CharSequence shortDateText =
+            DateUtils.getRelativeTimeSpanString(date,
+                    System.currentTimeMillis(),
+                    DateUtils.MINUTE_IN_MILLIS,
+                    DateUtils.FORMAT_ABBREV_RELATIVE);
+
+        CharSequence callTypeAndDateText = FormatUtils.applyStyleToSpan(Typeface.BOLD,
+                callTypeText + " " + shortDateText, 0, callTypeText.length(),
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        CharSequence numberFormattedLabel = null;
+        // Only show a label if the number is shown and it is not a SIP address.
+        if (!TextUtils.isEmpty(number) && !PhoneNumberUtils.isUriNumber(number.toString())) {
+            numberFormattedLabel = Phone.getTypeLabel(resources, numberType, numberLabel);
+        }
+
+        CharSequence nameText;
+        CharSequence numberText;
+        if (TextUtils.isEmpty(name)) {
+            nameText = number;
+            numberText = "";
+        } else {
+            nameText = name;
+            numberText = number;
+            if (callType != 0 && numberFormattedLabel != null) {
+                numberText = FormatUtils.applyStyleToSpan(Typeface.BOLD,
+                        numberFormattedLabel + " " + number, 0,
+                        numberFormattedLabel.length(),
+                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            }
+        }
+
+        mCallTypeAndDateView.setText(callTypeAndDateText);
+        mCallTypeAndDateView.setVisibility(View.VISIBLE);
+        mNameView.setText(nameText);
+        mNameView.setVisibility(View.VISIBLE);
+        // Do not show the number if it is not available. This happens if we have only the number,
+        // in which case the number is shown in the name field instead.
+        if (!TextUtils.isEmpty(numberText)) {
+            mNumberView.setText(numberText);
+            mNumberView.setVisibility(View.VISIBLE);
+        } else {
+            mNumberView.setVisibility(View.GONE);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 0541edb..30e445e 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -23,9 +23,12 @@
 import com.android.contacts.list.ContactsRequest;
 
 import android.app.ActionBar;
+import android.app.ActionBar.LayoutParams;
 import android.content.Context;
 import android.os.Bundle;
 import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
 import android.widget.SearchView;
 import android.widget.SearchView.OnCloseListener;
 import android.widget.SearchView.OnQueryTextListener;
@@ -60,14 +63,21 @@
 
     private ActionBar mActionBar;
 
-    public ActionBarAdapter(Context context) {
+    private View mCustomSearchView;
+    private LayoutParams mLayoutParams;
+    private boolean mIsSearchInOverflowMenu;
+
+    public ActionBarAdapter(Context context, Listener listener) {
         mContext = context;
+        mListener = listener;
         mSearchLabelText = mContext.getString(R.string.search_label);
     }
 
-    public void onCreate(Bundle savedState, ContactsRequest request, ActionBar actionBar) {
+    public void onCreate(Bundle savedState, ContactsRequest request, ActionBar actionBar,
+            boolean searchInOverflowMenu) {
         mActionBar = actionBar;
         mQueryString = null;
+        mIsSearchInOverflowMenu = searchInOverflowMenu;
 
         if (savedState != null) {
             mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE);
@@ -100,6 +110,10 @@
         mFilterController.addListener(this);
     }
 
+    public boolean isSearchInOverflowMenu() {
+        return mIsSearchInOverflowMenu;
+    }
+
     public boolean isSearchMode() {
         return mSearchMode;
     }
@@ -132,12 +146,31 @@
 
     public void update() {
         if (mSearchMode) {
+            // If the search icon was in the overflow menu, then inflate a custom view containing
+            // a search view for the action bar (and hide the tabs).
+            if (mIsSearchInOverflowMenu) {
+                if (mCustomSearchView == null) {
+                    mCustomSearchView = LayoutInflater.from(mContext).inflate(
+                            R.layout.custom_action_bar, null);
+                    mLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
+                            LayoutParams.WRAP_CONTENT);
+                    SearchView searchView = (SearchView) mCustomSearchView.
+                            findViewById(R.id.search_view);
+                    searchView.setQueryHint(mContext.getString(R.string.hint_findContacts));
+                    setSearchView(searchView);
+                }
+                mActionBar.setDisplayShowCustomEnabled(true);
+                mActionBar.setCustomView(mCustomSearchView, mLayoutParams);
+                mSearchView.requestFocus();
+            } else {
+                mActionBar.setTitle(mSearchLabelText);
+            }
             mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
-            mActionBar.setTitle(mSearchLabelText);
             if (mListener != null) {
                 mListener.onAction(Action.START_SEARCH_MODE);
             }
         } else {
+            mActionBar.setDisplayShowCustomEnabled(false);
             mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
             mActionBar.setTitle(null);
             if (mListener != null) {
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index c255ba1..c63f76a 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -217,6 +217,7 @@
             case ContactsRequest.ACTION_PICK_CONTACT: {
                 ContactPickerFragment fragment = new ContactPickerFragment();
                 fragment.setSearchMode(mRequest.isSearchMode());
+                fragment.setIncludeProfile(mRequest.shouldIncludeProfile());
                 mListFragment = fragment;
                 break;
             }
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 502c31d..53dd306 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -18,17 +18,12 @@
 
 import com.android.contacts.ContactSaveService;
 import com.android.contacts.ContactsActivity;
-import com.android.contacts.GroupMetaData;
 import com.android.contacts.R;
-import com.android.contacts.calllog.CallLogFragment;
 import com.android.contacts.detail.ContactDetailFragment;
-import com.android.contacts.dialpad.DialpadFragment;
 import com.android.contacts.group.GroupBrowseListFragment;
 import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener;
 import com.android.contacts.group.GroupDetailFragment;
 import com.android.contacts.interactions.ContactDeletionInteraction;
-import com.android.contacts.interactions.GroupDeletionDialogFragment;
-import com.android.contacts.interactions.GroupRenamingDialogFragment;
 import com.android.contacts.interactions.ImportExportDialogFragment;
 import com.android.contacts.interactions.PhoneNumberInteraction;
 import com.android.contacts.list.ContactBrowseListContextMenuAdapter;
@@ -47,7 +42,6 @@
 import com.android.contacts.list.ProviderStatusLoader;
 import com.android.contacts.list.ProviderStatusLoader.ProviderStatusListener;
 import com.android.contacts.list.StrequentContactListFragment;
-import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.preference.ContactsPreferenceActivity;
 import com.android.contacts.util.AccountSelectionUtil;
 import com.android.contacts.util.DialogManager;
@@ -70,7 +64,6 @@
 import android.os.Bundle;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Intents;
 import android.provider.ContactsContract.ProviderStatus;
 import android.provider.Settings;
 import android.util.Log;
@@ -79,7 +72,6 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.Window;
 import android.widget.SearchView;
 import android.widget.Toast;
@@ -133,19 +125,22 @@
 
     private ContactListFilterController mContactListFilterController;
 
-    private View mAddContactImageView;
-
     private ContactsUnavailableFragment mContactsUnavailableFragment;
     private ProviderStatusLoader mProviderStatusLoader;
     private int mProviderStatus = -1;
 
     private boolean mOptionsMenuContactsAvailable;
-    private boolean mOptionsMenuGroupActionsEnabled;
 
     private DefaultContactBrowseListFragment mContactsFragment;
     private StrequentContactListFragment mFavoritesFragment;
     private GroupBrowseListFragment mGroupsFragment;
 
+    private enum TabState {
+        FAVORITES, CONTACTS, GROUPS
+    }
+
+    private TabState mSelectedTab;
+
     public PeopleActivity() {
         mIntentResolver = new ContactsIntentResolver(this);
         // TODO: Get rid of the ContactListFilterController class because there aren't any
@@ -190,17 +185,6 @@
     protected void onCreate(Bundle savedState) {
         super.onCreate(savedState);
 
-        mAddContactImageView = getLayoutInflater().inflate(
-                R.layout.add_contact_menu_item, null, false);
-        View item = mAddContactImageView.findViewById(R.id.menu_item);
-        item.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
-                startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
-            }
-        });
-
         configureContentView(true, savedState);
     }
 
@@ -264,8 +248,8 @@
 
         setTitle(mRequest.getActivityTitle());
         ActionBar actionBar = getActionBar();
-        mActionBarAdapter = new ActionBarAdapter(this);
-        mActionBarAdapter.onCreate(savedState, mRequest, getActionBar());
+        mActionBarAdapter = new ActionBarAdapter(this, this);
+        mActionBarAdapter.onCreate(savedState, mRequest, getActionBar(), !mContentPaneDisplayed);
         mActionBarAdapter.setContactListFilterController(mContactListFilterController);
 
         if (createContentView) {
@@ -273,19 +257,19 @@
             Tab favoritesTab = actionBar.newTab();
             favoritesTab.setText(getString(R.string.strequentList));
             favoritesTab.setTabListener(new TabChangeListener(mFavoritesFragment,
-                    mContactDetailFragment));
+                    mContactDetailFragment, TabState.FAVORITES));
             actionBar.addTab(favoritesTab);
 
             Tab peopleTab = actionBar.newTab();
             peopleTab.setText(getString(R.string.people));
             peopleTab.setTabListener(new TabChangeListener(mContactsFragment,
-                    mContactDetailFragment));
+                    mContactDetailFragment, TabState.CONTACTS));
             actionBar.addTab(peopleTab);
 
             Tab groupsTab = actionBar.newTab();
             groupsTab.setText(getString(R.string.contactsGroupsLabel));
             groupsTab.setTabListener(new TabChangeListener(mGroupsFragment,
-                    mGroupDetailFragment));
+                    mGroupDetailFragment, TabState.GROUPS));
             actionBar.addTab(groupsTab);
             actionBar.setDisplayShowTitleEnabled(true);
 
@@ -293,7 +277,7 @@
             boolean showHomeIcon = a.getBoolean(R.styleable.ActionBarHomeIcon_show_home_icon, true);
             actionBar.setDisplayShowHomeEnabled(showHomeIcon);
 
-            invalidateOptionsMenu();
+            invalidateOptionsMenuIfNeeded();
         }
 
         configureFragments(savedState == null);
@@ -312,10 +296,12 @@
          * null for smaller screen sizes).
          */
         private final Fragment mDetailFragment;
+        private final TabState mTabState;
 
-        public TabChangeListener(Fragment listFragment, Fragment detailFragment) {
+        public TabChangeListener(Fragment listFragment, Fragment detailFragment, TabState state) {
             mBrowseListFragment = listFragment;
             mDetailFragment = detailFragment;
+            mTabState = state;
         }
 
         @Override
@@ -332,6 +318,8 @@
             if (mDetailFragment != null) {
                 ft.show(mDetailFragment);
             }
+            setSelectedTab(mTabState);
+            invalidateOptionsMenu();
         }
 
         @Override
@@ -339,6 +327,10 @@
         }
     }
 
+    private void setSelectedTab(TabState tab) {
+        mSelectedTab = tab;
+    }
+
     @Override
     protected void onPause() {
         if (mActionBarAdapter != null) {
@@ -346,7 +338,6 @@
         }
 
         mOptionsMenuContactsAvailable = false;
-        mOptionsMenuGroupActionsEnabled = false;
 
         mProviderStatus = -1;
         mProviderStatusLoader.setProviderStatusListener(null);
@@ -408,15 +399,16 @@
             }
 
             mListFragment.setContactsRequest(mRequest);
-            configureListFragmentForRequest();
+            configureContactListFragmentForRequest();
 
         } else {
             mSearchMode = mActionBarAdapter.isSearchMode();
         }
 
-        configureListFragment();
+        configureContactListFragment();
+        configureGroupListFragment();
 
-        invalidateOptionsMenu();
+        invalidateOptionsMenuIfNeeded();
     }
 
     @Override
@@ -427,7 +419,7 @@
 
         mListFragment.setFilter(mContactListFilterController.getFilter());
 
-        invalidateOptionsMenu();
+        invalidateOptionsMenuIfNeeded();
     }
 
     @Override
@@ -438,7 +430,7 @@
 
         mListFragment.setFilter(mContactListFilterController.getFilter());
 
-        invalidateOptionsMenu();
+        invalidateOptionsMenuIfNeeded();
     }
 
     @Override
@@ -449,10 +441,12 @@
 
     private void setupContactDetailFragment(final Uri contactLookupUri) {
         mContactDetailFragment.loadUri(contactLookupUri);
+        invalidateOptionsMenuIfNeeded();
     }
 
     private void setupGroupDetailFragment(Uri groupUri) {
         mGroupDetailFragment.loadGroup(groupUri);
+        invalidateOptionsMenuIfNeeded();
     }
 
     /**
@@ -462,23 +456,45 @@
     public void onAction(Action action) {
         switch (action) {
             case START_SEARCH_MODE:
-                // Bring the contact list fragment to the front.
+                // Bring the contact list fragment (and detail fragment if applicable) to the front
                 FragmentTransaction ft = getFragmentManager().beginTransaction();
                 ft.show(mContactsFragment);
+                if (mContactDetailFragment != null) ft.show(mContactDetailFragment);
                 ft.commit();
+                clearSearch();
                 break;
             case STOP_SEARCH_MODE:
+                // Refresh the fragments because search mode was using them to display search
+                // results.
+                clearSearch();
+
+                // If the last selected tab was not the "All contacts" tab, then hide these
+                // fragments because we need to show favorites or groups.
+                if (mSelectedTab != null && !mSelectedTab.equals(TabState.CONTACTS)) {
+                    FragmentTransaction transaction = getFragmentManager().beginTransaction();
+                    transaction.hide(mContactsFragment);
+                    if (mContactDetailFragment != null) transaction.hide(mContactDetailFragment);
+                    transaction.commit();
+                }
+                break;
             case CHANGE_SEARCH_QUERY:
-                // Refresh the contact list fragment.
-                configureFragments(false /* from request */);
-                mListFragment.setQueryString(mActionBarAdapter.getQueryString(), true);
+                loadSearch(mActionBarAdapter.getQueryString());
                 break;
             default:
                 throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action);
         }
     }
 
-    private void configureListFragmentForRequest() {
+    private void clearSearch() {
+        loadSearch("");
+    }
+
+    private void loadSearch(String query) {
+        configureFragments(false /* from request */);
+        mListFragment.setQueryString(query, true);
+    }
+
+    private void configureContactListFragmentForRequest() {
         Uri contactUri = mRequest.getContactUri();
         if (contactUri != null) {
             mListFragment.setSelectedContactUri(contactUri);
@@ -498,7 +514,7 @@
         }
     }
 
-    private void configureListFragment() {
+    private void configureContactListFragment() {
         mListFragment.setSearchMode(mSearchMode);
 
         mListFragment.setVisibleScrollbarEnabled(!mSearchMode);
@@ -510,6 +526,14 @@
         mListFragment.setQuickContactEnabled(!mContentPaneDisplayed);
     }
 
+    private void configureGroupListFragment() {
+        mGroupsFragment.setVerticalScrollbarPosition(
+                mContentPaneDisplayed
+                        ? View.SCROLLBAR_POSITION_LEFT
+                        : View.SCROLLBAR_POSITION_RIGHT);
+        mGroupsFragment.setSelectionVisible(mContentPaneDisplayed);
+    }
+
     @Override
     public void onProviderStatusChange() {
         updateFragmentVisibility();
@@ -555,7 +579,7 @@
             }
         }
 
-        invalidateOptionsMenu();
+        invalidateOptionsMenuIfNeeded();
     }
 
     private final class ContactBrowserActionListener implements OnContactBrowserActionListener {
@@ -768,8 +792,6 @@
 
         MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.actions, menu);
-        // TODO: Figure out if R.menu.list or R.menu.search are necessary according to the overflow
-        // menus on the UX mocks.
         MenuItem searchMenuItem = menu.findItem(R.id.menu_search);
         if (searchMenuItem != null && searchMenuItem.getActionView() instanceof SearchView) {
             SearchView searchView = (SearchView) searchMenuItem.getActionView();
@@ -780,18 +802,12 @@
                 mActionBarAdapter.setSearchView(searchView);
             }
         }
-
-        // TODO: Can remove this as a custom view because the account selector is in the editor now.
-        // Change add contact button to button with a custom view
-        final MenuItem addContact = menu.findItem(R.id.menu_add);
-        addContact.setActionView(mAddContactImageView);
         return true;
     }
 
-    @Override
-    public void invalidateOptionsMenu() {
+    private void invalidateOptionsMenuIfNeeded() {
         if (isOptionsMenuChanged()) {
-            super.invalidateOptionsMenu();
+            invalidateOptionsMenu();
         }
     }
 
@@ -800,10 +816,6 @@
             return true;
         }
 
-        if (mOptionsMenuGroupActionsEnabled != areGroupActionsEnabled()) {
-            return true;
-        }
-
         if (mListFragment != null && mListFragment.isOptionsMenuChanged()) {
             return true;
         }
@@ -812,6 +824,10 @@
             return true;
         }
 
+        if (mGroupDetailFragment != null && mGroupDetailFragment.isOptionsMenuChanged()) {
+            return true;
+        }
+
         return false;
     }
 
@@ -822,39 +838,42 @@
             return false;
         }
 
+        final MenuItem addContactMenu = menu.findItem(R.id.menu_add_contact);
+        final MenuItem addGroupMenu = menu.findItem(R.id.menu_add_group);
+        final MenuItem searchMenu = menu.findItem(R.id.menu_search);
+
+        if (mActionBarAdapter.isSearchMode()) {
+            addContactMenu.setVisible(false);
+            addGroupMenu.setVisible(false);
+            // If search is normally in the overflow menu, when we are in search
+            // mode, hide this option.
+            if (mActionBarAdapter.isSearchInOverflowMenu()) {
+                searchMenu.setVisible(false);
+            }
+        } else {
+            switch (mSelectedTab) {
+                case FAVORITES:
+                    // TODO: Fall through until we determine what the menu items should be for
+                    // this tab
+                case CONTACTS:
+                    addContactMenu.setVisible(true);
+                    addGroupMenu.setVisible(false);
+                    break;
+                case GROUPS:
+                    addContactMenu.setVisible(false);
+                    addGroupMenu.setVisible(true);
+                    break;
+            }
+        }
+
         MenuItem settings = menu.findItem(R.id.menu_settings);
         if (settings != null) {
             settings.setVisible(!ContactsPreferenceActivity.isEmpty(this));
         }
 
-        mOptionsMenuGroupActionsEnabled = areGroupActionsEnabled();
-
-        MenuItem renameGroup = menu.findItem(R.id.menu_rename_group);
-        if (renameGroup != null) {
-            renameGroup.setVisible(mOptionsMenuGroupActionsEnabled);
-        }
-
-        MenuItem deleteGroup = menu.findItem(R.id.menu_delete_group);
-        if (deleteGroup != null) {
-            deleteGroup.setVisible(mOptionsMenuGroupActionsEnabled);
-        }
-
         return true;
     }
 
-    private boolean areGroupActionsEnabled() {
-        boolean groupActionsEnabled = false;
-        if (mListFragment != null) {
-            ContactListFilter filter = mListFragment.getFilter();
-            if (filter != null
-                    && filter.filterType == ContactListFilter.FILTER_TYPE_GROUP
-                    && !filter.groupReadOnly) {
-                groupActionsEnabled = true;
-            }
-        }
-        return groupActionsEnabled;
-    }
-
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
@@ -872,11 +891,16 @@
                 onSearchRequested();
                 return true;
             }
-            case R.id.menu_add: {
+            case R.id.menu_add_contact: {
                 final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
                 startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
                 return true;
             }
+            case R.id.menu_add_group: {
+                // TODO: Hook up "new group" functionality
+                Toast.makeText(this, "NEW GROUP", Toast.LENGTH_SHORT).show();
+                return true;
+            }
             case R.id.menu_import_export: {
                 ImportExportDialogFragment.show(getFragmentManager());
                 return true;
@@ -890,18 +914,6 @@
                 startActivity(intent);
                 return true;
             }
-            case R.id.menu_rename_group: {
-                ContactListFilter filter = mListFragment.getFilter();
-                GroupRenamingDialogFragment.show(getFragmentManager(), filter.groupId,
-                        filter.title);
-                return true;
-            }
-            case R.id.menu_delete_group: {
-                ContactListFilter filter = mListFragment.getFilter();
-                GroupDeletionDialogFragment.show(getFragmentManager(), filter.groupId,
-                        filter.title);
-                return true;
-            }
         }
         return false;
     }
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index f3a4805..41ac0ad 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -1159,7 +1159,7 @@
 
     @Override
     public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
-        inflater.inflate(R.menu.view, menu);
+        inflater.inflate(R.menu.view_contact, menu);
     }
 
     public boolean isOptionsMenuChanged() {
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
index c17fc08..f530186 100644
--- a/src/com/android/contacts/group/GroupBrowseListAdapter.java
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -18,6 +18,7 @@
 
 import com.android.contacts.GroupMetaData;
 import com.android.contacts.R;
+import com.android.contacts.list.ContactListPinnedHeaderView;
 
 import android.content.ContentUris;
 import android.content.Context;
@@ -31,19 +32,55 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Adapter to populate the list of groups.
  */
 public class GroupBrowseListAdapter extends BaseAdapter {
 
+    private Context mContext;
     private LayoutInflater mLayoutInflater;
-    private List<GroupMetaData> mGroupList;
 
-    public GroupBrowseListAdapter(Context context, List<GroupMetaData> groupList) {
+    private List<GroupListEntry> mGroupList = new ArrayList<GroupListEntry>();
+    private boolean mSelectionVisible;
+    private Uri mSelectedGroupUri;
+
+    enum ViewType {
+        HEADER, ITEM;
+    }
+
+    private static final int VIEW_TYPE_COUNT = ViewType.values().length;
+
+    public GroupBrowseListAdapter(Context context, Map<String, List<GroupMetaData>> groupMap) {
+        mContext = context;
         mLayoutInflater = LayoutInflater.from(context);
-        mGroupList = groupList;
+        for (String accountName : groupMap.keySet()) {
+            List<GroupMetaData> groupsListForAccount = groupMap.get(accountName);
+
+            // Add account name as header for section
+            mGroupList.add(GroupListEntry.createEntryForHeader(accountName,
+                    groupsListForAccount.size()));
+
+            // Add groups within that account as subsequent list items.
+            for (GroupMetaData singleGroup : groupsListForAccount) {
+                mGroupList.add(GroupListEntry.createEntryForGroup(singleGroup));
+            }
+        }
+    }
+
+    public void setSelectionVisible(boolean flag) {
+        mSelectionVisible = flag;
+    }
+
+    public void setSelectedGroup(Uri groupUri) {
+        mSelectedGroupUri = groupUri;
+    }
+
+    private boolean isSelectedGroup(Uri groupUri) {
+        return mSelectedGroupUri != null && mSelectedGroupUri.equals(groupUri);
     }
 
     @Override
@@ -53,24 +90,109 @@
 
     @Override
     public long getItemId(int position) {
-        return getItem(position).getGroupId();
+        return mGroupList.get(position).id;
     }
 
     @Override
-    public GroupMetaData getItem(int position) {
+    public GroupListEntry getItem(int position) {
         return mGroupList.get(position);
     }
 
     @Override
+    public int getItemViewType(int position) {
+        return mGroupList.get(position).type.ordinal();
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        return VIEW_TYPE_COUNT;
+    }
+
+    @Override
+    public boolean areAllItemsEnabled() {
+        return false;
+    }
+
+    @Override
+    public boolean isEnabled(int position) {
+        return mGroupList.get(position).type == ViewType.ITEM;
+    }
+
+    @Override
     public View getView(int position, View convertView, ViewGroup parent) {
+        GroupListEntry item = getItem(position);
+        switch (item.type) {
+            case HEADER:
+                return getHeaderView(item, convertView, parent);
+            case ITEM:
+                return getGroupListItemView(item, convertView, parent);
+            default:
+                throw new IllegalStateException("Invalid GroupListEntry item type " + item.type);
+        }
+
+    }
+
+    private View getHeaderView(GroupListEntry entry, View convertView, ViewGroup parent) {
+        ContactListPinnedHeaderView result = (ContactListPinnedHeaderView) (convertView == null ?
+                new ContactListPinnedHeaderView(mContext, null) :
+                convertView);
+        String groupCountString = mContext.getResources().getQuantityString(
+                R.plurals.num_groups_in_account, entry.count, entry.count);
+        // TODO: Format this correctly by using 2 TextViews when the
+        // ContactListPinnedHeaderView is refactored.
+        result.setSectionHeader(entry.title + " " + groupCountString);
+                        return result;
+    }
+
+    private View getGroupListItemView(GroupListEntry entry, View convertView, ViewGroup parent) {
         GroupListItem result = (GroupListItem) (convertView == null ?
                 mLayoutInflater.inflate(R.layout.group_browse_list_item, parent, false) :
                 convertView);
-        result.loadFromGroup(getItem(position));
+        result.loadFromGroup(entry.groupData);
+        if (mSelectionVisible) {
+            result.setActivated(isSelectedGroup(result.getUri()));
+        }
         return result;
     }
 
     /**
+     * This is a data model object to represent one row in the list of groups were the entry
+     * could be a header or group item.
+     */
+    public static class GroupListEntry {
+        public final ViewType type;
+        public final String title;
+        public final int count;
+        public final GroupMetaData groupData;
+        /**
+         * The id is equal to the group ID (if groupData is available), otherwise it is -1 for
+         * header entries.
+         */
+        public final long id;
+
+        private GroupListEntry(ViewType entryType, String headerTitle, int headerGroupCount,
+                GroupMetaData groupMetaData, long entryId) {
+            type = entryType;
+            title = headerTitle;
+            count = headerGroupCount;
+            groupData = groupMetaData;
+            id = entryId;
+        }
+
+        public static GroupListEntry createEntryForHeader(String headerTitle, int groupCount) {
+            return new GroupListEntry(ViewType.HEADER, headerTitle, groupCount, null, -1);
+        }
+
+        public static GroupListEntry createEntryForGroup(GroupMetaData groupMetaData) {
+            if (groupMetaData == null) {
+                throw new IllegalStateException("Cannot create list entry for a null group");
+            }
+            return new GroupListEntry(ViewType.ITEM, null, 0, groupMetaData,
+                    groupMetaData.getGroupId());
+        }
+    }
+
+    /**
      * A row in a list of groups, where this row displays a single group's title
      * and associated account.
      */
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
index 150a00f..958d9f8 100644
--- a/src/com/android/contacts/group/GroupBrowseListFragment.java
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -44,7 +44,9 @@
 import android.widget.ListView;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Fragment to display the list of groups.
@@ -71,12 +73,24 @@
 
     private Context mContext;
     private Cursor mGroupListCursor;
-    private List<GroupMetaData> mGroupList = new ArrayList<GroupMetaData>();
+
+    /**
+     * Map of account name to a list of {@link GroupMetaData} objects
+     * representing groups within that account.
+     * TODO: Change account name string into a wrapper object that has
+     * account name, type, and authority.
+     */
+    private Map<String, List<GroupMetaData>> mGroupMap = new HashMap<String, List<GroupMetaData>>();
 
     private View mRootView;
     private ListView mListView;
     private View mEmptyView;
 
+    private GroupBrowseListAdapter mAdapter;
+    private boolean mSelectionVisible;
+
+    private int mVerticalScrollbarPosition = View.SCROLLBAR_POSITION_RIGHT;
+
     private OnGroupBrowserActionListener mListener;
 
     public GroupBrowseListFragment() {
@@ -93,6 +107,31 @@
         return mRootView;
     }
 
+    public void setVerticalScrollbarPosition(int position) {
+        if (mVerticalScrollbarPosition != position) {
+            mVerticalScrollbarPosition = position;
+            configureVerticalScrollbar();
+        }
+    }
+
+    private void configureVerticalScrollbar() {
+        mListView.setFastScrollEnabled(true);
+        mListView.setFastScrollAlwaysVisible(true);
+        mListView.setVerticalScrollbarPosition(mVerticalScrollbarPosition);
+        mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+        int leftPadding = 0;
+        int rightPadding = 0;
+        if (mVerticalScrollbarPosition == View.SCROLLBAR_POSITION_LEFT) {
+            leftPadding = mContext.getResources().getDimensionPixelOffset(
+                    R.dimen.list_visible_scrollbar_padding);
+        } else {
+            rightPadding = mContext.getResources().getDimensionPixelOffset(
+                    R.dimen.list_visible_scrollbar_padding);
+        }
+        mListView.setPadding(leftPadding, mListView.getPaddingTop(),
+                rightPadding, mListView.getPaddingBottom());
+    }
+
     @Override
     public void onAttach(Activity activity) {
         super.onAttach(activity);
@@ -136,7 +175,7 @@
         if (mGroupListCursor == null) {
             return;
         }
-        mGroupList.clear();
+        mGroupMap.clear();
         mGroupListCursor.moveToPosition(-1);
         while (mGroupListCursor.moveToNext()) {
             String accountName = mGroupListCursor.getString(GroupMetaDataLoader.ACCOUNT_NAME);
@@ -150,12 +189,24 @@
                     ? false
                     : mGroupListCursor.getInt(GroupMetaDataLoader.FAVORITES) != 0;
 
-            // TODO: Separate groups according to account name and type.
-            mGroupList.add(new GroupMetaData(
-                    accountName, accountType, groupId, title, defaultGroup, favorites));
+            GroupMetaData newGroup = new GroupMetaData(accountName, accountType, groupId, title,
+                    defaultGroup, favorites);
+
+            if (mGroupMap.containsKey(accountName)) {
+                List<GroupMetaData> groups = mGroupMap.get(accountName);
+                groups.add(newGroup);
+            } else {
+                List<GroupMetaData> groups = new ArrayList<GroupMetaData>();
+                groups.add(newGroup);
+                mGroupMap.put(accountName, groups);
+            }
+
         }
 
-        mListView.setAdapter(new GroupBrowseListAdapter(mContext, mGroupList));
+        mAdapter = new GroupBrowseListAdapter(mContext, mGroupMap);
+        mAdapter.setSelectionVisible(mSelectionVisible);
+
+        mListView.setAdapter(mAdapter);
         mListView.setEmptyView(mEmptyView);
         mListView.setOnItemClickListener(new OnItemClickListener() {
             @Override
@@ -170,7 +221,17 @@
         mListener = listener;
     }
 
+    public void setSelectionVisible(boolean flag) {
+        mSelectionVisible = flag;
+    }
+
+    private void setSelectedGroup(Uri groupUri) {
+        mAdapter.setSelectedGroup(groupUri);
+        mListView.invalidateViews();
+    }
+
     private void viewGroup(Uri groupUri) {
+        setSelectedGroup(groupUri);
         if (mListener != null) mListener.onViewGroupAction(groupUri);
     }
 
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index 8cffd05..8d049d5 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -17,23 +17,21 @@
 package com.android.contacts.group;
 
 import com.android.contacts.ContactPhotoManager;
-import com.android.contacts.GroupMetaData;
 import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
-import com.android.contacts.activities.GroupDetailActivity;
+import com.android.contacts.interactions.GroupDeletionDialogFragment;
+import com.android.contacts.interactions.GroupRenamingDialogFragment;
 import com.android.contacts.list.ContactListAdapter;
 import com.android.contacts.list.ContactListFilter;
 import com.android.contacts.list.DefaultContactListAdapter;
+import com.android.contacts.util.PhoneCapabilityTester;
 
-import android.accounts.Account;
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.LoaderManager;
 import android.app.LoaderManager.LoaderCallbacks;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.CursorLoader;
-import android.content.Intent;
 import android.content.Loader;
 import android.database.Cursor;
 import android.net.Uri;
@@ -41,6 +39,9 @@
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Directory;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
@@ -49,8 +50,7 @@
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ListView;
 import android.widget.TextView;
-
-import java.util.ArrayList;
+import android.widget.Toast;
 
 /**
  * Displays the details of a group and shows a list of actions possible for the group.
@@ -88,6 +88,9 @@
 
     private Uri mGroupUri;
     private long mGroupId;
+    private String mGroupName;
+
+    private boolean mOptionsMenuEditable;
 
     public GroupDetailFragment() {
     }
@@ -107,6 +110,7 @@
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+        setHasOptionsMenu(true);
         mRootView = inflater.inflate(R.layout.group_detail_fragment, container, false);
         mGroupTitle = (TextView) mRootView.findViewById(R.id.group_title);
         mGroupSize = (TextView) mRootView.findViewById(R.id.group_size);
@@ -224,7 +228,8 @@
         cursor.moveToPosition(-1);
         if (cursor.moveToNext()) {
             mGroupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
-            updateTitle(cursor.getString(GroupMetaDataLoader.TITLE));
+            mGroupName = cursor.getString(GroupMetaDataLoader.TITLE);
+            updateTitle(mGroupName);
         }
     }
 
@@ -257,4 +262,53 @@
             mPhotoManager.resume();
         }
     }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
+        inflater.inflate(R.menu.view_group, menu);
+    }
+
+    public boolean isOptionsMenuChanged() {
+        return mOptionsMenuEditable != isGroupEditable();
+    }
+
+    public boolean isGroupEditable() {
+        // TODO: This should check the group_is_read_only flag. Modify GroupMetaDataLoader.
+        // Bug: 4601729.
+        return mGroupUri != null;
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        mOptionsMenuEditable = isGroupEditable();
+
+        final MenuItem editMenu = menu.findItem(R.id.menu_edit_group);
+        editMenu.setVisible(mOptionsMenuEditable);
+
+        final MenuItem renameMenu = menu.findItem(R.id.menu_rename_group);
+        renameMenu.setVisible(mOptionsMenuEditable);
+
+        final MenuItem deleteMenu = menu.findItem(R.id.menu_delete_group);
+        deleteMenu.setVisible(mOptionsMenuEditable);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_edit_group: {
+                // TODO: Open group editor
+                Toast.makeText(mContext, "EDIT GROUP", Toast.LENGTH_SHORT).show();
+                break;
+            }
+            case R.id.menu_rename_group: {
+                GroupRenamingDialogFragment.show(getFragmentManager(), mGroupId, mGroupName);
+                return true;
+            }
+            case R.id.menu_delete_group: {
+                GroupDeletionDialogFragment.show(getFragmentManager(), mGroupId, mGroupName);
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index 7f7dad4..fc9abe7 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -386,6 +386,9 @@
                         mSelectedContactDirectoryId, mSelectedContactLookupKey, mSelectedContactId);
             }
         }
+
+        // Display the user's profile.
+        adapter.setIncludeProfile(true);
     }
 
     @Override
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index a2b264b..95a8c2b 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -28,6 +28,7 @@
 import android.os.Bundle;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.ContactCounts;
+import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Directory;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
@@ -62,6 +63,7 @@
 
     private boolean mDisplayPhotos;
     private boolean mQuickContactEnabled;
+    private boolean mIncludeProfile;
     private ContactPhotoManager mPhotoLoader;
 
     private String mQueryString;
@@ -266,6 +268,14 @@
         mQuickContactEnabled = quickContactEnabled;
     }
 
+    public boolean shouldIncludeProfile() {
+        return mIncludeProfile;
+    }
+
+    public void setIncludeProfile(boolean includeProfile) {
+        mIncludeProfile = includeProfile;
+    }
+
     public boolean isDataRestrictedByCallingPackage() {
         return mDataRestrictedByCallingPackage;
     }
@@ -411,7 +421,9 @@
     @Override
     public int getItemViewType(int partitionIndex, int position) {
         int type = super.getItemViewType(partitionIndex, position);
-        if (isSectionHeaderDisplayEnabled() && partitionIndex == getIndexedPartition()) {
+        if (!isUserProfile(position)
+                && isSectionHeaderDisplayEnabled()
+                && partitionIndex == getIndexedPartition()) {
             Placement placement = getItemPlacementInSection(position);
             return placement.firstInSection ? type : getItemViewTypeCount() + type;
         } else {
@@ -526,6 +538,33 @@
         }
     }
 
+    /**
+     * Checks whether the contact entry at the given position represents the user's profile.
+     */
+    protected boolean isUserProfile(int position) {
+        // The profile only ever appears in the first position if it is present.  So if the position
+        // is anything beyond 0, it can't be the profile.
+        boolean isUserProfile = false;
+        if (position == 0) {
+            int partition = getPartitionForPosition(position);
+            if (partition >= 0) {
+                // Save the old cursor position - the call to getItem() may modify the cursor
+                // position.
+                int offset = getCursor(partition).getPosition();
+                Cursor cursor = (Cursor) getItem(position);
+                if (cursor != null) {
+                    int profileColumnIndex = cursor.getColumnIndex(Contacts.IS_USER_PROFILE);
+                    if (profileColumnIndex != -1) {
+                        isUserProfile = cursor.getInt(profileColumnIndex) == 1;
+                    }
+                    // Restore the old cursor position.
+                    cursor.moveToPosition(offset);
+                }
+            }
+        }
+        return isUserProfile;
+    }
+
     // TODO: fix PluralRules to handle zero correctly and use Resources.getQuantityText directly
     public String getQuantityText(int count, int zeroResourceId, int pluralResourceId) {
         if (count == 0) {
@@ -544,4 +583,30 @@
         }
         return true;
     }
+
+    @Override
+    public Placement getItemPlacementInSection(int position) {
+        // Special case code to prevent a section header from being displayed above the user's
+        // profile entry.
+        if (isUserProfile(position)) {
+            // The user profile entry shouldn't display a section header above; the header should be
+            // displayed on top of the item below.
+            Placement placement = new Placement();
+            placement.firstInSection = false;
+            placement.lastInSection = false;
+            placement.sectionHeader = null;
+            return placement;
+        } else if (position > 0 && isUserProfile(position - 1)) {
+            // If the item in the previous position is the user's profile, behave as if this entry
+            // is the first in the section.
+            Placement profilePlacement = super.getItemPlacementInSection(position - 1);
+            String profileHeader = profilePlacement.sectionHeader;
+            Placement placement = super.getItemPlacementInSection(position);
+            placement.firstInSection = true;
+            placement.sectionHeader = profileHeader;
+            return placement;
+        } else {
+            return super.getItemPlacementInSection(position);
+        }
+    }
 }
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index a080b37..0937114 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -78,6 +78,7 @@
     private static final String KEY_SECTION_HEADER_DISPLAY_ENABLED = "sectionHeaderDisplayEnabled";
     private static final String KEY_PHOTO_LOADER_ENABLED = "photoLoaderEnabled";
     private static final String KEY_QUICK_CONTACT_ENABLED = "quickContactEnabled";
+    private static final String KEY_INCLUDE_PROFILE = "includeProfile";
     private static final String KEY_SEARCH_MODE = "searchMode";
     private static final String KEY_VISIBLE_SCROLLBAR_ENABLED = "visibleScrollbarEnabled";
     private static final String KEY_SCROLLBAR_POSITION = "scrollbarPosition";
@@ -100,6 +101,7 @@
     private boolean mSectionHeaderDisplayEnabled;
     private boolean mPhotoLoaderEnabled;
     private boolean mQuickContactEnabled = true;
+    private boolean mIncludeProfile;
     private boolean mSearchMode;
     private boolean mVisibleScrollbarEnabled;
     private int mVerticalScrollbarPosition = View.SCROLLBAR_POSITION_RIGHT;
@@ -235,6 +237,7 @@
         outState.putBoolean(KEY_SECTION_HEADER_DISPLAY_ENABLED, mSectionHeaderDisplayEnabled);
         outState.putBoolean(KEY_PHOTO_LOADER_ENABLED, mPhotoLoaderEnabled);
         outState.putBoolean(KEY_QUICK_CONTACT_ENABLED, mQuickContactEnabled);
+        outState.putBoolean(KEY_INCLUDE_PROFILE, mIncludeProfile);
         outState.putBoolean(KEY_SEARCH_MODE, mSearchMode);
         outState.putBoolean(KEY_VISIBLE_SCROLLBAR_ENABLED, mVisibleScrollbarEnabled);
         outState.putInt(KEY_SCROLLBAR_POSITION, mVerticalScrollbarPosition);
@@ -266,6 +269,7 @@
         mSectionHeaderDisplayEnabled = savedState.getBoolean(KEY_SECTION_HEADER_DISPLAY_ENABLED);
         mPhotoLoaderEnabled = savedState.getBoolean(KEY_PHOTO_LOADER_ENABLED);
         mQuickContactEnabled = savedState.getBoolean(KEY_QUICK_CONTACT_ENABLED);
+        mIncludeProfile = savedState.getBoolean(KEY_INCLUDE_PROFILE);
         mSearchMode = savedState.getBoolean(KEY_SEARCH_MODE);
         mVisibleScrollbarEnabled = savedState.getBoolean(KEY_VISIBLE_SCROLLBAR_ENABLED);
         mVerticalScrollbarPosition = savedState.getInt(KEY_SCROLLBAR_POSITION);
@@ -579,6 +583,10 @@
         this.mQuickContactEnabled = flag;
     }
 
+    public void setIncludeProfile(boolean flag) {
+        mIncludeProfile = flag;
+    }
+
     public void setSearchMode(boolean flag) {
         if (mSearchMode != flag) {
             mSearchMode = flag;
@@ -782,6 +790,7 @@
         }
 
         mAdapter.setQuickContactEnabled(mQuickContactEnabled);
+        mAdapter.setIncludeProfile(mIncludeProfile);
         mAdapter.setQueryString(mQueryString);
         mAdapter.setDirectorySearchMode(mDirectorySearchMode);
         mAdapter.setPinnedPartitionHeadersEnabled(mSearchMode);
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index 2c57983..cc4bea5 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -23,6 +23,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.Profile;
 import android.provider.ContactsContract.SearchSnippetColumns;
 import android.text.TextUtils;
 import android.view.View;
@@ -30,9 +31,10 @@
 import android.widget.ListView;
 import android.widget.QuickContactBadge;
 
-
 /**
  * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
+ * Also includes support for including the {@link ContactsContract.Profile} record in the
+ * list.
  */
 public abstract class ContactListAdapter extends ContactEntryListAdapter {
 
@@ -49,6 +51,7 @@
         Contacts.LOOKUP_KEY,                    // 9
         Contacts.PHONETIC_NAME,                 // 10
         Contacts.HAS_PHONE_NUMBER,              // 11
+        Contacts.IS_USER_PROFILE,               // 12
     };
 
     protected static final String[] PROJECTION_DATA = new String[] {
@@ -79,7 +82,8 @@
         Contacts.LOOKUP_KEY,                    // 9
         Contacts.PHONETIC_NAME,                 // 10
         Contacts.HAS_PHONE_NUMBER,              // 11
-        SearchSnippetColumns.SNIPPET,           // 12
+        Contacts.IS_USER_PROFILE,               // 12
+        SearchSnippetColumns.SNIPPET,           // 13
     };
 
     protected static final int CONTACT_ID_COLUMN_INDEX = 0;
@@ -94,7 +98,8 @@
     protected static final int CONTACT_LOOKUP_KEY_COLUMN_INDEX = 9;
     protected static final int CONTACT_PHONETIC_NAME_COLUMN_INDEX = 10;
     protected static final int CONTACT_HAS_PHONE_COLUMN_INDEX = 11;
-    protected static final int CONTACT_SNIPPET_COLUMN_INDEX = 12;
+    protected static final int CONTACT_IS_USER_PROFILE = 12;
+    protected static final int CONTACT_SNIPPET_COLUMN_INDEX = 13;
 
     private CharSequence mUnknownNameText;
     private int mDisplayNameColumnIndex;
@@ -106,10 +111,15 @@
 
     private ContactListFilter mFilter;
 
+    // View types for entries in the list view.
+    private final int mViewTypeProfileEntry;
+
+
     public ContactListAdapter(Context context) {
         super(context);
 
         mUnknownNameText = context.getText(android.R.string.unknownName);
+        mViewTypeProfileEntry = getViewTypeCount() - 1;
     }
 
     public CharSequence getUnknownNameText() {
@@ -150,6 +160,11 @@
                 .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
     }
 
+    protected static Uri includeProfileEntry(Uri uri) {
+        return uri.buildUpon()
+                .appendQueryParameter(ContactsContract.INCLUDE_PROFILE, "true").build();
+    }
+
     public boolean getHasPhoneNumber(int position) {
         return ((Cursor)getItem(position)).getInt(CONTACT_HAS_PHONE_COLUMN_INDEX) != 0;
     }
@@ -221,7 +236,12 @@
     @Override
     protected View newView(Context context, int partition, Cursor cursor, int position,
             ViewGroup parent) {
-        final ContactListItemView view = new ContactListItemView(context, null);
+        ContactListItemView view;
+        if (getItemViewType(position) == mViewTypeProfileEntry) {
+            view = new ContactListProfileItemView(context, null);
+        } else {
+            view = new ContactListItemView(context, null);
+        }
         view.setUnknownNameText(mUnknownNameText);
         view.setTextWithHighlightingFactory(getTextWithHighlightingFactory());
         view.setQuickContactEnabled(isQuickContactEnabled());
@@ -229,6 +249,24 @@
         return view;
     }
 
+    @Override
+    public int getItemViewType(int position) {
+        return isUserProfile(position)
+                ? mViewTypeProfileEntry
+                : super.getItemViewType(position);
+    }
+
+    @Override
+    public int getItemViewTypeCount() {
+        return super.getItemViewTypeCount() + 1;
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        // One extra view type - the user's profile entry view.
+        return super.getViewTypeCount() + 1;
+    }
+
     protected void bindSectionHeaderAndDivider(ContactListItemView view, int position) {
         if (isSectionHeaderDisplayEnabled()) {
             Placement placement = getItemPlacementInSection(position);
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index bc81cd3..032b60f 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -472,11 +472,19 @@
                         ViewGroup.LayoutParams.WRAP_CONTENT);
                 a.recycle();
             } else {
-                mPhotoViewWidth = mPhotoViewHeight = mDefaultPhotoViewSize;
+                mPhotoViewWidth = mPhotoViewHeight = getDefaultPhotoViewSize();
             }
         }
     }
 
+    protected void setDefaultPhotoViewSize(int pixels) {
+        mDefaultPhotoViewSize = pixels;
+    }
+
+    protected int getDefaultPhotoViewSize() {
+        return mDefaultPhotoViewSize;
+    }
+
     @Override
     protected void drawableStateChanged() {
         super.drawableStateChanged();
diff --git a/src/com/android/contacts/list/ContactListProfileItemView.java b/src/com/android/contacts/list/ContactListProfileItemView.java
new file mode 100644
index 0000000..f189366
--- /dev/null
+++ b/src/com/android/contacts/list/ContactListProfileItemView.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.list;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * Contact list entry that represents the user's personal profile data.
+ */
+public class ContactListProfileItemView extends ContactListItemView {
+
+    public ContactListProfileItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ContactListItemView);
+        setDefaultPhotoViewSize(a.getDimensionPixelOffset(
+                R.styleable.ContactListItemView_list_item_profile_photo_size, 0));
+    }
+
+    @Override
+    public TextView getNameTextView() {
+        TextView nameTextView = super.getNameTextView();
+        nameTextView.setTextAppearance(getContext(), android.R.style.TextAppearance_Large);
+        return nameTextView;
+    }
+
+    @Override
+    public void showDisplayName(Cursor cursor, int nameColumnIndex, int alternativeNameColumnIndex,
+            boolean highlightingEnabled, int displayOrder) {
+        getNameTextView().setText(getContext().getText(R.string.profile_display_name));
+    }
+}
diff --git a/src/com/android/contacts/list/ContactsRequest.java b/src/com/android/contacts/list/ContactsRequest.java
index e20d189..469cd1d 100644
--- a/src/com/android/contacts/list/ContactsRequest.java
+++ b/src/com/android/contacts/list/ContactsRequest.java
@@ -83,6 +83,7 @@
     private CharSequence mTitle;
     private boolean mSearchMode;
     private String mQueryString;
+    private boolean mIncludeProfile;
     private String mGroupName;
     private boolean mLegacyCompatibilityMode;
     private boolean mDirectorySearchEnabled = true;
@@ -98,6 +99,7 @@
         mTitle = request.mTitle;
         mSearchMode = request.mSearchMode;
         mQueryString = request.mQueryString;
+        mIncludeProfile = request.mIncludeProfile;
         mGroupName = request.mGroupName;
         mLegacyCompatibilityMode = request.mLegacyCompatibilityMode;
         mDirectorySearchEnabled = request.mDirectorySearchEnabled;
@@ -119,6 +121,7 @@
             request.mTitle = source.readCharSequence();
             request.mSearchMode = source.readInt() != 0;
             request.mQueryString = source.readString();
+            request.mIncludeProfile = source.readInt() != 0;
             request.mGroupName = source.readString();
             request.mLegacyCompatibilityMode  = source.readInt() != 0;
             request.mDirectorySearchEnabled = source.readInt() != 0;
@@ -134,6 +137,7 @@
         dest.writeCharSequence(mTitle);
         dest.writeInt(mSearchMode ? 1 : 0);
         dest.writeString(mQueryString);
+        dest.writeInt(mIncludeProfile ? 1 : 0);
         dest.writeString(mGroupName);
         dest.writeInt(mLegacyCompatibilityMode ? 1 : 0);
         dest.writeInt(mDirectorySearchEnabled ? 1 : 0);
@@ -192,6 +196,14 @@
         mQueryString = string;
     }
 
+    public boolean shouldIncludeProfile() {
+        return mIncludeProfile;
+    }
+
+    public void setIncludeProfile(boolean includeProfile) {
+        mIncludeProfile = includeProfile;
+    }
+
     public String getGroupName() {
         return mGroupName;
     }
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
index 3e8c6f1..6916514 100644
--- a/src/com/android/contacts/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -131,6 +131,11 @@
         }
         uri = applyDataRestriction(uri);
 
+        // Include the user's personal profile.
+        if (shouldIncludeProfile()) {
+            uri = includeProfileEntry(uri);
+        }
+
         loader.setUri(uri);
     }