am 06f73a15: Ignore new raw contact display names when saving contacts

* commit '06f73a15ed812d99ea28d836b02a5538bb1200c9':
  Ignore new raw contact display names when saving contacts
diff --git a/src/com/android/contacts/editor/CompactContactEditorFragment.java b/src/com/android/contacts/editor/CompactContactEditorFragment.java
index 9eecaab..23d5bb3 100644
--- a/src/com/android/contacts/editor/CompactContactEditorFragment.java
+++ b/src/com/android/contacts/editor/CompactContactEditorFragment.java
@@ -36,8 +36,6 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
@@ -201,7 +199,11 @@
         // Add input fields for the loaded Contact
         final CompactRawContactsEditorView editorView = getContent();
         editorView.setListener(this);
-        editorView.setState(mState, getMaterialPalette(), mViewIdGenerator, mPhotoId, mNameId);
+        editorView.setState(mState, getMaterialPalette(), mViewIdGenerator, mPhotoId, mNameId,
+                mReadOnlyDisplayName);
+        if (mReadOnlyDisplayName != null) {
+            mReadOnlyNameEditorView = editorView.getDefaultNameEditorView();
+        }
 
         // Set up the photo widget
         mPhotoHandler = createPhotoHandler();
diff --git a/src/com/android/contacts/editor/CompactRawContactsEditorView.java b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
index 669e400..49cd2a2 100644
--- a/src/com/android/contacts/editor/CompactRawContactsEditorView.java
+++ b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
@@ -137,6 +137,8 @@
 
     private long mPhotoRawContactId;
 
+    private StructuredNameEditorView mDefaultNameEditorView;
+
     public CompactRawContactsEditorView(Context context) {
         super(context);
     }
@@ -233,6 +235,10 @@
         return mPhotoRawContactId;
     }
 
+    public StructuredNameEditorView getDefaultNameEditorView() {
+        return mDefaultNameEditorView;
+    }
+
     public StructuredNameEditorView getStructuredNameEditorView() {
         // We only ever show one StructuredName
         return mNames.getChildCount() == 0
@@ -254,9 +260,13 @@
         return mNames.getChildAt(0).findViewById(R.id.anchor_view);
     }
 
+    /**
+     * @param readOnlyDisplayName The display name to set on the new raw contact created in order
+     *         to edit a read-only contact.
+     */
     public void setState(RawContactDeltaList rawContactDeltas,
-            MaterialColorMapUtils.MaterialPalette materialPalette,
-            ViewIdGenerator viewIdGenerator, long photoId, long nameId) {
+            MaterialColorMapUtils.MaterialPalette materialPalette, ViewIdGenerator viewIdGenerator,
+            long photoId, long nameId, String readOnlyDisplayName) {
         mNames.removeAllViews();
         mPhoneticNames.removeAllViews();
         mNicknames.removeAllViews();
@@ -275,7 +285,7 @@
 
         vlog("Setting compact editor state from " + rawContactDeltas);
         addPhotoView(rawContactDeltas, viewIdGenerator, photoId);
-        addStructuredNameView(rawContactDeltas, nameId);
+        addStructuredNameView(rawContactDeltas, nameId, readOnlyDisplayName);
         addEditorViews(rawContactDeltas);
         removeExtraEmptyTextFields(mPhoneNumbers);
         removeExtraEmptyTextFields(mEmails);
@@ -364,7 +374,42 @@
         mPhoto.setVisibility(View.GONE);
     }
 
