Merge branch 'goog/jb-ub-mail-ur8' into master

Conflicts:
	photoviewer/AndroidManifest.xml
	photoviewer/res/values/dimen.xml
	photoviewer/src/com/android/ex/photo/Intents.java
	photoviewer/src/com/android/ex/photo/PhotoViewActivity.java
	photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java
	photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java
	photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java
	photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java
	photoviewer/src/com/android/ex/photo/provider/PhotoContract.java
	photoviewer/src/com/android/ex/photo/util/ImageUtils.java
	photoviewer/src/com/android/ex/photo/views/PhotoView.java

Change-Id: Icb06b228f0eab1f828a21507ec2fbe7ede998536
diff --git a/res/drawable-hdpi/ic_contact_picture.png b/res/drawable-hdpi/ic_contact_picture.png
index 2eef7b5..4c0e35e 100644
--- a/res/drawable-hdpi/ic_contact_picture.png
+++ b/res/drawable-hdpi/ic_contact_picture.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture.png b/res/drawable-mdpi/ic_contact_picture.png
index 6c7cb61..ead9718 100644
--- a/res/drawable-mdpi/ic_contact_picture.png
+++ b/res/drawable-mdpi/ic_contact_picture.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture.png b/res/drawable-xhdpi/ic_contact_picture.png
index 1a2bfde..05a65f6 100644
--- a/res/drawable-xhdpi/ic_contact_picture.png
+++ b/res/drawable-xhdpi/ic_contact_picture.png
Binary files differ
diff --git a/src/com/android/ex/chips/BaseRecipientAdapter.java b/src/com/android/ex/chips/BaseRecipientAdapter.java
index b6d4fa8..f742cf1 100644
--- a/src/com/android/ex/chips/BaseRecipientAdapter.java
+++ b/src/com/android/ex/chips/BaseRecipientAdapter.java
@@ -73,12 +73,12 @@
      * The number of extra entries requested to allow for duplicates. Duplicates
      * are removed from the overall result.
      */
-    private static final int ALLOWANCE_FOR_DUPLICATES = 5;
+    static final int ALLOWANCE_FOR_DUPLICATES = 5;
 
     // This is ContactsContract.PRIMARY_ACCOUNT_NAME. Available from ICS as hidden
-    private static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
+    static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
     // This is ContactsContract.PRIMARY_ACCOUNT_TYPE. Available from ICS as hidden
-    private static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account";
+    static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account";
 
     /** The number of photos cached in this Adapter. */
     private static final int PHOTO_CACHE_SIZE = 20;
@@ -118,7 +118,7 @@
         public static final int PHOTO = 0;
     }
 
