Collapse extra raw contact editors

There are now two different header styles
1) When creating a new raw contact: if there are multiple
 accounts to create the raw contact under, then show a
 header with an acconut selector
2) Otherwise, show a visually distinct account header without
 a drop down account selector. If there are multiple raw
 contacts, this header also lets you collapse raw contacts

Typical users, who only have a single writeable account, will
never see header (1). This is fortunate, since switching between
the two header styles is a bit visually inconsistent.

Bug: 18004959
Change-Id: I133a7355a220f20b55657a6c1a51ff44665ea3b9
diff --git a/res/drawable-hdpi/ic_account_circle_black_24dp.png b/res/drawable-hdpi/ic_account_circle_black_24dp.png
new file mode 100644
index 0000000..ba5a509
--- /dev/null
+++ b/res/drawable-hdpi/ic_account_circle_black_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_account_circle_black_24dp.png b/res/drawable-mdpi/ic_account_circle_black_24dp.png
new file mode 100644
index 0000000..0c1202d
--- /dev/null
+++ b/res/drawable-mdpi/ic_account_circle_black_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_account_circle_black_24dp.png b/res/drawable-xhdpi/ic_account_circle_black_24dp.png
new file mode 100644
index 0000000..f26b201
--- /dev/null
+++ b/res/drawable-xhdpi/ic_account_circle_black_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_account_circle_black_24dp.png b/res/drawable-xxhdpi/ic_account_circle_black_24dp.png
new file mode 100644
index 0000000..3cc0a63
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_account_circle_black_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_account_circle_black_24dp.png b/res/drawable-xxxhdpi/ic_account_circle_black_24dp.png
new file mode 100644
index 0000000..c6b56c3
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_account_circle_black_24dp.png
Binary files differ
diff --git a/res/layout/editor_account_header_expandable.xml b/res/layout/editor_account_header_expandable.xml
new file mode 100644
index 0000000..5d347f0
--- /dev/null
+++ b/res/layout/editor_account_header_expandable.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Header at the top of a raw contact editor. This is clickable to expand/collapse the editor. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/account_header_container"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:background="?android:attr/selectableItemBackground"
+    android:paddingStart="16dp"
+    android:focusable="true"
+    >
+
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:paddingBottom="24dp"
+        android:paddingTop="24dp"
+        android:orientation="vertical"
+        >
+
+        <TextView
+            android:id="@+id/account_type"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textSize="16sp"
+            android:singleLine="true"
+            android:textColor="@color/primary_text_color"
+            android:ellipsize="end"
+            />
+
+        <TextView
+            android:id="@+id/account_name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textSize="14sp"
+            android:singleLine="true"
+            android:textColor="@color/secondary_text_color"
+            android:ellipsize="end"
+            />
+
+    </LinearLayout>
+
+    <ImageView
+        android:id="@+id/expand_account_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:layout_gravity="center_vertical|end"
+        android:clickable="false"
+        android:paddingLeft="@dimen/editor_round_button_padding_left"
+        android:paddingRight="@dimen/editor_round_button_padding_right"
+        android:paddingStart="@dimen/editor_round_button_padding_left"
+        android:paddingEnd="@dimen/editor_round_button_padding_right"
+        android:paddingTop="@dimen/editor_round_button_padding_top"
+        android:paddingBottom="@dimen/editor_round_button_padding_bottom"
+        />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/editor_account_header_with_dropdown.xml b/res/layout/editor_account_selector.xml
similarity index 61%
rename from res/layout/editor_account_header_with_dropdown.xml
rename to res/layout/editor_account_selector.xml
index 015358e..2f883f2 100644
--- a/res/layout/editor_account_header_with_dropdown.xml
+++ b/res/layout/editor_account_selector.xml
@@ -14,19 +14,24 @@
      limitations under the License.
 -->
 
+<!-- Header at the top of a raw contact editor. This allows users to change the account that
+    the raw contact is saved in. -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/account_container"
+    android:id="@+id/account_selector_container"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:minHeight="48dip"
-    android:background="#EEEEEE"
+    android:layout_marginBottom="@dimen/editor_padding_between_editor_views"
     android:orientation="horizontal"
-    android:gravity="center_vertical"
-    android:paddingLeft="@dimen/account_container_left_padding"
-    android:paddingRight="28dip"
-    android:paddingStart="@dimen/account_container_left_padding"
-    android:paddingEnd="28dip">
+    android:paddingTop="16dp"
+    android:visibility="gone" >
+
+    <ImageView
+        android:id="@+id/kind_icon"
+        android:src="@drawable/ic_account_circle_black_24dp"
+        android:contentDescription="@string/header_account_entry"
+        style="@style/EditKindIconStyle" />
 
     <LinearLayout
         android:id="@+id/account"
@@ -34,42 +39,29 @@
         android:layout_width="0dip"
         android:layout_weight="1"
         android:orientation="vertical"
