Merge "Insert name into toast during contact deletion"
diff --git a/res/layout/item_kind_section.xml b/res/layout/item_kind_section.xml
deleted file mode 100644
index d62523b..0000000
--- a/res/layout/item_kind_section.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<!-- the body surrounding all editors for a specific kind -->
-
-<com.android.contacts.editor.KindSectionView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="horizontal">
-
-    <ImageView
-        android:id="@+id/kind_icon"
-        style="@style/EditKindIconStyle" />
-
-    <LinearLayout
-        android:id="@+id/kind_editors"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical" />
-
-</com.android.contacts.editor.KindSectionView>
\ No newline at end of file
diff --git a/res/layout/item_photo_editor.xml b/res/layout/item_photo_editor.xml
deleted file mode 100644
index e3eff4e..0000000
--- a/res/layout/item_photo_editor.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<view class="com.android.contacts.editor.PhotoEditorView"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:orientation="horizontal" >
-
-    <ImageView
-        android:id="@+id/kind_icon"
-        android:src="@drawable/ic_camera_alt_black_24dp"
-        android:layout_marginTop="13dp"
-        android:contentDescription="@string/header_photo_entry"
-        style="@style/EditKindIconStyle" />
-
-    <!-- Needs 10dp of top padding, in order get a total of 32dp of padding between this view
-        and the previous DataKindSection. Note that EditTexts in other editor.xml files have this
-        padding built in. Similarly, we need to add 4dp of start margin to make up for the padding
-        that an EditText would have in this image's place. -->
-    <ImageView
-        android:id="@+id/photo"
-        android:layout_width="72dip"
-        android:layout_height="72dip"
-        android:cropToPadding="true"
-        android:scaleType="centerCrop"
-        android:layout_marginTop="15dp"
-        android:layout_marginStart="4dp"
-        android:contentDescription="@string/description_contact_photo"
-        android:layout_marginBottom="@dimen/editor_padding_below_photo"
-        android:gravity="start" />
-
-    <!-- We want 16dp for the effective marginStart. So we set 12dp, since the private
-        @android:dimen/control_inset_material already includes 4dp of padding. -->
-    <LinearLayout
-        android:orientation="vertical"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="12dp" >
-
-        <!-- The values applied to this button are complicated:
-            1) We want 16dp internal padding in the button. The background drawable is inset
-            by private @android:dimen/button_inset_horizontal_material=4dp. Therefore,
-            we need paddingStart/End of 20dp.
-            2) In order to leave enough room for the 32dp RadioButton, this can only be 46dp.
-            This is 2dp less than the default touch target size.
-            3) This button will appear to be offset by the private
-            @android:dimen/button_inset_vertical_material amount. Therefore, in order to achieve
-            15dp of apparent top margin, we only need to apply 9dp. -->
-        <Button
-            android:id="@+id/change_button"
-            android:layout_width="wrap_content"
-            android:layout_height="46dp"
-            android:textSize="@dimen/editor_form_text_size"
-            android:textColor="@color/primary_text_color"
-            android:layout_marginTop="9dp"
-            android:paddingStart="20dp"
-            android:paddingEnd="20dp"
-            android:text="@string/change_photo" />
-
-        <!-- Don't explicitly set the layout_height in case we need to rely on text wrapping.
-            For one line, we can expect the height to be 32dp with 16dp text size. -->
-        <RadioButton
-            android:id="@+id/primary_checkbox"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="48dp"
-            android:textSize="@dimen/editor_form_text_size"
-            android:text="@string/primary_photo" />
-    </LinearLayout>
-</view>
diff --git a/res/layout/item_photo_editor_readonly.xml b/res/layout/item_photo_editor_readonly.xml
deleted file mode 100644
index edb6f61..0000000
--- a/res/layout/item_photo_editor_readonly.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?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.
--->
-
-<!-- A readonly version of item_photo_editor.xml shown in the readonly raw contact editor. -->
-<view class="com.android.contacts.editor.PhotoEditorView"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:orientation="horizontal" >
-
-    <ImageView
-        android:id="@+id/kind_icon"
-        android:src="@drawable/ic_camera_alt_black_24dp"
-        android:layout_marginTop="0dp"
-        android:contentDescription="@string/header_photo_entry"
-        style="@style/EditKindIconStyle" />
-
-    <!-- We need to add 2dp of start margin to make up for the padding that a TextView would
-        have in this image's place. We add 2dp of top margin, so that icon drawable is a little
-        below the top of this ImageView. -->
-    <ImageView
-        android:id="@+id/photo"
-        android:layout_width="72dip"
-        android:layout_height="72dip"
-        android:cropToPadding="true"
-        android:scaleType="centerCrop"
-        android:layout_marginTop="2dp"
-        android:layout_marginStart="2dp"
-        android:contentDescription="@string/description_contact_photo"
-        android:layout_marginBottom="@dimen/editor_padding_around_read_only_photo_editor"
-        android:gravity="start" />
-
-
-    <!-- Don't explicitly set the layout_height in case we need to rely on text wrapping.
-        For one line, we can expect the height to be 32dp with 16dp text size. -->
-    <RadioButton
-        android:id="@+id/primary_checkbox"
-        android:layout_marginStart="12dp"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/editor_delete_button_width"
-        android:layout_marginTop="2dp"
-        android:textSize="@dimen/editor_form_text_size"
-        android:text="@string/primary_photo" />
-</view>
diff --git a/res/layout/raw_contact_readonly_editor_view.xml b/res/layout/raw_contact_readonly_editor_view.xml
deleted file mode 100644
index e50aa98..0000000
--- a/res/layout/raw_contact_readonly_editor_view.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<com.android.contacts.editor.RawContactReadOnlyEditorView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical" >
-
-    <include
-        layout="@layout/editor_account_header" />
-
-    <LinearLayout
-        android:id="@+id/collapsable_section"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical" >
-
-        <!-- Want 16dp of apparent top padding. Since TextView has 4dp of inset/padding built in,
-            only set marginTop=12dp. -->
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="@dimen/editor_padding_around_read_only_photo_editor"
-            android:layout_marginTop="12dp"
-            android:orientation="horizontal">
-
-            <ImageView
-                android:id="@+id/kind_icon"
-                android:layout_marginTop="2dp"
-                android:src="@drawable/ic_person_black_24dp"
-                android:contentDescription="@string/header_name_entry"
-                style="@style/EditKindIconStyle" />
-
-            <TextView
-                android:id="@+id/read_only_name"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginRight="@dimen/editor_delete_button_width"
-                android:layout_marginEnd="@dimen/editor_delete_button_width"
-                android:singleLine="true"
-                android:textSize="@dimen/editor_form_text_size"
-                android:textColor="?android:attr/textColorSecondary"
-                android:textAlignment="viewStart"
-                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="4dp"
-            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>
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/divider_line_height"
-        android:background="@color/divider_line_color_light" />
-
-</com.android.contacts.editor.RawContactReadOnlyEditorView>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 5a8fb88..e1c70ff 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -90,15 +90,6 @@
         padding between TextView's in the readonly Raw Contact Editor. -->
     <dimen name="editor_padding_between_read_only_editor_views">9dp</dimen>
 
-    <!-- Padding above and below the photo editor. This value is chosen to give 19dp of apparent
-        padding between TextView's and the photo's ImageView. -->
-    <dimen name="editor_padding_around_read_only_photo_editor">15dp</dimen>
-
-    <!-- Padding below the photo editor. This value is larger than
-        editor_padding_between_editor_views, since ImageView's don't have space between the bottom
-        of their visual bottom, like an EditText does. -->
-    <dimen name="editor_padding_below_photo">9dp</dimen>
-
     <!-- Width of the Type-Label in the Editor -->
     <dimen name="editor_type_label_width">150dip</dimen>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 09df33b..8ee4c8d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -387,12 +387,6 @@
          the email address, e.g. "Add xyz@foo.com to contacts?" -->
     <string name="add_contact_dlg_message_fmt">Add \"<xliff:g id="email">%s</xliff:g>\" to contacts?</string>
 
-    <!-- String describing the Contact Photo Image
-
-         Used by AccessibilityService to announce the purpose of the view.
-    -->
-    <string name="description_contact_photo">contact photo</string>
-
     <!-- String describing the Contact Editor Plus button
 
          Used by AccessibilityService to announce the purpose of the button.
@@ -520,9 +514,6 @@
     <!-- Button used for changing a photo in the Raw Contact Editor [CHAR LIMIT=15] -->
     <string name="change_photo">Change</string>
 
-    <!-- RadioButton that determines whether a raw contact's photo should be used for the entire contact [CHAR LIMIT=25] -->
-    <string name="primary_photo">Primary photo</string>
-
     <!-- String describing the Star/Favorite checkbox
 
          Used by AccessibilityService to announce the purpose of the view.
@@ -768,8 +759,6 @@
     <string name="header_email_entry">Email</string>
     <!-- Content description for the phone fields header entry [CHAR LIMIT=NONE] -->
     <string name="header_phone_entry">Phone</string>
-    <!-- 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>
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 9a0aba5..0d29e06 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -54,7 +54,6 @@
 import com.android.contacts.R;
 import com.android.contacts.common.Experiments;
 import com.android.contacts.common.activity.RequestPermissionsActivity;
-import com.android.contacts.common.interactions.ImportExportDialogFragment;
 import com.android.contacts.common.list.ContactListFilter;
 import com.android.contacts.common.list.ProviderStatusWatcher;
 import com.android.contacts.common.list.ProviderStatusWatcher.ProviderStatusListener;
@@ -66,7 +65,6 @@
 import com.android.contacts.common.util.Constants;
 import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.common.widget.FloatingActionButtonController;
-import com.android.contacts.editor.EditorIntents;
 import com.android.contacts.group.GroupMembersFragment;
 import com.android.contacts.group.GroupMetaData;
 import com.android.contacts.group.GroupUtil;
@@ -74,9 +72,9 @@
 import com.android.contacts.list.ContactsRequest;
 import com.android.contacts.list.ContactsUnavailableFragment;
 import com.android.contacts.list.DefaultContactBrowseListFragment;
-import com.android.contacts.list.OnContactsUnavailableActionListener;
 import com.android.contacts.quickcontact.QuickContactActivity;
 import com.android.contacts.util.SyncUtil;
+import com.android.contactsbind.FeatureHighlightHelper;
 import com.android.contactsbind.ObjectFactory;
 import com.android.contactsbind.experiments.Flags;
 import com.android.contacts.widget.FloatingActionButtonBehavior;
@@ -199,26 +197,6 @@
         return (mProviderStatus != null) && mProviderStatus.equals(ProviderStatus.STATUS_NORMAL);
     }
 
-    /**
-     * Initialize fragments that are (or may not be) in the layout.
-     *
-     * For the fragments that are in the layout, we initialize them in
-     * {@link #createViewsAndFragments()} after inflating the layout.
-     *
-     * However, the {@link ContactsUnavailableFragment} is a special fragment which may not
-     * be in the layout, so we have to do the initialization here.
-     *
-     * The ContactsUnavailableFragment is always created at runtime.
-     */
-    @Override
-    public void onAttachFragment(Fragment fragment) {
-        if (fragment instanceof ContactsUnavailableFragment) {
-            mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
-            mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
-                    new ContactsUnavailableFragmentListener());
-        }
-    }
-
     @Override
     protected void onCreate(Bundle savedState) {
         if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
@@ -553,8 +531,6 @@
             }
             if (mContactsUnavailableFragment == null) {
                 mContactsUnavailableFragment = new ContactsUnavailableFragment();
-                mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
-                        new ContactsUnavailableFragmentListener());
                 transaction.add(R.id.contacts_list_container, mContactsUnavailableFragment,
                         TAG_UNAVAILABLE);
             }
@@ -588,28 +564,6 @@
         return !allAccounts.get(0).isLocalAccount();
     }
 
-    private class ContactsUnavailableFragmentListener
-            implements OnContactsUnavailableActionListener {
-        ContactsUnavailableFragmentListener() {}
-
-        @Override
-        public void onCreateNewContactAction() {
-            ImplicitIntentsUtil.startActivityInApp(PeopleActivity.this,
-                    EditorIntents.createCompactInsertContactIntent(PeopleActivity.this));
-        }
-
-        @Override
-        public void onAddAccountAction() {
-            final Intent intent = ImplicitIntentsUtil.getIntentForAddingGoogleAccount();
-            ImplicitIntentsUtil.startActivityOutsideApp(PeopleActivity.this, intent);
-        }
-
-        @Override
-        public void onImportContactsFromFileAction() {
-            showImportExportDialogFragment();
-        }
-    }
-
     private void invalidateOptionsMenuIfNeeded() {
         if (mAllFragment != null
                 && mAllFragment.getOptionsMenuContactsAvailable() != areContactsAvailable()) {
@@ -617,11 +571,6 @@
         }
     }
 
