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