-    private static class DirectoryListQuery {
+    protected static class DirectoryListQuery {
 
         public static final Uri URI =
                 Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories");
@@ -250,7 +250,7 @@
                         directoryCursor = mContentResolver.query(
                                 DirectoryListQuery.URI, DirectoryListQuery.PROJECTION,
                                 null, null, null);
-                        paramsList = setupOtherDirectories(directoryCursor);
+                        paramsList = setupOtherDirectories(mContext, directoryCursor, mAccount);
                     } else {
                         // We don't need to search other directories.
                         paramsList = null;
@@ -556,8 +556,9 @@
         return new DefaultFilter();
     }
 
-    private List<DirectorySearchParams> setupOtherDirectories(Cursor directoryCursor) {
-        final PackageManager packageManager = mContext.getPackageManager();
+    public static List<DirectorySearchParams> setupOtherDirectories(Context context,
+            Cursor directoryCursor, Account account) {
+        final PackageManager packageManager = context.getPackageManager();
         final List<DirectorySearchParams> paramsList = new ArrayList<DirectorySearchParams>();
         DirectorySearchParams preferredDirectory = null;
         while (directoryCursor.moveToNext()) {
@@ -594,8 +595,8 @@
             // If an account has been provided and we found a directory that
             // corresponds to that account, place that directory second, directly
             // underneath the local contacts.
-            if (mAccount != null && mAccount.name.equals(params.accountName) &&
-                    mAccount.type.equals(params.accountType)) {
+            if (account != null && account.name.equals(params.accountName) &&
+                    account.type.equals(params.accountType)) {
                 preferredDirectory = params;
             } else {
                 paramsList.add(params);
@@ -708,6 +709,11 @@
         return entries;
     }
 
+
+    protected interface EntriesUpdatedObserver {
+        public void onChanged(List<RecipientEntry> entries);
+    }
+
     public void registerUpdateObserver(EntriesUpdatedObserver observer) {
         mEntriesUpdatedObserver = observer;
     }
@@ -975,11 +981,7 @@
         return android.R.id.icon;
     }
 
-    /**
-     * Interface called before the BaseRecipientAdapter updates recipient
-     * results in the popup window.
-     */
-    protected interface EntriesUpdatedObserver {
-        public void onChanged(List<RecipientEntry> entries);
+    public Account getAccount() {
+        return mAccount;
     }
 }
diff --git a/src/com/android/ex/chips/InvisibleRecipientChip.java b/src/com/android/ex/chips/InvisibleRecipientChip.java
index 46fe19a..beeb499 100644
--- a/src/com/android/ex/chips/InvisibleRecipientChip.java
+++ b/src/com/android/ex/chips/InvisibleRecipientChip.java
@@ -42,6 +42,9 @@
 
     private CharSequence mOriginalText;
 
+    /** <code>true</code> to display the original text, <code>false</code> to display nothing */
+    private boolean mDisplayOriginalText = false;
+
     public InvisibleRecipientChip(RecipientEntry entry, int offset) {
         super();
         mDisplay = entry.getDisplayName();
@@ -112,15 +115,28 @@
         return !TextUtils.isEmpty(mOriginalText) ? mOriginalText : mEntry.getDestination();
     }
 
-    @Override
-    public void draw(Canvas arg0, CharSequence arg1, int arg2, int arg3, float arg4, int arg5,
-            int arg6, int arg7, Paint arg8) {
-        // Do nothing.
+    public void setDisplayOriginalText(final boolean displayOriginalText) {
+        mDisplayOriginalText = displayOriginalText;
     }
 
     @Override
-    public int getSize(Paint arg0, CharSequence arg1, int arg2, int arg3, FontMetricsInt arg4) {
-        return 0;
+    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
+            int bottom, Paint paint) {
+        if (mDisplayOriginalText) {
+            canvas.drawText(text, start, end, x, y, paint);
+        } else {
+            // Do nothing.
+        }
+    }
+
+    @Override
+    public int getSize(
+            Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
+        if (mDisplayOriginalText) {
+            return (int) paint.measureText(text, start, end);
+        } else {
+            return 0;
+        }
     }
 
     @Override
diff --git a/src/com/android/ex/chips/RecipientAlternatesAdapter.java b/src/com/android/ex/chips/RecipientAlternatesAdapter.java
index b54d83d..00f1ff4 100644
--- a/src/com/android/ex/chips/RecipientAlternatesAdapter.java
+++ b/src/com/android/ex/chips/RecipientAlternatesAdapter.java
@@ -16,9 +16,13 @@
 
 package com.android.ex.chips;
 
+import android.accounts.Account;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.MatrixCursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
 import android.text.util.Rfc822Token;
 import android.text.util.Rfc822Tokenizer;
 import android.util.Log;
@@ -29,11 +33,14 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.ex.chips.BaseRecipientAdapter.DirectoryListQuery;
+import com.android.ex.chips.BaseRecipientAdapter.DirectorySearchParams;
 import com.android.ex.chips.Queries.Query;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * RecipientAlternatesAdapter backs the RecipientEditTextView for managing contacts
@@ -55,9 +62,13 @@
     public static final int QUERY_TYPE_PHONE = 1;
     private Query mQuery;
 
-    public static HashMap<String, RecipientEntry> getMatchingRecipients(Context context,
-            ArrayList<String> inAddresses) {
-        return getMatchingRecipients(context, inAddresses, QUERY_TYPE_EMAIL);
+    public interface RecipientMatchCallback {
+        public void matchesFound(HashMap<String, RecipientEntry> results);
+    }
+
+    public static void getMatchingRecipients(Context context, ArrayList<String> inAddresses,
+            Account account, RecipientMatchCallback callback) {
+        getMatchingRecipients(context, inAddresses, QUERY_TYPE_EMAIL, account, callback);
     }
 
     /**
@@ -67,10 +78,11 @@
      *
      * @param context Context.
      * @param inAddresses Array of addresses on which to perform the lookup.
+     * @param callback RecipientMatchCallback called when a match or matches are found.
      * @return HashMap<String,RecipientEntry>
      */
-    public static HashMap<String, RecipientEntry> getMatchingRecipients(Context context,
-            ArrayList<String> inAddresses, int addressType) {
+    public static void getMatchingRecipients(Context context, ArrayList<String> inAddresses,
+            int addressType, Account account, RecipientMatchCallback callback) {
         Queries.Query query;
         if (addressType == QUERY_TYPE_EMAIL) {
             query = Queries.EMAIL;
@@ -78,12 +90,12 @@
             query = Queries.PHONE;
         }
         int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.size());
-        String[] addresses = new String[addressesSize];
+        HashSet<String> addresses = new HashSet<String>();
         StringBuilder bindString = new StringBuilder();
         // Create the "?" string and set up arguments.
         for (int i = 0; i < addressesSize; i++) {
             Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses.get(i).toLowerCase());
-            addresses[i] = (tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i));
+            addresses.add(tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i));
             bindString.append("?");
             if (i < addressesSize - 1) {
                 bindString.append(",");
@@ -94,44 +106,115 @@
             Log.d(TAG, "Doing reverse lookup for " + addresses.toString());
         }
 
-        HashMap<String, RecipientEntry> recipientEntries = new HashMap<String, RecipientEntry>();
-        Cursor c = context.getContentResolver().query(
-                query.getContentUri(),
-                query.getProjection(),
-                query.getProjection()[Queries.Query.DESTINATION] + " IN (" + bindString.toString()
-                        + ")", addresses, null);
+        String[] addressArray = new String[addresses.size()];
+        addresses.toArray(addressArray);
+        HashMap<String, RecipientEntry> recipientEntries = null;
+        Cursor c = null;
 
-        if (c != null) {
-            try {
-                if (c.moveToFirst()) {
-                    do {
-                        String address = c.getString(Queries.Query.DESTINATION);
-                        recipientEntries.put(address, RecipientEntry.constructTopLevelEntry(
-                                c.getString(Queries.Query.NAME),
-                                c.getInt(Queries.Query.DISPLAY_NAME_SOURCE),
-                                c.getString(Queries.Query.DESTINATION),
-                                c.getInt(Queries.Query.DESTINATION_TYPE),
-                                c.getString(Queries.Query.DESTINATION_LABEL),
-                                c.getLong(Queries.Query.CONTACT_ID),
-                                c.getLong(Queries.Query.DATA_ID),
-                                c.getString(Queries.Query.PHOTO_THUMBNAIL_URI),
-                                true));
-                        if (Log.isLoggable(TAG, Log.DEBUG)) {
-                            Log.d(TAG, "Received reverse look up information for " + address
-                                    + " RESULTS: "
-                                    + " NAME : " + c.getString(Queries.Query.NAME)
-                                    + " CONTACT ID : " + c.getLong(Queries.Query.CONTACT_ID)
-                                    + " ADDRESS :" + c.getString(Queries.Query.DESTINATION));
-                        }
-                    } while (c.moveToNext());
-                }
-            } finally {
+        try {
+            c = context.getContentResolver().query(
+                    query.getContentUri(),
+                    query.getProjection(),
+                    query.getProjection()[Queries.Query.DESTINATION] + " IN ("
+                            + bindString.toString() + ")", addressArray, null);
+            recipientEntries = processContactEntries(c);
+            callback.matchesFound(recipientEntries);
+        } finally {
+            if (c != null) {
                 c.close();
             }
         }
+        // See if any entries did not resolve; if so, we need to check other
+        // directories
+        if (recipientEntries.size() < addresses.size()) {
+            final List<DirectorySearchParams> paramsList;
+            Cursor directoryCursor = context.getContentResolver().query(DirectoryListQuery.URI,
+                    DirectoryListQuery.PROJECTION, null, null, null);
+            paramsList = BaseRecipientAdapter.setupOtherDirectories(context, directoryCursor,
+                    account);
+            // Run a directory query for each unmatched recipient.
+            HashSet<String> unresolvedAddresses = new HashSet<String>();
+            for (String address : addresses) {
+                if (!recipientEntries.containsKey(address)) {
+                    unresolvedAddresses.add(address);
+                }
+            }
+            Cursor directoryContactsCursor = null;
+            for (String unresolvedAddress : unresolvedAddresses) {
+                for (int i = 0; i < paramsList.size(); i++) {
+                    try {
+                        directoryContactsCursor = doQuery(unresolvedAddress, 1,
+                                paramsList.get(i).directoryId, account,
+                                context.getContentResolver(), query);
+                    } finally {
+                        if (directoryContactsCursor != null
+                                && directoryContactsCursor.getCount() == 0) {
+                            directoryContactsCursor.close();
+                            directoryContactsCursor = null;
+                        } else {
+                            break;
+                        }
+                    }
+                }
+                if (directoryContactsCursor != null) {
+                    try {
+                        callback.matchesFound(processContactEntries(directoryContactsCursor));
+                    } finally {
+                        directoryContactsCursor.close();
+                    }
+                }
+            }
+        }
+    }
+
+    private static HashMap<String, RecipientEntry> processContactEntries(Cursor c) {
+        HashMap<String, RecipientEntry> recipientEntries = new HashMap<String, RecipientEntry>();
+        if (c != null && c.moveToFirst()) {
+            do {
+                String address = c.getString(Queries.Query.DESTINATION);
+                recipientEntries.put(address, RecipientEntry.constructTopLevelEntry(
+                        c.getString(Queries.Query.NAME),
+                        c.getInt(Queries.Query.DISPLAY_NAME_SOURCE),
+                        c.getString(Queries.Query.DESTINATION),
+                        c.getInt(Queries.Query.DESTINATION_TYPE),
+                        c.getString(Queries.Query.DESTINATION_LABEL),
+                        c.getLong(Queries.Query.CONTACT_ID),
+                        c.getLong(Queries.Query.DATA_ID),
+                        c.getString(Queries.Query.PHOTO_THUMBNAIL_URI),
+                        true));
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Received reverse look up information for " + address
+                            + " RESULTS: "
+                            + " NAME : " + c.getString(Queries.Query.NAME)
+                            + " CONTACT ID : " + c.getLong(Queries.Query.CONTACT_ID)
+                            + " ADDRESS :" + c.getString(Queries.Query.DESTINATION));
+                }
+            } while (c.moveToNext());
+        }
         return recipientEntries;
     }
 
+    private static Cursor doQuery(CharSequence constraint, int limit, Long directoryId,
+            Account account, ContentResolver resolver, Query query) {
+        final Uri.Builder builder = query
+                .getContentFilterUri()
+                .buildUpon()
+                .appendPath(constraint.toString())
+                .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
+                        String.valueOf(limit + BaseRecipientAdapter.ALLOWANCE_FOR_DUPLICATES));
+        if (directoryId != null) {
+            builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+                    String.valueOf(directoryId));
+        }
+        if (account != null) {
+            builder.appendQueryParameter(BaseRecipientAdapter.PRIMARY_ACCOUNT_NAME, account.name);
+            builder.appendQueryParameter(BaseRecipientAdapter.PRIMARY_ACCOUNT_TYPE, account.type);
+        }
+        final Cursor cursor = resolver.query(builder.build(), query.getProjection(), null, null,
+                null);
+        return cursor;
+    }
+
     public RecipientAlternatesAdapter(Context context, long contactId, long currentId, int viewId,
             OnCheckedItemChangedListener listener) {
         this(context, contactId, currentId, viewId, QUERY_TYPE_EMAIL, listener);
diff --git a/src/com/android/ex/chips/RecipientEditTextView.java b/src/com/android/ex/chips/RecipientEditTextView.java
index 4b1d53f..c786126 100644
--- a/src/com/android/ex/chips/RecipientEditTextView.java
+++ b/src/com/android/ex/chips/RecipientEditTextView.java
@@ -54,7 +54,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.TypedValue;
-import android.util.Patterns;
 import android.view.ActionMode;
 import android.view.ActionMode.Callback;
 import android.view.DragEvent;
@@ -80,6 +79,8 @@
 import android.widget.ScrollView;
 import android.widget.TextView;
 
+import com.android.ex.chips.RecipientAlternatesAdapter.RecipientMatchCallback;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -311,6 +312,8 @@
         if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
             outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
         }
+
+        outAttrs.actionId = EditorInfo.IME_ACTION_DONE;
         outAttrs.actionLabel = getContext().getString(R.string.done);
         return connection;
     }
@@ -711,6 +714,7 @@
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecipientEditTextView, 0,
                 0);
         Resources r = getContext().getResources();
