Merge "Changing spinner width to avoid truncation in other languages"
diff --git a/res/layout/user_profile_header.xml b/res/layout/user_profile_header.xml
deleted file mode 100644
index 828f08c..0000000
--- a/res/layout/user_profile_header.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/user_profile_header"
-    android:orientation="horizontal"
-    android:layout_width="match_parent"
-    android:layout_height="?attr/list_item_header_height"
-    android:paddingLeft="?attr/list_item_padding_left"
-    android:paddingRight="?attr/list_item_padding_right"
-    android:paddingStart="?attr/list_item_padding_left"
-    android:paddingEnd="?attr/list_item_padding_right"
-    android:paddingTop="4dp"
-    android:paddingBottom="8dp" >
-
-    <TextView android:id="@+id/profile_title"
-        android:layout_width="@dimen/contact_list_section_header_width"
-        android:layout_height="?android:attr/listPreferredItemHeight"
-        android:singleLine="true"
-        android:text="@string/user_profile_contacts_list_header"
-        android:ellipsize="end"
-        android:textAppearance="@style/SectionHeaderStyle"
-        android:gravity="start|center_vertical" />
-
-    <Button android:id="@+id/user_profile_button"
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="?android:attr/listPreferredItemHeight"
-        android:paddingStart="?attr/list_item_padding_left"
-        android:paddingEnd="?attr/list_item_padding_right"
-        android:background="?android:attr/selectableItemBackground"
-        android:singleLine="true"
-        android:text="@string/profile_display_name"
-        android:ellipsize="end"
-        android:gravity="start|center_vertical"
-        android:textAppearance="?android:attr/textAppearanceMedium" />
-
-</LinearLayout>
diff --git a/res/values/donottranslate_config.xml b/res/values/donottranslate_config.xml
index 1c776ab..3428fa1 100644
--- a/res/values/donottranslate_config.xml
+++ b/res/values/donottranslate_config.xml
@@ -22,9 +22,6 @@
     <!-- If true, phonetic name is included in the contact editor by default -->
     <bool name="config_editor_include_phonetic_name">false</bool>
 
-    <!-- If true, the "home" icon on the action bar will be shown. -->
-    <bool name="show_home_icon">false</bool>
-
     <!--
       If true, the "view updates from group" button in the action bar will be
       shown. Otherwise it will be part of the content on the group detail page.
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1f6c80a..37e276f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -624,10 +624,6 @@
         <xliff:g id="call_type" example="Friends">%1$s</xliff:g>  <xliff:g id="call_short_date" example="Friends">%2$s</xliff:g>
     </string>
 
-    <!-- Text displayed in place of the display name for the contact that represents the user's
-      personal profile entry [CHAR LIMIT=64] -->
-    <string name="profile_display_name">Set up my profile</string>
-
     <!-- Label to instruct the user to type in a contact's name to add the contact as a member of the current group. [CHAR LIMIT=64] -->
     <string name="enter_contact_name">Type person\'s name</string>
 
