Remember editor expansion state on rotates (E10)

* Prompt user about discarding changes when back pressed
  on editor (regressed in E5)
* Show a toast and close when no editors (i.e. input
  fields) can be bound.
* Try to clarify the different behavior of
  CompactSectionSectionView for names and groups.

Bug 23589603

Change-Id: If045ddb6d839574dc4109195b0d8841cd6083561
diff --git a/src/com/android/contacts/editor/CompactRawContactsEditorView.java b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
index 8ca6389..656f25f 100644
--- a/src/com/android/contacts/editor/CompactRawContactsEditorView.java
+++ b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
@@ -34,6 +34,8 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Event;
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
@@ -102,6 +104,11 @@
          */
         public void onRebindEditorsForNewContact(RawContactDelta oldState,
                 AccountWithDataSet oldAccount, AccountWithDataSet newAccount);
+
+        /**
+         * Invoked when no editors could be bound for the contact.
+         */
+        public void onBindEditorsFailed();
     }
 
     /** Used to sort entire kind sections. */
@@ -130,7 +137,7 @@
      *     <li>All names are together at the top.</li>
      *     <li>IM is moved up after addresses</li>
      *     <li>SIP addresses are moved to below phone numbers</li>
-     *     <li>Group membership is palced at the end</li>
+     *     <li>Group membership is placed at the end</li>
      * </ol>
      */
     private static final class MimeTypeComparator implements Comparator<String> {
@@ -240,13 +247,43 @@
             }
 
             // The primary account name should be before all others
-            if (isRawContactDelta1Primary) return 1;
-            if (isRawContactDelta2Primary) return -1;
+            if (isRawContactDelta1Primary) return -1;
+            if (isRawContactDelta2Primary) return 1;
 
            return mRawContactDeltaComparator.compare(rawContactDelta1, rawContactDelta2);
         }
     }
 
+    public static class SavedState extends BaseSavedState {
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
+
+        private boolean mIsExpanded;
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        private SavedState(Parcel in) {
+            super(in);
+            mIsExpanded = in.readInt() != 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeInt(mIsExpanded ? 1 : 0);
+        }
+    }
+
     private CompactRawContactsEditorView.Listener mListener;
 
     private AccountTypeManager mAccountTypeManager;
@@ -277,6 +314,7 @@
     private Map<String,List<CompactKindSectionView>> mKindSectionViewsMap = new HashMap<>();
     private View mMoreFields;
 
+    private boolean mIsExpanded;
     private long mPhotoRawContactId;
 
     public CompactRawContactsEditorView(Context context) {
@@ -322,19 +360,7 @@
     @Override
     public void onClick(View view) {
         if (view.getId() == R.id.more_fields) {
-            // Stop hiding empty editors and allow the user to enter values for all kinds now
-            for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
-                final CompactKindSectionView kindSectionView =
-                        (CompactKindSectionView) mKindSectionViews.getChildAt(i);
-                kindSectionView.setHideWhenEmpty(false);
-                // Except the user is never allowed to add new names
-                final String mimeType = kindSectionView.getMimeType();
-                if (!StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    kindSectionView.setShowOneEmptyEditor(true);
-                }
-                kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ false);
-            }
-
+            showMoreFields();
             updateMoreFieldsButton();
         }
     }
@@ -348,6 +374,28 @@
         }
     }
 
+    @Override
+    public Parcelable onSaveInstanceState() {
+        final Parcelable superState = super.onSaveInstanceState();
+        final SavedState savedState = new SavedState(superState);
+        savedState.mIsExpanded = mIsExpanded;
+        return savedState;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        if(!(state instanceof SavedState)) {
+            super.onRestoreInstanceState(state);
+            return;
+        }
+        final SavedState savedState = (SavedState) state;
+        super.onRestoreInstanceState(savedState.getSuperState());
+        mIsExpanded = savedState.mIsExpanded;
+        if (mIsExpanded && !mKindSectionDataMap.isEmpty()) {
+            showMoreFields();
+        }
+    }
+
     /**
      * Pass through to {@link CompactPhotoEditorView#setPhotoHandler}.
      */
@@ -398,6 +446,12 @@
         for (CompactKindSectionView kindSectionView : kindSectionViews) {
             kindSectionView.setGroupMetaData(groupMetaData);
         }
+
+        // Groups metadata may be set after we restore expansion state so just do it again
+        if (mIsExpanded) {
+            showMoreFields();
+        }
+        updateMoreFieldsButton();
     }
 
     public void setState(RawContactDeltaList rawContactDeltas,
@@ -423,11 +477,13 @@
         // Parse the given raw contact deltas
         if (rawContactDeltas == null || rawContactDeltas.isEmpty()) {
             elog("No raw contact deltas");
+            if (mListener != null) mListener.onBindEditorsFailed();
             return;
         }
         parseRawContactDeltas(rawContactDeltas, mPrimaryAccount);
-        if (mKindSectionDataMap == null || mKindSectionDataMap.isEmpty()) {
+        if (mKindSectionDataMap.isEmpty()) {
             elog("No kind section data parsed from RawContactDelta(s)");
+            if (mListener != null) mListener.onBindEditorsFailed();
             return;
         }
 
@@ -437,6 +493,7 @@
         addAccountInfo();
         addPhotoView();
         addKindSectionViews();
+
         updateMoreFieldsButton();
     }
 
@@ -509,10 +566,9 @@
                         new KindSectionData(accountType, dataKind, rawContactDelta);
                 kindSectionDataList.add(kindSectionData);
 
-                // Note we must create a nickname entry on inserts
+                // Note we must create nickname entries
                 if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)
-                        && kindSectionData.getValuesDeltas().isEmpty()
-                        && mHasNewContact) {
+                        && kindSectionData.getValuesDeltas().isEmpty()) {
                     RawContactModifier.insertChild(rawContactDelta, dataKind);
                 }
 
@@ -659,10 +715,6 @@
     private Pair<KindSectionData,ValuesDelta> getPrimaryKindSectionData(long id) {
         final String mimeType = Photo.CONTENT_ITEM_TYPE;
         final List<KindSectionData> kindSectionDataList = mKindSectionDataMap.get(mimeType);
-        if (kindSectionDataList == null || kindSectionDataList.isEmpty()) {
-            wlog("photo: no kind section data parsed");
-            return null;
-        }
 
         KindSectionData resultKindSectionData = null;
         ValuesDelta resultValuesDelta = null;
@@ -779,12 +831,22 @@
             Collections.sort(kindSectionDataList, new EditorComparator(getContext()));
         }
 
-        kindSectionView.setState(kindSectionDataList, /* readOnly =*/ false, mViewIdGenerator,
-                mListener);
+        kindSectionView.setState(kindSectionDataList, mViewIdGenerator, mListener);
 
         return kindSectionView;
     }
 
+    private void showMoreFields() {
+        // Stop hiding empty editors and allow the user to enter values for all kinds now
+        for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {
+            final CompactKindSectionView kindSectionView =
+                    (CompactKindSectionView) mKindSectionViews.getChildAt(i);
+            kindSectionView.setHideWhenEmpty(false);
+            kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
+        }
+        mIsExpanded = true;
+    }
+
     private void updateMoreFieldsButton() {
         // If any kind section views are hidden then show the link
         for (int i = 0; i < mKindSectionViews.getChildCount(); i++) {