-        style="?android:attr/spinnerStyle">
+        android:layout_marginEnd="48dp"
+        style="@android:style/Widget.Material.Spinner.Underlined">
 
         <TextView
-            android:id="@+id/account_type"
+            android:id="@+id/account_type_selector"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textSize="16sp"
             android:singleLine="true"
             android:textColor="@color/primary_text_color"
             android:ellipsize="end" />
 
         <TextView
-             android:id="@+id/account_name"
+             android:id="@+id/account_name_selector"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:paddingRight="8dip"
              android:paddingEnd="8dip"
-             android:textAppearance="?android:attr/textAppearanceSmall"
+             android:textSize="14sp"
              android:singleLine="true"
-             android:textColor="@color/primary_text_color"
+             android:textColor="@color/secondary_text_color"
              android:ellipsize="end" />
 
     </LinearLayout>
 
-    <FrameLayout
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_marginLeft="10dip"
-        android:layout_marginStart="10dip">
-
-        <ImageView
-             android:id="@+id/account_icon"
-             android:layout_width="32dip"
-             android:layout_height="32dip"
-             android:layout_gravity="center_vertical" />
-
-    </FrameLayout>
-
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/expanding_entry_card_view.xml b/res/layout/expanding_entry_card_view.xml
index 6b89759..7eacd1f 100644
--- a/res/layout/expanding_entry_card_view.xml
+++ b/res/layout/expanding_entry_card_view.xml
@@ -33,8 +33,8 @@
     <View
         android:id="@+id/title_separator"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/expanding_entry_card_item_separator_height"
-        android:background="@color/expanding_entry_card_item_separator_color"
+        android:layout_height="@dimen/divider_line_height"
+        android:background="@color/divider_line_color_light"
         android:visibility="gone" />
 
     <LinearLayout
diff --git a/res/layout/quickcontact_expanding_entry_card_button.xml b/res/layout/quickcontact_expanding_entry_card_button.xml
index 1f0c724..b198b06 100644
--- a/res/layout/quickcontact_expanding_entry_card_button.xml
+++ b/res/layout/quickcontact_expanding_entry_card_button.xml
@@ -22,8 +22,8 @@
 
     <View
         android:layout_width="match_parent"
-        android:layout_height="@dimen/expanding_entry_card_item_separator_height"
-        android:background="@color/expanding_entry_card_item_separator_color" />
+        android:layout_height="@dimen/divider_line_height"
+        android:background="@color/divider_line_color_light" />
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/res/layout/raw_contact_editor_view.xml b/res/layout/raw_contact_editor_view.xml
index ace6c8f..54aef1c 100644
--- a/res/layout/raw_contact_editor_view.xml
+++ b/res/layout/raw_contact_editor_view.xml
@@ -19,39 +19,51 @@
     android:id="@+id/body"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:paddingTop="@dimen/editor_padding_top" >
+    android:orientation="vertical" >
 
+    <!-- There are two mutually exclusive account headers that look significantly different.
+        The editor_account_selector is used when an account needs to be chosen. -->
     <include
-        layout="@layout/editor_account_header_with_dropdown" />
-
-    <Space
-        android:layout_height="8dip"
-        android:layout_width="match_parent" />
-
+        layout="@layout/editor_account_header_expandable" />
     <include
-        android:id="@+id/edit_name"
-        layout="@layout/structured_name_editor_view" />
-
-    <include
-        android:id="@+id/edit_phonetic_name"
-        layout="@layout/phonetic_name_editor_view" />
-
-    <include
-        android:id="@+id/edit_nick_name"
-        layout="@layout/item_kind_section" />
-
-    <include
-        android:id="@+id/edit_photo"
-        android:layout_marginRight="8dip"
-        android:layout_marginEnd="8dip"
-        layout="@layout/item_photo_editor" />
+        layout="@layout/editor_account_selector" />
 
     <LinearLayout
-        android:id="@+id/sect_fields"
+        android:id="@+id/collapsable_section"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:layout_marginBottom="16dip"/>
+        android:orientation="vertical" >
+
+        <include
+            android:id="@+id/edit_name"
+            layout="@layout/structured_name_editor_view" />
+
+        <include
+            android:id="@+id/edit_phonetic_name"
+            layout="@layout/phonetic_name_editor_view" />
+
+        <include
+            android:id="@+id/edit_nick_name"
+            layout="@layout/item_kind_section" />
+
+        <include
+            android:id="@+id/edit_photo"
+            android:layout_marginRight="8dip"
+            android:layout_marginEnd="8dip"
+            layout="@layout/item_photo_editor" />
+
+        <LinearLayout
+            android:id="@+id/sect_fields"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_marginBottom="16dip"/>
+
+    </LinearLayout>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/divider_line_height"
+        android:background="@color/divider_line_color_light" />
 
 </com.android.contacts.editor.RawContactEditorView>