-    private void addStructuredNameView(RawContactDeltaList rawContactDeltas, long nameId) {
+    private void addStructuredNameView(RawContactDeltaList rawContactDeltas, long nameId,
+            String readOnlyDisplayName) {
+        // If we're editing a read-only contact we want to display the name from the read-only
+        // contact in a structured name editor backed by the new raw contact that was created.
+        // The new raw contact is writable and merging it with the read-only contact allows us
+        // to edit the read-only contact. See go/editing-read-only-contacts
+        if (!TextUtils.isEmpty(readOnlyDisplayName)) {
+            for (RawContactDelta rawContactDelta : rawContactDeltas) {
+                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);
+
+                if (accountType.areContactsWritable()) {
+                    for (ValuesDelta valuesDelta : rawContactDelta.getMimeEntries(
+                            StructuredName.CONTENT_ITEM_TYPE)) {
+                        if (valuesDelta != null) {
+                            mNameValuesDelta = valuesDelta;
+                            final NameEditorListener nameEditorListener = new NameEditorListener(
+                                    mNameValuesDelta, rawContactDelta.getRawContactId(), mListener);
+                            final StructuredNameEditorView nameEditorView =
+                                    inflateStructuredNameEditorView(mNames, accountType,
+                                            mNameValuesDelta, rawContactDelta, nameEditorListener,
+                                            !accountType.areContactsWritable());
+                            nameEditorView.setDisplayName(readOnlyDisplayName);
+                            mNames.addView(nameEditorView);
+                            mDefaultNameEditorView = nameEditorView;
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+
         // Look for a match for the name ID that was passed in
         for (RawContactDelta rawContactDelta : rawContactDeltas) {
             if (!rawContactDelta.isVisible()) continue;
diff --git a/src/com/android/contacts/editor/ContactEditorBaseFragment.java b/src/com/android/contacts/editor/ContactEditorBaseFragment.java
index a103fd3..e262cee 100644
--- a/src/com/android/contacts/editor/ContactEditorBaseFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorBaseFragment.java
@@ -378,7 +378,11 @@
     //
 
     // Used to pre-populate the editor with a display name when a user edits a read-only contact.
-    protected String mDefaultDisplayName;
+    protected String mReadOnlyDisplayName;
+
+    // The name editor view for the new raw contact that was created so that the user can
+    // edit a read-only contact (to which the new raw contact was joined)
+    protected StructuredNameEditorView mReadOnlyNameEditorView;
 
     /**
      * The contact data loader listener.
@@ -917,7 +921,35 @@
 
         mStatus = Status.SAVING;
 
-        if (!hasPendingChanges()) {
+        // Determine if changes were made in the editor that need to be saved
+        // See go/editing-read-only-contacts
+        boolean hasPendingChanges;
+        if (mReadOnlyNameEditorView == null || mReadOnlyDisplayName == null) {
+            hasPendingChanges = hasPendingChanges();
+        } else {
+            // We created a new raw contact delta with a default display name. We must test for
+            // pending changes while ignoring the default display name.
+            final String displayName = mReadOnlyNameEditorView.getDisplayName();
+            if (mReadOnlyDisplayName.equals(displayName)) {
+                // The user did not modify the default display name, erase it and
+                // check if the user made any other changes
+                mReadOnlyNameEditorView.setDisplayName(null);
+                if (hasPendingChanges()) {
+                    // Other changes were made to the aggregate contact, restore
+                    // the display name and proceed.
+                    mReadOnlyNameEditorView.setDisplayName(displayName);
+                    hasPendingChanges = true;
+                } else {
+                    // No other changes were made to the aggregate contact. Don't add back
+                    // the displayName so that a "bogus" contact is not created.
+                    hasPendingChanges = false;
+                }
+            } else {
+                hasPendingChanges = true;
+            }
+        }
+
+        if (!hasPendingChanges) {
             if (mLookupUri == null && saveMode == SaveMode.RELOAD) {
                 // We don't have anything to save and there isn't even an existing contact yet.
                 // Nothing to do, simply go back to editing mode
@@ -1120,7 +1152,7 @@
             }
         }
 
-        String displayName = null;
+        String readOnlyDisplayName = null;
         // Check for writable raw contacts.  If there are none, then we need to create one so user
         // can edit.  For the user profile case, there is already an editable contact.
         if (!contact.isUserProfile() && !contact.isWritableContact(mContext)) {
@@ -1128,12 +1160,13 @@
 
             // This is potentially an asynchronous call and will add deltas to list.
             selectAccountAndCreateContact();
-            displayName = contact.getDisplayName();
+
+            readOnlyDisplayName = contact.getDisplayName();
         }
 
-        // This also adds deltas to list
-        // If displayName is null at this point it is simply ignored later on by the editor.
-        setStateForExistingContact(displayName, contact.isUserProfile(), mRawContacts);
+        // This also adds deltas to list.  If readOnlyDisplayName is null at this point it is
+        // simply ignored later on by the editor.
+        setStateForExistingContact(readOnlyDisplayName, contact.isUserProfile(), mRawContacts);
     }
 
     /**
@@ -1202,10 +1235,10 @@
     /**
      * Prepare {@link #mState} for an existing contact.
      */
-    protected void setStateForExistingContact(String displayName, boolean isUserProfile,
+    protected void setStateForExistingContact(String readOnlyDisplayName, boolean isUserProfile,
             ImmutableList<RawContact> rawContacts) {
         setEnabled(true);
-        mDefaultDisplayName = displayName;
+        mReadOnlyDisplayName = readOnlyDisplayName;
 
         mState.addAll(rawContacts.iterator());
         setIntentExtras(mIntentExtras);
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 3ad5261..7b17d63 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -169,7 +169,7 @@
             mState = new RawContactDeltaList();
             setStateForNewContact(newAccount, newAccountType, oldState, oldAccountType);
             if (mIsEdit) {
-                setStateForExistingContact(mDefaultDisplayName, mIsUserProfile, mRawContacts);
+                setStateForExistingContact(mReadOnlyDisplayName, mIsUserProfile, mRawContacts);
             }
         }
     }
@@ -293,8 +293,10 @@
 
                 final StructuredNameEditorView nameEditor = rawContactEditor.getNameEditor();
                 nameEditor.setEditorListener(structuredNameListener);
-                if (!TextUtils.isEmpty(mDefaultDisplayName)) {
-                    nameEditor.setDisplayName(mDefaultDisplayName);
+                if (TextUtils.isEmpty(nameEditor.getDisplayName()) &&
+                        !TextUtils.isEmpty(mReadOnlyDisplayName)) {
+                    nameEditor.setDisplayName(mReadOnlyDisplayName);
+                    mReadOnlyNameEditorView = nameEditor;
                 }
 
                 rawContactEditor.setAutoAddToDefaultGroup(mAutoAddToDefaultGroup);