RecipientAlternatesAdapter.java revision 8af0d3b6f34e03c08c8e67be2190da01c59889da
184dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira/*
284dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira * Copyright (C) 2011 The Android Open Source Project
384dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira *
484dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
584dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira * you may not use this file except in compliance with the License.
684dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira * You may obtain a copy of the License at
784dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira *
884dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira *      http://www.apache.org/licenses/LICENSE-2.0
984dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira *
1084dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira * Unless required by applicable law or agreed to in writing, software
1184dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
1284dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1384dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira * See the License for the specific language governing permissions and
1484dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira * limitations under the License.
1584dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira */
1697d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
1797d7768576ff27d779d68f77e23925b69f3290bcMindy Pereirapackage com.android.ex.chips;
1897d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
1916923ee63a79fce4be3f62b08bcd1f80617c1205mindypimport android.accounts.Account;
2016923ee63a79fce4be3f62b08bcd1f80617c1205mindypimport android.content.ContentResolver;
2197d7768576ff27d779d68f77e23925b69f3290bcMindy Pereiraimport android.content.Context;
2297d7768576ff27d779d68f77e23925b69f3290bcMindy Pereiraimport android.database.Cursor;
23a5d37c8a968edf94755215617b593d3f61738a92Makoto Onukiimport android.database.MatrixCursor;
2416923ee63a79fce4be3f62b08bcd1f80617c1205mindypimport android.net.Uri;
2516923ee63a79fce4be3f62b08bcd1f80617c1205mindypimport android.provider.ContactsContract;
267a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedyimport android.provider.ContactsContract.Contacts;
27ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedyimport android.text.TextUtils;
281852931de1e24e77cb708f4ba010eaa269426657Mindy Pereiraimport android.text.util.Rfc822Token;
291852931de1e24e77cb708f4ba010eaa269426657Mindy Pereiraimport android.text.util.Rfc822Tokenizer;
3018987c44006700a2bfe614ec1e39a29ed5b23d78Mindy Pereiraimport android.util.Log;
3197d7768576ff27d779d68f77e23925b69f3290bcMindy Pereiraimport android.view.View;
3297d7768576ff27d779d68f77e23925b69f3290bcMindy Pereiraimport android.view.ViewGroup;
3397d7768576ff27d779d68f77e23925b69f3290bcMindy Pereiraimport android.widget.CursorAdapter;
3497d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
3516923ee63a79fce4be3f62b08bcd1f80617c1205mindypimport com.android.ex.chips.BaseRecipientAdapter.DirectoryListQuery;
3616923ee63a79fce4be3f62b08bcd1f80617c1205mindypimport com.android.ex.chips.BaseRecipientAdapter.DirectorySearchParams;
37b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Linimport com.android.ex.chips.DropdownChipLayouter.AdapterType;
3880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylorimport com.android.ex.chips.Queries.Query;
39a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki
4003cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereiraimport java.util.ArrayList;
411852931de1e24e77cb708f4ba010eaa269426657Mindy Pereiraimport java.util.HashMap;
42a5d37c8a968edf94755215617b593d3f61738a92Makoto Onukiimport java.util.HashSet;
4316923ee63a79fce4be3f62b08bcd1f80617c1205mindypimport java.util.List;
4494fa301de71cc110671802eba8376c275b4055a4Scott Kennedyimport java.util.Map;
4594fa301de71cc110671802eba8376c275b4055a4Scott Kennedyimport java.util.Set;
461852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira
4780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor/**
4880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor * RecipientAlternatesAdapter backs the RecipientEditTextView for managing contacts
4980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor * queried by email or by phone number.
5080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor */
5197d7768576ff27d779d68f77e23925b69f3290bcMindy Pereirapublic class RecipientAlternatesAdapter extends CursorAdapter {
528af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public static final int MAX_LOOKUPS = 50;
5397d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
5497d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    private final long mCurrentId;
5597d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
56007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira    private int mCheckedItemPosition = -1;
57007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira
585086391a478c3b1badbb86074c3cef72126c7d0fMindy Pereira    private OnCheckedItemChangedListener mCheckedItemChangedListener;
595086391a478c3b1badbb86074c3cef72126c7d0fMindy Pereira
6018987c44006700a2bfe614ec1e39a29ed5b23d78Mindy Pereira    private static final String TAG = "RecipAlternates";
6118987c44006700a2bfe614ec1e39a29ed5b23d78Mindy Pereira
6280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public static final int QUERY_TYPE_EMAIL = 0;
6380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public static final int QUERY_TYPE_PHONE = 1;
647a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy    private final Long mDirectoryId;
65b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    private DropdownChipLayouter mDropdownChipLayouter;
6680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
677a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy    private static final Map<String, String> sCorrectedPhotoUris = new HashMap<String, String>();
687a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
6916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    public interface RecipientMatchCallback {
7094fa301de71cc110671802eba8376c275b4055a4Scott Kennedy        public void matchesFound(Map<String, RecipientEntry> results);
7194fa301de71cc110671802eba8376c275b4055a4Scott Kennedy        /**
7294fa301de71cc110671802eba8376c275b4055a4Scott Kennedy         * Called with all addresses that could not be resolved to valid recipients.
7394fa301de71cc110671802eba8376c275b4055a4Scott Kennedy         */
74f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy        public void matchesNotFound(Set<String> unfoundAddresses);
7516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    }
7616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp
7776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    public static void getMatchingRecipients(Context context, BaseRecipientAdapter adapter,
7876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            ArrayList<String> inAddresses, Account account, RecipientMatchCallback callback) {
7976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        getMatchingRecipients(context, adapter, inAddresses, QUERY_TYPE_EMAIL, account, callback);
8080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
8180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
821852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira    /**
831852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira     * Get a HashMap of address to RecipientEntry that contains all contact
841852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira     * information for a contact with the provided address, if one exists. This
851852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira     * may block the UI, so run it in an async task.
861852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira     *
871852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira     * @param context Context.
887211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa     * @param inAddresses Array of addresses on which to perform the lookup.
8916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp     * @param callback RecipientMatchCallback called when a match or matches are found.
901852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira     */
9176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    public static void getMatchingRecipients(Context context, BaseRecipientAdapter adapter,
9276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            ArrayList<String> inAddresses, int addressType, Account account,
9376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            RecipientMatchCallback callback) {
9480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        Queries.Query query;
9580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        if (addressType == QUERY_TYPE_EMAIL) {
9680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            query = Queries.EMAIL;
9780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        } else {
9880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            query = Queries.PHONE;
9980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        }
10003cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira        int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.size());
10116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        HashSet<String> addresses = new HashSet<String>();
1021852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        StringBuilder bindString = new StringBuilder();
1031852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        // Create the "?" string and set up arguments.
1041852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        for (int i = 0; i < addressesSize; i++) {
10503cfe3eee5635e419ab1d70d463b2b8beac72f00Mindy Pereira            Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses.get(i).toLowerCase());
10616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            addresses.add(tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i));
1071852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            bindString.append("?");
1081852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            if (i < addressesSize - 1) {
1091852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira                bindString.append(",");
1101852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            }
1111852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        }
1121852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira
11318987c44006700a2bfe614ec1e39a29ed5b23d78Mindy Pereira        if (Log.isLoggable(TAG, Log.DEBUG)) {
11418987c44006700a2bfe614ec1e39a29ed5b23d78Mindy Pereira            Log.d(TAG, "Doing reverse lookup for " + addresses.toString());
11518987c44006700a2bfe614ec1e39a29ed5b23d78Mindy Pereira        }
11618987c44006700a2bfe614ec1e39a29ed5b23d78Mindy Pereira
11716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        String[] addressArray = new String[addresses.size()];
11816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        addresses.toArray(addressArray);
11916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        HashMap<String, RecipientEntry> recipientEntries = null;
12016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        Cursor c = null;
12116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp
12216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        try {
12316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            c = context.getContentResolver().query(
12416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    query.getContentUri(),
12516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    query.getProjection(),
12616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    query.getProjection()[Queries.Query.DESTINATION] + " IN ("
12716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                            + bindString.toString() + ")", addressArray, null);
1287a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            recipientEntries = processContactEntries(c, null /* directoryId */);
12916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            callback.matchesFound(recipientEntries);
13016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        } finally {
13116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            if (c != null) {
13216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                c.close();
13316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            }
13416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        }
1358af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
1368af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        final Set<String> matchesNotFound = new HashSet<String>();
1378af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
1388af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        getMatchingRecipientsFromDirectoryQueries(context, recipientEntries,
1398af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                addresses, account, matchesNotFound, query, callback);
1408af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
1418af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        getMatchingRecipientsFromExtensionMatcher(adapter, matchesNotFound, callback);
1428af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
1438af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
1448af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public static void getMatchingRecipientsFromDirectoryQueries(Context context,
1458af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            Map<String, RecipientEntry> recipientEntries, Set<String> addresses,
1468af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            Account account, Set<String> matchesNotFound,
1478af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            RecipientMatchCallback callback) {
1488af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        getMatchingRecipientsFromDirectoryQueries(
1498af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                context, recipientEntries, addresses, account,
1508af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                matchesNotFound, Queries.EMAIL, callback);
1518af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
1528af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
1538af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    private static void getMatchingRecipientsFromDirectoryQueries(Context context,
1548af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            Map<String, RecipientEntry> recipientEntries, Set<String> addresses,
1558af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            Account account, Set<String> matchesNotFound, Queries.Query query,
1568af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            RecipientMatchCallback callback) {
15716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        // See if any entries did not resolve; if so, we need to check other
15816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        // directories
1598af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
16016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        if (recipientEntries.size() < addresses.size()) {
16116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            final List<DirectorySearchParams> paramsList;
162539c2b12cca5e9bccd9acabc4721c876058a68d4Scott Kennedy            Cursor directoryCursor = null;
163539c2b12cca5e9bccd9acabc4721c876058a68d4Scott Kennedy            try {
164539c2b12cca5e9bccd9acabc4721c876058a68d4Scott Kennedy                directoryCursor = context.getContentResolver().query(DirectoryListQuery.URI,
165539c2b12cca5e9bccd9acabc4721c876058a68d4Scott Kennedy                        DirectoryListQuery.PROJECTION, null, null, null);
166771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                if (directoryCursor == null) {
167771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                    paramsList = null;
168771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                } else {
169771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                    paramsList = BaseRecipientAdapter.setupOtherDirectories(context,
170771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                            directoryCursor, account);
171771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                }
172539c2b12cca5e9bccd9acabc4721c876058a68d4Scott Kennedy            } finally {
173539c2b12cca5e9bccd9acabc4721c876058a68d4Scott Kennedy                if (directoryCursor != null) {
174539c2b12cca5e9bccd9acabc4721c876058a68d4Scott Kennedy                    directoryCursor.close();
175539c2b12cca5e9bccd9acabc4721c876058a68d4Scott Kennedy                }
176539c2b12cca5e9bccd9acabc4721c876058a68d4Scott Kennedy            }
17716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            // Run a directory query for each unmatched recipient.
17816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            HashSet<String> unresolvedAddresses = new HashSet<String>();
17916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            for (String address : addresses) {
18016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                if (!recipientEntries.containsKey(address)) {
18116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    unresolvedAddresses.add(address);
18216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                }
18316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            }
18494fa301de71cc110671802eba8376c275b4055a4Scott Kennedy
18594fa301de71cc110671802eba8376c275b4055a4Scott Kennedy            matchesNotFound.addAll(unresolvedAddresses);
18694fa301de71cc110671802eba8376c275b4055a4Scott Kennedy
187771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy            if (paramsList != null) {
188771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                Cursor directoryContactsCursor = null;
189771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                for (String unresolvedAddress : unresolvedAddresses) {
1907a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    Long directoryId = null;
191771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                    for (int i = 0; i < paramsList.size(); i++) {
192771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                        try {
193771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                            directoryContactsCursor = doQuery(unresolvedAddress, 1,
194771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                                    paramsList.get(i).directoryId, account,
195771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                                    context.getContentResolver(), query);
196771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                        } finally {
197771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                            if (directoryContactsCursor != null
198771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                                    && directoryContactsCursor.getCount() == 0) {
199771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                                directoryContactsCursor.close();
200771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                                directoryContactsCursor = null;
201771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                            } else {
2027a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                                directoryId = paramsList.get(i).directoryId;
203771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                                break;
204771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                            }
20518987c44006700a2bfe614ec1e39a29ed5b23d78Mindy Pereira                        }
20616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    }
207771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                    if (directoryContactsCursor != null) {
208771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                        try {
209771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                            final Map<String, RecipientEntry> entries =
2107a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                                    processContactEntries(directoryContactsCursor, directoryId);
21194fa301de71cc110671802eba8376c275b4055a4Scott Kennedy
212771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                            for (final String address : entries.keySet()) {
213771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                                matchesNotFound.remove(address);
214771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                            }
21594fa301de71cc110671802eba8376c275b4055a4Scott Kennedy
216771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                            callback.matchesFound(entries);
217771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                        } finally {
218771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                            directoryContactsCursor.close();
219771e417fe305e0aa65f0a864f2b4f4da7606b6b1Scott Kennedy                        }
22016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    }
2211852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira                }
2221852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira            }
2231852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        }
2248af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
22594fa301de71cc110671802eba8376c275b4055a4Scott Kennedy
2268af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public static void getMatchingRecipientsFromExtensionMatcher(BaseRecipientAdapter adapter,
2278af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            Set<String> matchesNotFound, RecipientMatchCallback callback) {
22876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        // If no matches found in contact provider or the directories, try the extension
22976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        // matcher.
23076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        // todo (aalbert): This whole method needs to be in the adapter?
23176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        if (adapter != null) {
23276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            final Map<String, RecipientEntry> entries =
23376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                    adapter.getMatchingRecipients(matchesNotFound);
23476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            if (entries != null && entries.size() > 0) {
23576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                callback.matchesFound(entries);
23676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                for (final String address : entries.keySet()) {
23776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                    matchesNotFound.remove(address);
23876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                }
23976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            }
24076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        }
24194fa301de71cc110671802eba8376c275b4055a4Scott Kennedy        callback.matchesNotFound(matchesNotFound);
24216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    }
24316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp
2447a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy    private static HashMap<String, RecipientEntry> processContactEntries(Cursor c,
2457a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            Long directoryId) {
24616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        HashMap<String, RecipientEntry> recipientEntries = new HashMap<String, RecipientEntry>();
24716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        if (c != null && c.moveToFirst()) {
24816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            do {
24916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                String address = c.getString(Queries.Query.DESTINATION);
250ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
251ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                final RecipientEntry newRecipientEntry = RecipientEntry.constructTopLevelEntry(
25216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        c.getString(Queries.Query.NAME),
25316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        c.getInt(Queries.Query.DISPLAY_NAME_SOURCE),
25416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        c.getString(Queries.Query.DESTINATION),
25516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        c.getInt(Queries.Query.DESTINATION_TYPE),
25616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        c.getString(Queries.Query.DESTINATION_LABEL),
25716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        c.getLong(Queries.Query.CONTACT_ID),
2587a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        directoryId,
25916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        c.getLong(Queries.Query.DATA_ID),
26016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        c.getString(Queries.Query.PHOTO_THUMBNAIL_URI),
261514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                        true,
2627a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        c.getString(Queries.Query.LOOKUP_KEY));
263ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
264ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                /*
265ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                 * In certain situations, we may have two results for one address, where one of the
266ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                 * results is just the email address, and the other has a name and photo, so we want
267ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                 * to use the better one.
268ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                 */
269ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                final RecipientEntry recipientEntry =
270ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                        getBetterRecipient(recipientEntries.get(address), newRecipientEntry);
271ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
272ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                recipientEntries.put(address, recipientEntry);
27316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                if (Log.isLoggable(TAG, Log.DEBUG)) {
27416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    Log.d(TAG, "Received reverse look up information for " + address
27516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                            + " RESULTS: "
27616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                            + " NAME : " + c.getString(Queries.Query.NAME)
27716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                            + " CONTACT ID : " + c.getLong(Queries.Query.CONTACT_ID)
27816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                            + " ADDRESS :" + c.getString(Queries.Query.DESTINATION));
27916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                }
28016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            } while (c.moveToNext());
28116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        }
2821852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira        return recipientEntries;
2831852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira    }
2841852931de1e24e77cb708f4ba010eaa269426657Mindy Pereira
285ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy    /**
286ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy     * Given two {@link RecipientEntry}s for the same email address, this will return the one that
287ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy     * contains more complete information for display purposes. Defaults to <code>entry2</code> if
288ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy     * no significant differences are found.
289ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy     */
29078f38a09c9753c0ac1838ce0bfd3a6bc1a307ff7Scott Kennedy    static RecipientEntry getBetterRecipient(final RecipientEntry entry1,
291ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy            final RecipientEntry entry2) {
292ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        // If only one has passed in, use it
293ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        if (entry2 == null) {
294ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy            return entry1;
295ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        }
296ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
297ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        if (entry1 == null) {
298ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy            return entry2;
299ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        }
300ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
301ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        // If only one has a display name, use it
302ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        if (!TextUtils.isEmpty(entry1.getDisplayName())
303ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                && TextUtils.isEmpty(entry2.getDisplayName())) {
304ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy            return entry1;
305ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        }
306ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
307ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        if (!TextUtils.isEmpty(entry2.getDisplayName())
308ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                && TextUtils.isEmpty(entry1.getDisplayName())) {
309ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy            return entry2;
310ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        }
311ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
312ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        // If only one has a display name that is not the same as the destination, use it
313ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        if (!TextUtils.equals(entry1.getDisplayName(), entry1.getDestination())
314ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                && TextUtils.equals(entry2.getDisplayName(), entry2.getDestination())) {
315ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy            return entry1;
316ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        }
317ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
318ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        if (!TextUtils.equals(entry2.getDisplayName(), entry2.getDestination())
319ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                && TextUtils.equals(entry1.getDisplayName(), entry1.getDestination())) {
320ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy            return entry2;
321ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        }
322ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
323ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        // If only one has a photo, use it
324ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        if ((entry1.getPhotoThumbnailUri() != null || entry1.getPhotoBytes() != null)
325ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                && (entry2.getPhotoThumbnailUri() == null && entry2.getPhotoBytes() == null)) {
326ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy            return entry1;
327ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        }
328ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
329ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        if ((entry2.getPhotoThumbnailUri() != null || entry2.getPhotoBytes() != null)
330ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy                && (entry1.getPhotoThumbnailUri() == null && entry1.getPhotoBytes() == null)) {
331ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy            return entry2;
332ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        }
333ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
334ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        // Go with the second option as a default
335ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy        return entry2;
336ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy    }
337ef3f76c8c0e4fc83a0ab7fbe8db9a98cc1e2a357Scott Kennedy
33816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    private static Cursor doQuery(CharSequence constraint, int limit, Long directoryId,
33916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            Account account, ContentResolver resolver, Query query) {
34016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        final Uri.Builder builder = query
34116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                .getContentFilterUri()
34216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                .buildUpon()
34316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                .appendPath(constraint.toString())
34416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
34516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        String.valueOf(limit + BaseRecipientAdapter.ALLOWANCE_FOR_DUPLICATES));
34616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        if (directoryId != null) {
34716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
34816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    String.valueOf(directoryId));
34916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        }
35016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        if (account != null) {
35116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            builder.appendQueryParameter(BaseRecipientAdapter.PRIMARY_ACCOUNT_NAME, account.name);
35216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            builder.appendQueryParameter(BaseRecipientAdapter.PRIMARY_ACCOUNT_TYPE, account.type);
35316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        }
35416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        final Cursor cursor = resolver.query(builder.build(), query.getProjection(), null, null,
35516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                null);
35616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        return cursor;
35716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    }
35816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp
3597a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy    public RecipientAlternatesAdapter(Context context, long contactId, Long directoryId,
3607a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            String lookupKey, long currentId, int queryMode, OnCheckedItemChangedListener listener,
3617a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            DropdownChipLayouter dropdownChipLayouter) {
3627a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        super(context,
3637a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                getCursorForConstruction(context, contactId, directoryId, lookupKey, queryMode), 0);
36497d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira        mCurrentId = currentId;
3657a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        mDirectoryId = directoryId;
3665086391a478c3b1badbb86074c3cef72126c7d0fMindy Pereira        mCheckedItemChangedListener = listener;
36780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
368b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        mDropdownChipLayouter = dropdownChipLayouter;
36980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
37080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
3717a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy    private static Cursor getCursorForConstruction(Context context, long contactId,
3727a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            Long directoryId, String lookupKey, int queryType) {
373a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki        final Cursor cursor;
3747a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        final String desiredMimeType;
37580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        if (queryType == QUERY_TYPE_EMAIL) {
3767a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            final Uri uri;
3777a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            final StringBuilder selection = new StringBuilder();
3787a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            selection.append(Queries.EMAIL.getProjection()[Queries.Query.CONTACT_ID]);
3797a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            selection.append(" = ?");
3807a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
3817a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            if (directoryId == null || lookupKey == null) {
3827a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                uri = Queries.EMAIL.getContentUri();
3837a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                desiredMimeType = null;
3847a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            } else {
3857a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                final Uri.Builder builder = Contacts.getLookupUri(contactId, lookupKey).buildUpon();
3867a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                builder.appendPath(Contacts.Entity.CONTENT_DIRECTORY)
3877a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
3887a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                                String.valueOf(directoryId));
3897a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                uri = builder.build();
3907a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                desiredMimeType = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
3917a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            }
392a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki            cursor = context.getContentResolver().query(
3937a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    uri,
39480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                    Queries.EMAIL.getProjection(),
3957a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    selection.toString(), new String[] {
39680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                        String.valueOf(contactId)
39780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                    }, null);
39880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        } else {
3997a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            final Uri uri;
4007a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            final StringBuilder selection = new StringBuilder();
4017a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            selection.append(Queries.PHONE.getProjection()[Queries.Query.CONTACT_ID]);
4027a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            selection.append(" = ?");
4037a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
4047a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            if (lookupKey == null) {
4057a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                uri = Queries.PHONE.getContentUri();
4067a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                desiredMimeType = null;
4077a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            } else {
4087a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                final Uri.Builder builder = Contacts.getLookupUri(contactId, lookupKey).buildUpon();
4097a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                builder.appendPath(Contacts.Entity.CONTENT_DIRECTORY)
4107a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
4117a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                                String.valueOf(directoryId));
4127a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                uri = builder.build();
4137a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                desiredMimeType = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
4147a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            }
415a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki            cursor = context.getContentResolver().query(
4167a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    uri,
41780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                    Queries.PHONE.getProjection(),
4187a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    selection.toString(), new String[] {
41980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                        String.valueOf(contactId)
42080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                    }, null);
42180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        }
4227a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
4237a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        final Cursor resultCursor = removeUndesiredDestinations(cursor, desiredMimeType, lookupKey);
4247a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        cursor.close();
4257a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
4267a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        return resultCursor;
427a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki    }
428a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki
429a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki    /**
430a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki     * @return a new cursor based on the given cursor with all duplicate destinations removed.
431a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki     *
432a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki     * It's only intended to use for the alternate list, so...
433a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki     * - This method ignores all other fields and dedupe solely on the destination.  Normally,
434a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki     * if a cursor contains multiple contacts and they have the same destination, we'd still want
435a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki     * to show both.
436a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki     * - This method creates a MatrixCursor, so all data will be kept in memory.  We wouldn't want
437a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki     * to do this if the original cursor is large, but it's okay here because the alternate list
438a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki     * won't be that big.
4397a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy     *
4407a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy     * @param desiredMimeType If this is non-<code>null</code>, only entries with this mime type
4417a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy     *            will be added to the cursor
4427a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy     * @param lookupKey The lookup key used for this contact if there isn't one in the cursor. This
4437a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy     *            should be the same one used in the query that returned the cursor
444a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki     */
445a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki    // Visible for testing
4467a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy    static Cursor removeUndesiredDestinations(final Cursor original, final String desiredMimeType,
4477a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            final String lookupKey) {
448a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki        final MatrixCursor result = new MatrixCursor(
449a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki                original.getColumnNames(), original.getCount());
450a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki        final HashSet<String> destinationsSeen = new HashSet<String>();
451a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki
4527a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        String defaultDisplayName = null;
4537a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        String defaultPhotoThumbnailUri = null;
4547a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        int defaultDisplayNameSource = 0;
4557a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
4567a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        // Find some nice defaults in case we need them
4577a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        original.moveToPosition(-1);
4587a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        while (original.moveToNext()) {
4597a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            final String mimeType = original.getString(Query.MIME_TYPE);
4607a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
4617a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            if (ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE.equals(
4627a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    mimeType)) {
4637a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                // Store this data
4647a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                defaultDisplayName = original.getString(Query.NAME);
4657a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                defaultPhotoThumbnailUri = original.getString(Query.PHOTO_THUMBNAIL_URI);
4667a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                defaultDisplayNameSource = original.getInt(Query.DISPLAY_NAME_SOURCE);
4677a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                break;
4687a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            }
4697a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        }
4707a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
471a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki        original.moveToPosition(-1);
472a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki        while (original.moveToNext()) {
4737a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            if (desiredMimeType != null) {
4747a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                final String mimeType = original.getString(Query.MIME_TYPE);
4757a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                if (!desiredMimeType.equals(mimeType)) {
4767a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    continue;
4777a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                }
4787a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            }
479a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki            final String destination = original.getString(Query.DESTINATION);
480a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki            if (destinationsSeen.contains(destination)) {
481a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki                continue;
482a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki            }
483a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki            destinationsSeen.add(destination);
484a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki
4857a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            final Object[] row = new Object[] {
486a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki                    original.getString(Query.NAME),
487a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki                    original.getString(Query.DESTINATION),
488a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki                    original.getInt(Query.DESTINATION_TYPE),
489a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki                    original.getString(Query.DESTINATION_LABEL),
490a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki                    original.getLong(Query.CONTACT_ID),
491a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki                    original.getLong(Query.DATA_ID),
492a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki                    original.getString(Query.PHOTO_THUMBNAIL_URI),
4937a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    original.getInt(Query.DISPLAY_NAME_SOURCE),
4947a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    original.getString(Query.LOOKUP_KEY),
4957a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    original.getString(Query.MIME_TYPE)
4967a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            };
4977a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
4987a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            if (row[Query.NAME] == null) {
4997a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                row[Query.NAME] = defaultDisplayName;
5007a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            }
5017a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            if (row[Query.PHOTO_THUMBNAIL_URI] == null) {
5027a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                row[Query.PHOTO_THUMBNAIL_URI] = defaultPhotoThumbnailUri;
5037a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            }
5047a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            if ((Integer) row[Query.DISPLAY_NAME_SOURCE] == 0) {
5057a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                row[Query.DISPLAY_NAME_SOURCE] = defaultDisplayNameSource;
5067a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            }
5077a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            if (row[Query.LOOKUP_KEY] == null) {
5087a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                row[Query.LOOKUP_KEY] = lookupKey;
5097a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            }
5107a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
5117a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            // Ensure we don't have two '?' like content://.../...?account_name=...?sz=...
5127a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            final String photoThumbnailUri = (String) row[Query.PHOTO_THUMBNAIL_URI];
5137a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            if (photoThumbnailUri != null) {
5147a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                if (sCorrectedPhotoUris.containsKey(photoThumbnailUri)) {
5157a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    row[Query.PHOTO_THUMBNAIL_URI] = sCorrectedPhotoUris.get(photoThumbnailUri);
5167a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                } else if (photoThumbnailUri.indexOf('?') != photoThumbnailUri.lastIndexOf('?')) {
5177a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    final String[] parts = photoThumbnailUri.split("\\?");
5187a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    final StringBuilder correctedUriBuilder = new StringBuilder();
5197a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    for (int i = 0; i < parts.length; i++) {
5207a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        if (i == 1) {
5217a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                            correctedUriBuilder.append("?"); // We only want one of these
5227a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        } else if (i > 1) {
5237a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                            correctedUriBuilder.append("&"); // And we want these elsewhere
5247a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        }
5257a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        correctedUriBuilder.append(parts[i]);
5267a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    }
5277a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
5287a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    final String correctedUri = correctedUriBuilder.toString();
5297a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    sCorrectedPhotoUris.put(photoThumbnailUri, correctedUri);
5307a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    row[Query.PHOTO_THUMBNAIL_URI] = correctedUri;
5317a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                }
5327a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            }
5337a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
5347a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            result.addRow(row);
535a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki        }
536a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki
537a5d37c8a968edf94755215617b593d3f61738a92Makoto Onuki        return result;
53897d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    }
53997d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
54097d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    @Override
54197d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    public long getItemId(int position) {
54297d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira        Cursor c = getCursor();
5433bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        if (c.moveToPosition(position)) {
54480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            c.getLong(Queries.Query.DATA_ID);
5453bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        }
5463bb52162b7e842243d4a14e73c15b20dbd1804d7Mindy Pereira        return -1;
54797d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    }
54897d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
54997d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    public RecipientEntry getRecipientEntry(int position) {
55097d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira        Cursor c = getCursor();
55197d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira        c.moveToPosition(position);
55280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        return RecipientEntry.constructTopLevelEntry(
55380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                c.getString(Queries.Query.NAME),
55400adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                c.getInt(Queries.Query.DISPLAY_NAME_SOURCE),
55580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                c.getString(Queries.Query.DESTINATION),
55680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                c.getInt(Queries.Query.DESTINATION_TYPE),
55780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                c.getString(Queries.Query.DESTINATION_LABEL),
55880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                c.getLong(Queries.Query.CONTACT_ID),
5597a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                mDirectoryId,
56080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                c.getLong(Queries.Query.DATA_ID),
561ccb8e237ec80934d1c983bb61f66b75541786ddcmindyp                c.getString(Queries.Query.PHOTO_THUMBNAIL_URI),
562514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                true,
5637a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                c.getString(Queries.Query.LOOKUP_KEY));
56497d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    }
56597d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
56697d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    @Override
56797d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    public View getView(int position, View convertView, ViewGroup parent) {
56867ad489cd290f7d2b468b4d69ce679edeac9bb7aMindy Pereira        Cursor cursor = getCursor();
56967ad489cd290f7d2b468b4d69ce679edeac9bb7aMindy Pereira        cursor.moveToPosition(position);
57097d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira        if (convertView == null) {
571b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin            convertView = mDropdownChipLayouter.newView();
572007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira        }
57380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        if (cursor.getLong(Queries.Query.DATA_ID) == mCurrentId) {
574007a76baab414c9d432d31c661668b1bd07e3f80Mindy Pereira            mCheckedItemPosition = position;
5755086391a478c3b1badbb86074c3cef72126c7d0fMindy Pereira            if (mCheckedItemChangedListener != null) {
5765086391a478c3b1badbb86074c3cef72126c7d0fMindy Pereira                mCheckedItemChangedListener.onCheckedItemChanged(mCheckedItemPosition);
5775086391a478c3b1badbb86074c3cef72126c7d0fMindy Pereira            }
57897d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira        }
57967ad489cd290f7d2b468b4d69ce679edeac9bb7aMindy Pereira        bindView(convertView, convertView.getContext(), cursor);
58097d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira        return convertView;
58197d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    }
58297d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
58397d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    @Override
58497d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    public void bindView(View view, Context context, Cursor cursor) {
58567ad489cd290f7d2b468b4d69ce679edeac9bb7aMindy Pereira        int position = cursor.getPosition();
58667ad489cd290f7d2b468b4d69ce679edeac9bb7aMindy Pereira        RecipientEntry entry = getRecipientEntry(position);
587b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
588b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        mDropdownChipLayouter.bindView(view, null, entry, position,
589b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin                AdapterType.RECIPIENT_ALTERNATES, null);
59097d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    }
59197d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
59297d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    @Override
59397d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    public View newView(Context context, Cursor cursor, ViewGroup parent) {
594b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return mDropdownChipLayouter.newView();
59597d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    }
59697d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira
5975086391a478c3b1badbb86074c3cef72126c7d0fMindy Pereira    /*package*/ static interface OnCheckedItemChangedListener {
5985086391a478c3b1badbb86074c3cef72126c7d0fMindy Pereira        public void onCheckedItemChanged(int position);
59997d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira    }
60097d7768576ff27d779d68f77e23925b69f3290bcMindy Pereira}
601