Merge "Fix test failures on API 22" into ub-contactsdialer-h-dev
diff --git a/res/layout/item_read_only_field.xml b/res/layout/item_read_only_field.xml
index e5444a4..8c77eee 100644
--- a/res/layout/item_read_only_field.xml
+++ b/res/layout/item_read_only_field.xml
@@ -16,9 +16,10 @@
 
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/read_only_row"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginEnd="@dimen/editor_delete_button_width"
+    android:focusableInTouchMode="true"
     android:layout_marginBottom="@dimen/editor_padding_between_read_only_editor_views"
     android:orientation="horizontal">
 
@@ -41,9 +42,7 @@
             android:textSize="@dimen/editor_form_text_size"
             android:textColor="?android:attr/textColorSecondary"
             android:singleLine="true"
-            android:saveEnabled="false"
-            android:textAlignment="viewStart"
-            android:enabled="false"/>
+            android:textAlignment="viewStart"/>
 
         <TextView
             android:id="@+id/type"
@@ -52,9 +51,7 @@
             android:textSize="@dimen/editor_form_text_size"
             android:textColor="?android:attr/textColorSecondary"
             android:singleLine="true"
-            android:saveEnabled="false"
-            android:textAlignment="viewStart"
-            android:enabled="false"/>
+            android:textAlignment="viewStart"/>
 
     </LinearLayout>
 
diff --git a/res/menu/people_options.xml b/res/menu/people_options.xml
index a2402b9..10a47ba 100644
--- a/res/menu/people_options.xml
+++ b/res/menu/people_options.xml
@@ -18,7 +18,7 @@
     <item
         android:id="@+id/menu_search"
         android:icon="@drawable/ic_ab_search"
-        android:title="@string/menu_search"
+        android:title="@string/hint_findContacts"
         contacts:showAsAction="ifRoom" />
 
     <item
diff --git a/res/menu/search_menu.xml b/res/menu/search_menu.xml
index 89068d0..6ed7178 100644
--- a/res/menu/search_menu.xml
+++ b/res/menu/search_menu.xml
@@ -18,6 +18,6 @@
     <item
         android:id="@+id/menu_search"
         android:icon="@drawable/ic_ab_search"
-        android:title="@string/menu_search"
+        android:title="@string/hint_findContacts"
         contacts:showAsAction="always" />
 </menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9a1dd19..a50c155 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -193,7 +193,7 @@
     <string name="contacts_deleted_two_named_toast"><xliff:g id="name">%1$s</xliff:g> and <xliff:g id="name">%2$s</xliff:g> deleted</string>
 
     <!-- Toast shown with names after user selected contacts are deleted by user action. [CHAR LIMIT=50] -->
-    <string name="contacts_deleted_many_named_toast"><xliff:g id="name">%1$s</xliff:g>, <xliff:g id="name">%2$s</xliff:g>, <xliff:g id="name">%3$s</xliff:g>... deleted</string>
+    <string name="contacts_deleted_many_named_toast"><xliff:g id="name">%1$s</xliff:g>, <xliff:g id="name">%2$s</xliff:g>, <xliff:g id="name">%3$s</xliff:g>\u2026 deleted</string>
 
     <!-- Toast shown after contacts that the user has selected are deleted by a user action. [CHAR LIMIT=30] -->
     <plurals name="contacts_deleted_toast">
@@ -724,7 +724,7 @@
     <!-- Title of the editor activity when creating a new contact. The char
          limit is short and cannot be increased, since this needs to be displayed in a single line
          at a pre-determined text size. [CHAR LIMIT=20] -->
-    <string name="contact_editor_title_new_contact">Add new contact</string>
+    <string name="contact_editor_title_new_contact">Create new contact</string>
 
     <!-- Title of the editor activity when editing a contact that already exists. The char
          limit is short and cannot be increased, since this needs to be displayed in a single line
@@ -759,7 +759,7 @@
 
     <!-- Content description for the button that adds a new contact
          [CHAR LIMIT=NONE] -->
-    <string name="action_menu_add_new_contact_button">add new contact</string>
+    <string name="action_menu_add_new_contact_button">Create new contact</string>
     <!-- Button Label to see more on an ExpandingEntryCardView [CHAR LIMIT=40] -->
     <string name="expanding_entry_card_view_see_more">See more</string>
     <!-- Button Label to see less on an ExpandingEntryCardView [CHAR LIMIT=40] -->
@@ -848,8 +848,14 @@
     <!-- Button to expand the contact editor to show all available input fields. [CHAR LIMIT=60] -->
     <string name="editor_more_fields">More fields</string>
 
-    <!-- Content description for the contact editor photo overlay which, when clicked, shows a dialog with the options for changing the contact photo. [CHAR LIMIT=30] -->
-    <string name="editor_change_photo_content_description">Change photo</string>
+    <!-- Content description for the contact editor photo overlay which, when clicked, shows a dialog with the options for changing the contact photo. [CHAR LIMIT=NONE] -->
+    <string name="editor_change_photo_content_description">Change contact photo</string>
+
+    <!-- Content description for the contact editor photo overlay which, when clicked, shows a dialog with the options for adding a contact photo. [CHAR LIMIT=NONE] -->
+    <string name="editor_add_photo_content_description">Add contact photo</string>
+
+    <!-- Accessibility content description, describes the image as being the photo for the contact the user is viewing. [CHAR LIMIT=NONE] -->
+    <string name="editor_contact_photo_content_description">Contact photo</string>
 
     <!-- Toast message displayed when the editor fails to load for a contacts. [CHAR LIMIT=NONE] -->
     <string name="editor_failed_to_load">Failed to open editor.</string>
