Making contact joining select the highest priority name

Bug: 3113342
Change-Id: I50997f0434a82c549ece29f9451d411f0b7b153a
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index dc622b1..6596d5e 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -70,6 +70,7 @@
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.DisplayNameSources;
 import android.provider.ContactsContract.Intents;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.MediaStore;
@@ -837,6 +838,7 @@
                 RawContacts._ID,
                 RawContacts.CONTACT_ID,
                 RawContacts.NAME_VERIFIED,
+                RawContacts.DISPLAY_NAME_SOURCE,
         };
 
         String SELECTION = RawContacts.CONTACT_ID + "=? OR " + RawContacts.CONTACT_ID + "=?";
@@ -844,6 +846,7 @@
         int _ID = 0;
         int CONTACT_ID = 1;
         int NAME_VERIFIED = 2;
+        int DISPLAY_NAME_SOURCE = 3;
     }
 
     /**
@@ -862,15 +865,31 @@
         long rawContactIds[];
         long verifiedNameRawContactId = -1;
         try {
+            int maxDisplayNameSource = -1;
             rawContactIds = new long[c.getCount()];
             for (int i = 0; i < rawContactIds.length; i++) {
-                c.moveToNext();
+                c.moveToPosition(i);
                 long rawContactId = c.getLong(JoinContactQuery._ID);
                 rawContactIds[i] = rawContactId;
-                if (c.getLong(JoinContactQuery.CONTACT_ID) == mContactIdForJoin) {
-                    if (verifiedNameRawContactId == -1
-                            || c.getInt(JoinContactQuery.NAME_VERIFIED) != 0) {
-                        verifiedNameRawContactId = rawContactId;
+                int nameSource = c.getInt(JoinContactQuery.DISPLAY_NAME_SOURCE);
+                if (nameSource > maxDisplayNameSource) {
+                    maxDisplayNameSource = nameSource;
+                }
+            }
+
+            // Find an appropriate display name for the joined contact:
+            // if should have a higher DisplayNameSource or be the name
+            // of the original contact that we are joining with another.
+            if (isContactWritable()) {
+                for (int i = 0; i < rawContactIds.length; i++) {
+                    c.moveToPosition(i);
+                    if (c.getLong(JoinContactQuery.CONTACT_ID) == mContactIdForJoin) {
+                        int nameSource = c.getInt(JoinContactQuery.DISPLAY_NAME_SOURCE);
+                        if (nameSource == maxDisplayNameSource
+                                && (verifiedNameRawContactId == -1
+                                        || c.getInt(JoinContactQuery.NAME_VERIFIED) != 0)) {
+                            verifiedNameRawContactId = c.getLong(JoinContactQuery._ID);
+                        }
                     }
                 }
             }
@@ -890,10 +909,12 @@
 
         // Mark the original contact as "name verified" to make sure that the contact
         // display name does not change as a result of the join
-        Builder builder = ContentProviderOperation.newUpdate(
+        if (verifiedNameRawContactId != -1) {
+            Builder builder = ContentProviderOperation.newUpdate(
                     ContentUris.withAppendedId(RawContacts.CONTENT_URI, verifiedNameRawContactId));
-        builder.withValue(RawContacts.NAME_VERIFIED, 1);
-        operations.add(builder.build());
+            builder.withValue(RawContacts.NAME_VERIFIED, 1);
+            operations.add(builder.build());
+        }
 
         boolean success = false;
         // Apply all aggregation exceptions as one batch
@@ -915,6 +936,24 @@
     }
 
     /**
+     * Returns true if there is at least one writable raw contact in the current contact.
+     */
+    private boolean isContactWritable() {
+        final AccountTypes sources = AccountTypes.getInstance(mContext);
+        int size = mState.size();
+        for (int i = 0; i < size; i++) {
+            ValuesDelta values = mState.get(i).getValues();
+            final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+            final BaseAccountType source = sources.getInflatedSource(accountType,
+                    BaseAccountType.LEVEL_CONSTRAINTS);
+            if (!source.readOnly) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Construct a {@link AggregationExceptions#TYPE_KEEP_TOGETHER} ContentProviderOperation.
      */
     private void buildJoinContactDiff(ArrayList<ContentProviderOperation> operations,