Ignore read-only raw contacts in picker modal pt. 1
am: a172180513

Change-Id: Id87e9b8fc403742ec39d92c3835f243375e6757a
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b1e4762..41df84f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -726,7 +726,7 @@
     <string name="contact_editor_title_read_only_contact">View only</string>
 
     <!-- Dialog title when the user is selecting a raw contact to edit.  [CHAR LIMIT=128] -->
-    <string name="contact_editor_pick_raw_contact_dialog_title">Choose linked contact</string>
+    <string name="contact_editor_pick_raw_contact_to_edit_dialog_title">Choose contact to edit</string>
 
     <!-- Button label to prompt the user to add an account (when there are 0 existing accounts on the device) [CHAR LIMIT=30] -->
     <string name="add_account">Add account</string>
diff --git a/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java b/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java
index 69dccb5..3d47b8f 100644
--- a/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java
@@ -6,7 +6,6 @@
 import android.content.ContentUris;
 import android.content.Intent;
 import android.content.Loader;
-import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract;
@@ -17,13 +16,14 @@
 import com.android.contacts.R;
 import com.android.contacts.common.activity.RequestPermissionsActivity;
 import com.android.contacts.common.model.AccountTypeManager;
-import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
 import com.android.contacts.editor.ContactEditorFragment;
 import com.android.contacts.editor.EditorIntents;
 import com.android.contacts.editor.PickRawContactDialogFragment;
 import com.android.contacts.editor.PickRawContactLoader;
+import com.android.contacts.editor.PickRawContactLoader.RawContactsMetadata;
+import com.android.contactsbind.FeedbackHelper;
 
 /**
  * Transparent springboard activity that hosts a dialog to select a raw contact to edit.
@@ -36,36 +36,36 @@
     private static final String TAG_RAW_CONTACTS_DIALOG = "rawContactsDialog";
     private static final int LOADER_RAW_CONTACTS = 1;
 
+    public static final String EXTRA_SHOW_READ_ONLY = "showReadOnly";
+
     private Uri mUri;
-    private Cursor mCursor;
+    private RawContactsMetadata mResult;
     private MaterialPalette mMaterialPalette;
-    private boolean mIsUserProfile;
     private boolean mHasWritableAccount;
     private int mWritableAccountPosition;
 
     /**
      * The contact data loader listener.
      */