@@ -1677,9 +1683,6 @@
     <!-- Toast indicating that no visible contact to share [CHAR LIMIT=NONE]  -->
     <string name="no_contact_to_share">There are no contacts to share.</string>
 
-    <!-- Menu item to search contacts -->
-    <string name="menu_search">Search</string>
-
     <!-- The menu item to filter the list of contacts displayed -->
     <string name="menu_contacts_filter">Contacts to display</string>
 
@@ -1695,12 +1698,7 @@
     <!-- Query hint displayed inside the search field [CHAR LIMIT=64] -->
     <string name="hint_findContacts">Search contacts</string>
 
-    <!-- The description text for the favorites tab.
-
-         Note: AccessibilityServices use this attribute to announce what the view represents.
-         This is especially valuable for views without textual representation like ImageView.
-
-         [CHAR LIMIT=NONE] -->
+    <!-- The content description text for star icon in contacts list [CHAR LIMIT=30] -->
     <string name="contactsFavoritesLabel">Favorites</string>
 
     <!-- Displayed at the top of the contacts showing the zero total number of contacts visible when "All contacts" is selected  [CHAR LIMIT=64]-->
diff --git a/src-bind/com/android/contactsbind/ObjectFactory.java b/src-bind/com/android/contactsbind/ObjectFactory.java
index af11c90..6697ebc 100644
--- a/src-bind/com/android/contactsbind/ObjectFactory.java
+++ b/src-bind/com/android/contactsbind/ObjectFactory.java
@@ -14,7 +14,6 @@
 package com.android.contactsbind;
 
 import com.android.contacts.common.logging.Logger;
-import com.android.contactsbind.search.AutocompleteHelper;
 import com.android.contacts.common.util.DeviceLocalAccountTypeFactory;
 
 import android.app.Fragment;
@@ -31,10 +30,6 @@
         return null;
     }
 
-    public static AutocompleteHelper getAutocompleteHelper(Context context) {
-        return null;
-    }
-
     public static DeviceLocalAccountTypeFactory getDeviceLocalAccountTypeFactory(Context context) {
         return new DeviceLocalAccountTypeFactory.Default(context);
     }
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
index 79d0fe2..b786940 100644
--- a/src/com/android/contacts/ContactsDrawerActivity.java
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -24,6 +24,7 @@
 import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.os.Bundle;
+import android.os.Handler;
 import android.provider.ContactsContract.Intents;
 import android.support.annotation.LayoutRes;
 import android.support.design.widget.NavigationView;
@@ -112,11 +113,11 @@
     private static final String KEY_NEW_GROUP_ACCOUNT = "newGroupAccount";
     private static final String KEY_CONTACTS_VIEW = "contactsView";
 
+    private static final long DRAWER_CLOSE_DELAY = 300L;
+
     protected ContactsView mCurrentView;
 
     private class ContactsActionBarDrawerToggle extends ActionBarDrawerToggle {
-
-        private Runnable mRunnable;
         private boolean mMenuClickedBefore = SharedPreferenceUtil.getHamburgerMenuClickedBefore(
                 ContactsDrawerActivity.this);
 
@@ -177,16 +178,8 @@
             if (newState != DrawerLayout.STATE_IDLE) {
                 updateStatusBarBackground();
             }
-            if (mRunnable != null && newState == DrawerLayout.STATE_IDLE) {
-                mRunnable.run();
-                mRunnable = null;
-            }
             initializeAssistantNewBadge();
         }
-
-        public void runWhenIdle(Runnable runnable) {
-            mRunnable = runnable;
-        }
     }
 
     protected ContactListFilterController mContactListFilterController;
@@ -437,14 +430,9 @@
                 menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
                     @Override
                     public boolean onMenuItemClick(MenuItem item) {
-                        mToggle.runWhenIdle(new Runnable() {
-                            @Override
-                            public void run() {
-                                onGroupMenuItemClicked(groupListItem.getGroupId(),
-                                        groupListItem.getTitle());
-                                updateMenuSelection(menuItem);
-                            }
-                        });
+                        onGroupMenuItemClicked(groupListItem.getGroupId(),
+                                groupListItem.getTitle());
+                        updateMenuSelection(menuItem);
                         mDrawer.closeDrawer(GravityCompat.START);
                         return true;
                     }
@@ -464,12 +452,7 @@
         menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
             @Override
             public boolean onMenuItemClick(MenuItem item) {
-                mToggle.runWhenIdle(new Runnable() {
-                    @Override
-                    public void run() {
-                        onCreateGroupMenuItemClicked();
-                    }
-                });
+                onCreateGroupMenuItemClicked();
                 mDrawer.closeDrawer(GravityCompat.START);
                 return true;
             }
@@ -562,13 +545,8 @@
             menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
                 @Override
                 public boolean onMenuItemClick(MenuItem item) {
-                    mToggle.runWhenIdle(new Runnable() {
-                        @Override
-                        public void run() {
-                            onFilterMenuItemClicked(intent);
-                            updateMenuSelection(menuItem);
-                        }
-                    });
+                    onFilterMenuItemClicked(intent);
+                    updateMenuSelection(menuItem);
                     mDrawer.closeDrawer(GravityCompat.START);
                     return true;
                 }
