Older EAS types, new Email field, fix count and INSERT bugs.

The EAS sync-adapter isn't ready to handle type-less Email
and IM entries yet, so bring back the original strongly-
typed editors.  Partially fixes http://b/2089080

Provide a second edit field for Email.DISPLAY_NAME when
editing EAS contacts.  Partially fixes http://b/2092744

Correct count when determining canInsert() for fields that
have typeOverallMax constraints.  Fixes http://b/2089132

Correctly build incoming StructuredName using existing
field, and check for empty fields to fix http://b/2078726
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 04707df..e51389c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -856,6 +856,14 @@
 <string name="type_radio">Radio</string>
 <string name="type_assistant">Assistant</string>
 
+<string name="type_email_1">Email 1</string>
+<string name="type_email_2">Email 2</string>
+<string name="type_email_3">Email 3</string>
+
+<string name="type_im_1">Im 1</string>
+<string name="type_im_2">Im 2</string>
+<string name="type_im_3">Im 3</string>
+
 <string name="type_im_aim">AIM</string>
 <string name="type_im_msn">Windows Live</string>
 <string name="type_im_yahoo">Yahoo</string>
@@ -888,6 +896,10 @@
          and will not be synced. -->
     <string name="account_phone">Phone-only (unsynced)</string>
 
+    <!-- The label describing the display name field of an Email address, allowing the
+         user to give a specific name to describe this address. -->
+    <string name="label_email_display_name">Display name</string>
+
 <string name="call_home">Call home</string>
 <string name="call_mobile">Call mobile</string>
 <string name="call_work">Call work</string>
@@ -898,6 +910,8 @@
 <string name="call_custom">Call <xliff:g id="custom">%s</xliff:g></string>
 
 <!-- exchange specific -->
+<string name="call_home_2">Call home 2</string>
+<string name="call_work_2">Call work 2</string>
 <string name="call_car">Call car</string>
 <string name="call_company_main">Call company main</string>
 <string name="call_mms">Call MMS</string>
@@ -917,6 +931,8 @@
 <string name="sms_custom">Text <xliff:g id="custom">%s</xliff:g></string>
 
 <!-- exchange specific -->
+<string name="sms_home_2">Text home 2</string>
+<string name="sms_work_2">Text work 2</string>
 <string name="sms_car">Text car</string>
 <string name="sms_company_main">Text company main</string>
 <string name="sms_mms">Text MMS</string>
@@ -930,6 +946,9 @@
 <string name="email_other">Email other</string>
 <string name="email_custom">Email <xliff:g id="custom">%s</xliff:g></string>
 
+<string name="email_1">Email 1</string>
+<string name="email_2">Email 2</string>
+<string name="email_3">Email 2</string>
 
 
 
diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java
index 1b99881..cb4a8c9 100644
--- a/src/com/android/contacts/model/EntityDelta.java
+++ b/src/com/android/contacts/model/EntityDelta.java
@@ -121,13 +121,11 @@
     }
 
     /**
-     * Get the {@link ValuesDelta} child marked as {@link Data#IS_PRIMARY}.
+     * Get the {@link ValuesDelta} child marked as {@link Data#IS_PRIMARY},
+     * which may return null when no entry exists.
      */
     public ValuesDelta getPrimaryEntry(String mimeType) {
         final ArrayList<ValuesDelta> mimeEntries = getMimeEntries(mimeType, false);
-
-        // TODO: handle the case where the caller must have a non-null value,
-        // for example inserting a displayname automatically
         if (mimeEntries == null) return null;
 
         for (ValuesDelta entry : mimeEntries) {
@@ -157,9 +155,17 @@
         return getMimeEntries(mimeType, false);
     }
 
-    public int getMimeEntriesCount(String mimeType) {
-        final ArrayList<ValuesDelta> mimeEntries = getMimeEntries(mimeType, false);
-        return mimeEntries == null ? 0 : mimeEntries.size();
+    public int getMimeEntriesCount(String mimeType, boolean onlyVisible) {
+        final ArrayList<ValuesDelta> mimeEntries = getMimeEntries(mimeType);
+        if (mimeEntries == null) return 0;
+
+        int count = 0;
+        for (ValuesDelta child : mimeEntries) {
+            // Skip deleted items when requesting only visible
+            if (onlyVisible && !child.isVisible()) continue;
+            count++;
+        }
+        return count;
     }
 
     public boolean hasMimeEntries(String mimeType) {
@@ -196,12 +202,8 @@
      */
     public int getEntryCount(boolean onlyVisible) {
         int count = 0;
-        for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
-            for (ValuesDelta child : mimeEntries) {
-                // Skip deleted items when requesting only visible
-                if (onlyVisible && child.isVisible()) continue;
-                count++;
-            }
+        for (String mimeType : mEntries.keySet()) {
+            count += getMimeEntriesCount(mimeType, onlyVisible);
         }
         return count;
     }
diff --git a/src/com/android/contacts/model/EntityModifier.java b/src/com/android/contacts/model/EntityModifier.java
index 1c3c8f7..87c8432 100644
--- a/src/com/android/contacts/model/EntityModifier.java
+++ b/src/com/android/contacts/model/EntityModifier.java
@@ -52,9 +52,10 @@
      */
     public static boolean canInsert(EntityDelta state, DataKind kind) {
         // Insert possible when have valid types and under overall maximum
+        final int visibleCount = state.getMimeEntriesCount(kind.mimeType, true);
         final boolean validTypes = hasValidTypes(state, kind);
         final boolean validOverall = (kind.typeOverallMax == -1)
-                || (state.getEntryCount(true) < kind.typeOverallMax);
+                || (visibleCount < kind.typeOverallMax);
         return (validTypes && validOverall);
     }
 
@@ -72,7 +73,7 @@
      */
     public static void ensureKindExists(EntityDelta state, ContactsSource source, String mimeType) {
         final DataKind kind = source.getKindForMimetype(mimeType);
-        final boolean hasChild = state.getMimeEntriesCount(mimeType) > 0;
+        final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0;
 
         if (!hasChild && kind != null) {
             // Create child when none exists and valid kind
@@ -202,6 +203,7 @@
      * assuming the given {@link DataKind} dictates the possible types.
      */
     public static EditType getCurrentType(Cursor cursor, DataKind kind) {
+        if (kind.typeColumn == null) return null;
         final int index = cursor.getColumnIndex(kind.typeColumn);
         final int rawValue = cursor.getInt(index);
         return getType(kind, rawValue);
@@ -319,14 +321,16 @@
 
         {
             // StructuredName
-            final DataKind kind = source.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
+            EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE);
+            final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
+
             final String name = extras.getString(Insert.NAME);
+            if (!TextUtils.isEmpty(name) && TextUtils.isGraphic(name)) {
+                child.put(StructuredName.GIVEN_NAME, name);
+            }
+
             final String phoneticName = extras.getString(Insert.PHONETIC_NAME);
-            if (kind != null && (TextUtils.isGraphic(name) || TextUtils.isGraphic(phoneticName))) {
-                // TODO: handle the case where name already exists and limited to one
-                // TODO: represent phonetic name as structured fields
-                final ValuesDelta child = EntityModifier.insertChild(state, kind, null);
-                child.put(StructuredName.DISPLAY_NAME, name);
+            if (!TextUtils.isEmpty(phoneticName) && TextUtils.isGraphic(phoneticName)) {
                 child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName);
             }
         }
diff --git a/src/com/android/contacts/model/HardCodedSources.java b/src/com/android/contacts/model/HardCodedSources.java
index d9fd83c..07e5877 100644
--- a/src/com/android/contacts/model/HardCodedSources.java
+++ b/src/com/android/contacts/model/HardCodedSources.java
@@ -369,26 +369,37 @@
     // TODO: this should come from resource in the future
     private static final String GOOGLE_MY_CONTACTS_GROUP = "System Group: My Contacts";
 
-    public static final ValuesDelta buildMyContactsMembership(Context context) {
+    public static final void attemptMyContactsMembership(EntityDelta state, Context context) {
         final ContentResolver resolver = context.getContentResolver();
         final Cursor cursor = resolver.query(Groups.CONTENT_URI, new String[] { Groups.SOURCE_ID },
                 Groups.TITLE + "=?", new String[] { GOOGLE_MY_CONTACTS_GROUP }, null);
-
-        final ContentValues values = new ContentValues();
-        if (cursor.moveToFirst()) {
-            final String sourceId = cursor.getString(0);
-            values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
-            values.put(GroupMembership.GROUP_SOURCE_ID, sourceId);
+        try {
+            if (cursor.moveToFirst()) {
+                final ContentValues values = new ContentValues();
+                final String sourceId = cursor.getString(0);
+                values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
+                values.put(GroupMembership.GROUP_SOURCE_ID, sourceId);
+                state.addEntry(ValuesDelta.fromAfter(values));
+            }
+        } finally {
+            cursor.close();
         }
-
-        cursor.close();
-        return ValuesDelta.fromAfter(values);
     }
 
     /**
      * The constants below are shared with the Exchange sync adapter, and are
      * currently static. These values should be maintained in parallel.
      */
+    private static final int TYPE_EMAIL1 = 20;
+    private static final int TYPE_EMAIL2 = 21;
+    private static final int TYPE_EMAIL3 = 22;
+
+    private static final int TYPE_IM1 = 23;
+    private static final int TYPE_IM2 = 24;
+    private static final int TYPE_IM3 = 25;
+
+    private static final int TYPE_WORK2 = 26;
+    private static final int TYPE_HOME2 = 27;
     private static final int TYPE_CAR = 28;
     private static final int TYPE_COMPANY_MAIN = 29;
     private static final int TYPE_MMS = 30;
@@ -443,11 +454,15 @@
             kind.typeColumn = Phone.TYPE;
             kind.typeList = Lists.newArrayList();
             kind.typeList.add(new EditType(Phone.TYPE_HOME, R.string.type_home, R.string.call_home,
-                    R.string.sms_home).setSpecificMax(2));
+                    R.string.sms_home).setSpecificMax(1));
+            kind.typeList.add(new EditType(TYPE_HOME2, R.string.type_home_2, R.string.call_home_2,
+                    R.string.sms_home_2).setSecondary(true).setSpecificMax(1));
             kind.typeList.add(new EditType(Phone.TYPE_MOBILE, R.string.type_mobile,
                     R.string.call_mobile, R.string.sms_mobile).setSpecificMax(1));
             kind.typeList.add(new EditType(Phone.TYPE_WORK, R.string.type_work, R.string.call_work,
-                    R.string.sms_work).setSpecificMax(2));
+                    R.string.sms_work).setSpecificMax(1));
+            kind.typeList.add(new EditType(TYPE_WORK2, R.string.type_work_2, R.string.call_work_2,
+                    R.string.sms_work_2).setSecondary(true).setSpecificMax(1));
             kind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, R.string.type_fax_work,
                     R.string.call_fax_work, R.string.sms_fax_work).setSecondary(true)
                     .setSpecificMax(1));
@@ -482,10 +497,20 @@
 
             kind.actionHeader = new ActionInflater(list.resPackageName, kind);
             kind.actionBody = new SimpleInflater(Email.DATA);
-            kind.typeOverallMax = 3;
+
+            kind.typeColumn = Email.TYPE;
+            kind.typeList = Lists.newArrayList();
+            kind.typeList.add(new EditType(TYPE_EMAIL1, R.string.type_email_1, R.string.email_1)
+                    .setSpecificMax(1));
+            kind.typeList.add(new EditType(TYPE_EMAIL2, R.string.type_email_2, R.string.email_2)
+                    .setSpecificMax(1));
+            kind.typeList.add(new EditType(TYPE_EMAIL3, R.string.type_email_3, R.string.email_3)
+                    .setSpecificMax(1));
 
             kind.fieldList = Lists.newArrayList();
             kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+            kind.fieldList.add(new EditField(Email.DISPLAY_NAME, R.string.label_email_display_name,
+                    FLAGS_PERSON_NAME));
 
             list.add(kind);
         }
