Merge "Try to show the "right" name on the compact contact editor"
diff --git a/src/com/android/contacts/editor/CompactContactEditorFragment.java b/src/com/android/contacts/editor/CompactContactEditorFragment.java
index 199d21f..f25d01a 100644
--- a/src/com/android/contacts/editor/CompactContactEditorFragment.java
+++ b/src/com/android/contacts/editor/CompactContactEditorFragment.java
@@ -19,16 +19,15 @@
 import com.android.contacts.ContactSaveService;
 import com.android.contacts.R;
 import com.android.contacts.activities.CompactContactEditorActivity;
-import com.android.contacts.activities.ContactEditorActivity;
 import com.android.contacts.activities.ContactEditorBaseActivity;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.RawContactDelta;
 import com.android.contacts.common.model.RawContactDeltaList;
+import com.android.contacts.common.model.ValuesDelta;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.common.util.MaterialColorMapUtils;
 import com.android.contacts.detail.PhotoSelectionHandler;
-import com.android.contacts.editor.Editor.EditorListener;
 import com.android.contacts.util.ContactPhotoUtils;
 
 import android.app.Activity;
@@ -38,7 +37,6 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.Contacts;
 import android.provider.ContactsContract;
 import android.text.TextUtils;
 import android.util.Log;
@@ -221,28 +219,6 @@
         mPhotoRawContactId = editorView.getPhotoRawContactId();
         editorView.setPhotoHandler(mPhotoHandler);
 
-        // Attach the aggregation suggestion engine to the structured name field
-        final StructuredNameEditorView nameEditorView = editorView.getStructuredNameEditorView();
-        if (nameEditorView != null) {
-            nameEditorView.setEditorListener(new EditorListener() {
-                @Override
-                public void onRequest(int request) {
-                    final Activity activity = getActivity();
-                    if (activity == null || activity.isFinishing()) {
-                        return;
-                    }
-                    if (request == EditorListener.FIELD_CHANGED && !mIsUserProfile) {
-                        acquireAggregationSuggestions(activity, nameEditorView.getRawContactId(),
-                                nameEditorView.getValues());
-                    }
-                }
-
-                @Override
-                public void onDeleteRequested(Editor editor) {
-                }
-            });
-        }
-
         // The editor is ready now so make it visible
         editorView.setEnabled(isEnabled());
         editorView.setVisibility(View.VISIBLE);
@@ -397,6 +373,17 @@
         ImplicitIntentsUtil.startActivityInApp(getActivity(), intent);
     }
 
+    @Override
+    public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta) {
+        final Activity activity = getActivity();
+        if (activity == null || activity.isFinishing()) {
+            return;
+        }
+        if (!mIsUserProfile) {
+            acquireAggregationSuggestions(activity, rawContactId, valuesDelta);
+        }
+    }
+
     private CompactRawContactsEditorView getContent() {
         return (CompactRawContactsEditorView) mContent;
     }
diff --git a/src/com/android/contacts/editor/CompactRawContactsEditorView.java b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
index 1287d47..cb605c6 100644
--- a/src/com/android/contacts/editor/CompactRawContactsEditorView.java
+++ b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
@@ -64,6 +64,54 @@
          * Invoked when the compact editor should be expanded to show all fields.
          */
         public void onExpandEditor();