@@ -614,28 +592,29 @@
     @Override
     public boolean onNavigationItemSelected(final MenuItem item) {
         final int id = item.getItemId();
-        mToggle.runWhenIdle(new Runnable() {
-            @Override
-            public void run() {
-                if (id == R.id.nav_settings) {
+
+        if (id == R.id.nav_settings) {
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
                     startActivity(createPreferenceIntent());
-                } else if (id == R.id.nav_help) {
-                    HelpUtils.launchHelpAndFeedbackForMainScreen(ContactsDrawerActivity.this);
-                } else if (id == R.id.nav_all_contacts) {
-                    switchToAllContacts();
-                } else if (id == R.id.nav_assistant) {
-                    if (!isAssistantView()) {
-                        launchAssistant();
-                        updateMenuSelection(item);
-                    }
-                } else if (item.getIntent() != null) {
-                    ImplicitIntentsUtil.startActivityInApp(ContactsDrawerActivity.this,
-                            item.getIntent());
-                } else {
-                    Log.w(TAG, "Unhandled navigation view item selection");
                 }
+            }, DRAWER_CLOSE_DELAY);
+        } else if (id == R.id.nav_help) {
+            HelpUtils.launchHelpAndFeedbackForMainScreen(ContactsDrawerActivity.this);
+        } else if (id == R.id.nav_all_contacts) {
+            switchToAllContacts();
+        } else if (id == R.id.nav_assistant) {
+            if (!isAssistantView()) {
+                launchAssistant();
+                updateMenuSelection(item);
             }
-        });
+        } else if (item.getIntent() != null) {
+            ImplicitIntentsUtil.startActivityInApp(ContactsDrawerActivity.this,
+                    item.getIntent());
+        } else {
+            Log.w(TAG, "Unhandled navigation view item selection");
+        }
 
         mDrawer.closeDrawer(GravityCompat.START);
         return true;
diff --git a/src/com/android/contacts/SimImportFragment.java b/src/com/android/contacts/SimImportFragment.java
index 09bc4de..a873842 100644
--- a/src/com/android/contacts/SimImportFragment.java
+++ b/src/com/android/contacts/SimImportFragment.java
@@ -259,11 +259,6 @@
         updateSelectedCount();
     }
 