@@ -497,7 +522,12 @@
 
             kind.actionHeader = new ActionInflater(list.resPackageName, kind);
             kind.actionBody = new SimpleInflater(Im.DATA);
-            kind.typeOverallMax = 3;
+
+            kind.typeColumn = Im.TYPE;
+            kind.typeList = new ArrayList<EditType>();
+            kind.typeList.add(new EditType(TYPE_IM1, R.string.type_im_1).setSpecificMax(1));
+            kind.typeList.add(new EditType(TYPE_IM2, R.string.type_im_2).setSpecificMax(1));
+            kind.typeList.add(new EditType(TYPE_IM3, R.string.type_im_3).setSpecificMax(1));
 
             kind.fieldList = Lists.newArrayList();
             kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index d6acf30..60dc19b 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -730,9 +730,7 @@
                     // Create "My Contacts" membership for Google contacts
                     // TODO: move this off into "templates" for each given source
                     if (HardCodedSources.ACCOUNT_TYPE_GOOGLE.equals(source.accountType)) {
-                        final ValuesDelta membership = HardCodedSources
-                                .buildMyContactsMembership(target);
-                        insert.addEntry(membership);
+                        HardCodedSources.attemptMyContactsMembership(insert, target);
                     }
 
                     target.mState.add(insert);