Adding support for 3rd party contact editing

Change-Id: I0379f7392a23aea68d0f59a9869bb72275f5bddb
diff --git a/res/layout-xlarge/read_only_raw_contact_editor_view.xml b/res/layout-xlarge/external_raw_contact_editor_view.xml
similarity index 79%
rename from res/layout-xlarge/read_only_raw_contact_editor_view.xml
rename to res/layout-xlarge/external_raw_contact_editor_view.xml
index f8edf32..ffda3a5 100644
--- a/res/layout-xlarge/read_only_raw_contact_editor_view.xml
+++ b/res/layout-xlarge/external_raw_contact_editor_view.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<com.android.contacts.editor.ReadOnlyRawContactEditorView
+<com.android.contacts.editor.ExternalRawContactEditorView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
     android:layout_width="match_parent"
@@ -90,5 +90,24 @@
                     android:orientation="vertical" />
             </LinearLayout>
         </com.android.contacts.widget.InterpolatingLayout>
+
+        <com.android.contacts.widget.InterpolatingLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dip"
+            android:layout_marginBottom="42dip">
+            <Button
+                android:id="@+id/button_edit_externally"
+                android:text="@string/edit_contact"
+                ex:layout_wideParentWidth="960dip"
+                ex:layout_wideLeftMargin="240dip"
+                ex:layout_wideRightMargin="190dip"
+                ex:layout_narrowParentWidth="800dip"
+                ex:layout_narrowLeftMargin="160dip"
+                ex:layout_narrowRightMargin="102dip"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+            />
+        </com.android.contacts.widget.InterpolatingLayout>
     </LinearLayout>
-</com.android.contacts.editor.ReadOnlyRawContactEditorView>
+</com.android.contacts.editor.ExternalRawContactEditorView>
diff --git a/res/layout/read_only_raw_contact_editor_view.xml b/res/layout/external_raw_contact_editor_view.xml
similarity index 91%
rename from res/layout/read_only_raw_contact_editor_view.xml
rename to res/layout/external_raw_contact_editor_view.xml
index f5075a3..4a29735 100644
--- a/res/layout/read_only_raw_contact_editor_view.xml
+++ b/res/layout/external_raw_contact_editor_view.xml
@@ -15,7 +15,7 @@
 -->
 
 <!-- placed inside act_edit as tabcontent -->
-<com.android.contacts.editor.ReadOnlyRawContactEditorView
+<com.android.contacts.editor.ExternalRawContactEditorView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -137,6 +137,16 @@
             android:drawablePadding="10dip"
         />
 
+        <Button
+            android:id="@+id/button_edit_externally"
+            android:text="@string/edit_contact"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="13dip"
+            android:layout_marginBottom="13dip"
+            android:layout_marginLeft="13dip"
+        />
+
         <View
             android:layout_width="match_parent"
             android:layout_height="1px"
@@ -150,4 +160,4 @@
         />
     </LinearLayout>
 
-</com.android.contacts.editor.ReadOnlyRawContactEditorView>
+</com.android.contacts.editor.ExternalRawContactEditorView>
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index ebeabba..8d464c9 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -21,8 +21,11 @@
 import com.android.contacts.editor.ContactEditorFragment;
 import com.android.contacts.editor.ContactEditorFragment.SaveMode;
 import com.android.contacts.interactions.ContactDeletionInteraction;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypes;
 import com.android.contacts.util.DialogManager;
 
+import android.accounts.Account;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.app.Dialog;
@@ -31,6 +34,8 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
 import android.util.Log;
 import android.view.MenuItem;
 import android.view.View;
@@ -208,6 +213,54 @@
             startActivity(intent);
             finish();
         }
