Fields Summary |
---|
private static final String | TAG |
private static final long | DIRECTORY_LOCAL_INVISIBLE |
private static final String | DIRECTORY_PARAM_KEY |
private static final String | LIMIT_PARAM_KEY |
private static final String | PRIMARY_ACCOUNT_NAME |
private static final String | PRIMARY_ACCOUNT_TYPE |
private static final int | DEFAULT_PREFERRED_MAX_RESULT_COUNTThe preferred number of results to be retrieved. This number may be
exceeded if there are several directories configured, because we will use
the same limit for all directories. |
private static final int | ALLOWANCE_FOR_DUPLICATESThe number of extra entries requested to allow for duplicates. Duplicates
are removed from the overall result. |
private static final int | MESSAGE_SEARCH_PENDING_DELAYThe "Searching..." message will be displayed if search is not complete
within this many milliseconds. |
private static final int | MESSAGE_SEARCH_PENDING |
private static final String | SEARCHING_CURSOR_MARKERA fake column name that indicates a "Searching..." item in the list. |
protected final android.content.ContentResolver | mContentResolver |
private boolean | mDirectoriesLoaded |
private android.accounts.Account | mAccount |
private int | mPreferredMaxResultCount |
private android.os.Handler | mHandler |
Methods Summary |
---|
public boolean | areAllItemsEnabled()
return false;
|
protected void | bindView(android.view.View v, int partition, android.database.Cursor cursor, int position)
DirectoryPartition directoryPartition = (DirectoryPartition)getPartition(partition);
String directoryType = directoryPartition.directoryType;
String directoryName = directoryPartition.displayName;
if (directoryPartition.loading) {
bindViewLoading(v, directoryType, directoryName);
} else {
String displayName = cursor.getString(EmailQuery.NAME);
String emailAddress = cursor.getString(EmailQuery.ADDRESS);
if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, emailAddress)) {
displayName = emailAddress;
emailAddress = null;
}
bindView(v, directoryType, directoryName, displayName, emailAddress);
}
|
protected abstract void | bindView(android.view.View view, java.lang.String directoryType, java.lang.String directoryName, java.lang.String displayName, java.lang.String emailAddress)Override to populate the autocomplete suggestion line item UI with data.
|
protected abstract void | bindViewLoading(android.view.View view, java.lang.String directoryType, java.lang.String directoryName)Override to populate the "Searching directory" line item UI with data.
|
private android.database.Cursor | createLoadingCursor()Creates a dummy cursor to represent the "Searching directory..." item.
MatrixCursor cursor = new MatrixCursor(new String[]{SEARCHING_CURSOR_MARKER});
cursor.addRow(new Object[]{""});
return cursor;
|
public android.widget.Filter | getFilter()
return new DefaultPartitionFilter();
|
protected int | getItemViewType(int partitionIndex, int position)
DirectoryPartition partition = (DirectoryPartition)getPartition(partitionIndex);
return partition.loading ? 1 : 0;
|
private boolean | hasDuplicates(android.database.Cursor cursor, int partition)
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
String emailAddress = cursor.getString(EmailQuery.ADDRESS);
if (isDuplicate(emailAddress, partition)) {
return true;
}
}
return false;
|
protected abstract android.view.View | inflateItemView(android.view.ViewGroup parent)Override to create a view for line item in the autocomplete suggestion list UI.
|
protected abstract android.view.View | inflateItemViewLoading(android.view.ViewGroup parent)Override to create a view for a "Searching directory" line item, which is
displayed temporarily while the corresponding filter is running.
|
private boolean | isDuplicate(java.lang.String emailAddress, int excludePartition)Checks if the supplied email address is already present in partitions other
than the supplied one.
int partitionCount = getPartitionCount();
for (int partition = 0; partition < partitionCount; partition++) {
if (partition != excludePartition && !isLoading(partition)) {
Cursor cursor = getCursor(partition);
if (cursor != null) {
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
String address = cursor.getString(EmailQuery.ADDRESS);
if (TextUtils.equals(emailAddress, address)) {
return true;
}
}
}
}
}
return false;
|
protected boolean | isEnabled(int partitionIndex, int position)
// The "Searching..." item should not be selectable
return !isLoading(partitionIndex);
|
private boolean | isLoading(int partitionIndex)
return ((DirectoryPartition)getPartition(partitionIndex)).loading;
|
private final java.lang.String | makeDisplayString(android.database.Cursor cursor)
if (cursor.getColumnName(0).equals(SEARCHING_CURSOR_MARKER)) {
return "";
}
String displayName = cursor.getString(EmailQuery.NAME);
String emailAddress = cursor.getString(EmailQuery.ADDRESS);
if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, emailAddress)) {
return emailAddress;
} else {
return new Rfc822Token(displayName, emailAddress, null).toString();
}
|
protected android.view.View | newView(android.content.Context context, int partitionIndex, android.database.Cursor cursor, int position, android.view.ViewGroup parent)
DirectoryPartition partition = (DirectoryPartition)getPartition(partitionIndex);
if (partition.loading) {
return inflateItemViewLoading(parent);
} else {
return inflateItemView(parent);
}
|
protected void | onDirectoryLoadFinished(java.lang.CharSequence constraint, android.database.Cursor directoryCursor, android.database.Cursor defaultPartitionCursor)Handles the result of the initial call, which brings back the list of
directories as well as the search results for the local directories.
if (directoryCursor != null) {
PackageManager packageManager = getContext().getPackageManager();
DirectoryPartition preferredDirectory = null;
List<DirectoryPartition> directories = new ArrayList<DirectoryPartition>();
while (directoryCursor.moveToNext()) {
long id = directoryCursor.getLong(DirectoryListQuery.ID);
// Skip the local invisible directory, because the default directory
// already includes all local results.
if (id == DIRECTORY_LOCAL_INVISIBLE) {
continue;
}
DirectoryPartition partition = new DirectoryPartition();
partition.directoryId = id;
partition.displayName = directoryCursor.getString(DirectoryListQuery.DISPLAY_NAME);
partition.accountName = directoryCursor.getString(DirectoryListQuery.ACCOUNT_NAME);
partition.accountType = directoryCursor.getString(DirectoryListQuery.ACCOUNT_TYPE);
String packageName = directoryCursor.getString(DirectoryListQuery.PACKAGE_NAME);
int resourceId = directoryCursor.getInt(DirectoryListQuery.TYPE_RESOURCE_ID);
if (packageName != null && resourceId != 0) {
try {
Resources resources =
packageManager.getResourcesForApplication(packageName);
partition.directoryType = resources.getString(resourceId);
if (partition.directoryType == null) {
Log.e(TAG, "Cannot resolve directory name: "
+ resourceId + "@" + packageName);
}
} catch (NameNotFoundException e) {
Log.e(TAG, "Cannot resolve directory name: "
+ resourceId + "@" + packageName, e);
}
}
// If an account has been provided and we found a directory that
// corresponds to that account, place that directory second, directly
// underneath the local contacts.
if (mAccount != null && mAccount.name.equals(partition.accountName) &&
mAccount.type.equals(partition.accountType)) {
preferredDirectory = partition;
} else {
directories.add(partition);
}
}
if (preferredDirectory != null) {
directories.add(1, preferredDirectory);
}
for (DirectoryPartition partition : directories) {
addPartition(partition);
}
}
int count = getPartitionCount();
int limit = 0;
// Since we will be changing several partitions at once, hold the data change
// notifications
setNotificationsEnabled(false);
try {
// The filter has loaded results for the default partition too.
if (defaultPartitionCursor != null && getPartitionCount() > 0) {
changeCursor(0, defaultPartitionCursor);
}
int defaultPartitionCount = (defaultPartitionCursor == null ? 0
: defaultPartitionCursor.getCount());
limit = mPreferredMaxResultCount - defaultPartitionCount;
// Show non-default directories as "loading"
// Note: skipping the default partition (index 0), which has already been loaded
for (int i = 1; i < count; i++) {
DirectoryPartition partition = (DirectoryPartition) getPartition(i);
partition.constraint = constraint;
if (limit > 0) {
if (!partition.loading) {
partition.loading = true;
changeCursor(i, null);
}
} else {
partition.loading = false;
changeCursor(i, null);
}
}
} finally {
setNotificationsEnabled(true);
}
// Start search in other directories
// Note: skipping the default partition (index 0), which has already been loaded
for (int i = 1; i < count; i++) {
DirectoryPartition partition = (DirectoryPartition) getPartition(i);
if (partition.loading) {
mHandler.removeMessages(MESSAGE_SEARCH_PENDING, partition);
Message msg = mHandler.obtainMessage(MESSAGE_SEARCH_PENDING, i, 0, partition);
mHandler.sendMessageDelayed(msg, MESSAGE_SEARCH_PENDING_DELAY);
if (partition.filter == null) {
partition.filter = new DirectoryPartitionFilter(i, partition.directoryId);
}
partition.filter.setLimit(limit);
partition.filter.filter(constraint);
} else {
if (partition.filter != null) {
// Cancel any previous loading request
partition.filter.filter(null);
}
}
}
|
public void | onPartitionLoadFinished(java.lang.CharSequence constraint, int partitionIndex, android.database.Cursor cursor)
if (partitionIndex < getPartitionCount()) {
DirectoryPartition partition = (DirectoryPartition) getPartition(partitionIndex);
// Check if the received result matches the current constraint
// If not - the user must have continued typing after the request
// was issued
if (partition.loading && TextUtils.equals(constraint, partition.constraint)) {
partition.loading = false;
mHandler.removeMessages(MESSAGE_SEARCH_PENDING, partition);
changeCursor(partitionIndex, removeDuplicatesAndTruncate(partitionIndex, cursor));
} else {
// We got the result for an unexpected query (the user is still typing)
// Just ignore this result
if (cursor != null) {
cursor.close();
}
}
} else if (cursor != null) {
cursor.close();
}
|
private android.database.Cursor | removeDuplicatesAndTruncate(int partition, android.database.Cursor cursor)Post-process the cursor to eliminate duplicates. Closes the original cursor
and returns a new one.
if (cursor == null) {
return null;
}
if (cursor.getCount() <= DEFAULT_PREFERRED_MAX_RESULT_COUNT
&& !hasDuplicates(cursor, partition)) {
return cursor;
}
int count = 0;
MatrixCursor newCursor = new MatrixCursor(EmailQuery.PROJECTION);
cursor.moveToPosition(-1);
while (cursor.moveToNext() && count < DEFAULT_PREFERRED_MAX_RESULT_COUNT) {
String displayName = cursor.getString(EmailQuery.NAME);
String emailAddress = cursor.getString(EmailQuery.ADDRESS);
if (!isDuplicate(emailAddress, partition)) {
newCursor.addRow(new Object[]{displayName, emailAddress});
count++;
}
}
cursor.close();
return newCursor;
|
public void | setAccount(android.accounts.Account account)Set the account when known. Causes the search to prioritize contacts from
that account.
mAccount = account;
|
void | showSearchPendingIfNotComplete(int partitionIndex)
if (partitionIndex < getPartitionCount()) {
DirectoryPartition partition = (DirectoryPartition) getPartition(partitionIndex);
if (partition.loading) {
changeCursor(partitionIndex, createLoadingCursor());
}
}
|