+
+        /**
+         * Invoked when the structured name editor field has changed.
+         *
+         * @param rawContactId The raw contact ID from the underlying {@link RawContactDelta}.
+         * @param valuesDelta The values from the underlying {@link RawContactDelta}.
+         */
+        public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta);
+    }
+
+    /**
+     * Marks a name as super primary when it is changed.
+     *
+     * This is for the case when two or more raw contacts with names are joined where neither is
+     * marked as super primary.  If the user hits back (which causes a save) after changing the
+     * name that was arbitrarily displayed, we want that to be the name that is used.
+     *
+     * Should only be set when a super primary name does not already exist since we only show
+     * one name field.
+     */
+    static final class NameEditorListener implements Editor.EditorListener {
+
+        private final ValuesDelta mValuesDelta;
+        private final long mRawContactId;
+        private final Listener mListener;
+
+        public NameEditorListener(ValuesDelta valuesDelta, long rawContactId,
+                Listener listener) {
+            mValuesDelta = valuesDelta;
+            mRawContactId = rawContactId;
+            mListener = listener;
+        }
+
+        @Override
+        public void onRequest(int request) {
+            if (request == Editor.EditorListener.FIELD_CHANGED) {
+                mValuesDelta.setSuperPrimary(true);
+                if (mListener != null) {
+                    mListener.onNameFieldChanged(mRawContactId, mValuesDelta);
+                }
+            } else if (request == Editor.EditorListener.FIELD_TURNED_EMPTY) {
+                mValuesDelta.setSuperPrimary(false);
+            }
+        }
+
+        @Override
+        public void onDeleteRequested(Editor editor) {
+        }
     }
 
     private Listener mListener;
@@ -83,6 +131,9 @@
     private ViewGroup mOther;
     private View mMoreFields;
 
+    // The ValuesDelta for the non super primary name that was displayed to the user.
+    private ValuesDelta mNameValuesDelta;
+
     private long mPhotoRawContactId;
 
     public CompactRawContactsEditorView(Context context) {
@@ -122,6 +173,13 @@
     @Override
     public void onClick(View view) {
         if (view.getId() == R.id.more_fields && mListener != null ) {
+            // We mark the name that was displayed as super primary before expanding
+            // so that a save on the expanded editor (without a name change) does not
+            // cause the displayed name to change.
+            if (mNameValuesDelta != null) {
+                mNameValuesDelta.setSuperPrimary(true);
+            }
+
             mListener.onExpandEditor();
         }
     }
@@ -208,14 +266,14 @@
                 /* valuesDelta =*/ null, ViewIdGenerator.NO_VIEW_INDEX));
         mMaterialPalette = materialPalette;
 
-        addHeaderView(rawContactDeltas, viewIdGenerator);
+        addPhotoView(rawContactDeltas, viewIdGenerator);
         addStructuredNameView(rawContactDeltas);
         addEditorViews(rawContactDeltas);
         removeExtraEmptyTextFields(mPhoneNumbers);
         removeExtraEmptyTextFields(mEmails);
     }
 