-    @Override
-    public void onSelectedContactsChangedViaCheckBox() {
-        updateSelectedCount();
-    }
-
     private void updateSelectedCount() {
         final int selectedCount = mAdapter.getSelectedContactIds().size();
         if (selectedCount == 0) {
diff --git a/src/com/android/contacts/common/Experiments.java b/src/com/android/contacts/common/Experiments.java
index b182491..f1fc274 100644
--- a/src/com/android/contacts/common/Experiments.java
+++ b/src/com/android/contacts/common/Experiments.java
@@ -49,16 +49,6 @@
     public static final String PULL_TO_REFRESH_CANCEL_REFRESH_MILLIS =
             "PullToRefresh__cancel_refresh_millis";
 
-    /**
-     * Search study boolean indicating whether to inject yenta search results before CP2 results.
-     */
-    public static final String SEARCH_YENTA = "Search__yenta";
-
-    /**
-     * The time to wait for Yenta search results before giving up.
-     */
-    public static final String SEARCH_YENTA_TIMEOUT_MILLIS = "Search__yenta_timeout";
-
     private Experiments() {
     }
 }
diff --git a/src/com/android/contacts/common/activity/LicenseActivity.java b/src/com/android/contacts/common/activity/LicenseActivity.java
index 71bcd84..9e86ee8 100644
--- a/src/com/android/contacts/common/activity/LicenseActivity.java
+++ b/src/com/android/contacts/common/activity/LicenseActivity.java
@@ -17,8 +17,8 @@
 
 import com.android.contacts.common.R;
 
-import android.app.ActionBar;
-import android.app.Activity;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
 import android.os.Bundle;
 import android.view.MenuItem;
 import android.webkit.WebView;
@@ -26,7 +26,7 @@
 /**
  * Displays the licenses for all open source libraries.
  */
-public class LicenseActivity extends Activity {
+public class LicenseActivity extends AppCompatActivity {
     private static final String LICENSE_FILE = "file:///android_asset/licenses.html";
     private WebView mWebView;
 
@@ -36,7 +36,7 @@
         setContentView(R.layout.licenses);
         mWebView = (WebView) findViewById(R.id.webview);
         mWebView.loadUrl(LICENSE_FILE);
-        final ActionBar actionBar = getActionBar();
+        final ActionBar actionBar = getSupportActionBar();
         if (actionBar != null) {
             actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
         }
diff --git a/src/com/android/contacts/common/list/ContactListItemView.java b/src/com/android/contacts/common/list/ContactListItemView.java
index 6e742a3..d20c6e8 100644
--- a/src/com/android/contacts/common/list/ContactListItemView.java
+++ b/src/com/android/contacts/common/list/ContactListItemView.java
@@ -1035,7 +1035,7 @@
         headerImageView.setImageTintList(ColorStateList.valueOf(getResources()
                 .getColor(R.color.material_star_pink)));
         headerImageView.setContentDescription(
-                getContext().getString(R.string.list_filter_all_starred));
+                getContext().getString(R.string.contactsFavoritesLabel));
         headerImageView.setVisibility(View.VISIBLE);
         addView(headerImageView);
     }
diff --git a/src/com/android/contacts/common/list/DefaultContactListAdapter.java b/src/com/android/contacts/common/list/DefaultContactListAdapter.java
index cea2062..5827930 100644
--- a/src/com/android/contacts/common/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/common/list/DefaultContactListAdapter.java
@@ -116,13 +116,6 @@
                 loader.setUri(builder.build());
                 loader.setProjection(getProjection(true));
                 sortOrder = STREQUENT_SORT;
-                if (Flags.getInstance().getBoolean(Experiments.SEARCH_YENTA)
-                        && loader instanceof FavoritesAndContactsLoader
-                        && directoryId == Directory.DEFAULT) {
-                    final FavoritesAndContactsLoader favoritesAndContactsLoader =
-                            (FavoritesAndContactsLoader) loader;
-                    favoritesAndContactsLoader.setAutocompleteQuery(query);
-                }
             }
         } else {
             final ContactListFilter filter = getFilter();
diff --git a/src/com/android/contacts/common/list/FavoritesAndContactsLoader.java b/src/com/android/contacts/common/list/FavoritesAndContactsLoader.java
index e112846..422cf7c 100644
--- a/src/com/android/contacts/common/list/FavoritesAndContactsLoader.java
+++ b/src/com/android/contacts/common/list/FavoritesAndContactsLoader.java
@@ -21,37 +21,24 @@
 import android.database.MergeCursor;
 import android.os.Bundle;
 import android.provider.ContactsContract.Contacts;
-import android.util.Log;
 
-import com.android.contacts.common.Experiments;
-import com.android.contactsbind.ObjectFactory;
-import com.android.contactsbind.experiments.Flags;
-import com.android.contactsbind.search.AutocompleteHelper;
 import com.google.common.collect.Lists;
 
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 /**
  * A loader for use in the default contact list, which will also query for favorite contacts
  * if configured to do so.
  */
-public class FavoritesAndContactsLoader extends CursorLoader implements AutocompleteHelper.Listener {
+public class FavoritesAndContactsLoader extends CursorLoader {
 
     private boolean mLoadFavorites;
 
     private String[] mProjection;
 
-    private String mAutocompleteQuery;
-    private CountDownLatch mAutocompleteLatch = new CountDownLatch(1);
-    private Cursor mAutocompleteCursor;
-    private int mAutocompleteTimeout;
 
     public FavoritesAndContactsLoader(Context context) {
         super(context);
-        mAutocompleteTimeout = Flags.getInstance().getInteger(
-                Experiments.SEARCH_YENTA_TIMEOUT_MILLIS);
     }
 
     /** Whether to load favorites and merge results in before any other results. */
@@ -59,10 +46,6 @@
         mLoadFavorites = flag;
     }
 
-    public void setAutocompleteQuery(String autocompleteQuery) {
-        mAutocompleteQuery = autocompleteQuery;
-    }
-
     public void setProjection(String[] projection) {
         super.setProjection(projection);
         mProjection = projection;
@@ -74,36 +57,8 @@
         if (mLoadFavorites) {
             cursors.add(loadFavoritesContacts());
         }
-
-        if (mAutocompleteQuery != null) {
-            final AutocompleteHelper autocompleteHelper =
-                    ObjectFactory.getAutocompleteHelper(getContext());
-            if (autocompleteHelper != null) {
-                autocompleteHelper.setListener(this);
-                autocompleteHelper.setProjection(mProjection);
-                autocompleteHelper.setQuery(mAutocompleteQuery);
-                try {
-                    if (!mAutocompleteLatch.await(mAutocompleteTimeout, TimeUnit.MILLISECONDS)) {
-                        logw("Timeout expired before receiving autocompletions");
-                    }
-                } catch (InterruptedException e) {
-                    logw("Interrupted while waiting for autocompletions");
-                }
-                if (mAutocompleteCursor != null) {
-                    cursors.add(mAutocompleteCursor);
-                    // TODO: exclude these results from the main loader results, see b/30742359
-                }
-            }
-        }
-
-        // TODO: if the autocomplete experiment in on, only show those results even if they're empty
-        final Cursor contactsCursor = mAutocompleteQuery == null ? loadContacts() : null;
-        if (mAutocompleteQuery == null) {
-            cursors.add(contactsCursor);
-        }
-        // Guard against passing an empty array to the MergeCursor constructor
-        if (cursors.isEmpty()) cursors.add(null);
-
+        final Cursor contactsCursor = loadContacts();
+        cursors.add(contactsCursor);
         return new MergeCursor(cursors.toArray(new Cursor[cursors.size()])) {
             @Override
             public Bundle getExtras() {
@@ -136,18 +91,4 @@
                 Contacts.CONTENT_URI, mProjection, selection.toString(), new String[]{"1"},
                 getSortOrder());
     }
-
-    @Override
-    public void onAutocompletesAvailable(Cursor cursor) {
-        if (cursor != null && cursor.getCount() > 0) {
-            mAutocompleteCursor = cursor;
-            mAutocompleteLatch.countDown();
-        }
-    }
-
-    private static void logw(String message) {
-        if (Log.isLoggable(AutocompleteHelper.TAG, Log.WARN)) {
-            Log.w(AutocompleteHelper.TAG, message);
-        }
-    }
 }
diff --git a/src/com/android/contacts/common/list/MultiSelectEntryContactListAdapter.java b/src/com/android/contacts/common/list/MultiSelectEntryContactListAdapter.java
index b08e367..efba9ca 100644
--- a/src/com/android/contacts/common/list/MultiSelectEntryContactListAdapter.java
+++ b/src/com/android/contacts/common/list/MultiSelectEntryContactListAdapter.java
@@ -20,9 +20,9 @@
 import android.database.Cursor;
 import android.provider.ContactsContract;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.widget.CheckBox;
 
+import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.group.GroupUtil;
 
 import java.util.TreeSet;
@@ -35,13 +35,12 @@
 
     private SelectedContactsListener mSelectedContactsListener;
     private DeleteContactListener mDeleteContactListener;
-    private TreeSet<Long> mSelectedContactIds = new TreeSet<Long>();
+    private TreeSet<Long> mSelectedContactIds = new TreeSet<>();
     private boolean mDisplayCheckBoxes;
     private final int mContactIdColumnIndex;
 
     public interface SelectedContactsListener {
         void onSelectedContactsChanged();
-        void onSelectedContactsChangedViaCheckBox();
     }
 
     public interface DeleteContactListener {
@@ -149,18 +148,35 @@
             return cursor.getLong(getContactColumnIdIndex());
         }
         return 0;
-     }
+    }
 
     @Override
     protected void bindView(View itemView, int partition, Cursor cursor, int position) {
         super.bindView(itemView, partition, cursor, position);
         final ContactListItemView view = (ContactListItemView) itemView;
         bindViewId(view, cursor, getContactColumnIdIndex());
-        bindCheckBox(view, cursor, position, partition == ContactsContract.Directory.DEFAULT);
+        bindCheckBox(view, cursor, partition == ContactsContract.Directory.DEFAULT);
     }
 
