BaseRecipientAdapter.java revision cf9337aa4b2d844f4941d75832f52266c4fc3fa7
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
7874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /** The number of photos cached in this Adapter. */
7974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private static final int PHOTO_CACHE_SIZE = 20;
8074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
81d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public static final int QUERY_TYPE_EMAIL = 0;
82d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public static final int QUERY_TYPE_PHONE = 1;
83d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
846ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Model object for a {@link Directory} row.
866ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public final static class DirectorySearchParams {
886ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public long directoryId;
896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String directoryType;
906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String displayName;
916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String accountName;
926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public String accountType;
936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence constraint;
946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public DirectoryFilter filter;
956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static class EmailQuery {
986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final String[] PROJECTION = {
996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Contacts.DISPLAY_NAME,       // 0
1006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Email.DATA,                  // 1
1016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Email.CONTACT_ID,            // 2
1026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Contacts.PHOTO_THUMBNAIL_URI // 3
1036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        };
1046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int NAME = 0;
1066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ADDRESS = 1;
1076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int CONTACT_ID = 2;
1086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int PHOTO_THUMBNAIL_URI = 3;
1096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
111d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private static class PhoneQuery {
112d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        public static final String[] PROJECTION = {
113d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            Contacts.DISPLAY_NAME,       // 0
114d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            Phone.DATA,                  // 1
115d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            Phone.CONTACT_ID,            // 2
116d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            Contacts.PHOTO_THUMBNAIL_URI // 3
117d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        };
118d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        public static final int NAME = 0;
119d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        public static final int NUMBER = 1;
120d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        public static final int CONTACT_ID = 2;
121d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        public static final int PHOTO_THUMBNAIL_URI = 3;
122d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
1236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
12474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private static class PhotoQuery {
12574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public static final String[] PROJECTION = {
12674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            Photo.PHOTO
12774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        };
12874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
12974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public static final int PHOTO = 0;
13074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
13174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
1326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private static class DirectoryListQuery {
1336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final Uri URI =
1356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories");
1366ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final String[] PROJECTION = {
1376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory._ID,              // 0
1386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_NAME,     // 1
1396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.ACCOUNT_TYPE,     // 2
1406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.DISPLAY_NAME,     // 3
1416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.PACKAGE_NAME,     // 4
1426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Directory.TYPE_RESOURCE_ID, // 5
1436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        };
1446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ID = 0;
1466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_NAME = 1;
1476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int ACCOUNT_TYPE = 2;
1486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int DISPLAY_NAME = 3;
1496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int PACKAGE_NAME = 4;
1506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public static final int TYPE_RESOURCE_ID = 5;
1516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
1526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
1546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter used for loading two data sets: email rows from the local
1556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * contact provider and the list of {@link Directory}'s.
1566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
1576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final class DefaultFilter extends Filter {
1586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
1606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
16174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
1626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            Cursor cursor = null;
1636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (!TextUtils.isEmpty(constraint)) {
164d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                cursor = doQuery(constraint, mPreferredMaxResultCount, null);
1656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                if (cursor != null) {
1666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    results.count = cursor.getCount();
1676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
1686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
1696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // TODO: implement group feature
1716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final Cursor directoryCursor = mContentResolver.query(
1736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    DirectoryListQuery.URI, DirectoryListQuery.PROJECTION, null, null, null);
1746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
17574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (DEBUG && cursor == null) {
17674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                Log.w(TAG, "null cursor returned for default Email filter query.");
17774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
1786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            results.values = new Cursor[] { directoryCursor, cursor };
1796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
1806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
1816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
1836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
1846ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (results.values != null) {
1856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                final Cursor[] cursors = (Cursor[]) results.values;
1866ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                // Run on one thread.
1876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                mHandler.post(new Runnable() {
1886ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    @Override
1896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    public void run() {
1906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                        onFirstDirectoryLoadFinished(constraint, cursors[0], cursors[1]);
1916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    }
1926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                });
1936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
1946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            results.count = getCount();
1956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
1966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
1976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
1986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public CharSequence convertResultToString(Object resultValue) {
1996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final RecipientListEntry entry = (RecipientListEntry)resultValue;
2006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String displayName = entry.getDisplayName();
2016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String emailAddress = entry.getDestination();
2026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, emailAddress)) {
2036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                 return emailAddress;
2046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
2056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                return new Rfc822Token(displayName, emailAddress, null).toString();
2066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
2076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
2096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
2116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * An asynchronous filter that performs search in a particular directory.
2126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
2136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final class DirectoryFilter extends Filter {
21474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        private final DirectorySearchParams mParams;
2156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        private int mLimit;
2166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
21774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        public DirectoryFilter(DirectorySearchParams params) {
21874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            this.mParams = params;
2196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized void setLimit(int limit) {
2226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            this.mLimit = limit;
2236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        public synchronized int getLimit() {
2266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return this.mLimit;
2276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
2306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected FilterResults performFiltering(CharSequence constraint) {
23174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final FilterResults results = new FilterResults();
2326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (!TextUtils.isEmpty(constraint)) {
233d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                final Cursor cursor = doQuery(constraint, getLimit(), mParams.directoryId);
23474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (cursor != null) {
23574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    results.values = cursor;
23674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
2376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
2386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // TODO: implement group feature
2406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            return results;
2426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2436ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        @Override
2456ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        protected void publishResults(final CharSequence constraint, FilterResults results) {
2466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final Cursor cursor = (Cursor) results.values;
2476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            mHandler.post(new Runnable() {
2486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                @Override
2496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                public void run() {
25074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    onDirectoryLoadFinished(constraint, mParams, cursor);
2516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
2526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            });
2536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            results.count = getCount();
2546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
2556ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
2566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
25774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final Context mContext;
2586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final ContentResolver mContentResolver;
259d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private final LayoutInflater mInflater;
260d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private final int mQueryType;
2616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private Account mAccount;
26274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final int mPreferredMaxResultCount;
2636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private final Handler mHandler = new Handler();
2646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
2656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
26674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Each destination (an email address or a phone number) with a valid contactId is first
26774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * inserted into {@link #mEntryMap} and grouped by the contactId.
26874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Destinations without valid contactId (possible if they aren't in local storage) are stored
26974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * in {@link #mNonAggregatedEntries}.
27074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Duplicates are removed using {@link #mExistingDestinations}.
27174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     *
27274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * After having all results from ContentResolver, all elements in mEntryMap are copied to
27374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * mEntry, which will be used to find items in this Adapter. If the number of contacts in
27474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * mEntries are less than mPreferredMaxResultCount, contacts in
27574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * mNonAggregatedEntries are also used.
2766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
277cf9337aa4b2d844f4941d75832f52266c4fc3fa7Daisuke Miyakawa    private final LinkedHashMap<Integer, List<RecipientListEntry>> mEntryMap;
27874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final List<RecipientListEntry> mNonAggregatedEntries;
27974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final List<RecipientListEntry> mEntries;
28074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final Set<String> mExistingDestinations;
2816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
28274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /**
28374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Used to ignore asynchronous queries with a different constraint, which may appear when
28474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * users type characters quickly.
28574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     */
28674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private CharSequence mCurrentConstraint;
28774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
28874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final HandlerThread mPhotoHandlerThread;
28974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final Handler mPhotoHandler;
29074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private final LruCache<Uri, byte[]> mPhotoCacheMap;
2916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
292d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
293d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Constructor for email queries.
294d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
2956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public BaseRecipientAdapter(Context context) {
296d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        this(context, QUERY_TYPE_EMAIL, DEFAULT_PREFERRED_MAX_RESULT_COUNT);
297d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
298d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
299d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public BaseRecipientAdapter(Context context, int queryType) {
300d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        this(context, queryType, DEFAULT_PREFERRED_MAX_RESULT_COUNT);
3016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
303d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    public BaseRecipientAdapter(Context context, int queryType, int preferredMaxResultCount) {
3046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContext = context;
3056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mContentResolver = context.getContentResolver();
306d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        mInflater = LayoutInflater.from(context);
307d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        mQueryType = queryType;
3086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mPreferredMaxResultCount = preferredMaxResultCount;
309cf9337aa4b2d844f4941d75832f52266c4fc3fa7Daisuke Miyakawa        mEntryMap = new LinkedHashMap<Integer, List<RecipientListEntry>>();
31074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mNonAggregatedEntries = new ArrayList<RecipientListEntry>();
31174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mEntries = new ArrayList<RecipientListEntry>();
31274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mExistingDestinations = new HashSet<String>();
31374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoHandlerThread = new HandlerThread("photo_handler");
31474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoHandlerThread.start();
31574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoHandler = new Handler(mPhotoHandlerThread.getLooper());
31674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoCacheMap = new LruCache<Uri, byte[]>(PHOTO_CACHE_SIZE);
3176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
3206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Set the account when known. Causes the search to prioritize contacts from that account.
3216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
3226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public void setAccount(Account account) {
3236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        mAccount = account;
3246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /** Will be called from {@link AutoCompleteTextView} to prepare auto-complete list. */
3276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
3286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public Filter getFilter() {
3296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return new DefaultFilter();
3306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
3336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Handles the result of the initial call, which brings back the list of directories as well
3346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * as the search results for the local directories.
33574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     *
33674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Must be inside a default Looper thread to avoid synchronization problem.
3376ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
3386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    protected void onFirstDirectoryLoadFinished(
3396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            CharSequence constraint, Cursor directoryCursor, Cursor defaultDirectoryCursor) {
34074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mCurrentConstraint = constraint;
34174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
3426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        try {
34374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final List<DirectorySearchParams> paramsList;
3446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (directoryCursor != null) {
34574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                paramsList = setupOtherDirectories(directoryCursor);
34674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            } else {
34774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                paramsList = null;
3486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            int limit = 0;
3516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
35274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (defaultDirectoryCursor != null) {
3536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                mEntryMap.clear();
35474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                mNonAggregatedEntries.clear();
35574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                mExistingDestinations.clear();
35674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                putEntriesWithCursor(defaultDirectoryCursor, true);
3576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                constructEntryList();
3586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                limit = mPreferredMaxResultCount - getCount();
3596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3606ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
36174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (limit > 0 && paramsList != null) {
36274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                searchOtherDirectories(constraint, paramsList, limit);
3636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        } finally {
3656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (directoryCursor != null) {
3666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                directoryCursor.close();
3676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (defaultDirectoryCursor != null) {
3696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                defaultDirectoryCursor.close();
3706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
3726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
3736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
37474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private List<DirectorySearchParams> setupOtherDirectories(Cursor directoryCursor) {
3756ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        final PackageManager packageManager = mContext.getPackageManager();
37674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final List<DirectorySearchParams> paramsList = new ArrayList<DirectorySearchParams>();
3776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        DirectorySearchParams preferredDirectory = null;
3786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        while (directoryCursor.moveToNext()) {
3796ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final long id = directoryCursor.getLong(DirectoryListQuery.ID);
3806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // Skip the local invisible directory, because the default directory already includes
3826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // all local results.
3836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (id == Directory.LOCAL_INVISIBLE) {
3846ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                continue;
3856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
3866ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
3876ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final DirectorySearchParams params = new DirectorySearchParams();
3886ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final String packageName = directoryCursor.getString(DirectoryListQuery.PACKAGE_NAME);
3896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int resourceId = directoryCursor.getInt(DirectoryListQuery.TYPE_RESOURCE_ID);
3906ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.directoryId = id;
3916ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.displayName = directoryCursor.getString(DirectoryListQuery.DISPLAY_NAME);
3926ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountName = directoryCursor.getString(DirectoryListQuery.ACCOUNT_NAME);
3936ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            params.accountType = directoryCursor.getString(DirectoryListQuery.ACCOUNT_TYPE);
3946ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (packageName != null && resourceId != 0) {
3956ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                try {
3966ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    final Resources resources =
3976ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            packageManager.getResourcesForApplication(packageName);
3986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    params.directoryType = resources.getString(resourceId);
3996ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    if (params.directoryType == null) {
4006ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                        Log.e(TAG, "Cannot resolve directory name: "
4016ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                                + resourceId + "@" + packageName);
4026ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    }
4036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                } catch (NameNotFoundException e) {
4046ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    Log.e(TAG, "Cannot resolve directory name: "
4056ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                            + resourceId + "@" + packageName, e);
4066ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
4076ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
4086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // If an account has been provided and we found a directory that
4106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // corresponds to that account, place that directory second, directly
4116ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            // underneath the local contacts.
4126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            if (mAccount != null && mAccount.name.equals(params.accountName) &&
4136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    mAccount.type.equals(params.accountType)) {
4146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                preferredDirectory = params;
4156ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
41674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                paramsList.add(params);
4176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
4186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        if (preferredDirectory != null) {
42174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            paramsList.add(1, preferredDirectory);
4226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
42474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return paramsList;
4256ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
4266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
4286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Starts search in other directories
4296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
43074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private void searchOtherDirectories(
43174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            CharSequence constraint, List<DirectorySearchParams> paramsList, int limit) {
43274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final int count = paramsList.size();
4336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        // Note: skipping the default partition (index 0), which has already been loaded
4346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        for (int i = 1; i < count; i++) {
43574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final DirectorySearchParams params = paramsList.get(i);
43674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.constraint = constraint;
43774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (params.filter == null) {
43874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                params.filter = new DirectoryFilter(params);
43974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
44074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.setLimit(limit);
44174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            params.filter.filter(constraint);
44274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
44374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
44474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
44574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    /** Must be inside a default Looper thread to avoid synchronization problem. */
44674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public void onDirectoryLoadFinished(
44774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            CharSequence constraint, DirectorySearchParams params, Cursor cursor) {
44874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (cursor != null) {
44974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            try {
45074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (DEBUG) {
45174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    Log.v(TAG, "finished loading directory \"" + params.displayName + "\"" +
45274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            " with query " + constraint);
45374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
45474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
45574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                // Check if the received result matches the current constraint
45674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                // If not - the user must have continued typing after the request was issued
45774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                final boolean usesSameConstraint;
45874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                usesSameConstraint = TextUtils.equals(constraint, mCurrentConstraint);
45974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (usesSameConstraint) {
46074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    putEntriesWithCursor(cursor, params.directoryId == Directory.DEFAULT);
46174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    constructEntryList();
46274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
46374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            } finally {
46474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                cursor.close();
4656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
4666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
4676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
4686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
4696ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
4706ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Stores each contact information to {@link #mEntryMap}. {@link #mEntries} isn't touched here.
4716ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     *
4726ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * In order to make the new information available from outside Adapter,
4736ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * call {@link #constructEntryList()} after this method.
4746ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
47574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private void putEntriesWithCursor(Cursor cursor, boolean validContactId) {
4766ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        cursor.move(-1);
4776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        while (cursor.moveToNext()) {
478d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final String displayName;
479d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final String destination;
480d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final int contactId;
481d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final String thumbnailUriString;
482d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            if (mQueryType == QUERY_TYPE_EMAIL) {
483d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                displayName = cursor.getString(EmailQuery.NAME);
484d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                destination = cursor.getString(EmailQuery.ADDRESS);
485d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                contactId = cursor.getInt(EmailQuery.CONTACT_ID);
486d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                thumbnailUriString = cursor.getString(EmailQuery.PHOTO_THUMBNAIL_URI);
487d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            } else if (mQueryType == QUERY_TYPE_PHONE) {
488d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                displayName = cursor.getString(PhoneQuery.NAME);
489d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                destination = cursor.getString(PhoneQuery.NUMBER);
490d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                contactId = cursor.getInt(PhoneQuery.CONTACT_ID);
491d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                thumbnailUriString = cursor.getString(PhoneQuery.PHOTO_THUMBNAIL_URI);
492d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            } else {
493d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                throw new IndexOutOfBoundsException("Unexpected query type: " + mQueryType);
494d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            }
49574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
49674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            // Note: At this point each entry doesn't contain have any photo (thus getPhotoBytes()
49774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            // returns null).
49874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
49974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (mExistingDestinations.contains(destination)) {
50074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                continue;
50174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
50274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            mExistingDestinations.add(destination);
5036ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
50474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (!validContactId) {
50574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                mNonAggregatedEntries.add(RecipientListEntry.constructTopLevelEntry(
50674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        displayName, destination, contactId, thumbnailUriString));
50774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            } else if (mEntryMap.containsKey(contactId)) {
5086ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                // We already have a section for the person.
5096ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                final List<RecipientListEntry> entryList = mEntryMap.get(contactId);
51074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                entryList.add(RecipientListEntry.constructSecondLevelEntry(
51174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        displayName, destination, contactId));
5126ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            } else {
5136ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                final List<RecipientListEntry> entryList = new ArrayList<RecipientListEntry>();
5146ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                entryList.add(RecipientListEntry.constructTopLevelEntry(
51574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        displayName, destination, contactId, thumbnailUriString));
5166ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                mEntryMap.put(contactId, entryList);
5176ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
5186ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
5196ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5206ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
5216ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
52274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * Constructs an actual list for this Adapter using {@link #mEntryMap}. Also tries to
52374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * fetch a cached photo for each contact entry (other than separators), or request another
52474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * thread to get one from directories. The thread ({@link #mPhotoHandlerThread}) will
52574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa     * request {@link #notifyDataSetChanged()} after having the photo asynchronously.
5266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
5276ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    private void constructEntryList() {
52874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mEntries.clear();
52974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        int validEntryCount = 0;
5306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        for (Map.Entry<Integer, List<RecipientListEntry>> mapEntry : mEntryMap.entrySet()) {
5316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final List<RecipientListEntry> entryList = mapEntry.getValue();
5326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            final int size = entryList.size();
5336ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            for (int i = 0; i < size; i++) {
5346ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                RecipientListEntry entry = entryList.get(i);
5356ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                mEntries.add(entry);
53674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                tryFetchPhoto(entry);
53774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
5386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                if (i < size - 1) {
5396ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    mEntries.add(RecipientListEntry.SEP_WITHIN_GROUP);
5406ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
5416ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
5426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            mEntries.add(RecipientListEntry.SEP_NORMAL);
54374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (validEntryCount > mPreferredMaxResultCount) {
54474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                break;
54574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
5466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
54774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (validEntryCount <= mPreferredMaxResultCount) {
54874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            for (RecipientListEntry entry : mNonAggregatedEntries) {
54974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (validEntryCount > mPreferredMaxResultCount) {
55074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    break;
55174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
55274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                mEntries.add(entry);
55374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                tryFetchPhoto(entry);
55474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
55574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                mEntries.add(RecipientListEntry.SEP_NORMAL);
55674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                validEntryCount++;
55774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
55874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
55974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
56074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        // Remove last divider
5616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        if (mEntries.size() > 1) {
5626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            mEntries.remove(mEntries.size() - 1);
5636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
5646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        notifyDataSetChanged();
5656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
56774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private void tryFetchPhoto(final RecipientListEntry entry) {
56874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        final Uri photoThumbnailUri = entry.getPhotoThumbnailUri();
56974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (photoThumbnailUri != null) {
57074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            final byte[] photoBytes = mPhotoCacheMap.get(photoThumbnailUri);
57174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            if (photoBytes != null) {
57274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                entry.setPhotoBytes(photoBytes);
57374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                // notifyDataSetChanged() should be called by a caller.
57474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            } else {
57574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (DEBUG) {
57674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    Log.d(TAG, "No photo cache for " + entry.getDisplayName()
57774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            + ". Fetch one asynchronously");
5786ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
57974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                fetchPhotoAsync(entry, photoThumbnailUri);
5806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
5816ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
5826ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
5836ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
58474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    private void fetchPhotoAsync(final RecipientListEntry entry, final Uri photoThumbnailUri) {
58574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoHandler.post(new Runnable() {
58674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            @Override
58774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            public void run() {
58874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                final Cursor photoCursor = mContentResolver.query(
58974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        photoThumbnailUri, PhotoQuery.PROJECTION, null, null, null);
59074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (photoCursor != null) {
59174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    try {
59274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        if (photoCursor.moveToFirst()) {
59374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            final byte[] photoBytes = photoCursor.getBlob(PhotoQuery.PHOTO);
59474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            entry.setPhotoBytes(photoBytes);
59574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
59674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            mHandler.post(new Runnable() {
59774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                @Override
59874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                public void run() {
59974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
60074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                    notifyDataSetChanged();
60174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                }
60274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            });
60374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        }
60474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    } finally {
60574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        photoCursor.close();
60674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    }
60774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
60874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            }
60974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        });
61074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
61174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
612d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    private Cursor doQuery(CharSequence constraint, int limit, Long directoryId) {
613d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        final Cursor cursor;
614d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        if (mQueryType == QUERY_TYPE_EMAIL) {
615d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final Uri.Builder builder = Email.CONTENT_FILTER_URI.buildUpon()
616d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    .appendPath(constraint.toString())
617d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
618d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                            String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES));
619d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            if (directoryId != null) {
620d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
621d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        String.valueOf(directoryId));
622d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            }
623d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            cursor = mContentResolver.query(
624d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    builder.build(), EmailQuery.PROJECTION, null, null, null);
625d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        } else if (mQueryType == QUERY_TYPE_PHONE){
626d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            final Uri.Builder builder = Phone.CONTENT_FILTER_URI.buildUpon()
627d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    .appendPath(constraint.toString())
628d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
629d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                            String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES));
630d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            if (directoryId != null) {
631d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
632d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        String.valueOf(directoryId));
633d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            }
634d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            cursor = mContentResolver.query(
635d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    builder.build(), PhoneQuery.PROJECTION, null, null, null);
636d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        } else {
637d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa            cursor = null;
638d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        }
639d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return cursor;
640d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
641d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
6426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public void close() {
64374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mEntryMap.clear();
64474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mNonAggregatedEntries.clear();
64574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mExistingDestinations.clear();
64674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mEntries.clear();
64774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        mPhotoCacheMap.evictAll();
64874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        if (!mPhotoHandlerThread.quit()) {
64974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            Log.w(TAG, "Failed to quit photo handler thread, ignoring it.");
65074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        }
6516ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6526ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6536ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
6546ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public int getCount() {
65574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return mEntries.size();
6566ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6576ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6586ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
6596ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public Object getItem(int position) {
66074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return mEntries.get(position);
6616ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6626ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6636ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
6646ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    public long getItemId(int position) {
6656ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        return position;
6666ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
6676ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
6686ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    @Override
66974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getViewTypeCount() {
67074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return RecipientListEntry.ENTRY_TYPE_SIZE;
67174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
67274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa
67374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
67474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public int getItemViewType(int position) {
67574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        return mEntries.get(position).getEntryType();
67674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    }
6776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
67874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    @Override
67974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa    public View getView(int position, View convertView, ViewGroup parent) {
6806ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        final RecipientListEntry entry = mEntries.get(position);
68174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa        switch (entry.getEntryType()) {
68274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            case RecipientListEntry.ENTRY_TYPE_SEP_NORMAL: {
683d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                return convertView != null ? convertView
684d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        : mInflater.inflate(getSeparatorLayout(), parent, false);
6856ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
68674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            case RecipientListEntry.ENTRY_TYPE_SEP_WITHIN_GROUP: {
68774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                return convertView != null ? convertView
688d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        : mInflater.inflate(getSeparatorWithinGroupLayout(), parent, false);
6896ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
69074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa            default: {
69174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                String displayName = entry.getDisplayName();
69274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                String emailAddress = entry.getDestination();
69374a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (TextUtils.isEmpty(displayName)
69474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        || TextUtils.equals(displayName, emailAddress)) {
69574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    displayName = emailAddress;
69674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    emailAddress = null;
69774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
6986ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
699d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                final View itemView = convertView != null ? convertView
700d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        : mInflater.inflate(getItemLayout(), parent, false);
701d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                final TextView displayNameView =
702d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        (TextView)itemView.findViewById(getDisplayNameId());
703d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                final TextView emailAddressView =
704d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        (TextView)itemView.findViewById(getDestinationId());
705d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                final ImageView imageView = (ImageView)itemView.findViewById(getPhotoId());
70674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                displayNameView.setText(displayName);
70774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                if (!TextUtils.isEmpty(emailAddress)) {
70874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    emailAddressView.setText(emailAddress);
70974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                }
7106ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                if (entry.isFirstLevel()) {
71174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    displayNameView.setVisibility(View.VISIBLE);
71274a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                    if (imageView != null) {
713d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                        imageView.setVisibility(View.VISIBLE);
71474a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        final byte[] photoBytes = entry.getPhotoBytes();
71574a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        if (photoBytes != null && imageView != null) {
71674a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            final Bitmap photo = BitmapFactory.decodeByteArray(
71774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                                    photoBytes, 0, photoBytes.length);
71874a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            imageView.setImageBitmap(photo);
71974a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        } else {
72074a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                            imageView.setImageResource(getDefaultPhotoResource());
72174a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                        }
7226ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    }
7236ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                } else {
7246ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                    displayNameView.setVisibility(View.GONE);
725d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa                    if (imageView != null) imageView.setVisibility(View.GONE);
7266ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa                }
72774a977cbc35413866ca039d509801fe0b02e1901Daisuke Miyakawa                return itemView;
7286ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa            }
7296ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa        }
7306ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    }
7316ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7326ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
733d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Returns a layout id for each item inside auto-complete list.
734d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     *
735d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Each View must contain two TextViews (for display name and destination) and one ImageView
736d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * (for photo). Ids for those should be available via {@link #getDisplayNameId()},
737d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * {@link #getDestinationId()}, and {@link #getPhotoId()}.
7386ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
739efcac0cbb3efc645cd6cf1cb1e2431e1bd2b2d2aMindy Pereira    protected abstract int getItemLayout();
740d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /** Returns a layout id for a separator dividing two person or groups. */
741efcac0cbb3efc645cd6cf1cb1e2431e1bd2b2d2aMindy Pereira    protected abstract int getSeparatorLayout();
7426ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
743d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Returns a layout id for a separator dividing two destinations for a same person or group.
7446ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
745efcac0cbb3efc645cd6cf1cb1e2431e1bd2b2d2aMindy Pereira    protected abstract int getSeparatorWithinGroupLayout();
7466ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa
7476ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa    /**
7486ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * Returns a resource ID representing an image which should be shown when ther's no relevant
7496ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     * photo is available.
7506ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa     */
751efcac0cbb3efc645cd6cf1cb1e2431e1bd2b2d2aMindy Pereira    protected abstract int getDefaultPhotoResource();
752d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
753d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
754d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Returns an id for TextView in an item View for showing a display name. In default
755d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * {@link android.R.id#text1} is returned.
756d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
757d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    protected int getDisplayNameId() {
758d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return android.R.id.text1;
759d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
760d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
761d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
762d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Returns an id for TextView in an item View for showing a destination
763d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * (an email address or a phone number).
764d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * In default {@link android.R.id#text2} is returned.
765d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
766d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    protected int getDestinationId() {
767d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return android.R.id.text2;
768d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
769d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa
770d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    /**
771d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * Returns an id for ImageView in an item View for showing photo image for a person. In default
772d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     * {@link android.R.id#icon} is returned.
773d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa     */
774d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    protected int getPhotoId() {
775d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa        return android.R.id.icon;
776d4baa3f2d02f1e52d325de3402e0999ac5ca65d2Daisuke Miyakawa    }
7776ac7d0c6f6db2f5af06f9f2c73f24760d4df7b84Daisuke Miyakawa}
778