-    private void showImportExportDialogFragment(){
-        ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(),
-                PeopleActivity.class, ImportExportDialogFragment.EXPORT_MODE_ALL_CONTACTS);
-    }
-
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         // Bring up the search UI if the user starts typing
@@ -644,26 +593,57 @@
             return;
         }
 
+        // Handle the back event in drawer first.
         if (mDrawer.isDrawerOpen(GravityCompat.START)) {
             mDrawer.closeDrawer(GravityCompat.START);
-        } else if (isGroupView()) {
-            if (mMembersFragment.isEditMode()) {
-                mMembersFragment.exitEditMode();
-            } else if (mMembersFragment.getActionBarAdapter().isSelectionMode()) {
-                mMembersFragment.getActionBarAdapter().setSelectionMode(false);
-                mMembersFragment.displayCheckBoxes(false);
-            } else if (mMembersFragment.getActionBarAdapter().isSearchMode()) {
-                mMembersFragment.getActionBarAdapter().setSearchMode(false);
-            } else {
-                switchToAllContacts();
-            }
-        } else if (isDuplicatesView()) {
-            switchToAllContacts();
-        } else if (mAllFragment.tryRemoveHighlight()) {
             return;
-        } else if (isAllFragmentInSelectionMode()) {
+        }
+
+        // Handle the back event in "second level".
+        if (isGroupView()) {
+            onBackPressedGroupView();
+            return;
+        }
+
+        if (isDuplicatesView()) {
+            switchToAllContacts();
+            return;
+        }
+
+        // If feature highlight is present, let it handle the back event before mAllFragment.
+        if (FeatureHighlightHelper.tryRemoveHighlight(this)) {
+            return;
+        }
+
+        // Handle the back event in "first level" - mAllFragment.
+        if (maybeHandleInAllFragment()) {
+            return;
+        }
+
+        super.onBackPressed();
+    }
+
+    private void onBackPressedGroupView() {
+        if (mMembersFragment.isEditMode()) {
+            mMembersFragment.exitEditMode();
+        } else if (mMembersFragment.getActionBarAdapter().isSelectionMode()) {
+            mMembersFragment.getActionBarAdapter().setSelectionMode(false);
+            mMembersFragment.displayCheckBoxes(false);
+        } else if (mMembersFragment.getActionBarAdapter().isSearchMode()) {
+            mMembersFragment.getActionBarAdapter().setSearchMode(false);
+        } else {
+            switchToAllContacts();
+        }
+    }
+
+    // Returns true if back event is handled in this method.
+    private boolean maybeHandleInAllFragment() {
+        if (isAllFragmentInSelectionMode()) {
             mAllFragment.getActionBarAdapter().setSelectionMode(false);
-        } else if (isAllFragmentInSearchMode()) {
+            return true;
+        }
+
+        if (isAllFragmentInSearchMode()) {
             mAllFragment.getActionBarAdapter().setSearchMode(false);
             if (mAllFragment.wasSearchResultClicked()) {
                 mAllFragment.resetSearchResultClicked();
@@ -671,14 +651,18 @@
                 Logger.logScreenView(this, ScreenType.SEARCH_EXIT);
                 Logger.logSearchEvent(mAllFragment.createSearchState());
             }
-        } else if (!AccountFilterUtil.isAllContactsFilter(mAllFragment.getFilter())
+            return true;
+        }
+
+        if (!AccountFilterUtil.isAllContactsFilter(mAllFragment.getFilter())
                 && !mAllFragment.isHidden()) {
             // If mAllFragment is hidden, then mContactsUnavailableFragment is visible so we
             // don't need to switch to all contacts.
             switchToAllContacts();
-        } else {
-            super.onBackPressed();
+            return true;
         }
+
+        return false;
     }
 
     private boolean isAllFragmentInSelectionMode() {
diff --git a/src/com/android/contacts/common/list/ContactListFilter.java b/src/com/android/contacts/common/list/ContactListFilter.java
index 63eae17..c6baf41 100644
--- a/src/com/android/contacts/common/list/ContactListFilter.java
+++ b/src/com/android/contacts/common/list/ContactListFilter.java
@@ -335,9 +335,12 @@
             throw new IllegalStateException(
                     "filterType must be FILTER_TYPE_ACCOUNT or FILER_TYPE_GROUP_MEMBERS");
         }
-        uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName);
-        uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType);
-        if (!TextUtils.isEmpty(dataSet)) {
+        // null account names are not valid, see ContactsProvider2#appendAccountFromParameter
+        if (accountName != null) {
+            uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName);
+            uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType);
+        }
+        if (dataSet != null) {
             uriBuilder.appendQueryParameter(RawContacts.DATA_SET, dataSet);
         }
         return uriBuilder;
diff --git a/src/com/android/contacts/common/logging/Logger.java b/src/com/android/contacts/common/logging/Logger.java
index 3ce208e..68cbbbb 100644
--- a/src/com/android/contacts/common/logging/Logger.java
+++ b/src/com/android/contacts/common/logging/Logger.java
@@ -87,11 +87,11 @@
         final Logger logger = getInstance();
         if (logger != null) {
             final QuickContactEvent event = new QuickContactEvent();
-            event.referrer = referrer;
+            event.referrer = referrer == null ? "Unknown" : referrer;
             event.contactType = contactType;
             event.cardType = cardType;
             event.actionType = actionType;
-            event.thirdPartyAction = thirdPartyAction;
+            event.thirdPartyAction = thirdPartyAction == null ? "" : thirdPartyAction;
             logger.logQuickContactEventImpl(event);
         }
     }
diff --git a/src/com/android/contacts/common/model/DeviceLocalAccountLocator.java b/src/com/android/contacts/common/model/DeviceLocalAccountLocator.java
index 0efadc4..8c45c40 100644
--- a/src/com/android/contacts/common/model/DeviceLocalAccountLocator.java
+++ b/src/com/android/contacts/common/model/DeviceLocalAccountLocator.java
@@ -78,16 +78,8 @@
 
         // Many device accounts have default groups associated with them.
         addAccountsFromQuery(ContactsContract.Groups.CONTENT_URI, localAccounts);
-
         addAccountsFromQuery(ContactsContract.Settings.CONTENT_URI, localAccounts);
-
-        if (localAccounts.isEmpty()) {
-            // It's probably safe to assume that if one of the earlier queries found a "device"
-            // account then this query isn't going to find any different device accounts.
-            // We skip this query because it probably is kind of expensive (relative to the other
-            // queries).
-            addAccountsFromQuery(ContactsContract.RawContacts.CONTENT_URI, localAccounts);
-        }
+        addAccountsFromQuery(ContactsContract.RawContacts.CONTENT_URI, localAccounts);
 
         return new ArrayList<>(localAccounts);
     }
diff --git a/src/com/android/contacts/editor/BaseRawContactEditorView.java b/src/com/android/contacts/editor/BaseRawContactEditorView.java
deleted file mode 100644
index 1cdfafb..0000000
--- a/src/com/android/contacts/editor/BaseRawContactEditorView.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-package com.android.contacts.editor;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-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;
-import com.android.contacts.common.model.ValuesDelta;
-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
- * RawContact represented through an {@link RawContactDelta}.
- * <p>
- * Internal updates are performed against {@link ValuesDelta} so that the
- * source {@link RawContact} can be swapped out. Any state-based changes, such as
- * adding {@link Data} rows or changing {@link EditType}, are performed through
- * {@link RawContactModifier} to ensure that {@link AccountType} are enforced.
- */
-public abstract class BaseRawContactEditorView extends LinearLayout {
-
-    private PhotoEditorView mPhoto;
-
-    private View mAccountHeaderContainer;
-    private ImageView mExpandAccountButton;
-    private LinearLayout mCollapsibleSection;
-    private TextView mAccountName;
-    private TextView mAccountType;
-
-    protected Listener mListener;
-
-    public interface Listener {
-        void onExternalEditorRequest(AccountWithDataSet account, Uri uri);
-        void onEditorExpansionChanged();
-    }
-
-    public BaseRawContactEditorView(Context context) {
-        super(context);
-    }
-
-    public BaseRawContactEditorView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mPhoto = (PhotoEditorView)findViewById(R.id.edit_photo);
-        mPhoto.setEnabled(isEnabled());
-
-        mAccountHeaderContainer = findViewById(R.id.account_header_container);
-        mExpandAccountButton = (ImageView) findViewById(R.id.account_expander_icon);
-        mExpandAccountButton.setColorFilter(R.color.quantum_black_secondary_text);
-        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.
-     */
-    public void setPhotoEntry(Bitmap bitmap) {
-        mPhoto.setPhotoEntry(bitmap);
-    }
-
-    /**
-     * Assign the given photo {@link Uri} to UI of the {@link PhotoEditorView}, so that it can
-     * display a full sized photo.
-     */
-    public void setFullSizedPhoto(Uri uri) {
-        mPhoto.setFullSizedPhoto(uri);
-    }
-
-    protected void setHasPhotoEditor(boolean hasPhotoEditor) {
-        mPhoto.setVisibility(hasPhotoEditor ? View.VISIBLE : View.GONE);
-    }
-
-    /**
-     * Return true if internal {@link PhotoEditorView} has a {@link Photo} set.
-     */
-    public boolean hasSetPhoto() {
-        return mPhoto.hasSetPhoto();
-    }
-
-    public PhotoEditorView getPhotoEditor() {
-        return mPhoto;
-    }
-
-    /**
-     * @return the RawContact ID that this editor is editing.
-     */
-    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);
-                        // We want to place the focus near the top of the screen now that a
-                        // potentially focused editor is being collapsed.
-                        EditorAnimator.placeFocusAtTopOfScreenAfterReLayout(mCollapsibleSection);
-                    } else {
-                        // When expanding we should scroll the expanded view onto the screen.
-                        // Otherwise, user's may not notice that any expansion happened.
-                        EditorAnimator.getInstance().scrollViewToTop(mAccountHeaderContainer);
-                        mCollapsibleSection.requestFocus();
-                    }
-                    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.setImageDrawable(getContext().getDrawable(
-                    R.drawable.ic_menu_expand_minimized_24dp));
-        } else {
-            params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
-            mCollapsibleSection.setLayoutParams(params);
-            mExpandAccountButton.setImageDrawable(getContext().getDrawable(
-                    R.drawable.ic_menu_expand_maximized_24dp));
-        }
-    }
-
-    protected void updateAccountHeaderContentDescription() {
-        final StringBuilder builder = new StringBuilder();
-        builder.append(EditorUiUtils.getAccountInfoContentDescription(
-                mAccountName.getText(), mAccountType.getText()));
-        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);
-}
diff --git a/src/com/android/contacts/editor/CompactKindSectionView.java b/src/com/android/contacts/editor/CompactKindSectionView.java
index f550c6d..cbc4772 100644
--- a/src/com/android/contacts/editor/CompactKindSectionView.java
+++ b/src/com/android/contacts/editor/CompactKindSectionView.java
@@ -16,13 +16,6 @@
 
 package com.android.contacts.editor;
 
-import com.android.contacts.R;
-import com.android.contacts.common.model.RawContactDelta;
-import com.android.contacts.common.model.RawContactModifier;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.model.account.AccountType;
-import com.android.contacts.common.model.dataitem.DataKind;
-
 import android.content.Context;
 import android.database.Cursor;
 import android.provider.ContactsContract.CommonDataKinds.Event;
@@ -37,11 +30,20 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.contacts.R;
+import com.android.contacts.common.model.RawContactDelta;
+import com.android.contacts.common.model.RawContactModifier;
+import com.android.contacts.common.model.ValuesDelta;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.dataitem.DataKind;
+
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * Version of {@link KindSectionView} that supports multiple RawContactDeltas.
+ * Custom view for an entire section of data as segmented by
+ * {@link DataKind} around a {@link Data#MIMETYPE}. This view shows a
+ * section header and a trigger for adding new {@link Data} rows.
  */
 public class CompactKindSectionView extends LinearLayout {
 
@@ -147,7 +149,7 @@
         }
     }
 
-    private KindSectionDataList mKindSectionDataList;
+    private KindSectionData mKindSectionData;
     private ViewIdGenerator mViewIdGenerator;
     private CompactRawContactsEditorView.Listener mListener;
 