+
         mChipBackground = a.getDrawable(R.styleable.RecipientEditTextView_chipBackground);
         if (mChipBackground == null) {
             mChipBackground = r.getDrawable(R.drawable.chip_background);
@@ -758,7 +762,6 @@
             mActionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources()
                     .getDisplayMetrics());
         }
-
         a.recycle();
     }
 
@@ -851,7 +854,8 @@
                 for (int i = 0; i < mPendingChips.size(); i++) {
                     String current = mPendingChips.get(i);
                     int tokenStart = editable.toString().indexOf(current);
-                    int tokenEnd = tokenStart + current.length();
+                    // Always leave a space at the end between tokens.
+                    int tokenEnd = tokenStart + current.length() - 1;
                     if (tokenStart >= 0) {
                         // When we have a valid token, include it with the token
                         // to the left.
@@ -1319,10 +1323,6 @@
             removeChip(mSelectedChip);
         }
 
-        if (keyCode == KeyEvent.KEYCODE_ENTER && event.hasNoModifiers()) {
-            return true;
-        }
-
         return super.onKeyDown(keyCode, event);
     }
 
@@ -1904,6 +1904,13 @@
                     end = chipEnd = Math.min(editable.length(), chipStart + token.length());
                     // Only set the span if we found a matching token.
                     if (chipStart != -1) {
+                        if (chip instanceof InvisibleRecipientChip) {
+                            /*
+                             * We want the original text to be displayed until we can replace it
+                             * with a real chip
+                             */
+                            ((InvisibleRecipientChip) chip).setDisplayOriginalText(true);
+                        }
                         editable.setSpan(chip, chipStart, chipEnd,
                                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                     }
@@ -2256,6 +2263,14 @@
                     editable.delete(tokenStart, tokenEnd);
                     getSpannable().removeSpan(repl[0]);
                 }
