BaseRecipientAdapter.java revision 6b6de6266d3bede33728cf995f1fd5c59ec5a55d
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.graphics.Bitmap;
276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.graphics.BitmapFactory;
286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.net.Uri;
296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.os.Handler;
3074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport android.os.HandlerThread;
316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract;
326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Email;
33d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Phone;
3474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Photo;
356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract.Contacts;
366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.provider.ContactsContract.Directory;
376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.text.TextUtils;
386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.text.util.Rfc822Token;
396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.util.Log;
4074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport android.util.LruCache;
41d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawaimport android.view.LayoutInflater;
426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.view.View;
436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.view.ViewGroup;
446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.AutoCompleteTextView;
456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.BaseAdapter;
466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.Filter;
476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.Filterable;
486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.ImageView;
496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport android.widget.TextView;
506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.ArrayList;
5274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport java.util.HashSet;
53cf9337aa4b2d844f4941d75832f52266c4fc3fa7Daisuke Miyakawaimport java.util.LinkedHashMap;
546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.List;
556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawaimport java.util.Map;
5674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawaimport java.util.Set;
576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa/**
596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa * Adapter for showing a recipient list.
606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa */
61efcac0cbb3efc645cd6cf1cb1e2431e1bd2b2d2aMindy Pereirapublic abstract class BaseRecipientAdapter extends BaseAdapter implements Filterable {
626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final String TAG = "BaseRecipientAdapter";
6374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private static final boolean DEBUG = false;
646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * The preferred number of results to be retrieved. This number may be
676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * exceeded if there are several directories configured, because we will use
686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * the same limit for all directories.
696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final int DEFAULT_PREFERRED_MAX_RESULT_COUNT = 10;
716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * The number of extra entries requested to allow for duplicates. Duplicates
746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * are removed from the overall result.
756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static final int ALLOWANCE_FOR_DUPLICATES = 5;
776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
787537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    // This is ContactsContract.PRIMARY_ACCOUNT_NAME. Available from ICS as hidden
797537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    private static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
807537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    // This is ContactsContract.PRIMARY_ACCOUNT_TYPE. Available from ICS as hidden
817537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa    private static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account";
827537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa
8374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /** The number of photos cached in this Adapter. */
8474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private static final int PHOTO_CACHE_SIZE = 20;
8574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
86d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public static final int QUERY_TYPE_EMAIL = 0;
87d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public static final int QUERY_TYPE_PHONE = 1;
88d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Model object for a {@link Directory} row.
916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public final static class DirectorySearchParams {
936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public long directoryId;
946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String directoryType;
956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String displayName;
966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String accountName;
976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String accountType;
986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence constraint;
996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public DirectoryFilter filter;
1006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
10284dd9a88f0d9984996e951e59e73cb76cd2eaa5eMindy Pereira    /* package */ static class EmailQuery {
1036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final String[] PROJECTION = {
1046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Contacts.DISPLAY_NAME,       // 0
1056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Email.DATA,                  // 1
1066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Email.CONTACT_ID,            // 2
1077537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa            Email._ID,                   // 3
1087537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa            Contacts.PHOTO_THUMBNAIL_URI // 4
1096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        };
1106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int NAME = 0;
1126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ADDRESS = 1;
1136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int CONTACT_ID = 2;
1147537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa        public static final int DATA_ID = 3;
1157537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa        public static final int PHOTO_THUMBNAIL_URI = 4;
1166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
118d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private static class PhoneQuery {
119d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        public static final String[] PROJECTION = {
120d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            Contacts.DISPLAY_NAME,       // 0
121d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            Phone.DATA,                  // 1
122d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            Phone.CONTACT_ID,            // 2
1237537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa            Phone._ID,                   // 3
1247537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa            Contacts.PHOTO_THUMBNAIL_URI // 4
125d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        };
126d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        public static final int NAME = 0;
127d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        public static final int NUMBER = 1;
128d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        public static final int CONTACT_ID = 2;
1297537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa        public static final int DATA_ID = 3;
130d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        public static final int PHOTO_THUMBNAIL_URI = 3;
131d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
1326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
13374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private static class PhotoQuery {
13474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public static final String[] PROJECTION = {
13574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            Photo.PHOTO
13674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        };
13774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
13874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public static final int PHOTO = 0;
13974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
14074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
1416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static class DirectoryListQuery {
1426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final Uri URI =
1446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories");
1456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final String[] PROJECTION = {
1466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory._ID,              // 0
1476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_NAME,     // 1
1486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_TYPE,     // 2
1496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.DISPLAY_NAME,     // 3
1506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.PACKAGE_NAME,     // 4
1516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.TYPE_RESOURCE_ID, // 5
1526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        };
1536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ID = 0;
1556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_NAME = 1;
1566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_TYPE = 2;
1576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int DISPLAY_NAME = 3;
1586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int PACKAGE_NAME = 4;
1596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int TYPE_RESOURCE_ID = 5;
1606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
1636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter used for loading two data sets: email rows from the local
1646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * contact provider and the list of {@link Directory}'s.
1656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
1666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final class DefaultFilter extends Filter {
1676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
1696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
17074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
1716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Cursor cursor = null;
1726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (!TextUtils.isEmpty(constraint)) {
173d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                cursor = doQuery(constraint, mPreferredMaxResultCount, null);
1746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                if (cursor != null) {
1756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    results.count = cursor.getCount();
1766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
1776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
1786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // TODO: implement group feature
1806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final Cursor directoryCursor = mContentResolver.query(
1826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    DirectoryListQuery.URI, DirectoryListQuery.PROJECTION, null, null, null);
1836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
18474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (DEBUG && cursor == null) {
18574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                Log.w(TAG, "null cursor returned for default Email filter query.");
18674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
1876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            results.values = new Cursor[] { directoryCursor, cursor };
1886ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
1896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
1906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
1926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
1936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (results.values != null) {
1946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                final Cursor[] cursors = (Cursor[]) results.values;
1956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                // Run on one thread.
1966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                mHandler.post(new Runnable() {
1976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    @Override
1986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    public void run() {
1996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                        onFirstDirectoryLoadFinished(constraint, cursors[0], cursors[1]);
2006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    }
2016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                });
2026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
2036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            results.count = getCount();
2046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
2076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence convertResultToString(Object resultValue) {
208f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            final RecipientEntry entry = (RecipientEntry)resultValue;
2096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String displayName = entry.getDisplayName();
2106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String emailAddress = entry.getDestination();
2116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, emailAddress)) {
2126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                 return emailAddress;
2136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
2146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                return new Rfc822Token(displayName, emailAddress, null).toString();
2156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
2166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
2186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
2206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter that performs search in a particular directory.
2216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
2226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final class DirectoryFilter extends Filter {
22374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        private final DirectorySearchParams mParams;
2246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        private int mLimit;
2256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
22674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public DirectoryFilter(DirectorySearchParams params) {
22774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            this.mParams = params;
2286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized void setLimit(int limit) {
2316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            this.mLimit = limit;
2326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized int getLimit() {
2356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return this.mLimit;
2366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
2396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
24074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
2416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (!TextUtils.isEmpty(constraint)) {
242d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                final Cursor cursor = doQuery(constraint, getLimit(), mParams.directoryId);
24374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (cursor != null) {
24474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    results.values = cursor;
24574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
2466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
2476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // TODO: implement group feature
2496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
2516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
2546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
2556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final Cursor cursor = (Cursor) results.values;
2566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            mHandler.post(new Runnable() {
2576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                @Override
2586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                public void run() {
25974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    onDirectoryLoadFinished(constraint, mParams, cursor);
2606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
2616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            });
2626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            results.count = getCount();
2636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
2656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
26674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final Context mContext;
2676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final ContentResolver mContentResolver;
268d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private final LayoutInflater mInflater;
269d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private final int mQueryType;
2706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private Account mAccount;
27174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final int mPreferredMaxResultCount;
2726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final Handler mHandler = new Handler();
2736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
27574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Each destination (an email address or a phone number) with a valid contactId is first
27674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * inserted into {@link #mEntryMap} and grouped by the contactId.
27774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Destinations without valid contactId (possible if they aren't in local storage) are stored
27874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * in {@link #mNonAggregatedEntries}.
27974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Duplicates are removed using {@link #mExistingDestinations}.
28074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     *
28174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * After having all results from ContentResolver, all elements in mEntryMap are copied to
28274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * mEntry, which will be used to find items in this Adapter. If the number of contacts in
28374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * mEntries are less than mPreferredMaxResultCount, contacts in
28474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * mNonAggregatedEntries are also used.
2856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
286f15b448bb9fd41ec7aea7cd95104ab3d82355a06Daisuke Miyakawa    private final LinkedHashMap<Long, List<RecipientEntry>> mEntryMap;
287f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira    private final List<RecipientEntry> mNonAggregatedEntries;
288f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira    private final List<RecipientEntry> mEntries;
28974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final Set<String> mExistingDestinations;
2906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
29174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /**
29274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Used to ignore asynchronous queries with a different constraint, which may appear when
29374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * users type characters quickly.
29474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     */
29574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private CharSequence mCurrentConstraint;
29674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
29774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final HandlerThread mPhotoHandlerThread;
29874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final Handler mPhotoHandler;
29974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final LruCache<Uri, byte[]> mPhotoCacheMap;
3006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
301d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
302d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Constructor for email queries.
303d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
3046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public BaseRecipientAdapter(Context context) {
305d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        this(context, QUERY_TYPE_EMAIL, DEFAULT_PREFERRED_MAX_RESULT_COUNT);
306d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
307d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
308d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public BaseRecipientAdapter(Context context, int queryType) {
309d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        this(context, queryType, DEFAULT_PREFERRED_MAX_RESULT_COUNT);
3106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
312d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public BaseRecipientAdapter(Context context, int queryType, int preferredMaxResultCount) {
3136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContext = context;
3146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContentResolver = context.getContentResolver();
315d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        mInflater = LayoutInflater.from(context);
316d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        mQueryType = queryType;
3176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mPreferredMaxResultCount = preferredMaxResultCount;
318f15b448bb9fd41ec7aea7cd95104ab3d82355a06Daisuke Miyakawa        mEntryMap = new LinkedHashMap<Long, List<RecipientEntry>>();
319f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira        mNonAggregatedEntries = new ArrayList<RecipientEntry>();
320f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira        mEntries = new ArrayList<RecipientEntry>();
32174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mExistingDestinations = new HashSet<String>();
32274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoHandlerThread = new HandlerThread("photo_handler");
32374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoHandlerThread.start();
32474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoHandler = new Handler(mPhotoHandlerThread.getLooper());
32574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoCacheMap = new LruCache<Uri, byte[]>(PHOTO_CACHE_SIZE);
3266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
3296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Set the account when known. Causes the search to prioritize contacts from that account.
3306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
3316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public void setAccount(Account account) {
3326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mAccount = account;
3336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /** Will be called from {@link AutoCompleteTextView} to prepare auto-complete list. */
3366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
3376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public Filter getFilter() {
3386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return new DefaultFilter();
3396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
3426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Handles the result of the initial call, which brings back the list of directories as well
3436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * as the search results for the local directories.
34474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     *
34574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Must be inside a default Looper thread to avoid synchronization problem.
3466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
3476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    protected void onFirstDirectoryLoadFinished(
3486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            CharSequence constraint, Cursor directoryCursor, Cursor defaultDirectoryCursor) {
34974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mCurrentConstraint = constraint;
35074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
3516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        try {
35274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final List<DirectorySearchParams> paramsList;
3536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (directoryCursor != null) {
35474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                paramsList = setupOtherDirectories(directoryCursor);
35574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            } else {
35674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                paramsList = null;
3576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            int limit = 0;
3606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
36174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (defaultDirectoryCursor != null) {
3626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                mEntryMap.clear();
36374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                mNonAggregatedEntries.clear();
36474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                mExistingDestinations.clear();
36574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                putEntriesWithCursor(defaultDirectoryCursor, true);
3666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                constructEntryList();
3676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                limit = mPreferredMaxResultCount - getCount();
3686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
37074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (limit > 0 && paramsList != null) {
37174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                searchOtherDirectories(constraint, paramsList, limit);
3726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        } finally {
3746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (directoryCursor != null) {
3756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                directoryCursor.close();
3766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (defaultDirectoryCursor != null) {
3786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                defaultDirectoryCursor.close();
3796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
38374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private List<DirectorySearchParams> setupOtherDirectories(Cursor directoryCursor) {
3846ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        final PackageManager packageManager = mContext.getPackageManager();
38574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final List<DirectorySearchParams> paramsList = new ArrayList<DirectorySearchParams>();
3866ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        DirectorySearchParams preferredDirectory = null;
3876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        while (directoryCursor.moveToNext()) {
3886ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final long id = directoryCursor.getLong(DirectoryListQuery.ID);
3896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // Skip the local invisible directory, because the default directory already includes
3916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // all local results.
3926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (id == Directory.LOCAL_INVISIBLE) {
3936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                continue;
3946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final DirectorySearchParams params = new DirectorySearchParams();
3976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String packageName = directoryCursor.getString(DirectoryListQuery.PACKAGE_NAME);
3986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int resourceId = directoryCursor.getInt(DirectoryListQuery.TYPE_RESOURCE_ID);
3996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.directoryId = id;
4006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.displayName = directoryCursor.getString(DirectoryListQuery.DISPLAY_NAME);
4016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountName = directoryCursor.getString(DirectoryListQuery.ACCOUNT_NAME);
4026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountType = directoryCursor.getString(DirectoryListQuery.ACCOUNT_TYPE);
4036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (packageName != null && resourceId != 0) {
4046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                try {
4056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    final Resources resources =
4066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            packageManager.getResourcesForApplication(packageName);
4076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    params.directoryType = resources.getString(resourceId);
4086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    if (params.directoryType == null) {
4096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                        Log.e(TAG, "Cannot resolve directory name: "
4106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                                + resourceId + "@" + packageName);
4116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    }
4126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                } catch (NameNotFoundException e) {
4136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    Log.e(TAG, "Cannot resolve directory name: "
4146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            + resourceId + "@" + packageName, e);
4156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
4166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
4176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // If an account has been provided and we found a directory that
4196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // corresponds to that account, place that directory second, directly
4206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // underneath the local contacts.
4216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (mAccount != null && mAccount.name.equals(params.accountName) &&
4226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    mAccount.type.equals(params.accountType)) {
4236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                preferredDirectory = params;
4246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
42574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                paramsList.add(params);
4266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
4276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        if (preferredDirectory != null) {
43074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            paramsList.add(1, preferredDirectory);
4316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
43374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return paramsList;
4346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
4356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
4376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Starts search in other directories
4386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
43974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private void searchOtherDirectories(
44074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            CharSequence constraint, List<DirectorySearchParams> paramsList, int limit) {
44174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final int count = paramsList.size();
4426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        // Note: skipping the default partition (index 0), which has already been loaded
4436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        for (int i = 1; i < count; i++) {
44474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final DirectorySearchParams params = paramsList.get(i);
44574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.constraint = constraint;
44674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (params.filter == null) {
44774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                params.filter = new DirectoryFilter(params);
44874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
44974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.setLimit(limit);
45074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.filter(constraint);
45174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
45274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
45374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
45474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /** Must be inside a default Looper thread to avoid synchronization problem. */
45574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public void onDirectoryLoadFinished(
45674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            CharSequence constraint, DirectorySearchParams params, Cursor cursor) {
45774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (cursor != null) {
45874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            try {
45974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (DEBUG) {
46074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    Log.v(TAG, "finished loading directory \"" + params.displayName + "\"" +
46174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            " with query " + constraint);
46274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
46374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
46474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                // Check if the received result matches the current constraint
46574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                // If not - the user must have continued typing after the request was issued
46674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                final boolean usesSameConstraint;
46774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                usesSameConstraint = TextUtils.equals(constraint, mCurrentConstraint);
46874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (usesSameConstraint) {
46974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    putEntriesWithCursor(cursor, params.directoryId == Directory.DEFAULT);
47074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    constructEntryList();
47174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
47274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            } finally {
47374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                cursor.close();
4746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
4756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
4776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
4796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Stores each contact information to {@link #mEntryMap}. {@link #mEntries} isn't touched here.
4806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     *
4816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * In order to make the new information available from outside Adapter,
4826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * call {@link #constructEntryList()} after this method.
4836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
48474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private void putEntriesWithCursor(Cursor cursor, boolean validContactId) {
4856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        cursor.move(-1);
4866ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        while (cursor.moveToNext()) {
487d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final String displayName;
488d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final String destination;
489f15b448bb9fd41ec7aea7cd95104ab3d82355a06Daisuke Miyakawa            final long contactId;
490f15b448bb9fd41ec7aea7cd95104ab3d82355a06Daisuke Miyakawa            final long dataId;
491d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final String thumbnailUriString;
492d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            if (mQueryType == QUERY_TYPE_EMAIL) {
493d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                displayName = cursor.getString(EmailQuery.NAME);
494d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                destination = cursor.getString(EmailQuery.ADDRESS);
495f15b448bb9fd41ec7aea7cd95104ab3d82355a06Daisuke Miyakawa                contactId = cursor.getLong(EmailQuery.CONTACT_ID);
496f15b448bb9fd41ec7aea7cd95104ab3d82355a06Daisuke Miyakawa                dataId = cursor.getLong(EmailQuery.DATA_ID);
497d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                thumbnailUriString = cursor.getString(EmailQuery.PHOTO_THUMBNAIL_URI);
498d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            } else if (mQueryType == QUERY_TYPE_PHONE) {
499d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                displayName = cursor.getString(PhoneQuery.NAME);
500d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                destination = cursor.getString(PhoneQuery.NUMBER);
501f15b448bb9fd41ec7aea7cd95104ab3d82355a06Daisuke Miyakawa                contactId = cursor.getLong(PhoneQuery.CONTACT_ID);
502f15b448bb9fd41ec7aea7cd95104ab3d82355a06Daisuke Miyakawa                dataId = cursor.getLong(PhoneQuery.DATA_ID);
503d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                thumbnailUriString = cursor.getString(PhoneQuery.PHOTO_THUMBNAIL_URI);
504d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            } else {
505d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                throw new IndexOutOfBoundsException("Unexpected query type: " + mQueryType);
506d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            }
50774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
50874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            // Note: At this point each entry doesn't contain have any photo (thus getPhotoBytes()
50974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            // returns null).
51074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
51174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (mExistingDestinations.contains(destination)) {
51274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                continue;
51374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
51474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            mExistingDestinations.add(destination);
5156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
51674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (!validContactId) {
517f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                mNonAggregatedEntries.add(RecipientEntry.constructTopLevelEntry(
5187537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa                        displayName, destination, contactId, dataId, thumbnailUriString));
51974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            } else if (mEntryMap.containsKey(contactId)) {
5206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                // We already have a section for the person.
521f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                final List<RecipientEntry> entryList = mEntryMap.get(contactId);
522f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                entryList.add(RecipientEntry.constructSecondLevelEntry(
5236b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                        displayName, destination, contactId, dataId, thumbnailUriString));
5246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
525f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                final List<RecipientEntry> entryList = new ArrayList<RecipientEntry>();
526f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                entryList.add(RecipientEntry.constructTopLevelEntry(
5277537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa                        displayName, destination, contactId, dataId, thumbnailUriString));
5286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                mEntryMap.put(contactId, entryList);
5296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
5306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
5316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
5336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
53474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Constructs an actual list for this Adapter using {@link #mEntryMap}. Also tries to
53574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * fetch a cached photo for each contact entry (other than separators), or request another
53674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * thread to get one from directories. The thread ({@link #mPhotoHandlerThread}) will
53774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * request {@link #notifyDataSetChanged()} after having the photo asynchronously.
5386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
5396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private void constructEntryList() {
54074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mEntries.clear();
54174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        int validEntryCount = 0;
542f15b448bb9fd41ec7aea7cd95104ab3d82355a06Daisuke Miyakawa        for (Map.Entry<Long, List<RecipientEntry>> mapEntry : mEntryMap.entrySet()) {
543f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            final List<RecipientEntry> entryList = mapEntry.getValue();
5446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int size = entryList.size();
5456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            for (int i = 0; i < size; i++) {
546f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                RecipientEntry entry = entryList.get(i);
5476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                mEntries.add(entry);
54874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                tryFetchPhoto(entry);
54974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
5506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                if (i < size - 1) {
551f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                    mEntries.add(RecipientEntry.SEP_WITHIN_GROUP);
5526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
5536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
554f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            mEntries.add(RecipientEntry.SEP_NORMAL);
55574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (validEntryCount > mPreferredMaxResultCount) {
55674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                break;
55774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
5586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
55974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (validEntryCount <= mPreferredMaxResultCount) {
560f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            for (RecipientEntry entry : mNonAggregatedEntries) {
56174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (validEntryCount > mPreferredMaxResultCount) {
56274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    break;
56374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
56474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                mEntries.add(entry);
56574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                tryFetchPhoto(entry);
56674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
567f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira                mEntries.add(RecipientEntry.SEP_NORMAL);
56874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
56974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
57074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
57174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
57274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        // Remove last divider
5736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        if (mEntries.size() > 1) {
5746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            mEntries.remove(mEntries.size() - 1);
5756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
5766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        notifyDataSetChanged();
5776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
579f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira    private void tryFetchPhoto(final RecipientEntry entry) {
58074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final Uri photoThumbnailUri = entry.getPhotoThumbnailUri();
58174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (photoThumbnailUri != null) {
58274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final byte[] photoBytes = mPhotoCacheMap.get(photoThumbnailUri);
58374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (photoBytes != null) {
58474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                entry.setPhotoBytes(photoBytes);
58574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                // notifyDataSetChanged() should be called by a caller.
58674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            } else {
58774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (DEBUG) {
58874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    Log.d(TAG, "No photo cache for " + entry.getDisplayName()
58974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            + ". Fetch one asynchronously");
5906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
59174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                fetchPhotoAsync(entry, photoThumbnailUri);
5926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
5936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
5946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
596f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira    private void fetchPhotoAsync(final RecipientEntry entry, final Uri photoThumbnailUri) {
59774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoHandler.post(new Runnable() {
59874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            @Override
59974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            public void run() {
60074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                final Cursor photoCursor = mContentResolver.query(
60174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        photoThumbnailUri, PhotoQuery.PROJECTION, null, null, null);
60274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (photoCursor != null) {
60374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    try {
60474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        if (photoCursor.moveToFirst()) {
60574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            final byte[] photoBytes = photoCursor.getBlob(PhotoQuery.PHOTO);
60674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            entry.setPhotoBytes(photoBytes);
60774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
60874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            mHandler.post(new Runnable() {
60974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                @Override
61074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                public void run() {
61174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
61274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                    notifyDataSetChanged();
61374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                }
61474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            });
61574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        }
61674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    } finally {
61774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        photoCursor.close();
61874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    }
61974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
62074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
62174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        });
62274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
62374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
6246b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira    protected void fetchPhoto(final RecipientEntry entry, final Uri photoThumbnailUri) {
6256b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        byte[] photoBytes = mPhotoCacheMap.get(photoThumbnailUri);
6266b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        if (photoBytes != null) {
6276b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            entry.setPhotoBytes(photoBytes);
6286b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            return;
6296b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        }
6306b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        final Cursor photoCursor = mContentResolver.query(photoThumbnailUri, PhotoQuery.PROJECTION,
6316b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                null, null, null);
6326b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        if (photoCursor != null) {
6336b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            try {
6346b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                if (photoCursor.moveToFirst()) {
6356b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                    photoBytes = photoCursor.getBlob(PhotoQuery.PHOTO);
6366b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                    entry.setPhotoBytes(photoBytes);
6376b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
6386b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                }
6396b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            } finally {
6406b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira                photoCursor.close();
6416b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira            }
6426b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira        }
6436b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira    }
6446b6de6266d3bede33728cf995f1fd5c59ec5a55dMindy Pereira
645d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private Cursor doQuery(CharSequence constraint, int limit, Long directoryId) {
646d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        final Cursor cursor;
647d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        if (mQueryType == QUERY_TYPE_EMAIL) {
648d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final Uri.Builder builder = Email.CONTENT_FILTER_URI.buildUpon()
649d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    .appendPath(constraint.toString())
650d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
651d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                            String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES));
652d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            if (directoryId != null) {
653d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
654d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        String.valueOf(directoryId));
655d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            }
6567537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa            if (mAccount != null) {
6577537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa                builder.appendQueryParameter(PRIMARY_ACCOUNT_NAME, mAccount.name);
6587537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa                builder.appendQueryParameter(PRIMARY_ACCOUNT_TYPE, mAccount.type);
6597537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa            }
660d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            cursor = mContentResolver.query(
661d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    builder.build(), EmailQuery.PROJECTION, null, null, null);
662d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        } else if (mQueryType == QUERY_TYPE_PHONE){
663d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final Uri.Builder builder = Phone.CONTENT_FILTER_URI.buildUpon()
664d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    .appendPath(constraint.toString())
665d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
666d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                            String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES));
667d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            if (directoryId != null) {
668d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
669d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        String.valueOf(directoryId));
670d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            }
6717537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa            if (mAccount != null) {
6727537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa                builder.appendQueryParameter(PRIMARY_ACCOUNT_NAME, mAccount.name);
6737537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa                builder.appendQueryParameter(PRIMARY_ACCOUNT_TYPE, mAccount.type);
6747537f840506bcb642bed9dc1c2bdcf6d31c6b2a7Daisuke Miyakawa            }
675d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            cursor = mContentResolver.query(
676d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    builder.build(), PhoneQuery.PROJECTION, null, null, null);
677d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        } else {
678d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            cursor = null;
679d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        }
680d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return cursor;
681d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
682d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
6836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public void close() {
68474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mEntryMap.clear();
68574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mNonAggregatedEntries.clear();
68674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mExistingDestinations.clear();
68774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mEntries.clear();
68874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoCacheMap.evictAll();
68974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (!mPhotoHandlerThread.quit()) {
69074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            Log.w(TAG, "Failed to quit photo handler thread, ignoring it.");
69174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
6926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
6956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public int getCount() {
69674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return mEntries.size();
6976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
7006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public Object getItem(int position) {
70174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return mEntries.get(position);
7026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
7036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
7056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public long getItemId(int position) {
7066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return position;
7076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
7086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
71074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getViewTypeCount() {
711f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira        return RecipientEntry.ENTRY_TYPE_SIZE;
71274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
71374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
71474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
71574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getItemViewType(int position) {
71674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return mEntries.get(position).getEntryType();
71774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
7186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
71974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
72074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public View getView(int position, View convertView, ViewGroup parent) {
721f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira        final RecipientEntry entry = mEntries.get(position);
72274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        switch (entry.getEntryType()) {
723f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            case RecipientEntry.ENTRY_TYPE_SEP_NORMAL: {
724d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                return convertView != null ? convertView
725d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        : mInflater.inflate(getSeparatorLayout(), parent, false);
7266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
727f621a601e1f966c89b7aadbcca384021e14d668dMindy Pereira            case RecipientEntry.ENTRY_TYPE_SEP_WITHIN_GROUP: {
72874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                return convertView != null ? convertView
729d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        : mInflater.inflate(getSeparatorWithinGroupLayout(), parent, false);
7306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
73174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            default: {
73274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                String displayName = entry.getDisplayName();
73374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                String emailAddress = entry.getDestination();
73474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (TextUtils.isEmpty(displayName)
73574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        || TextUtils.equals(displayName, emailAddress)) {
73674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    displayName = emailAddress;
73774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    emailAddress = null;
73874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
7396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
740d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                final View itemView = convertView != null ? convertView
741d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        : mInflater.inflate(getItemLayout(), parent, false);
742d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                final TextView displayNameView =
743d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        (TextView)itemView.findViewById(getDisplayNameId());
744d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                final TextView emailAddressView =
745d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        (TextView)itemView.findViewById(getDestinationId());
746d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                final ImageView imageView = (ImageView)itemView.findViewById(getPhotoId());
74774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                displayNameView.setText(displayName);
74874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (!TextUtils.isEmpty(emailAddress)) {
74974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    emailAddressView.setText(emailAddress);
750e3a7365ee50e5df8d7935143bb88c65644be4b59Daisuke Miyakawa                } else {
751e3a7365ee50e5df8d7935143bb88c65644be4b59Daisuke Miyakawa                    emailAddressView.setText(null);
75274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
7536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                if (entry.isFirstLevel()) {
75474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    displayNameView.setVisibility(View.VISIBLE);
75574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    if (imageView != null) {
756d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        imageView.setVisibility(View.VISIBLE);
75774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        final byte[] photoBytes = entry.getPhotoBytes();
75874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        if (photoBytes != null && imageView != null) {
75974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            final Bitmap photo = BitmapFactory.decodeByteArray(
76074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                    photoBytes, 0, photoBytes.length);
76174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            imageView.setImageBitmap(photo);
76274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        } else {
76374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            imageView.setImageResource(getDefaultPhotoResource());
76474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        }
7656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    }
7666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                } else {
7676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    displayNameView.setVisibility(View.GONE);
768d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    if (imageView != null) imageView.setVisibility(View.GONE);
7696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
77074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                return itemView;
7716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
7726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
7736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
7746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
776d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Returns a layout id for each item inside auto-complete list.
777d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     *
778d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Each View must contain two TextViews (for display name and destination) and one ImageView
779d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * (for photo). Ids for those should be available via {@link #getDisplayNameId()},
780d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * {@link #getDestinationId()}, and {@link #getPhotoId()}.
7816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
782efcac0cbb3efc645cd6cf1cb1e2431e1bd2b2d2aMindy Pereira    protected abstract int getItemLayout();
783d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /** Returns a layout id for a separator dividing two person or groups. */
784efcac0cbb3efc645cd6cf1cb1e2431e1bd2b2d2aMindy Pereira    protected abstract int getSeparatorLayout();
7856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
786d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Returns a layout id for a separator dividing two destinations for a same person or group.
7876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
788efcac0cbb3efc645cd6cf1cb1e2431e1bd2b2d2aMindy Pereira    protected abstract int getSeparatorWithinGroupLayout();
7896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
7916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Returns a resource ID representing an image which should be shown when ther's no relevant
7926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * photo is available.
7936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
794efcac0cbb3efc645cd6cf1cb1e2431e1bd2b2d2aMindy Pereira    protected abstract int getDefaultPhotoResource();
795d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
796d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
797d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Returns an id for TextView in an item View for showing a display name. In default
798d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * {@link android.R.id#text1} is returned.
799d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
800d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    protected int getDisplayNameId() {
801d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return android.R.id.text1;
802d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
803d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
804d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
805d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Returns an id for TextView in an item View for showing a destination
806d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * (an email address or a phone number).
807d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * In default {@link android.R.id#text2} is returned.
808d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
809d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    protected int getDestinationId() {
810d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return android.R.id.text2;
811d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
812d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
813d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
814d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Returns an id for ImageView in an item View for showing photo image for a person. In default
815d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * {@link android.R.id#icon} is returned.
816d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
817d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    protected int getPhotoId() {
818d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return android.R.id.icon;
819d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
8206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa}
821