diff --git a/res/layout/raw_contact_readonly_editor_view.xml b/res/layout/raw_contact_readonly_editor_view.xml
index a1eab90..f85971f 100644
--- a/res/layout/raw_contact_readonly_editor_view.xml
+++ b/res/layout/raw_contact_readonly_editor_view.xml
@@ -18,57 +18,69 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:paddingTop="@dimen/editor_padding_top">
+    android:orientation="vertical" >
 
     <include
-        layout="@layout/editor_account_header" />
+        layout="@layout/editor_account_header_expandable" />
 
-    <!-- Want 16dp of apparent top padding. Since EditText has 10dp of inset/padding built in,
-        only set marginTop=6dp. -->
     <LinearLayout
+        android:id="@+id/collapsable_section"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:minHeight="@dimen/editor_min_line_item_height"
-        android:layout_marginBottom="@dimen/editor_padding_between_editor_views"
-        android:layout_marginTop="6dp"
-        android:orientation="horizontal">
+        android:orientation="vertical" >
 
-        <ImageView
-            android:id="@+id/kind_icon"
-            android:src="@drawable/ic_person_black_24dp"
-            android:contentDescription="@string/header_name_entry"
-            style="@style/EditKindIconStyle" />
-
-        <EditText
-            android:id="@+id/read_only_name"
+        <!-- Want 16dp of apparent top padding. Since EditText has 10dp of inset/padding built in,
+            only set marginTop=6dp. -->
+        <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_marginRight="@dimen/editor_delete_button_width"
-            android:singleLine="true"
-            android:textSize="@dimen/editor_form_text_size"
-            android:textColor="?android:attr/textColorSecondary"
-            android:enabled="false"/>
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/editor_min_line_item_height"
+            android:layout_marginBottom="@dimen/editor_padding_between_editor_views"
+            android:layout_marginTop="6dp"
+            android:orientation="horizontal">
+
+            <ImageView
+                android:id="@+id/kind_icon"
+                android:src="@drawable/ic_person_black_24dp"
+                android:contentDescription="@string/header_name_entry"
+                style="@style/EditKindIconStyle" />
+
+            <EditText
+                android:id="@+id/read_only_name"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_marginRight="@dimen/editor_delete_button_width"
+                android:singleLine="true"
+                android:textSize="@dimen/editor_form_text_size"
+                android:textColor="?android:attr/textColorSecondary"
+                android:enabled="false"/>
+
+        </LinearLayout>
+
+        <include
+            android:id="@+id/edit_photo"
+            layout="@layout/item_photo_editor_readonly" />
+
+        <Button
+            android:id="@+id/button_edit_externally"
+            android:text="@string/edit_contact"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="@dimen/editor_padding_below_photo"
+            android:layout_marginEnd="13dip"
+            android:layout_marginStart="13dip"/>
+
+        <LinearLayout android:id="@+id/sect_general"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"/>
 
     </LinearLayout>
 
-    <include
-        android:id="@+id/edit_photo"
-        layout="@layout/item_photo_editor_readonly" />
-
-    <Button
-        android:id="@+id/button_edit_externally"
-        android:text="@string/edit_contact"
+    <View
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="10dp"
-        android:layout_marginBottom="@dimen/editor_padding_below_photo"
-        android:layout_marginEnd="13dip"
-        android:layout_marginStart="13dip"/>
-
-    <LinearLayout android:id="@+id/sect_general"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"/>
+        android:layout_height="@dimen/divider_line_height"
+        android:background="@color/divider_line_color_light" />
 
 </com.android.contacts.editor.RawContactReadOnlyEditorView>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index daea14b..d9ac84a 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -37,8 +37,8 @@
 
     <color name="floating_action_button_icon_color">@color/contacts_accent_color</color>
 
-    <!-- Color of the separator between entries in an ExpandingEntryCardView -->
-    <color name="expanding_entry_card_item_separator_color">#e4e4e4</color>
+    <!-- Horizontal separator line should be 12% dark in the light theme. -->
+    <color name="divider_line_color_light">#e0e0e0</color>
 
     <!-- Color of the text on an ExpandingEntryCard button -->
     <color name="expanding_entry_card_button_text_color">@android:color/black</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1a77057..b31b5b9 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -25,9 +25,6 @@
     <!-- The ratio of width:height for the contact's photo in landscape -->
     <item name="quickcontact_landscape_photo_ratio" type="dimen" format="float">0.7</item>
 
-    <!-- Top padding of the entire contact editor  -->
-    <dimen name="editor_padding_top">0dip</dimen>
-
     <!-- Padding of the rounded plus/minus/expand/collapse buttons in the editor  -->
     <dimen name="editor_round_button_padding_left">16dip</dimen>
     <dimen name="editor_round_button_padding_right">16dip</dimen>
@@ -174,8 +171,9 @@
     <!-- Extra top padding if the title is set to null -->
     <dimen name="expanding_entry_card_null_title_top_extra_padding">2dp</dimen>
 