-    private void bindCheckBox(ContactListItemView view, Cursor cursor, int position,
-            boolean isLocalDirectory) {
+    /**
+      * Loads the photo for the photo view.
+      * @param photoIdColumn Index of the photo id column
+      * @param lookUpKeyColumn Index of the lookup key column
+      * @param displayNameColumn Index of the display name column
+      */
+    protected void bindPhoto(final ContactListItemView view, final Cursor cursor,
+           final int photoIdColumn, final int lookUpKeyColumn, final int displayNameColumn) {
+        final long photoId = cursor.isNull(photoIdColumn)
+            ? 0 : cursor.getLong(photoIdColumn);
+        final ContactPhotoManager.DefaultImageRequest imageRequest = photoId == 0
+            ? getDefaultImageRequestFromCursor(cursor, displayNameColumn,
+            lookUpKeyColumn)
+            : null;
+        getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, getCircularPhotos(),
+                imageRequest);
+    }
+
+    private void bindCheckBox(ContactListItemView view, Cursor cursor, boolean isLocalDirectory) {
         // Disable clicking on all contacts from remote directories when showing check boxes. We do
         // this by telling the view to handle clicking itself.
         view.setClickable(!isLocalDirectory && mDisplayCheckBoxes);
@@ -173,24 +189,7 @@
         final CheckBox checkBox = view.getCheckBox();
         final long contactId = cursor.getLong(mContactIdColumnIndex);
         checkBox.setChecked(mSelectedContactIds.contains(contactId));
+        checkBox.setClickable(false);
         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();
-            }
-        }
-    };
 }
diff --git a/src/com/android/contacts/common/vcard/ImportVCardActivity.java b/src/com/android/contacts/common/vcard/ImportVCardActivity.java
index d7b64b4..e5fab4e 100644
--- a/src/com/android/contacts/common/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/common/vcard/ImportVCardActivity.java
@@ -290,7 +290,12 @@
             } finally {
                 Log.i(LOG_TAG, "Finished caching vCard.");
                 mWakeLock.release();
-                unbindService(mConnection);
+                try {
+                    unbindService(mConnection);
+                } catch (IllegalArgumentException e) {
+                    FeedbackHelper.sendFeedback(ImportVCardActivity.this, LOG_TAG,
+                            "Cannot unbind service connection", e);
+                }
                 mProgressDialogForCachingVCard.dismiss();
                 mProgressDialogForCachingVCard = null;
                 finish();
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
index a76e408..a2bd2e9 100644
--- a/src/com/android/contacts/editor/GroupMembershipView.java
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -17,6 +17,7 @@
 package com.android.contacts.editor;
 
 import android.app.Activity;
+import android.app.FragmentManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
@@ -161,6 +162,18 @@
     private boolean mDefaultGroupVisibilityKnown;
     private boolean mDefaultGroupVisible;
     private boolean mCreatedNewGroup;
+    private GroupNameEditDialogFragment mGroupNameEditDialogFragment;
+    private GroupNameEditDialogFragment.Listener mListener =
+            new GroupNameEditDialogFragment.Listener() {
+                @Override
+                public void onGroupNameEditCancelled() {
+                }
+
+                @Override
+                public void onGroupNameEditCompleted(String name) {
+                    mCreatedNewGroup = true;
+                }
+            };
 
     private String mNoGroupString;
     private int mPrimaryTextColor;
@@ -185,6 +198,15 @@
         setFocusableInTouchMode(true);
     }
 