+
+        @Override
+        public void onCustomCreateContactActivityRequested(Account account, Bundle intentExtras) {
+            final AccountTypes sources = AccountTypes.getInstance(
+                    ContactEditorActivity.this);
+            final AccountType source = sources.getInflatedSource(
+                    account.type, AccountType.LEVEL_CONSTRAINTS);
+
+            Intent intent = new Intent();
+            intent.setClassName(source.resPackageName, source.getCreateContactActivityClassName());
+            intent.setAction(Intent.ACTION_INSERT);
+            intent.setType(Contacts.CONTENT_ITEM_TYPE);
+            if (intentExtras != null) {
+                intent.putExtras(intentExtras);
+            }
+            intent.putExtra(RawContacts.ACCOUNT_NAME, account.name);
+            intent.putExtra(RawContacts.ACCOUNT_TYPE, account.type);
+            intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                    | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            startActivity(intent);
+            finish();
+        }
+
+        @Override
+        public void onCustomEditContactActivityRequested(Account account, Uri rawContactUri,
+                Bundle intentExtras, boolean redirect) {
+            final AccountTypes sources = AccountTypes.getInstance(
+                    ContactEditorActivity.this);
+            final AccountType source = sources.getInflatedSource(
+                    account.type, AccountType.LEVEL_CONSTRAINTS);
+
+            Intent intent = new Intent();
+            intent.setClassName(source.resPackageName, source.getEditContactActivityClassName());
+            intent.setAction(Intent.ACTION_EDIT);
+            intent.setData(rawContactUri);
+            if (intentExtras != null) {
+                intent.putExtras(intentExtras);
+            }
+
+            if (redirect) {
+                intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                        | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+                startActivity(intent);
+                finish();
+            } else {
+                startActivity(intent);
+            }
+        }
     };
 
     @Override
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index a26204f..d10fbe5 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -24,6 +24,7 @@
 import com.android.contacts.activities.JoinContactActivity;
 import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
 import com.android.contacts.editor.Editor.EditorListener;
+import com.android.contacts.editor.ExternalRawContactEditorView.Listener;
 import com.android.contacts.model.AccountType;
 import com.android.contacts.model.AccountTypes;
 import com.android.contacts.model.EntityDelta;
@@ -101,7 +102,8 @@
 
 public class ContactEditorFragment extends Fragment implements
         SplitContactConfirmationDialogFragment.Listener, SelectAccountDialogFragment.Listener,