@@ -180,6 +182,7 @@
 
     @Override
     protected void onFinishInflate() {
+        super.onFinishInflate();
         setDrawingCacheEnabled(true);
         setAlwaysDrawnWithCacheEnabled(true);
 
@@ -227,7 +230,7 @@
      * and nicknames) are empty.
      */
     public boolean isEmptyName() {
-        if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionDataList.getMimeType())) {
+        if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionData.getMimeType())) {
             return false;
         }
         for (int i = 0; i < mEditors.getChildCount(); i++) {
@@ -247,7 +250,7 @@
      * without informing editor listeners.
      */
     public void setName(String displayName) {
-        if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionDataList.getMimeType())) {
+        if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionData.getMimeType())) {
             return;
         }
         for (int i = 0; i < mEditors.getChildCount(); i++) {
@@ -270,7 +273,7 @@
     }
 
     public StructuredNameEditorView getPrimaryNameEditorView() {
-        if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionDataList.getMimeType())
+        if (!StructuredName.CONTENT_ITEM_TYPE.equals(mKindSectionData.getMimeType())
             || mEditors.getChildCount() == 0) {
             return null;
         }
@@ -278,7 +281,7 @@
     }
 
     /**
-     * Binds views for the given {@link KindSectionData} list.
+     * Binds views for the given {@link KindSectionData}.
      *
      * We create a structured name and phonetic name editor for each {@link DataKind} with a
      * {@link StructuredName#CONTENT_ITEM_TYPE} mime type.  The number and order of editors are
@@ -287,15 +290,15 @@
      * Empty name editors are never added and at least one structured name editor is always
      * displayed, even if it is empty.
      */
-    public void setState(KindSectionDataList kindSectionDataList,
+    public void setState(KindSectionData kindSectionData,
             ViewIdGenerator viewIdGenerator, CompactRawContactsEditorView.Listener listener,
             ValuesDelta primaryValuesDelta) {
-        mKindSectionDataList = kindSectionDataList;
+        mKindSectionData = kindSectionData;
         mViewIdGenerator = viewIdGenerator;
         mListener = listener;
 
-        // Set the icon using the first DataKind
-        final DataKind dataKind = mKindSectionDataList.getDataKind();
+        // Set the icon using the DataKind
+        final DataKind dataKind = mKindSectionData.getDataKind();
         if (dataKind != null) {
             mIcon.setImageDrawable(EditorUiUtils.getMimeTypeDrawable(getContext(),
                     dataKind.mimeType));
@@ -313,27 +316,26 @@
     private void rebuildFromState(ValuesDelta primaryValuesDelta) {
         mEditors.removeAllViews();
 
-        final String mimeType = mKindSectionDataList.getMimeType();
-        for (KindSectionData kindSectionData : mKindSectionDataList) {
-            if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                addNameEditorViews(kindSectionData.getAccountType(),
-                        primaryValuesDelta, kindSectionData.getRawContactDelta());
-            } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                addGroupEditorView(kindSectionData.getRawContactDelta(),
-                        kindSectionData.getDataKind());
+        final String mimeType = mKindSectionData.getMimeType();
+        if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+            addNameEditorViews(mKindSectionData.getAccountType(),
+                    primaryValuesDelta, mKindSectionData.getRawContactDelta());
+        } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
+            addGroupEditorView(mKindSectionData.getRawContactDelta(),
+                    mKindSectionData.getDataKind());
+        } else {
+            final Editor.EditorListener editorListener;
+            if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                editorListener = new OtherNameKindEditorListener();
+            } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                editorListener = new EventEditorListener();
             } else {
-                final Editor.EditorListener editorListener;
-                if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    editorListener = new OtherNameKindEditorListener();
-                } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    editorListener = new EventEditorListener();
-                } else {
-                    editorListener = new NonNameEditorListener();
-                }
-                for (ValuesDelta valuesDelta : kindSectionData.getVisibleValuesDeltas()) {
-                    addNonNameEditorView(kindSectionData.getRawContactDelta(),
-                            kindSectionData.getDataKind(), valuesDelta, editorListener);
-                }
+                editorListener = new NonNameEditorListener();
+            }
+            final List<ValuesDelta> valuesDeltas = mKindSectionData.getVisibleValuesDeltas();
+            for (int i = 0; i < valuesDeltas.size(); i++ ) {
+                addNonNameEditorView(mKindSectionData.getRawContactDelta(),
+                        mKindSectionData.getDataKind(), valuesDeltas.get(i), editorListener);
             }
         }
     }
@@ -442,9 +444,9 @@
      */
     public void updateEmptyEditors(boolean shouldAnimate) {
         final boolean isNameKindSection = StructuredName.CONTENT_ITEM_TYPE.equals(
-                mKindSectionDataList.getMimeType());
+                mKindSectionData.getMimeType());
         final boolean isGroupKindSection = GroupMembership.CONTENT_ITEM_TYPE.equals(
-                mKindSectionDataList.getMimeType());
+                mKindSectionData.getMimeType());
 
         if (isNameKindSection) {
             // The name kind section is always visible
@@ -531,7 +533,8 @@
         if (emptyEditors.size() > 1) {
             // If there is more than 1 empty editor, then remove it from the list of editors.
             int deleted = 0;
-            for (final View view : emptyEditors) {
+            for (int i = 0; i < emptyEditors.size(); i++) {
+                final View view = emptyEditors.get(i);
                 // If no child {@link View}s are being focused on within this {@link View}, then
                 // remove this empty editor. We can assume that at least one empty editor has
                 // focus. One way to get two empty editors is by deleting characters from a
@@ -546,9 +549,8 @@
             return;
         }
         // Determine if we should add a new empty editor
-        final DataKind dataKind = mKindSectionDataList.get(0).getDataKind();
-        final RawContactDelta rawContactDelta =
-                mKindSectionDataList.get(0).getRawContactDelta();
+        final DataKind dataKind = mKindSectionData.getDataKind();
+        final RawContactDelta rawContactDelta = mKindSectionData.getRawContactDelta();
         if (dataKind == null // There is nothing we can do.
                 // We have already reached the maximum number of editors, don't add any more.
                 || !RawContactModifier.canInsert(rawContactDelta, dataKind)
@@ -558,7 +560,7 @@
         }
         // Add a new empty editor
         if (mShowOneEmptyEditor) {
-            final String mimeType = mKindSectionDataList.getMimeType();
+            final String mimeType = mKindSectionData.getMimeType();
             if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) && mEditors.getChildCount() > 0) {
                 return;
             }
diff --git a/src/com/android/contacts/editor/CompactPhotoEditorView.java b/src/com/android/contacts/editor/CompactPhotoEditorView.java
index 899e22a..99abb73 100644
--- a/src/com/android/contacts/editor/CompactPhotoEditorView.java
+++ b/src/com/android/contacts/editor/CompactPhotoEditorView.java
@@ -16,13 +16,6 @@
 
 package com.android.contacts.editor;
 
-import com.android.contacts.R;
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
-import com.android.contacts.util.SchedulingUtils;
-import com.android.contacts.widget.QuickContactImageView;
-
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -36,6 +29,13 @@
 import android.view.ViewGroup;
 import android.widget.RelativeLayout;
 
+import com.android.contacts.R;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.model.ValuesDelta;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+import com.android.contacts.util.SchedulingUtils;
+import com.android.contacts.widget.QuickContactImageView;
+
 /**
  * Displays a photo and calls the host back when the user clicks it.
  */
diff --git a/src/com/android/contacts/editor/CompactRawContactsEditorView.java b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
index 922987b..c853981 100644
--- a/src/com/android/contacts/editor/CompactRawContactsEditorView.java
+++ b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
@@ -18,8 +18,10 @@
 
 import android.content.ContentUris;
 import android.content.Context;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -54,6 +56,8 @@
 import android.widget.TextView;
 
 import com.android.contacts.R;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.RawContactDelta;
 import com.android.contacts.common.model.RawContactDeltaList;
@@ -63,6 +67,7 @@
 import com.android.contacts.common.model.account.AccountDisplayInfoFactory;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.dataitem.CustomDataItem;
 import com.android.contacts.common.model.dataitem.DataKind;
 import com.android.contacts.common.util.AccountsListAdapter;
 import com.android.contacts.common.util.MaterialColorMapUtils;
@@ -76,6 +81,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeSet;
 
 /**
@@ -85,9 +91,6 @@
 
     static final String TAG = "CompactEditorView";
 
-    private static final KindSectionDataMapEntryComparator
-            KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR = new KindSectionDataMapEntryComparator();
-
     /**
      * Callbacks for hosts of {@link CompactRawContactsEditorView}s.
      */
@@ -195,26 +198,6 @@
         }
     }
 
