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;
20f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovicimport android.app.Activity;
216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.ContentResolver;
226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.Context;
236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.pm.PackageManager;
246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.pm.PackageManager.NameNotFoundException;
256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.res.Resources;
266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.database.Cursor;
27f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovicimport android.database.MatrixCursor;
286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.net.Uri;
296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.os.Handler;
304bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawaimport android.os.Message;
316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract;
326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract.Directory;
334b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovicimport android.support.annotation.Nullable;
346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.text.TextUtils;
356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.text.util.Rfc822Token;
366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.util.Log;
376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.view.View;
386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.view.ViewGroup;
396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.AutoCompleteTextView;
406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.BaseAdapter;
416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.Filter;
426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.Filterable;
43b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
444b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovicimport com.android.ex.chips.ChipsUtil.PermissionsCheckListener;
45b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Linimport com.android.ex.chips.DropdownChipLayouter.AdapterType;
466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.ArrayList;
48fa7b0fb73f80ceafdf3ff3260b345b61d2766f93Scott Kennedyimport java.util.Collections;
4974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport java.util.HashSet;
50cf9337aa4b2d844f4941d75832f52266c4fc3fa7Daisuke Miyakawaimport java.util.LinkedHashMap;
516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.List;
526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.Map;
5374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport java.util.Set;
546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa/**
566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Adapter for showing a recipient list.
57f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic *
58f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic * <p>It checks whether all permissions are granted before doing
59f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic * query. If not all permissions in {@link ChipsUtil#REQUIRED_PERMISSIONS} are granted and
60f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic * {@link #mShowRequestPermissionsItem} is true it will return single entry that asks user to grant
61f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic * permissions to the app. Any app that uses this library should set this when it wants us to
62f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic * display that entry but then it should set
63f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic * {@link RecipientEditTextView.PermissionsRequestItemClickedListener} on
64f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic * {@link RecipientEditTextView} as well.
656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa */
668af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinpublic class BaseRecipientAdapter extends BaseAdapter implements Filterable, AccountSpecifier,
678af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        PhotoManager.PhotoManagerCallback {
686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final String TAG = "BaseRecipientAdapter";
698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
706b616f1d06b092825da345078ce4a899b829290dDaisuke Miyakawa    private static final boolean DEBUG = false;
716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * The preferred number of results to be retrieved. This number may be
746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * exceeded if there are several directories configured, because we will use
756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * the same limit for all directories.
766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final int DEFAULT_PREFERRED_MAX_RESULT_COUNT = 10;
786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * The number of extra entries requested to allow for duplicates. Duplicates
816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * are removed from the overall result.
826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
8316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final int ALLOWANCE_FOR_DUPLICATES = 5;
846ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
857537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    // This is ContactsContract.PRIMARY_ACCOUNT_NAME. Available from ICS as hidden
8616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
877537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    // This is ContactsContract.PRIMARY_ACCOUNT_TYPE. Available from ICS as hidden
8816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account";
897537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa
904bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /**
914bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * The "Waiting for more contacts" message will be displayed if search is not complete
924bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * within this many milliseconds.
934bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     */
944bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private static final int MESSAGE_SEARCH_PENDING_DELAY = 1000;
954bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /** Used to prepare "Waiting for more contacts" message. */
964bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private static final int MESSAGE_SEARCH_PENDING = 1;
974bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
98d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public static final int QUERY_TYPE_EMAIL = 0;
99d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public static final int QUERY_TYPE_PHONE = 1;
100d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
1011db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein    private final Queries.Query mQueryMode;
10280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    private final int mQueryType;
10380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
1046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
1056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Model object for a {@link Directory} row.
1066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
1076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public final static class DirectorySearchParams {
1086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public long directoryId;
1096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String directoryType;
1106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String displayName;
1116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String accountName;
1126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String accountType;
1136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence constraint;
1146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public DirectoryFilter filter;
1156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
11716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    protected static class DirectoryListQuery {
1186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final Uri URI =
1206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories");
1216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final String[] PROJECTION = {
1226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory._ID,              // 0
1236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_NAME,     // 1
1246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_TYPE,     // 2
1256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.DISPLAY_NAME,     // 3
1266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.PACKAGE_NAME,     // 4
1276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.TYPE_RESOURCE_ID, // 5
1286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        };
1296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ID = 0;
1316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_NAME = 1;
1326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_TYPE = 2;
1336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int DISPLAY_NAME = 3;
1346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int PACKAGE_NAME = 4;
1356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int TYPE_RESOURCE_ID = 5;
1366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1388383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Used to temporarily hold results in Cursor objects. */
13976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected static class TemporaryEntry {
1408383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String displayName;
1418383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String destination;
1427211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa        public final int destinationType;
1437211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa        public final String destinationLabel;
1448383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final long contactId;
1457a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        public final Long directoryId;
1468383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final long dataId;
1478383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String thumbnailUriString;
14800adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki        public final int displayNameSource;
1497a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        public final String lookupKey;
1508383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
15176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        public TemporaryEntry(
15276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String displayName,
15376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String destination,
15476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                int destinationType,
15576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String destinationLabel,
15676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                long contactId,
1577a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                Long directoryId,
15876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                long dataId,
15976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String thumbnailUriString,
160514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                int displayNameSource,
1617a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                String lookupKey) {
16276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.displayName = displayName;
16376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destination = destination;
16476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destinationType = destinationType;
16576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destinationLabel = destinationLabel;
16676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.contactId = contactId;
1677a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.directoryId = directoryId;
16876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.dataId = dataId;
16976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.thumbnailUriString = thumbnailUriString;
17076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.displayNameSource = displayNameSource;
1717a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.lookupKey = lookupKey;
17276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        }
17376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
1747a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        public TemporaryEntry(Cursor cursor, Long directoryId) {
17580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.displayName = cursor.getString(Queries.Query.NAME);
17680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destination = cursor.getString(Queries.Query.DESTINATION);
17780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destinationType = cursor.getInt(Queries.Query.DESTINATION_TYPE);
17880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destinationLabel = cursor.getString(Queries.Query.DESTINATION_LABEL);
17980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.contactId = cursor.getLong(Queries.Query.CONTACT_ID);
1807a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.directoryId = directoryId;
18180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.dataId = cursor.getLong(Queries.Query.DATA_ID);
18280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.thumbnailUriString = cursor.getString(Queries.Query.PHOTO_THUMBNAIL_URI);
18300adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki            this.displayNameSource = cursor.getInt(Queries.Query.DISPLAY_NAME_SOURCE);
1847a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.lookupKey = cursor.getString(Queries.Query.LOOKUP_KEY);
1858383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
1868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
1878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
1888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /**
1898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Used to pass results from {@link DefaultFilter#performFiltering(CharSequence)} to
1908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link DefaultFilter#publishResults(CharSequence, android.widget.Filter.FilterResults)}
1918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     */
1928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private static class DefaultFilterResult {
1938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<RecipientEntry> entries;
1948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final LinkedHashMap<Long, List<RecipientEntry>> entryMap;
1958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<RecipientEntry> nonAggregatedEntries;
1968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final Set<String> existingDestinations;
1978383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<DirectorySearchParams> paramsList;
1988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
1998383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public DefaultFilterResult(List<RecipientEntry> entries,
2008383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                LinkedHashMap<Long, List<RecipientEntry>> entryMap,
2018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                List<RecipientEntry> nonAggregatedEntries,
2028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Set<String> existingDestinations,
2038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                List<DirectorySearchParams> paramsList) {
2048383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.entries = entries;
2058383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.entryMap = entryMap;
2068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.nonAggregatedEntries = nonAggregatedEntries;
2078383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.existingDestinations = existingDestinations;
2088383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.paramsList = paramsList;
2098383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
210f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
211f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        private static DefaultFilterResult createResultWithNonAggregatedEntry(
212f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                RecipientEntry entry) {
213f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            return new DefaultFilterResult(
214f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                    Collections.singletonList(entry),
215f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                    new LinkedHashMap<Long, List<RecipientEntry>>() /* entryMap */,
216f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                    Collections.singletonList(entry) /* nonAggregatedEntries */,
217f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                    Collections.<String>emptySet() /* existingDestinations */,
218f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                    null /* paramsList */);
219f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        }
2208383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
2218383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
2236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter used for loading two data sets: email rows from the local
2246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * contact provider and the list of {@link Directory}'s.
2256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
2266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final class DefaultFilter extends Filter {
2276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
2296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
2308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
2318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "start filtering. constraint: " + constraint + ", thread:"
2328383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + Thread.currentThread());
2338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
2348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
23574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
2368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2378383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (TextUtils.isEmpty(constraint)) {
23837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                clearTempEntries();
2398383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // Return empty results.
2408383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                return results;
2416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
2426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2434b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic            if (!ChipsUtil.hasPermissions(mContext, mPermissionsCheckListener)) {
244f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                if (DEBUG) {
245f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                    Log.d(TAG, "No Contacts permission. mShowRequestPermissionsItem: "
246f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                            + mShowRequestPermissionsItem);
247f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                }
248f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                clearTempEntries();
249f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                if (!mShowRequestPermissionsItem) {
250f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                    // App doesn't want to show request permission entry. Returning empty results.
251f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                    return results;
252f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                }
253f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
254f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                // Return result with only permission request entry.
255f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                results.values = DefaultFilterResult.createResultWithNonAggregatedEntry(
256f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                        RecipientEntry.constructPermissionEntry(ChipsUtil.REQUIRED_PERMISSIONS));
257f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                results.count = 1;
258f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                return results;
259f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            }
260f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
261f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            Cursor defaultDirectoryCursor = null;
262f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
2638383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            try {
2647a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                defaultDirectoryCursor = doQuery(constraint, mPreferredMaxResultCount,
2657a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        null /* directoryId */);
26680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
2678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultDirectoryCursor == null) {
2688383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (DEBUG) {
2698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        Log.w(TAG, "null cursor returned for default Email filter query.");
2708383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
2718383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                } else {
2728383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // These variables will become mEntries, mEntryMap, mNonAggregatedEntries, and
2738383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // mExistingDestinations. Here we shouldn't use those member variables directly
2748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // since this method is run outside the UI thread.
2758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final LinkedHashMap<Long, List<RecipientEntry>> entryMap =
2768383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            new LinkedHashMap<Long, List<RecipientEntry>>();
2778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final List<RecipientEntry> nonAggregatedEntries =
2788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            new ArrayList<RecipientEntry>();
2798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final Set<String> existingDestinations = new HashSet<String>();
2808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2818383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    while (defaultDirectoryCursor.moveToNext()) {
2828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        // Note: At this point each entry doesn't contain any photo
2838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        // (thus getPhotoBytes() returns null).
284514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                        putOneEntry(new TemporaryEntry(defaultDirectoryCursor,
2857a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                                null /* directoryId */),
2868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                true, entryMap, nonAggregatedEntries, existingDestinations);
2878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
2888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We'll copy this result to mEntry in publicResults() (run in the UX thread).
290f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                    final List<RecipientEntry> entries = constructEntryList(
291f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                            entryMap, nonAggregatedEntries);
2928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2938af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                    final List<DirectorySearchParams> paramsList =
2948af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                            searchOtherDirectories(existingDestinations);
2956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.values = new DefaultFilterResult(
2978383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            entries, entryMap, nonAggregatedEntries,
2988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            existingDestinations, paramsList);
2992a87fbfbbee4279c399980cba42de1d361a8ad56Scott Kennedy                    results.count = entries.size();
3008383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
3018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            } finally {
3028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultDirectoryCursor != null) {
3038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    defaultDirectoryCursor.close();
3048383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
30574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
3066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
3076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
3106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
3118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mCurrentConstraint = constraint;
3128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
31337726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook            clearTempEntries();
31437726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
3156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (results.values != null) {
3168383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                DefaultFilterResult defaultFilterResult = (DefaultFilterResult) results.values;
3178383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mEntryMap = defaultFilterResult.entryMap;
3188383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mNonAggregatedEntries = defaultFilterResult.nonAggregatedEntries;
3198383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mExistingDestinations = defaultFilterResult.existingDestinations;
3208383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
32131c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao                cacheCurrentEntriesIfNeeded(defaultFilterResult.entries.size(),
32231c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao                        defaultFilterResult.paramsList == null ? 0 :
32331c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao                                defaultFilterResult.paramsList.size());
32437726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
3258383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                updateEntries(defaultFilterResult.entries);
3268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // We need to search other remote directories, doing other Filter requests.
3288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultFilterResult.paramsList != null) {
3298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final int limit = mPreferredMaxResultCount -
3308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            defaultFilterResult.existingDestinations.size();
3318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    startSearchOtherDirectories(constraint, defaultFilterResult.paramsList, limit);
3328383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
333fa7b0fb73f80ceafdf3ff3260b345b61d2766f93Scott Kennedy            } else {
334fa7b0fb73f80ceafdf3ff3260b345b61d2766f93Scott Kennedy                updateEntries(Collections.<RecipientEntry>emptyList());
3356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
3396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence convertResultToString(Object resultValue) {
340f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            final RecipientEntry entry = (RecipientEntry)resultValue;
3416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String displayName = entry.getDisplayName();
3426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String emailAddress = entry.getDestination();
3436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, emailAddress)) {
3446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                 return emailAddress;
3456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
3466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                return new Rfc822Token(displayName, emailAddress, null).toString();
3476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
351f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    /**
352f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic     * Returns the list of models for directory search  (using {@link DirectoryFilter}) or
353f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic     * {@code null} when we don't need or can't search other directories.
354f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic     */
3558af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected List<DirectorySearchParams> searchOtherDirectories(Set<String> existingDestinations) {
3564b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic        if (!ChipsUtil.hasPermissions(mContext, mPermissionsCheckListener)) {
357f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            // If we don't have permissions we can't search other directories.
358f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            if (DEBUG) {
359f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                Log.d(TAG, "Not searching other directories because we don't have required "
360f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                        + "permissions.");
361f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            }
362f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            return null;
363f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        }
364f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
3658af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        // After having local results, check the size of results. If the results are
3668af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        // not enough, we search remote directories, which will take longer time.
3678af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        final int limit = mPreferredMaxResultCount - existingDestinations.size();
3688af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        if (limit > 0) {
3698af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            if (DEBUG) {
3708af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                Log.d(TAG, "More entries should be needed (current: "
3718af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                        + existingDestinations.size()
3728af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                        + ", remaining limit: " + limit + ") ");
3738af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            }
3740437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein            Cursor directoryCursor = null;
3750437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein            try {
3760437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                directoryCursor = mContentResolver.query(
3770437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                        DirectoryListQuery.URI, DirectoryListQuery.PROJECTION,
3780437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                        null, null, null);
3790437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                return setupOtherDirectories(mContext, directoryCursor, mAccount);
3800437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein            } finally {
3810437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                if (directoryCursor != null) {
3820437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                    directoryCursor.close();
3830437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein                }
3840437e862005912a1e5bf054769f23ed9cc69f811Andrew Sapperstein            }
3858af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        } else {
3868af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            // We don't need to search other directories.
3878af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            return null;
3888af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        }
3898af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
3908af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
3916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
3926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter that performs search in a particular directory.
3936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
39476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected class DirectoryFilter extends Filter {
39574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        private final DirectorySearchParams mParams;
3966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        private int mLimit;
3976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
39874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public DirectoryFilter(DirectorySearchParams params) {
3998383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mParams = params;
4006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized void setLimit(int limit) {
4036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            this.mLimit = limit;
4046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized int getLimit() {
4076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return this.mLimit;
4086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
4116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
4128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
4138383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "DirectoryFilter#performFiltering. directoryId: " + mParams.directoryId
4148383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + ", constraint: " + constraint + ", thread: " + Thread.currentThread());
4158383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
41674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
4178383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            results.values = null;
4188383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            results.count = 0;
4198383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (!TextUtils.isEmpty(constraint)) {
4218383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                final ArrayList<TemporaryEntry> tempEntries = new ArrayList<TemporaryEntry>();
4228383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4238383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Cursor cursor = null;
4248383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                try {
4258383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We don't want to pass this Cursor object to UI thread (b/5017608).
4268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // Assuming the result should contain fairly small results (at most ~10),
4278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We just copy everything to local structure.
4288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    cursor = doQuery(constraint, getLimit(), mParams.directoryId);
42980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
4308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (cursor != null) {
4318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        while (cursor.moveToNext()) {
4327a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                            tempEntries.add(new TemporaryEntry(cursor, mParams.directoryId));
4338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        }
4348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
4358383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                } finally {
4368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (cursor != null) {
4378383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        cursor.close();
4388383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
43974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
4408383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (!tempEntries.isEmpty()) {
4418383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.values = tempEntries;
4422a87fbfbbee4279c399980cba42de1d361a8ad56Scott Kennedy                    results.count = tempEntries.size();
4438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
4448383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4458383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4468383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
4478383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.v(TAG, "finished loading directory \"" + mParams.displayName + "\"" +
4488383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        " with query " + constraint);
4496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
4506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
4526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
4556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
4568383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
4578383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "DirectoryFilter#publishResult. constraint: " + constraint
4588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + ", mCurrentConstraint: " + mCurrentConstraint);
4598383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4608383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mDelayedMessageHandler.removeDelayedLoadMessage();
4618383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // Check if the received result matches the current constraint
4628383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // If not - the user must have continued typing after the request was issued, which
4638383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // means several member variables (like mRemainingDirectoryLoad) are already
4648383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // overwritten so shouldn't be touched here anymore.
4658383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (TextUtils.equals(constraint, mCurrentConstraint)) {
4668383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (results.count > 0) {
467dfd7e0774f9a4f47c127b21e5468430374340f66Andy Huang                    @SuppressWarnings("unchecked")
4688383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final ArrayList<TemporaryEntry> tempEntries =
4698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            (ArrayList<TemporaryEntry>) results.values;
4708383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4718383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    for (TemporaryEntry tempEntry : tempEntries) {
4728af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                        putOneEntry(tempEntry, mParams.directoryId == Directory.DEFAULT);
4738383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
4748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
4758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4768383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // If there are remaining directories, set up delayed message again.
4778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mRemainingDirectoryCount--;
4788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (mRemainingDirectoryCount > 0) {
4798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (DEBUG) {
4808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        Log.d(TAG, "Resend delayed load message. Current mRemainingDirectoryLoad: "
4818383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                + mRemainingDirectoryCount);
4828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
4838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    mDelayedMessageHandler.sendDelayedLoadMessage();
4848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
48537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
48637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // If this directory result has some items, or there are no more directories that
48737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // we are waiting for, clear the temp results
48837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                if (results.count > 0 || mRemainingDirectoryCount == 0) {
48937726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                    // Clear the temp entries
49037726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                    clearTempEntries();
49137726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                }
4928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // Show the list again without "waiting" message.
4958af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            updateEntries(constructEntryList());
4966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
4986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
49974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final Context mContext;
5006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final ContentResolver mContentResolver;
5016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private Account mAccount;
5028af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected final int mPreferredMaxResultCount;
503b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    private DropdownChipLayouter mDropdownChipLayouter;
5046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
5056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
5068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mEntries} is responsible for showing every result for this Adapter. To
5078383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * construct it, we use {@link #mEntryMap}, {@link #mNonAggregatedEntries}, and
5088383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mExistingDestinations}.
5098383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     *
5108383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * First, each destination (an email address or a phone number) with a valid contactId is
5118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * inserted into {@link #mEntryMap} and grouped by the contactId. Destinations without valid
5128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * contactId (possible if they aren't in local storage) are stored in
5138383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mNonAggregatedEntries}.
51474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Duplicates are removed using {@link #mExistingDestinations}.
51574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     *
5168383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * After having all results from Cursor objects, all destinations in mEntryMap are copied to
5178383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mEntries}. If the number of destinations is not enough (i.e. less than
5188383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mPreferredMaxResultCount}), destinations in mNonAggregatedEntries are also used.
5198383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     *
5208383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * These variables are only used in UI thread, thus should not be touched in
5218383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * performFiltering() methods.
5226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
5238383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private LinkedHashMap<Long, List<RecipientEntry>> mEntryMap;
5248383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> mNonAggregatedEntries;
5258383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private Set<String> mExistingDestinations;
5268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Note: use {@link #updateEntries(List)} to update this variable. */
5278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> mEntries;
52837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    private List<RecipientEntry> mTempEntries;
5296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
5304bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /** The number of directories this adapter is waiting for results. */
5314bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private int mRemainingDirectoryCount;
5324bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
53374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /**
5348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Used to ignore asynchronous queries with a different constraint, which may happen when
53574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * users type characters quickly.
53674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     */
5378af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected CharSequence mCurrentConstraint;
53874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
5398af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
5408af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Performs all photo querying as well as caching for repeated lookups.
5418af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
5428af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    private PhotoManager mPhotoManager;
5436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
544f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    protected boolean mShowRequestPermissionsItem;
545f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
5464b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic    private PermissionsCheckListener mPermissionsCheckListener;
5474b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic
548d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
5494bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * Handler specific for maintaining "Waiting for more contacts" message, which will be shown
5504bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * when:
5514bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * - there are directories to be searched
5524bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * - results from directories are slow to come
5534bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     */
5544bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private final class DelayedMessageHandler extends Handler {
5554bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        @Override
5564bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void handleMessage(Message msg) {
5574bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            if (mRemainingDirectoryCount > 0) {
5588af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                updateEntries(constructEntryList());
5594bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            }
5604bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5614bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5624bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void sendDelayedLoadMessage() {
5634bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            sendMessageDelayed(obtainMessage(MESSAGE_SEARCH_PENDING, 0, 0, null),
5644bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa                    MESSAGE_SEARCH_PENDING_DELAY);
5654bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5664bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5674bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void removeDelayedLoadMessage() {
5684bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            removeMessages(MESSAGE_SEARCH_PENDING);
5694bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5704bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    }
5714bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5724bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private final DelayedMessageHandler mDelayedMessageHandler = new DelayedMessageHandler();
5734bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5748c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    private EntriesUpdatedObserver mEntriesUpdatedObserver;
5758c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
5764bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /**
577d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Constructor for email queries.
578d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
5796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public BaseRecipientAdapter(Context context) {
58080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT, QUERY_TYPE_EMAIL);
5816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
5838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    public BaseRecipientAdapter(Context context, int preferredMaxResultCount) {
58480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, preferredMaxResultCount, QUERY_TYPE_EMAIL);
58580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
58680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
58780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(int queryMode, Context context) {
58880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT, queryMode);
58980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
59080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
59180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(int queryMode, Context context, int preferredMaxResultCount) {
59280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, preferredMaxResultCount, queryMode);
59380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
59480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
59580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(Context context, int preferredMaxResultCount, int queryMode) {
5966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContext = context;
5976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContentResolver = context.getContentResolver();
5986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mPreferredMaxResultCount = preferredMaxResultCount;
5998af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        mPhotoManager = new DefaultPhotoManager(mContentResolver);
60080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        mQueryType = queryMode;
60180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
60280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        if (queryMode == QUERY_TYPE_EMAIL) {
6031db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein            mQueryMode = Queries.EMAIL;
60480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        } else if (queryMode == QUERY_TYPE_PHONE) {
6051db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein            mQueryMode = Queries.PHONE;
60680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        } else {
6071db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein            mQueryMode = Queries.EMAIL;
60880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            Log.e(TAG, "Unsupported query type: " + queryMode);
60980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        }
61080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
61180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
61276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    public Context getContext() {
61376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        return mContext;
61476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    }
61576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
61680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public int getQueryType() {
61780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        return mQueryType;
6186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
620b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    public void setDropdownChipLayouter(DropdownChipLayouter dropdownChipLayouter) {
621b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        mDropdownChipLayouter = dropdownChipLayouter;
6221db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein        mDropdownChipLayouter.setQuery(mQueryMode);
623b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
624b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
625b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    public DropdownChipLayouter getDropdownChipLayouter() {
626b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return mDropdownChipLayouter;
627b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
628b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
6294b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic    public void setPermissionsCheckListener(PermissionsCheckListener permissionsCheckListener) {
6304b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic        mPermissionsCheckListener = permissionsCheckListener;
6314b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic    }
6324b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic
6334b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic    @Nullable
6344b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic    public PermissionsCheckListener getPermissionsCheckListener() {
6354b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic        return mPermissionsCheckListener;
6364b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic    }
6374b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic
6386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
6398af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Enables overriding the default photo manager that is used.
6408af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
6418af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public void setPhotoManager(PhotoManager photoManager) {
6428af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        mPhotoManager = photoManager;
6438af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
6448af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
6458af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public PhotoManager getPhotoManager() {
6468af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        return mPhotoManager;
6478af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
6488af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
6498af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
6508af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * If true, forces using the {@link com.android.ex.chips.SingleRecipientArrayAdapter}
6518af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * instead of {@link com.android.ex.chips.RecipientAlternatesAdapter} when
6528af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * clicking on a chip. Default implementation returns {@code false}.
6538af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
6548af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public boolean forceShowAddress() {
6558af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        return false;
6568af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
6578af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
6588af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
6598af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Used to replace email addresses with chips. Default behavior
6608af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * queries the ContactsProvider for contact information about the contact.
6618af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Derived classes should override this method if they wish to use a
6628af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * new data source.
6638af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * @param inAddresses addresses to query
6648af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * @param callback callback to return results in case of success or failure
6658af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
6668af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public void getMatchingRecipients(ArrayList<String> inAddresses,
6678af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein            RecipientAlternatesAdapter.RecipientMatchCallback callback) {
6688af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        RecipientAlternatesAdapter.getMatchingRecipients(
6694b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic                getContext(), this, inAddresses, getAccount(), callback, mPermissionsCheckListener);
6708af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
6718af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
6728af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
6736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Set the account when known. Causes the search to prioritize contacts from that account.
6746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
675dfd7e0774f9a4f47c127b21e5468430374340f66Andy Huang    @Override
6766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public void setAccount(Account account) {
6776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mAccount = account;
6786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
680f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    /**
681f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic     * Returns permissions that this adapter needs in order to provide results.
682f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic     */
683f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    public String[] getRequiredPermissions() {
684f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        return ChipsUtil.REQUIRED_PERMISSIONS;
685f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    }
686f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
687f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    /**
688f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic     * Sets whether to ask user to grant permission if they are missing.
689f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic     */
690f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    public void setShowRequestPermissionsItem(boolean show) {
691f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        mShowRequestPermissionsItem = show;
692f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic    }
693f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
6946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /** Will be called from {@link AutoCompleteTextView} to prepare auto-complete list. */
6956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
6966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public Filter getFilter() {
6976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return new DefaultFilter();
6986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
70076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    /**
7018af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * An extension to {@link RecipientAlternatesAdapter#getMatchingRecipients} that allows
70276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * additional sources of contacts to be considered as matching recipients.
70376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * @param addresses A set of addresses to be matched
70476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * @return A list of matches or null if none found
70576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     */
70676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    public Map<String, RecipientEntry> getMatchingRecipients(Set<String> addresses) {
70776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        return null;
70876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    }
70976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
71016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    public static List<DirectorySearchParams> setupOtherDirectories(Context context,
71116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            Cursor directoryCursor, Account account) {
71216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        final PackageManager packageManager = context.getPackageManager();
71374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final List<DirectorySearchParams> paramsList = new ArrayList<DirectorySearchParams>();
7146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        DirectorySearchParams preferredDirectory = null;
7156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        while (directoryCursor.moveToNext()) {
7166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final long id = directoryCursor.getLong(DirectoryListQuery.ID);
7176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // Skip the local invisible directory, because the default directory already includes
7196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // all local results.
7206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (id == Directory.LOCAL_INVISIBLE) {
7216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                continue;
7226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
7236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final DirectorySearchParams params = new DirectorySearchParams();
7256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String packageName = directoryCursor.getString(DirectoryListQuery.PACKAGE_NAME);
7266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int resourceId = directoryCursor.getInt(DirectoryListQuery.TYPE_RESOURCE_ID);
7276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.directoryId = id;
7286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.displayName = directoryCursor.getString(DirectoryListQuery.DISPLAY_NAME);
7296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountName = directoryCursor.getString(DirectoryListQuery.ACCOUNT_NAME);
7306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountType = directoryCursor.getString(DirectoryListQuery.ACCOUNT_TYPE);
7316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (packageName != null && resourceId != 0) {
7326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                try {
7336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    final Resources resources =
7346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            packageManager.getResourcesForApplication(packageName);
7356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    params.directoryType = resources.getString(resourceId);
7366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    if (params.directoryType == null) {
7376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                        Log.e(TAG, "Cannot resolve directory name: "
7386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                                + resourceId + "@" + packageName);
7396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    }
7406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                } catch (NameNotFoundException e) {
7416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    Log.e(TAG, "Cannot resolve directory name: "
7426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            + resourceId + "@" + packageName, e);
7436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
7446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
7456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // If an account has been provided and we found a directory that
7476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // corresponds to that account, place that directory second, directly
7486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // underneath the local contacts.
749f2eb07a61d474d3b5da100d928d83f1e7e853771Andy Huang            if (preferredDirectory == null && account != null
750f2eb07a61d474d3b5da100d928d83f1e7e853771Andy Huang                    && account.name.equals(params.accountName)
751f2eb07a61d474d3b5da100d928d83f1e7e853771Andy Huang                    && account.type.equals(params.accountType)) {
7526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                preferredDirectory = params;
7536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
75474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                paramsList.add(params);
7556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
7566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
7576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        if (preferredDirectory != null) {
75974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            paramsList.add(1, preferredDirectory);
7606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
7616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
76274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return paramsList;
7636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
7646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
7668383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Starts search in other directories using {@link Filter}. Results will be handled in
7678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link DirectoryFilter}.
7686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
76976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected void startSearchOtherDirectories(
77074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            CharSequence constraint, List<DirectorySearchParams> paramsList, int limit) {
77174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final int count = paramsList.size();
7726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        // Note: skipping the default partition (index 0), which has already been loaded
7736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        for (int i = 1; i < count; i++) {
77474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final DirectorySearchParams params = paramsList.get(i);
77574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.constraint = constraint;
77674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (params.filter == null) {
77774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                params.filter = new DirectoryFilter(params);
77874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
77974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.setLimit(limit);
78074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.filter(constraint);
78174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
7824bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
7838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        // Directory search started. We may show "waiting" message if directory results are slow
7848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        // enough.
7854bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        mRemainingDirectoryCount = count - 1;
7864bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        mDelayedMessageHandler.sendDelayedLoadMessage();
78774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
78874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
7898af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
7908af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Called whenever {@link com.android.ex.chips.BaseRecipientAdapter.DirectoryFilter}
7918af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * wants to add an additional entry to the results. Derived classes should override
7928af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * this method if they are not using the default data structures provided by
7938af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * {@link com.android.ex.chips.BaseRecipientAdapter} and are instead using their
7948af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * own data structures to store and collate data.
7958af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * @param entry the entry being added
7968af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * @param isAggregatedEntry
7978af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
7988af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected void putOneEntry(TemporaryEntry entry, boolean isAggregatedEntry) {
7998af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        putOneEntry(entry, isAggregatedEntry,
8008af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                mEntryMap, mNonAggregatedEntries, mExistingDestinations);
8018af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
8028af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
803f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy    private static void putOneEntry(TemporaryEntry entry, boolean isAggregatedEntry,
8048383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            LinkedHashMap<Long, List<RecipientEntry>> entryMap,
8058383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            List<RecipientEntry> nonAggregatedEntries,
8068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Set<String> existingDestinations) {
8078383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (existingDestinations.contains(entry.destination)) {
8088383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            return;
8098383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
81074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
8118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        existingDestinations.add(entry.destination);
8128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
8138383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (!isAggregatedEntry) {
8148383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            nonAggregatedEntries.add(RecipientEntry.constructTopLevelEntry(
8157211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
81600adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
8177211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
8187a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
8197a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    true, entry.lookupKey));
8208383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        } else if (entryMap.containsKey(entry.contactId)) {
8218383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // We already have a section for the person.
8228383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            final List<RecipientEntry> entryList = entryMap.get(entry.contactId);
8238383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryList.add(RecipientEntry.constructSecondLevelEntry(
8247211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
82500adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
8267211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
8277a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
8287a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    true, entry.lookupKey));
8298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        } else {
8308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            final List<RecipientEntry> entryList = new ArrayList<RecipientEntry>();
8318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryList.add(RecipientEntry.constructTopLevelEntry(
8327211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
83300adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
8347211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
8357a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
8367a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    true, entry.lookupKey));
8378383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryMap.put(entry.contactId, entryList);
8386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
8396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
8406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
8416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
8428af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * Returns the actual list to use for this Adapter. Derived classes
8438af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * should override this method if overriding how the adapter stores and collates
8448af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     * data.
8458af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein     */
8468af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected List<RecipientEntry> constructEntryList() {
8478af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        return constructEntryList(mEntryMap, mNonAggregatedEntries);
8488af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
8498af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
8508af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    /**
85174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Constructs an actual list for this Adapter using {@link #mEntryMap}. Also tries to
85274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * fetch a cached photo for each contact entry (other than separators), or request another
8538383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * thread to get one from directories.
8546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
8558383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> constructEntryList(
8568383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            LinkedHashMap<Long, List<RecipientEntry>> entryMap,
857f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy            List<RecipientEntry> nonAggregatedEntries) {
8588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final List<RecipientEntry> entries = new ArrayList<RecipientEntry>();
85974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        int validEntryCount = 0;
8608383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        for (Map.Entry<Long, List<RecipientEntry>> mapEntry : entryMap.entrySet()) {
861f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            final List<RecipientEntry> entryList = mapEntry.getValue();
8626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int size = entryList.size();
8636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            for (int i = 0; i < size; i++) {
864f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                RecipientEntry entry = entryList.get(i);
8658383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                entries.add(entry);
8668af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                mPhotoManager.populatePhotoBytesAsync(entry, this);
86774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
8686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
86974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (validEntryCount > mPreferredMaxResultCount) {
87074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                break;
87174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
8726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
87374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (validEntryCount <= mPreferredMaxResultCount) {
8748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            for (RecipientEntry entry : nonAggregatedEntries) {
87574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (validEntryCount > mPreferredMaxResultCount) {
87674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    break;
87774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
8788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                entries.add(entry);
8798af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein                mPhotoManager.populatePhotoBytesAsync(entry, this);
88074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
88174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
88274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
88374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
8848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        return entries;
8858383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
8868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
8878c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
88838fe984ca3e9ed7af3d5441ee3a7e82dbfdb3d2cMike Schneider    public interface EntriesUpdatedObserver {
8898c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        public void onChanged(List<RecipientEntry> entries);
8908c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    }
8918c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
8928c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    public void registerUpdateObserver(EntriesUpdatedObserver observer) {
8938c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        mEntriesUpdatedObserver = observer;
8948c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    }
8958c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
8968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Resets {@link #mEntries} and notify the event to its parent ListView. */
8978af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected void updateEntries(List<RecipientEntry> newEntries) {
8988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        mEntries = newEntries;
8998c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        mEntriesUpdatedObserver.onChanged(newEntries);
9006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        notifyDataSetChanged();
9016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
90331c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao    /**
90431c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     * If there are no local results and we are searching alternate results,
90531c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     * in the new result set, cache off what had been shown to the user for use until
90631c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     * the first directory result is returned
90731c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     * @param newEntryCount number of newly loaded entries
90831c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     * @param paramListCount number of alternate filters it will search (including the current one).
90931c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao     */
91031c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao    protected void cacheCurrentEntriesIfNeeded(int newEntryCount, int paramListCount) {
91131c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao        if (newEntryCount == 0 && paramListCount > 1) {
91231c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao            cacheCurrentEntries();
91331c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao        }
91431c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao    }
91531c33ef87cb0612250b812233ffbc0c46e88a371Jin Cao
9168af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected void cacheCurrentEntries() {
91737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        mTempEntries = mEntries;
91837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
91937726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
9208af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    protected void clearTempEntries() {
92137726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        mTempEntries = null;
92237726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
92337726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
92476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected List<RecipientEntry> getEntries() {
92537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return mTempEntries != null ? mTempEntries : mEntries;
92637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
92737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
92835e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao    protected void fetchPhoto(final RecipientEntry entry, PhotoManager.PhotoManagerCallback cb) {
92935e82d4f9522906f7953667cf5c5f8137ec2f5acJin Cao        mPhotoManager.populatePhotoBytesAsync(entry, cb);
9306b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira    }
9316b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira
932d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private Cursor doQuery(CharSequence constraint, int limit, Long directoryId) {
9334b01e3b35a9634336e5de163ba7c4110785bf221Milos Stankovic        if (!ChipsUtil.hasPermissions(mContext, mPermissionsCheckListener)) {
934f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            if (DEBUG) {
935f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic                Log.d(TAG, "Not doing query because we don't have required permissions.");
936f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            }
937f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic            return null;
938f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic        }
939f1ad4f6dba7c6ea28f529736b8662faa6fb498f8Milos Stankovic
9401db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein        final Uri.Builder builder = mQueryMode.getContentFilterUri().buildUpon()
9418383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                .appendPath(constraint.toString())
9428383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
9438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES));
9448383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (directoryId != null) {
9458383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
9468383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    String.valueOf(directoryId));
9478383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
9488383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (mAccount != null) {
9498383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(PRIMARY_ACCOUNT_NAME, mAccount.name);
9508383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(PRIMARY_ACCOUNT_TYPE, mAccount.type);
9518383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
9528383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final long start = System.currentTimeMillis();
9538383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final Cursor cursor = mContentResolver.query(
9541db635b5a09c77a27fe17a9ce31c21a67458c2bcAndrew Sapperstein                builder.build(), mQueryMode.getProjection(), null, null, null);
9558383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final long end = System.currentTimeMillis();
9568383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (DEBUG) {
9578383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Log.d(TAG, "Time for autocomplete (query: " + constraint
9588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + ", directoryId: " + directoryId + ", num_of_results: "
9598383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + (cursor != null ? cursor.getCount() : "null") + "): "
9608383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + (end - start) + " ms");
961d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        }
962d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return cursor;
963d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
964d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
965cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    // TODO: This won't be used at all. We should find better way to quit the thread..
966cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    /*public void close() {
9678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        mEntries = null;
96874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoCacheMap.evictAll();
969cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa        if (!sPhotoHandlerThread.quit()) {
97074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            Log.w(TAG, "Failed to quit photo handler thread, ignoring it.");
97174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
972cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    }*/
9736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
9756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public int getCount() {
97637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        final List<RecipientEntry> entries = getEntries();
97737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return entries != null ? entries.size() : 0;
9786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
981858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy    public RecipientEntry getItem(int position) {
98237726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position);
9836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9846ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
9866ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public long getItemId(int position) {
9876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return position;
9886ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
99174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getViewTypeCount() {
992f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira        return RecipientEntry.ENTRY_TYPE_SIZE;
99374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
99474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
99574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
99674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getItemViewType(int position) {
99737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position).getEntryType();
99874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
9996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
100074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
10016d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    public boolean isEnabled(int position) {
100237726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position).isSelectable();
10036d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    }
10046d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa
10056d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    @Override
100674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public View getView(int position, View convertView, ViewGroup parent) {
100737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        final RecipientEntry entry = getEntries().get(position);
10086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1009d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin        final String constraint = mCurrentConstraint == null ? null :
1010d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin                mCurrentConstraint.toString();
1011d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin
1012b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return mDropdownChipLayouter.bindView(convertView, parent, entry, position,
1013d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin                AdapterType.BASE_RECIPIENT, constraint);
1014d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
101516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp
101616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    public Account getAccount() {
101716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        return mAccount;
101816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    }
10198af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein
10208af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    @Override
102150429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein    public void onPhotoBytesPopulated() {
102250429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein        // Default implementation does nothing
102350429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein    }
102450429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein
102550429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein    @Override
10268af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    public void onPhotoBytesAsynchronouslyPopulated() {
10278af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein        notifyDataSetChanged();
10288af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein    }
10290efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao
10300efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao    @Override
10310efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao    public void onPhotoBytesAsyncLoadFailed() {
103250429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein        // Default implementation does nothing
10330efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao    }
10346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa}
1035