BaseRecipientAdapter.java revision fa7b0fb73f80ceafdf3ff3260b345b61d2766f93
16ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa/*
26ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Copyright (C) 2011 The Android Open Source Project
36ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *
46ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License");
56ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * you may not use this file except in compliance with the License.
66ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * You may obtain a copy of the License at
76ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *
86ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *      http://www.apache.org/licenses/LICENSE-2.0
96ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *
106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS,
126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * See the License for the specific language governing permissions and
146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * limitations under the License.
156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa */
166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawapackage com.android.ex.chips;
186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.accounts.Account;
206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.ContentResolver;
216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.Context;
226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.pm.PackageManager;
236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.pm.PackageManager.NameNotFoundException;
246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.res.Resources;
256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.database.Cursor;
267a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedyimport android.graphics.Bitmap;
277a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedyimport android.graphics.BitmapFactory;
286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.net.Uri;
2934f5320d620877f757ed78a6e37754bbeabee5aaDaisuke Miyakawaimport android.os.AsyncTask;
306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.os.Handler;
314bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawaimport android.os.Message;
326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract;
3374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Photo;
346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract.Directory;
351650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedyimport android.support.v4.util.LruCache;
366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.text.TextUtils;
376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.text.util.Rfc822Token;
386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.util.Log;
39d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawaimport android.view.LayoutInflater;
406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.view.View;
416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.view.ViewGroup;
426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.AutoCompleteTextView;
436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.BaseAdapter;
446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.Filter;
456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.Filterable;
46b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
47b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Linimport com.android.ex.chips.DropdownChipLayouter.AdapterType;
486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
49c88702e49917a21b9753d587800de25f87f2c5a8Jay Shraunerimport java.io.ByteArrayOutputStream;
507a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedyimport java.io.FileNotFoundException;
51c88702e49917a21b9753d587800de25f87f2c5a8Jay Shraunerimport java.io.IOException;
52b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Linimport java.io.InputStream;
536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.ArrayList;
54fa7b0fb73f80ceafdf3ff3260b345b61d2766f93Scott Kennedyimport java.util.Collections;
5574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport java.util.HashSet;
56cf9337aa4b2d844f4941d75832f52266c4fc3fa7Daisuke Miyakawaimport java.util.LinkedHashMap;
576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.List;
586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.Map;
5974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport java.util.Set;
606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa/**
626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Adapter for showing a recipient list.
636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa */
64bb7e7e1b8ff6163816fa04d50ca9382d569bee82Scott Kennedypublic class BaseRecipientAdapter extends BaseAdapter implements Filterable, AccountSpecifier {
656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final String TAG = "BaseRecipientAdapter";
668383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
676b616f1d06b092825da345078ce4a899b829290dDaisuke Miyakawa    private static final boolean DEBUG = false;
686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * The preferred number of results to be retrieved. This number may be
716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * exceeded if there are several directories configured, because we will use
726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * the same limit for all directories.
736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final int DEFAULT_PREFERRED_MAX_RESULT_COUNT = 10;
756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * The number of extra entries requested to allow for duplicates. Duplicates
786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * are removed from the overall result.
796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
8016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final int ALLOWANCE_FOR_DUPLICATES = 5;
816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
827537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    // This is ContactsContract.PRIMARY_ACCOUNT_NAME. Available from ICS as hidden
8316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
847537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    // This is ContactsContract.PRIMARY_ACCOUNT_TYPE. Available from ICS as hidden
8516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account";
867537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa
8774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /** The number of photos cached in this Adapter. */
8874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private static final int PHOTO_CACHE_SIZE = 20;
8974a977cbc35413866ca039d509801fe0b02e1901Daisuke 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
10180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    private final Queries.Query mQuery;
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
11774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private static class PhotoQuery {
11874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public static final String[] PROJECTION = {
11974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            Photo.PHOTO
12074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        };
12174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
12274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public static final int PHOTO = 0;
12374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
12474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
12516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    protected static class DirectoryListQuery {
1266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final Uri URI =
1286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories");
1296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final String[] PROJECTION = {
1306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory._ID,              // 0
1316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_NAME,     // 1
1326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_TYPE,     // 2
1336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.DISPLAY_NAME,     // 3
1346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.PACKAGE_NAME,     // 4
1356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.TYPE_RESOURCE_ID, // 5
1366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        };
1376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ID = 0;
1396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_NAME = 1;
1406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_TYPE = 2;
1416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int DISPLAY_NAME = 3;
1426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int PACKAGE_NAME = 4;
1436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int TYPE_RESOURCE_ID = 5;
1446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1468383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Used to temporarily hold results in Cursor objects. */
14776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected static class TemporaryEntry {
1488383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String displayName;
1498383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String destination;
1507211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa        public final int destinationType;
1517211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa        public final String destinationLabel;
1528383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final long contactId;
1537a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        public final Long directoryId;
1548383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final long dataId;
1558383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String thumbnailUriString;
15600adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki        public final int displayNameSource;
1577a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        public final String lookupKey;
1588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
15976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        public TemporaryEntry(
16076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String displayName,
16176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String destination,
16276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                int destinationType,
16376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String destinationLabel,
16476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                long contactId,
1657a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                Long directoryId,
16676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                long dataId,
16776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String thumbnailUriString,
168514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                int displayNameSource,
1697a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                String lookupKey) {
17076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.displayName = displayName;
17176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destination = destination;
17276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destinationType = destinationType;
17376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destinationLabel = destinationLabel;
17476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.contactId = contactId;
1757a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.directoryId = directoryId;
17676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.dataId = dataId;
17776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.thumbnailUriString = thumbnailUriString;
17876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.displayNameSource = displayNameSource;
1797a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.lookupKey = lookupKey;
18076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        }
18176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
1827a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        public TemporaryEntry(Cursor cursor, Long directoryId) {
18380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.displayName = cursor.getString(Queries.Query.NAME);
18480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destination = cursor.getString(Queries.Query.DESTINATION);
18580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destinationType = cursor.getInt(Queries.Query.DESTINATION_TYPE);
18680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destinationLabel = cursor.getString(Queries.Query.DESTINATION_LABEL);
18780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.contactId = cursor.getLong(Queries.Query.CONTACT_ID);
1887a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.directoryId = directoryId;
18980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.dataId = cursor.getLong(Queries.Query.DATA_ID);
19080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.thumbnailUriString = cursor.getString(Queries.Query.PHOTO_THUMBNAIL_URI);
19100adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki            this.displayNameSource = cursor.getInt(Queries.Query.DISPLAY_NAME_SOURCE);
1927a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            this.lookupKey = cursor.getString(Queries.Query.LOOKUP_KEY);
1938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
1948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
1958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
1968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /**
1978383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Used to pass results from {@link DefaultFilter#performFiltering(CharSequence)} to
1988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link DefaultFilter#publishResults(CharSequence, android.widget.Filter.FilterResults)}
1998383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     */
2008383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private static class DefaultFilterResult {
2018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<RecipientEntry> entries;
2028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final LinkedHashMap<Long, List<RecipientEntry>> entryMap;
2038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<RecipientEntry> nonAggregatedEntries;
2048383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final Set<String> existingDestinations;
2058383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<DirectorySearchParams> paramsList;
2068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2078383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public DefaultFilterResult(List<RecipientEntry> entries,
2088383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                LinkedHashMap<Long, List<RecipientEntry>> entryMap,
2098383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                List<RecipientEntry> nonAggregatedEntries,
2108383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Set<String> existingDestinations,
2118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                List<DirectorySearchParams> paramsList) {
2128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.entries = entries;
2138383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.entryMap = entryMap;
2148383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.nonAggregatedEntries = nonAggregatedEntries;
2158383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.existingDestinations = existingDestinations;
2168383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.paramsList = paramsList;
2178383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
2188383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
2198383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
2216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter used for loading two data sets: email rows from the local
2226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * contact provider and the list of {@link Directory}'s.
2236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
2246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final class DefaultFilter extends Filter {
2256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
2276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
2288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
2298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "start filtering. constraint: " + constraint + ", thread:"
2308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + Thread.currentThread());
2318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
2328383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
23374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
2348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Cursor defaultDirectoryCursor = null;
2358383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Cursor directoryCursor = null;
2368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2378383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (TextUtils.isEmpty(constraint)) {
23837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                clearTempEntries();
2398383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // Return empty results.
2408383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                return results;
2416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
2426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            try {
2447a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                defaultDirectoryCursor = doQuery(constraint, mPreferredMaxResultCount,
2457a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        null /* directoryId */);
24680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
2478383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultDirectoryCursor == null) {
2488383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (DEBUG) {
2498383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        Log.w(TAG, "null cursor returned for default Email filter query.");
2508383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
2518383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                } else {
2528383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // These variables will become mEntries, mEntryMap, mNonAggregatedEntries, and
2538383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // mExistingDestinations. Here we shouldn't use those member variables directly
2548383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // since this method is run outside the UI thread.
2558383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final LinkedHashMap<Long, List<RecipientEntry>> entryMap =
2568383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            new LinkedHashMap<Long, List<RecipientEntry>>();
2578383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final List<RecipientEntry> nonAggregatedEntries =
2588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            new ArrayList<RecipientEntry>();
2598383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final Set<String> existingDestinations = new HashSet<String>();
2608383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2618383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    while (defaultDirectoryCursor.moveToNext()) {
2628383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        // Note: At this point each entry doesn't contain any photo
2638383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        // (thus getPhotoBytes() returns null).
264514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                        putOneEntry(new TemporaryEntry(defaultDirectoryCursor,
2657a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                                null /* directoryId */),
2668383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                true, entryMap, nonAggregatedEntries, existingDestinations);
2678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
2688383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We'll copy this result to mEntry in publicResults() (run in the UX thread).
270f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                    final List<RecipientEntry> entries = constructEntryList(
271f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                            entryMap, nonAggregatedEntries);
2728383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2738383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // After having local results, check the size of results. If the results are
2748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // not enough, we search remote directories, which will take longer time.
2758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final int limit = mPreferredMaxResultCount - existingDestinations.size();
2768383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final List<DirectorySearchParams> paramsList;
2778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (limit > 0) {
2788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        if (DEBUG) {
2798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            Log.d(TAG, "More entries should be needed (current: "
2808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                    + existingDestinations.size()
2818383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                    + ", remaining limit: " + limit + ") ");
2828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        }
2838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        directoryCursor = mContentResolver.query(
2848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                DirectoryListQuery.URI, DirectoryListQuery.PROJECTION,
2858383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                null, null, null);
28616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        paramsList = setupOtherDirectories(mContext, directoryCursor, mAccount);
2878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    } else {
2888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        // We don't need to search other directories.
2898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        paramsList = null;
2908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
2916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.values = new DefaultFilterResult(
2938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            entries, entryMap, nonAggregatedEntries,
2948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            existingDestinations, paramsList);
2958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.count = 1;
2968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
2978383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            } finally {
2988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultDirectoryCursor != null) {
2998383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    defaultDirectoryCursor.close();
3008383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
3018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (directoryCursor != null) {
3028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    directoryCursor.close();
3038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
30474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
3056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
3066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
3096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
3108383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // If a user types a string very quickly and database is slow, "constraint" refers to
3118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // an older text which shows inconsistent results for users obsolete (b/4998713).
3128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // TODO: Fix it.
3138383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mCurrentConstraint = constraint;
3148383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
31537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook            clearTempEntries();
31637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
3176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (results.values != null) {
3188383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                DefaultFilterResult defaultFilterResult = (DefaultFilterResult) results.values;
3198383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mEntryMap = defaultFilterResult.entryMap;
3208383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mNonAggregatedEntries = defaultFilterResult.nonAggregatedEntries;
3218383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mExistingDestinations = defaultFilterResult.existingDestinations;
3228383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
32337726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // If there are no local results, in the new result set, cache off what had been
32437726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // shown to the user for use until the first directory result is returned
32537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                if (defaultFilterResult.entries.size() == 0 &&
32637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                        defaultFilterResult.paramsList != null) {
32737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                    cacheCurrentEntries();
32837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                }
32937726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
3308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                updateEntries(defaultFilterResult.entries);
3318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3328383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // We need to search other remote directories, doing other Filter requests.
3338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultFilterResult.paramsList != null) {
3348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final int limit = mPreferredMaxResultCount -
3358383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            defaultFilterResult.existingDestinations.size();
3368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    startSearchOtherDirectories(constraint, defaultFilterResult.paramsList, limit);
3378383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
338fa7b0fb73f80ceafdf3ff3260b345b61d2766f93Scott Kennedy            } else {
339fa7b0fb73f80ceafdf3ff3260b345b61d2766f93Scott Kennedy                updateEntries(Collections.<RecipientEntry>emptyList());
3406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
3446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence convertResultToString(Object resultValue) {
345f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            final RecipientEntry entry = (RecipientEntry)resultValue;
3466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String displayName = entry.getDisplayName();
3476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String emailAddress = entry.getDestination();
3486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, emailAddress)) {
3496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                 return emailAddress;
3506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
3516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                return new Rfc822Token(displayName, emailAddress, null).toString();
3526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
3576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter that performs search in a particular directory.
3586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
35976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected class DirectoryFilter extends Filter {
36074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        private final DirectorySearchParams mParams;
3616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        private int mLimit;
3626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
36374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public DirectoryFilter(DirectorySearchParams params) {
3648383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mParams = params;
3656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized void setLimit(int limit) {
3686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            this.mLimit = limit;
3696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized int getLimit() {
3726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return this.mLimit;
3736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
3766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
3778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
3788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "DirectoryFilter#performFiltering. directoryId: " + mParams.directoryId
3798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + ", constraint: " + constraint + ", thread: " + Thread.currentThread());
3808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
38174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
3828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            results.values = null;
3838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            results.count = 0;
3848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (!TextUtils.isEmpty(constraint)) {
3868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                final ArrayList<TemporaryEntry> tempEntries = new ArrayList<TemporaryEntry>();
3878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Cursor cursor = null;
3898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                try {
3908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We don't want to pass this Cursor object to UI thread (b/5017608).
3918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // Assuming the result should contain fairly small results (at most ~10),
3928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We just copy everything to local structure.
3938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    cursor = doQuery(constraint, getLimit(), mParams.directoryId);
39480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
3958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (cursor != null) {
3968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        while (cursor.moveToNext()) {
3977a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                            tempEntries.add(new TemporaryEntry(cursor, mParams.directoryId));
3988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        }
3998383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
4008383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                } finally {
4018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (cursor != null) {
4028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        cursor.close();
4038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
40474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
4058383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (!tempEntries.isEmpty()) {
4068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.values = tempEntries;
4078383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.count = 1;
4088383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
4098383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4108383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
4128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.v(TAG, "finished loading directory \"" + mParams.displayName + "\"" +
4138383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        " with query " + constraint);
4146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
4156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
4176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
4206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
4218383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
4228383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "DirectoryFilter#publishResult. constraint: " + constraint
4238383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + ", mCurrentConstraint: " + mCurrentConstraint);
4248383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4258383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mDelayedMessageHandler.removeDelayedLoadMessage();
4268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // Check if the received result matches the current constraint
4278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // If not - the user must have continued typing after the request was issued, which
4288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // means several member variables (like mRemainingDirectoryLoad) are already
4298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // overwritten so shouldn't be touched here anymore.
4308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (TextUtils.equals(constraint, mCurrentConstraint)) {
4318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (results.count > 0) {
432dfd7e0774f9a4f47c127b21e5468430374340f66Andy Huang                    @SuppressWarnings("unchecked")
4338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final ArrayList<TemporaryEntry> tempEntries =
4348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            (ArrayList<TemporaryEntry>) results.values;
4358383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    for (TemporaryEntry tempEntry : tempEntries) {
4378383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        putOneEntry(tempEntry, mParams.directoryId == Directory.DEFAULT,
4388383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                mEntryMap, mNonAggregatedEntries, mExistingDestinations);
4398383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
4408383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
4418383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4428383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // If there are remaining directories, set up delayed message again.
4438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mRemainingDirectoryCount--;
4448383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (mRemainingDirectoryCount > 0) {
4458383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (DEBUG) {
4468383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        Log.d(TAG, "Resend delayed load message. Current mRemainingDirectoryLoad: "
4478383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                + mRemainingDirectoryCount);
4488383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
4498383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    mDelayedMessageHandler.sendDelayedLoadMessage();
4508383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
45137726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
45237726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // If this directory result has some items, or there are no more directories that
45337726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // we are waiting for, clear the temp results
45437726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                if (results.count > 0 || mRemainingDirectoryCount == 0) {
45537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                    // Clear the temp entries
45637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                    clearTempEntries();
45737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                }
4588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4598383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4608383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // Show the list again without "waiting" message.
461f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy            updateEntries(constructEntryList(mEntryMap, mNonAggregatedEntries));
4626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
4646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
46574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final Context mContext;
4666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final ContentResolver mContentResolver;
467d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private final LayoutInflater mInflater;
4686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private Account mAccount;
46974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final int mPreferredMaxResultCount;
470b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    private DropdownChipLayouter mDropdownChipLayouter;
4716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
4738383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mEntries} is responsible for showing every result for this Adapter. To
4748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * construct it, we use {@link #mEntryMap}, {@link #mNonAggregatedEntries}, and
4758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mExistingDestinations}.
4768383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     *
4778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * First, each destination (an email address or a phone number) with a valid contactId is
4788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * inserted into {@link #mEntryMap} and grouped by the contactId. Destinations without valid
4798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * contactId (possible if they aren't in local storage) are stored in
4808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mNonAggregatedEntries}.
48174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Duplicates are removed using {@link #mExistingDestinations}.
48274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     *
4838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * After having all results from Cursor objects, all destinations in mEntryMap are copied to
4848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mEntries}. If the number of destinations is not enough (i.e. less than
4858383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mPreferredMaxResultCount}), destinations in mNonAggregatedEntries are also used.
4868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     *
4878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * These variables are only used in UI thread, thus should not be touched in
4888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * performFiltering() methods.
4896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
4908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private LinkedHashMap<Long, List<RecipientEntry>> mEntryMap;
4918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> mNonAggregatedEntries;
4928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private Set<String> mExistingDestinations;
4938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Note: use {@link #updateEntries(List)} to update this variable. */
4948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> mEntries;
49537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    private List<RecipientEntry> mTempEntries;
4966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4974bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /** The number of directories this adapter is waiting for results. */
4984bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private int mRemainingDirectoryCount;
4994bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
50074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /**
5018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Used to ignore asynchronous queries with a different constraint, which may happen when
50274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * users type characters quickly.
50374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     */
50474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private CharSequence mCurrentConstraint;
50574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
50674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final LruCache<Uri, byte[]> mPhotoCacheMap;
5076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
508d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
5094bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * Handler specific for maintaining "Waiting for more contacts" message, which will be shown
5104bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * when:
5114bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * - there are directories to be searched
5124bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * - results from directories are slow to come
5134bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     */
5144bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private final class DelayedMessageHandler extends Handler {
5154bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        @Override
5164bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void handleMessage(Message msg) {
5174bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            if (mRemainingDirectoryCount > 0) {
518f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                updateEntries(constructEntryList(mEntryMap, mNonAggregatedEntries));
5194bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            }
5204bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5214bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5224bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void sendDelayedLoadMessage() {
5234bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            sendMessageDelayed(obtainMessage(MESSAGE_SEARCH_PENDING, 0, 0, null),
5244bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa                    MESSAGE_SEARCH_PENDING_DELAY);
5254bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5264bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5274bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void removeDelayedLoadMessage() {
5284bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            removeMessages(MESSAGE_SEARCH_PENDING);
5294bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5304bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    }
5314bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5324bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private final DelayedMessageHandler mDelayedMessageHandler = new DelayedMessageHandler();
5334bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5348c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    private EntriesUpdatedObserver mEntriesUpdatedObserver;
5358c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
5364bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /**
537d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Constructor for email queries.
538d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
5396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public BaseRecipientAdapter(Context context) {
54080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT, QUERY_TYPE_EMAIL);
5416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
5438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    public BaseRecipientAdapter(Context context, int preferredMaxResultCount) {
54480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, preferredMaxResultCount, QUERY_TYPE_EMAIL);
54580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
54680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
54780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(int queryMode, Context context) {
54880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT, queryMode);
54980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
55080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
55180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(int queryMode, Context context, int preferredMaxResultCount) {
55280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, preferredMaxResultCount, queryMode);
55380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
55480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
55580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(Context context, int preferredMaxResultCount, int queryMode) {
5566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContext = context;
5576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContentResolver = context.getContentResolver();
558d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        mInflater = LayoutInflater.from(context);
5596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mPreferredMaxResultCount = preferredMaxResultCount;
56074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoCacheMap = new LruCache<Uri, byte[]>(PHOTO_CACHE_SIZE);
56180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        mQueryType = queryMode;
56280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
56380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        if (queryMode == QUERY_TYPE_EMAIL) {
56480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            mQuery = Queries.EMAIL;
56580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        } else if (queryMode == QUERY_TYPE_PHONE) {
56680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            mQuery = Queries.PHONE;
56780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        } else {
56880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            mQuery = Queries.EMAIL;
56980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            Log.e(TAG, "Unsupported query type: " + queryMode);
57080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        }
57180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
57280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
57376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    public Context getContext() {
57476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        return mContext;
57576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    }
57676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
57780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public int getQueryType() {
57880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        return mQueryType;
5796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
581b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    public void setDropdownChipLayouter(DropdownChipLayouter dropdownChipLayouter) {
582b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        mDropdownChipLayouter = dropdownChipLayouter;
583b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        mDropdownChipLayouter.setQuery(mQuery);
584b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
585b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
586b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    public DropdownChipLayouter getDropdownChipLayouter() {
587b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return mDropdownChipLayouter;
588b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
589b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
5906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
5916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Set the account when known. Causes the search to prioritize contacts from that account.
5926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
593dfd7e0774f9a4f47c127b21e5468430374340f66Andy Huang    @Override
5946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public void setAccount(Account account) {
5956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mAccount = account;
5966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
5986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /** Will be called from {@link AutoCompleteTextView} to prepare auto-complete list. */
5996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
6006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public Filter getFilter() {
6016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return new DefaultFilter();
6026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
60476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    /**
60576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * An extesion to {@link RecipientAlternatesAdapter#getMatchingRecipients} that allows
60676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * additional sources of contacts to be considered as matching recipients.
60776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * @param addresses A set of addresses to be matched
60876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * @return A list of matches or null if none found
60976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     */
61076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    public Map<String, RecipientEntry> getMatchingRecipients(Set<String> addresses) {
61176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        return null;
61276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    }
61376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
61416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    public static List<DirectorySearchParams> setupOtherDirectories(Context context,
61516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            Cursor directoryCursor, Account account) {
61616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        final PackageManager packageManager = context.getPackageManager();
61774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final List<DirectorySearchParams> paramsList = new ArrayList<DirectorySearchParams>();
6186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        DirectorySearchParams preferredDirectory = null;
6196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        while (directoryCursor.moveToNext()) {
6206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final long id = directoryCursor.getLong(DirectoryListQuery.ID);
6216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // Skip the local invisible directory, because the default directory already includes
6236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // all local results.
6246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (id == Directory.LOCAL_INVISIBLE) {
6256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                continue;
6266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
6276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final DirectorySearchParams params = new DirectorySearchParams();
6296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String packageName = directoryCursor.getString(DirectoryListQuery.PACKAGE_NAME);
6306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int resourceId = directoryCursor.getInt(DirectoryListQuery.TYPE_RESOURCE_ID);
6316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.directoryId = id;
6326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.displayName = directoryCursor.getString(DirectoryListQuery.DISPLAY_NAME);
6336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountName = directoryCursor.getString(DirectoryListQuery.ACCOUNT_NAME);
6346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountType = directoryCursor.getString(DirectoryListQuery.ACCOUNT_TYPE);
6356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (packageName != null && resourceId != 0) {
6366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                try {
6376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    final Resources resources =
6386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            packageManager.getResourcesForApplication(packageName);
6396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    params.directoryType = resources.getString(resourceId);
6406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    if (params.directoryType == null) {
6416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                        Log.e(TAG, "Cannot resolve directory name: "
6426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                                + resourceId + "@" + packageName);
6436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    }
6446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                } catch (NameNotFoundException e) {
6456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    Log.e(TAG, "Cannot resolve directory name: "
6466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            + resourceId + "@" + packageName, e);
6476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
6486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
6496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // If an account has been provided and we found a directory that
6516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // corresponds to that account, place that directory second, directly
6526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // underneath the local contacts.
65316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            if (account != null && account.name.equals(params.accountName) &&
65416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    account.type.equals(params.accountType)) {
6556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                preferredDirectory = params;
6566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
65774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                paramsList.add(params);
6586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
6596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
6606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        if (preferredDirectory != null) {
66274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            paramsList.add(1, preferredDirectory);
6636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
6646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
66574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return paramsList;
6666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
6698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Starts search in other directories using {@link Filter}. Results will be handled in
6708383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link DirectoryFilter}.
6716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
67276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected void startSearchOtherDirectories(
67374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            CharSequence constraint, List<DirectorySearchParams> paramsList, int limit) {
67474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final int count = paramsList.size();
6756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        // Note: skipping the default partition (index 0), which has already been loaded
6766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        for (int i = 1; i < count; i++) {
67774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final DirectorySearchParams params = paramsList.get(i);
67874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.constraint = constraint;
67974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (params.filter == null) {
68074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                params.filter = new DirectoryFilter(params);
68174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
68274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.setLimit(limit);
68374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.filter(constraint);
68474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
6854bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
6868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        // Directory search started. We may show "waiting" message if directory results are slow
6878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        // enough.
6884bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        mRemainingDirectoryCount = count - 1;
6894bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        mDelayedMessageHandler.sendDelayedLoadMessage();
69074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
69174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
692f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy    private static void putOneEntry(TemporaryEntry entry, boolean isAggregatedEntry,
6938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            LinkedHashMap<Long, List<RecipientEntry>> entryMap,
6948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            List<RecipientEntry> nonAggregatedEntries,
6958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Set<String> existingDestinations) {
6968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (existingDestinations.contains(entry.destination)) {
6978383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            return;
6988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
69974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
7008383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        existingDestinations.add(entry.destination);
7018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
7028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (!isAggregatedEntry) {
7038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            nonAggregatedEntries.add(RecipientEntry.constructTopLevelEntry(
7047211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
70500adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
7067211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
7077a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
7087a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    true, entry.lookupKey));
7098383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        } else if (entryMap.containsKey(entry.contactId)) {
7108383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // We already have a section for the person.
7118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            final List<RecipientEntry> entryList = entryMap.get(entry.contactId);
7128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryList.add(RecipientEntry.constructSecondLevelEntry(
7137211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
71400adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
7157211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
7167a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
7177a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    true, entry.lookupKey));
7188383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        } else {
7198383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            final List<RecipientEntry> entryList = new ArrayList<RecipientEntry>();
7208383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryList.add(RecipientEntry.constructTopLevelEntry(
7217211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
72200adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
7237211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
7247a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
7257a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    true, entry.lookupKey));
7268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryMap.put(entry.contactId, entryList);
7276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
7286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
7296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
73174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Constructs an actual list for this Adapter using {@link #mEntryMap}. Also tries to
73274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * fetch a cached photo for each contact entry (other than separators), or request another
7338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * thread to get one from directories.
7346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
7358383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> constructEntryList(
7368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            LinkedHashMap<Long, List<RecipientEntry>> entryMap,
737f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy            List<RecipientEntry> nonAggregatedEntries) {
7388383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final List<RecipientEntry> entries = new ArrayList<RecipientEntry>();
73974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        int validEntryCount = 0;
7408383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        for (Map.Entry<Long, List<RecipientEntry>> mapEntry : entryMap.entrySet()) {
741f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            final List<RecipientEntry> entryList = mapEntry.getValue();
7426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int size = entryList.size();
7436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            for (int i = 0; i < size; i++) {
744f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                RecipientEntry entry = entryList.get(i);
7458383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                entries.add(entry);
74674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                tryFetchPhoto(entry);
74774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
7486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
74974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (validEntryCount > mPreferredMaxResultCount) {
75074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                break;
75174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
7526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
75374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (validEntryCount <= mPreferredMaxResultCount) {
7548383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            for (RecipientEntry entry : nonAggregatedEntries) {
75574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (validEntryCount > mPreferredMaxResultCount) {
75674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    break;
75774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
7588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                entries.add(entry);
75974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                tryFetchPhoto(entry);
76074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
76174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
76274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
76374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
76474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
7658383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        return entries;
7668383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
7678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
7688c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
76938fe984ca3e9ed7af3d5441ee3a7e82dbfdb3d2cMike Schneider    public interface EntriesUpdatedObserver {
7708c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        public void onChanged(List<RecipientEntry> entries);
7718c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    }
7728c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
7738c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    public void registerUpdateObserver(EntriesUpdatedObserver observer) {
7748c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        mEntriesUpdatedObserver = observer;
7758c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    }
7768c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
7778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Resets {@link #mEntries} and notify the event to its parent ListView. */
7788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private void updateEntries(List<RecipientEntry> newEntries) {
7798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        mEntries = newEntries;
7808c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        mEntriesUpdatedObserver.onChanged(newEntries);
7816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        notifyDataSetChanged();
7826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
7836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
78437726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    private void cacheCurrentEntries() {
78537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        mTempEntries = mEntries;
78637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
78737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
78837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    private void clearTempEntries() {
78937726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        mTempEntries = null;
79037726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
79137726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
79276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected List<RecipientEntry> getEntries() {
79337726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return mTempEntries != null ? mTempEntries : mEntries;
79437726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
79537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
796f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira    private void tryFetchPhoto(final RecipientEntry entry) {
79774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final Uri photoThumbnailUri = entry.getPhotoThumbnailUri();
79874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (photoThumbnailUri != null) {
79974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final byte[] photoBytes = mPhotoCacheMap.get(photoThumbnailUri);
80074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (photoBytes != null) {
80174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                entry.setPhotoBytes(photoBytes);
80274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                // notifyDataSetChanged() should be called by a caller.
80374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            } else {
80474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (DEBUG) {
80574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    Log.d(TAG, "No photo cache for " + entry.getDisplayName()
80674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            + ". Fetch one asynchronously");
8076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
80874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                fetchPhotoAsync(entry, photoThumbnailUri);
8096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
8106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
8116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
8126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
813c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner    // For reading photos for directory contacts, this is the chunksize for
814c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner    // copying from the inputstream to the output stream.
815c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner    private static final int BUFFER_SIZE = 1024*16;
816c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner
817f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira    private void fetchPhotoAsync(final RecipientEntry entry, final Uri photoThumbnailUri) {
818c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner        final AsyncTask<Void, Void, byte[]> photoLoadTask = new AsyncTask<Void, Void, byte[]>() {
81974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            @Override
820c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner            protected byte[] doInBackground(Void... params) {
821c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                // First try running a query. Images for local contacts are
822c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                // loaded by sending a query to the ContactsProvider.
82374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                final Cursor photoCursor = mContentResolver.query(
82474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        photoThumbnailUri, PhotoQuery.PROJECTION, null, null, null);
82574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (photoCursor != null) {
82674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    try {
82774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        if (photoCursor.moveToFirst()) {
828c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            return photoCursor.getBlob(PhotoQuery.PHOTO);
82974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        }
83074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    } finally {
83174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        photoCursor.close();
83274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    }
833c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                } else {
834c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    // If the query fails, try streaming the URI directly.
835c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    // For remote directory images, this URI resolves to the
836c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    // directory provider and the images are loaded by sending
837c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    // an openFile call to the provider.
838c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    try {
839c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                        InputStream is = mContentResolver.openInputStream(
840c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                photoThumbnailUri);
841c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                        if (is != null) {
842c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            byte[] buffer = new byte[BUFFER_SIZE];
843c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
844c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            try {
845c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                int size;
846c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                while ((size = is.read(buffer)) != -1) {
847c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                    baos.write(buffer, 0, size);
848c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                }
849c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            } finally {
850c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                is.close();
851c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            }
852c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            return baos.toByteArray();
853c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                        }
854c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    } catch (IOException ex) {
855c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                        // ignore
856c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    }
85774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
85834f5320d620877f757ed78a6e37754bbeabee5aaDaisuke Miyakawa                return null;
85974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
860c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner
861c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner            @Override
862c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner            protected void onPostExecute(final byte[] photoBytes) {
863c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                entry.setPhotoBytes(photoBytes);
864c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                if (photoBytes != null) {
865c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
866c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    notifyDataSetChanged();
867c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                }
868c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner            }
86934f5320d620877f757ed78a6e37754bbeabee5aaDaisuke Miyakawa        };
87034f5320d620877f757ed78a6e37754bbeabee5aaDaisuke Miyakawa        photoLoadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
87174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
87274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
8736b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira    protected void fetchPhoto(final RecipientEntry entry, final Uri photoThumbnailUri) {
8746b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        byte[] photoBytes = mPhotoCacheMap.get(photoThumbnailUri);
8756b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        if (photoBytes != null) {
8766b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            entry.setPhotoBytes(photoBytes);
8776b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            return;
8786b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        }
8796b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        final Cursor photoCursor = mContentResolver.query(photoThumbnailUri, PhotoQuery.PROJECTION,
8806b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                null, null, null);
8816b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        if (photoCursor != null) {
8826b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            try {
8836b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                if (photoCursor.moveToFirst()) {
8846b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                    photoBytes = photoCursor.getBlob(PhotoQuery.PHOTO);
8856b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                    entry.setPhotoBytes(photoBytes);
8866b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
8876b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                }
8886b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            } finally {
8896b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                photoCursor.close();
8906b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            }
8917a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy        } else {
8927a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            InputStream inputStream = null;
8937a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            ByteArrayOutputStream outputStream = null;
8947a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            try {
8957a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                inputStream = mContentResolver.openInputStream(photoThumbnailUri);
8967a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
8977a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
8987a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                if (bitmap != null) {
8997a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    outputStream = new ByteArrayOutputStream();
9007a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
9017a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    photoBytes = outputStream.toByteArray();
9027a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy
9037a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    entry.setPhotoBytes(photoBytes);
9047a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
9057a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                }
9067a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            } catch (final FileNotFoundException e) {
9077a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                Log.w(TAG, "Error opening InputStream for photo", e);
9087a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            } finally {
9097a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                try {
9107a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    if (inputStream != null) {
9117a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        inputStream.close();
9127a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    }
9137a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                } catch (IOException e) {
9147a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    Log.e(TAG, "Error closing photo input stream", e);
9157a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                }
9167a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                try {
9177a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    if (outputStream != null) {
9187a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                        outputStream.close();
9197a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    }
9207a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                } catch (IOException e) {
9217a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                    Log.e(TAG, "Error closing photo output stream", e);
9227a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy                }
9237a4e67708498ec46c2e9b3bad69d3807d88c064eScott Kennedy            }
9246b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        }
9256b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira    }
9266b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira
927d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private Cursor doQuery(CharSequence constraint, int limit, Long directoryId) {
92880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        final Uri.Builder builder = mQuery.getContentFilterUri().buildUpon()
9298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                .appendPath(constraint.toString())
9308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
9318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES));
9328383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (directoryId != null) {
9338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
9348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    String.valueOf(directoryId));
9358383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
9368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (mAccount != null) {
9378383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(PRIMARY_ACCOUNT_NAME, mAccount.name);
9388383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(PRIMARY_ACCOUNT_TYPE, mAccount.type);
9398383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
9408383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final long start = System.currentTimeMillis();
9418383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final Cursor cursor = mContentResolver.query(
94280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                builder.build(), mQuery.getProjection(), null, null, null);
9438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final long end = System.currentTimeMillis();
9448383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (DEBUG) {
9458383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Log.d(TAG, "Time for autocomplete (query: " + constraint
9468383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + ", directoryId: " + directoryId + ", num_of_results: "
9478383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + (cursor != null ? cursor.getCount() : "null") + "): "
9488383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + (end - start) + " ms");
949d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        }
950d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return cursor;
951d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
952d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
953cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    // TODO: This won't be used at all. We should find better way to quit the thread..
954cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    /*public void close() {
9558383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        mEntries = null;
95674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoCacheMap.evictAll();
957cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa        if (!sPhotoHandlerThread.quit()) {
95874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            Log.w(TAG, "Failed to quit photo handler thread, ignoring it.");
95974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
960cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    }*/
9616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
9636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public int getCount() {
96437726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        final List<RecipientEntry> entries = getEntries();
96537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return entries != null ? entries.size() : 0;
9666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
969858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy    public RecipientEntry getItem(int position) {
97037726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position);
9716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
9746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public long getItemId(int position) {
9756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return position;
9766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
97974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getViewTypeCount() {
980f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira        return RecipientEntry.ENTRY_TYPE_SIZE;
98174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
98274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
98374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
98474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getItemViewType(int position) {
98537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position).getEntryType();
98674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
9876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
98874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
9896d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    public boolean isEnabled(int position) {
99037726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position).isSelectable();
9916d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    }
9926d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa
9936d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    @Override
99474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public View getView(int position, View convertView, ViewGroup parent) {
99537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        final RecipientEntry entry = getEntries().get(position);
9966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
997d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin        final String constraint = mCurrentConstraint == null ? null :
998d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin                mCurrentConstraint.toString();
999d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin
1000b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return mDropdownChipLayouter.bindView(convertView, parent, entry, position,
1001d5ce95bdc1ab57d91cc5408fda49d23d9630da37Kevin Lin                AdapterType.BASE_RECIPIENT, constraint);
1002d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
100316923ee63a79fce4be3f62b08bcd1f80617c1205mindyp
100416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    public Account getAccount() {
100516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        return mAccount;
100616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    }
10076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa}
1008