-    /** Used to sort entire kind sections. */
-    private static final class KindSectionDataMapEntryComparator implements
-            Comparator<Map.Entry<String,KindSectionDataList>> {
-
-        final MimeTypeComparator mMimeTypeComparator = new MimeTypeComparator();
-
-        @Override
-        public int compare(Map.Entry<String, KindSectionDataList> entry1,
-                Map.Entry<String, KindSectionDataList> entry2) {
-            if (entry1 == entry2) return 0;
-            if (entry1 == null) return -1;
-            if (entry2 == null) return 1;
-
-            final String mimeType1 = entry1.getKey();
-            final String mimeType2 = entry2.getKey();
-
-            return mMimeTypeComparator.compare(mimeType1, mimeType2);
-        }
-    }
-
     /**
      * Sorts kinds roughly the same as quick contacts; we diverge in the following ways:
      * <ol>
@@ -260,34 +243,6 @@
         }
     }
 
-    /**
-     * Sorts primary accounts and google account types before others.
-     */
-    private static final class EditorComparator implements Comparator<KindSectionData> {
-
-        private RawContactDeltaComparator mRawContactDeltaComparator;
-
-        private EditorComparator(Context context) {
-            mRawContactDeltaComparator = new RawContactDeltaComparator(context);
-        }
-
-        @Override
-        public int compare(KindSectionData kindSectionData1, KindSectionData kindSectionData2) {
-            if (kindSectionData1 == kindSectionData2) return 0;
-            if (kindSectionData1 == null) return -1;
-            if (kindSectionData2 == null) return 1;
-
-            final RawContactDelta rawContactDelta1 = kindSectionData1.getRawContactDelta();
-            final RawContactDelta rawContactDelta2 = kindSectionData2.getRawContactDelta();
-
-            if (rawContactDelta1 == rawContactDelta2) return 0;
-            if (rawContactDelta1 == null) return -1;
-            if (rawContactDelta2 == null) return 1;
-
-            return mRawContactDeltaComparator.compare(rawContactDelta1, rawContactDelta2);
-        }
-    }
-
     public static class SavedState extends BaseSavedState {
 
         public static final Parcelable.Creator<SavedState> CREATOR =
@@ -326,7 +281,7 @@
 
     private ViewIdGenerator mViewIdGenerator;
     private MaterialColorMapUtils.MaterialPalette mMaterialPalette;
-    private long mPhotoId = -1;
+    private long mAggregatePhotoId = -1;
     private boolean mHasNewContact;
     private boolean mIsUserProfile;
     private AccountWithDataSet mPrimaryAccount;
@@ -334,7 +289,9 @@
     private long mRawContactIdToDisplayAlone = -1;
     private boolean mRawContactDisplayAloneIsReadOnly;
     private boolean mIsEditingReadOnlyRawContactWithNewContact;
-    private Map<String,KindSectionDataList> mKindSectionDataMap = new HashMap<>();
+    private KindSectionDataList mPhotoKindSectionDataList = new KindSectionDataList();
+    private Map<String, KindSectionData> mKindSectionDataMap = new HashMap<>();
+    private Set<String> mSortedMimetypes = new TreeSet<>(new MimeTypeComparator());
 
     // Account header
     private View mAccountHeaderContainer;
@@ -349,7 +306,7 @@
 
     private CompactPhotoEditorView mPhotoView;
     private ViewGroup mKindSectionViews;
-    private Map<String,List<CompactKindSectionView>> mKindSectionViewsMap = new HashMap<>();
+    private Map<String, CompactKindSectionView> mKindSectionViewMap = new HashMap<>();
     private View mMoreFields;
 
     private boolean mIsExpanded;
@@ -357,8 +314,6 @@
     private long mPhotoRawContactId;
     private ValuesDelta mPhotoValuesDelta;
 
-    private Pair<KindSectionData, ValuesDelta> mPrimaryNameKindSectionData;
-
     public CompactRawContactsEditorView(Context context) {
         super(context);
     }
@@ -486,11 +441,11 @@
     }
 
     private void unsetSuperPrimaryFromAllPhotos() {
-        final List<KindSectionData> kindSectionDataList =
-                mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
-        for (KindSectionData kindSectionData : kindSectionDataList) {
-            for (ValuesDelta valuesDelta : kindSectionData.getNonEmptyValuesDeltas()) {
-                valuesDelta.setSuperPrimary(false);
+        for (int i = 0; i < mPhotoKindSectionDataList.size(); i++) {
+            final KindSectionData kindSectionData = mPhotoKindSectionDataList.get(0);
+            final List<ValuesDelta> valuesDeltas = kindSectionData.getNonEmptyValuesDeltas();
+            for (int j = 0; j < valuesDeltas.size(); j++) {
+                valuesDeltas.get(j).setSuperPrimary(false);
             }
         }
     }
@@ -524,10 +479,8 @@
 
         final Bundle updatedPhotos = mListener == null ? null : mListener.getUpdatedPhotos();
 
-        final List<KindSectionData> kindSectionDataList =
-                mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
-        for (int i = 0; i < kindSectionDataList.size(); i++) {
-            final KindSectionData kindSectionData = kindSectionDataList.get(i);
+        for (int i = 0; i < mPhotoKindSectionDataList.size(); i++) {
+            final KindSectionData kindSectionData = mPhotoKindSectionDataList.get(i);
             final AccountType accountType = kindSectionData.getAccountType();
             final List<ValuesDelta> valuesDeltas = kindSectionData.getNonEmptyValuesDeltas();
             if (valuesDeltas.isEmpty()) continue;
@@ -570,15 +523,13 @@
      */
     public void setPrimaryPhoto(CompactPhotoSelectionFragment.Photo photo) {
         // Find the values delta to mark as primary
-        final KindSectionDataList kindSectionDataList =
-                mKindSectionDataMap.get(Photo.CONTENT_ITEM_TYPE);
         if (photo.kindSectionDataListIndex < 0
-                || photo.kindSectionDataListIndex >= kindSectionDataList.size()) {
+                || photo.kindSectionDataListIndex >= mPhotoKindSectionDataList.size()) {
             wlog("Invalid kind section data list index");
             return;
         }
         final KindSectionData kindSectionData =
-                kindSectionDataList.get(photo.kindSectionDataListIndex);
+                mPhotoKindSectionDataList.get(photo.kindSectionDataListIndex);
         final List<ValuesDelta> valuesDeltaList = kindSectionData.getNonEmptyValuesDeltas();
         if (photo.valuesDeltaListIndex >= valuesDeltaList.size()) {
             wlog("Invalid values delta list index");
@@ -596,23 +547,20 @@
     }
 
     public View getAggregationAnchorView() {
-        final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
-                StructuredName.CONTENT_ITEM_TYPE);
-        if (!kindSectionViews.isEmpty()) {
-            return mKindSectionViews.getChildAt(0).findViewById(R.id.anchor_view);
-        }
-        return null;
+        CompactKindSectionView nameView = mKindSectionViewMap.get(StructuredName.CONTENT_ITEM_TYPE);
+        return nameView != null ? nameView.getChildAt(0).findViewById(R.id.anchor_view) : null;
     }
 
     public void setGroupMetaData(Cursor groupMetaData) {
-        final List<CompactKindSectionView> kindSectionViews = getKindSectionViews(
-                GroupMembership.CONTENT_ITEM_TYPE);
-        for (CompactKindSectionView kindSectionView : kindSectionViews) {
-            kindSectionView.setGroupMetaData(groupMetaData);
-            if (mIsExpanded) {
-                kindSectionView.setHideWhenEmpty(false);
-                kindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
-            }
+        final CompactKindSectionView groupKindSectionView =
+                mKindSectionViewMap.get(GroupMembership.CONTENT_ITEM_TYPE);
+        if (groupKindSectionView == null) {
+            return;
+        }
+        groupKindSectionView.setGroupMetaData(groupMetaData);
+        if (mIsExpanded) {
+            groupKindSectionView.setHideWhenEmpty(false);
+            groupKindSectionView.updateEmptyEditors(/* shouldAnimate =*/ true);
         }
     }
 
@@ -629,13 +577,13 @@
         mIsEditingReadOnlyRawContactWithNewContact = isEditingReadOnlyRawContactWithNewContact;
 
         mKindSectionDataMap.clear();
-        mKindSectionViewsMap.clear();
+        mKindSectionViewMap.clear();
         mKindSectionViews.removeAllViews();
         mMoreFields.setVisibility(View.VISIBLE);
 
         mMaterialPalette = materialPalette;
         mViewIdGenerator = viewIdGenerator;
-        mPhotoId = photoId;
+        mAggregatePhotoId = photoId;
 
         mHasNewContact = hasNewContact;
         mIsUserProfile = isUserProfile;
@@ -658,15 +606,12 @@
             return;
         }
 
-
-        // Get the primary name kind section data
-        mPrimaryNameKindSectionData =
-                getOrCreateKindSectionDataList(StructuredName.CONTENT_ITEM_TYPE)
-                        .getEntryToWrite(/* id =*/ -1, mPrimaryAccount, mIsUserProfile);
-        if (mPrimaryNameKindSectionData != null) {
-            // Ensure that a structured name and photo exists
+        final KindSectionData nameSectionData =
+                mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
+        // Ensure that a structured name and photo exists
+        if (nameSectionData != null) {
             final RawContactDelta rawContactDelta =
-                    mPrimaryNameKindSectionData.first.getRawContactDelta();
+                    nameSectionData.getRawContactDelta();
             RawContactModifier.ensureKindExists(
                     rawContactDelta,
                     rawContactDelta.getAccountType(mAccountTypeManager),
@@ -700,7 +645,7 @@
             // TODO: Don't render any input fields. Eventually we will show a list of account
             // types and names but for now just show the account selector and hide the "More fields"
             // link.
-            addAccountInfo(rawContactDeltas);
+            addAccountInfo();
             mMoreFields.setVisibility(View.GONE);
         } else {
             setupCompactEditorNormally();
@@ -709,7 +654,7 @@
     }
 
     private void setupCompactEditorNormally() {
-        addAccountInfo(mRawContactDeltas);
+        addAccountInfo();
         addKindSectionViews();
 
         mMoreFields.setVisibility(hasMoreFields() ? View.VISIBLE : View.GONE);
@@ -750,11 +695,45 @@
                     continue;
                 }
 
-                final KindSectionDataList kindSectionDataList =
-                        getOrCreateKindSectionDataList(mimeType);
+                // Skip custom fields
+                // TODO: Handle them when we implement editing custom fields.
+                if (CustomDataItem.MIMETYPE_CUSTOM_FIELD.equals(mimeType)) {
+                    vlog("parse: " + i + " " + dataKind.mimeType + " dropped custom field");
+                    continue;
+                }
+
+                // Add all photo data.
+                if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    final KindSectionData photoKindSectionData =
+                            new KindSectionData(accountType, dataKind, rawContactDelta);
+                    mPhotoKindSectionDataList.add(photoKindSectionData);
+                    vlog("parse: " + i + " " + dataKind.mimeType + " " +
+                            photoKindSectionData.getValuesDeltas().size() + " value(s) " +
+                            photoKindSectionData.getNonEmptyValuesDeltas().size() +
+                            " non-empty value(s) " +
+                            photoKindSectionData.getVisibleValuesDeltas().size() +
+                            " visible value(s)");
+                    continue;
+                }
+
+                // Skip the non-writable names when we're auto creating a new writable contact.
+                if (mIsEditingReadOnlyRawContactWithNewContact
+                        && !accountType.areContactsWritable()
+                        && StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                    vlog("parse: " + i + " " + dataKind.mimeType + " dropped non-writable name");
+                    continue;
+                }
+
+                // Skip non-photo data that doesn't belong to the single raw contact we're editing.
+                if (mRawContactIdToDisplayAlone > 0 &&
+                        !rawContactDelta.getRawContactId().equals(mRawContactIdToDisplayAlone)) {
+                    continue;
+                }
+
                 final KindSectionData kindSectionData =
                         new KindSectionData(accountType, dataKind, rawContactDelta);
-                kindSectionDataList.add(kindSectionData);
+                mKindSectionDataMap.put(mimeType, kindSectionData);
+                mSortedMimetypes.add(mimeType);
 
                 vlog("parse: " + i + " " + dataKind.mimeType + " " +
                         kindSectionData.getValuesDeltas().size() + " value(s) " +
@@ -765,43 +744,161 @@
         }
     }
 
-    private KindSectionDataList getOrCreateKindSectionDataList(String mimeType) {
-        KindSectionDataList kindSectionDataList = mKindSectionDataMap.get(mimeType);
-        if (kindSectionDataList == null) {
-            kindSectionDataList = new KindSectionDataList();
-            mKindSectionDataMap.put(mimeType, kindSectionDataList);
-        }
-        return kindSectionDataList;
-    }
-
     private void addReadOnlyRawContactEditorViews() {
-        final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
+        mKindSectionViews.removeAllViews();
         final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
                 getContext());
+        final RawContactDelta state = mRawContactDeltas
+                .getByRawContactId(mRawContactIdToDisplayAlone);
+        final AccountType type = state.getAccountType(accountTypes);
 
-        for (int i = 0; i < mRawContactDeltas.size(); i++) {
-            final RawContactDelta rawContactDelta = mRawContactDeltas.get(i);
-            if (!rawContactDelta.isVisible()) continue;
-            final AccountType type = rawContactDelta.getAccountType(accountTypes);
-            if (type.areContactsWritable()) continue;
+        // Bail if invalid state or source
+        if (state == null || type == null) return;
 
-            final BaseRawContactEditorView editor = (BaseRawContactEditorView) inflater.inflate(
-                        R.layout.raw_contact_readonly_editor_view, mKindSectionViews, false);
-            editor.setCollapsed(false);
-            mKindSectionViews.addView(editor);
-            editor.setState(rawContactDelta, type, mViewIdGenerator, mIsUserProfile);
+        // Make sure we have StructuredName
+        RawContactModifier.ensureKindExists(state, type, StructuredName.CONTENT_ITEM_TYPE);
+
+        final AccountDisplayInfo account = mAccountDisplayInfoFactory
+                .getAccountDisplayInfoFor(state);
+
+        final String accountTypeLabel;
+        final String accountNameLabel;
+        if (mIsUserProfile) {
+            accountTypeLabel = EditorUiUtils.getAccountHeaderLabelForMyProfile(
+                    getContext(), account);
+            accountNameLabel = account.getNameLabel().toString();
+        } else {
+            accountTypeLabel = account.getTypeLabel().toString();
+            accountNameLabel = account.getNameLabel().toString();
         }
+
+        if (!account.hasDistinctName()) {
+            // Hide this view so the other view will be centered vertically
+            mAccountHeaderName.setVisibility(View.GONE);
+        } else {
+            mAccountHeaderName.setVisibility(View.VISIBLE);
+            mAccountHeaderName.setText(accountNameLabel);
+        }
+        mAccountHeaderType.setText(accountTypeLabel);
+        updateAccountHeaderContentDescription();
+
+        mAccountHeaderIcon.setImageDrawable(state.getRawContactAccountType(getContext())
+                .getDisplayIcon(getContext()));
+
+        ValuesDelta primary;
+
+        // Name
+        final Context context = getContext();
+        final Resources res = context.getResources();
+        primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
+        final String name = primary != null ? primary.getAsString(StructuredName.DISPLAY_NAME) :
+            getContext().getString(R.string.missing_name);
+        final Drawable nameDrawable = context.getDrawable(R.drawable.ic_person_24dp);
+        final String nameContentDescription = res.getString(R.string.header_name_entry);
+        bindData(nameDrawable, nameContentDescription, name, /* type */ null,
+                /* isFirstEntry */ true);
+
+        // Phones
+        final ArrayList<ValuesDelta> phones = state.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
+        final Drawable phoneDrawable = context.getDrawable(R.drawable.ic_phone_24dp);
+        final String phoneContentDescription = res.getString(R.string.header_phone_entry);
+        if (phones != null) {
+            boolean isFirstPhoneBound = true;
+            for (ValuesDelta phone : phones) {
+                final String phoneNumber = phone.getPhoneNumber();
+                if (TextUtils.isEmpty(phoneNumber)) {
+                    continue;
+                }
+                final String formattedNumber = PhoneNumberUtilsCompat.formatNumber(
+                        phoneNumber, phone.getPhoneNormalizedNumber(),
+                        GeoUtil.getCurrentCountryIso(getContext()));
+                CharSequence phoneType = null;
+                if (phone.hasPhoneType()) {
+                    phoneType = Phone.getTypeLabel(
+                            res, phone.getPhoneType(), phone.getPhoneLabel());
+                }
+                bindData(phoneDrawable, phoneContentDescription, formattedNumber, phoneType,
+                        isFirstPhoneBound, true);
+                isFirstPhoneBound = false;
+            }
+        }
+
+        // Emails
+        final ArrayList<ValuesDelta> emails = state.getMimeEntries(Email.CONTENT_ITEM_TYPE);
+        final Drawable emailDrawable = context.getDrawable(R.drawable.ic_email_24dp);
+        final String emailContentDescription = res.getString(R.string.header_email_entry);
+        if (emails != null) {
+            boolean isFirstEmailBound = true;
+            for (ValuesDelta email : emails) {
+                final String emailAddress = email.getEmailData();
+                if (TextUtils.isEmpty(emailAddress)) {
+                    continue;
+                }
+                CharSequence emailType = null;
+                if (email.hasEmailType()) {
+                    emailType = Email.getTypeLabel(
+                            res, email.getEmailType(), email.getEmailLabel());
+                }
+                bindData(emailDrawable, emailContentDescription, emailAddress, emailType,
+                        isFirstEmailBound);
+                isFirstEmailBound = false;
+            }
+        }
+
+        mKindSectionViews.setVisibility(mKindSectionViews.getChildCount() > 0 ? VISIBLE : GONE);
+        // Hide the "More fields" link
+        mMoreFields.setVisibility(GONE);
     }
 
-    // TODO: we have mRawContactDeltas, we don't need to pass the RawContactDeltaList to this method
-    private void addAccountInfo(RawContactDeltaList rawContactDeltas) {
+    protected void updateAccountHeaderContentDescription() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append(EditorUiUtils.getAccountInfoContentDescription(
+                mAccountHeaderName.getText(), mAccountHeaderType.getText()));
+        mAccountHeaderContainer.setContentDescription(builder);
+    }
+
+    private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
+            CharSequence type, boolean isFirstEntry) {
+        bindData(icon, iconContentDescription, data, type, isFirstEntry, false);
+    }
+
+    private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
+            CharSequence type, boolean isFirstEntry, boolean forceLTR) {
+        final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+        final View field = inflater.inflate(R.layout.item_read_only_field, mKindSectionViews,
+                false);
+        if (isFirstEntry) {
+            final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
+            imageView.setImageDrawable(icon);
+            imageView.setContentDescription(iconContentDescription);
+        } else {
+            final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
+            imageView.setVisibility(View.INVISIBLE);
+            imageView.setContentDescription(null);
+        }
+        final TextView dataView = (TextView) field.findViewById(R.id.data);
+        dataView.setText(data);
+        if (forceLTR) {
+            dataView.setTextDirection(View.TEXT_DIRECTION_LTR);
+        }
+        final TextView typeView = (TextView) field.findViewById(R.id.type);
+        if (!TextUtils.isEmpty(type)) {
+            typeView.setText(type);
+        } else {
+            typeView.setVisibility(View.GONE);
+        }
+        mKindSectionViews.addView(field);
+    }
+
+    private void addAccountInfo() {
         mAccountHeaderContainer.setVisibility(View.GONE);
         mRawContactContainer.setVisibility(View.GONE);
-
-        if (mPrimaryNameKindSectionData == null) return;
+        final KindSectionData nameSectionData =
+                mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
+        if (nameSectionData == null) return;
         final RawContactDelta rawContactDelta =
-                mPrimaryNameKindSectionData.first.getRawContactDelta();
+                nameSectionData.getRawContactDelta();
 
         final AccountDisplayInfo account =
                 mAccountDisplayInfoFactory.getAccountDisplayInfoFor(rawContactDelta);
@@ -820,14 +917,14 @@
             } else {
                 addAccountHeader(accountLabel);
             }
-        } else if (mIsUserProfile || !shouldHideAccountContainer(rawContactDeltas)) {
+        } else if (mIsUserProfile || !shouldHideAccountContainer(mRawContactDeltas)) {
             addAccountHeader(accountLabel);
         }
 
         // The raw contact selector should only display linked raw contacts that can be edited in
         // the full editor (i.e. they are not newly created raw contacts)
         final RawContactAccountListAdapter adapter =  new RawContactAccountListAdapter(getContext(),
-                getRawContactDeltaListForSelector(rawContactDeltas));
+                getRawContactDeltaListForSelector(mRawContactDeltas));
         if (adapter.getCount() > 0) {
             final String accountsSummary = getResources().getQuantityString(
                     R.plurals.compact_editor_linked_contacts_selector_title,
@@ -842,7 +939,8 @@
         Collections.sort(rawContactDeltas, new RawContactDeltaComparator(getContext()));
 
         final RawContactDeltaList result = new RawContactDeltaList();
-        for (RawContactDelta rawContactDelta : rawContactDeltas) {
+        for (int i = 0; i < rawContactDeltas.size(); i++) {
+            final RawContactDelta rawContactDelta = rawContactDeltas.get(i);
             if (rawContactDelta.isVisible() && rawContactDelta.getRawContactId() > 0) {
                 // Only add raw contacts that can be opened in the editor
                 result.add(rawContactDelta);
@@ -888,9 +986,11 @@
         mAccountHeaderType.setText(selectorTitle);
 
         // Set the icon
-        if (mPrimaryNameKindSectionData != null) {
+        final KindSectionData nameSectionData =
+                mKindSectionDataMap.get(StructuredName.CONTENT_ITEM_TYPE);
+        if (nameSectionData != null) {
             final RawContactDelta rawContactDelta =
-                    mPrimaryNameKindSectionData.first.getRawContactDelta();
+                    nameSectionData.getRawContactDelta();
             if (rawContactDelta != null) {
                 final AccountType accountType =
                         rawContactDelta.getRawContactAccountType(getContext());
@@ -983,12 +1083,14 @@
     }
 
     private void addPhotoView() {
-        // Get the kind section data and values delta that we will display in the photo view
-        final KindSectionDataList kindSectionDataList =
-                getOrCreateKindSectionDataList(Photo.CONTENT_ITEM_TYPE);
-        final Pair<KindSectionData,ValuesDelta> photoToDisplay =
-                kindSectionDataList.getEntryToDisplay(mPhotoId);
-        if (photoToDisplay == null) {
+        // Get the kind section data and values delta that we will display in the photo view. Either
+        // the aggregate photo or the photo from the raw contact that is being edited.
+        final Pair<KindSectionData, ValuesDelta> photo =
+                mPhotoKindSectionDataList.getEntryToDisplay(
+                        mRawContactIdToDisplayAlone > 0
+                                ? mRawContactIdToDisplayAlone
+                                : mAggregatePhotoId);
+        if (photo == null) {
             wlog("photo: no kind section data parsed");
             mPhotoView.setVisibility(View.GONE);
             return;
@@ -997,92 +1099,57 @@
         }
 
         // Set the photo view
-        mPhotoView.setPhoto(photoToDisplay.second, mMaterialPalette);
+        mPhotoView.setPhoto(photo.second, mMaterialPalette);
 
-        // Find the raw contact ID and values delta that will be written when the photo is edited
-        final Pair<KindSectionData, ValuesDelta> photoToWrite = kindSectionDataList.getEntryToWrite(
-                mPhotoId, mPrimaryAccount, mIsUserProfile);
-        if (photoToWrite == null) {
+        // If we're showing an aggregate photo, set it to read only.
+        if (mRawContactIdToDisplayAlone < 1) {
             mPhotoView.setReadOnly(true);
             return;
         }
         mPhotoView.setReadOnly(false);
-        mPhotoRawContactId = photoToWrite.first.getRawContactDelta().getRawContactId();
-        mPhotoValuesDelta = photoToWrite.second;
+        mPhotoRawContactId = photo.first.getRawContactDelta().getRawContactId();
+        mPhotoValuesDelta = photo.second;
     }
 
     private void addKindSectionViews() {
-        // Sort the kinds
-        final TreeSet<Map.Entry<String,KindSectionDataList>> entries =
-                new TreeSet<>(KIND_SECTION_DATA_MAP_ENTRY_COMPARATOR);
-        entries.addAll(mKindSectionDataMap.entrySet());
-
-        vlog("kind: " + entries.size() + " kindSection(s)");
         int i = -1;
-        for (Map.Entry<String, KindSectionDataList> entry : entries) {
+
+        for (String mimeType : mSortedMimetypes) {
             i++;
-
-            final String mimeType = entry.getKey();
-
+            final CompactKindSectionView kindSectionView;
+            // TODO: Since we don't have a primary name kind anymore, refactor and collapse
+            // these two branches and the following code paths.
             if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                if (mPrimaryNameKindSectionData == null) {
+                final KindSectionData nameSectionData = mKindSectionDataMap.get(mimeType);
+                if (nameSectionData == null) {
                     vlog("kind: " + i + " " + mimeType + " dropped");
                     continue;
                 }
                 vlog("kind: " + i + " " + mimeType + " using first entry only");
-                final KindSectionDataList kindSectionDataList = new KindSectionDataList();
-                kindSectionDataList.add(mPrimaryNameKindSectionData.first);
-                final CompactKindSectionView kindSectionView = inflateKindSectionView(
-                        mKindSectionViews, kindSectionDataList, mimeType,
-                        mPrimaryNameKindSectionData.second);
-                mKindSectionViews.addView(kindSectionView);
-
-                // Keep a pointer to all the KindSectionsViews for each mimeType
-                getKindSectionViews(mimeType).add(kindSectionView);
+                kindSectionView = inflateKindSectionView(
+                        mKindSectionViews, nameSectionData, mimeType,
+                        nameSectionData.getValuesDeltas().get(0));
             } else {
-                final KindSectionDataList kindSectionDataList = entry.getValue();
+                final KindSectionData kindSectionData = mKindSectionDataMap.get(mimeType);
 
                 // Ignore mime types that we've already handled
                 if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
                     vlog("kind: " + i + " " + mimeType + " dropped");
                     continue;
                 }
-
-                // Don't show more than one group editor on the compact editor.
-                // Groups will still be editable for each raw contact individually on the full editor.
-                if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)
-                        && kindSectionDataList.size() > 1) {
-                    vlog("kind: " + i + " " + mimeType + " dropped");
-                    continue;
-                }
-
-                if (kindSectionDataList != null && !kindSectionDataList.isEmpty()) {
-                    vlog("kind: " + i + " " + mimeType + " " + kindSectionDataList.size() +
-                            " kindSectionData(s)");
-
-                    final CompactKindSectionView kindSectionView = inflateKindSectionView(
-                            mKindSectionViews, kindSectionDataList, mimeType,
-                            /* primaryValueDelta =*/ null);
-                    mKindSectionViews.addView(kindSectionView);
-
-                    // Keep a pointer to all the KindSectionsViews for each mimeType
-                    getKindSectionViews(mimeType).add(kindSectionView);
-                }
+                kindSectionView = inflateKindSectionView(
+                        mKindSectionViews, kindSectionData, mimeType,
+                        /* primaryValueDelta =*/ null);
             }
-        }
-    }
+            mKindSectionViews.addView(kindSectionView);
 
-    private List<CompactKindSectionView> getKindSectionViews(String mimeType) {
-        List<CompactKindSectionView> kindSectionViews = mKindSectionViewsMap.get(mimeType);
-        if (kindSectionViews == null) {
-            kindSectionViews = new ArrayList<>();
-            mKindSectionViewsMap.put(mimeType, kindSectionViews);
+            // Keep a pointer to the KindSectionView for each mimeType
+            mKindSectionViewMap.put(mimeType, kindSectionView);
         }
-        return kindSectionViews;
     }
 
     private CompactKindSectionView inflateKindSectionView(ViewGroup viewGroup,
-            KindSectionDataList kindSectionDataList, String mimeType,
+            KindSectionData kindSectionData, String mimeType,
             ValuesDelta primaryValuesDelta) {
         final CompactKindSectionView kindSectionView = (CompactKindSectionView)
                 mLayoutInflater.inflate(R.layout.compact_item_kind_section, viewGroup,
@@ -1100,12 +1167,7 @@
         // they will be the only types you add new values to initially for new contacts
         kindSectionView.setShowOneEmptyEditor(true);
 
-        // Sort non-name editors so they wind up in the order we want
-        if (!StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
-            Collections.sort(kindSectionDataList, new EditorComparator(getContext()));
-        }
-
-        kindSectionView.setState(kindSectionDataList, mViewIdGenerator, mListener,
+        kindSectionView.setState(kindSectionData, mViewIdGenerator, mListener,
                 primaryValuesDelta);
 
         return kindSectionView;
@@ -1121,10 +1183,7 @@
     }
 
     private CompactKindSectionView getPrimaryNameKindSectionView() {
-        final List<CompactKindSectionView> kindSectionViews
-                = mKindSectionViewsMap.get(StructuredName.CONTENT_ITEM_TYPE);
-        return kindSectionViews == null || kindSectionViews.isEmpty()
-                ? null : kindSectionViews.get(0);
+        return mKindSectionViewMap.get(StructuredName.CONTENT_ITEM_TYPE);
     }
 
     private void showAllFields() {
@@ -1142,11 +1201,9 @@
     }
 
     private boolean hasMoreFields() {
-        for (List<CompactKindSectionView> sections : mKindSectionViewsMap.values()) {
-            for (CompactKindSectionView section : sections) {
-                if (section.getVisibility() != View.VISIBLE) {
-                    return true;
-                }
+        for (CompactKindSectionView section : mKindSectionViewMap.values()) {
+            if (section.getVisibility() != View.VISIBLE) {
+                return true;
             }
         }
         return false;
diff --git a/src/com/android/contacts/editor/KindSectionData.java b/src/com/android/contacts/editor/KindSectionData.java
index 7e2899f..03aa667 100644
--- a/src/com/android/contacts/editor/KindSectionData.java
+++ b/src/com/android/contacts/editor/KindSectionData.java
@@ -16,14 +16,14 @@
 
 package com.android.contacts.editor;
 
+import android.text.TextUtils;
+
 import com.android.contacts.common.model.RawContactDelta;
 import com.android.contacts.common.model.ValuesDelta;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountType.EditField;
 import com.android.contacts.common.model.dataitem.DataKind;
 
-import android.text.TextUtils;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -118,4 +118,8 @@
     public RawContactDelta getRawContactDelta() {
         return mRawContactDelta;
     }
+
+    public String getMimeType() {
+        return mDataKind.mimeType;
+    }
 }
diff --git a/src/com/android/contacts/editor/KindSectionView.java b/src/com/android/contacts/editor/KindSectionView.java
deleted file mode 100644
index dddc6aa..0000000
--- a/src/com/android/contacts/editor/KindSectionView.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-package com.android.contacts.editor;
-
-import android.content.Context;
-import android.provider.ContactsContract.Data;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.contacts.R;
-import com.android.contacts.common.model.RawContactDelta;
-import com.android.contacts.common.model.RawContactModifier;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.model.dataitem.DataKind;
-import com.android.contacts.editor.Editor.EditorListener;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Custom view for an entire section of data as segmented by
- * {@link DataKind} around a {@link Data#MIMETYPE}. This view shows a
- * section header and a trigger for adding new {@link Data} rows.
- */
-public class KindSectionView extends LinearLayout implements EditorListener {
-
-    public interface Listener {
-
-        /**
-         * Invoked when any editor that is displayed in this section view is deleted by the user.
-         */
-        public void onDeleteRequested(Editor editor);
-    }
-
-    private ViewGroup mEditors;
-    private ImageView mIcon;
-
-    private DataKind mKind;
-    private RawContactDelta mState;
-    private boolean mReadOnly;
-
-    private ViewIdGenerator mViewIdGenerator;
-
-    private LayoutInflater mInflater;
-
-    private Listener mListener;
-
-    public KindSectionView(Context context) {
-        this(context, null);
-    }
-
-    public KindSectionView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public void setEnabled(boolean enabled) {
-        super.setEnabled(enabled);
-        if (mEditors != null) {
-            int childCount = mEditors.getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                mEditors.getChildAt(i).setEnabled(enabled);
-            }
-        }
-
-        updateEmptyEditors(/* shouldAnimate = */ true);
-    }
-
-    public boolean isReadOnly() {
-        return mReadOnly;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void onFinishInflate() {
-        setDrawingCacheEnabled(true);
-        setAlwaysDrawnWithCacheEnabled(true);
-
-        mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-        mEditors = (ViewGroup) findViewById(R.id.kind_editors);
-        mIcon = (ImageView) findViewById(R.id.kind_icon);
-    }
-
-    @Override
-    public void onDeleteRequested(Editor editor) {
-        if (getEditorCount() == 1) {
-            // If there is only 1 editor in the section, then don't allow the user to delete it.
-            // Just clear the fields in the editor.
-            editor.clearAllFields();
-        } else {
-            // If there is a listener, let it decide whether to delete the Editor or the entire
-            // KindSectionView so that there is no jank from both animations happening in succession.
-            if (mListener != null) {
-                editor.markDeleted();
-                mListener.onDeleteRequested(editor);
-            } else {
-                editor.deleteEditor();
-            }
-        }
-    }
-
-    @Override
-    public void onRequest(int request) {
-        // If a field has become empty or non-empty, then check if another row
-        // can be added dynamically.
-        if (request == FIELD_TURNED_EMPTY || request == FIELD_TURNED_NON_EMPTY) {
-            updateEmptyEditors(/* shouldAnimate = */ true);
-        }
-    }
-
-    public void setListener(Listener listener) {
-        mListener = listener;
-    }
-
-    public void setState(DataKind kind, RawContactDelta state, boolean readOnly,
-            ViewIdGenerator vig) {
-        mKind = kind;
-        mState = state;
-        mReadOnly = readOnly;
-        mViewIdGenerator = vig;
-
-        setId(mViewIdGenerator.getId(state, kind, null, ViewIdGenerator.NO_VIEW_INDEX));
-
-        // TODO: handle resources from remote packages
-        final String titleString = (kind.titleRes == -1 || kind.titleRes == 0)
-                ? ""
-                : getResources().getString(kind.titleRes);
-        mIcon.setContentDescription(titleString);
-
-        mIcon.setImageDrawable(EditorUiUtils.getMimeTypeDrawable(getContext(), kind.mimeType));
-        if (mIcon.getDrawable() == null) {
-            mIcon.setContentDescription(null);
-        }
-
-        rebuildFromState();
-        updateEmptyEditors(/* shouldAnimate = */ false);
-    }
-
-    /**
-     * Build editors for all current {@link #mState} rows.
-     */
-    private void rebuildFromState() {
-        // Remove any existing editors
-        mEditors.removeAllViews();
-
-        // Check if we are displaying anything here
-        boolean hasEntries = mState.hasMimeEntries(mKind.mimeType);
-
-        if (hasEntries) {
-            for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) {
-                // Skip entries that aren't visible
-                if (!entry.isVisible()) continue;
-
-                createEditorView(entry);
-            }
-        }
-    }
-
-
-    /**
-     * Creates an EditorView for the given entry. This function must be used while constructing
-     * the views corresponding to the the object-model. The resulting EditorView is also added
-     * to the end of mEditors
-     */
-    private View createEditorView(ValuesDelta entry) {
-        final View view;
-        final int layoutResId = EditorUiUtils.getLayoutResourceId(mKind.mimeType);
-        try {
-            view = mInflater.inflate(layoutResId, mEditors, false);
-        } catch (Exception e) {
-            throw new RuntimeException(
-                    "Cannot allocate editor with layout resource ID " +
-                    layoutResId + " for MIME type " + mKind.mimeType +
-                    " with error " + e.toString());
-        }
-        view.setEnabled(isEnabled());
-        if (view instanceof Editor) {
-            Editor editor = (Editor) view;
-            editor.setDeletable(true);
-            editor.setValues(mKind, entry, mState, mReadOnly, mViewIdGenerator);
-            editor.setEditorListener(this);
-        }
-        mEditors.addView(view);
-        return view;
-    }
-
-    /**
-     * Updates the editors being displayed to the user removing extra empty
-     * {@link Editor}s, so there is only max 1 empty {@link Editor} view at a time.
-     */
-    public void updateEmptyEditors(boolean shouldAnimate) {
-
-        final List<View> emptyEditors = getEmptyEditors();
-
-        // If there is more than 1 empty editor, then remove it from the list of editors.
-        if (emptyEditors.size() > 1) {
-            for (final View emptyEditorView : emptyEditors) {
-                // If no child {@link View}s are being focused on within this {@link View}, then
-                // remove this empty editor. We can assume that at least one empty editor has focus.
-                // The only way to get two empty editors is by deleting characters from a non-empty
-                // editor, in which case this editor has focus.
-                if (emptyEditorView.findFocus() == null) {
-                    final Editor editor = (Editor) emptyEditorView;
-                    if (shouldAnimate) {
-                        editor.deleteEditor();
-                    } else {
-                        mEditors.removeView(emptyEditorView);
-                    }
-                }
-            }
-        } else if (mKind == null) {
-            // There is nothing we can do.
-            return;
-        } else if (isReadOnly()) {
-            // We don't show empty editors for read only data kinds.
-            return;
-        } else if (!RawContactModifier.canInsert(mState, mKind)) {
-            // We have already reached the maximum number of editors. Lets not add any more.
-            return;
-        } else if (emptyEditors.size() == 1) {
-            // We have already reached the maximum number of empty editors. Lets not add any more.
-            return;
-        } else {
-            final ValuesDelta values = RawContactModifier.insertChild(mState, mKind);
-            final View newField = createEditorView(values);
-            if (shouldAnimate) {
-                newField.setVisibility(View.GONE);
-                EditorAnimator.getInstance().showFieldFooter(newField);
-            }
-        }
-    }
-
-    /**
-     * Returns a list of empty editor views in this section.
-     */
-    private List<View> getEmptyEditors() {
-        List<View> emptyEditorViews = new ArrayList<View>();
-        for (int i = 0; i < mEditors.getChildCount(); i++) {
-            View view = mEditors.getChildAt(i);
-            if (((Editor) view).isEmpty()) {
-                emptyEditorViews.add(view);
-            }
-        }
-        return emptyEditorViews;
-    }
-
-    public int getEditorCount() {
-        return mEditors.getChildCount();
-    }
-
-    public DataKind getKind() {
-        return mKind;
-    }
-}
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
deleted file mode 100644
index f69c935..0000000
--- a/src/com/android/contacts/editor/PhotoEditorView.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-package com.android.contacts.editor;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.DisplayPhoto;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-
-import com.android.contacts.R;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageProvider;
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.model.RawContactDelta;
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.common.ContactsUtils;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.model.dataitem.DataKind;
-import com.android.contacts.util.ContactPhotoUtils;
-
-/**
- * Simple editor for {@link Photo}.
- */
-public class PhotoEditorView extends LinearLayout implements Editor {
-
-    private ImageView mPhotoImageView;
-    private Button mChangeButton;
-    private RadioButton mPrimaryCheckBox;
-
-    private ValuesDelta mEntry;
-    private EditorListener mListener;
-    private ContactPhotoManager mContactPhotoManager;
-
-    private boolean mHasSetPhoto = false;
-
-    public PhotoEditorView(Context context) {
-        super(context);
-    }
-
-    public PhotoEditorView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public void setEnabled(boolean enabled) {
-        super.setEnabled(enabled);
-    }
-
-    @Override
-    public void editNewlyAddedField() {
-        // Never called, since the user never adds a new photo-editor;
-        // you can only change the picture in an existing editor.
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mContactPhotoManager = ContactPhotoManager.getInstance(getContext());
-        mPhotoImageView = (ImageView) findViewById(R.id.photo);
-        mPrimaryCheckBox = (RadioButton) findViewById(R.id.primary_checkbox);
-        mChangeButton = (Button) findViewById(R.id.change_button);
-        mPrimaryCheckBox = (RadioButton) findViewById(R.id.primary_checkbox);
-        if (mChangeButton != null) {
-            mChangeButton.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (mListener != null) {
-                        mListener.onRequest(EditorListener.REQUEST_PICK_PHOTO);
-                    }
-                }
-            });
-        }
-        // Turn off own state management. We do this ourselves on rotation.
-        mPrimaryCheckBox.setSaveEnabled(false);
-        mPrimaryCheckBox.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mListener != null) {
-                    mListener.onRequest(EditorListener.REQUEST_PICK_PRIMARY_PHOTO);
-                }
-            }
-        });
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onFieldChanged(String column, String value) {
-        throw new UnsupportedOperationException("Photos don't support direct field changes");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void setValues(DataKind kind, ValuesDelta values, RawContactDelta state, boolean readOnly,
-            ViewIdGenerator vig) {
-        mEntry = values;
-
-        setId(vig.getId(state, kind, values, 0));
-
-        mPrimaryCheckBox.setChecked(values != null && values.isSuperPrimary());
-
-        if (values != null) {
-            // Try decoding photo if actual entry
-            final byte[] photoBytes = values.getAsByteArray(Photo.PHOTO);
-            if (photoBytes != null) {
-                final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
-                        photoBytes.length);
-
-                mPhotoImageView.setImageBitmap(photo);
-                mHasSetPhoto = true;
-                mEntry.setFromTemplate(false);
-
-                if (values.getAfter() == null || values.getAfter().get(Photo.PHOTO) == null) {
-                    // If the user hasn't updated the PHOTO value, then PHOTO_FILE_ID may contain
-                    // a reference to a larger version of PHOTO that we can bind to the UI.
-                    // Otherwise, we need to wait for a call to #setFullSizedPhoto() to update
-                    // our full sized image.
-                    final Integer photoFileId = values.getAsInteger(Photo.PHOTO_FILE_ID);
-                    if (photoFileId != null) {
-                        final Uri photoUri = DisplayPhoto.CONTENT_URI.buildUpon()
-                                .appendPath(photoFileId.toString()).build();
-                        setFullSizedPhoto(photoUri);
-                    }
-                }
-
-            } else {
-                resetDefault();
-            }
-        } else {
-            resetDefault();
-        }
-    }
-
-    /**
-     * Whether to display a "Primary photo" RadioButton. This is only needed if there are multiple
-     * candidate photos.
-     */
-    public void setShowPrimary(boolean showPrimaryCheckBox) {
-        mPrimaryCheckBox.setVisibility(showPrimaryCheckBox ? View.VISIBLE : View.GONE);
-    }
-
-    /**
-     * Return true if a valid {@link Photo} has been set.
-     */
-    public boolean hasSetPhoto() {
-        return mHasSetPhoto;
-    }
-
-    /**
-     * Assign the given {@link Bitmap} as the new value for the sake of building
-     * {@link ValuesDelta}. We may as well bind a thumbnail to the UI while we are at it.
-     */
-    public void setPhotoEntry(Bitmap photo) {
-        if (photo == null) {
-            // Clear any existing photo and return
-            mEntry.put(Photo.PHOTO, (byte[])null);
-            resetDefault();
-            return;
-        }
-
-        final int size = ContactsUtils.getThumbnailSize(getContext());
-        final Bitmap scaled = Bitmap.createScaledBitmap(photo, size, size, false);
-
-        mPhotoImageView.setImageBitmap(scaled);
-        mHasSetPhoto = true;
-        mEntry.setFromTemplate(false);
-
-        // When the user chooses a new photo mark it as super primary
-        mEntry.setSuperPrimary(true);
-
-        // Even though high-res photos cannot be saved by passing them via
-        // an EntityDeltaList (since they cause the Bundle size limit to be
-        // exceeded), we still pass a low-res thumbnail. This simplifies
-        // code all over the place, because we don't have to test whether
-        // there is a change in EITHER the delta-list OR a changed photo...
-        // this way, there is always a change in the delta-list.
-        final byte[] compressed = ContactPhotoUtils.compressBitmap(scaled);
-        if (compressed != null) {
-            mEntry.setPhoto(compressed);
-        }
-    }
-
-    /**
-     * Bind the {@param photoUri}'s photo to editor's UI. This doesn't affect {@link ValuesDelta}.
-     */
-    public void setFullSizedPhoto(Uri photoUri) {
-        if (photoUri != null) {
-            final DefaultImageProvider fallbackToPreviousImage = new DefaultImageProvider() {
-                @Override
-                public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
-                        DefaultImageRequest defaultImageRequest) {
-                    // Before we finish setting the full sized image, don't change the current
-                    // image that is set in any way.
-                }
-            };
-            mContactPhotoManager.loadPhoto(mPhotoImageView, photoUri,
-                    mPhotoImageView.getWidth(), /* darkTheme = */ false, /* isCircular = */ false,
-                    /* defaultImageRequest = */ null, fallbackToPreviousImage);
-        }
-    }
-
-    /**
-     * Set the super primary bit on the photo.
-     */
-    public void setSuperPrimary(boolean superPrimary) {
-        mEntry.put(Photo.IS_SUPER_PRIMARY, superPrimary ? 1 : 0);
-    }
-
-    protected void resetDefault() {
-        // Invalid photo, show default "add photo" place-holder
-        mPhotoImageView.setImageDrawable(
-                ContactPhotoManager.getDefaultAvatarDrawableForContact(getResources(), false, null));
-        mHasSetPhoto = false;
-        mEntry.setFromTemplate(true);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void setEditorListener(EditorListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    public void setDeletable(boolean deletable) {
-        // Photo is not deletable
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return !mHasSetPhoto;
-    }
-
-    @Override
-    public void markDeleted() {
-        // Photo is not deletable
-    }
-
-    @Override
-    public void deleteEditor() {
-        // Photo is not deletable
-    }
-
-    @Override
-    public void clearAllFields() {
-        resetDefault();
-    }
-
-    /**
-     * The change drop down menu should be anchored to this view.
-     */
-    public View getChangeAnchorView() {
-        return mChangeButton;
-    }
-}
diff --git a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java b/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
deleted file mode 100644
index d55403b..0000000
--- a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-package com.android.contacts.editor;
-
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.RawContacts;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.contacts.common.model.account.AccountDisplayInfo;
-import com.android.contacts.common.model.account.AccountDisplayInfoFactory;
-import com.android.contacts.R;
-import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
-import com.android.contacts.common.model.RawContactModifier;
-import com.android.contacts.common.model.RawContactDelta;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.model.account.AccountType;
-import com.android.contacts.common.model.account.AccountWithDataSet;
-import com.android.contacts.common.model.dataitem.DataKind;
-
-import java.util.ArrayList;
-
-/**
- * Custom view that displays external contacts in the edit screen.
- */
-public class RawContactReadOnlyEditorView extends BaseRawContactEditorView
-        implements OnClickListener {
-    private LayoutInflater mInflater;
-
-    private AccountDisplayInfoFactory mAccountDisplayInfoFactory;
-
-    private TextView mName;
-    private Button mEditExternallyButton;
-    private ViewGroup mGeneral;
-
-    private TextView mAccountHeaderTypeTextView;
-    private TextView mAccountHeaderNameTextView;
-    private ImageView mAccountIconImageView;
-
-    private String mAccountName;
-    private String mAccountType;
-    private String mDataSet;
-    private long mRawContactId = -1;
-
-    public RawContactReadOnlyEditorView(Context context) {
-        super(context);
-    }
-
-    public RawContactReadOnlyEditorView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-
-    /** {@inheritDoc} */
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mInflater = (LayoutInflater)getContext().getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-
-        mName = (TextView) findViewById(R.id.read_only_name);
-        mEditExternallyButton = (Button) findViewById(R.id.button_edit_externally);
-        mEditExternallyButton.setOnClickListener(this);
-        mGeneral = (ViewGroup)findViewById(R.id.sect_general);
-
-        mAccountHeaderTypeTextView = (TextView) findViewById(R.id.account_type);
-        mAccountHeaderNameTextView = (TextView) findViewById(R.id.account_name);
-        mAccountIconImageView = (ImageView) findViewById(R.id.account_type_icon);
-
-        mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forAllAccounts(getContext());
-    }
-
-    /**
-     * Set the internal state for this view, given a current
-     * {@link RawContactDelta} state and the {@link AccountType} that
-     * apply to that state.
-     */
-    @Override
-    public void setState(RawContactDelta state, AccountType type, ViewIdGenerator vig,
-            boolean isProfile) {
-        // Remove any existing sections
-        mGeneral.removeAllViews();
-
-        // Bail if invalid state or source
-        if (state == null || type == null) return;
-
-        // Make sure we have StructuredName
-        RawContactModifier.ensureKindExists(state, type, StructuredName.CONTENT_ITEM_TYPE);
-
-        // Fill in the header info
-        mAccountName = state.getAccountName();
-        mAccountType = state.getAccountType();
-        mDataSet = state.getDataSet();
-
-
-        final AccountDisplayInfo account = mAccountDisplayInfoFactory
-                .getAccountDisplayInfoFor(state);
-
-        final String accountTypeLabel;
-        final String accountNameLabel;
-        if (isProfile) {
-            accountTypeLabel = EditorUiUtils.getAccountHeaderLabelForMyProfile(
-                    getContext(), account);
-            accountNameLabel = account.getNameLabel().toString();
-        } else {
-            accountTypeLabel = account.getTypeLabel().toString();
-            accountNameLabel = account.getNameLabel().toString();
-        }
-
-        if (!account.hasDistinctName()) {
-            // Hide this view so the other view will be centered vertically
-            mAccountHeaderNameTextView.setVisibility(View.GONE);
-        } else {
-            mAccountHeaderNameTextView.setVisibility(View.VISIBLE);
-            mAccountHeaderNameTextView.setText(accountNameLabel);
-        }
-        mAccountHeaderTypeTextView.setText(accountTypeLabel);
-        updateAccountHeaderContentDescription();
-
-        mAccountIconImageView.setImageDrawable(state.getRawContactAccountType(getContext())
-                .getDisplayIcon(getContext()));
-
-        // TODO: Expose data set in the UI somehow?
-
-        mRawContactId = state.getRawContactId();
-
-        ValuesDelta primary;
-
-        // Photo
-        DataKind kind = type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
-        if (kind != null) {
-            RawContactModifier.ensureKindExists(state, type, Photo.CONTENT_ITEM_TYPE);
-            boolean hasPhotoEditor = type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null;
-            setHasPhotoEditor(hasPhotoEditor);
-            primary = state.getPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
-            getPhotoEditor().setValues(kind, primary, state, !type.areContactsWritable(), vig);
-        }
-
-        // Name
-        primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
-        mName.setText(primary != null ? primary.getAsString(StructuredName.DISPLAY_NAME) :
-                getContext().getString(R.string.missing_name));
-
-        if (type.getEditContactActivityClassName() != null) {
-            mEditExternallyButton.setVisibility(View.VISIBLE);
-        } else {
-            mEditExternallyButton.setVisibility(View.GONE);
-        }
-
-        final Resources res = getContext().getResources();
-        // Phones
-        final ArrayList<ValuesDelta> phones = state.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
-        final Drawable phoneDrawable = getResources().getDrawable(R.drawable.ic_phone_24dp);
-        final String phoneContentDescription = res.getString(R.string.header_phone_entry);
-        if (phones != null) {
-            boolean isFirstPhoneBound = true;
-            for (ValuesDelta phone : phones) {
-                final String phoneNumber = phone.getPhoneNumber();
-                if (TextUtils.isEmpty(phoneNumber)) {
-                    continue;
-                }
-                final String formattedNumber = PhoneNumberUtilsCompat.formatNumber(
-                        phoneNumber, phone.getPhoneNormalizedNumber(),
-                        GeoUtil.getCurrentCountryIso(getContext()));
-                CharSequence phoneType = null;
-                if (phone.hasPhoneType()) {
-                    phoneType = Phone.getTypeLabel(
-                            res, phone.getPhoneType(), phone.getPhoneLabel());
-                }
-                bindData(phoneDrawable, phoneContentDescription, formattedNumber, phoneType,
-                        isFirstPhoneBound, true);
-                isFirstPhoneBound = false;
-            }
-        }
-
-        // Emails
-        final ArrayList<ValuesDelta> emails = state.getMimeEntries(Email.CONTENT_ITEM_TYPE);
-        final Drawable emailDrawable = getResources().getDrawable(R.drawable.ic_email_24dp);
-        final String emailContentDescription = res.getString(R.string.header_email_entry);
-        if (emails != null) {
-            boolean isFirstEmailBound = true;
-            for (ValuesDelta email : emails) {
-                final String emailAddress = email.getEmailData();
-                if (TextUtils.isEmpty(emailAddress)) {
-                    continue;
-                }
-                CharSequence emailType = null;
-                if (email.hasEmailType()) {
-                    emailType = Email.getTypeLabel(
-                            res, email.getEmailType(), email.getEmailLabel());
-                }
-                bindData(emailDrawable, emailContentDescription, emailAddress, emailType,
-                        isFirstEmailBound);
-                isFirstEmailBound = false;
-            }
-        }
-
-        // Hide mGeneral if it's empty
-        if (mGeneral.getChildCount() > 0) {
-            mGeneral.setVisibility(View.VISIBLE);
-        } else {
-            mGeneral.setVisibility(View.GONE);
-        }
-    }
-
-    private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
-            CharSequence type, boolean isFirstEntry) {
-        bindData(icon, iconContentDescription, data, type, isFirstEntry, false);
-    }
-
-    private void bindData(Drawable icon, String iconContentDescription, CharSequence data,
-            CharSequence type, boolean isFirstEntry, boolean forceLTR) {
-        final View field = mInflater.inflate(R.layout.item_read_only_field, mGeneral, false);
-        if (isFirstEntry) {
-            final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
-            imageView.setImageDrawable(icon);
-            imageView.setContentDescription(iconContentDescription);
-        } else {
-            final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
-            imageView.setVisibility(View.INVISIBLE);
-            imageView.setContentDescription(null);
-        }
-        final TextView dataView = (TextView) field.findViewById(R.id.data);
-        dataView.setText(data);
-        if (forceLTR) {
-            dataView.setTextDirection(View.TEXT_DIRECTION_LTR);
-        }
-        final TextView typeView = (TextView) field.findViewById(R.id.type);
-        if (!TextUtils.isEmpty(type)) {
-            typeView.setText(type);
-        } else {
-            typeView.setVisibility(View.GONE);
-        }
-
-        mGeneral.addView(field);
-    }
-
-    @Override
-    public long getRawContactId() {
-        return mRawContactId;
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v.getId() == R.id.button_edit_externally) {
-            if (mListener != null) {
-                mListener.onExternalEditorRequest(
-                        new AccountWithDataSet(mAccountName, mAccountType, mDataSet),
-                        ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactId));
-            }
-        }
-    }
-}
diff --git a/src/com/android/contacts/editor/ViewIdGenerator.java b/src/com/android/contacts/editor/ViewIdGenerator.java
index e7e7948..ad99bf8 100644
--- a/src/com/android/contacts/editor/ViewIdGenerator.java
+++ b/src/com/android/contacts/editor/ViewIdGenerator.java
@@ -25,7 +25,7 @@
 import com.android.contacts.common.model.dataitem.DataKind;
 
 /**
- * A class that provides unique view ids for {@link ContentEditorView}, {@link KindSectionView},
+ * A class that provides unique view ids for {@link ContentEditorView},
  * {@link LabeledEditorView} and {@link EditView} on {@link EditContactActivity}.
  * It is used to assign a unique but consistent id to each view across {@link EditContactActivity}'s
  * lifecycle, so that we can re-construct view state (e.g. focused view) when the screen rotates.
diff --git a/src/com/android/contacts/group/GroupMembersFragment.java b/src/com/android/contacts/group/GroupMembersFragment.java
index 66bd509..f7897bc 100644
--- a/src/com/android/contacts/group/GroupMembersFragment.java
+++ b/src/com/android/contacts/group/GroupMembersFragment.java
@@ -318,7 +318,7 @@
         final long[] contactIds = getAdapter().getSelectedContactIdsArray();
         new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE,
                 getContext(), contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName,
-                mGroupMetaData.accountType).execute();
+                mGroupMetaData.accountType, mGroupMetaData.dataSet).execute();
 
         mActionBarAdapter.setSelectionMode(false);
     }
@@ -340,7 +340,7 @@
             new UpdateGroupMembersAsyncTask(
                     UpdateGroupMembersAsyncTask.TYPE_ADD,
                     getContext(), contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName,
-                    mGroupMetaData.accountType).execute();
+                    mGroupMetaData.accountType, mGroupMetaData.dataSet).execute();
         }
     }
 
@@ -636,7 +636,7 @@
             contactIds[0] = contactId;
             new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE,
                     getContext(), contactIds, mGroupMetaData.groupId, mGroupMetaData.accountName,
-                    mGroupMetaData.accountType).execute();
+                    mGroupMetaData.accountType, mGroupMetaData.dataSet).execute();
         }
     }
 
diff --git a/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java b/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java
index 46aa674..9b255eb 100644
--- a/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java
+++ b/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java
@@ -22,6 +22,7 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.provider.ContactsContract;
+import android.provider.ContactsContract.RawContacts;
 import android.widget.Toast;
 
 import com.android.contacts.ContactSaveService;
@@ -42,15 +43,17 @@
     private final long mGroupId;
     private final String mAccountName;
     private final String mAccountType;
+    private final String mDataSet;
 
     public UpdateGroupMembersAsyncTask(int type, Context context, long[] contactIds,
-            long groupId, String accountName, String accountType) {
+            long groupId, String accountName, String accountType, String dataSet) {
         mContext = context;
         mType = type;
         mContactIds = contactIds;
         mGroupId = groupId;
         mAccountName = accountName;
         mAccountType = accountType;
+        mDataSet = dataSet;
     }
 
     @Override
@@ -81,10 +84,16 @@
     // TODO(wjang): prune raw contacts that are already in the group; ContactSaveService will
     // log a warning if the raw contact is already a member and keep going but it is not ideal.
     private long[] getRawContactIds() {
-        final Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon()
-                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccountName)
-                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccountType)
-                .build();
+        final Uri.Builder builder = RawContacts.CONTENT_URI.buildUpon();
+        // null account names are not valid, see ContactsProvider2#appendAccountFromParameter
+        if (mAccountName != null) {
+            builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccountName);
+            builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, mAccountType);
+        }
+        if (mDataSet != null) {
+            builder.appendQueryParameter(RawContacts.DATA_SET, mDataSet);
+        }
+        final Uri rawContactUri = builder.build();
         final String[] projection = new String[]{ContactsContract.RawContacts._ID};
         final StringBuilder selection = new StringBuilder();
         final String[] selectionArgs = new String[mContactIds.length];
diff --git a/src/com/android/contacts/interactions/CallLogInteraction.java b/src/com/android/contacts/interactions/CallLogInteraction.java
index 06fd273..9e6b5a2 100644
--- a/src/com/android/contacts/interactions/CallLogInteraction.java
+++ b/src/com/android/contacts/interactions/CallLogInteraction.java
@@ -16,6 +16,8 @@
 package com.android.contacts.interactions;
 
 import com.android.contacts.R;
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
 import com.android.contacts.common.util.BitmapUtil;
 import com.android.contacts.common.util.ContactDisplayUtils;
 
@@ -66,7 +68,14 @@
 
     @Override
     public String getViewHeader(Context context) {
-        return getNumber();
+        String number = mValues.getAsString(Calls.NUMBER);
+        if (number != null) {
+            number = PhoneNumberUtilsCompat.formatNumber(number,
+                    PhoneNumberUtilsCompat.normalizeNumber(number),
+                    GeoUtil.getCurrentCountryIso(context));
+            return sBidiFormatter.unicodeWrap(number, TextDirectionHeuristics.LTR);
+        }
+        return null;
     }
 
     @Override
diff --git a/src/com/android/contacts/list/ContactsUnavailableFragment.java b/src/com/android/contacts/list/ContactsUnavailableFragment.java
index aa08a32..c567dd2 100644
--- a/src/com/android/contacts/list/ContactsUnavailableFragment.java
+++ b/src/com/android/contacts/list/ContactsUnavailableFragment.java
@@ -17,9 +17,11 @@
 
 import android.app.Fragment;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.PorterDuff;
 import android.os.Bundle;
+import android.provider.ContactsContract.ProviderStatus;
 import android.support.v4.content.ContextCompat;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -34,6 +36,8 @@
 
 import com.android.contacts.R;
 import com.android.contacts.common.compat.ProviderStatusCompat;
+import com.android.contacts.common.interactions.ImportExportDialogFragment;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
 
 /**
  * Fragment shown when contacts are unavailable. It contains provider status
@@ -48,18 +52,10 @@
     private Button mImportContactsButton;
     private ProgressBar mProgress;
     private View mButtonsContainer;
-    private int mNoContactsMsgResId = -1;
-
-    private OnContactsUnavailableActionListener mListener;
 
     private Integer mProviderStatus;
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
     public View onCreateView(
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         mView = inflater.inflate(R.layout.contacts_unavailable_fragment, null);
@@ -99,11 +95,6 @@
         return mView;
     }
 
-    public void setOnContactsUnavailableActionListener(
-            OnContactsUnavailableActionListener listener) {
-        mListener = listener;
-    }
-
     public void updateStatus(int providerStatus) {
         mProviderStatus = providerStatus;
         if (mView == null) {
@@ -123,6 +114,7 @@
      * Update views in the fragment when provider status is empty.
      */
     private void updateViewsForEmptyStatus() {
+        updateButtonVisibility(View.VISIBLE);
         mProgress.setVisibility(View.GONE);
     }
 
@@ -135,7 +127,7 @@
         mMessageView.setText(resId);
         mMessageView.setVisibility(View.VISIBLE);
         mImageView.setVisibility(View.GONE);
-        updateButtonVisibilty(View.GONE);
+        updateButtonVisibility(View.GONE);
         mProgress.setVisibility(View.VISIBLE);
 
         final ViewGroup.MarginLayoutParams layoutParams =
@@ -148,20 +140,25 @@
 
     @Override
     public void onClick(View v) {
-        if (mListener == null) {
-            return;
-        }
         switch (v.getId()) {
             case R.id.add_account_button:
-                mListener.onAddAccountAction();
+                final Intent intent = ImplicitIntentsUtil.getIntentForAddingGoogleAccount();
+                ImplicitIntentsUtil.startActivityOutsideApp(getActivity(), intent);
                 break;
             case R.id.import_contacts_button:
-                mListener.onImportContactsFromFileAction();
+                ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(),
+                        getActivity().getClass(),
+                        ImportExportDialogFragment.EXPORT_MODE_ALL_CONTACTS);
                 break;
         }
     }
 
-    private void updateButtonVisibilty(int visibility) {
+    private boolean areContactsAvailable() {
+        return (mProviderStatus != null) && mProviderStatus.equals(ProviderStatus.STATUS_NORMAL);
+    }
+
+
+    private void updateButtonVisibility(int visibility) {
         if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
             mAddAccountButton.setVisibility(visibility);
             mImportContactsButton.setVisibility(visibility);
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index f92e0ab..310d4d9 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -269,10 +269,6 @@
         }
     }
 
-    public boolean tryRemoveHighlight() {
-        return FeatureHighlightHelper.tryRemoveHighlight(mActivity);
-    }
-
     private void bindListHeader(int numberOfContacts) {
         final ContactListFilter filter = getFilter();
         // If the phone has at least one Google account whose sync status is unsyncable or pending
diff --git a/src/com/android/contacts/list/OnContactsUnavailableActionListener.java b/src/com/android/contacts/list/OnContactsUnavailableActionListener.java
deleted file mode 100644
index cc381e4..0000000
--- a/src/com/android/contacts/list/OnContactsUnavailableActionListener.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-package com.android.contacts.list;
-
-/**
- * Action callbacks that can be sent by the "contacts unavailable" fragment.
- */
-public interface OnContactsUnavailableActionListener  {
-
-    /**
-     * Creates a new contact.
-     */
-    void onCreateNewContactAction();
-
-    /**
-     * Initiates addition of a contacts account.
-     */
-    void onAddAccountAction();
-
-    /**
-     * Initiates contact import from a file.
-     */
-    void onImportContactsFromFileAction();
-}
diff --git a/tests/src/com/android/contacts/common/model/DeviceLocalAccountLocatorTests.java b/tests/src/com/android/contacts/common/model/DeviceLocalAccountLocatorTests.java
index f1a2714..e8c4e2f 100644
--- a/tests/src/com/android/contacts/common/model/DeviceLocalAccountLocatorTests.java
+++ b/tests/src/com/android/contacts/common/model/DeviceLocalAccountLocatorTests.java
@@ -140,24 +140,6 @@
         assertEquals(2, sut.getDeviceLocalAccounts().size());
     }
 
-    public void test_getDeviceLocalAccounts_onlyQueriesRawContactsIfNecessary() {
-        final DeviceLocalAccountTypeFactory stubFactory = new FakeDeviceAccountTypeFactory()
-                .withDeviceTypes(null, "vnd.sec.contact.phone")
-                .withSimTypes("vnd.sec.contact.sim");
-        final FakeContactsProvider contactsProvider = new FakeContactsProvider()
-                .withQueryResult(ContactsContract.Groups.CONTENT_URI, queryResult(
-                        "phone_account", "vnd.sec.contact.phone",
-                        "sim_account", "vnd.sec.contact.sim"
-                ));
-        final DeviceLocalAccountLocator sut = new DeviceLocalAccountLocator(
-                createContentResolverWithProvider(contactsProvider), stubFactory,
-                Collections.<AccountWithDataSet>emptyList());
-
-        sut.getDeviceLocalAccounts();
-
-        assertEquals(0, contactsProvider.getQueryCountFor(RawContacts.CONTENT_URI));
-    }
-
     private DeviceLocalAccountLocator createWithQueryResult(
             Cursor cursor) {
         final DeviceLocalAccountLocator locator = new DeviceLocalAccountLocator(
@@ -194,7 +176,6 @@
     private static class FakeContactsProvider extends MockContentProvider {
         public Cursor mNextQueryResult;
         public Map<Uri, Cursor> mNextResultMapping = new HashMap<>();
-        public Map<Uri, Integer> mQueryCountMapping = new HashMap<>();
 
         public FakeContactsProvider() {}
 
@@ -214,17 +195,10 @@
             return query(uri, projection, selection, selectionArgs, sortOrder, null);
         }
 
-        public int getQueryCountFor(Uri uri) {
-            ensureCountInitialized(uri);
-            return mQueryCountMapping.get(uri);
-        }
-
         @Nullable
         @Override
         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                 String sortOrder, CancellationSignal cancellationSignal) {
-            incrementQueryCount(uri);
-
             final Cursor result = mNextResultMapping.get(uri);
             if (result == null) {
                 return mNextQueryResult;
@@ -232,17 +206,5 @@
                 return result;
             }
         }
-
-        private void ensureCountInitialized(Uri uri) {
-            if (!mQueryCountMapping.containsKey(uri)) {
-                mQueryCountMapping.put(uri, 0);
-            }
-        }
-
-        private void incrementQueryCount(Uri uri) {
-            ensureCountInitialized(uri);
-            final int count = mQueryCountMapping.get(uri);
-            mQueryCountMapping.put(uri, count + 1);
-        }
     }
 }