+            } else if (count > before) {
+                if (mSelectedChip != null
+                    && isGeneratedContact(mSelectedChip)) {
+                    if (lastCharacterIsCommitCharacter(s)) {
+                        commitByCharacter();
+                        return;
+                    }
+                }
             }
         }
 
@@ -2434,54 +2449,80 @@
                     addresses.add(createAddressText(chip.getEntry()));
                 }
             }
-            HashMap<String, RecipientEntry> entries = RecipientAlternatesAdapter
-                    .getMatchingRecipients(getContext(), addresses);
-            final ArrayList<RecipientChip> replacements = new ArrayList<RecipientChip>();
-            for (final RecipientChip temp : originalRecipients) {
-                RecipientEntry entry = null;
-                if (RecipientEntry.isCreatedRecipient(temp.getEntry().getContactId())
-                        && getSpannable().getSpanStart(temp) != -1) {
-                    // Replace this.
-                    entry = createValidatedEntry(entries.get(tokenizeAddress(temp.getEntry()
-                            .getDestination())));
-                }
-                if (entry != null) {
-                    replacements.add(createFreeChip(entry));
-                } else {
-                    replacements.add(temp);
-                }
-            }
-            if (replacements != null && replacements.size() > 0) {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        Editable oldText = getText();
-                        int start, end;
-                        int i = 0;
-                        for (RecipientChip chip : originalRecipients) {
-                            // Find the location of the chip in the text currently shown.
-                            start = oldText.getSpanStart(chip);
-                            if (start != -1) {
-                                end = oldText.getSpanEnd(chip);
-                                oldText.removeSpan(chip);
-                                RecipientChip replacement = replacements.get(i);
-                                // Make sure we always have just 1 space at the
-                                // end to separate this chip from the next chip.
-                                SpannableString displayText = new SpannableString(
-                                        createAddressText(replacement.getEntry()).trim() + " ");
-                                displayText.setSpan(replacement, 0, displayText.length()-1,
-                                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-                                // Replace the old text we found with with the new display text,
-                                // which now may also contain the display name of the recipient.
-                                oldText.replace(start, end, displayText);
-                                replacement.setOriginalText(displayText.toString());
+            RecipientAlternatesAdapter.getMatchingRecipients(getContext(), addresses,
+                    ((BaseRecipientAdapter) getAdapter()).getAccount(),
+                    new RecipientMatchCallback() {
+
+                        @Override
+                        public void matchesFound(HashMap<String, RecipientEntry> entries) {
+                            final ArrayList<RecipientChip> replacements =
+                                    new ArrayList<RecipientChip>();
+                            for (final RecipientChip temp : originalRecipients) {
+                                RecipientEntry entry = null;
+                                if (RecipientEntry.isCreatedRecipient(temp.getEntry()
+                                        .getContactId())
+                                        && getSpannable().getSpanStart(temp) != -1) {
+                                    // Replace this.
+                                    entry = createValidatedEntry(
+                                            entries.get(tokenizeAddress(temp.getEntry()
+                                                    .getDestination())));
+                                }
+                                if (entry != null) {
+                                    replacements.add(createFreeChip(entry));
+                                } else {
+                                    replacements.add(null);
+                                }
                             }
-                            i++;
+                            if (replacements != null && replacements.size() > 0) {
+                                mHandler.post(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        Editable oldText = getText();
+                                        int start, end;
+                                        int i = 0;
+                                        for (RecipientChip chip : originalRecipients) {
+                                            // Find the location of the chip in
+                                            // the text currently shown.
+                                            start = oldText.getSpanStart(chip);
+                                            if (start != -1) {
+                                                RecipientChip replacement = replacements.get(i);
+                                                if (replacement != null) {
+                                                    // Replacing the entirety of
+                                                    // what the chip
+                                                    // represented, including
+                                                    // the extra space dividing
+                                                    // it from other chips.
+                                                    end = oldText.getSpanEnd(chip) + 1;
+                                                    oldText.removeSpan(chip);
+                                                    // Make sure we always have just 1 space at the
+                                                    // end to separate this chip from the next chip.
+                                                    SpannableString displayText =
+                                                            new SpannableString(
+                                                            createAddressText(
+                                                                    replacement.getEntry()).trim()
+                                                                    + " ");
+                                                    displayText.setSpan(replacement, 0,
+                                                            displayText.length() - 1,
+                                                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                                                    // Replace the old text we
+                                                    // found with with the new
+                                                    // display text, which now
+                                                    // may also contain the
+                                                    // display name of the
+                                                    // recipient.
+                                                    oldText.replace(start, end, displayText);
+                                                    replacement.setOriginalText(displayText
+                                                            .toString());
+                                                    replacements.set(i, null);
+                                                }
+                                            }
+                                            i++;
+                                        }
+                                    }
+                                });
+                            }
                         }
-                        originalRecipients.clear();
-                    }
-                });
-            }
+                    });
             return null;
         }
     }