+    private void setGroupNameEditDialogFragment() {
+        final FragmentManager fragmentManager = ((Activity) getContext()).getFragmentManager();
+        mGroupNameEditDialogFragment = (GroupNameEditDialogFragment)
+                fragmentManager.findFragmentByTag(TAG_CREATE_GROUP_FRAGMENT);
+        if (mGroupNameEditDialogFragment != null) {
+            mGroupNameEditDialogFragment.setListener(mListener);
+        }
+    }
+
     @Override
     public void setEnabled(boolean enabled) {
         super.setEnabled(enabled);
@@ -240,6 +262,7 @@
         mDefaultGroupVisibilityKnown = false;
         mCreatedNewGroup = false;
         updateView();
+        setGroupNameEditDialogFragment();
     }
 
     private void updateView() {
@@ -453,24 +476,12 @@
     private void createNewGroup() {
         UiClosables.closeQuietly(mPopup);
         mPopup = null;
-
-        final GroupNameEditDialogFragment dialog =
-                GroupNameEditDialogFragment.newInstanceForCreation(
-                        new AccountWithDataSet(mAccountName, mAccountType, mDataSet), null);
-
-        // If the device is rotated after the dialog is shown, the listener will become null,
-        // so that the popup from GroupMembershipView will not be shown.
-        dialog.setListener(new GroupNameEditDialogFragment.Listener() {
-            @Override
-            public void onGroupNameEditStarted(String groupName) {
-                mCreatedNewGroup = true;
-            }
-            @Override
-            public void onGroupNameEditCancelled() { }
-        });
-        dialog.show(
+        mGroupNameEditDialogFragment =
+                    GroupNameEditDialogFragment.newInstanceForCreation(
+                            new AccountWithDataSet(mAccountName, mAccountType, mDataSet), null);
+        mGroupNameEditDialogFragment.setListener(mListener);
+        mGroupNameEditDialogFragment.show(
                 ((Activity) getContext()).getFragmentManager(),
                 TAG_CREATE_GROUP_FRAGMENT);
     }
-
 }
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
index 5283009..1e727df 100644
--- a/src/com/android/contacts/editor/PhotoEditorView.java
+++ b/src/com/android/contacts/editor/PhotoEditorView.java
@@ -116,10 +116,13 @@
             mPhotoIcon.setVisibility(View.GONE);
             mPhotoIconOverlay.setVisibility(View.GONE);
             mPhotoTouchInterceptOverlay.setClickable(false);
+            mPhotoTouchInterceptOverlay.setContentDescription(getContext().getString(
+                    R.string.editor_contact_photo_content_description));
         } else {
             mPhotoIcon.setVisibility(View.VISIBLE);
             mPhotoIconOverlay.setVisibility(View.VISIBLE);
             mPhotoTouchInterceptOverlay.setOnClickListener(this);
+            updatePhotoDescription();
         }
     }
 
@@ -208,13 +211,21 @@
     private void setPhoto(Bitmap bitmap) {
         mPhotoImageView.setImageBitmap(bitmap);
         mIsNonDefaultPhotoBound = true;
+        updatePhotoDescription();
     }
 
     private void setDefaultPhoto(MaterialPalette materialPalette) {
         mIsNonDefaultPhotoBound = false;
+        updatePhotoDescription();
         EditorUiUtils.setDefaultPhoto(mPhotoImageView, getResources(), materialPalette);
     }
 
+    private void updatePhotoDescription() {
+        mPhotoTouchInterceptOverlay.setContentDescription(getContext().getString(
+                mIsNonDefaultPhotoBound
+                        ? R.string.editor_change_photo_content_description
+                        : R.string.editor_add_photo_content_description));
+    }
     /**
      * Binds a full size photo loaded from the given Uri.
      */
@@ -222,6 +233,7 @@
         EditorUiUtils.loadPhoto(ContactPhotoManager.getInstance(getContext()),
                 mPhotoImageView, photoUri);
         mIsNonDefaultPhotoBound = true;
+        updatePhotoDescription();
     }
 
     /**
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 8df7c37..8e177e4 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -685,10 +685,8 @@
 
     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);
+        final View field = mLayoutInflater.inflate(R.layout.item_read_only_field, mKindSectionViews,
+                /* attachToRoot */ false);
         if (isFirstEntry) {
             final ImageView imageView = (ImageView) field.findViewById(R.id.kind_icon);
             imageView.setImageDrawable(icon);
@@ -749,8 +747,7 @@
 
         // Set the content description
         mAccountHeaderContainer.setContentDescription(
-                EditorUiUtils.getAccountInfoContentDescription(primaryText,
-                        secondaryText));
+                EditorUiUtils.getAccountInfoContentDescription(secondaryText, primaryText));
     }
 
     private void addAccountSelector(final RawContactDelta rawContactDelta) {
diff --git a/src/com/android/contacts/group/GroupMembersAdapter.java b/src/com/android/contacts/group/GroupMembersAdapter.java
index af1f61e..c501984 100644
--- a/src/com/android/contacts/group/GroupMembersAdapter.java
+++ b/src/com/android/contacts/group/GroupMembersAdapter.java
@@ -150,7 +150,8 @@
         final ContactListItemView view = (ContactListItemView) v;
         bindSectionHeaderAndDivider(view, position);
         bindName(view, cursor);
-        bindPhoto(view, cursor);
+        bindPhoto(view, cursor, GroupMembersQuery.CONTACT_PHOTO_ID,
+                GroupMembersQuery.CONTACT_LOOKUP_KEY, GroupMembersQuery.CONTACT_DISPLAY_NAME);
         bindDeleteButton(view, position);
     }
 
@@ -169,17 +170,6 @@
                 getContactNameDisplayOrder());
     }
 
