Merge "Use initLoader instead of restartLoader for ContactTileListFragment"
diff --git a/res/layout-sw680dp-w1000dp/contact_detail_list_item.xml b/res/layout-sw680dp-w1000dp/contact_detail_list_item.xml
index 98ffce6..2016131 100644
--- a/res/layout-sw680dp-w1000dp/contact_detail_list_item.xml
+++ b/res/layout-sw680dp-w1000dp/contact_detail_list_item.xml
@@ -50,15 +50,6 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:textAppearance="?android:attr/textAppearanceMedium" />
-
-            <TextView
-                android:id="@+id/footer"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textAppearance="?android:attr/textAppearanceSmall"
-                android:textColor="?android:attr/textColorSecondary"
-                android:visibility="gone" />
-
         </LinearLayout>
 
         <ImageView
diff --git a/res/layout/contact_detail_list_item.xml b/res/layout/contact_detail_list_item.xml
index 44f5a53..666b67c 100644
--- a/res/layout/contact_detail_list_item.xml
+++ b/res/layout/contact_detail_list_item.xml
@@ -82,15 +82,6 @@
 
             </LinearLayout>
 
-            <TextView
-                android:id="@+id/footer"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:textAppearance="?android:attr/textAppearanceSmall"
-                android:textColor="?android:attr/textColorSecondary"
-                android:visibility="gone" />
-
         </LinearLayout>
 
         <View
diff --git a/res/layout/quickcontact_list_item.xml b/res/layout/quickcontact_list_item.xml
index ee3a89f..1b66ec3 100755
--- a/res/layout/quickcontact_list_item.xml
+++ b/res/layout/quickcontact_list_item.xml
@@ -27,13 +27,27 @@
             android:textAppearance="?android:attr/textAppearanceMedium"
             android:singleLine="true"
             android:ellipsize="end" />
-        <TextView
-            android:id="@android:id/text2"
+        <LinearLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textColor="@color/secondary_text_color"
-            android:textAllCaps="true"
-            android:textAppearance="?android:attr/textAppearanceSmall" />
+            android:orientation="horizontal">
+            <ImageView
+                android:id="@+id/presence_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="1dip"
+                android:layout_marginRight="4dip"
+                android:layout_gravity="center_vertical"
+                android:gravity="center"
+                android:scaleType="centerInside" />
+            <TextView
+                android:id="@android:id/text2"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="@color/secondary_text_color"
+                android:textAllCaps="true"
+                android:textAppearance="?android:attr/textAppearanceSmall" />
+        </LinearLayout>
     </LinearLayout>
     <include layout="@layout/quickcontact_list_item_base"/>
 </LinearLayout>
diff --git a/res/layout/quickcontact_list_item_address.xml b/res/layout/quickcontact_list_item_address.xml
index 9773b10..cb99673 100755
--- a/res/layout/quickcontact_list_item_address.xml
+++ b/res/layout/quickcontact_list_item_address.xml
@@ -28,13 +28,27 @@
             android:layout_height="wrap_content"
             android:textColor="@color/primary_text_color"
             android:textAppearance="?android:attr/textAppearanceMedium" />
-        <TextView
-            android:id="@android:id/text2"
+        <LinearLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textColor="@color/secondary_text_color"
-            android:textAllCaps="true"
-            android:textAppearance="?android:attr/textAppearanceSmall" />
+            android:orientation="horizontal">
+            <ImageView
+                android:id="@+id/presence_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="1dip"
+                android:layout_marginRight="4dip"
+                android:layout_gravity="center_vertical"
+                android:gravity="center"
+                android:scaleType="centerInside" />
+            <TextView
+                android:id="@android:id/text2"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="@color/secondary_text_color"
+                android:textAllCaps="true"
+                android:textAppearance="?android:attr/textAppearanceSmall" />
+        </LinearLayout>
     </LinearLayout>
     <include layout="@layout/quickcontact_list_item_base"/>
 </LinearLayout>
diff --git a/res/layout/quickcontact_photo_container.xml b/res/layout/quickcontact_photo_container.xml
index e970934..3b46ef9 100644
--- a/res/layout/quickcontact_photo_container.xml
+++ b/res/layout/quickcontact_photo_container.xml
@@ -26,11 +26,6 @@
             android:layout_height="match_parent"
             android:scaleType="centerCrop" />
         <View
-            android:layout_width="match_parent"
-            android:layout_height="1dip"
-            android:layout_alignParentTop="true"
-            android:background="#4CFFFFFF" />
-        <View
             android:id="@+id/photo_text_bar"
             android:layout_width="0dip"
             android:layout_height="42dip"
diff --git a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
index 4b297d9..98abfbc 100644
--- a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
+++ b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
@@ -319,15 +319,23 @@
         // Apply a limit of 1 result to the query because we only need to
         // determine whether or not at least one other contact has the same
         // name. We don't need to find ALL other contacts with the same name.
-        Builder builder = Contacts.CONTENT_URI.buildUpon();
+        final Builder builder = Contacts.CONTENT_URI.buildUpon();
         builder.appendQueryParameter("limit", String.valueOf(1));
-        Uri uri = builder.build();
+        final Uri uri = builder.build();
 