@@ -2503,30 +2544,40 @@
                     addresses.add(createAddressText(chip.getEntry()));
                 }
             }
-            HashMap<String, RecipientEntry> entries = RecipientAlternatesAdapter
-                    .getMatchingRecipients(getContext(), addresses);
-            for (final RecipientChip temp : originalRecipients) {
-                if (RecipientEntry.isCreatedRecipient(temp.getEntry().getContactId())
-                        && getSpannable().getSpanStart(temp) != -1) {
-                    // Replace this.
-                    RecipientEntry entry = createValidatedEntry(entries.get(tokenizeAddress(
-                            temp.getEntry().getDestination()).toLowerCase()));
-                    // If we don't have a validated contact match, just use the
-                    // entry as it existed before.
-                    if (entry == null && !isPhoneQuery()) {
-                        entry = temp.getEntry();
-                    }
-                    final RecipientEntry tempEntry = entry;
-                    if (tempEntry != null) {
-                        mHandler.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                replaceChip(temp, tempEntry);
+            RecipientAlternatesAdapter.getMatchingRecipients(getContext(), addresses,
+                    ((BaseRecipientAdapter) getAdapter()).getAccount(),
+                    new RecipientMatchCallback() {
+
+                        @Override
+                        public void matchesFound(HashMap<String, RecipientEntry> entries) {
+                            for (final RecipientChip temp : originalRecipients) {
+                                if (RecipientEntry.isCreatedRecipient(temp.getEntry()
+                                        .getContactId())
+                                        && getSpannable().getSpanStart(temp) != -1) {
+                                    // Replace this.
+                                    RecipientEntry entry = createValidatedEntry(entries
+                                            .get(tokenizeAddress(temp.getEntry().getDestination())
+                                                    .toLowerCase()));
+                                    // If we don't have a validated contact
+                                    // match, just use the
+                                    // entry as it existed before.
+                                    if (entry == null && !isPhoneQuery()) {
+                                        entry = temp.getEntry();
+                                    }
+                                    final RecipientEntry tempEntry = entry;
+                                    if (tempEntry != null) {
+                                        mHandler.post(new Runnable() {
+                                            @Override
+                                            public void run() {
+                                                replaceChip(temp, tempEntry);
+                                            }
+                                        });
+                                    }
+                                }
                             }
-                        });
-                    }
-                }
-            }
+                        }
+
+                    });
             return null;
         }
     }