-    <!-- Height of the separator between entries in an ExpandingEntryCardView -->
-    <dimen name="expanding_entry_card_item_separator_height">1dp</dimen>
+    <!-- Height of the separator between entries in an ExpandingEntryCardView and contact editor. -->
+    <dimen name="divider_line_height">1dp</dimen>
+
     <!-- Dimensions for an entry in ExpandingEntryCardView -->
     <dimen name="expanding_entry_card_item_padding_start">20dp</dimen>
     <dimen name="expanding_entry_card_item_padding_end">20dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5d59f32..e11a519 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -705,6 +705,8 @@
     <string name="header_event_entry">Event</string>
     <!-- Header for the Relation entry [CHAR LIMIT=40] -->
     <string name="header_relation_entry">Relation</string>
+    <!-- Content description for the account field header image. Example accounts listed in this field: Google, Hotmail and Exchange. [CHAR LIMIT=NONE] -->
+    <string name="header_account_entry">Account</string>
     <!-- Content description for the name fields header entry [CHAR LIMIT=NONE] -->
     <string name="header_name_entry">Name</string>
     <!-- Content description for the email fields header entry [CHAR LIMIT=NONE] -->
@@ -714,6 +716,11 @@
     <!-- Content description for the camera icon beside the photo section in the Raw Contact Editor [CHAR LIMIT=NONE] -->
     <string name="header_photo_entry">Photo</string>
 
+    <!-- Content description for the expand button inside the raw contact editor's header. [CHAR LIMIT=NONE] -->
+    <string name="content_description_expand_editor">Click to expand contact editor.</string>
+    <!-- Content description for the collapse button inside the raw contact editor's header. [CHAR LIMIT=NONE] -->
+    <string name="content_description_collapse_editor">Click to collapse contact editor.</string>
+
     <!-- Content description for directions secondary button [CHAR LIMIT=NONE] -->
     <string name="content_description_directions">directions to location</string>
 
diff --git a/src/com/android/contacts/editor/BaseRawContactEditorView.java b/src/com/android/contacts/editor/BaseRawContactEditorView.java
index 28261cf..473f2ab 100644
--- a/src/com/android/contacts/editor/BaseRawContactEditorView.java
+++ b/src/com/android/contacts/editor/BaseRawContactEditorView.java
@@ -22,10 +22,13 @@
 import android.net.Uri;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.provider.ContactsContract.Data;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.contacts.R;
 import com.android.contacts.common.model.RawContactDelta;