diff --git a/src/com/android/contacts/GroupMetaDataLoader.java b/src/com/android/contacts/GroupMetaDataLoader.java
index 8344041..47648b3 100644
--- a/src/com/android/contacts/GroupMetaDataLoader.java
+++ b/src/com/android/contacts/GroupMetaDataLoader.java
@@ -50,7 +50,7 @@
 
     public GroupMetaDataLoader(Context context, Uri groupUri) {
         super(context, ensureIsGroupUri(groupUri), COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND "
-                + Groups.ACCOUNT_NAME + " NOT NULL", null, null);
+                + Groups.ACCOUNT_NAME + " NOT NULL", null, Groups.TITLE);
     }
 
     /**
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index febfa44..2d6740d 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -108,6 +108,7 @@
     private final FrameLayout mToolBarFrame;
 
     private boolean mShowHomeIcon;
+    private boolean mShowHomeAsUp;
 
     public interface TabState {
         public static int ALL = 0;
@@ -129,12 +130,19 @@
         mToolbar = toolbar;
         mToolBarFrame = (FrameLayout) mToolbar.getParent();
         mMaxToolbarContentInsetStart = mToolbar.getContentInsetStart();
-        mShowHomeIcon = mActivity.getResources().getBoolean(R.bool.show_home_icon);
 
         setupSearchAndSelectionViews();
         setupTabs(mActivity);
     }
 
+    public void setShowHomeIcon(boolean showHomeIcon) {
+        mShowHomeIcon = showHomeIcon;
+    }
+
+    public void setShowHomeAsUp(boolean showHomeAsUp) {
+        mShowHomeAsUp = showHomeAsUp;
+    }
+
     private void setupTabs(Context context) {
         final TypedArray attributeArray = context.obtainStyledAttributes(
                 new int[]{android.R.attr.actionBarSize});
@@ -354,6 +362,9 @@
         int newFlags = 0;
         if (mShowHomeIcon && !isSearchOrSelectionMode) {
             newFlags |= ActionBar.DISPLAY_SHOW_HOME;
+            if (mShowHomeAsUp) {
+                newFlags |= ActionBar.DISPLAY_HOME_AS_UP;
+            }
         }
         if (mSearchMode && !mSelectionMode) {
             // The search container is placed inside the toolbar. So we need to disable the
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index c2951d8..50f50dd 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -290,7 +290,6 @@
             case ContactsRequest.ACTION_DEFAULT:
             case ContactsRequest.ACTION_PICK_CONTACT: {
                 ContactPickerFragment fragment = new ContactPickerFragment();
-                fragment.setIncludeProfile(mRequest.shouldIncludeProfile());
                 fragment.setIncludeFavorites(mRequest.shouldIncludeFavorites());
                 mListFragment = fragment;
                 break;
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index eea55da..ac88619 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -93,7 +93,7 @@
 import com.android.contacts.list.ContactsIntentResolver;
 import com.android.contacts.list.ContactsRequest;
 import com.android.contacts.list.ContactsUnavailableFragment;
-import com.android.contacts.list.MultiSelectContactsListFragment;
+import com.android.contacts.list.DefaultContactBrowseListFragment;
 import com.android.contacts.list.MultiSelectContactsListFragment.OnCheckBoxListActionListener;
 import com.android.contacts.list.OnContactBrowserActionListener;
 import com.android.contacts.list.OnContactsUnavailableActionListener;
@@ -149,7 +149,7 @@
     /**
      * Showing a list of Contacts. Also used for showing search results in search mode.
      */
-    private MultiSelectContactsListFragment mAllFragment;
+    private DefaultContactBrowseListFragment mAllFragment;
     private GroupsFragment mGroupsFragment;
     private AccountFiltersFragment mAccountFiltersFragment;
 
@@ -375,7 +375,7 @@
         // However, if it's after screen rotation, the fragments have been re-created by
         // the fragment manager, so first see if there're already the target fragments
         // existing.
-        mAllFragment = (MultiSelectContactsListFragment)
+        mAllFragment = (DefaultContactBrowseListFragment)
                 fragmentManager.findFragmentByTag(ALL_TAG);
         mGroupsFragment = (GroupsFragment)
                 fragmentManager.findFragmentByTag(GROUPS_TAG);
