Contacts UI changes for profiles.

The user's profile entry is displayed slightly differently from other Contacts.
The profile photo is 25% larger (e.g. 56dip -> 70dip) and rather than showing
the display name, it displays "My profile".

Section headers are also special-cased for the profile entry - it appears above
the section header rather than below it.

Change-Id: I6fd99d303c7dd1347031d3607741827ea2a31a0b
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/values-xlarge/styles.xml b/res/values-xlarge/styles.xml
index f73e51d..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>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 52eab2f..f259ea7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1537,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 509180e..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>
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/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);
     }