@@ -33,6 +36,7 @@
 import com.android.contacts.common.model.RawContactModifier;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountType.EditType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
 
 /**
  * Base view that provides common code for the editor interaction for a specific
@@ -47,10 +51,18 @@
 
     private PhotoEditorView mPhoto;
 
-    private View mBody;
-    private View mDivider;
+    private View mAccountHeaderContainer;
+    private ImageView mExpandAccountButton;
+    private LinearLayout mCollapsibleSection;
+    private TextView mAccountName;
+    private TextView mAccountType;
 
-    private boolean mExpanded = true;
+    protected Listener mListener;
+
+    public interface Listener {
+        void onExternalEditorRequest(AccountWithDataSet account, Uri uri);
+        void onEditorExpansionChanged();
+    }
 
     public BaseRawContactEditorView(Context context) {
         super(context);
@@ -64,16 +76,27 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mBody = findViewById(R.id.body);
-        mDivider = findViewById(R.id.divider);
-
         mPhoto = (PhotoEditorView)findViewById(R.id.edit_photo);
         mPhoto.setEnabled(isEnabled());
+
+        mAccountHeaderContainer = findViewById(R.id.account_header_container);
+        mExpandAccountButton = (ImageView) findViewById(R.id.expand_account_button);
+        mCollapsibleSection = (LinearLayout) findViewById(R.id.collapsable_section);
+        mAccountName = (TextView) findViewById(R.id.account_name);
+        mAccountType = (TextView) findViewById(R.id.account_type);
+
+        setCollapsed(false);
+        setCollapsible(true);
     }
 
     public void setGroupMetaData(Cursor groupMetaData) {
     }
 
+
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
     /**
      * Assign the given {@link Bitmap} to the internal {@link PhotoEditorView}
      * in order to update the {@link RawContactDelta} currently being edited.
@@ -111,25 +134,80 @@
     public abstract long getRawContactId();
 
     /**
+     * If {@param isCollapsible} is TRUE, then this editor can be collapsed by clicking on its
+     * account header.
+     */
+    public void setCollapsible(boolean isCollapsible) {
+        if (isCollapsible) {
+            mAccountHeaderContainer.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    final int startingHeight = mCollapsibleSection.getMeasuredHeight();
+                    final boolean isCollapsed = isCollapsed();
+                    setCollapsed(!isCollapsed);
+                    // The slideAndFadeIn animation only looks good when collapsing. For expanding,
+                    // it looks like the editor is loading sluggishly. I tried animating the
+                    // clipping bounds instead of the alpha value. But because the editors are very
+                    // tall, this animation looked very similar to doing no animation at all. It
+                    // wasn't worth the significant additional complexity.
+                    if (!isCollapsed) {
+                        EditorAnimator.getInstance().slideAndFadeIn(mCollapsibleSection,
+                                startingHeight);
+                    }
+                    if (mListener != null) {
+                        mListener.onEditorExpansionChanged();
+                    }
+                    updateAccountHeaderContentDescription();
+                }
+            });
+            mExpandAccountButton.setVisibility(View.VISIBLE);
+            mAccountHeaderContainer.setClickable(true);
+        } else {
+            mAccountHeaderContainer.setOnClickListener(null);
+            mExpandAccountButton.setVisibility(View.GONE);
+            mAccountHeaderContainer.setClickable(false);
+        }
+    }
+
+    public boolean isCollapsed() {
+        return mCollapsibleSection.getLayoutParams().height == 0;
+    }
+
+    public void setCollapsed(boolean isCollapsed) {
+        final LinearLayout.LayoutParams params
+                = (LayoutParams) mCollapsibleSection.getLayoutParams();
+        if (isCollapsed) {
+            params.height = 0;
+            mCollapsibleSection.setLayoutParams(params);
+            mExpandAccountButton.setImageResource(R.drawable.ic_menu_expander_minimized_holo_light);
+        } else {
+            params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+            mCollapsibleSection.setLayoutParams(params);
+            mExpandAccountButton.setImageResource(R.drawable.ic_menu_expander_maximized_holo_light);
+        }
+    }
+
+    protected void updateAccountHeaderContentDescription() {
+        final StringBuilder builder = new StringBuilder();
+        if (!TextUtils.isEmpty(mAccountType.getText())) {
+            builder.append(mAccountType.getText()).append('\n');
+        }
+        if (!TextUtils.isEmpty(mAccountName.getText())) {
+            builder.append(mAccountName.getText()).append('\n');
+        }
+        if (mExpandAccountButton.getVisibility() == View.VISIBLE) {
+            builder.append(getResources().getString(isCollapsed()
+                    ? R.string.content_description_expand_editor
+                    : R.string.content_description_collapse_editor));
+        }
+        mAccountHeaderContainer.setContentDescription(builder);
+    }
+
+    /**
      * Set the internal state for this view, given a current
      * {@link RawContactDelta} state and the {@link AccountType} that
      * apply to that state.
      */
     public abstract void setState(RawContactDelta state, AccountType source, ViewIdGenerator vig,
             boolean isProfile);
-
-    /* package */ void setExpanded(boolean value) {
-        // only allow collapsing if we are one of several children
-        final boolean newValue;
-        if (getParent() instanceof ViewGroup && ((ViewGroup) getParent()).getChildCount() == 1) {
-            newValue = true;
-        } else {
-            newValue = value;
-        }
-
-        if (newValue == mExpanded) return;
-        mExpanded = newValue;
-        mBody.setVisibility(newValue ? View.VISIBLE : View.GONE);
-        mDivider.setVisibility(newValue ? View.GONE : View.VISIBLE);
-    }
 }
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index ac8a641..55f2ad0 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -99,6 +99,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 
 public class ContactEditorFragment extends Fragment implements
@@ -134,6 +135,7 @@
     private static final String KEY_SEND_TO_VOICE_MAIL_STATE = "sendToVoicemailState";
     private static final String KEY_CUSTOM_RINGTONE = "customRingtone";
     private static final String KEY_ARE_PHONE_OPTIONS_CHANGEABLE = "arePhoneOptionsChangable";
+    private static final String KEY_EXPANDED_EDITORS = "expandedEditors";
 
     public static final String SAVE_MODE_EXTRA_KEY = "saveMode";
 
@@ -276,6 +278,9 @@
     // Used to temporarily store existing contact data during a rebind call (i.e. account switch)
     private ImmutableList<RawContact> mRawContacts;
 
+    // Used to store which raw contact editors have been expanded. Keyed on raw contact ids.
+    private HashMap<Long, Boolean> mExpandedEditors = new HashMap<Long, Boolean>();
+
     private AggregationSuggestionEngine mAggregationSuggestionEngine;
     private long mAggregationSuggestionsRawContactId;
     private View mAggregationSuggestionView;
@@ -522,6 +527,8 @@
             mSendToVoicemailState = savedState.getBoolean(KEY_SEND_TO_VOICE_MAIL_STATE);
             mCustomRingtone =  savedState.getString(KEY_CUSTOM_RINGTONE);
             mArePhoneOptionsChangable =  savedState.getBoolean(KEY_ARE_PHONE_OPTIONS_CHANGEABLE);
+            mExpandedEditors = (HashMap<Long, Boolean>)
+                    savedState.getSerializable(KEY_EXPANDED_EDITORS);
         }
 
         // mState can still be null because it may not have have finished loading before