-    private void addHeaderView(RawContactDeltaList rawContactDeltas,
+    private void addPhotoView(RawContactDeltaList rawContactDeltas,
             ViewIdGenerator viewIdGenerator) {
         for (RawContactDelta rawContactDelta : rawContactDeltas) {
             if (!rawContactDelta.isVisible()) {
@@ -243,24 +301,68 @@
 
     private void addStructuredNameView(RawContactDeltaList rawContactDeltas) {
         for (RawContactDelta rawContactDelta : rawContactDeltas) {
-            if (!rawContactDelta.isVisible()) {
-                continue;
-            }
+            if (!rawContactDelta.isVisible()) continue;
             final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
 
             // Make sure we have a structured name
             RawContactModifier.ensureKindExists(
                     rawContactDelta, accountType, StructuredName.CONTENT_ITEM_TYPE);
 
+            // Note use of pseudo mime type to get the DataKind and StructuredName to get value
             final DataKind dataKind = accountType.getKindForMimetype(
+                    DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
+            if (dataKind == null || !dataKind.editable) continue;
+
+            final ValuesDelta superPrimaryValuesDelta = getNonEmptySuperPrimaryValuesDeltas(
+                    rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
+            if (superPrimaryValuesDelta != null) {
+                // Our first preference is for a non-empty super primary name
+                mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
+                        superPrimaryValuesDelta, rawContactDelta, /* nameEditorListener =*/ null));
+                return;
+            }
+        }
+        // We didn't find a super primary name
+        for (RawContactDelta rawContactDelta : rawContactDeltas) {
+            if (!rawContactDelta.isVisible()) continue;
+            final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
+
+            final DataKind dataKind = accountType.getKindForMimetype(
+                    DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
+            if (dataKind == null || !dataKind.editable) continue;
+
+            final List<ValuesDelta> nonEmptyValuesDeltas = getNonEmptyValuesDeltas(
+                    rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind);
+            if (nonEmptyValuesDeltas != null && !nonEmptyValuesDeltas.isEmpty()) {
+                // Take the first non-empty name, also make it super primary before expanding to the
+                // full editor (see #onCLick) so that name does not change if the user saves from
+                // the fully expanded editor.
+                mNameValuesDelta = nonEmptyValuesDeltas.get(0);
+                final NameEditorListener nameEditorListener = new NameEditorListener(
+                        mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
+                mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
+                        mNameValuesDelta, rawContactDelta, nameEditorListener));
+                return;
+            }
+        }
+        for (RawContactDelta rawContactDelta : rawContactDeltas) {
+            if (!rawContactDelta.isVisible()) continue;
+            final AccountType accountType = rawContactDelta.getAccountType(mAccountTypeManager);
+
+            final DataKind dataKind = accountType.getKindForMimetype(
+                    DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME);
+            if (dataKind == null || !dataKind.editable) continue;
+
+            // Fall back to the first entry
+            final ArrayList<ValuesDelta> valuesDeltas = rawContactDelta.getMimeEntries(
                     StructuredName.CONTENT_ITEM_TYPE);
-            if (dataKind != null) {
-                final ValuesDelta valuesDelta = rawContactDelta.getPrimaryEntry(dataKind.mimeType);
-                if (valuesDelta != null) {
-                    mNames.addView(inflateStructuredNameEditorView(
-                            mNames, accountType, valuesDelta, rawContactDelta));
-                    return;
-                }
+            if (valuesDeltas != null && !valuesDeltas.isEmpty()) {
+                mNameValuesDelta = valuesDeltas.get(0);
+                final NameEditorListener nameEditorListener = new NameEditorListener(
+                        mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
+                mNames.addView(inflateStructuredNameEditorView(mNames, accountType,
+                        mNameValuesDelta, rawContactDelta, nameEditorListener));
+                return;
             }
         }
     }
@@ -281,20 +383,24 @@
                 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)
                         || StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)
                         || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    // Photos and name are handled separately; group membership is not supported
+                    // Photos and structured names are handled separately and
+                    // group membership is not supported
                     continue;
                 } else if (DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
-                    // Use the StructuredName mime type to get values
-                    if (hasNonEmptyPrimaryValuesDelta(
+                    // Only add phonetic names if there is a non-empty one. Note the use of
+                    // StructuredName mimeType below, even though we matched a pseudo mime type.
+                    if (hasNonEmptyValuesDelta(
                             rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind)) {
-                        final ValuesDelta valuesDelta = rawContactDelta.getPrimaryEntry(
-                                StructuredName.CONTENT_ITEM_TYPE);
-                        mPhoneticNames.addView(inflatePhoneticNameEditorView(
-                                mPhoneticNames, accountType, valuesDelta, rawContactDelta));
+                        for (ValuesDelta valuesDelta : getNonEmptyValuesDeltas(
+                                rawContactDelta, StructuredName.CONTENT_ITEM_TYPE, dataKind)) {
+                            mPhoneticNames.addView(inflatePhoneticNameEditorView(
+                                    mPhoneticNames, accountType, valuesDelta, rawContactDelta));
+                        }
                     }
                 } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    // Only add nicknames if there is a non-empty one
                     if (hasNonEmptyValuesDelta(rawContactDelta, mimeType, dataKind)) {
-                        mNicknames.addView(inflateNicknameEditorView(
+                        mNicknames.addView(inflateKindSectionView(
                                 mNicknames, dataKind, rawContactDelta));
                     }
                 } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
@@ -357,7 +463,18 @@
         return !getNonEmptyValuesDeltas(rawContactDelta, mimeType, dataKind).isEmpty();
     }
 
-    private static List<ValuesDelta> getNonEmptyValuesDeltas(RawContactDelta rawContactDelta,
+    private static ValuesDelta getNonEmptySuperPrimaryValuesDeltas(RawContactDelta rawContactDelta,
+            String mimeType, DataKind dataKind) {
+        for (ValuesDelta valuesDelta : getNonEmptyValuesDeltas(
+                rawContactDelta, mimeType, dataKind)) {
+            if (valuesDelta.isSuperPrimary()) {
+                return valuesDelta;
+            }
+        }
+        return null;
+    }
+
+    static List<ValuesDelta> getNonEmptyValuesDeltas(RawContactDelta rawContactDelta,
             String mimeType, DataKind dataKind) {
         final List<ValuesDelta> result = new ArrayList<>();
         if (rawContactDelta == null) {
@@ -385,28 +502,14 @@
         return result;
     }
 
-    private static boolean hasNonEmptyPrimaryValuesDelta(RawContactDelta rawContactDelta,
-            String mimeType, DataKind dataKind) {
-        final ValuesDelta valuesDelta = rawContactDelta.getPrimaryEntry(mimeType);
-        if (valuesDelta == null) {
-            return false;
-        }
-        for (EditField editField : dataKind.fieldList) {
-            final String column = editField.column;
-            final String value = valuesDelta == null ? null : valuesDelta.getAsString(column);
-            log(Log.VERBOSE, "Field (primary) " + column + " empty=" + TextUtils.isEmpty(value) +
-                    " value=" + value);
-            if (!TextUtils.isEmpty(value)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private StructuredNameEditorView inflateStructuredNameEditorView(ViewGroup viewGroup,
-            AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta) {
+            AccountType accountType, ValuesDelta valuesDelta, RawContactDelta rawContactDelta,
+            NameEditorListener nameEditorListener) {
         final StructuredNameEditorView result = (StructuredNameEditorView) mLayoutInflater.inflate(
                 R.layout.structured_name_editor_view, viewGroup, /* attachToRoot =*/ false);
+        if (nameEditorListener != null) {
+            result.setEditorListener(nameEditorListener);
+        }
         result.setValues(
                 accountType.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME),
                 valuesDelta,
@@ -429,19 +532,6 @@
         return result;
     }
 
-    private KindSectionView inflateNicknameEditorView(ViewGroup viewGroup, DataKind dataKind,
-            RawContactDelta rawContactDelta) {
-        final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
-                R.layout.item_kind_section, viewGroup, /* attachToRoot =*/ false);
-        result.setState(
-                dataKind,
-                rawContactDelta,
-                /* readOnly =*/ false,
-                /* showOneEmptyEditor =*/ false,
-                mViewIdGenerator);
-        return result;
-    }
-
     private KindSectionView inflateKindSectionView(ViewGroup viewGroup, DataKind dataKind,
             RawContactDelta rawContactDelta) {
         final KindSectionView result = (KindSectionView) mLayoutInflater.inflate(
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index eb2de3a..96aaecd 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -22,6 +22,8 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.text.TextUtils;
 import android.util.Log;
@@ -241,18 +243,49 @@
             if (editor instanceof RawContactEditorView) {
                 final Activity activity = getActivity();
                 final RawContactEditorView rawContactEditor = (RawContactEditorView) editor;
-                EditorListener listener = new EditorListener() {
+                final ValuesDelta nameValuesDelta = rawContactEditor.getNameEditor().getValues();
+                final EditorListener structuredNameListener = new EditorListener() {
 
                     @Override
                     public void onRequest(int request) {
-                        if (activity.isFinishing()) { // Make sure activity is still running.
+                        // Make sure the activity is running
+                        if (activity.isFinishing()) {
                             return;
                         }
-                        if (request == EditorListener.FIELD_CHANGED && !isEditingUserProfile()) {
-                            acquireAggregationSuggestions(activity,
-                                    rawContactEditor.getNameEditor().getRawContactId(),
-                                    rawContactEditor.getNameEditor().getValues());
-                        } else if (request == EditorListener.EDITOR_FOCUS_CHANGED) {
+                        if (request == EditorListener.EDITOR_FOCUS_CHANGED) {
+                            adjustNameFieldsHintDarkness(rawContactEditor);
+                            return;
+                        }
+                        if (!isEditingUserProfile()) {
+                            if (request == EditorListener.FIELD_CHANGED) {
+                                if (!nameValuesDelta.isSuperPrimary()) {
+                                    unsetSuperPrimaryForAllNameEditors();
+                                    nameValuesDelta.setSuperPrimary(true);
+                                }
+                                acquireAggregationSuggestions(activity,
+                                        rawContactEditor.getNameEditor().getRawContactId(),
+                                        rawContactEditor.getNameEditor().getValues());
+                            } else if (request == EditorListener.FIELD_TURNED_EMPTY) {
+                                if (nameValuesDelta.isSuperPrimary()) {
+                                    nameValuesDelta.setSuperPrimary(false);
+                                }
+                            }
+                        }
+                    }
+
+                    @Override
+                    public void onDeleteRequested(Editor removedEditor) {
+                    }
+                };
+                final EditorListener otherNamesListener = new EditorListener() {
+
+                    @Override
+                    public void onRequest(int request) {
+                        // Make sure the activity is running
+                        if (activity.isFinishing()) {
+                            return;
+                        }
+                        if (request == EditorListener.EDITOR_FOCUS_CHANGED) {
                             adjustNameFieldsHintDarkness(rawContactEditor);
                         }
                     }
@@ -267,19 +300,19 @@
                     nameEditor.requestFocus();
                     mRequestFocus = false;
                 }
-                nameEditor.setEditorListener(listener);
+                nameEditor.setEditorListener(structuredNameListener);
                 if (!TextUtils.isEmpty(mDefaultDisplayName)) {
                     nameEditor.setDisplayName(mDefaultDisplayName);
                 }
 
                 final TextFieldsEditorView phoneticNameEditor =
                         rawContactEditor.getPhoneticNameEditor();
-                phoneticNameEditor.setEditorListener(listener);
+                phoneticNameEditor.setEditorListener(otherNamesListener);
                 rawContactEditor.setAutoAddToDefaultGroup(mAutoAddToDefaultGroup);
 
                 final TextFieldsEditorView nickNameEditor =
                         rawContactEditor.getNickNameEditor();
-                nickNameEditor.setEditorListener(listener);
+                nickNameEditor.setEditorListener(otherNamesListener);
 
                 if (isAggregationSuggestionRawContactId(rawContactId)) {
                     acquireAggregationSuggestions(activity,
@@ -305,6 +338,23 @@
         updatedExpandedEditorsMap();
     }
 
+    private void unsetSuperPrimaryForAllNameEditors() {
+        for (int i = 0; i < mContent.getChildCount(); i++) {
+            final View view = mContent.getChildAt(i);
+            if (view instanceof RawContactEditorView) {
+                final RawContactEditorView rawContactEditorView = (RawContactEditorView) view;
+                final StructuredNameEditorView nameEditorView =
+                        rawContactEditorView.getNameEditor();
+                if (nameEditorView != null) {
+                    final ValuesDelta valuesDelta = nameEditorView.getValues();
+                    if (valuesDelta != null) {
+                        valuesDelta.setSuperPrimary(false);
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Adjust how dark the hint text should be on all the names' text fields.
      *