BaseRecipientAdapter.java revision b10d1c652d0416c284d9792fc9a0a92b3acd51ca
16ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa/*
26ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Copyright (C) 2011 The Android Open Source Project
36ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *
46ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License");
56ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * you may not use this file except in compliance with the License.
66ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * You may obtain a copy of the License at
76ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *
86ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *      http://www.apache.org/licenses/LICENSE-2.0
96ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa *
106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS,
126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * See the License for the specific language governing permissions and
146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * limitations under the License.
156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa */
166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawapackage com.android.ex.chips;
186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.accounts.Account;
206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.ContentResolver;
216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.Context;
226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.pm.PackageManager;
236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.pm.PackageManager.NameNotFoundException;
246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.content.res.Resources;
256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.database.Cursor;
266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.net.Uri;
2734f5320d620877f757ed78a6e37754bbeabee5aaDaisuke Miyakawaimport android.os.AsyncTask;
286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.os.Handler;
294bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawaimport android.os.Message;
306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract;
3174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Photo;
326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract.Directory;
331650ef6bb75ba88340cc59d22b2578c0f460b4f1Scott Kennedyimport android.support.v4.util.LruCache;
346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.text.TextUtils;
356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.text.util.Rfc822Token;
366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.util.Log;
37d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawaimport android.view.LayoutInflater;
386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.view.View;
396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.view.ViewGroup;
406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.AutoCompleteTextView;
416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.BaseAdapter;
426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.Filter;
436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.Filterable;
44b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
45b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Linimport com.android.ex.chips.DropdownChipLayouter.AdapterType;
466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
47c88702e49917a21b9753d587800de25f87f2c5a8Jay Shraunerimport java.io.ByteArrayOutputStream;
48c88702e49917a21b9753d587800de25f87f2c5a8Jay Shraunerimport java.io.IOException;
49b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Linimport java.io.InputStream;
506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.ArrayList;
5174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport java.util.HashSet;
52cf9337aa4b2d844f4941d75832f52266c4fc3fa7Daisuke Miyakawaimport java.util.LinkedHashMap;
536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.List;
546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.Map;
5574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport java.util.Set;
566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa/**
586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Adapter for showing a recipient list.
596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa */
60df4457285cf0a54d957f1fad3bbc07112f750818Mindy Pereirapublic abstract class BaseRecipientAdapter extends BaseAdapter implements Filterable,
61941187c70e06e977eb80f3ccaccd421148faadaeMindy Pereira        AccountSpecifier {
626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final String TAG = "BaseRecipientAdapter";
638383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
646b616f1d06b092825da345078ce4a899b829290dDaisuke Miyakawa    private static final boolean DEBUG = false;
656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * The preferred number of results to be retrieved. This number may be
686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * exceeded if there are several directories configured, because we will use
696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * the same limit for all directories.
706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final int DEFAULT_PREFERRED_MAX_RESULT_COUNT = 10;
726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * The number of extra entries requested to allow for duplicates. Duplicates
756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * are removed from the overall result.
766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
7716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final int ALLOWANCE_FOR_DUPLICATES = 5;
786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
797537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    // This is ContactsContract.PRIMARY_ACCOUNT_NAME. Available from ICS as hidden
8016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
817537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    // This is ContactsContract.PRIMARY_ACCOUNT_TYPE. Available from ICS as hidden
8216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account";
837537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa
8474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /** The number of photos cached in this Adapter. */
8574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private static final int PHOTO_CACHE_SIZE = 20;
8674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
874bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /**
884bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * The "Waiting for more contacts" message will be displayed if search is not complete
894bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * within this many milliseconds.
904bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     */
914bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private static final int MESSAGE_SEARCH_PENDING_DELAY = 1000;
924bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /** Used to prepare "Waiting for more contacts" message. */
934bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private static final int MESSAGE_SEARCH_PENDING = 1;
944bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
95d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public static final int QUERY_TYPE_EMAIL = 0;
96d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public static final int QUERY_TYPE_PHONE = 1;
97d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
9880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    private final Queries.Query mQuery;
9980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    private final int mQueryType;
10080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
1016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
1026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Model object for a {@link Directory} row.
1036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
1046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public final static class DirectorySearchParams {
1056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public long directoryId;
1066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String directoryType;
1076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String displayName;
1086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String accountName;
1096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String accountType;
1106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence constraint;
1116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public DirectoryFilter filter;
1126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
11474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private static class PhotoQuery {
11574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public static final String[] PROJECTION = {
11674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            Photo.PHOTO
11774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        };
11874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
11974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public static final int PHOTO = 0;
12074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
12174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
12216923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    protected static class DirectoryListQuery {
1236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final Uri URI =
1256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories");
1266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final String[] PROJECTION = {
1276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory._ID,              // 0
1286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_NAME,     // 1
1296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_TYPE,     // 2
1306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.DISPLAY_NAME,     // 3
1316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.PACKAGE_NAME,     // 4
1326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.TYPE_RESOURCE_ID, // 5
1336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        };
1346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ID = 0;
1366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_NAME = 1;
1376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_TYPE = 2;
1386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int DISPLAY_NAME = 3;
1396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int PACKAGE_NAME = 4;
1406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int TYPE_RESOURCE_ID = 5;
1416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Used to temporarily hold results in Cursor objects. */
14476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected static class TemporaryEntry {
1458383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String displayName;
1468383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String destination;
1477211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa        public final int destinationType;
1487211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa        public final String destinationLabel;
1498383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final long contactId;
1508383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final long dataId;
1518383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final String thumbnailUriString;
15200adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki        public final int displayNameSource;
153514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy        public final boolean isGalContact;
1548383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
15576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        public TemporaryEntry(
15676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String displayName,
15776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String destination,
15876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                int destinationType,
15976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String destinationLabel,
16076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                long contactId,
16176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                long dataId,
16276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert                String thumbnailUriString,
163514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                int displayNameSource,
164514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                boolean isGalContact) {
16576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.displayName = displayName;
16676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destination = destination;
16776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destinationType = destinationType;
16876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.destinationLabel = destinationLabel;
16976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.contactId = contactId;
17076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.dataId = dataId;
17176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.thumbnailUriString = thumbnailUriString;
17276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert            this.displayNameSource = displayNameSource;
173514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy            this.isGalContact = isGalContact;
17476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        }
17576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
176514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy        public TemporaryEntry(Cursor cursor, boolean isGalContact) {
17780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.displayName = cursor.getString(Queries.Query.NAME);
17880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destination = cursor.getString(Queries.Query.DESTINATION);
17980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destinationType = cursor.getInt(Queries.Query.DESTINATION_TYPE);
18080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.destinationLabel = cursor.getString(Queries.Query.DESTINATION_LABEL);
18180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.contactId = cursor.getLong(Queries.Query.CONTACT_ID);
18280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.dataId = cursor.getLong(Queries.Query.DATA_ID);
18380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            this.thumbnailUriString = cursor.getString(Queries.Query.PHOTO_THUMBNAIL_URI);
18400adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki            this.displayNameSource = cursor.getInt(Queries.Query.DISPLAY_NAME_SOURCE);
185514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy            this.isGalContact = isGalContact;
1868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
1878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
1888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
1898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /**
1908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Used to pass results from {@link DefaultFilter#performFiltering(CharSequence)} to
1918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link DefaultFilter#publishResults(CharSequence, android.widget.Filter.FilterResults)}
1928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     */
1938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private static class DefaultFilterResult {
1948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<RecipientEntry> entries;
1958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final LinkedHashMap<Long, List<RecipientEntry>> entryMap;
1968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<RecipientEntry> nonAggregatedEntries;
1978383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final Set<String> existingDestinations;
1988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public final List<DirectorySearchParams> paramsList;
1998383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2008383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        public DefaultFilterResult(List<RecipientEntry> entries,
2018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                LinkedHashMap<Long, List<RecipientEntry>> entryMap,
2028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                List<RecipientEntry> nonAggregatedEntries,
2038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Set<String> existingDestinations,
2048383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                List<DirectorySearchParams> paramsList) {
2058383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.entries = entries;
2068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.entryMap = entryMap;
2078383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.nonAggregatedEntries = nonAggregatedEntries;
2088383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.existingDestinations = existingDestinations;
2098383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            this.paramsList = paramsList;
2108383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
2118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
2128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
2146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter used for loading two data sets: email rows from the local
2156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * contact provider and the list of {@link Directory}'s.
2166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
2176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final class DefaultFilter extends Filter {
2186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
2206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
2218383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
2228383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "start filtering. constraint: " + constraint + ", thread:"
2238383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + Thread.currentThread());
2248383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
2258383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
22674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
2278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Cursor defaultDirectoryCursor = null;
2288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Cursor directoryCursor = null;
2298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (TextUtils.isEmpty(constraint)) {
23137726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                clearTempEntries();
2328383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // Return empty results.
2338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                return results;
2346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
2356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            try {
2378383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                defaultDirectoryCursor = doQuery(constraint, mPreferredMaxResultCount, null);
23880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
2398383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultDirectoryCursor == null) {
2408383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (DEBUG) {
2418383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        Log.w(TAG, "null cursor returned for default Email filter query.");
2428383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
2438383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                } else {
2448383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // These variables will become mEntries, mEntryMap, mNonAggregatedEntries, and
2458383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // mExistingDestinations. Here we shouldn't use those member variables directly
2468383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // since this method is run outside the UI thread.
2478383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final LinkedHashMap<Long, List<RecipientEntry>> entryMap =
2488383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            new LinkedHashMap<Long, List<RecipientEntry>>();
2498383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final List<RecipientEntry> nonAggregatedEntries =
2508383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            new ArrayList<RecipientEntry>();
2518383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final Set<String> existingDestinations = new HashSet<String>();
2528383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2538383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    while (defaultDirectoryCursor.moveToNext()) {
2548383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        // Note: At this point each entry doesn't contain any photo
2558383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        // (thus getPhotoBytes() returns null).
256514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                        putOneEntry(new TemporaryEntry(defaultDirectoryCursor,
257514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                                false /* isGalContact */),
2588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                true, entryMap, nonAggregatedEntries, existingDestinations);
2598383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
2608383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2618383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We'll copy this result to mEntry in publicResults() (run in the UX thread).
262f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                    final List<RecipientEntry> entries = constructEntryList(
263f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                            entryMap, nonAggregatedEntries);
2648383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
2658383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // After having local results, check the size of results. If the results are
2668383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // not enough, we search remote directories, which will take longer time.
2678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final int limit = mPreferredMaxResultCount - existingDestinations.size();
2688383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final List<DirectorySearchParams> paramsList;
2698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (limit > 0) {
2708383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        if (DEBUG) {
2718383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            Log.d(TAG, "More entries should be needed (current: "
2728383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                    + existingDestinations.size()
2738383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                    + ", remaining limit: " + limit + ") ");
2748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        }
2758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        directoryCursor = mContentResolver.query(
2768383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                DirectoryListQuery.URI, DirectoryListQuery.PROJECTION,
2778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                null, null, null);
27816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                        paramsList = setupOtherDirectories(mContext, directoryCursor, mAccount);
2798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    } else {
2808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        // We don't need to search other directories.
2818383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        paramsList = null;
2828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
2836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.values = new DefaultFilterResult(
2858383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            entries, entryMap, nonAggregatedEntries,
2868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            existingDestinations, paramsList);
2878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.count = 1;
2888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
2898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            } finally {
2908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultDirectoryCursor != null) {
2918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    defaultDirectoryCursor.close();
2928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
2938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (directoryCursor != null) {
2948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    directoryCursor.close();
2958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
29674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
2976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
2986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
3016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
3028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // If a user types a string very quickly and database is slow, "constraint" refers to
3038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // an older text which shows inconsistent results for users obsolete (b/4998713).
3048383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // TODO: Fix it.
3058383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mCurrentConstraint = constraint;
3068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
30737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook            clearTempEntries();
30837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
3096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (results.values != null) {
3108383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                DefaultFilterResult defaultFilterResult = (DefaultFilterResult) results.values;
3118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mEntryMap = defaultFilterResult.entryMap;
3128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mNonAggregatedEntries = defaultFilterResult.nonAggregatedEntries;
3138383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mExistingDestinations = defaultFilterResult.existingDestinations;
3148383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
31537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // If there are no local results, in the new result set, cache off what had been
31637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // shown to the user for use until the first directory result is returned
31737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                if (defaultFilterResult.entries.size() == 0 &&
31837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                        defaultFilterResult.paramsList != null) {
31937726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                    cacheCurrentEntries();
32037726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                }
32137726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
3228383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                updateEntries(defaultFilterResult.entries);
3238383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3248383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // We need to search other remote directories, doing other Filter requests.
3258383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (defaultFilterResult.paramsList != null) {
3268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final int limit = mPreferredMaxResultCount -
3278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            defaultFilterResult.existingDestinations.size();
3288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    startSearchOtherDirectories(constraint, defaultFilterResult.paramsList, limit);
3298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
3306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
3356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence convertResultToString(Object resultValue) {
336f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            final RecipientEntry entry = (RecipientEntry)resultValue;
3376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String displayName = entry.getDisplayName();
3386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String emailAddress = entry.getDestination();
3396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, emailAddress)) {
3406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                 return emailAddress;
3416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
3426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                return new Rfc822Token(displayName, emailAddress, null).toString();
3436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
3486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter that performs search in a particular directory.
3496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
35076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected class DirectoryFilter extends Filter {
35174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        private final DirectorySearchParams mParams;
3526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        private int mLimit;
3536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
35474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public DirectoryFilter(DirectorySearchParams params) {
3558383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mParams = params;
3566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized void setLimit(int limit) {
3596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            this.mLimit = limit;
3606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized int getLimit() {
3636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return this.mLimit;
3646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
3676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
3688383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
3698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "DirectoryFilter#performFiltering. directoryId: " + mParams.directoryId
3708383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + ", constraint: " + constraint + ", thread: " + Thread.currentThread());
3718383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
37274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
3738383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            results.values = null;
3748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            results.count = 0;
3758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (!TextUtils.isEmpty(constraint)) {
3778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                final ArrayList<TemporaryEntry> tempEntries = new ArrayList<TemporaryEntry>();
3788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
3798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Cursor cursor = null;
3808383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                try {
3818383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We don't want to pass this Cursor object to UI thread (b/5017608).
3828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // Assuming the result should contain fairly small results (at most ~10),
3838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    // We just copy everything to local structure.
3848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    cursor = doQuery(constraint, getLimit(), mParams.directoryId);
38580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
3868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (cursor != null) {
3878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        while (cursor.moveToNext()) {
388514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                            tempEntries.add(new TemporaryEntry(cursor, true /* isGalContact */));
3898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        }
3908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
3918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                } finally {
3928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (cursor != null) {
3938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        cursor.close();
3948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
39574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
3968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (!tempEntries.isEmpty()) {
3978383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.values = tempEntries;
3988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    results.count = 1;
3998383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
4008383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
4038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.v(TAG, "finished loading directory \"" + mParams.displayName + "\"" +
4048383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        " with query " + constraint);
4056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
4066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
4086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
4116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
4128383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (DEBUG) {
4138383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                Log.d(TAG, "DirectoryFilter#publishResult. constraint: " + constraint
4148383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        + ", mCurrentConstraint: " + mCurrentConstraint);
4158383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4168383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            mDelayedMessageHandler.removeDelayedLoadMessage();
4178383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // Check if the received result matches the current constraint
4188383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // If not - the user must have continued typing after the request was issued, which
4198383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // means several member variables (like mRemainingDirectoryLoad) are already
4208383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // overwritten so shouldn't be touched here anymore.
4218383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            if (TextUtils.equals(constraint, mCurrentConstraint)) {
4228383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (results.count > 0) {
423dfd7e0774f9a4f47c127b21e5468430374340f66Andy Huang                    @SuppressWarnings("unchecked")
4248383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    final ArrayList<TemporaryEntry> tempEntries =
4258383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                            (ArrayList<TemporaryEntry>) results.values;
4268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    for (TemporaryEntry tempEntry : tempEntries) {
4288383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        putOneEntry(tempEntry, mParams.directoryId == Directory.DEFAULT,
4298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                mEntryMap, mNonAggregatedEntries, mExistingDestinations);
4308383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
4318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
4328383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4338383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                // If there are remaining directories, set up delayed message again.
4348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                mRemainingDirectoryCount--;
4358383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                if (mRemainingDirectoryCount > 0) {
4368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    if (DEBUG) {
4378383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        Log.d(TAG, "Resend delayed load message. Current mRemainingDirectoryLoad: "
4388383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                                + mRemainingDirectoryCount);
4398383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    }
4408383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    mDelayedMessageHandler.sendDelayedLoadMessage();
4418383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                }
44237726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
44337726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // If this directory result has some items, or there are no more directories that
44437726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                // we are waiting for, clear the temp results
44537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                if (results.count > 0 || mRemainingDirectoryCount == 0) {
44637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                    // Clear the temp entries
44737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                    clearTempEntries();
44837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook                }
4498383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            }
4508383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
4518383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // Show the list again without "waiting" message.
452f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy            updateEntries(constructEntryList(mEntryMap, mNonAggregatedEntries));
4536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
4556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
45674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final Context mContext;
4576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final ContentResolver mContentResolver;
458d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private final LayoutInflater mInflater;
4596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private Account mAccount;
46074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final int mPreferredMaxResultCount;
461b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    private DropdownChipLayouter mDropdownChipLayouter;
4626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
4648383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mEntries} is responsible for showing every result for this Adapter. To
4658383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * construct it, we use {@link #mEntryMap}, {@link #mNonAggregatedEntries}, and
4668383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mExistingDestinations}.
4678383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     *
4688383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * First, each destination (an email address or a phone number) with a valid contactId is
4698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * inserted into {@link #mEntryMap} and grouped by the contactId. Destinations without valid
4708383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * contactId (possible if they aren't in local storage) are stored in
4718383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mNonAggregatedEntries}.
47274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Duplicates are removed using {@link #mExistingDestinations}.
47374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     *
4748383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * After having all results from Cursor objects, all destinations in mEntryMap are copied to
4758383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mEntries}. If the number of destinations is not enough (i.e. less than
4768383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link #mPreferredMaxResultCount}), destinations in mNonAggregatedEntries are also used.
4778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     *
4788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * These variables are only used in UI thread, thus should not be touched in
4798383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * performFiltering() methods.
4806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
4818383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private LinkedHashMap<Long, List<RecipientEntry>> mEntryMap;
4828383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> mNonAggregatedEntries;
4838383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private Set<String> mExistingDestinations;
4848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Note: use {@link #updateEntries(List)} to update this variable. */
4858383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> mEntries;
48637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    private List<RecipientEntry> mTempEntries;
4876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4884bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /** The number of directories this adapter is waiting for results. */
4894bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private int mRemainingDirectoryCount;
4904bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
49174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /**
4928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Used to ignore asynchronous queries with a different constraint, which may happen when
49374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * users type characters quickly.
49474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     */
49574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private CharSequence mCurrentConstraint;
49674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
49774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final LruCache<Uri, byte[]> mPhotoCacheMap;
4986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
499d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
5004bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * Handler specific for maintaining "Waiting for more contacts" message, which will be shown
5014bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * when:
5024bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * - there are directories to be searched
5034bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     * - results from directories are slow to come
5044bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa     */
5054bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private final class DelayedMessageHandler extends Handler {
5064bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        @Override
5074bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void handleMessage(Message msg) {
5084bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            if (mRemainingDirectoryCount > 0) {
509f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy                updateEntries(constructEntryList(mEntryMap, mNonAggregatedEntries));
5104bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            }
5114bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5124bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5134bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void sendDelayedLoadMessage() {
5144bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            sendMessageDelayed(obtainMessage(MESSAGE_SEARCH_PENDING, 0, 0, null),
5154bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa                    MESSAGE_SEARCH_PENDING_DELAY);
5164bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5174bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5184bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        public void removeDelayedLoadMessage() {
5194bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa            removeMessages(MESSAGE_SEARCH_PENDING);
5204bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        }
5214bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    }
5224bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5234bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    private final DelayedMessageHandler mDelayedMessageHandler = new DelayedMessageHandler();
5244bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
5258c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    private EntriesUpdatedObserver mEntriesUpdatedObserver;
5268c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
5274bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa    /**
528d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Constructor for email queries.
529d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
5306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public BaseRecipientAdapter(Context context) {
53180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT, QUERY_TYPE_EMAIL);
5326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
5348383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    public BaseRecipientAdapter(Context context, int preferredMaxResultCount) {
53580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, preferredMaxResultCount, QUERY_TYPE_EMAIL);
53680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
53780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
53880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(int queryMode, Context context) {
53980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT, queryMode);
54080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
54180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
54280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(int queryMode, Context context, int preferredMaxResultCount) {
54380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        this(context, preferredMaxResultCount, queryMode);
54480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
54580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
54680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public BaseRecipientAdapter(Context context, int preferredMaxResultCount, int queryMode) {
5476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContext = context;
5486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContentResolver = context.getContentResolver();
549d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        mInflater = LayoutInflater.from(context);
5506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mPreferredMaxResultCount = preferredMaxResultCount;
55174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoCacheMap = new LruCache<Uri, byte[]>(PHOTO_CACHE_SIZE);
55280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        mQueryType = queryMode;
55380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
55480f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        if (queryMode == QUERY_TYPE_EMAIL) {
55580f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            mQuery = Queries.EMAIL;
55680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        } else if (queryMode == QUERY_TYPE_PHONE) {
55780f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            mQuery = Queries.PHONE;
55880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        } else {
55980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            mQuery = Queries.EMAIL;
56080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor            Log.e(TAG, "Unsupported query type: " + queryMode);
56180f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        }
56280f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    }
56380f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor
56476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    public Context getContext() {
56576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        return mContext;
56676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    }
56776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
56880f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor    public int getQueryType() {
56980f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        return mQueryType;
5706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
572b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    public void setDropdownChipLayouter(DropdownChipLayouter dropdownChipLayouter) {
573b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        mDropdownChipLayouter = dropdownChipLayouter;
574b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        mDropdownChipLayouter.setQuery(mQuery);
575b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
576b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
577b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    public DropdownChipLayouter getDropdownChipLayouter() {
578b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return mDropdownChipLayouter;
579b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin    }
580b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin
5816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
5826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Set the account when known. Causes the search to prioritize contacts from that account.
5836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
584dfd7e0774f9a4f47c127b21e5468430374340f66Andy Huang    @Override
5856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public void setAccount(Account account) {
5866ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mAccount = account;
5876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5886ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
5896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /** Will be called from {@link AutoCompleteTextView} to prepare auto-complete list. */
5906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
5916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public Filter getFilter() {
5926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return new DefaultFilter();
5936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
59576f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    /**
59676f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * An extesion to {@link RecipientAlternatesAdapter#getMatchingRecipients} that allows
59776f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * additional sources of contacts to be considered as matching recipients.
59876f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * @param addresses A set of addresses to be matched
59976f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     * @return A list of matches or null if none found
60076f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert     */
60176f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    public Map<String, RecipientEntry> getMatchingRecipients(Set<String> addresses) {
60276f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert        return null;
60376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    }
60476f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert
60516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    public static List<DirectorySearchParams> setupOtherDirectories(Context context,
60616923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            Cursor directoryCursor, Account account) {
60716923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        final PackageManager packageManager = context.getPackageManager();
60874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final List<DirectorySearchParams> paramsList = new ArrayList<DirectorySearchParams>();
6096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        DirectorySearchParams preferredDirectory = null;
6106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        while (directoryCursor.moveToNext()) {
6116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final long id = directoryCursor.getLong(DirectoryListQuery.ID);
6126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // Skip the local invisible directory, because the default directory already includes
6146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // all local results.
6156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (id == Directory.LOCAL_INVISIBLE) {
6166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                continue;
6176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
6186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final DirectorySearchParams params = new DirectorySearchParams();
6206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String packageName = directoryCursor.getString(DirectoryListQuery.PACKAGE_NAME);
6216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int resourceId = directoryCursor.getInt(DirectoryListQuery.TYPE_RESOURCE_ID);
6226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.directoryId = id;
6236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.displayName = directoryCursor.getString(DirectoryListQuery.DISPLAY_NAME);
6246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountName = directoryCursor.getString(DirectoryListQuery.ACCOUNT_NAME);
6256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountType = directoryCursor.getString(DirectoryListQuery.ACCOUNT_TYPE);
6266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (packageName != null && resourceId != 0) {
6276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                try {
6286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    final Resources resources =
6296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            packageManager.getResourcesForApplication(packageName);
6306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    params.directoryType = resources.getString(resourceId);
6316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    if (params.directoryType == null) {
6326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                        Log.e(TAG, "Cannot resolve directory name: "
6336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                                + resourceId + "@" + packageName);
6346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    }
6356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                } catch (NameNotFoundException e) {
6366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    Log.e(TAG, "Cannot resolve directory name: "
6376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            + resourceId + "@" + packageName, e);
6386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
6396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
6406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // If an account has been provided and we found a directory that
6426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // corresponds to that account, place that directory second, directly
6436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // underneath the local contacts.
64416923ee63a79fce4be3f62b08bcd1f80617c1205mindyp            if (account != null && account.name.equals(params.accountName) &&
64516923ee63a79fce4be3f62b08bcd1f80617c1205mindyp                    account.type.equals(params.accountType)) {
6466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                preferredDirectory = params;
6476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
64874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                paramsList.add(params);
6496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
6506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
6516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        if (preferredDirectory != null) {
65374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            paramsList.add(1, preferredDirectory);
6546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
6556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
65674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return paramsList;
6576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
6608383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * Starts search in other directories using {@link Filter}. Results will be handled in
6618383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * {@link DirectoryFilter}.
6626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
66376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected void startSearchOtherDirectories(
66474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            CharSequence constraint, List<DirectorySearchParams> paramsList, int limit) {
66574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final int count = paramsList.size();
6666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        // Note: skipping the default partition (index 0), which has already been loaded
6676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        for (int i = 1; i < count; i++) {
66874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final DirectorySearchParams params = paramsList.get(i);
66974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.constraint = constraint;
67074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (params.filter == null) {
67174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                params.filter = new DirectoryFilter(params);
67274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
67374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.setLimit(limit);
67474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.filter(constraint);
67574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
6764bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa
6778383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        // Directory search started. We may show "waiting" message if directory results are slow
6788383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        // enough.
6794bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        mRemainingDirectoryCount = count - 1;
6804bb6a342f5aa1f38b0e0083d014e538e937eccceDaisuke Miyakawa        mDelayedMessageHandler.sendDelayedLoadMessage();
68174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
68274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
683f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy    private static void putOneEntry(TemporaryEntry entry, boolean isAggregatedEntry,
6848383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            LinkedHashMap<Long, List<RecipientEntry>> entryMap,
6858383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            List<RecipientEntry> nonAggregatedEntries,
6868383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Set<String> existingDestinations) {
6878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (existingDestinations.contains(entry.destination)) {
6888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            return;
6898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
69074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
6918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        existingDestinations.add(entry.destination);
6928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
6938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (!isAggregatedEntry) {
6948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            nonAggregatedEntries.add(RecipientEntry.constructTopLevelEntry(
6957211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
69600adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
6977211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
698514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                    entry.contactId, entry.dataId, entry.thumbnailUriString, true,
699514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                    entry.isGalContact));
7008383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        } else if (entryMap.containsKey(entry.contactId)) {
7018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            // We already have a section for the person.
7028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            final List<RecipientEntry> entryList = entryMap.get(entry.contactId);
7038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryList.add(RecipientEntry.constructSecondLevelEntry(
7047211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
70500adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
7067211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
707514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                    entry.contactId, entry.dataId, entry.thumbnailUriString, true,
708514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                    entry.isGalContact));
7098383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        } else {
7108383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            final List<RecipientEntry> entryList = new ArrayList<RecipientEntry>();
7118383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryList.add(RecipientEntry.constructTopLevelEntry(
7127211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.displayName,
71300adb32f3cea49ec82467c0e1a9e42659b556836Makoto Onuki                    entry.displayNameSource,
7147211747e51623ae1305053f533c09dd335e013a2Daisuke Miyakawa                    entry.destination, entry.destinationType, entry.destinationLabel,
715514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                    entry.contactId, entry.dataId, entry.thumbnailUriString, true,
716514f8a75f72fa2e735418ffb5d6e30aa914fbe7bScott Kennedy                    entry.isGalContact));
7178383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            entryMap.put(entry.contactId, entryList);
7186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
7196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
7206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
72274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Constructs an actual list for this Adapter using {@link #mEntryMap}. Also tries to
72374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * fetch a cached photo for each contact entry (other than separators), or request another
7248383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa     * thread to get one from directories.
7256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
7268383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private List<RecipientEntry> constructEntryList(
7278383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            LinkedHashMap<Long, List<RecipientEntry>> entryMap,
728f7e202d8b83bfbd73ca47ba7843ebc4dd57c2fa4Scott Kennedy            List<RecipientEntry> nonAggregatedEntries) {
7298383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final List<RecipientEntry> entries = new ArrayList<RecipientEntry>();
73074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        int validEntryCount = 0;
7318383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        for (Map.Entry<Long, List<RecipientEntry>> mapEntry : entryMap.entrySet()) {
732f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            final List<RecipientEntry> entryList = mapEntry.getValue();
7336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int size = entryList.size();
7346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            for (int i = 0; i < size; i++) {
735f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                RecipientEntry entry = entryList.get(i);
7368383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                entries.add(entry);
73774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                tryFetchPhoto(entry);
73874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
7396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
74074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (validEntryCount > mPreferredMaxResultCount) {
74174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                break;
74274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
7436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
74474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (validEntryCount <= mPreferredMaxResultCount) {
7458383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            for (RecipientEntry entry : nonAggregatedEntries) {
74674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (validEntryCount > mPreferredMaxResultCount) {
74774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    break;
74874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
7498383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                entries.add(entry);
75074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                tryFetchPhoto(entry);
75174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
75274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
75374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
75474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
75574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
7568383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        return entries;
7578383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    }
7588383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa
7598c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
7608c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    protected interface EntriesUpdatedObserver {
7618c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        public void onChanged(List<RecipientEntry> entries);
7628c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    }
7638c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
7648c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    public void registerUpdateObserver(EntriesUpdatedObserver observer) {
7658c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        mEntriesUpdatedObserver = observer;
7668c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp    }
7678c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp
7688383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    /** Resets {@link #mEntries} and notify the event to its parent ListView. */
7698383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa    private void updateEntries(List<RecipientEntry> newEntries) {
7708383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        mEntries = newEntries;
7718c474ec629cca3cf4bf2c867f37513ae35e3ff72mindyp        mEntriesUpdatedObserver.onChanged(newEntries);
7726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        notifyDataSetChanged();
7736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
7746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
77537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    private void cacheCurrentEntries() {
77637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        mTempEntries = mEntries;
77737726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
77837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
77937726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    private void clearTempEntries() {
78037726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        mTempEntries = null;
78137726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
78237726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
78376f1f2d4eb66956814254b0e4f86da7aa8261bcbAlon Albert    protected List<RecipientEntry> getEntries() {
78437726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return mTempEntries != null ? mTempEntries : mEntries;
78537726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook    }
78637726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook
787f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira    private void tryFetchPhoto(final RecipientEntry entry) {
78874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final Uri photoThumbnailUri = entry.getPhotoThumbnailUri();
78974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (photoThumbnailUri != null) {
79074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final byte[] photoBytes = mPhotoCacheMap.get(photoThumbnailUri);
79174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (photoBytes != null) {
79274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                entry.setPhotoBytes(photoBytes);
79374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                // notifyDataSetChanged() should be called by a caller.
79474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            } else {
79574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (DEBUG) {
79674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    Log.d(TAG, "No photo cache for " + entry.getDisplayName()
79774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            + ". Fetch one asynchronously");
7986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
79974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                fetchPhotoAsync(entry, photoThumbnailUri);
8006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
8016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
8026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
8036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
804c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner    // For reading photos for directory contacts, this is the chunksize for
805c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner    // copying from the inputstream to the output stream.
806c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner    private static final int BUFFER_SIZE = 1024*16;
807c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner
808f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira    private void fetchPhotoAsync(final RecipientEntry entry, final Uri photoThumbnailUri) {
809c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner        final AsyncTask<Void, Void, byte[]> photoLoadTask = new AsyncTask<Void, Void, byte[]>() {
81074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            @Override
811c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner            protected byte[] doInBackground(Void... params) {
812c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                // First try running a query. Images for local contacts are
813c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                // loaded by sending a query to the ContactsProvider.
81474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                final Cursor photoCursor = mContentResolver.query(
81574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        photoThumbnailUri, PhotoQuery.PROJECTION, null, null, null);
81674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (photoCursor != null) {
81774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    try {
81874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        if (photoCursor.moveToFirst()) {
819c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            return photoCursor.getBlob(PhotoQuery.PHOTO);
82074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        }
82174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    } finally {
82274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        photoCursor.close();
82374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    }
824c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                } else {
825c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    // If the query fails, try streaming the URI directly.
826c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    // For remote directory images, this URI resolves to the
827c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    // directory provider and the images are loaded by sending
828c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    // an openFile call to the provider.
829c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    try {
830c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                        InputStream is = mContentResolver.openInputStream(
831c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                photoThumbnailUri);
832c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                        if (is != null) {
833c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            byte[] buffer = new byte[BUFFER_SIZE];
834c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
835c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            try {
836c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                int size;
837c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                while ((size = is.read(buffer)) != -1) {
838c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                    baos.write(buffer, 0, size);
839c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                }
840c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            } finally {
841c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                                is.close();
842c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            }
843c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                            return baos.toByteArray();
844c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                        }
845c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    } catch (IOException ex) {
846c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                        // ignore
847c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    }
84874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
84934f5320d620877f757ed78a6e37754bbeabee5aaDaisuke Miyakawa                return null;
85074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
851c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner
852c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner            @Override
853c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner            protected void onPostExecute(final byte[] photoBytes) {
854c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                entry.setPhotoBytes(photoBytes);
855c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                if (photoBytes != null) {
856c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
857c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                    notifyDataSetChanged();
858c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner                }
859c88702e49917a21b9753d587800de25f87f2c5a8Jay Shrauner            }
86034f5320d620877f757ed78a6e37754bbeabee5aaDaisuke Miyakawa        };
86134f5320d620877f757ed78a6e37754bbeabee5aaDaisuke Miyakawa        photoLoadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
86274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
86374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
8646b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira    protected void fetchPhoto(final RecipientEntry entry, final Uri photoThumbnailUri) {
8656b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        byte[] photoBytes = mPhotoCacheMap.get(photoThumbnailUri);
8666b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        if (photoBytes != null) {
8676b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            entry.setPhotoBytes(photoBytes);
8686b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            return;
8696b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        }
8706b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        final Cursor photoCursor = mContentResolver.query(photoThumbnailUri, PhotoQuery.PROJECTION,
8716b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                null, null, null);
8726b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        if (photoCursor != null) {
8736b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            try {
8746b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                if (photoCursor.moveToFirst()) {
8756b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                    photoBytes = photoCursor.getBlob(PhotoQuery.PHOTO);
8766b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                    entry.setPhotoBytes(photoBytes);
8776b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
8786b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                }
8796b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            } finally {
8806b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                photoCursor.close();
8816b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            }
8826b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        }
8836b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira    }
8846b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira
885d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private Cursor doQuery(CharSequence constraint, int limit, Long directoryId) {
88680f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor        final Uri.Builder builder = mQuery.getContentFilterUri().buildUpon()
8878383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                .appendPath(constraint.toString())
8888383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
8898383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                        String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES));
8908383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (directoryId != null) {
8918383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
8928383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    String.valueOf(directoryId));
8938383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
8948383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (mAccount != null) {
8958383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(PRIMARY_ACCOUNT_NAME, mAccount.name);
8968383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            builder.appendQueryParameter(PRIMARY_ACCOUNT_TYPE, mAccount.type);
8978383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        }
8988383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final long start = System.currentTimeMillis();
8998383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final Cursor cursor = mContentResolver.query(
90080f4abfb682426384e88fb1dddc682be1c8a6c7fTom Taylor                builder.build(), mQuery.getProjection(), null, null, null);
9018383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        final long end = System.currentTimeMillis();
9028383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        if (DEBUG) {
9038383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa            Log.d(TAG, "Time for autocomplete (query: " + constraint
9048383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + ", directoryId: " + directoryId + ", num_of_results: "
9058383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + (cursor != null ? cursor.getCount() : "null") + "): "
9068383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa                    + (end - start) + " ms");
907d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        }
908d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return cursor;
909d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
910d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
911cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    // TODO: This won't be used at all. We should find better way to quit the thread..
912cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    /*public void close() {
9138383f44fc5608259474a24e398b2ee028f729db8Daisuke Miyakawa        mEntries = null;
91474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoCacheMap.evictAll();
915cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa        if (!sPhotoHandlerThread.quit()) {
91674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            Log.w(TAG, "Failed to quit photo handler thread, ignoring it.");
91774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
918cc20880f80a3a6f6e4aaef4547ed088d9364e741Daisuke Miyakawa    }*/
9196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
9216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public int getCount() {
92237726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        final List<RecipientEntry> entries = getEntries();
92337726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return entries != null ? entries.size() : 0;
9246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
927858e094f1c695aefdf6a23f522c0f16d81bd79f7Scott Kennedy    public RecipientEntry getItem(int position) {
92837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position);
9296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
9326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public long getItemId(int position) {
9336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return position;
9346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
9356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
9366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
93774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getViewTypeCount() {
938f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira        return RecipientEntry.ENTRY_TYPE_SIZE;
93974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
94074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
94174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
94274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getItemViewType(int position) {
94337726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position).getEntryType();
94474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
9456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
94674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
9476d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    public boolean isEnabled(int position) {
94837726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        return getEntries().get(position).isSelectable();
9496d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    }
9506d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa
9516d6bd68a492fbbfca50ae4793313083105e6f270Daisuke Miyakawa    @Override
95274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public View getView(int position, View convertView, ViewGroup parent) {
95337726c00b6d53f7afd0c31b8f8f1805976ba722aPaul Westbrook        final RecipientEntry entry = getEntries().get(position);
9546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
955b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin        return mDropdownChipLayouter.bindView(convertView, parent, entry, position,
956b10d1c652d0416c284d9792fc9a0a92b3acd51caKevin Lin                AdapterType.BASE_RECIPIENT, mCurrentConstraint.toString());
957d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
95816923ee63a79fce4be3f62b08bcd1f80617c1205mindyp
95916923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    public Account getAccount() {
96016923ee63a79fce4be3f62b08bcd1f80617c1205mindyp        return mAccount;
96116923ee63a79fce4be3f62b08bcd1f80617c1205mindyp    }
9626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa}
963