-        AggregationSuggestionEngine.Listener, AggregationSuggestionView.Listener {
+        AggregationSuggestionEngine.Listener, AggregationSuggestionView.Listener,
+        ExternalRawContactEditorView.Listener {
 
     private static final String TAG = "ContactEditorFragment";
 
@@ -375,6 +377,35 @@
             return;
         }
 
+        // See if this edit operation needs to be redirected to a custom editor
+        ArrayList<Entity> entities = data.getEntities();
+        if (entities.size() == 1) {
+            Entity entity = entities.get(0);
+            ContentValues entityValues = entity.getEntityValues();
+            String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
+            AccountType accountType = AccountTypes.getInstance(mContext).getInflatedSource(
+                    type, AccountType.LEVEL_SUMMARY);
+            if (accountType.getEditContactActivityClassName() != null) {
+                if (mListener != null) {
+                    String name = entityValues.getAsString(RawContacts.ACCOUNT_NAME);
+                    long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
+                    mListener.onCustomEditContactActivityRequested(new Account(name, type),
+                            ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
+                            mIntentExtras, true);
+                }
+                return;
+            }
+        }
+
+        bindEditorsForExistingContact(data);
+    }
+
+    @Override
+    public void onExternalEditorRequest(Account account, Uri uri) {
+        mListener.onCustomEditContactActivityRequested(account, uri, null, false);
+    }
+
+    private void bindEditorsForExistingContact(ContactLoader.Result data) {
         setEnabled(true);
 
         // Build Filter mQuerySelection
@@ -443,6 +474,19 @@
      */
     private void createContact(Account account) {
         final AccountTypes sources = AccountTypes.getInstance(mContext);
+        final AccountType source = sources.getInflatedSource(
+                account != null ? account.type : null, AccountType.LEVEL_CONSTRAINTS);
+
+        if (source.getCreateContactActivityClassName() != null) {
+            if (mListener != null) {
+                mListener.onCustomCreateContactActivityRequested(account, mIntentExtras);
+            }
+        } else {
+            bindEditorsForNewContact(account, source);
+        }
+    }
+
+    private void bindEditorsForNewContact(Account account, final AccountType source) {
         final ContentValues values = new ContentValues();
         if (account != null) {
             values.put(RawContacts.ACCOUNT_NAME, account.name);
@@ -454,9 +498,6 @@
 
         // Parse any values from incoming intent
         EntityDelta insert = new EntityDelta(ValuesDelta.fromAfter(values));
-        final AccountType source = sources.getInflatedSource(
-                account != null ? account.type : null,
-                AccountType.LEVEL_CONSTRAINTS);
         EntityModifier.parseExtras(mContext, source, insert, mIntentExtras);
 
         // Ensure we have some default fields (if the source does not supper a field,
@@ -503,12 +544,13 @@
             final long rawContactId = values.getAsLong(RawContacts._ID);
 
             final BaseRawContactEditorView editor;
-            if (!source.readOnly) {
+            if (source.isExternal()) {
+                editor = (BaseRawContactEditorView) inflater.inflate(
+                        R.layout.external_raw_contact_editor_view, mContent, false);
+                ((ExternalRawContactEditorView) editor).setListener(this);
+            } else {
                 editor = (BaseRawContactEditorView)
                         inflater.inflate(R.layout.raw_contact_editor_view, mContent, false);
-            } else {
-                editor = (BaseRawContactEditorView) inflater.inflate(
-                        R.layout.read_only_raw_contact_editor_view, mContent, false);
             }
             editor.setEnabled(mEnabled);
 
@@ -954,6 +996,22 @@
          */
         void onEditOtherContactRequested(
                 Uri contactLookupUri, ArrayList<ContentValues> contentValues);
+
+        /**
+         * Contact is being created for an external account that provides its own
+         * new contact activity.
+         */
+        void onCustomCreateContactActivityRequested(Account account, Bundle intentExtras);
+
+        /**
+         * The edited raw contact belongs to an external account that provides
+         * its own edit activity.
+         *
+         * @param redirect indicates that the current editor should be closed
+         *            before the custom editor is shown.
+         */
+        void onCustomEditContactActivityRequested(Account account, Uri rawContactUri,
+                Bundle intentExtras, boolean redirect);
     }
 
     private class EntityDeltaComparator implements Comparator<EntityDelta> {
diff --git a/src/com/android/contacts/editor/ReadOnlyRawContactEditorView.java b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
similarity index 77%
rename from src/com/android/contacts/editor/ReadOnlyRawContactEditorView.java
rename to src/com/android/contacts/editor/ExternalRawContactEditorView.java
index 69fc39e..0775fb0 100644
--- a/src/com/android/contacts/editor/ReadOnlyRawContactEditorView.java
+++ b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
@@ -18,13 +18,17 @@
 
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
+import com.android.contacts.editor.ExternalRawContactEditorView.Listener;
 import com.android.contacts.model.AccountType;
 import com.android.contacts.model.AccountType.DataKind;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.android.contacts.model.EntityModifier;
 
+import android.accounts.Account;
+import android.content.ContentUris;
 import android.content.Context;
+import android.net.Uri;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
@@ -35,37 +39,53 @@
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 import java.util.ArrayList;
 
 /**
- * Custom view that displays read-only contacts in the edit screen.
+ * Custom view that displays external contacts in the edit screen.
  */
-public class ReadOnlyRawContactEditorView extends BaseRawContactEditorView {
+public class ExternalRawContactEditorView extends BaseRawContactEditorView
+        implements OnClickListener {
     private LayoutInflater mInflater;
 
     private View mPhotoStub;
     private TextView mName;
     private TextView mReadOnlyWarning;
+    private Button mEditExternallyButton;
     private ViewGroup mGeneral;
 
     private ImageView mHeaderIcon;
     private TextView mHeaderAccountType;
     private TextView mHeaderAccountName;
 
+    private String mAccountName;
+    private String mAccountType;
     private long mRawContactId = -1;
 
-    public ReadOnlyRawContactEditorView(Context context) {
+    private Listener mListener;
+
+    public interface Listener {
+        void onExternalEditorRequest(Account account, Uri uri);
+    }
+
+    public ExternalRawContactEditorView(Context context) {
         super(context);
     }
 
-    public ReadOnlyRawContactEditorView(Context context, AttributeSet attrs) {
+    public ExternalRawContactEditorView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
     /** {@inheritDoc} */
     @Override
     protected void onFinishInflate() {
@@ -78,6 +98,8 @@
 
         mName = (TextView) findViewById(R.id.read_only_name);
         mReadOnlyWarning = (TextView) findViewById(R.id.read_only_warning);
+        mEditExternallyButton = (Button) findViewById(R.id.button_edit_externally);
+        mEditExternallyButton.setOnClickListener(this);
         mGeneral = (ViewGroup)findViewById(R.id.sect_general);
 
         mHeaderIcon = (ImageView) findViewById(R.id.header_icon);
@@ -105,14 +127,15 @@
 
         // Fill in the header info
         ValuesDelta values = state.getValues();
-        String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+        mAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+        mAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
         CharSequence accountType = source.getDisplayLabel(mContext);
         if (TextUtils.isEmpty(accountType)) {
             accountType = mContext.getString(R.string.account_phone);
         }
-        if (!TextUtils.isEmpty(accountName)) {
+        if (!TextUtils.isEmpty(mAccountName)) {
             mHeaderAccountName.setText(
-                    mContext.getString(R.string.from_account_format, accountName));
+                    mContext.getString(R.string.from_account_format, mAccountName));
         }
         mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType));
         mHeaderIcon.setImageDrawable(source.getDisplayIcon(mContext));
@@ -142,8 +165,14 @@
         primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
         mName.setText(primary.getAsString(StructuredName.DISPLAY_NAME));
 
-        // Read only warning
-        mReadOnlyWarning.setText(mContext.getString(R.string.contact_read_only, accountType));
+        if (source.readOnly) {
+            mReadOnlyWarning.setText(mContext.getString(R.string.contact_read_only, accountType));
+            mReadOnlyWarning.setVisibility(View.VISIBLE);
+            mEditExternallyButton.setVisibility(View.GONE);
+        } else {
+            mReadOnlyWarning.setVisibility(View.GONE);
+            mEditExternallyButton.setVisibility(View.VISIBLE);
+        }
 
         // Phones
         ArrayList<ValuesDelta> phones = state.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
@@ -189,4 +218,14 @@
     public long getRawContactId() {
         return mRawContactId;
     }
+
+    @Override
+    public void onClick(View v) {
+        if (v.getId() == R.id.button_edit_externally) {
+            if (mListener != null) {
+                mListener.onExternalEditorRequest(new Account(mAccountName, mAccountType),
+                        ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactId));
+            }
+        }
+    }
 }
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index e6d8987..2571cba 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -118,6 +118,26 @@
         setInflatedLevel(LEVEL_NONE);
     }
 
+    public boolean isExternal() {
+        return false;
+    }
+
+    /**
+     * Returns an optional custom edit activity.  The activity class should reside
+     * in the sync adapter package as determined by {@link #resPackageName}.
+     */
+    public String getEditContactActivityClassName() {
+        return null;
+    }
+
+    /**
+     * Returns an optional custom new contact activity. The activity class should reside
+     * in the sync adapter package as determined by {@link #resPackageName}.
+     */
+    public String getCreateContactActivityClassName() {
+        return null;
+    }
+
     public CharSequence getDisplayLabel(Context context) {
         if (this.titleRes != -1 && this.summaryResPackageName != null) {
             final PackageManager pm = context.getPackageManager();
@@ -389,5 +409,4 @@
         public CharSequence inflateUsing(Context context, Cursor cursor);
         public CharSequence inflateUsing(Context context, ContentValues values);
     }
-
 }
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index 8b35242..35336f3 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -23,69 +23,55 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.Xml;
 
 import java.io.IOException;
 import java.util.List;
 
-/*
-
-<!-- example of what SourceConstraints would look like in XML -->
-<!-- NOTE: may not directly match the current structure version -->
-
-<DataKind
-    mimeType="vnd.android.cursor.item/email"
-    title="@string/title_postal"
-    icon="@drawable/icon_postal"
-    weight="12"
-    editable="true">
-
-    <!-- these are defined using string-builder-ish -->
-    <ActionHeader></ActionHeader>
-    <ActionBody socialSummary="true" />  <!-- can pull together various columns -->
-
-    <!-- ordering handles precedence the "insert/add" case -->
-    <!-- assume uniform type when missing "column", use title in place -->
-    <EditTypes column="data5" overallMax="-1">
-        <EditType rawValue="0" label="@string/type_home" specificMax="-1" />
-        <EditType rawValue="1" label="@string/type_work" specificMax="-1" secondary="true" />
-        <EditType rawValue="4" label="@string/type_custom" customColumn="data6" specificMax="-1" secondary="true" />
-    </EditTypes>
-
-    <!-- when single edit field, simplifies edit case -->
-    <EditField column="data1" title="@string/field_family_name" android:inputType="textCapWords|textPhonetic" />
-    <EditField column="data2" title="@string/field_given_name" android:minLines="2" />
-    <EditField column="data3" title="@string/field_suffix" />
-
-</DataKind>
-
-*/
-
 /**
- * Internal structure that represents constraints and styles for a specific data
- * source, such as the various data types they support, including details on how
- * those types should be rendered and edited.
- * <p>
- * In the future this may be inflated from XML defined by a data source.
+ * A general contacts account type descriptor.
  */
 public class ExternalAccountType extends FallbackAccountType {
+    private static final String TAG = "ExternalAccountType";
+
     private static final String ACTION_SYNC_ADAPTER = "android.content.SyncAdapter";
     private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
 
-    private interface InflateTags {
-        final String CONTACTS_SOURCE_LEGACY = "ContactsSource";
-        final String CONTACTS_ACCOUNT_TYPE = "ContactsAccountType";
-        final String CONTACTS_DATA_KIND = "ContactsDataKind";
-    }
+    private static final String TAG_CONTACTS_SOURCE_LEGACY = "ContactsSource";
+    private static final String TAG_CONTACTS_ACCOUNT_TYPE = "ContactsAccountType";
+    private static final String TAG_CONTACTS_DATA_KIND = "ContactsDataKind";
+
+    private static final String ATTR_EDIT_CONTACT_ACTIVITY = "editContactActivity";
+    private static final String ATTR_CREATE_CONTACT_ACTIVITY = "createContactActivity";
+
+    private String mEditContactActivityClassName;
+    private String mCreateContactActivityClassName;
 
     public ExternalAccountType(String resPackageName) {
         this.resPackageName = resPackageName;
         this.summaryResPackageName = resPackageName;
     }
 
+    @Override
+    public boolean isExternal() {
+        return true;
+    }
+
+    @Override
+    public String getEditContactActivityClassName() {
+        return mEditContactActivityClassName;
+    }
+
+    @Override
+    public String getCreateContactActivityClassName() {
+        return mCreateContactActivityClassName;
+    }
+
     /**
      * Ensure that the constraint rules behind this {@link AccountType} have
      * been inflated. Because this may involve parsing meta-data from
@@ -99,10 +85,13 @@
         final List<ResolveInfo> matches = pm.queryIntentServices(syncAdapter,
                 PackageManager.GET_META_DATA);
         for (ResolveInfo info : matches) {
-            final XmlResourceParser parser = info.serviceInfo.loadXmlMetaData(pm,
-                    METADATA_CONTACTS);
-            if (parser == null) continue;
-            inflate(context, parser);
+            ServiceInfo serviceInfo = info.serviceInfo;
+            if (serviceInfo.packageName.equals(resPackageName)) {
+                final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm,
+                        METADATA_CONTACTS);
+                if (parser == null) continue;
+                inflate(context, parser);
+            }
         }
 
         // Bring in name and photo from fallback source, which are non-optional
@@ -131,10 +120,22 @@
             }
 
             String rootTag = parser.getName();
-            if (!InflateTags.CONTACTS_ACCOUNT_TYPE.equals(rootTag) &&
-                    !InflateTags.CONTACTS_SOURCE_LEGACY.equals(rootTag)) {
+            if (!TAG_CONTACTS_ACCOUNT_TYPE.equals(rootTag) &&
+                    !TAG_CONTACTS_SOURCE_LEGACY.equals(rootTag)) {
                 throw new IllegalStateException("Top level element must be "
-                        + InflateTags.CONTACTS_ACCOUNT_TYPE + ", not " + rootTag);
+                        + TAG_CONTACTS_ACCOUNT_TYPE + ", not " + rootTag);
+            }
+
+            int attributeCount = parser.getAttributeCount();
+            for (int i = 0; i < attributeCount; i++) {
+                String attr = parser.getAttributeName(i);
+                if (ATTR_EDIT_CONTACT_ACTIVITY.equals(attr)) {
+                    mEditContactActivityClassName = parser.getAttributeValue(i);
+                } else if (ATTR_CREATE_CONTACT_ACTIVITY.equals(attr)) {
+                    mCreateContactActivityClassName = parser.getAttributeValue(i);
+                } else {
+                    Log.e(TAG, "Unsupported attribute " + attr);
+                }
             }
 
             // Parse all children kinds
@@ -142,8 +143,7 @@
             while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
                     && type != XmlPullParser.END_DOCUMENT) {
                 String tag = parser.getName();
-                if (type == XmlPullParser.END_TAG
-                        || !InflateTags.CONTACTS_DATA_KIND.equals(tag)) {
+                if (type == XmlPullParser.END_TAG || !TAG_CONTACTS_DATA_KIND.equals(tag)) {
                     continue;
                 }