BaseRecipientAdapter.java revision 0437e862005912a1e5bf054769f23ed9cc69f811
16ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa/*
26ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Copyright (C) 2011 The Android Open Source Project
36ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *
46ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License");
56ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * you may not use this file except in compliance with the License.
66ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * You may obtain a copy of the License at
76ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *
86ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *      http://www.apache.org/licenses/LICENSE-2.0
96ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *
106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS,
126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * See the License for the specific language governing permissions and
146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * limitations under the License.
156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa */
166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawapackage com.android.ex.chips;
186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.accounts.Account;
206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.ContentResolver;
216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.Context;
226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.pm.PackageManager;
236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.pm.PackageManager.NameNotFoundException;
246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.res.Resources;
256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.database.Cursor;
266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.net.Uri;
276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.os.Handler;
284bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawaimport android.os.Message;
296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract;
306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract.Directory;
316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.text.TextUtils;
326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.text.util.Rfc822Token;
336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.util.Log;
346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.view.View;
356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.view.ViewGroup;
366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.AutoCompleteTextView;
376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.BaseAdapter;
386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.Filter;
396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.Filterable;
40b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
41b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Linimport com.android.ex.chips.DropdownChipLayouter.AdapterType;
426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.ArrayList;
44fa7b0fb73f80ceafdf3ff3260b345b61d2766f93Scott Kennedyimport java.util.Collections;
4574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport java.util.HashSet;
46cf9337aa4b2d844f4941d75832f52266c4fc3fa7Daisuke Miyakawaimport java.util.LinkedHashMap;
476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.List;
486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.Map;
4974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport java.util.Set;
506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa/**
526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Adapter for showing a recipient list.
536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa */
548af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinpublic class BaseRecipientAdapter extends BaseAdapter implements Filterable, AccountSpecifier,
558af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        PhotoManager.PhotoManagerCallback {
566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final String TAG = "BaseRecipientAdapter";
578383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
586b616f1d06b092825da345078ce4a899b829290dDaisuke Miyakawa    private static final boolean DEBUG = false;
596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * The preferred number of results to be retrieved. This number may be
626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * exceeded if there are several directories configured, because we will use
636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * the same limit for all directories.
646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final int DEFAULT_PREFERRED_MAX_RESULT_COUNT = 10;
666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * The number of extra entries requested to allow for duplicates. Duplicates
696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * are removed from the overall result.
706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
7116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final int ALLOWANCE_FOR_DUPLICATES = 5;
726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
737537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    // This is ContactsContract.PRIMARY_ACCOUNT_NAME. Available from ICS as hidden
7416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
757537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    // This is ContactsContract.PRIMARY_ACCOUNT_TYPE. Available from ICS as hidden
7616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account";
777537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa
784bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /**
794bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * The "Waiting for more contacts" message will be displayed if search is not complete
804bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * within this many milliseconds.
814bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     */
824bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private static final int MESSAGE_SEARCH_PENDING_DELAY = 1000;
834bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /** Used to prepare "Waiting for more contacts" message. */
844bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private static final int MESSAGE_SEARCH_PENDING = 1;
854bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
86d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public static final int QUERY_TYPE_EMAIL = 0;
87d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public static final int QUERY_TYPE_PHONE = 1;
88d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
891db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein    private final Queries.Query mQueryMode;
9080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    private final int mQueryType;
9180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Model object for a {@link Directory} row.
946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public final static class DirectorySearchParams {
966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public long directoryId;
976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String directoryType;
986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String displayName;
996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String accountName;
1006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String accountType;
1016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence constraint;
1026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public DirectoryFilter filter;
1036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
10516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    protected static class DirectoryListQuery {
1066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final Uri URI =
1086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories");
1096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final String[] PROJECTION = {
1106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory._ID,              // 0
1116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_NAME,     // 1
1126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_TYPE,     // 2
1136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.DISPLAY_NAME,     // 3
1146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.PACKAGE_NAME,     // 4
1156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.TYPE_RESOURCE_ID, // 5
1166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        };
1176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ID = 0;
1196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_NAME = 1;
1206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_TYPE = 2;
1216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int DISPLAY_NAME = 3;
1226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int PACKAGE_NAME = 4;
1236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int TYPE_RESOURCE_ID = 5;
1246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Used to temporarily hold results in Cursor objects. */
12776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected static class TemporaryEntry {
1288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String displayName;
1298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String destination;
1307211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa        public final int destinationType;
1317211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa        public final String destinationLabel;
1328383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final long contactId;
1337a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        public final Long directoryId;
1348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final long dataId;
1358383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String thumbnailUriString;
13600adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki        public final int displayNameSource;
1377a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        public final String lookupKey;
1388383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
13976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        public TemporaryEntry(
14076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String displayName,
14176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String destination,
14276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                int destinationType,
14376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String destinationLabel,
14476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                long contactId,
1457a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                Long directoryId,
14676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                long dataId,
14776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String thumbnailUriString,
148514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                int displayNameSource,
1497a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                String lookupKey) {
15076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.displayName = displayName;
15176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destination = destination;
15276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destinationType = destinationType;
15376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destinationLabel = destinationLabel;
15476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.contactId = contactId;
1557a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.directoryId = directoryId;
15676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.dataId = dataId;
15776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.thumbnailUriString = thumbnailUriString;
15876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.displayNameSource = displayNameSource;
1597a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.lookupKey = lookupKey;
16076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        }
16176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
1627a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        public TemporaryEntry(Cursor cursor, Long directoryId) {
16380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.displayName = cursor.getString(Queries.Query.NAME);
16480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destination = cursor.getString(Queries.Query.DESTINATION);
16580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destinationType = cursor.getInt(Queries.Query.DESTINATION_TYPE);
16680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destinationLabel = cursor.getString(Queries.Query.DESTINATION_LABEL);
16780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.contactId = cursor.getLong(Queries.Query.CONTACT_ID);
1687a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.directoryId = directoryId;
16980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.dataId = cursor.getLong(Queries.Query.DATA_ID);
17080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.thumbnailUriString = cursor.getString(Queries.Query.PHOTO_THUMBNAIL_URI);
17100adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki            this.displayNameSource = cursor.getInt(Queries.Query.DISPLAY_NAME_SOURCE);
1727a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.lookupKey = cursor.getString(Queries.Query.LOOKUP_KEY);
1738383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
1748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
1758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
1768383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /**
1778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Used to pass results from {@link DefaultFilter#performFiltering(CharSequence)} to
1788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link DefaultFilter#publishResults(CharSequence, android.widget.Filter.FilterResults)}
1798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     */
1808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private static class DefaultFilterResult {
1818383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<RecipientEntry> entries;
1828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final LinkedHashMap<Long, List<RecipientEntry>> entryMap;
1838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<RecipientEntry> nonAggregatedEntries;
1848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final Set<String> existingDestinations;
1858383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<DirectorySearchParams> paramsList;
1868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
1878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public DefaultFilterResult(List<RecipientEntry> entries,
1888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                LinkedHashMap<Long, List<RecipientEntry>> entryMap,
1898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                List<RecipientEntry> nonAggregatedEntries,
1908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Set<String> existingDestinations,
1918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                List<DirectorySearchParams> paramsList) {
1928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.entries = entries;
1938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.entryMap = entryMap;
1948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.nonAggregatedEntries = nonAggregatedEntries;
1958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.existingDestinations = existingDestinations;
1968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.paramsList = paramsList;
1978383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
1988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
1998383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
2016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter used for loading two data sets: email rows from the local
2026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * contact provider and the list of {@link Directory}'s.
2036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
2046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final class DefaultFilter extends Filter {
2056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
2076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
2088383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
2098383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "start filtering. constraint: " + constraint + ", thread:"
2108383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + Thread.currentThread());
2118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
2128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
21374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
2148383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Cursor defaultDirectoryCursor = null;
2158383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Cursor directoryCursor = null;
2168383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2178383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (TextUtils.isEmpty(constraint)) {
21837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                clearTempEntries();
2198383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // Return empty results.
2208383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                return results;
2216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
2226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2238383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            try {
2247a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                defaultDirectoryCursor = doQuery(constraint, mPreferredMaxResultCount,
2257a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        null /* directoryId */);
22680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
2278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultDirectoryCursor == null) {
2288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (DEBUG) {
2298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        Log.w(TAG, "null cursor returned for default Email filter query.");
2308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
2318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                } else {
2328383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // These variables will become mEntries, mEntryMap, mNonAggregatedEntries, and
2338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // mExistingDestinations. Here we shouldn't use those member variables directly
2348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // since this method is run outside the UI thread.
2358383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final LinkedHashMap<Long, List<RecipientEntry>> entryMap =
2368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            new LinkedHashMap<Long, List<RecipientEntry>>();
2378383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final List<RecipientEntry> nonAggregatedEntries =
2388383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            new ArrayList<RecipientEntry>();
2398383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final Set<String> existingDestinations = new HashSet<String>();
2408383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2418383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    while (defaultDirectoryCursor.moveToNext()) {
2428383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        // Note: At this point each entry doesn't contain any photo
2438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        // (thus getPhotoBytes() returns null).
244514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                        putOneEntry(new TemporaryEntry(defaultDirectoryCursor,
2457a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                                null /* directoryId */),
2468383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                true, entryMap, nonAggregatedEntries, existingDestinations);
2478383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
2488383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2498383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We'll copy this result to mEntry in publicResults() (run in the UX thread).
250f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                    final List<RecipientEntry> entries = constructEntryList(
251f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                            entryMap, nonAggregatedEntries);
2528383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2538af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                    final List<DirectorySearchParams> paramsList =
2548af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                            searchOtherDirectories(existingDestinations);
2556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2568383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.values = new DefaultFilterResult(
2578383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            entries, entryMap, nonAggregatedEntries,
2588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            existingDestinations, paramsList);
2592a87fbfbbee4279c399980cba42de1d361a8ad56Scott Kennedy                    results.count = entries.size();
2608383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
2618383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            } finally {
2628383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultDirectoryCursor != null) {
2638383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    defaultDirectoryCursor.close();
2648383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
2658383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (directoryCursor != null) {
2668383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    directoryCursor.close();
2678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
26874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
2696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
2706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
2736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
2748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mCurrentConstraint = constraint;
2758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
27637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook            clearTempEntries();
27737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
2786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (results.values != null) {
2798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                DefaultFilterResult defaultFilterResult = (DefaultFilterResult) results.values;
2808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mEntryMap = defaultFilterResult.entryMap;
2818383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mNonAggregatedEntries = defaultFilterResult.nonAggregatedEntries;
2828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mExistingDestinations = defaultFilterResult.existingDestinations;
2838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
28431c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao                cacheCurrentEntriesIfNeeded(defaultFilterResult.entries.size(),
28531c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao                        defaultFilterResult.paramsList == null ? 0 :
28631c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao                                defaultFilterResult.paramsList.size());
28737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
2888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                updateEntries(defaultFilterResult.entries);
2898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // We need to search other remote directories, doing other Filter requests.
2918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultFilterResult.paramsList != null) {
2928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final int limit = mPreferredMaxResultCount -
2938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            defaultFilterResult.existingDestinations.size();
2948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    startSearchOtherDirectories(constraint, defaultFilterResult.paramsList, limit);
2958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
296fa7b0fb73f80ceafdf3ff3260b345b61d2766f93Scott Kennedy            } else {
297fa7b0fb73f80ceafdf3ff3260b345b61d2766f93Scott Kennedy                updateEntries(Collections.<RecipientEntry>emptyList());
2986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
2996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
3026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence convertResultToString(Object resultValue) {
303f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            final RecipientEntry entry = (RecipientEntry)resultValue;
3046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String displayName = entry.getDisplayName();
3056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String emailAddress = entry.getDestination();
3066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, emailAddress)) {
3076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                 return emailAddress;
3086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
3096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                return new Rfc822Token(displayName, emailAddress, null).toString();
3106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3148af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected List<DirectorySearchParams> searchOtherDirectories(Set<String> existingDestinations) {
3158af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        // After having local results, check the size of results. If the results are
3168af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        // not enough, we search remote directories, which will take longer time.
3178af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        final int limit = mPreferredMaxResultCount - existingDestinations.size();
3188af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        if (limit > 0) {
3198af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            if (DEBUG) {
3208af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                Log.d(TAG, "More entries should be needed (current: "
3218af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                        + existingDestinations.size()
3228af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                        + ", remaining limit: " + limit + ") ");
3238af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            }
3240437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein            Cursor directoryCursor = null;
3250437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein            try {
3260437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                directoryCursor = mContentResolver.query(
3270437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                        DirectoryListQuery.URI, DirectoryListQuery.PROJECTION,
3280437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                        null, null, null);
3290437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                return setupOtherDirectories(mContext, directoryCursor, mAccount);
3300437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein            } finally {
3310437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                if (directoryCursor != null) {
3320437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                    directoryCursor.close();
3330437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                }
3340437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein            }
3358af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        } else {
3368af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            // We don't need to search other directories.
3378af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            return null;
3388af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        }
3398af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
3408af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
3416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
3426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter that performs search in a particular directory.
3436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
34476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected class DirectoryFilter extends Filter {
34574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        private final DirectorySearchParams mParams;
3466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        private int mLimit;
3476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
34874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public DirectoryFilter(DirectorySearchParams params) {
3498383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mParams = params;
3506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized void setLimit(int limit) {
3536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            this.mLimit = limit;
3546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized int getLimit() {
3576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return this.mLimit;
3586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
3616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
3628383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
3638383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "DirectoryFilter#performFiltering. directoryId: " + mParams.directoryId
3648383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + ", constraint: " + constraint + ", thread: " + Thread.currentThread());
3658383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
36674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
3678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            results.values = null;
3688383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            results.count = 0;
3698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (!TextUtils.isEmpty(constraint)) {
3718383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                final ArrayList<TemporaryEntry> tempEntries = new ArrayList<TemporaryEntry>();
3728383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3738383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Cursor cursor = null;
3748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                try {
3758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We don't want to pass this Cursor object to UI thread (b/5017608).
3768383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // Assuming the result should contain fairly small results (at most ~10),
3778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We just copy everything to local structure.
3788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    cursor = doQuery(constraint, getLimit(), mParams.directoryId);
37980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
3808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (cursor != null) {
3818383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        while (cursor.moveToNext()) {
3827a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                            tempEntries.add(new TemporaryEntry(cursor, mParams.directoryId));
3838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        }
3848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
3858383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                } finally {
3868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (cursor != null) {
3878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        cursor.close();
3888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
38974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
3908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (!tempEntries.isEmpty()) {
3918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.values = tempEntries;
3922a87fbfbbee4279c399980cba42de1d361a8ad56Scott Kennedy                    results.count = tempEntries.size();
3938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
3948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
3958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
3978383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.v(TAG, "finished loading directory \"" + mParams.displayName + "\"" +
3988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        " with query " + constraint);
3996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
4006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
4026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
4056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
4068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
4078383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "DirectoryFilter#publishResult. constraint: " + constraint
4088383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + ", mCurrentConstraint: " + mCurrentConstraint);
4098383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4108383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mDelayedMessageHandler.removeDelayedLoadMessage();
4118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // Check if the received result matches the current constraint
4128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // If not - the user must have continued typing after the request was issued, which
4138383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // means several member variables (like mRemainingDirectoryLoad) are already
4148383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // overwritten so shouldn't be touched here anymore.
4158383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (TextUtils.equals(constraint, mCurrentConstraint)) {
4168383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (results.count > 0) {
417dfd7e0774f9a4f47c127b21e5468430374340f66Andy Huang                    @SuppressWarnings("unchecked")
4188383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final ArrayList<TemporaryEntry> tempEntries =
4198383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            (ArrayList<TemporaryEntry>) results.values;
4208383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4218383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    for (TemporaryEntry tempEntry : tempEntries) {
4228af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                        putOneEntry(tempEntry, mParams.directoryId == Directory.DEFAULT);
4238383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
4248383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
4258383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // If there are remaining directories, set up delayed message again.
4278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mRemainingDirectoryCount--;
4288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (mRemainingDirectoryCount > 0) {
4298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (DEBUG) {
4308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        Log.d(TAG, "Resend delayed load message. Current mRemainingDirectoryLoad: "
4318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                + mRemainingDirectoryCount);
4328383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
4338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    mDelayedMessageHandler.sendDelayedLoadMessage();
4348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
43537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
43637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // If this directory result has some items, or there are no more directories that
43737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // we are waiting for, clear the temp results
43837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                if (results.count > 0 || mRemainingDirectoryCount == 0) {
43937726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                    // Clear the temp entries
44037726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                    clearTempEntries();
44137726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                }
4428383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4448383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // Show the list again without "waiting" message.
4458af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            updateEntries(constructEntryList());
4466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
4486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
44974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final Context mContext;
4506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final ContentResolver mContentResolver;
4516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private Account mAccount;
4528af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected final int mPreferredMaxResultCount;
453b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    private DropdownChipLayouter mDropdownChipLayouter;
4546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
4568383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mEntries} is responsible for showing every result for this Adapter. To
4578383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * construct it, we use {@link #mEntryMap}, {@link #mNonAggregatedEntries}, and
4588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mExistingDestinations}.
4598383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     *
4608383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * First, each destination (an email address or a phone number) with a valid contactId is
4618383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * inserted into {@link #mEntryMap} and grouped by the contactId. Destinations without valid
4628383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * contactId (possible if they aren't in local storage) are stored in
4638383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mNonAggregatedEntries}.
46474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Duplicates are removed using {@link #mExistingDestinations}.
46574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     *
4668383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * After having all results from Cursor objects, all destinations in mEntryMap are copied to
4678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mEntries}. If the number of destinations is not enough (i.e. less than
4688383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mPreferredMaxResultCount}), destinations in mNonAggregatedEntries are also used.
4698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     *
4708383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * These variables are only used in UI thread, thus should not be touched in
4718383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * performFiltering() methods.
4726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
4738383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private LinkedHashMap<Long, List<RecipientEntry>> mEntryMap;
4748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> mNonAggregatedEntries;
4758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private Set<String> mExistingDestinations;
4768383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Note: use {@link #updateEntries(List)} to update this variable. */
4778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> mEntries;
47837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    private List<RecipientEntry> mTempEntries;
4796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4804bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /** The number of directories this adapter is waiting for results. */
4814bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private int mRemainingDirectoryCount;
4824bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
48374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /**
4848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Used to ignore asynchronous queries with a different constraint, which may happen when
48574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * users type characters quickly.
48674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     */
4878af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected CharSequence mCurrentConstraint;
48874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
4898af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
4908af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Performs all photo querying as well as caching for repeated lookups.
4918af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
4928af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    private PhotoManager mPhotoManager;
4936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
494d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
4954bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * Handler specific for maintaining "Waiting for more contacts" message, which will be shown
4964bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * when:
4974bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * - there are directories to be searched
4984bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * - results from directories are slow to come
4994bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     */
5004bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private final class DelayedMessageHandler extends Handler {
5014bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        @Override
5024bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void handleMessage(Message msg) {
5034bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            if (mRemainingDirectoryCount > 0) {
5048af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                updateEntries(constructEntryList());
5054bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            }
5064bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5074bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5084bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void sendDelayedLoadMessage() {
5094bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            sendMessageDelayed(obtainMessage(MESSAGE_SEARCH_PENDING, 0, 0, null),
5104bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa                    MESSAGE_SEARCH_PENDING_DELAY);
5114bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5124bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5134bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void removeDelayedLoadMessage() {
5144bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            removeMessages(MESSAGE_SEARCH_PENDING);
5154bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5164bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    }
5174bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5184bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private final DelayedMessageHandler mDelayedMessageHandler = new DelayedMessageHandler();
5194bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5208c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    private EntriesUpdatedObserver mEntriesUpdatedObserver;
5218c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
5224bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /**
523d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Constructor for email queries.
524d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
5256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public BaseRecipientAdapter(Context context) {
52680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT, QUERY_TYPE_EMAIL);
5276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
5298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    public BaseRecipientAdapter(Context context, int preferredMaxResultCount) {
53080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, preferredMaxResultCount, QUERY_TYPE_EMAIL);
53180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
53280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
53380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(int queryMode, Context context) {
53480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT, queryMode);
53580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
53680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
53780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(int queryMode, Context context, int preferredMaxResultCount) {
53880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, preferredMaxResultCount, queryMode);
53980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
54080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
54180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(Context context, int preferredMaxResultCount, int queryMode) {
5426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContext = context;
5436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContentResolver = context.getContentResolver();
5446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mPreferredMaxResultCount = preferredMaxResultCount;
5458af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        mPhotoManager = new DefaultPhotoManager(mContentResolver);
54680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        mQueryType = queryMode;
54780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
54880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        if (queryMode == QUERY_TYPE_EMAIL) {
5491db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein            mQueryMode = Queries.EMAIL;
55080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        } else if (queryMode == QUERY_TYPE_PHONE) {
5511db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein            mQueryMode = Queries.PHONE;
55280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        } else {
5531db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein            mQueryMode = Queries.EMAIL;
55480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            Log.e(TAG, "Unsupported query type: " + queryMode);
55580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        }
55680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
55780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
55876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    public Context getContext() {
55976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        return mContext;
56076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    }
56176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
56280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public int getQueryType() {
56380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        return mQueryType;
5646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
566b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    public void setDropdownChipLayouter(DropdownChipLayouter dropdownChipLayouter) {
567b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        mDropdownChipLayouter = dropdownChipLayouter;
5681db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein        mDropdownChipLayouter.setQuery(mQueryMode);
569b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
570b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
571b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    public DropdownChipLayouter getDropdownChipLayouter() {
572b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return mDropdownChipLayouter;
573b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
574b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
5756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
5768af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Enables overriding the default photo manager that is used.
5778af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
5788af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public void setPhotoManager(PhotoManager photoManager) {
5798af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        mPhotoManager = photoManager;
5808af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
5818af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
5828af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public PhotoManager getPhotoManager() {
5838af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        return mPhotoManager;
5848af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
5858af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
5868af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
5878af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * If true, forces using the {@link com.android.ex.chips.SingleRecipientArrayAdapter}
5888af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * instead of {@link com.android.ex.chips.RecipientAlternatesAdapter} when
5898af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * clicking on a chip. Default implementation returns {@code false}.
5908af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
5918af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public boolean forceShowAddress() {
5928af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        return false;
5938af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
5948af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
5958af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
5968af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Used to replace email addresses with chips. Default behavior
5978af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * queries the ContactsProvider for contact information about the contact.
5988af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Derived classes should override this method if they wish to use a
5998af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * new data source.
6008af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * @param inAddresses addresses to query
6018af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * @param callback callback to return results in case of success or failure
6028af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
6038af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public void getMatchingRecipients(ArrayList<String> inAddresses,
6048af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            RecipientAlternatesAdapter.RecipientMatchCallback callback) {
6058af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        RecipientAlternatesAdapter.getMatchingRecipients(
6068af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                getContext(), this, inAddresses, getAccount(), callback);
6078af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
6088af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
6098af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
6106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Set the account when known. Causes the search to prioritize contacts from that account.
6116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
612dfd7e0774f9a4f47c127b21e5468430374340f66Andy Huang    @Override
6136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public void setAccount(Account account) {
6146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mAccount = account;
6156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /** Will be called from {@link AutoCompleteTextView} to prepare auto-complete list. */
6186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
6196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public Filter getFilter() {
6206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return new DefaultFilter();
6216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
62376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    /**
6248af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * An extension to {@link RecipientAlternatesAdapter#getMatchingRecipients} that allows
62576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * additional sources of contacts to be considered as matching recipients.
62676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * @param addresses A set of addresses to be matched
62776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * @return A list of matches or null if none found
62876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     */
62976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    public Map<String, RecipientEntry> getMatchingRecipients(Set<String> addresses) {
63076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        return null;
63176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    }
63276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
63316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    public static List<DirectorySearchParams> setupOtherDirectories(Context context,
63416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            Cursor directoryCursor, Account account) {
63516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        final PackageManager packageManager = context.getPackageManager();
63674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final List<DirectorySearchParams> paramsList = new ArrayList<DirectorySearchParams>();
6376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        DirectorySearchParams preferredDirectory = null;
6386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        while (directoryCursor.moveToNext()) {
6396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final long id = directoryCursor.getLong(DirectoryListQuery.ID);
6406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // Skip the local invisible directory, because the default directory already includes
6426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // all local results.
6436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (id == Directory.LOCAL_INVISIBLE) {
6446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                continue;
6456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
6466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final DirectorySearchParams params = new DirectorySearchParams();
6486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String packageName = directoryCursor.getString(DirectoryListQuery.PACKAGE_NAME);
6496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int resourceId = directoryCursor.getInt(DirectoryListQuery.TYPE_RESOURCE_ID);
6506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.directoryId = id;
6516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.displayName = directoryCursor.getString(DirectoryListQuery.DISPLAY_NAME);
6526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountName = directoryCursor.getString(DirectoryListQuery.ACCOUNT_NAME);
6536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountType = directoryCursor.getString(DirectoryListQuery.ACCOUNT_TYPE);
6546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (packageName != null && resourceId != 0) {
6556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                try {
6566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    final Resources resources =
6576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            packageManager.getResourcesForApplication(packageName);
6586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    params.directoryType = resources.getString(resourceId);
6596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    if (params.directoryType == null) {
6606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                        Log.e(TAG, "Cannot resolve directory name: "
6616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                                + resourceId + "@" + packageName);
6626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    }
6636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                } catch (NameNotFoundException e) {
6646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    Log.e(TAG, "Cannot resolve directory name: "
6656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            + resourceId + "@" + packageName, e);
6666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
6676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
6686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // If an account has been provided and we found a directory that
6706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // corresponds to that account, place that directory second, directly
6716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // underneath the local contacts.
67216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            if (account != null && account.name.equals(params.accountName) &&
67316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    account.type.equals(params.accountType)) {
6746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                preferredDirectory = params;
6756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
67674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                paramsList.add(params);
6776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
6786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
6796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        if (preferredDirectory != null) {
68174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            paramsList.add(1, preferredDirectory);
6826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
6836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
68474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return paramsList;
6856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6866ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
6888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Starts search in other directories using {@link Filter}. Results will be handled in
6898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link DirectoryFilter}.
6906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
69176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected void startSearchOtherDirectories(
69274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            CharSequence constraint, List<DirectorySearchParams> paramsList, int limit) {
69374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final int count = paramsList.size();
6946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        // Note: skipping the default partition (index 0), which has already been loaded
6956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        for (int i = 1; i < count; i++) {
69674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final DirectorySearchParams params = paramsList.get(i);
69774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.constraint = constraint;
69874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (params.filter == null) {
69974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                params.filter = new DirectoryFilter(params);
70074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
70174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.setLimit(limit);
70274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.filter(constraint);
70374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
7044bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
7058383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        // Directory search started. We may show "waiting" message if directory results are slow
7068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        // enough.
7074bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        mRemainingDirectoryCount = count - 1;
7084bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        mDelayedMessageHandler.sendDelayedLoadMessage();
70974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
71074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
7118af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
7128af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Called whenever {@link com.android.ex.chips.BaseRecipientAdapter.DirectoryFilter}
7138af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * wants to add an additional entry to the results. Derived classes should override
7148af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * this method if they are not using the default data structures provided by
7158af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * {@link com.android.ex.chips.BaseRecipientAdapter} and are instead using their
7168af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * own data structures to store and collate data.
7178af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * @param entry the entry being added
7188af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * @param isAggregatedEntry
7198af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
7208af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected void putOneEntry(TemporaryEntry entry, boolean isAggregatedEntry) {
7218af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        putOneEntry(entry, isAggregatedEntry,
7228af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                mEntryMap, mNonAggregatedEntries, mExistingDestinations);
7238af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
7248af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
725f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy    private static void putOneEntry(TemporaryEntry entry, boolean isAggregatedEntry,
7268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            LinkedHashMap<Long, List<RecipientEntry>> entryMap,
7278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            List<RecipientEntry> nonAggregatedEntries,
7288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Set<String> existingDestinations) {
7298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (existingDestinations.contains(entry.destination)) {
7308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            return;
7318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
73274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
7338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        existingDestinations.add(entry.destination);
7348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
7358383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (!isAggregatedEntry) {
7368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            nonAggregatedEntries.add(RecipientEntry.constructTopLevelEntry(
7377211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
73800adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
7397211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
7407a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
7417a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    true, entry.lookupKey));
7428383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        } else if (entryMap.containsKey(entry.contactId)) {
7438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // We already have a section for the person.
7448383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            final List<RecipientEntry> entryList = entryMap.get(entry.contactId);
7458383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryList.add(RecipientEntry.constructSecondLevelEntry(
7467211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
74700adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
7487211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
7497a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
7507a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    true, entry.lookupKey));
7518383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        } else {
7528383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            final List<RecipientEntry> entryList = new ArrayList<RecipientEntry>();
7538383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryList.add(RecipientEntry.constructTopLevelEntry(
7547211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
75500adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
7567211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
7577a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
7587a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    true, entry.lookupKey));
7598383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryMap.put(entry.contactId, entryList);
7606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
7616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
7626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
7648af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Returns the actual list to use for this Adapter. Derived classes
7658af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * should override this method if overriding how the adapter stores and collates
7668af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * data.
7678af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
7688af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected List<RecipientEntry> constructEntryList() {
7698af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        return constructEntryList(mEntryMap, mNonAggregatedEntries);
7708af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
7718af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
7728af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
77374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Constructs an actual list for this Adapter using {@link #mEntryMap}. Also tries to
77474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * fetch a cached photo for each contact entry (other than separators), or request another
7758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * thread to get one from directories.
7766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
7778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> constructEntryList(
7788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            LinkedHashMap<Long, List<RecipientEntry>> entryMap,
779f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy            List<RecipientEntry> nonAggregatedEntries) {
7808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final List<RecipientEntry> entries = new ArrayList<RecipientEntry>();
78174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        int validEntryCount = 0;
7828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        for (Map.Entry<Long, List<RecipientEntry>> mapEntry : entryMap.entrySet()) {
783f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            final List<RecipientEntry> entryList = mapEntry.getValue();
7846ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int size = entryList.size();
7856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            for (int i = 0; i < size; i++) {
786f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                RecipientEntry entry = entryList.get(i);
7878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                entries.add(entry);
7888af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                mPhotoManager.populatePhotoBytesAsync(entry, this);
78974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
7906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
79174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (validEntryCount > mPreferredMaxResultCount) {
79274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                break;
79374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
7946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
79574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (validEntryCount <= mPreferredMaxResultCount) {
7968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            for (RecipientEntry entry : nonAggregatedEntries) {
79774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (validEntryCount > mPreferredMaxResultCount) {
79874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    break;
79974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
8008383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                entries.add(entry);
8018af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                mPhotoManager.populatePhotoBytesAsync(entry, this);
80274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
80374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
80474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
80574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
8068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        return entries;
8078383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
8088383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
8098c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
81038fe984ca3e9ed7af3d5441ee3a7e82dbfdb3d2cMike Schneider    public interface EntriesUpdatedObserver {
8118c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        public void onChanged(List<RecipientEntry> entries);
8128c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    }
8138c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
8148c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    public void registerUpdateObserver(EntriesUpdatedObserver observer) {
8158c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        mEntriesUpdatedObserver = observer;
8168c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    }
8178c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
8188383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Resets {@link #mEntries} and notify the event to its parent ListView. */
8198af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected void updateEntries(List<RecipientEntry> newEntries) {
8208383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        mEntries = newEntries;
8218c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        mEntriesUpdatedObserver.onChanged(newEntries);
8226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        notifyDataSetChanged();
8236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
8246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
82531c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao    /**
82631c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     * If there are no local results and we are searching alternate results,
82731c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     * in the new result set, cache off what had been shown to the user for use until
82831c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     * the first directory result is returned
82931c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     * @param newEntryCount number of newly loaded entries
83031c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     * @param paramListCount number of alternate filters it will search (including the current one).
83131c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     */
83231c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao    protected void cacheCurrentEntriesIfNeeded(int newEntryCount, int paramListCount) {
83331c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao        if (newEntryCount == 0 && paramListCount > 1) {
83431c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao            cacheCurrentEntries();
83531c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao        }
83631c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao    }
83731c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao
8388af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected void cacheCurrentEntries() {
83937726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        mTempEntries = mEntries;
84037726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
84137726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
8428af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected void clearTempEntries() {
84337726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        mTempEntries = null;
84437726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
84537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
84676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected List<RecipientEntry> getEntries() {
84737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return mTempEntries != null ? mTempEntries : mEntries;
84837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
84937726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
85035e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao    protected void fetchPhoto(final RecipientEntry entry, PhotoManager.PhotoManagerCallback cb) {
85135e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        mPhotoManager.populatePhotoBytesAsync(entry, cb);
8526b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira    }
8536b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira
854d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private Cursor doQuery(CharSequence constraint, int limit, Long directoryId) {
8551db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein        final Uri.Builder builder = mQueryMode.getContentFilterUri().buildUpon()
8568383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                .appendPath(constraint.toString())
8578383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
8588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES));
8598383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (directoryId != null) {
8608383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
8618383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    String.valueOf(directoryId));
8628383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
8638383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (mAccount != null) {
8648383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(PRIMARY_ACCOUNT_NAME, mAccount.name);
8658383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(PRIMARY_ACCOUNT_TYPE, mAccount.type);
8668383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
8678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final long start = System.currentTimeMillis();
8688383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final Cursor cursor = mContentResolver.query(
8691db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein                builder.build(), mQueryMode.getProjection(), null, null, null);
8708383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final long end = System.currentTimeMillis();
8718383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (DEBUG) {
8728383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Log.d(TAG, "Time for autocomplete (query: " + constraint
8738383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + ", directoryId: " + directoryId + ", num_of_results: "
8748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + (cursor != null ? cursor.getCount() : "null") + "): "
8758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + (end - start) + " ms");
876d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        }
877d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return cursor;
878d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
879d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
880cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    // TODO: This won't be used at all. We should find better way to quit the thread..
881cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    /*public void close() {
8828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        mEntries = null;
88374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoCacheMap.evictAll();
884cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa        if (!sPhotoHandlerThread.quit()) {
88574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            Log.w(TAG, "Failed to quit photo handler thread, ignoring it.");
88674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
887cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    }*/
8886ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
8896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
8906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public int getCount() {
89137726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        final List<RecipientEntry> entries = getEntries();
89237726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return entries != null ? entries.size() : 0;
8936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
8946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
8956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
896858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy    public RecipientEntry getItem(int position) {
89737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position);
8986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
8996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
9016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public long getItemId(int position) {
9026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return position;
9036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
90674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getViewTypeCount() {
907f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira        return RecipientEntry.ENTRY_TYPE_SIZE;
90874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
90974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
91074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
91174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getItemViewType(int position) {
91237726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position).getEntryType();
91374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
9146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
91574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
9166d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    public boolean isEnabled(int position) {
91737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position).isSelectable();
9186d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    }
9196d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa
9206d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    @Override
92174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public View getView(int position, View convertView, ViewGroup parent) {
92237726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        final RecipientEntry entry = getEntries().get(position);
9236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
924d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin        final String constraint = mCurrentConstraint == null ? null :
925d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin                mCurrentConstraint.toString();
926d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin
927b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return mDropdownChipLayouter.bindView(convertView, parent, entry, position,
928d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin                AdapterType.BASE_RECIPIENT, constraint);
929d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
93016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp
93116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    public Account getAccount() {
93216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        return mAccount;
93316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    }
9348af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
9358af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    @Override
93650429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein    public void onPhotoBytesPopulated() {
93750429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein        // Default implementation does nothing
93850429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein    }
93950429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein
94050429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein    @Override
9418af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public void onPhotoBytesAsynchronouslyPopulated() {
9428af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        notifyDataSetChanged();
9438af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
9440efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao
9450efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao    @Override
9460efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao    public void onPhotoBytesAsyncLoadFailed() {
94750429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein        // Default implementation does nothing
9480efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao    }
9496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa}
950