@@ -383,7 +383,7 @@
                 fragmentManager.findFragmentByTag(FILTERS_TAG);
 
         if (mAllFragment == null) {
-            mAllFragment = new MultiSelectContactsListFragment();
+            mAllFragment = new DefaultContactBrowseListFragment();
             transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG);
 
             if (areGroupWritableAccountsAvailable()) {
@@ -1310,14 +1310,8 @@
     }
 
     private void joinSelectedContacts() {
-        final Long[] contactIdsArray = mAllFragment.getSelectedContactIds().toArray(
-                new Long[mAllFragment.getSelectedContactIds().size()]);
-        final long[] contactIdsArray2 = new long[contactIdsArray.length];
-        for (int i = 0; i < contactIdsArray.length; i++) {
-            contactIdsArray2[i] = contactIdsArray[i];
-        }
-        final Intent intent = ContactSaveService.createJoinSeveralContactsIntent(this,
-                contactIdsArray2);
+        final Intent intent = ContactSaveService.createJoinSeveralContactsIntent(
+                this, mAllFragment.getSelectedContactIdsArray());
         this.startService(intent);
 
         mActionBarAdapter.setSelectionMode(false);
diff --git a/src/com/android/contacts/group/GroupMembersListAdapter.java b/src/com/android/contacts/group/GroupMembersListAdapter.java
index 71e8f8d..f208fc8 100644
--- a/src/com/android/contacts/group/GroupMembersListAdapter.java
+++ b/src/com/android/contacts/group/GroupMembersListAdapter.java
@@ -28,12 +28,12 @@
 import android.view.ViewGroup;
 
 import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.list.ContactEntryListAdapter;
 import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.MultiSelectEntryContactListAdapter;
 import com.android.contacts.common.preference.ContactsPreferences;
 
 /** Group members cursor adapter. */
-public class GroupMembersListAdapter extends ContactEntryListAdapter {
+public class GroupMembersListAdapter extends MultiSelectEntryContactListAdapter {
 
     private static class GroupMembersQuery {
 
@@ -67,7 +67,7 @@
     private long mGroupId;
 
     public GroupMembersListAdapter(Context context) {
-        super(context);
+        super(context, GroupMembersQuery.CONTACT_ID);
         mUnknownNameText = context.getText(android.R.string.unknownName);
         setIndexedPartition(0);
     }
diff --git a/src/com/android/contacts/group/GroupMembersListFragment.java b/src/com/android/contacts/group/GroupMembersListFragment.java
index b8dad5e..6fe206d 100644
--- a/src/com/android/contacts/group/GroupMembersListFragment.java
+++ b/src/com/android/contacts/group/GroupMembersListFragment.java
@@ -41,9 +41,10 @@
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.interactions.GroupDeletionDialogFragment;
+import com.android.contacts.list.MultiSelectContactsListFragment;
 
 /** Displays the members of a group. */
-public class GroupMembersListFragment extends ContactEntryListFragment<GroupMembersListAdapter> {
+public class GroupMembersListFragment extends MultiSelectContactsListFragment {
 
     private static final String TAG = "GroupMembersList";
 
@@ -348,6 +349,11 @@
     }
 
     @Override
+    public GroupMembersListAdapter getAdapter() {
+        return (GroupMembersListAdapter) super.getAdapter();
+    }
+
+    @Override
     protected void configureAdapter() {
         super.configureAdapter();
         if (mGroupMetadata != null) {
diff --git a/src/com/android/contacts/interactions/CallLogInteractionsLoader.java b/src/com/android/contacts/interactions/CallLogInteractionsLoader.java
index edecca0..d1dc8b4 100644
--- a/src/com/android/contacts/interactions/CallLogInteractionsLoader.java
+++ b/src/com/android/contacts/interactions/CallLogInteractionsLoader.java
@@ -52,17 +52,17 @@
 
     @Override
     public List<ContactInteraction> loadInBackground() {
-        final boolean isPhoneNumbersEmpty = mPhoneNumbers == null || mPhoneNumbers.length <= 0;
-        final boolean isSipNumbersEmpty = mSipNumbers == null || mSipNumbers.length <= 0;
+        final boolean hasPhoneNumber = mPhoneNumbers != null && mPhoneNumbers.length > 0;
+        final boolean hasSipNumber = mSipNumbers != null && mSipNumbers.length > 0;
         if (!PermissionsUtil.hasPhonePermissions(getContext())
                 || !getContext().getPackageManager()
                         .hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
-                || (isPhoneNumbersEmpty && isSipNumbersEmpty) || mMaxToRetrieve <= 0) {
+                || (!hasPhoneNumber && !hasSipNumber) || mMaxToRetrieve <= 0) {
             return Collections.emptyList();
         }
 
         final List<ContactInteraction> interactions = new ArrayList<>();
-        if (!isPhoneNumbersEmpty) {
+        if (hasPhoneNumber) {
             for (String number : mPhoneNumbers) {
                 final String normalizedNumber = PhoneNumberUtilsCompat.normalizeNumber(number);
                 if (!TextUtils.isEmpty(normalizedNumber)) {
@@ -70,7 +70,7 @@
                 }
             }
         }
-        if (!isSipNumbersEmpty) {
+        if (hasSipNumber) {
             for (String number : mSipNumbers) {
                 interactions.addAll(getCallLogInteractions(number));
             }
@@ -90,7 +90,8 @@
             }
         });
         // Duplicates only occur because of fuzzy matching. No need to dedupe a single number.
-        if (mPhoneNumbers.length == 1) {
+        if ((hasPhoneNumber && mPhoneNumbers.length == 1 && !hasSipNumber)
+                || (hasSipNumber && mSipNumbers.length == 1 && !hasPhoneNumber)) {
             return interactions;
         }
         return pruneDuplicateCallLogInteractions(interactions, mMaxToRetrieve);
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index afd2543..ea55333 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -36,7 +36,6 @@
 
 import com.android.common.widget.CompositeCursorAdapter.Partition;
 import com.android.contacts.common.list.AutoScrollListView;
-import com.android.contacts.common.list.ContactEntryListFragment;
 import com.android.contacts.common.list.ContactListAdapter;
 import com.android.contacts.common.list.ContactListFilter;
 import com.android.contacts.common.list.DirectoryPartition;
@@ -49,7 +48,7 @@
  * picking a contact with one of the PICK intents).
  */
 public abstract class ContactBrowseListFragment extends
-        ContactEntryListFragment<ContactListAdapter> {
+        MultiSelectContactsListFragment<ContactListAdapter> {
 
     private static final String TAG = "ContactList";
 
@@ -383,6 +382,11 @@
     }
 
     @Override
+    public ContactListAdapter getAdapter() {
+        return (ContactListAdapter) super.getAdapter();
+    }
+
+    @Override
     protected void configureAdapter() {
         super.configureAdapter();
 
@@ -401,11 +405,8 @@
             }
         }
 
-        // Display the user's profile if not in search mode
-        adapter.setIncludeProfile(!searchMode);
-
-        // Display favorites if not in search mode
-        adapter.setIncludeFavorites(!searchMode);
+        adapter.setIncludeFavorites(!searchMode
+                && mFilter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
     }
 
     @Override
diff --git a/src/com/android/contacts/list/ContactsRequest.java b/src/com/android/contacts/list/ContactsRequest.java
index f867549..a1428be 100644
--- a/src/com/android/contacts/list/ContactsRequest.java
+++ b/src/com/android/contacts/list/ContactsRequest.java
@@ -85,7 +85,6 @@
     private CharSequence mTitle;
     private boolean mSearchMode;
     private String mQueryString;
-    private boolean mIncludeProfile;
     private boolean mIncludeFavorites;
     private boolean mLegacyCompatibilityMode;
     private boolean mDirectorySearchEnabled = true;
@@ -98,7 +97,6 @@
                 + " mTitle=" + mTitle
                 + " mSearchMode=" + mSearchMode
                 + " mQueryString=" + mQueryString
-                + " mIncludeProfile=" + mIncludeProfile
                 + " mIncludeFavorites=" + mIncludeFavorites
                 + " mLegacyCompatibilityMode=" + mLegacyCompatibilityMode
                 + " mDirectorySearchEnabled=" + mDirectorySearchEnabled
@@ -146,14 +144,6 @@
         mQueryString = string;
     }
 
-    public boolean shouldIncludeProfile() {
-        return mIncludeProfile;
-    }
-
-    public void setIncludeProfile(boolean includeProfile) {
-        mIncludeProfile = includeProfile;
-    }
-
     public boolean shouldIncludeFavorites() {
         return mIncludeFavorites;
     }
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 2ce22c0..97bb86a 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -17,43 +17,29 @@
 
 import android.content.Context;
 import android.content.CursorLoader;
-import android.content.Intent;
 import android.net.Uri;
 import android.provider.ContactsContract.Contacts;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
-import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.ListView;
 import android.widget.TextView;
 
 import com.android.contacts.R;
 import com.android.contacts.common.list.ContactListAdapter;
-import com.android.contacts.common.list.ContactListFilter;
 import com.android.contacts.common.list.ContactListItemView;
 import com.android.contacts.common.list.DefaultContactListAdapter;
-import com.android.contacts.common.list.ProfileAndContactsLoader;
-import com.android.contacts.common.util.ImplicitIntentsUtil;
-import com.android.contacts.editor.ContactEditorFragment;
-import com.android.contacts.common.util.AccountFilterUtil;
+import com.android.contacts.common.list.FavoritesAndContactsLoader;
 
 /**
  * Fragment containing a contact list used for browsing (as compared to
  * picking a contact with one of the PICK intents).
  */
 public class DefaultContactBrowseListFragment extends ContactBrowseListFragment {
-    private static final String TAG = DefaultContactBrowseListFragment.class.getSimpleName();
-
     private View mSearchHeaderView;
-    private View mAccountFilterHeader;
-    private FrameLayout mProfileHeaderContainer;
-    private View mProfileHeader;
-    private Button mProfileMessage;
-    private TextView mProfileTitle;
     private View mSearchProgress;
     private TextView mSearchProgressText;
 
@@ -68,7 +54,7 @@
 
     @Override
     public CursorLoader createCursorLoader(Context context) {
-        return new ProfileAndContactsLoader(context);
+        return new FavoritesAndContactsLoader(context);
     }
 
     @Override
@@ -77,6 +63,10 @@
         if (uri == null) {
             return;
         }
+        if (getAdapter().isDisplayingCheckBoxes()) {
+            super.onItemClick(position, id);
+            return;
+        }
         viewContact(uri, getAdapter().isEnterpriseContact(position));
     }
 
@@ -99,11 +89,6 @@
     protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
         super.onCreateView(inflater, container);
 
-        // Create an empty user profile header and hide it for now (it will be visible if the
-        // contacts list will have no user profile).
-        addEmptyUserProfileHeader(inflater);
-        showEmptyUserProfile(false);
-
         // Putting the header view inside a container will allow us to make
         // it invisible later. See checkHeaderViewVisibility()
         FrameLayout headerContainer = new FrameLayout(inflater.getContext());
@@ -138,70 +123,30 @@
     }
 
     @Override
-    protected void setProfileHeader() {
-        mUserProfileExists = getAdapter().hasProfile();
-        showEmptyUserProfile(!mUserProfileExists && !isSearchMode());
+    protected void setListHeader() {
+        if (!isSearchMode()) {
+            return;
+        }
+        ContactListAdapter adapter = getAdapter();
+        if (adapter == null) {
+            return;
+        }
 
-        if (isSearchMode()) {
-            ContactListAdapter adapter = getAdapter();
-            if (adapter == null) {
-                return;
-            }
-
-            // In search mode we only display the header if there is nothing found
-            if (TextUtils.isEmpty(getQueryString()) || !adapter.areAllPartitionsEmpty()) {
-                mSearchHeaderView.setVisibility(View.GONE);
-                showSearchProgress(false);
+        // In search mode we only display the header if there is nothing found
+        if (TextUtils.isEmpty(getQueryString()) || !adapter.areAllPartitionsEmpty()) {
+            mSearchHeaderView.setVisibility(View.GONE);
+            showSearchProgress(false);
+        } else {
+            mSearchHeaderView.setVisibility(View.VISIBLE);
+            if (adapter.isLoading()) {
+                mSearchProgressText.setText(R.string.search_results_searching);
+                showSearchProgress(true);
             } else {
-                mSearchHeaderView.setVisibility(View.VISIBLE);
-                if (adapter.isLoading()) {
-                    mSearchProgressText.setText(R.string.search_results_searching);
-                    showSearchProgress(true);
-                } else {
-                    mSearchProgressText.setText(R.string.listFoundAllContactsZero);
-                    mSearchProgressText.sendAccessibilityEvent(
-                            AccessibilityEvent.TYPE_VIEW_SELECTED);
-                    showSearchProgress(false);
-                }
+                mSearchProgressText.setText(R.string.listFoundAllContactsZero);
+                mSearchProgressText.sendAccessibilityEvent(
+                        AccessibilityEvent.TYPE_VIEW_SELECTED);
+                showSearchProgress(false);
             }
-            showEmptyUserProfile(false);
         }
     }
-
-    private void showEmptyUserProfile(boolean show) {
-        // Changing visibility of just the mProfileHeader doesn't do anything unless
-        // you change visibility of its children, hence the call to mCounterHeaderView
-        // and mProfileTitle
-        mProfileHeaderContainer.setVisibility(show ? View.VISIBLE : View.GONE);
-        mProfileHeader.setVisibility(show ? View.VISIBLE : View.GONE);
-        mProfileTitle.setVisibility(show ? View.VISIBLE : View.GONE);
-        mProfileMessage.setVisibility(show ? View.VISIBLE : View.GONE);
-    }
-
-    /**
-     * This method creates a pseudo user profile contact. When the returned query doesn't have
-     * a profile, this methods creates 2 views that are inserted as headers to the listview:
-     * 1. A header view with the "ME" title and the contacts count.
-     * 2. A button that prompts the user to create a local profile
-     */
-    private void addEmptyUserProfileHeader(LayoutInflater inflater) {
-        ListView list = getListView();
-        // Add a header with the "ME" name. The view is embedded in a frame view since you cannot
-        // change the visibility of a view in a ListView without having a parent view.
-        mProfileHeader = inflater.inflate(R.layout.user_profile_header, null, false);
-        mProfileTitle = (TextView) mProfileHeader.findViewById(R.id.profile_title);
-        mProfileHeaderContainer = new FrameLayout(inflater.getContext());
-        mProfileHeaderContainer.addView(mProfileHeader);
-        list.addHeaderView(mProfileHeaderContainer, null, false);
-
-        // Add a button with a message inviting the user to create a local profile
-        mProfileMessage = (Button) mProfileHeader.findViewById(R.id.user_profile_button);
-        mProfileMessage.setOnClickListener(new View.OnClickListener() {
-            public void onClick(View v) {
-                Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
-                intent.putExtra(ContactEditorFragment.INTENT_EXTRA_NEW_LOCAL_PROFILE, true);
-                ImplicitIntentsUtil.startActivityInApp(getActivity(), intent);
-            }
-        });
-    }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/list/MultiSelectContactsListFragment.java b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
index 76dd3f3..53f5a74 100644
--- a/src/com/android/contacts/list/MultiSelectContactsListFragment.java
+++ b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
@@ -16,18 +16,16 @@
 
 package com.android.contacts.list;
 
-import com.android.contacts.common.list.ContactListAdapter;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.contacts.common.list.DefaultContactListAdapter;
+import com.android.contacts.common.list.ContactEntryListFragment;
+import com.android.contacts.common.list.MultiSelectEntryContactListAdapter;
+import com.android.contacts.common.list.MultiSelectEntryContactListAdapter.SelectedContactsListener;
 import com.android.contacts.common.logging.SearchState;
-import com.android.contacts.list.MultiSelectEntryContactListAdapter.SelectedContactsListener;
 import com.android.contacts.common.logging.Logger;
 
 import android.database.Cursor;
-import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract;
-import android.text.TextUtils;
+import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
 
 import java.util.ArrayList;
@@ -38,9 +36,12 @@
  * Fragment containing a contact list used for browsing contacts and optionally selecting
  * multiple contacts via checkboxes.
  */
-public class MultiSelectContactsListFragment extends DefaultContactBrowseListFragment
+public abstract class MultiSelectContactsListFragment<T extends MultiSelectEntryContactListAdapter>
+        extends ContactEntryListFragment<T>
         implements SelectedContactsListener {
 
+    private static final String TAG = "MultiContactsList";
+
     public interface OnCheckBoxListActionListener {
         void onStartDisplayingCheckBoxes();
         void onSelectedContactIdsChanged();
@@ -60,7 +61,7 @@
 
     /**
      * Whether a search result was clicked by the user. Tracked so that we can distinguish
-     * between exiting the search mode after a result was clicked from existing w/o clicking
+     * between exiting the search mode after a result was clicked from exiting w/o clicking
      * any search result.
      */
     public boolean wasSearchResultClicked() {
@@ -76,9 +77,7 @@
 
     @Override
     public void onSelectedContactsChanged() {
-        if (mCheckBoxListListener != null) {
-            mCheckBoxListListener.onSelectedContactIdsChanged();
-        }
+        if (mCheckBoxListListener != null) mCheckBoxListListener.onSelectedContactIdsChanged();
     }
 
     @Override
@@ -106,13 +105,11 @@
     }
 
     public TreeSet<Long> getSelectedContactIds() {
-        final MultiSelectEntryContactListAdapter adapter = getAdapter();
-        return adapter.getSelectedContactIds();
+        return getAdapter().getSelectedContactIds();
     }
 
-    @Override
-    public MultiSelectEntryContactListAdapter getAdapter() {
-        return (MultiSelectEntryContactListAdapter) super.getAdapter();
+    public long[] getSelectedContactIdsArray() {
+        return getAdapter().getSelectedContactIdsArray();
     }
 
     @Override
@@ -129,9 +126,11 @@
     }
 
     public void displayCheckBoxes(boolean displayCheckBoxes) {
-        getAdapter().setDisplayCheckBoxes(displayCheckBoxes);
-        if (!displayCheckBoxes) {
-            clearCheckBoxes();
+        if (getAdapter() != null) {
+            getAdapter().setDisplayCheckBoxes(displayCheckBoxes);
+            if (!displayCheckBoxes) {
+                clearCheckBoxes();
+            }
         }
     }
 
@@ -142,24 +141,20 @@
     @Override
     protected boolean onItemLongClick(int position, long id) {
         final int previouslySelectedCount = getAdapter().getSelectedContactIds().size();
-        final Uri uri = getAdapter().getContactUri(position);
+        final long contactId = getContactId(position);
         final int partition = getAdapter().getPartitionForPosition(position);
-        if (uri != null && (partition == ContactsContract.Directory.DEFAULT
-                && (position > 0 || !getAdapter().hasProfile()))) {
-            final String contactId = uri.getLastPathSegment();
-            if (!TextUtils.isEmpty(contactId)) {
-                if (mCheckBoxListListener != null) {
-                    mCheckBoxListListener.onStartDisplayingCheckBoxes();
-                }
-                getAdapter().toggleSelectionOfContactId(Long.valueOf(contactId));
-                // Manually send clicked event if there is a checkbox.
-                // See b/24098561.  TalkBack will not read it otherwise.
-                final int index = position + getListView().getHeaderViewsCount() - getListView()
-                        .getFirstVisiblePosition();
-                if (index >= 0 && index < getListView().getChildCount()) {
-                    getListView().getChildAt(index).sendAccessibilityEvent(AccessibilityEvent
-                            .TYPE_VIEW_CLICKED);
-                }
+        if (contactId >= 0 && partition == ContactsContract.Directory.DEFAULT) {
+            if (mCheckBoxListListener != null) {
+                mCheckBoxListListener.onStartDisplayingCheckBoxes();
+            }
+            getAdapter().toggleSelectionOfContactId(contactId);
+            // Manually send clicked event if there is a checkbox.
+            // See b/24098561.  TalkBack will not read it otherwise.
+            final int index = position + getListView().getHeaderViewsCount() - getListView()
+                    .getFirstVisiblePosition();
+            if (index >= 0 && index < getListView().getChildCount()) {
+                getListView().getChildAt(index).sendAccessibilityEvent(AccessibilityEvent
+                        .TYPE_VIEW_CLICKED);
             }
         }
         final int nowSelectedCount = getAdapter().getSelectedContactIds().size();
@@ -173,27 +168,37 @@
 
     @Override
     protected void onItemClick(int position, long id) {
-        final Uri uri = getAdapter().getContactUri(position);
-        if (uri == null) {
+        final long contactId = getContactId(position);
+        if (contactId < 0) {
             return;
         }
         if (getAdapter().isDisplayingCheckBoxes()) {
-            final String contactId = uri.getLastPathSegment();
-            if (!TextUtils.isEmpty(contactId)) {
-                getAdapter().toggleSelectionOfContactId(Long.valueOf(contactId));
-            }
+            getAdapter().toggleSelectionOfContactId(contactId);
         } else {
             if (isSearchMode()) {
                 mSearchResultClicked = true;
                 Logger.logSearchEvent(createSearchStateForSearchResultClick(position));
             }
-            super.onItemClick(position, id);
         }
         if (mCheckBoxListListener != null && getAdapter().getSelectedContactIds().size() == 0) {
             mCheckBoxListListener.onStopDisplayingCheckBoxes();
         }
     }
 
+    private long getContactId(int position) {
+        final int contactIdColumnIndex = getAdapter().getContactColumnIdIndex();
+
+        final Cursor cursor = (Cursor) getAdapter().getItem(position);
+        if (cursor != null) {
+            if (cursor.getColumnCount() > contactIdColumnIndex) {
+                return cursor.getLong(contactIdColumnIndex);
+            }
+        }
+
+        Log.w(TAG, "Failed to get contact ID from cursor column " + contactIdColumnIndex);
+        return -1;
+    }
+
     /**
      * Returns the state of the search results currently presented to the user.
      */
@@ -260,14 +265,4 @@
         }
         return searchState;
     }
-
-    @Override
-    protected ContactListAdapter createListAdapter() {
-        DefaultContactListAdapter adapter = new MultiSelectEntryContactListAdapter(getContext());
-        adapter.setSectionHeaderDisplayEnabled(isSectionHeaderDisplayEnabled());
-        adapter.setDisplayPhotos(true);
-        adapter.setPhotoPosition(
-                ContactListItemView.getDefaultPhotoPosition(/* opposite = */ false));
-        return adapter;
-    }
 }
diff --git a/src/com/android/contacts/list/MultiSelectEntryContactListAdapter.java b/src/com/android/contacts/list/MultiSelectEntryContactListAdapter.java
deleted file mode 100644
index b080657..0000000
--- a/src/com/android/contacts/list/MultiSelectEntryContactListAdapter.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2015 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;
-
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.contacts.common.list.DefaultContactListAdapter;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.provider.ContactsContract;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.CheckBox;
-
-import java.util.TreeSet;
-
-/**
- * An extension of the default contact adapter that adds checkboxes and the ability
- * to select multiple contacts.
- */
-public class MultiSelectEntryContactListAdapter extends DefaultContactListAdapter {
-
-    private SelectedContactsListener mSelectedContactsListener;
-    private TreeSet<Long> mSelectedContactIds = new TreeSet<Long>();
-    private boolean mDisplayCheckBoxes;
-
-    public interface SelectedContactsListener {
-        void onSelectedContactsChanged();
-        void onSelectedContactsChangedViaCheckBox();
-    }
-
-    public MultiSelectEntryContactListAdapter(Context context) {
-        super(context);
-    }
-
-    public void setSelectedContactsListener(SelectedContactsListener listener) {
-        mSelectedContactsListener = listener;
-    }
-
-    /**
-     * Returns set of selected contacts.
-     */
-    public TreeSet<Long> getSelectedContactIds() {
-        return mSelectedContactIds;
-    }
-
-    /**
-     * Update set of selected contacts. This changes which checkboxes are set.
-     */
-    public void setSelectedContactIds(TreeSet<Long> selectedContactIds) {
-        this.mSelectedContactIds = selectedContactIds;
-        notifyDataSetChanged();
-        if (mSelectedContactsListener != null) {
-            mSelectedContactsListener.onSelectedContactsChanged();
-        }
-    }
-
-    /**
-     * Shows checkboxes beside contacts if {@param displayCheckBoxes} is {@code TRUE}.
-     * Not guaranteed to work with all configurations of this adapter.
-     */
-    public void setDisplayCheckBoxes(boolean showCheckBoxes) {
-        if (!mDisplayCheckBoxes && showCheckBoxes) {
-            setSelectedContactIds(new TreeSet<Long>());
-        }
-        mDisplayCheckBoxes = showCheckBoxes;
-        notifyDataSetChanged();
-        if (mSelectedContactsListener != null) {
-            mSelectedContactsListener.onSelectedContactsChanged();
-        }
-    }
-
-    /**
-     * Checkboxes are being displayed beside contacts.
-     */
-    public boolean isDisplayingCheckBoxes() {
-        return mDisplayCheckBoxes;
-    }
-
-    /**
-     * Toggle the checkbox beside the contact for {@param contactId}.
-     */
-    public void toggleSelectionOfContactId(long contactId) {
-        if (mSelectedContactIds.contains(contactId)) {
-            mSelectedContactIds.remove(contactId);
-        } else {
-            mSelectedContactIds.add(contactId);
-        }
-        notifyDataSetChanged();
-        if (mSelectedContactsListener != null) {
-            mSelectedContactsListener.onSelectedContactsChanged();
-        }
-    }
-
-    @Override
-    protected void bindView(View itemView, int partition, Cursor cursor, int position) {
-        super.bindView(itemView, partition, cursor, position);
-        final ContactListItemView view = (ContactListItemView) itemView;
-        bindCheckBox(view, cursor, position, partition == ContactsContract.Directory.DEFAULT);
-    }
-
-    private void bindCheckBox(ContactListItemView view, Cursor cursor, int position,
-            boolean isLocalDirectory) {
-        // Disable clicking on the ME profile and all contacts from remote directories
-        // when showing check boxes. We do this by telling the view to handle clicking itself.
-        view.setClickable((position == 0 && hasProfile() || !isLocalDirectory)
-                && mDisplayCheckBoxes);
-        // Only show checkboxes if mDisplayCheckBoxes is enabled. Also, never show the
-        // checkbox for the Me profile entry and other directory contacts except local directory.
-        if (position == 0 && hasProfile() || !mDisplayCheckBoxes || !isLocalDirectory) {
-            view.hideCheckBox();
-            return;
-        }
-        final CheckBox checkBox = view.getCheckBox();
-        final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
-        checkBox.setChecked(mSelectedContactIds.contains(contactId));
-        checkBox.setTag(contactId);
-        checkBox.setOnClickListener(mCheckBoxClickListener);
-    }
-
-    private final OnClickListener mCheckBoxClickListener = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            final CheckBox checkBox = (CheckBox) v;
-            final Long contactId = (Long) checkBox.getTag();
-            if (checkBox.isChecked()) {
-                mSelectedContactIds.add(contactId);
-            } else {
-                mSelectedContactIds.remove(contactId);
-            }
-            notifyDataSetChanged();
-            if (mSelectedContactsListener != null) {
-                mSelectedContactsListener.onSelectedContactsChangedViaCheckBox();
-            }
-        }
-    };
-}