@@ -584,6 +591,11 @@
         mListener.onCustomEditContactActivityRequested(account, uri, null, false);
     }
 
+    @Override
+    public void onEditorExpansionChanged() {
+        updatedExpandedEditorsMap();
+    }
+
     private void bindEditorsForExistingContact(String displayName, boolean isUserProfile,
             ImmutableList<RawContact> rawContacts) {
         setEnabled(true);
@@ -835,28 +847,30 @@
             if (!type.areContactsWritable()) {
                 editor = (BaseRawContactEditorView) inflater.inflate(
                         R.layout.raw_contact_readonly_editor_view, mContent, false);
-                ((RawContactReadOnlyEditorView) editor).setListener(this);
             } else {
                 editor = (RawContactEditorView) inflater.inflate(R.layout.raw_contact_editor_view,
                         mContent, false);
             }
-            if (mHasNewContact && !mNewLocalProfile) {
-                final List<AccountWithDataSet> accounts =
-                        AccountTypeManager.getInstance(mContext).getAccounts(true);
-                if (accounts.size() > 1) {
-                    addAccountSwitcher(mState.get(0), editor);
-                } else {
-                    disableAccountSwitcher(editor);
-                }
-            } else {
-                disableAccountSwitcher(editor);
+            editor.setListener(this);
+            final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(mContext)
+                    .getAccounts(true);
+            if (mHasNewContact && !mNewLocalProfile && accounts.size() > 1) {
+                addAccountSwitcher(mState.get(0), editor);
             }
 
             editor.setEnabled(mEnabled);
 
+            if (mExpandedEditors.containsKey(rawContactId)) {
+                editor.setCollapsed(mExpandedEditors.get(rawContactId));
+            } else {
+                // By default, only the first editor will be expanded.
+                editor.setCollapsed(i != 0);
+            }
+
             mContent.addView(editor);
 
             editor.setState(rawContactDelta, type, mViewIdGenerator, isEditingUserProfile());
+            editor.setCollapsible(numRawContacts > 1);
 
             // Set up the photo handler.
             bindPhotoHandler(editor, type, mState);
@@ -918,6 +932,21 @@
         // Activity can be null if we have been detached from the Activity
         final Activity activity = getActivity();
         if (activity != null) activity.invalidateOptionsMenu();
+
+        updatedExpandedEditorsMap();
+    }
+
+    /**
+     * Update the values in {@link #mExpandedEditors}.
+     */
+    private void updatedExpandedEditorsMap() {
+        for (int i = 0; i < mContent.getChildCount(); i++) {
+            final View childView = mContent.getChildAt(i);
+            if (childView instanceof BaseRawContactEditorView) {
+                BaseRawContactEditorView childEditor = (BaseRawContactEditorView) childView;
+                mExpandedEditors.put(childEditor.getRawContactId(), childEditor.isCollapsed());
+            }
+        }
     }
 
     /**
@@ -1002,7 +1031,11 @@
                 currentState.getAccountType(),
                 currentState.getDataSet());
         final View accountView = editor.findViewById(R.id.account);
-        final View anchorView = editor.findViewById(R.id.account_container);
+        final View anchorView = editor.findViewById(R.id.account_selector_container);
+        if (accountView == null) {
+            return;
+        }
+        anchorView.setVisibility(View.VISIBLE);
         accountView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -1031,14 +1064,6 @@
         });
     }
 
-    private void disableAccountSwitcher(BaseRawContactEditorView editor) {
-        // Remove the pressed state from the account header because the user cannot switch accounts
-        // on an existing contact
-        final View accountView = editor.findViewById(R.id.account);
-        accountView.setBackground(null);
-        accountView.setEnabled(false);
-    }
-
     @Override
     public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
         inflater.inflate(R.menu.edit_contact, menu);
@@ -1789,6 +1814,7 @@
         outState.putBoolean(KEY_SEND_TO_VOICE_MAIL_STATE, mSendToVoicemailState);
         outState.putString(KEY_CUSTOM_RINGTONE, mCustomRingtone);
         outState.putBoolean(KEY_ARE_PHONE_OPTIONS_CHANGEABLE, mArePhoneOptionsChangable);
+        outState.putSerializable(KEY_EXPANDED_EDITORS, mExpandedEditors);
 
         super.onSaveInstanceState(outState);
     }
diff --git a/src/com/android/contacts/editor/EditorAnimator.java b/src/com/android/contacts/editor/EditorAnimator.java
index 507e094..35ed1de 100644
--- a/src/com/android/contacts/editor/EditorAnimator.java
+++ b/src/com/android/contacts/editor/EditorAnimator.java
@@ -91,7 +91,6 @@
         mRunner.endOldAnimation();
         target.setVisibility(View.VISIBLE);
         target.setAlpha(0.0f);
-        target.requestFocus();
         SchedulingUtils.doAfterLayout(target, new Runnable() {
             @Override
             public void run() {
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 4a05d7a..a3d2f09 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -31,7 +31,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.contacts.GroupMetaDataLoader;
@@ -42,6 +41,7 @@
 import com.android.contacts.common.model.RawContactDelta;
 import com.android.contacts.common.model.ValuesDelta;
 import com.android.contacts.common.model.RawContactModifier;
+
 import com.google.common.base.Objects;
 
 import java.util.ArrayList;
@@ -69,9 +69,13 @@
 
     private ViewGroup mFields;
 
-    private ImageView mAccountIcon;
-    private TextView mAccountTypeTextView;
-    private TextView mAccountNameTextView;
+    private View mAccountSelector;
+    private TextView mAccountSelectorTypeTextView;
+    private TextView mAccountSelectorNameTextView;
+
+    private View mAccountHeader;
+    private TextView mAccountHeaderTypeTextView;
+    private TextView mAccountHeaderNameTextView;
 
     private long mRawContactId = -1;
     private boolean mAutoAddToDefaultGroup = true;
@@ -132,9 +136,13 @@
 
         mFields = (ViewGroup)findViewById(R.id.sect_fields);
 
-        mAccountIcon = (ImageView) findViewById(R.id.account_icon);
-        mAccountTypeTextView = (TextView) findViewById(R.id.account_type);
-        mAccountNameTextView = (TextView) findViewById(R.id.account_name);
+        mAccountHeader = findViewById(R.id.account_header_container);
+        mAccountHeaderTypeTextView = (TextView) findViewById(R.id.account_type);
+        mAccountHeaderNameTextView = (TextView) findViewById(R.id.account_name);
+
+        mAccountSelector = findViewById(R.id.account_selector_container);
+        mAccountSelectorTypeTextView = (TextView) findViewById(R.id.account_type_selector);
+        mAccountSelectorNameTextView = (TextView) findViewById(R.id.account_name_selector);
     }
 
     @Override
@@ -183,13 +191,13 @@
         if (isProfile) {
             String accountName = state.getAccountName();
             if (TextUtils.isEmpty(accountName)) {
-                mAccountNameTextView.setVisibility(View.GONE);
-                mAccountTypeTextView.setText(R.string.local_profile_title);
+                mAccountHeaderNameTextView.setVisibility(View.GONE);
+                mAccountHeaderTypeTextView.setText(R.string.local_profile_title);
             } else {
                 CharSequence accountType = type.getDisplayLabel(mContext);
-                mAccountTypeTextView.setText(mContext.getString(R.string.external_profile_title,
+                mAccountHeaderTypeTextView.setText(mContext.getString(R.string.external_profile_title,
                         accountType));
-                mAccountNameTextView.setText(accountName);
+                mAccountHeaderNameTextView.setText(accountName);
             }
         } else {
             String accountName = state.getAccountName();
@@ -198,17 +206,27 @@
                 accountType = mContext.getString(R.string.account_phone);
             }
             if (!TextUtils.isEmpty(accountName)) {
-                mAccountNameTextView.setVisibility(View.VISIBLE);
-                mAccountNameTextView.setText(
+                mAccountHeaderNameTextView.setVisibility(View.VISIBLE);
+                mAccountHeaderNameTextView.setText(
                         mContext.getString(R.string.from_account_format, accountName));
             } else {
                 // Hide this view so the other text view will be centered vertically
-                mAccountNameTextView.setVisibility(View.GONE);
+                mAccountHeaderNameTextView.setVisibility(View.GONE);
             }
-            mAccountTypeTextView.setText(
+            mAccountHeaderTypeTextView.setText(
                     mContext.getString(R.string.account_type_format, accountType));
         }
-        mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
+        updateAccountHeaderContentDescription();
+
+        // The account selector and header are both used to display the same information.
+        mAccountSelectorTypeTextView.setText(mAccountHeaderTypeTextView.getText());
+        mAccountSelectorTypeTextView.setVisibility(mAccountHeaderTypeTextView.getVisibility());
+        mAccountSelectorNameTextView.setText(mAccountHeaderNameTextView.getText());
+        mAccountSelectorNameTextView.setVisibility(mAccountHeaderNameTextView.getVisibility());
+        // Showing the account header at the same time as the account selector drop down is
+        // confusing. They should be mutually exclusive.
+        mAccountHeader.setVisibility(mAccountSelector.getVisibility() == View.GONE
+                ? View.VISIBLE : View.GONE);
 
         // Show photo editor when supported
         RawContactModifier.ensureKindExists(state, type, Photo.CONTENT_ITEM_TYPE);
diff --git a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java b/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
index 7901093..477cbd3 100644
--- a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
+++ b/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
@@ -36,7 +35,6 @@
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.contacts.R;
 import com.android.contacts.common.GeoUtil;
@@ -60,22 +58,14 @@
     private Button mEditExternallyButton;
     private ViewGroup mGeneral;
 
-    private View mAccountContainer;
-    private ImageView mAccountIcon;
-    private TextView mAccountTypeTextView;
-    private TextView mAccountNameTextView;
+    private TextView mAccountHeaderTypeTextView;
+    private TextView mAccountHeaderNameTextView;
 
     private String mAccountName;
     private String mAccountType;
     private String mDataSet;
     private long mRawContactId = -1;
 
-    private Listener mListener;
-
-    public interface Listener {
-        void onExternalEditorRequest(AccountWithDataSet account, Uri uri);
-    }
-
     public RawContactReadOnlyEditorView(Context context) {
         super(context);
     }
@@ -84,9 +74,6 @@
         super(context, attrs);
     }
 
-    public void setListener(Listener listener) {
-        mListener = listener;
-    }
 
     /** {@inheritDoc} */
     @Override
@@ -101,10 +88,8 @@
         mEditExternallyButton.setOnClickListener(this);
         mGeneral = (ViewGroup)findViewById(R.id.sect_general);
 
-        mAccountContainer = findViewById(R.id.account_container);
-        mAccountIcon = (ImageView) findViewById(R.id.account_icon);
-        mAccountTypeTextView = (TextView) findViewById(R.id.account_type);
-        mAccountNameTextView = (TextView) findViewById(R.id.account_name);
+        mAccountHeaderTypeTextView = (TextView) findViewById(R.id.account_type);
+        mAccountHeaderNameTextView = (TextView) findViewById(R.id.account_name);
     }
 
     /**
@@ -131,13 +116,13 @@
 
         if (isProfile) {
             if (TextUtils.isEmpty(mAccountName)) {
-                mAccountNameTextView.setVisibility(View.GONE);
-                mAccountTypeTextView.setText(R.string.local_profile_title);
+                mAccountHeaderNameTextView.setVisibility(View.GONE);
+                mAccountHeaderTypeTextView.setText(R.string.local_profile_title);
             } else {
                 CharSequence accountType = type.getDisplayLabel(mContext);
-                mAccountTypeTextView.setText(mContext.getString(R.string.external_profile_title,
+                mAccountHeaderTypeTextView.setText(mContext.getString(R.string.external_profile_title,
                         accountType));
-                mAccountNameTextView.setText(mAccountName);
+                mAccountHeaderNameTextView.setText(mAccountName);
             }
         } else {
             CharSequence accountType = type.getDisplayLabel(mContext);
@@ -145,23 +130,20 @@
                 accountType = mContext.getString(R.string.account_phone);
             }
             if (!TextUtils.isEmpty(mAccountName)) {
-                mAccountNameTextView.setVisibility(View.VISIBLE);
-                mAccountNameTextView.setText(
+                mAccountHeaderNameTextView.setVisibility(View.VISIBLE);
+                mAccountHeaderNameTextView.setText(
                         mContext.getString(R.string.from_account_format, mAccountName));
             } else {
                 // Hide this view so the other text view will be centered vertically
-                mAccountNameTextView.setVisibility(View.GONE);
+                mAccountHeaderNameTextView.setVisibility(View.GONE);
             }
-            mAccountTypeTextView.setText(mContext.getString(R.string.account_type_format,
+            mAccountHeaderTypeTextView.setText(mContext.getString(R.string.account_type_format,
                     accountType));
         }
-        mAccountTypeTextView.setTextColor(mContext.getResources().getColor(
-                R.color.secondary_text_color));
+        updateAccountHeaderContentDescription();
 
         // TODO: Expose data set in the UI somehow?
 
-        mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
-
         mRawContactId = state.getRawContactId();
 
         ValuesDelta primary;
@@ -182,16 +164,8 @@
                 mContext.getString(R.string.missing_name));
 
         if (type.getEditContactActivityClassName() != null) {
-            mAccountContainer.setEnabled(false);
             mEditExternallyButton.setVisibility(View.VISIBLE);
         } else {
-            mAccountContainer.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    Toast.makeText(mContext, mContext.getString(R.string.contact_read_only),
-                            Toast.LENGTH_SHORT).show();
-                }
-            });
             mEditExternallyButton.setVisibility(View.GONE);
         }
 
diff --git a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
index af50d6b..b912a3d 100644
--- a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
+++ b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
@@ -421,10 +421,10 @@
         Resources res = getResources();
 
         separator.setBackgroundColor(res.getColor(
-                R.color.expanding_entry_card_item_separator_color));
+                R.color.divider_line_color_light));
         LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
-                res.getDimensionPixelSize(R.dimen.expanding_entry_card_item_separator_height));
+                res.getDimensionPixelSize(R.dimen.divider_line_height));
         // The separator is aligned with the text in the entry. This is offset by a default
         // margin. If there is an icon present, the icon's width and margin are added
         int marginStart = res.getDimensionPixelSize(