-    private void bindPhoto(final ContactListItemView view, Cursor cursor) {
-        final long photoId = cursor.isNull(GroupMembersQuery.CONTACT_PHOTO_ID)
-                ? 0 : cursor.getLong(GroupMembersQuery.CONTACT_PHOTO_ID);
-        final DefaultImageRequest imageRequest = photoId == 0
-                ? getDefaultImageRequestFromCursor(cursor, GroupMembersQuery.CONTACT_DISPLAY_NAME,
-                        GroupMembersQuery.CONTACT_LOOKUP_KEY)
-                : null;
-        getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, getCircularPhotos(),
-                imageRequest);
-    }
-
     private void bindDeleteButton(final ContactListItemView view, int position) {
         if (mDisplayDeleteButtons) {
             view.getDeleteImageButton(getDeleteContactListener(), position);
diff --git a/src/com/android/contacts/group/GroupNameEditDialogFragment.java b/src/com/android/contacts/group/GroupNameEditDialogFragment.java
index 544dd87..da76c68 100644
--- a/src/com/android/contacts/group/GroupNameEditDialogFragment.java
+++ b/src/com/android/contacts/group/GroupNameEditDialogFragment.java
@@ -67,15 +67,15 @@
 
     /** Callbacks for hosts of the {@link GroupNameEditDialogFragment}. */
     public interface Listener {
-        void onGroupNameEditStarted(String name);
         void onGroupNameEditCancelled();
+        void onGroupNameEditCompleted(String name);
 
         public static final Listener None = new Listener() {
             @Override
-            public void onGroupNameEditStarted(String name) { }
+            public void onGroupNameEditCancelled() { }
 
             @Override
-            public void onGroupNameEditCancelled() { }
+            public void onGroupNameEditCompleted(String name) { }
         };
     }
 
@@ -126,6 +126,7 @@
         } else {
             mGroupName = savedInstanceState.getString(ARG_GROUP_NAME);
         }
+
         mGroupId = args.getLong(ARG_GROUP_ID, NO_GROUP_ID);
         mIsInsert = args.getBoolean(ARG_IS_INSERT, true);
         mAccount = getArguments().getParcelable(ARG_ACCOUNT);
@@ -252,7 +253,7 @@
                     name, getActivity().getClass(), callbackAction);
         }
         ContactSaveService.startService(getActivity(), serviceIntent);
-        getListener().onGroupNameEditStarted(name);
+        getListener().onGroupNameEditCompleted(mGroupName);
         dismiss();
     }
 
diff --git a/src/com/android/contacts/list/MultiSelectContactsListFragment.java b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
index 10e9370..7a6a64a 100644
--- a/src/com/android/contacts/list/MultiSelectContactsListFragment.java
+++ b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
@@ -115,16 +115,6 @@
     }
 
     @Override
