Fix directory lookups

The previous "fix" disabled the lookups.

Now we will do the lookups, with the proper lookup key and
directory id. We also do a much better job of showing directory
images.

Bug: 11693322
Bug: 12793279

Change-Id: I372711fd7b485f3183516fce2b11f9eff46a9b23
diff --git a/src/com/android/ex/chips/RecipientAlternatesAdapter.java b/src/com/android/ex/chips/RecipientAlternatesAdapter.java
index 547a76b..f6f662d 100644
--- a/src/com/android/ex/chips/RecipientAlternatesAdapter.java
+++ b/src/com/android/ex/chips/RecipientAlternatesAdapter.java
@@ -23,6 +23,7 @@
 import android.database.MatrixCursor;
 import android.net.Uri;
 import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
 import android.text.TextUtils;
 import android.text.util.Rfc822Token;
 import android.text.util.Rfc822Tokenizer;
@@ -60,9 +61,11 @@
 
     public static final int QUERY_TYPE_EMAIL = 0;
     public static final int QUERY_TYPE_PHONE = 1;
-    private Query mQuery;
+    private final Long mDirectoryId;
     private DropdownChipLayouter mDropdownChipLayouter;
 
+    private static final Map<String, String> sCorrectedPhotoUris = new HashMap<String, String>();
+
     public interface RecipientMatchCallback {
         public void matchesFound(Map<String, RecipientEntry> results);
         /**
@@ -123,7 +126,7 @@
                     query.getProjection(),
                     query.getProjection()[Queries.Query.DESTINATION] + " IN ("
                             + bindString.toString() + ")", addressArray, null);
-            recipientEntries = processContactEntries(c);
+            recipientEntries = processContactEntries(c, null /* directoryId */);
             callback.matchesFound(recipientEntries);
         } finally {
             if (c != null) {
@@ -163,6 +166,7 @@
             if (paramsList != null) {
                 Cursor directoryContactsCursor = null;
                 for (String unresolvedAddress : unresolvedAddresses) {
+                    Long directoryId = null;
                     for (int i = 0; i < paramsList.size(); i++) {
                         try {
                             directoryContactsCursor = doQuery(unresolvedAddress, 1,
@@ -174,6 +178,7 @@
                                 directoryContactsCursor.close();
                                 directoryContactsCursor = null;
                             } else {
+                                directoryId = paramsList.get(i).directoryId;
                                 break;
                             }
                         }
@@ -181,7 +186,7 @@
                     if (directoryContactsCursor != null) {
                         try {
                             final Map<String, RecipientEntry> entries =
-                                    processContactEntries(directoryContactsCursor);
+                                    processContactEntries(directoryContactsCursor, directoryId);
 
                             for (final String address : entries.keySet()) {
                                 matchesNotFound.remove(address);
@@ -212,7 +217,8 @@
         callback.matchesNotFound(matchesNotFound);
     }
 
-    private static HashMap<String, RecipientEntry> processContactEntries(Cursor c) {
+    private static HashMap<String, RecipientEntry> processContactEntries(Cursor c,
+            Long directoryId) {
         HashMap<String, RecipientEntry> recipientEntries = new HashMap<String, RecipientEntry>();
         if (c != null && c.moveToFirst()) {
             do {
@@ -225,10 +231,11 @@
                         c.getInt(Queries.Query.DESTINATION_TYPE),
                         c.getString(Queries.Query.DESTINATION_LABEL),
                         c.getLong(Queries.Query.CONTACT_ID),
+                        directoryId,
                         c.getLong(Queries.Query.DATA_ID),
                         c.getString(Queries.Query.PHOTO_THUMBNAIL_URI),
                         true,
-                        false /* isGalContact TODO(skennedy) We should look these up eventually */);
+                        c.getString(Queries.Query.LOOKUP_KEY));
 
                 /*
                  * In certain situations, we may have two results for one address, where one of the
@@ -325,43 +332,74 @@
         return cursor;
     }
 
-    public RecipientAlternatesAdapter(Context context, long contactId, long currentId,
-            int queryMode, OnCheckedItemChangedListener listener,
-        DropdownChipLayouter dropdownChipLayouter) {
-        super(context, getCursorForConstruction(context, contactId, queryMode), 0);
+    public RecipientAlternatesAdapter(Context context, long contactId, Long directoryId,
+            String lookupKey, long currentId, int queryMode, OnCheckedItemChangedListener listener,
+            DropdownChipLayouter dropdownChipLayouter) {
+        super(context,
+                getCursorForConstruction(context, contactId, directoryId, lookupKey, queryMode), 0);
         mCurrentId = currentId;
+        mDirectoryId = directoryId;
         mCheckedItemChangedListener = listener;
 
-        if (queryMode == QUERY_TYPE_EMAIL) {
-            mQuery = Queries.EMAIL;
-        } else if (queryMode == QUERY_TYPE_PHONE) {
-            mQuery = Queries.PHONE;
-        } else {
-            mQuery = Queries.EMAIL;
-            Log.e(TAG, "Unsupported query type: " + queryMode);
-        }
-
         mDropdownChipLayouter = dropdownChipLayouter;
     }
 
-    private static Cursor getCursorForConstruction(Context context, long contactId, int queryType) {
+    private static Cursor getCursorForConstruction(Context context, long contactId,
+            Long directoryId, String lookupKey, int queryType) {
         final Cursor cursor;
+        final String desiredMimeType;
         if (queryType == QUERY_TYPE_EMAIL) {
+            final Uri uri;
+            final StringBuilder selection = new StringBuilder();
+            selection.append(Queries.EMAIL.getProjection()[Queries.Query.CONTACT_ID]);
+            selection.append(" = ?");
+
+            if (directoryId == null || lookupKey == null) {
+                uri = Queries.EMAIL.getContentUri();
+                desiredMimeType = null;
+            } else {
+                final Uri.Builder builder = Contacts.getLookupUri(contactId, lookupKey).buildUpon();
+                builder.appendPath(Contacts.Entity.CONTENT_DIRECTORY)
+                        .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+                                String.valueOf(directoryId));
+                uri = builder.build();
+                desiredMimeType = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
+            }
             cursor = context.getContentResolver().query(
-                    Queries.EMAIL.getContentUri(),
+                    uri,
                     Queries.EMAIL.getProjection(),
-                    Queries.EMAIL.getProjection()[Queries.Query.CONTACT_ID] + " =?", new String[] {
+                    selection.toString(), new String[] {
                         String.valueOf(contactId)
                     }, null);
         } else {
+            final Uri uri;
+            final StringBuilder selection = new StringBuilder();
+            selection.append(Queries.PHONE.getProjection()[Queries.Query.CONTACT_ID]);
+            selection.append(" = ?");
+
+            if (lookupKey == null) {
+                uri = Queries.PHONE.getContentUri();
+                desiredMimeType = null;
+            } else {
+                final Uri.Builder builder = Contacts.getLookupUri(contactId, lookupKey).buildUpon();
+                builder.appendPath(Contacts.Entity.CONTENT_DIRECTORY)
+                        .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+                                String.valueOf(directoryId));
+                uri = builder.build();
+                desiredMimeType = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
+            }
             cursor = context.getContentResolver().query(
-                    Queries.PHONE.getContentUri(),
+                    uri,
                     Queries.PHONE.getProjection(),
-                    Queries.PHONE.getProjection()[Queries.Query.CONTACT_ID] + " =?", new String[] {
+                    selection.toString(), new String[] {
                         String.valueOf(contactId)
                     }, null);
         }
-        return removeDuplicateDestinations(cursor);
+
+        final Cursor resultCursor = removeUndesiredDestinations(cursor, desiredMimeType, lookupKey);
+        cursor.close();
+
+        return resultCursor;
     }
 
     /**
@@ -374,22 +412,53 @@
      * - This method creates a MatrixCursor, so all data will be kept in memory.  We wouldn't want
      * to do this if the original cursor is large, but it's okay here because the alternate list
      * won't be that big.
+     *
+     * @param desiredMimeType If this is non-<code>null</code>, only entries with this mime type
+     *            will be added to the cursor
+     * @param lookupKey The lookup key used for this contact if there isn't one in the cursor. This
+     *            should be the same one used in the query that returned the cursor
      */
     // Visible for testing
-    /* package */ static Cursor removeDuplicateDestinations(Cursor original) {
+    static Cursor removeUndesiredDestinations(final Cursor original, final String desiredMimeType,
+            final String lookupKey) {
         final MatrixCursor result = new MatrixCursor(
                 original.getColumnNames(), original.getCount());
         final HashSet<String> destinationsSeen = new HashSet<String>();
 
+        String defaultDisplayName = null;
+        String defaultPhotoThumbnailUri = null;
+        int defaultDisplayNameSource = 0;
+
+        // Find some nice defaults in case we need them
         original.moveToPosition(-1);
         while (original.moveToNext()) {
+            final String mimeType = original.getString(Query.MIME_TYPE);
+
+            if (ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE.equals(
+                    mimeType)) {
+                // Store this data
+                defaultDisplayName = original.getString(Query.NAME);
+                defaultPhotoThumbnailUri = original.getString(Query.PHOTO_THUMBNAIL_URI);
+                defaultDisplayNameSource = original.getInt(Query.DISPLAY_NAME_SOURCE);
+                break;
+            }
+        }
+
+        original.moveToPosition(-1);
+        while (original.moveToNext()) {
+            if (desiredMimeType != null) {
+                final String mimeType = original.getString(Query.MIME_TYPE);
+                if (!desiredMimeType.equals(mimeType)) {
+                    continue;
+                }
+            }
             final String destination = original.getString(Query.DESTINATION);
             if (destinationsSeen.contains(destination)) {
                 continue;
             }
             destinationsSeen.add(destination);
 
-            result.addRow(new Object[] {
+            final Object[] row = new Object[] {
                     original.getString(Query.NAME),
                     original.getString(Query.DESTINATION),
                     original.getInt(Query.DESTINATION_TYPE),
@@ -397,8 +466,48 @@
                     original.getLong(Query.CONTACT_ID),
                     original.getLong(Query.DATA_ID),
                     original.getString(Query.PHOTO_THUMBNAIL_URI),
-                    original.getInt(Query.DISPLAY_NAME_SOURCE)
-                    });
+                    original.getInt(Query.DISPLAY_NAME_SOURCE),
+                    original.getString(Query.LOOKUP_KEY),
+                    original.getString(Query.MIME_TYPE)
+            };
+
+            if (row[Query.NAME] == null) {
+                row[Query.NAME] = defaultDisplayName;
+            }
+            if (row[Query.PHOTO_THUMBNAIL_URI] == null) {
+                row[Query.PHOTO_THUMBNAIL_URI] = defaultPhotoThumbnailUri;
+            }
+            if ((Integer) row[Query.DISPLAY_NAME_SOURCE] == 0) {
+                row[Query.DISPLAY_NAME_SOURCE] = defaultDisplayNameSource;
+            }
+            if (row[Query.LOOKUP_KEY] == null) {
+                row[Query.LOOKUP_KEY] = lookupKey;
+            }
+
+            // Ensure we don't have two '?' like content://.../...?account_name=...?sz=...
+            final String photoThumbnailUri = (String) row[Query.PHOTO_THUMBNAIL_URI];
+            if (photoThumbnailUri != null) {
+                if (sCorrectedPhotoUris.containsKey(photoThumbnailUri)) {
+                    row[Query.PHOTO_THUMBNAIL_URI] = sCorrectedPhotoUris.get(photoThumbnailUri);
+                } else if (photoThumbnailUri.indexOf('?') != photoThumbnailUri.lastIndexOf('?')) {
+                    final String[] parts = photoThumbnailUri.split("\\?");
+                    final StringBuilder correctedUriBuilder = new StringBuilder();
+                    for (int i = 0; i < parts.length; i++) {
+                        if (i == 1) {
+                            correctedUriBuilder.append("?"); // We only want one of these
+                        } else if (i > 1) {
+                            correctedUriBuilder.append("&"); // And we want these elsewhere
+                        }
+                        correctedUriBuilder.append(parts[i]);
+                    }
+
+                    final String correctedUri = correctedUriBuilder.toString();
+                    sCorrectedPhotoUris.put(photoThumbnailUri, correctedUri);
+                    row[Query.PHOTO_THUMBNAIL_URI] = correctedUri;
+                }
+            }
+
+            result.addRow(row);
         }
 
         return result;
@@ -423,10 +532,11 @@
                 c.getInt(Queries.Query.DESTINATION_TYPE),
                 c.getString(Queries.Query.DESTINATION_LABEL),
                 c.getLong(Queries.Query.CONTACT_ID),
+                mDirectoryId,
                 c.getLong(Queries.Query.DATA_ID),
                 c.getString(Queries.Query.PHOTO_THUMBNAIL_URI),
                 true,
-                false /* isGalContact TODO(skennedy) We should look these up eventually */);
+                c.getString(Queries.Query.LOOKUP_KEY));
     }
 
     @Override