-    protected final LoaderManager.LoaderCallbacks<Cursor> mRawContactLoaderListener =
-            new LoaderManager.LoaderCallbacks<Cursor>() {
+    protected final LoaderManager.LoaderCallbacks<RawContactsMetadata> mRawContactLoaderListener =
+            new LoaderManager.LoaderCallbacks<RawContactsMetadata>() {
 
                 @Override
-                public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+                public Loader<RawContactsMetadata> onCreateLoader(int id, Bundle args) {
                     return new PickRawContactLoader(ContactEditorSpringBoardActivity.this, mUri);
                 }
 
                 @Override
-                public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
-                    if (cursor == null) {
-                        Toast.makeText(ContactEditorSpringBoardActivity.this,
-                                R.string.editor_failed_to_load, Toast.LENGTH_SHORT).show();
-                        finish();
+                public void onLoadFinished(Loader<RawContactsMetadata> loader,
+                        RawContactsMetadata result) {
+                    if (result == null) {
+                        toastErrorAndFinish();
                         return;
                     }
-                    mCursor = cursor;
-                    mIsUserProfile = ((PickRawContactLoader) loader).isUserProfile();
+                    mResult = result;
+                    maybeTrimReadOnly();
                     setHasWritableAccount();
-                    if (mCursor.getCount() > 1 && mHasWritableAccount) {
+                    if (mResult.rawContacts.size() > 1 && mHasWritableAccount) {
                         showDialog();
                     } else {
                         loadEditor();
@@ -73,8 +73,7 @@
                 }
 
                 @Override
-                public void onLoaderReset(Loader<Cursor> loader) {
-                    mCursor = null;
+                public void onLoaderReset(Loader<RawContactsMetadata> loader) {
                 }
             };
 
@@ -111,6 +110,11 @@
                 RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
             final long rawContactId = ContentUris.parseId(mUri);
             startEditorAndForwardExtras(getIntentForRawContact(rawContactId));
+        } else if (android.provider.Contacts.AUTHORITY.equals(authority)) {
+            // Fail if given a legacy URI.
+            FeedbackHelper.sendFeedback(this, TAG,
+                    "Legacy Uri was passed to editor.", new IllegalArgumentException());
+            toastErrorAndFinish();
         } else {
             getLoaderManager().initLoader(LOADER_RAW_CONTACTS, null, mRawContactLoaderListener);
         }
@@ -122,6 +126,19 @@
     }
 
     /**
+     * If not configured to show read only raw contact, trim them from the result.
+     */
+    private void maybeTrimReadOnly() {
+        final boolean showReadOnly = getIntent().getBooleanExtra(EXTRA_SHOW_READ_ONLY, false);
+        mResult.showReadOnly = showReadOnly;
+
+        if (showReadOnly) {
+            return;
+        }
+        mResult.trimReadOnly(AccountTypeManager.getInstance(this));
+    }
+
+    /**
      * Start the dialog to pick the raw contact to edit.
      */
     private void showDialog() {
@@ -130,8 +147,6 @@
                 fm.findFragmentByTag(TAG_RAW_CONTACTS_DIALOG);
         if (oldFragment != null && oldFragment.getDialog() != null
                 && oldFragment.getDialog().isShowing()) {
-            // Just update the cursor without reshowing the dialog.
-            oldFragment.setCursor(mCursor);
             return;
         }
         final FragmentTransaction ft = fm.beginTransaction();
@@ -139,7 +154,7 @@
             ft.remove(oldFragment);
         }
         final PickRawContactDialogFragment newFragment = PickRawContactDialogFragment.getInstance(
-                 mCursor, mIsUserProfile);
+                 mResult);
         ft.add(newFragment, TAG_RAW_CONTACTS_DIALOG);
         // commitAllowingStateLoss is safe in this activity because the fragment entirely depends
         // on the result of the loader. Even if we lose the fragment because the activity was
@@ -156,18 +171,13 @@
     private void loadEditor() {
         final Intent intent;
         if (mHasWritableAccount) {
-            mCursor.moveToPosition(mWritableAccountPosition);
-            final long rawContactId = mCursor.getLong(PickRawContactLoader.RAW_CONTACT_ID);
-            intent = getIntentForRawContact(rawContactId);
+            intent = getIntentForRawContact(mResult.rawContacts.get(mWritableAccountPosition).id);
         } else {
             // If the contact has only read-only raw contacts, we'll want to let the editor create
             // the writable raw contact for it.
             intent = EditorIntents.createEditContactIntent(this, mUri, mMaterialPalette, -1);
             intent.setClass(this, ContactEditorActivity.class);
         }
-        // Destroy the loader to prevent multiple onLoadFinished calls in case CP2 is updating in
-        // the background.
-        getLoaderManager().destroyLoader(LOADER_RAW_CONTACTS);
         startEditorAndForwardExtras(intent);
     }
 
@@ -175,18 +185,9 @@
      * Determines if this contact has a writable account.
      */
     private void setHasWritableAccount() {
-        mCursor.moveToPosition(-1);
-        while (mCursor.moveToNext()) {
-            final String accountType = mCursor.getString(PickRawContactLoader.ACCOUNT_TYPE);
-            final String dataSet = mCursor.getString(PickRawContactLoader.DATA_SET);
-            final AccountType account = AccountTypeManager.getInstance(this)
-                    .getAccountType(accountType, dataSet);
-            if (account.areContactsWritable()) {
-                mHasWritableAccount = true;
-                mWritableAccountPosition = mCursor.getPosition();
-                return;
-            }
-        }
+        mWritableAccountPosition = mResult.getIndexOfFirstWritableAccount(
+                AccountTypeManager.getInstance(this));
+        mHasWritableAccount = mWritableAccountPosition != -1;
     }
 
     /**
@@ -211,4 +212,11 @@
         }
         ImplicitIntentsUtil.startActivityInApp(this, intent);
     }
+
+    private void toastErrorAndFinish() {
+        Toast.makeText(ContactEditorSpringBoardActivity.this,
+                R.string.editor_failed_to_load, Toast.LENGTH_SHORT).show();
+        setResult(RESULT_CANCELED, null);
+        finish();
+    }
 }
diff --git a/src/com/android/contacts/editor/PickRawContactDialogFragment.java b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
index 6b0051e..aa3e015 100644
--- a/src/com/android/contacts/editor/PickRawContactDialogFragment.java
+++ b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
@@ -6,7 +6,6 @@
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract.RawContacts;
@@ -14,8 +13,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.CursorAdapter;
+import android.widget.BaseAdapter;
 import android.widget.ImageView;
+import android.widget.ListAdapter;
 import android.widget.TextView;
 
 import com.android.contacts.R;
@@ -27,6 +27,8 @@
 import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.model.account.GoogleAccountType;
 import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.contacts.editor.PickRawContactLoader.RawContact;
+import com.android.contacts.editor.PickRawContactLoader.RawContactsMetadata;
 
 /**
  * Should only be started from an activity that implements {@link PickRawContactListener}.
@@ -34,7 +36,7 @@
  * for the chosen raw contact.
  */
 public class PickRawContactDialogFragment extends DialogFragment {
-    private static final String ARGS_IS_USER_PROFILE = "isUserProfile";
+    private static final String ARGS_RAW_CONTACTS_METADATA = "rawContactsMetadata";
 
     public interface PickRawContactListener {
         void onPickRawContact(long rawContactId);
@@ -43,69 +45,93 @@
     /**
      * Used to list the account info for the given raw contacts list.
      */
-    private final class RawContactAccountListAdapter extends CursorAdapter {
+    private final class RawContactAccountListAdapter extends BaseAdapter {
         private final LayoutInflater mInflater;
         private final Context mContext;
+        private final RawContactsMetadata mRawContactsMetadata;
         private final AccountDisplayInfoFactory mAccountDisplayInfoFactory;
         private final AccountTypeManager mAccountTypeManager;
         private final ContactsPreferences mPreferences;
 
-        public RawContactAccountListAdapter(Context context, Cursor cursor) {
-            super(context, cursor, 0);
+        public RawContactAccountListAdapter(Context context,
+                RawContactsMetadata rawContactsMetadata) {
             mContext = context;
             mInflater = LayoutInflater.from(context);
             mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(context);
             mAccountTypeManager = AccountTypeManager.getInstance(context);
             mPreferences = new ContactsPreferences(context);
+            mRawContactsMetadata = rawContactsMetadata;
         }
 
         @Override
-        public void bindView(View view, Context context, Cursor cursor) {
-            final long rawContactId = cursor.getLong(PickRawContactLoader.RAW_CONTACT_ID);
-            final String accountName = cursor.getString(PickRawContactLoader.ACCOUNT_NAME);
-            final String accountType = cursor.getString(PickRawContactLoader.ACCOUNT_TYPE);
-            final String dataSet = cursor.getString(PickRawContactLoader.DATA_SET);
-            final AccountType account = mAccountTypeManager.getAccountType(accountType, dataSet);
+        public int getCount() {
+            return mRawContactsMetadata.rawContacts.size();
+        }
 
-            final int displayNameColumn =
+        @Override
+        public Object getItem(int position) {
+            return mRawContactsMetadata.rawContacts.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return mRawContactsMetadata.rawContacts.get(position).id;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final View view;
+            final RawContactViewHolder holder;
+            if (convertView == null) {
+                view = mInflater.inflate(R.layout.raw_contact_list_item, parent, false);
+                holder = new RawContactViewHolder();
+                holder.displayName = (TextView) view.findViewById(R.id.display_name);
+                holder.accountName = (TextView) view.findViewById(R.id.account_name);
+                holder.accountIcon = (ImageView) view.findViewById(R.id.account_icon);
+                holder.photo = (ImageView) view.findViewById(R.id.photo);
+                view.setTag(holder);
+            } else {
+                view = convertView;
+                holder = (RawContactViewHolder) view.getTag();
+            }
+            final RawContact rawContact = mRawContactsMetadata.rawContacts.get(position);
+            final AccountType account = mAccountTypeManager.getAccountType(rawContact.accountType,
+                    rawContact.accountDataSet);
+
+            String displayName =
                     mPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY
-                            ? PickRawContactLoader.DISPLAY_NAME_PRIMARY
-                            : PickRawContactLoader.DISPLAY_NAME_ALTERNATIVE;
-
-            String displayName = cursor.getString(displayNameColumn);
+                    ? rawContact.displayName : rawContact.displayNameAlt;
 
             if (TextUtils.isEmpty(displayName)) {
                 displayName = mContext.getString(R.string.missing_name);
             }
-            final RawContactViewHolder holder = (RawContactViewHolder) view.getTag();
             holder.displayName.setText(displayName);
 
             final String accountDisplayLabel;
 
             // Use the same string as editor if it's an editable user profile raw contact.
-            if (mIsUserProfile && account.areContactsWritable()) {
+            if (mRawContactsMetadata.isUserProfile && account.areContactsWritable()) {
                 final AccountDisplayInfo displayInfo =
                         mAccountDisplayInfoFactory.getAccountDisplayInfo(
-                                new AccountWithDataSet(accountName, accountType, dataSet));
+                                new AccountWithDataSet(rawContact.accountName,
+                                        rawContact.accountType, rawContact.accountDataSet));
                 accountDisplayLabel = EditorUiUtils.getAccountHeaderLabelForMyProfile(mContext,
                         displayInfo);
-            }
-            else if (GoogleAccountType.ACCOUNT_TYPE.equals(accountType)
+            } else if (GoogleAccountType.ACCOUNT_TYPE.equals(rawContact.accountType)
                     && account.dataSet == null) {
                 // Focus Google accounts have the account name shown
-                accountDisplayLabel = accountName;
+                accountDisplayLabel = rawContact.accountName;
             } else {
                 accountDisplayLabel = account.getDisplayLabel(mContext).toString();
             }
 
             holder.accountName.setText(accountDisplayLabel);
             holder.accountIcon.setImageDrawable(account.getDisplayIcon(mContext));
-
             final ContactPhotoManager.DefaultImageRequest
                     request = new ContactPhotoManager.DefaultImageRequest(
-                    displayName, String.valueOf(rawContactId), /* isCircular = */ true);
+                    displayName, String.valueOf(rawContact.id), /* isCircular = */ true);
             final Uri photoUri = Uri.withAppendedPath(
-                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
+                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContact.id),
                     RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
 
             ContactPhotoManager.getInstance(mContext).loadDirectoryPhoto(holder.photo,
@@ -113,26 +139,10 @@
                     /* darkTheme = */ false,
                     /* isCircular = */ true,
                     request);
-        }
 
-        @Override
-        public View newView(Context context, Cursor cursor, ViewGroup parent) {
-            final View view = mInflater.inflate(R.layout.raw_contact_list_item, parent, false);
-            final RawContactViewHolder holder = new RawContactViewHolder();
-            holder.displayName = (TextView) view.findViewById(R.id.display_name);
-            holder.accountName = (TextView) view.findViewById(R.id.account_name);
-            holder.accountIcon = (ImageView) view.findViewById(R.id.account_icon);
-            holder.photo = (ImageView) view.findViewById(R.id.photo);
-            view.setTag(holder);
             return view;
         }
 
-        @Override
-        public long getItemId(int position) {
-            getCursor().moveToPosition(position);
-            return getCursor().getLong(PickRawContactLoader.RAW_CONTACT_ID);
-        }
-
         class RawContactViewHolder {
             TextView displayName;
             TextView accountName;
@@ -141,17 +151,13 @@
         }
     }
 
-    // Cursor holding all raw contact rows for the given Contact.
-    private Cursor mCursor;
-    private CursorAdapter mAdapter;
-    private boolean mIsUserProfile;
+    private ListAdapter mAdapter;
 
-    public static PickRawContactDialogFragment getInstance(Cursor cursor, boolean isUserProfile) {
+    public static PickRawContactDialogFragment getInstance(RawContactsMetadata metadata) {
         final PickRawContactDialogFragment fragment = new PickRawContactDialogFragment();
         final Bundle args = new Bundle();
-        args.putBoolean(ARGS_IS_USER_PROFILE, isUserProfile);
+        args.putParcelable(ARGS_RAW_CONTACTS_METADATA, metadata);
         fragment.setArguments(args);
-        fragment.setCursor(cursor);
         return fragment;
     }
 
@@ -161,9 +167,19 @@
             throw new IllegalArgumentException(
                     "Host activity doesn't implement PickRawContactListener");
         }
+        final Bundle args = getArguments();
+        if (args == null) {
+            throw new IllegalArgumentException("Dialog created with no arguments");
+        }
+
+        final RawContactsMetadata metadata = args.getParcelable(ARGS_RAW_CONTACTS_METADATA);
+        if (metadata == null) {
+            throw new IllegalArgumentException("Dialog created with null RawContactsMetadata");
+        }
+
         final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-        mAdapter = new RawContactAccountListAdapter(getContext(), mCursor);
-        builder.setTitle(R.string.contact_editor_pick_raw_contact_dialog_title);
+        mAdapter = new RawContactAccountListAdapter(getContext(), metadata);
+        builder.setTitle(R.string.contact_editor_pick_raw_contact_to_edit_dialog_title);
         builder.setAdapter(mAdapter, new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
@@ -178,32 +194,14 @@
     @Override
     public void onDismiss(DialogInterface dialog) {
         super.onDismiss(dialog);
-        mCursor = null;
         finishActivity();
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        final Bundle args = getArguments();
-        if (args != null) {
-            mIsUserProfile = args.getBoolean(ARGS_IS_USER_PROFILE);
-        }
-    }
-
-    @Override
     public Context getContext() {
         return getActivity();
     }
 
-    public void setCursor(Cursor cursor) {
-        if (mAdapter != null) {
-            mAdapter.swapCursor(cursor);
-        }
-        mCursor = cursor;
-    }
-
     private void finishActivity() {
         if (getActivity() != null && !getActivity().isFinishing()) {
             getActivity().finish();
diff --git a/src/com/android/contacts/editor/PickRawContactLoader.java b/src/com/android/contacts/editor/PickRawContactLoader.java
index ca44e21..1939666 100644
--- a/src/com/android/contacts/editor/PickRawContactLoader.java
+++ b/src/com/android/contacts/editor/PickRawContactLoader.java
@@ -1,22 +1,31 @@
 package com.android.contacts.editor;
 
+import android.content.AsyncTaskLoader;
+import android.content.ContentResolver;
 import android.content.Context;
-import android.content.CursorLoader;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.RawContacts;
 
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountType;
+
+import java.util.ArrayList;
+
 /**
  * Loader for the pick a raw contact to edit activity. Loads all raw contact metadata for the
  * given Contact {@link Uri}.
  */
-public class PickRawContactLoader extends CursorLoader {
+public class PickRawContactLoader extends
+        AsyncTaskLoader<PickRawContactLoader.RawContactsMetadata> {
     private Uri mContactUri;
-    private boolean mIsUserProfile;
+    private RawContactsMetadata mCachedResult;
 
-    public static final String[] COLUMNS = new String[] {
+    private static final String[] RAW_CONTACT_PROJECTION = new String[] {
             RawContacts.ACCOUNT_NAME,
             RawContacts.ACCOUNT_TYPE,
             RawContacts.DATA_SET,
@@ -25,53 +34,97 @@
             RawContacts.DISPLAY_NAME_ALTERNATIVE
     };
 
-    public static final String SELECTION = RawContacts.CONTACT_ID + "=?";
+    private static final String RAW_CONTACT_SELECTION = RawContacts.CONTACT_ID + "=?";
 
-    public static final int ACCOUNT_NAME = 0;
-    public static final int ACCOUNT_TYPE = 1;
-    public static final int DATA_SET = 2;
-    public static final int RAW_CONTACT_ID = 3;
-    public static final int DISPLAY_NAME_PRIMARY = 4;
-    public static final int DISPLAY_NAME_ALTERNATIVE = 5;
+    private static final int ACCOUNT_NAME = 0;
+    private static final int ACCOUNT_TYPE = 1;
+    private static final int DATA_SET = 2;
+    private static final int RAW_CONTACT_ID = 3;
+    private static final int DISPLAY_NAME_PRIMARY = 4;
+    private static final int DISPLAY_NAME_ALTERNATIVE = 5;
 
     public PickRawContactLoader(Context context, Uri contactUri) {
-        super(context, ensureIsContactUri(contactUri), COLUMNS, SELECTION, null, RawContacts._ID);
-        mContactUri = contactUri;
+        super(context);
+        mContactUri = ensureIsContactUri(contactUri);
     }
 
     @Override
-    public Cursor loadInBackground() {
+    public RawContactsMetadata loadInBackground() {
+        final ContentResolver resolver = getContext().getContentResolver();
         // Get the id of the contact we're looking at.
-        final Cursor cursor = getContext().getContentResolver()
-                .query(mContactUri, new String[] { Contacts._ID, Contacts.IS_USER_PROFILE }, null,
+        final Cursor contactCursor = resolver.query(
+                mContactUri, new String[]{Contacts._ID, Contacts.IS_USER_PROFILE}, null,
                 null, null);
 
-        if (cursor == null) {
+        if (contactCursor == null) {
             return null;
         }
 
-        if (cursor.getCount() < 1) {
-            cursor.close();
+        if (contactCursor.getCount() < 1) {
+            contactCursor.close();
             return null;
         }
 
-        cursor.moveToFirst();
-        final long contactId = cursor.getLong(/* Contacts._ID */ 0);
-        mIsUserProfile = cursor.getInt(/* Contacts.IS_USER_PROFILE */ 1) == 1;
+        final RawContactsMetadata result = new RawContactsMetadata();
+        final long contactId;
+        try {
+            contactCursor.moveToFirst();
+            contactId = contactCursor.getLong(/* Contacts._ID */ 0);
+            result.isUserProfile = contactCursor.getInt(/* Contacts.IS_USER_PROFILE */ 1) == 1;
+        } finally {
+            contactCursor.close();
+        }
 
-        cursor.close();
-        // Update selection arguments and uri.
-        setSelectionArgs(new String[]{ Long.toString(contactId) });
-        if (mIsUserProfile) {
-            setUri(ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI);
+        // Load RawContact data
+        final Uri rawContactUri;
+        if (result.isUserProfile) {
+            rawContactUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI;
         } else {
-            setUri(RawContacts.CONTENT_URI);
+            rawContactUri = RawContacts.CONTENT_URI;
         }
-        return super.loadInBackground();
+
+        final Cursor rawContactCursor = resolver.query(
+                rawContactUri, RAW_CONTACT_PROJECTION, RAW_CONTACT_SELECTION,
+                new String[] {Long.toString(contactId)}, null);
+
+        if (rawContactCursor == null) {
+            return null;
+        }
+
+        rawContactCursor.moveToPosition(-1);
+        try {
+            while (rawContactCursor.moveToNext()) {
+                RawContact rawContact = new RawContact();
+                rawContact.id = rawContactCursor.getLong(RAW_CONTACT_ID);
+                rawContact.displayName = rawContactCursor.getString(DISPLAY_NAME_PRIMARY);
+                rawContact.displayNameAlt = rawContactCursor.getString(DISPLAY_NAME_ALTERNATIVE);
+                rawContact.accountName = rawContactCursor.getString(ACCOUNT_NAME);
+                rawContact.accountType = rawContactCursor.getString(ACCOUNT_TYPE);
+                rawContact.accountDataSet = rawContactCursor.getString(DATA_SET);
+                result.rawContacts.add(rawContact);
+            }
+        } finally {
+            rawContactCursor.close();
+        }
+        return result;
     }
 
-    public boolean isUserProfile() {
-        return mIsUserProfile;
+    @Override
+    public void deliverResult(RawContactsMetadata data) {
+        mCachedResult = data;
+        if (isStarted()) {
+            super.deliverResult(data);
+        }
+    }
+
+    @Override
+    protected void onStartLoading() {
+        super.onStartLoading();
+        if (mCachedResult == null) {
+            forceLoad();
+        } else {
+            deliverResult(mCachedResult);
+        }
     }
 
     /**
@@ -87,4 +140,121 @@
         }
         return uri;
     }
+
+    public static class RawContactsMetadata implements Parcelable {
+        public static final Parcelable.Creator<RawContactsMetadata> CREATOR =
+                new Parcelable.Creator<RawContactsMetadata>() {
+                    @Override
+                    public RawContactsMetadata createFromParcel(Parcel source) {
+                        return new RawContactsMetadata(source);
+                    }
+
+                    @Override
+                    public RawContactsMetadata[] newArray(int size) {
+                        return new RawContactsMetadata[size];
+                    }
+                };
+
+        public boolean isUserProfile;
+        public boolean showReadOnly = false;
+        public ArrayList<RawContact> rawContacts = new ArrayList<>();
+
+        public RawContactsMetadata() {}
+
+        private RawContactsMetadata(Parcel in) {
+            isUserProfile = in.readInt() == 1;
+            showReadOnly = in.readInt() == 1;
+            in.readTypedList(rawContacts, RawContact.CREATOR);
+        }
+
+        /**
+         * Removes all read-only raw contacts.
+         */
+        public void trimReadOnly(AccountTypeManager accountManager) {
+            for (int i = rawContacts.size() - 1; i >= 0 ; i--) {
+                final RawContact rawContact = rawContacts.get(i);
+                final AccountType account = accountManager.getAccountType(
+                        rawContact.accountType, rawContact.accountDataSet);
+                if (!account.areContactsWritable()) {
+                    rawContacts.remove(i);
+                }
+            }
+        }
+
+        /**
+         * Returns the index of the first writable account in this contact or -1 if none exist.
+         */
+        public int getIndexOfFirstWritableAccount(AccountTypeManager accountManager) {
+            for (int i = 0; i < rawContacts.size(); i++) {
+                final RawContact rawContact = rawContacts.get(i);
+                final AccountType account = accountManager.getAccountType(
+                        rawContact.accountType, rawContact.accountDataSet);
+                if (account.areContactsWritable()) {
+                    return i;
+                }
+            }
+
+            return -1;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(isUserProfile ? 1 : 0);
+            dest.writeInt(showReadOnly ? 1 : 0);
+            dest.writeTypedList(rawContacts);
+        }
+    }
+
+    public static class RawContact implements Parcelable {
+        public static final Parcelable.Creator<RawContact> CREATOR =
+                new Parcelable.Creator<RawContact>() {
+                    @Override
+                    public RawContact createFromParcel(Parcel source) {
+                        return new RawContact(source);
+                    }
+
+                    @Override
+                    public RawContact[] newArray(int size) {
+                        return new RawContact[size];
+                    }
+                };
+
+        public long id;
+        public String displayName;
+        public String displayNameAlt;
+        public String accountName;
+        public String accountType;
+        public String accountDataSet;
+
+        public RawContact() {}
+
+        private RawContact(Parcel in) {
+            id = in.readLong();
+            displayName = in.readString();
+            displayNameAlt = in.readString();
+            accountName = in.readString();
+            accountType = in.readString();
+            accountDataSet = in.readString();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeLong(id);
+            dest.writeString(displayName);
+            dest.writeString(displayNameAlt);
+            dest.writeString(accountName);
+            dest.writeString(accountType);
+            dest.writeString(accountDataSet);
+        }
+    }
 }