-    public void onSelectedContactsChangedViaCheckBox() {
-        if (getAdapter().getSelectedContactIds().size() == 0) {
-            // Last checkbox has been unchecked. So we should stop displaying checkboxes.
-            mCheckBoxListListener.onStopDisplayingCheckBoxes();
-        } else {
-            onSelectedContactsChanged();
-        }
-    }
-
-    @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
         if (savedInstanceState != null) {
@@ -403,8 +393,8 @@
         if (accountType instanceof GoogleAccountType) {
             accountFilterHeaderIcon.getLayoutParams().height = getResources()
                     .getDimensionPixelOffset(R.dimen.contact_browser_list_header_icon_size);
-            accountFilterHeaderIcon.getLayoutParams().width =
-                    accountFilterHeaderIcon.getLayoutParams().height;
+            accountFilterHeaderIcon.getLayoutParams().width = getResources()
+                    .getDimensionPixelOffset(R.dimen.contact_browser_list_header_icon_size);
 
             setMargins(accountFilterHeaderIcon,
                     getResources().getDimensionPixelOffset(
@@ -414,8 +404,8 @@
         } else {
             accountFilterHeaderIcon.getLayoutParams().height = getResources()
                     .getDimensionPixelOffset(R.dimen.contact_browser_list_header_icon_size_alt);
-            accountFilterHeaderIcon.getLayoutParams().width =
-                    accountFilterHeaderIcon.getLayoutParams().height;
+            accountFilterHeaderIcon.getLayoutParams().width = getResources()
+                    .getDimensionPixelOffset(R.dimen.contact_browser_list_header_icon_size_alt);
 
             setMargins(accountFilterHeaderIcon,
                     getResources().getDimensionPixelOffset(
diff --git a/src/com/android/contacts/list/MultiSelectEmailAddressesListAdapter.java b/src/com/android/contacts/list/MultiSelectEmailAddressesListAdapter.java
index af9ee52..7f4bb4b 100644
--- a/src/com/android/contacts/list/MultiSelectEmailAddressesListAdapter.java
+++ b/src/com/android/contacts/list/MultiSelectEmailAddressesListAdapter.java
@@ -29,6 +29,7 @@
 
 import android.provider.ContactsContract.CommonDataKinds.Email;
 
+import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.common.list.ContactListItemView;
 import com.android.contacts.common.list.MultiSelectEntryContactListAdapter;
 import com.android.contacts.common.preference.ContactsPreferences;
@@ -162,9 +163,8 @@
         bindViewId(view, cursor, EmailQuery.EMAIL_ID);
         if (isFirstEntry) {
             bindName(view, cursor);
-            bindQuickContact(view, partition, cursor, EmailQuery.PHOTO_ID,
-                    EmailQuery.PHOTO_URI, EmailQuery.CONTACT_ID,
-                    EmailQuery.LOOKUP_KEY, EmailQuery.DISPLAY_NAME);
+            bindPhoto(view, cursor, EmailQuery.PHOTO_ID, EmailQuery.LOOKUP_KEY,
+                    EmailQuery.DISPLAY_NAME);
         } else {
             unbindName(view);
             view.removePhotoView(true, false);
diff --git a/src/com/android/contacts/list/MultiSelectEmailAddressesListFragment.java b/src/com/android/contacts/list/MultiSelectEmailAddressesListFragment.java
index 956e473..64489a0 100644
--- a/src/com/android/contacts/list/MultiSelectEmailAddressesListFragment.java
+++ b/src/com/android/contacts/list/MultiSelectEmailAddressesListFragment.java
@@ -53,11 +53,6 @@
     }
 
     @Override
-    public void onSelectedContactsChangedViaCheckBox() {
-        onSelectedContactsChanged();
-    }
-
-    @Override
     public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
         inflater.inflate(R.menu.items_multi_select, menu);
diff --git a/src/com/android/contacts/list/MultiSelectPhoneNumbersListAdapter.java b/src/com/android/contacts/list/MultiSelectPhoneNumbersListAdapter.java
index 985809b..6bf6d5e 100644
--- a/src/com/android/contacts/list/MultiSelectPhoneNumbersListAdapter.java
+++ b/src/com/android/contacts/list/MultiSelectPhoneNumbersListAdapter.java
@@ -29,6 +29,7 @@
 
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 
+import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.common.list.ContactListItemView;
 import com.android.contacts.common.list.MultiSelectEntryContactListAdapter;
 import com.android.contacts.common.preference.ContactsPreferences;
@@ -162,9 +163,8 @@
         bindViewId(view, cursor, PhoneQuery.PHONE_ID);
         if (isFirstEntry) {
             bindName(view, cursor);
-            bindQuickContact(view, partition, cursor, PhoneQuery.PHOTO_ID,
-                        PhoneQuery.PHOTO_URI, PhoneQuery.CONTACT_ID,
-                        PhoneQuery.LOOKUP_KEY, PhoneQuery.DISPLAY_NAME);
+            bindPhoto(view, cursor, PhoneQuery.PHOTO_ID, PhoneQuery.LOOKUP_KEY,
+                PhoneQuery.DISPLAY_NAME);
         } else {
             unbindName(view);
             view.removePhotoView(true, false);
diff --git a/src/com/android/contacts/list/MultiSelectPhoneNumbersListFragment.java b/src/com/android/contacts/list/MultiSelectPhoneNumbersListFragment.java
index 751449b..96a1de6 100644
--- a/src/com/android/contacts/list/MultiSelectPhoneNumbersListFragment.java
+++ b/src/com/android/contacts/list/MultiSelectPhoneNumbersListFragment.java
@@ -53,11 +53,6 @@
     }
 
     @Override
-    public void onSelectedContactsChangedViaCheckBox() {
-        onSelectedContactsChanged();
-    }
-
-    @Override
     public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
         inflater.inflate(R.menu.items_multi_select, menu);
diff --git a/src/com/android/contacts/util/SharedPreferenceUtil.java b/src/com/android/contacts/util/SharedPreferenceUtil.java
index 14f5494..3d138fb 100644
--- a/src/com/android/contacts/util/SharedPreferenceUtil.java
+++ b/src/com/android/contacts/util/SharedPreferenceUtil.java
@@ -36,14 +36,14 @@
     public static final String PREFERENCE_KEY_GLOBAL_SYNC_OFF_DISMISSES =
             "num-of-dismisses-auto-sync-off";
 
-    private static final String PREFERENCE_KEY_HAMBURGER_PROMO_DISPLAYED_BEFORE =
-            "hamburgerPromoDisplayedBefore";
+    private static final String PREFERENCE_KEY_HAMBURGER_PROMO_DISPLAYED =
+            "hamburgerPromoDisplayed";
 
-    private static final String PREFERENCE_KEY_HAMBURGER_MENU_CLICKED_BEFORE =
-            "hamburgerMenuClickedBefore";
+    private static final String PREFERENCE_KEY_HAMBURGER_MENU_CLICKED =
+            "hamburgerMenuClicked";
 
-    private static final String PREFERENCE_KEY_HAMBURGER_PROMO_TRIGGER_ACTION_HAPPENED_BEFORE =
-            "hamburgerPromoTriggerActionHappenedBefore";
+    private static final String PREFERENCE_KEY_HAMBURGER_PROMO_TRIGGER_ACTION_HAPPENED =
+            "hamburgerPromoTriggerActionHappened";
 
     private static final String PREFERENCE_KEY_IMPORTED_SIM_CARDS =
             "importedSimCards";
@@ -62,34 +62,34 @@
 
     public static boolean getHamburgerPromoDisplayedBefore(Context context) {
         return getSharedPreferences(context)
-                .getBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_DISPLAYED_BEFORE, false);
+                .getBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_DISPLAYED, false);
     }
 
     public static void setHamburgerPromoDisplayedBefore(Context context) {
         getSharedPreferences(context).edit()
-                .putBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_DISPLAYED_BEFORE, true)
+                .putBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_DISPLAYED, true)
                 .apply();
     }
 
     public static boolean getHamburgerMenuClickedBefore(Context context) {
         return getSharedPreferences(context)
-                .getBoolean(PREFERENCE_KEY_HAMBURGER_MENU_CLICKED_BEFORE, false);
+                .getBoolean(PREFERENCE_KEY_HAMBURGER_MENU_CLICKED, false);
     }
 
     public static void setHamburgerMenuClickedBefore(Context context) {
         getSharedPreferences(context).edit()
-                .putBoolean(PREFERENCE_KEY_HAMBURGER_MENU_CLICKED_BEFORE, true)
+                .putBoolean(PREFERENCE_KEY_HAMBURGER_MENU_CLICKED, true)
                 .apply();
     }
 
     public static boolean getHamburgerPromoTriggerActionHappenedBefore(Context context) {
         return getSharedPreferences(context)
-                .getBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_TRIGGER_ACTION_HAPPENED_BEFORE, false);
+                .getBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_TRIGGER_ACTION_HAPPENED, false);
     }
 
     public static void setHamburgerPromoTriggerActionHappenedBefore(Context context) {
         getSharedPreferences(context).edit()
-                .putBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_TRIGGER_ACTION_HAPPENED_BEFORE, true)
+                .putBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_TRIGGER_ACTION_HAPPENED, true)
                 .apply();
     }