+        final String displayNameSelection;
+        final String[] selectionArgs;
+        if (TextUtils.isEmpty(contactDisplayName)) {
+            displayNameSelection = Contacts.DISPLAY_NAME_PRIMARY + " IS NULL";
+            selectionArgs = new String[] { String.valueOf(mContactId) };
+        } else {
+            displayNameSelection = Contacts.DISPLAY_NAME_PRIMARY + " = ?";
+            selectionArgs = new String[] { contactDisplayName, String.valueOf(mContactId) };
+        }
         mQueryHandler.startQuery(TOKEN_DISAMBIGUATION_QUERY, null, uri,
                 new String[] { Contacts._ID } /* unused projection but a valid one was needed */,
-                Contacts.DISPLAY_NAME_PRIMARY + " = ? and " + Contacts.PHOTO_ID + " is null and "
-                + Contacts._ID + " <> ?",
-                new String[] { contactDisplayName, String.valueOf(mContactId) }, null);
+                displayNameSelection + " AND " + Contacts.PHOTO_ID + " IS NULL AND "
+                + Contacts._ID + " <> ?", selectionArgs, null);
     }
 
     /**
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 8f77371..f24b7eb 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -84,7 +84,6 @@
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Directory;
 import android.provider.ContactsContract.DisplayNameSources;
-import android.provider.ContactsContract.Intents.UI;
 import android.provider.ContactsContract.PhoneLookup;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.StatusUpdates;
@@ -149,7 +148,6 @@
 
     private Button mQuickFixButton;
     private QuickFix mQuickFix;
-    private int mNumPhoneNumbers = 0;
     private String mDefaultCountryIso;
     private boolean mContactHasSocialUpdates;
     private boolean mShowStaticPhoto = true;
@@ -543,7 +541,6 @@
         mRawContactIds.clear();
 
         mPrimaryPhoneUri = null;
-        mNumPhoneNumbers = 0;
 
         final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
 
@@ -598,7 +595,6 @@
                     // Always ignore the name. It is shown in the header if set
                 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     // Build phone entries
-                    mNumPhoneNumbers++;
                     String phoneNumberE164 =
                             entryValues.getAsString(PhoneLookup.NORMALIZED_NUMBER);
                     entry.data = PhoneNumberUtils.formatNumber(
@@ -644,7 +640,7 @@
                                 imKind, dataId, entryValues, mContactData.isDirectoryEntry(),
                                 mContactData.getDirectoryId());
                         buildImActions(mContext, imEntry, entryValues);
-                        imEntry.applyStatus(status, false);
+                        imEntry.setPresence(status.getPresence());
                         imEntry.maxLines = imKind.maxLinesForDisplay;
                         mImEntries.add(imEntry);
                     }
@@ -656,10 +652,10 @@
                     // Build IM entries
                     buildImActions(mContext, entry, entryValues);
 
-                    // Apply presence and status details when available
+                    // Apply presence when available
                     final DataStatus status = mContactData.getStatuses().get(entry.id);
                     if (status != null) {
-                        entry.applyStatus(status, false);
+                        entry.setPresence(status.getPresence());
                     }
                     mImEntries.add(entry);
                 } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
@@ -1228,8 +1224,6 @@
         public int presence = -1;
         public int chatCapability = 0;
 
-        public CharSequence footerLine = null;
-
         private boolean mIsInSubSection = false;
 
         DetailViewEntry() {
@@ -1280,21 +1274,8 @@
             return entry;
         }
 
-        /**
-         * Apply given {@link DataStatus} values over this {@link DetailViewEntry}
-         *
-         * @param fillData When true, the given status replaces {@link #data}
-         *            and {@link #footerLine}. Otherwise only {@link #presence}
-         *            is updated.
-         */
-        public DetailViewEntry applyStatus(DataStatus status, boolean fillData) {
-            presence = status.getPresence();
-            if (fillData && status.isValid()) {
-                this.data = status.getStatus().toString();
-                this.footerLine = status.getTimestampLabel(context);
-            }
-
-            return this;
+        public void setPresence(int presence) {
+            this.presence = presence;
         }
 
         public void setIsInSubSection(boolean isInSubSection) {
@@ -1428,7 +1409,6 @@
     private static class DetailViewCache {
         public final TextView type;
         public final TextView data;
-        public final TextView footer;
         public final ImageView presenceIcon;
         public final ImageView secondaryActionButton;
         public final View actionsViewContainer;
@@ -1442,7 +1422,6 @@
                 OnClickListener secondaryActionClickListener) {
             type = (TextView) view.findViewById(R.id.type);
             data = (TextView) view.findViewById(R.id.data);
-            footer = (TextView) view.findViewById(R.id.footer);
             primaryIndicator = view.findViewById(R.id.primary_indicator);
             presenceIcon = (ImageView) view.findViewById(R.id.presence_icon);
 
@@ -1630,14 +1609,6 @@
             views.data.setText(entry.data);
             setMaxLines(views.data, entry.maxLines);
 
-            // Set the footer
-            if (!TextUtils.isEmpty(entry.footerLine)) {
-                views.footer.setText(entry.footerLine);
-                views.footer.setVisibility(View.VISIBLE);
-            } else {
-                views.footer.setVisibility(View.GONE);
-            }
-
             // Set the default contact method
             views.primaryIndicator.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
 
@@ -2158,17 +2129,14 @@
     private final static class InvitableAccountTypesAdapter extends BaseAdapter {
         private final Context mContext;
         private final LayoutInflater mInflater;
-        private final ContactLoader.Result mContactData;
         private final ArrayList<AccountType> mAccountTypes;
 
         public InvitableAccountTypesAdapter(Context context, ContactLoader.Result contactData) {
             mContext = context;
             mInflater = LayoutInflater.from(context);
-            mContactData = contactData;
             final List<AccountType> types = contactData.getInvitableAccountTypes();
             mAccountTypes = new ArrayList<AccountType>(types.size());
 
-            AccountTypeManager manager = AccountTypeManager.getInstance(context);
             for (int i = 0; i < types.size(); i++) {
                 mAccountTypes.add(types.get(i));
             }
diff --git a/src/com/android/contacts/editor/LabeledEditorView.java b/src/com/android/contacts/editor/LabeledEditorView.java
index 2a1ec5e..c9e713b 100644
--- a/src/com/android/contacts/editor/LabeledEditorView.java
+++ b/src/com/android/contacts/editor/LabeledEditorView.java
@@ -286,7 +286,17 @@
         }
 
         // Field changes are saved directly
+        saveValue(column, value);
+
+        // Notify listener if applicable
+        notifyEditorListener();
+    }
+
+    protected void saveValue(String column, String value) {
         mEntry.put(column, value);
+    }
+
+    protected void notifyEditorListener() {
         if (mListener != null) {
             mListener.onRequest(EditorListener.FIELD_CHANGED);
         }
diff --git a/src/com/android/contacts/editor/StructuredNameEditorView.java b/src/com/android/contacts/editor/StructuredNameEditorView.java
index 6911628..af1b3cf 100644
--- a/src/com/android/contacts/editor/StructuredNameEditorView.java
+++ b/src/com/android/contacts/editor/StructuredNameEditorView.java
@@ -77,11 +77,12 @@
         if (!isFieldChanged(column, value)) {
             return;
         }
-        super.onFieldChanged(column, value);
 
+        // First save the new value for the column.
+        saveValue(column, value);
         mChanged = true;
 
-        // Make sure the display name and the structured name are synced
+        // Next make sure the display name and the structured name are synced
         if (hasShortAndLongForms()) {
             if (areOptionalFieldsVisible()) {
                 rebuildFullName(getValues());
@@ -89,6 +90,10 @@
                 rebuildStructuredName(getValues());
             }
         }
+
+        // Then notify the listener, which will rely on the display and structured names to be
+        // synced (in order to provide aggregate suggestions).
+        notifyEditorListener();
     }
 
     @Override
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index d95a1bc..ac4d399 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -18,7 +18,6 @@
 import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.R;
 import com.android.contacts.widget.IndexerListAdapter;
-import com.android.contacts.widget.TextWithHighlightingFactory;
 
 import android.content.Context;
 import android.content.CursorLoader;
@@ -619,8 +618,16 @@
 
     // TODO: move sharable logic (bindXX() methods) to here with extra arguments
 
+    /**
+     * Loads the photo for the quick contact view and assigns the contact uri.
+     * @param photoIdColumn Index of the photo id column
+     * @param photoUriColumn Index of the photo uri column. Optional: Can be -1
+     * @param contactIdColumn Index of the contact id column
+     * @param lookUpKeyColumn Index of the lookup key column
+     */
     protected void bindQuickContact(final ContactListItemView view, int partitionIndex,
-            Cursor cursor, int photoIdColumn, int contactIdColumn, int lookUpKeyColumn) {
+            Cursor cursor, int photoIdColumn, int photoUriColumn, int contactIdColumn,
+            int lookUpKeyColumn) {
         long photoId = 0;
         if (!cursor.isNull(photoIdColumn)) {
             photoId = cursor.getLong(photoIdColumn);
@@ -629,7 +636,15 @@
         QuickContactBadge quickContact = view.getQuickContact();
         quickContact.assignContactUri(
                 getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn));
-        getPhotoLoader().loadPhoto(quickContact, photoId, false, mDarkTheme);
+
+        if (photoId != 0 || photoUriColumn == -1) {
+            getPhotoLoader().loadPhoto(quickContact, photoId, false, mDarkTheme);
+        } else {
+            final String photoUriString = cursor.getString(photoUriColumn);
+            final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
+            getPhotoLoader().loadPhoto(quickContact, photoUri, false, mDarkTheme);
+        }
+
     }
 
     protected Uri getContactUri(int partitionIndex, Cursor cursor,
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index a8caf3b..81fcb84 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -23,7 +23,6 @@
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.ContactCounts;
 import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Directory;
 import android.provider.ContactsContract.SearchSnippetColumns;
 import android.text.TextUtils;
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
index 348abb9..7a32e25 100644
--- a/src/com/android/contacts/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -26,9 +26,7 @@
 import android.net.Uri.Builder;
 import android.preference.PreferenceManager;
 import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
 import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Directory;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.SearchSnippetColumns;
@@ -209,7 +207,8 @@
 
         if (isQuickContactEnabled()) {
             bindQuickContact(view, partition, cursor, ContactQuery.CONTACT_PHOTO_ID,
-                    ContactQuery.CONTACT_ID, ContactQuery.CONTACT_LOOKUP_KEY);
+                    ContactQuery.CONTACT_PHOTO_URI, ContactQuery.CONTACT_ID,
+                    ContactQuery.CONTACT_LOOKUP_KEY);
         } else {
             bindPhoto(view, partition, cursor);
         }
diff --git a/src/com/android/contacts/list/PhoneNumberListAdapter.java b/src/com/android/contacts/list/PhoneNumberListAdapter.java
index 79114eb..960614c 100644
--- a/src/com/android/contacts/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/list/PhoneNumberListAdapter.java
@@ -15,8 +15,6 @@
  */
 package com.android.contacts.list;
 
-import com.android.contacts.R;
-
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.CursorLoader;
@@ -262,7 +260,9 @@
         if (isFirstEntry) {
             bindName(view, cursor);
             if (isQuickContactEnabled()) {
-                bindQuickContact(view, partition, cursor, PhoneQuery.PHONE_PHOTO_ID,
+                // No need for photo uri here, because we can not have directory results. If we
+                // ever do, we need to add photo uri to the query
+                bindQuickContact(view, partition, cursor, PhoneQuery.PHONE_PHOTO_ID, -1,
                         PhoneQuery.PHONE_CONTACT_ID, PhoneQuery.PHONE_LOOKUP_KEY);
             } else {
                 bindPhoto(view, cursor);
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index 22ea884..3cc54f1 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -25,7 +25,6 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.database.Cursor;
 import android.graphics.drawable.Drawable;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
@@ -270,6 +269,7 @@
      * {@link Comparator} to sort by {@link DataKind#weight}.
      */
     private static Comparator<DataKind> sWeightComparator = new Comparator<DataKind>() {
+        @Override
         public int compare(DataKind object1, DataKind object2) {
             return object1.weight - object2.weight;
         }
@@ -460,13 +460,12 @@
     }
 
     /**
-     * Generic method of inflating a given {@link Cursor} into a user-readable
+     * Generic method of inflating a given {@link ContentValues} into a user-readable
      * {@link CharSequence}. For example, an inflater could combine the multiple
      * columns of {@link StructuredPostal} together using a string resource
      * before presenting to the user.
      */
     public interface StringInflater {
-        public CharSequence inflateUsing(Context context, Cursor cursor);
         public CharSequence inflateUsing(Context context, ContentValues values);
     }
 
diff --git a/src/com/android/contacts/model/BaseAccountType.java b/src/com/android/contacts/model/BaseAccountType.java
index 4d82ece..ce74148 100644
--- a/src/com/android/contacts/model/BaseAccountType.java
+++ b/src/com/android/contacts/model/BaseAccountType.java
@@ -17,18 +17,13 @@
 package com.android.contacts.model;
 
 import com.android.contacts.R;
-import com.android.contacts.model.AccountType.DefinitionException;
 import com.android.contacts.util.DateUtils;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.res.Resources;
-import android.database.Cursor;
 import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Event;
@@ -48,6 +43,9 @@
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.util.List;
 import java.util.Locale;
@@ -466,25 +464,7 @@
             mColumnName = columnName;
         }
 
-        public CharSequence inflateUsing(Context context, Cursor cursor) {
-            final int index = mColumnName != null ? cursor.getColumnIndex(mColumnName) : -1;
-            final boolean validString = mStringRes > 0;
-            final boolean validColumn = index != -1;
-
-            final CharSequence stringValue = validString ? context.getText(mStringRes) : null;
-            final CharSequence columnValue = validColumn ? cursor.getString(index) : null;
-
-            if (validString && validColumn) {
-                return String.format(stringValue.toString(), columnValue);
-            } else if (validString) {
-                return stringValue;
-            } else if (validColumn) {
-                return columnValue;
-            } else {
-                return null;
-            }
-        }
-
+        @Override
         public CharSequence inflateUsing(Context context, ContentValues values) {
             final boolean validColumn = values.containsKey(mColumnName);
             final boolean validString = mStringRes > 0;
@@ -541,12 +521,7 @@
             }
         }
 
-        public CharSequence inflateUsing(Context context, Cursor cursor) {
-            final Integer type = cursor.getInt(cursor.getColumnIndex(getTypeColumn()));
-            final String label = cursor.getString(cursor.getColumnIndex(getLabelColumn()));
-            return getTypeLabel(context.getResources(), type, label);
-        }
-
+        @Override
         public CharSequence inflateUsing(Context context, ContentValues values) {
             final Integer type = values.getAsInteger(getTypeColumn());
             final String label = values.getAsString(getLabelColumn());
diff --git a/src/com/android/contacts/quickcontact/Action.java b/src/com/android/contacts/quickcontact/Action.java
index b2d869d..3a283fb 100644
--- a/src/com/android/contacts/quickcontact/Action.java
+++ b/src/com/android/contacts/quickcontact/Action.java
@@ -58,4 +58,7 @@
      * row
      */
     public long getDataId();
+
+    /** Returns the presence of this item or -1 if it was never set */
+    public int getPresence();
 }
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
index 415fa18..e839cda 100644
--- a/src/com/android/contacts/quickcontact/DataAction.java
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -21,14 +21,14 @@
 import com.android.contacts.model.AccountType.EditType;
 import com.android.contacts.model.DataKind;
 import com.android.contacts.util.Constants;
-import com.android.contacts.util.StructuredPostalUtils;
 import com.android.contacts.util.PhoneCapabilityTester;
+import com.android.contacts.util.StructuredPostalUtils;
 
 import android.content.ContentUris;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.database.Cursor;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.net.WebAddress;
@@ -59,6 +59,7 @@
     private Intent mAlternateIntent;
     private int mAlternateIconDescriptionRes;
     private int mAlternateIconRes;
+    private int mPresence = -1;
 
     private Uri mDataUri;
     private long mDataId;
@@ -67,7 +68,8 @@
     /**
      * Create an action from common {@link Data} elements.
      */
-    public DataAction(Context context, String mimeType, DataKind kind, long dataId, Cursor cursor) {
+    public DataAction(Context context, String mimeType, DataKind kind, long dataId,
+            ContentValues entryValues) {
         mContext = context;
         mKind = kind;
         mMimeType = mimeType;
@@ -75,9 +77,8 @@
         // Determine type for subtitle
         mSubtitle = "";
         if (kind.typeColumn != null) {
-            final int typeColumnIndex = cursor.getColumnIndex(kind.typeColumn);
-            if (typeColumnIndex != -1) {
-                final int typeValue = cursor.getInt(typeColumnIndex);
+            if (entryValues.containsKey(kind.typeColumn)) {
+                final int typeValue = entryValues.getAsInteger(kind.typeColumn);
 
                 // get type string
                 for (EditType type : kind.typeList) {
@@ -87,8 +88,7 @@
                             mSubtitle = context.getString(type.labelRes);
                         } else {
                             // Custom type. Read it from the database
-                            mSubtitle = cursor.getString(cursor.getColumnIndexOrThrow(
-                                    type.customColumn));
+                            mSubtitle = entryValues.getAsString(type.customColumn);
                         }
                         break;
                     }
@@ -96,12 +96,11 @@
             }
         }
 
-        if (getAsInt(cursor, Data.IS_SUPER_PRIMARY) != 0) {
-            mIsPrimary = true;
-        }
+        final Integer superPrimary = entryValues.getAsInteger(Data.IS_SUPER_PRIMARY);
+        mIsPrimary = superPrimary != null && superPrimary != 0;
 
         if (mKind.actionBody != null) {
-            mBody = mKind.actionBody.inflateUsing(context, cursor);
+            mBody = mKind.actionBody.inflateUsing(context, entryValues);
         }
 
         mDataId = dataId;
@@ -113,7 +112,7 @@
         // Handle well-known MIME-types with special care
         if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
             if (PhoneCapabilityTester.isPhone(mContext)) {
-                final String number = getAsString(cursor, Phone.NUMBER);
+                final String number = entryValues.getAsString(Phone.NUMBER);
                 if (!TextUtils.isEmpty(number)) {
 
                     final Intent phoneIntent = hasPhone ? ContactsUtils.getCallIntent(number)
@@ -136,7 +135,7 @@
             }
         } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) {
             if (PhoneCapabilityTester.isSipPhone(mContext)) {
-                final String address = getAsString(cursor, SipAddress.SIP_ADDRESS);
+                final String address = entryValues.getAsString(SipAddress.SIP_ADDRESS);
                 if (!TextUtils.isEmpty(address)) {
                     final Uri callUri = Uri.fromParts(Constants.SCHEME_SIP, address, null);
                     mIntent = ContactsUtils.getCallIntent(callUri);
@@ -149,14 +148,14 @@
                 }
             }
         } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
-            final String address = getAsString(cursor, Email.DATA);
+            final String address = entryValues.getAsString(Email.DATA);
             if (!TextUtils.isEmpty(address)) {
                 final Uri mailUri = Uri.fromParts(Constants.SCHEME_MAILTO, address, null);
                 mIntent = new Intent(Intent.ACTION_SENDTO, mailUri);
             }
 
         } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
-            final String url = getAsString(cursor, Website.URL);
+            final String url = entryValues.getAsString(Website.URL);
             if (!TextUtils.isEmpty(url)) {
                 WebAddress webAddress = new WebAddress(url);
                 mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webAddress.toString()));
@@ -164,21 +163,21 @@
 
         } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
             final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(
-                    getAsString(cursor, Data.MIMETYPE));
-            if (isEmail || isProtocolValid(cursor)) {
+                    entryValues.getAsString(Data.MIMETYPE));
+            if (isEmail || isProtocolValid(entryValues)) {
                 final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK :
-                        getAsInt(cursor, Im.PROTOCOL);
+                        entryValues.getAsInteger(Im.PROTOCOL);
 
                 if (isEmail) {
                     // Use Google Talk string when using Email, and clear data
                     // Uri so we don't try saving Email as primary.
-                    mSubtitle = context.getText(R.string.chat_gtalk);
+                    mSubtitle = Im.getProtocolLabel(context.getResources(), Im.PROTOCOL_GOOGLE_TALK,
+                            null);
                     mDataUri = null;
                 }
 
-                String host = getAsString(cursor, Im.CUSTOM_PROTOCOL);
-                String data = getAsString(cursor,
-                        isEmail ? Email.DATA : Im.DATA);
+                String host = entryValues.getAsString(Im.CUSTOM_PROTOCOL);
+                String data = entryValues.getAsString(isEmail ? Email.DATA : Im.DATA);
                 if (protocol != Im.PROTOCOL_CUSTOM) {
                     // Try bringing in a well-known host for specific protocols
                     host = ContactsUtils.lookupProviderNameFromId(protocol);
@@ -192,7 +191,8 @@
 
                     // If the address is also available for a video chat, we'll show the capability
                     // as a secondary action.
-                    final int chatCapability = getAsInt(cursor, Data.CHAT_CAPABILITY);
+                    final Integer chatCapabilityObj = entryValues.getAsInteger(Im.CHAT_CAPABILITY);
+                    final int chatCapability = chatCapabilityObj == null ? 0 : chatCapabilityObj;
                     final boolean isVideoChatCapable =
                             (chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0;
                     final boolean isAudioChatCapable =
@@ -211,7 +211,8 @@
                 }
             }
         } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
-            final String postalAddress = getAsString(cursor, StructuredPostal.FORMATTED_ADDRESS);
+            final String postalAddress =
+                    entryValues.getAsString(StructuredPostal.FORMATTED_ADDRESS);
             if (!TextUtils.isEmpty(postalAddress)) {
                 mIntent = StructuredPostalUtils.getViewPostalAddressIntent(postalAddress);
             }
@@ -227,25 +228,22 @@
         mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
     }
 
-    /** Read {@link String} from the given {@link Cursor}. */
-    private static String getAsString(Cursor cursor, String columnName) {
-        final int index = cursor.getColumnIndex(columnName);
-        return cursor.getString(index);
+    @Override
+    public int getPresence() {
+        return mPresence;
     }
 
-    /** Read {@link Integer} from the given {@link Cursor}. */
-    private static int getAsInt(Cursor cursor, String columnName) {
-        final int index = cursor.getColumnIndex(columnName);
-        return cursor.getInt(index);
+    public void setPresence(int presence) {
+        mPresence = presence;
     }
 
-    private boolean isProtocolValid(Cursor cursor) {
-        final int columnIndex = cursor.getColumnIndex(Im.PROTOCOL);
-        if (cursor.isNull(columnIndex)) {
+    private boolean isProtocolValid(ContentValues entryValues) {
+        final String protocol = entryValues.getAsString(Im.PROTOCOL);
+        if (protocol == null) {
             return false;
         }
         try {
-            Integer.valueOf(cursor.getString(columnIndex));
+            Integer.valueOf(protocol);
         } catch (NumberFormatException e) {
             return false;
         }
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 2c62fe4..b603e42 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -17,44 +17,42 @@
 package com.android.contacts.quickcontact;
 
 import com.android.contacts.Collapser;
+import com.android.contacts.ContactLoader;
 import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.R;
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.model.DataKind;
 import com.android.contacts.util.DataStatus;
-import com.android.contacts.util.NotifyingAsyncQueryHandler;
-import com.android.contacts.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
+import android.app.LoaderManager.LoaderCallbacks;
 import android.content.ActivityNotFoundException;
 import android.content.ContentUris;
+import android.content.ContentValues;
 import android.content.Context;
+import android.content.Entity;
+import android.content.Entity.NamedContentValues;
 import android.content.Intent;
+import android.content.Loader;
 import android.content.pm.PackageManager;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Im;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.DisplayPhoto;
 import android.provider.ContactsContract.QuickContact;
 import android.provider.ContactsContract.RawContacts;
 import android.support.v13.app.FragmentPagerAdapter;
@@ -74,7 +72,6 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
-import java.io.IOException;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -96,8 +93,6 @@
     @SuppressWarnings("deprecation")
     private static final String LEGACY_AUTHORITY = android.provider.Contacts.AUTHORITY;
 
-    private NotifyingAsyncQueryHandler mHandler;
-
     private Uri mLookupUri;
     private String[] mExcludeMimes;
     private List<String> mSortedActionMimeTypes = Lists.newArrayList();
@@ -147,8 +142,8 @@
     private static final List<String> TRAILING_MIMETYPES = Lists.newArrayList(
             StructuredPostal.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE);
 
-    /** Id for the background handler that loads the data */
-    private static final int HANDLER_ID_DATA = 1;
+    /** Id for the background loader */
+    private static final int LOADER_ID = 0;
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -190,8 +185,6 @@
         mListPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
         mListPager.setOnPageChangeListener(new PageChangeListener());
 
-        mHandler = new NotifyingAsyncQueryHandler(this, mQueryListener);
-
         show();
     }
 
@@ -225,16 +218,7 @@
         mPhotoContainer = findViewById(R.id.photo_container);
         setHeaderNameText(R.id.name, R.string.missing_name);
 
-        // Start background query for data, but only select photo rows when they
-        // directly match the super-primary PHOTO_ID.
-        final Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
-        mHandler.cancelOperation(HANDLER_ID_DATA);
-
-        // Select all data items of the contact (except for photos, where we only select the display
-        // photo)
-        mHandler.startQuery(HANDLER_ID_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
-                + "!=? OR (" + Data.MIMETYPE + "=? AND " + Data._ID + "=" + Contacts.PHOTO_ID
-                + ")", new String[] { Photo.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE }, null);
+        getLoaderManager().initLoader(LOADER_ID, null, mLoaderCallbacks);
     }
 
     private boolean handleOutsideTouch() {
@@ -248,7 +232,7 @@
 
     private void hide(boolean withAnimation) {
         // cancel any pending queries
-        mHandler.cancelOperation(HANDLER_ID_DATA);
+        getLoaderManager().destroyLoader(LOADER_ID);
 
         if (withAnimation) {
             mFloatingLayout.hideChild(new Runnable() {
@@ -268,47 +252,6 @@
         hide(true);
     }
 
-    private final AsyncQueryListener mQueryListener = new AsyncQueryListener() {
-        @Override
-        public synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
-            try {
-                if (isFinishing()) {
-                    hide(false);
-                    return;
-                } else if (cursor == null || cursor.getCount() == 0) {
-                    Toast.makeText(QuickContactActivity.this, R.string.invalidContactMessage,
-                            Toast.LENGTH_LONG).show();
-                    hide(false);
-                    return;
-                }
-
-                bindData(cursor);
-
-                if (TRACE_LAUNCH) {
-                    android.os.Debug.stopMethodTracing();
-                }
-
-                // Data bound and ready, pull curtain to show. Put this on the Handler to ensure
-                // that the layout passes are completed
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mFloatingLayout.showChild(new Runnable() {
-                            @Override
-                            public void run() {
-                                mHasFinishedAnimatingIn = true;
-                            }
-                        });
-                    }
-                });
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-        }
-    };
-
     /** Assign this string to the view if it is not empty. */
     private void setHeaderNameText(int id, int resId) {
         setHeaderNameText(id, getText(resId));
@@ -325,35 +268,6 @@
     }
 
     /**
-     * Assign this string to the view (if found in {@link #mPhotoContainer}), or hiding this view
-     * if there is no string.
-     */
-    private void setHeaderText(int id, int resId) {
-        setHeaderText(id, getText(resId));
-    }
-
-    /**
-     * Assign this string to the view (if found in {@link #mPhotoContainer}), or hiding this view
-     * if there is no string.
-     */
-    private void setHeaderText(int id, CharSequence value) {
-        final View view = mPhotoContainer.findViewById(id);
-        if (view instanceof TextView) {
-            ((TextView)view).setText(value);
-            view.setVisibility(TextUtils.isEmpty(value) ? View.GONE : View.VISIBLE);
-        }
-    }
-
-    /** Assign this image to the view, if found in {@link #mPhotoContainer}. */
-    private void setHeaderImage(int id, Drawable drawable) {
-        final View view = mPhotoContainer.findViewById(id);
-        if (view instanceof ImageView) {
-            ((ImageView)view).setImageDrawable(drawable);
-            view.setVisibility(drawable == null ? View.GONE : View.VISIBLE);
-        }
-    }
-
-    /**
      * Check if the given MIME-type appears in the list of excluded MIME-types
      * that the most-recent caller requested.
      */
@@ -368,9 +282,9 @@
     }
 
     /**
-     * Handle the result from the {@link #TOKEN_DATA} query.
+     * Handle the result from the ContactLoader
      */
-    private void bindData(Cursor cursor) {
+    private void bindData(ContactLoader.Result data) {
         final ResolveCache cache = ResolveCache.getInstance(this);
         final Context context = this;
 
@@ -379,90 +293,63 @@
 
         mDefaultsMap.clear();
 
-        final DataStatus status = new DataStatus();
         final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
                 context.getApplicationContext());
         final ImageView photoView = (ImageView) mPhotoContainer.findViewById(R.id.photo);
+        final byte[] photo = data.getPhotoBinaryData();
+        if (photo != null) {
+            photoView.setImageBitmap(BitmapFactory.decodeByteArray(photo, 0, photo.length));
+        } else {
+            photoView.setImageResource(
+                    ContactPhotoManager.getDefaultAvatarResId(true, false));
+        }
 
-        Bitmap photoBitmap = null;
-        while (cursor.moveToNext()) {
-            // Handle any social status updates from this row
-            status.possibleUpdate(cursor);
+        for (Entity entity : data.getEntities()) {
+            final ContentValues entityValues = entity.getEntityValues();
+            final String accountType = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
+            final String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
+            for (NamedContentValues subValue : entity.getSubValues()) {
+                final ContentValues entryValues = subValue.values;
+                final String mimeType = entryValues.getAsString(Data.MIMETYPE);
 
-            final String mimeType = cursor.getString(DataQuery.MIMETYPE);
+                // Skip this data item if MIME-type excluded
+                if (isMimeExcluded(mimeType)) continue;
 
-            // Skip this data item if MIME-type excluded
-            if (isMimeExcluded(mimeType)) continue;
+                final long dataId = entryValues.getAsLong(Data._ID);
+                final Integer primary = entryValues.getAsInteger(Data.IS_PRIMARY);
+                final boolean isPrimary = primary != null && primary != 0;
+                final Integer superPrimary = entryValues.getAsInteger(Data.IS_SUPER_PRIMARY);
+                final boolean isSuperPrimary = superPrimary != null && superPrimary != 0;
 
-            final long dataId = cursor.getLong(DataQuery._ID);
-            final String accountType = cursor.getString(DataQuery.ACCOUNT_TYPE);
-            final String dataSet = cursor.getString(DataQuery.DATA_SET);
-            final boolean isPrimary = cursor.getInt(DataQuery.IS_PRIMARY) != 0;
-            final boolean isSuperPrimary = cursor.getInt(DataQuery.IS_SUPER_PRIMARY) != 0;
+                final DataKind kind =
+                        accountTypes.getKindOrFallback(accountType, dataSet, mimeType);
 
-            // Handle photos included as data row
-            if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                final int displayPhotoColumnIndex = cursor.getColumnIndex(Photo.PHOTO_FILE_ID);
-                final boolean hasDisplayPhoto = !cursor.isNull(displayPhotoColumnIndex);
-                if (hasDisplayPhoto) {
-                    final long displayPhotoId = cursor.getLong(displayPhotoColumnIndex);
-                    final Uri displayPhotoUri = ContentUris.withAppendedId(
-                            DisplayPhoto.CONTENT_URI, displayPhotoId);
-                    // Fetch and JPEG uncompress on the background thread
-                    new AsyncTask<Void, Void, Bitmap>() {
-                        @Override
-                        protected Bitmap doInBackground(Void... params) {
-                            try {
-                                AssetFileDescriptor fd = getContentResolver()
-                                        .openAssetFileDescriptor(displayPhotoUri, "r");
-                                return BitmapFactory.decodeStream(fd.createInputStream());
-                            } catch (IOException e) {
-                                Log.e(TAG, "Error getting display photo. Ignoring, as we already " +
-                                        "have the thumbnail", e);
-                                return null;
-                            }
+                if (kind != null) {
+                    // Build an action for this data entry, find a mapping to a UI
+                    // element, build its summary from the cursor, and collect it
+                    // along with all others of this MIME-type.
+                    final Action action = new DataAction(context, mimeType, kind, dataId,
+                            entryValues);
+                    final boolean wasAdded = considerAdd(action, cache);
+                    if (wasAdded) {
+                        // Remember the default
+                        if (isSuperPrimary || (isPrimary && (mDefaultsMap.get(mimeType) == null))) {
+                            mDefaultsMap.put(mimeType, action);
                         }
-
-                        @Override
-                        protected void onPostExecute(Bitmap result) {
-                            if (result == null) return;
-                            photoView.setImageBitmap(result);
-                        }
-                    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
-                }
-                final int photoColumnIndex = cursor.getColumnIndex(Photo.PHOTO);
-                final byte[] photoBlob = cursor.getBlob(photoColumnIndex);
-                if (photoBlob != null) {
-                    photoBitmap = BitmapFactory.decodeByteArray(photoBlob, 0, photoBlob.length);
-                }
-                continue;
-            }
-
-            final DataKind kind = accountTypes.getKindOrFallback(accountType, dataSet, mimeType);
-
-            if (kind != null) {
-                // Build an action for this data entry, find a mapping to a UI
-                // element, build its summary from the cursor, and collect it
-                // along with all others of this MIME-type.
-                final Action action = new DataAction(context, mimeType, kind, dataId, cursor);
-                final boolean wasAdded = considerAdd(action, cache);
-                if (wasAdded) {
-                    // Remember the default
-                    if (isSuperPrimary || (isPrimary && (mDefaultsMap.get(mimeType) == null))) {
-                        mDefaultsMap.put(mimeType, action);
                     }
                 }
-            }
 
-            // Handle Email rows with presence data as Im entry
-            final boolean hasPresence = !cursor.isNull(DataQuery.PRESENCE);
-            if (hasPresence && Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                final DataKind imKind = accountTypes.getKindOrFallback(accountType, dataSet,
-                        Im.CONTENT_ITEM_TYPE);
-                if (imKind != null) {
-                    final DataAction action = new DataAction(context, Im.CONTENT_ITEM_TYPE, imKind,
-                            dataId, cursor);
-                    considerAdd(action, cache);
+                // Handle Email rows with presence data as Im entry
+                final DataStatus status = data.getStatuses().get(dataId);
+                if (status != null && Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final DataKind imKind = accountTypes.getKindOrFallback(accountType, dataSet,
+                            Im.CONTENT_ITEM_TYPE);
+                    if (imKind != null) {
+                        final DataAction action = new DataAction(context, Im.CONTENT_ITEM_TYPE,
+                                imKind, dataId, entryValues);
+                        action.setPresence(status.getPresence());
+                        considerAdd(action, cache);
+                    }
                 }
             }
         }
@@ -472,20 +359,7 @@
             Collapser.collapseList(actionChildren);
         }
 
-        if (cursor.moveToLast()) {
-            // Read contact name from last data row
-            final String name = cursor.getString(DataQuery.DISPLAY_NAME);
-            setHeaderNameText(R.id.name, name);
-        }
-
-        if (photoView != null) {
-            // Place photo when discovered in data, otherwise show generic avatar
-            if (photoBitmap != null) {
-                photoView.setImageBitmap(photoBitmap);
-            } else {
-                photoView.setImageResource(ContactPhotoManager.getDefaultAvatarResId(true, false));
-            }
-        }
+        setHeaderNameText(R.id.name, data.getDisplayName());
 
         // All the mime-types to add.
         final Set<String> containedTypes = new HashSet<String>(mActions.keySet());
@@ -515,6 +389,7 @@
         }
 
         // Add buttons for each mimetype
+        mTrack.removeAllViews();
         for (String mimeType : mSortedActionMimeTypes) {
             final View actionView = inflateAction(mimeType, cache, mTrack);
             mTrack.addView(actionView);
@@ -574,6 +449,61 @@
         listFragment.setListener(mListFragmentListener);
     }
 
+    private LoaderCallbacks<ContactLoader.Result> mLoaderCallbacks =
+            new LoaderCallbacks<ContactLoader.Result>() {
+        @Override
+        public void onLoaderReset(Loader<ContactLoader.Result> loader) {
+        }
+
+        @Override
+        public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
+            if (isFinishing()) {
+                hide(false);
+                return;
+            }
+            if (data.isError()) {
+                // This shouldn't ever happen, so throw an exception. The {@link ContactLoader}
+                // should log the actual exception.
+                throw new IllegalStateException("Failed to load contact", data.getException());
+            }
+            if (data.isNotFound()) {
+                Log.i(TAG, "No contact found: " + ((ContactLoader)loader).getLookupUri());
+                Toast.makeText(QuickContactActivity.this, R.string.invalidContactMessage,
+                        Toast.LENGTH_LONG).show();
+                hide(false);
+                return;
+            }
+
+            bindData(data);
+
+            if (TRACE_LAUNCH) {
+                android.os.Debug.stopMethodTracing();
+            }
+
+            // Data bound and ready, pull curtain to show. Put this on the Handler to ensure
+            // that the layout passes are completed
+            new Handler().post(new Runnable() {
+                @Override
+                public void run() {
+                    mFloatingLayout.showChild(new Runnable() {
+                        @Override
+                        public void run() {
+                            mHasFinishedAnimatingIn = true;
+                        }
+                    });
+                }
+            });
+        }
+
+        @Override
+        public Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
+            if (mLookupUri == null) {
+                Log.wtf(TAG, "Lookup uri wasn't initialized. Loader was started too early");
+            }
+            return new ContactLoader(getApplicationContext(), mLookupUri);
+        }
+    };
+
     /** A type (e.g. Call/Addresses was clicked) */
     private final OnClickListener mTypeViewClickListener = new OnClickListener() {
         @Override
@@ -653,53 +583,4 @@
             new Handler().post(startAppRunnable);
         }
     };
-
-    private interface DataQuery {
-        final String[] PROJECTION = new String[] {
-                Data._ID,
-
-                RawContacts.ACCOUNT_TYPE,
-                RawContacts.DATA_SET,
-                Contacts.STARRED,
-                Contacts.DISPLAY_NAME,
-
-                Data.STATUS,
-                Data.STATUS_RES_PACKAGE,
-                Data.STATUS_ICON,
-                Data.STATUS_LABEL,
-                Data.STATUS_TIMESTAMP,
-                Data.PRESENCE,
-                Data.CHAT_CAPABILITY,
-
-                Data.RES_PACKAGE,
-                Data.MIMETYPE,
-                Data.IS_PRIMARY,
-                Data.IS_SUPER_PRIMARY,
-                Data.RAW_CONTACT_ID,
-
-                Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5,
-                Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10, Data.DATA11,
-                Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15,
-        };
-
-        final int _ID = 0;
-
-        final int ACCOUNT_TYPE = 1;
-        final int DATA_SET = 2;
-        final int STARRED = 3;
-        final int DISPLAY_NAME = 4;
-
-        final int STATUS = 5;
-        final int STATUS_RES_PACKAGE = 6;
-        final int STATUS_ICON = 7;
-        final int STATUS_LABEL = 8;
-        final int STATUS_TIMESTAMP = 9;
-        final int PRESENCE = 10;
-        final int CHAT_CAPABILITY = 11;
-
-        final int RES_PACKAGE = 12;
-        final int MIMETYPE = 13;
-        final int IS_PRIMARY = 14;
-        final int IS_SUPER_PRIMARY = 15;
-    }
 }
diff --git a/src/com/android/contacts/quickcontact/QuickContactListFragment.java b/src/com/android/contacts/quickcontact/QuickContactListFragment.java
index c6187e9..da8fef8 100644
--- a/src/com/android/contacts/quickcontact/QuickContactListFragment.java
+++ b/src/com/android/contacts/quickcontact/QuickContactListFragment.java
@@ -16,14 +16,14 @@
 
 package com.android.contacts.quickcontact;
 
+import com.android.contacts.ContactPresenceIconUtil;
 import com.android.contacts.R;
 
 import android.app.Fragment;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -111,6 +111,8 @@
                 final ImageView alternateActionButton = (ImageView) resultView.findViewById(
                         R.id.secondary_action_button);
                 final View alternateActionDivider = resultView.findViewById(R.id.vertical_divider);
+                final ImageView presenceIconView =
+                        (ImageView) resultView.findViewById(R.id.presence_icon);
 
                 actionsContainer.setOnClickListener(mPrimaryActionClickListener);
                 actionsContainer.setTag(action);
@@ -143,6 +145,14 @@
                         text2.setVisibility(View.VISIBLE);
                     }
                 }
+                final Drawable presenceIcon = ContactPresenceIconUtil.getPresenceIcon(
+                        getActivity(), action.getPresence());
+                if (presenceIcon != null) {
+                    presenceIconView.setImageDrawable(presenceIcon);
+                    presenceIconView.setVisibility(View.VISIBLE);
+                } else {
+                    presenceIconView.setVisibility(View.GONE);
+                }
                 return resultView;
             }
         });
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
index 19ebde3..c3cbc10 100644
--- a/tests/res/values/donottranslate_strings.xml
+++ b/tests/res/values/donottranslate_strings.xml
@@ -23,7 +23,7 @@
         <!-- List modes -->
         <item>LIST_DEFAULT</item>
         <item>LIST_ALL_CONTACTS_ACTION</item>
-        <item>LIST_CONTACTS_WITH_PHONES_ACTION</item>
+        <item>LIST_CONTACTS_WITH_PHONES_ACTION (deprecated)</item>
         <item>LIST_STARRED_ACTION</item>
         <item>LIST_FREQUENT_ACTION</item>
         <item>LIST_STREQUENT_ACTION</item>