diff options
Diffstat (limited to 'src/com/android/contacts/common/list/PhoneNumberListAdapter.java')
-rw-r--r-- | src/com/android/contacts/common/list/PhoneNumberListAdapter.java | 334 |
1 files changed, 260 insertions, 74 deletions
diff --git a/src/com/android/contacts/common/list/PhoneNumberListAdapter.java b/src/com/android/contacts/common/list/PhoneNumberListAdapter.java index c62b6b1f..ad370353 100644 --- a/src/com/android/contacts/common/list/PhoneNumberListAdapter.java +++ b/src/com/android/contacts/common/list/PhoneNumberListAdapter.java @@ -29,12 +29,15 @@ import android.provider.ContactsContract.ContactCounts; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Directory; -import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import com.android.contacts.common.GeoUtil; import com.android.contacts.common.R; +import com.android.contacts.common.extensions.ExtendedPhoneDirectoriesManager; +import com.android.contacts.common.extensions.ExtensionsFactory; +import com.android.contacts.common.util.Constants; import java.util.ArrayList; import java.util.List; @@ -48,10 +51,19 @@ import java.util.List; * API instead of {@link Phone}. */ public class PhoneNumberListAdapter extends ContactEntryListAdapter { + private static final String TAG = PhoneNumberListAdapter.class.getSimpleName(); - protected static class PhoneQuery { - private static final String[] PROJECTION_PRIMARY = new String[] { + // A list of extended directories to add to the directories from the database + private final List<DirectoryPartition> mExtendedDirectories; + + // Extended directories will have ID's that are higher than any of the id's from the database. + // Thi sis so that we can identify them and set them up properly. If no extended directories + // exist, this will be Long.MAX_VALUE + private long mFirstExtendedDirectoryId = Long.MAX_VALUE; + + public static class PhoneQuery { + public static final String[] PROJECTION_PRIMARY = new String[] { Phone._ID, // 0 Phone.TYPE, // 1 Phone.LABEL, // 2 @@ -60,9 +72,10 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { Phone.LOOKUP_KEY, // 5 Phone.PHOTO_ID, // 6 Phone.DISPLAY_NAME_PRIMARY, // 7 + Phone.PHOTO_THUMBNAIL_URI, // 8 }; - private static final String[] PROJECTION_ALTERNATIVE = new String[] { + public static final String[] PROJECTION_ALTERNATIVE = new String[] { Phone._ID, // 0 Phone.TYPE, // 1 Phone.LABEL, // 2 @@ -71,16 +84,18 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { Phone.LOOKUP_KEY, // 5 Phone.PHOTO_ID, // 6 Phone.DISPLAY_NAME_ALTERNATIVE, // 7 + Phone.PHOTO_THUMBNAIL_URI, // 8 }; - public static final int PHONE_ID = 0; - public static final int PHONE_TYPE = 1; - public static final int PHONE_LABEL = 2; - public static final int PHONE_NUMBER = 3; - public static final int PHONE_CONTACT_ID = 4; - public static final int PHONE_LOOKUP_KEY = 5; - public static final int PHONE_PHOTO_ID = 6; - public static final int PHONE_DISPLAY_NAME = 7; + public static final int PHONE_ID = 0; + public static final int PHONE_TYPE = 1; + public static final int PHONE_LABEL = 2; + public static final int PHONE_NUMBER = 3; + public static final int CONTACT_ID = 4; + public static final int LOOKUP_KEY = 5; + public static final int PHOTO_ID = 6; + public static final int DISPLAY_NAME = 7; + public static final int PHOTO_URI = 8; } private final CharSequence mUnknownNameText; @@ -93,6 +108,15 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { super(context); setDefaultFilterHeaderText(R.string.list_filter_phones); mUnknownNameText = context.getText(android.R.string.unknownName); + + final ExtendedPhoneDirectoriesManager manager + = ExtensionsFactory.getExtendedPhoneDirectoriesManager(); + if (manager != null) { + mExtendedDirectories = manager.getExtendedDirectories(mContext); + } else { + // Empty list to avoid sticky NPE's + mExtendedDirectories = new ArrayList<DirectoryPartition>(); + } } protected CharSequence getUnknownNameText() { @@ -101,52 +125,82 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { @Override public void configureLoader(CursorLoader loader, long directoryId) { - if (directoryId != Directory.DEFAULT) { - Log.w(TAG, "PhoneNumberListAdapter is not ready for non-default directory ID (" - + "directoryId: " + directoryId + ")"); + String query = getQueryString(); + if (query == null) { + query = ""; } - - final Builder builder; - if (isSearchMode()) { - final Uri baseUri = - mUseCallableUri ? Callable.CONTENT_FILTER_URI : Phone.CONTENT_FILTER_URI; - builder = baseUri.buildUpon(); - final String query = getQueryString(); - if (TextUtils.isEmpty(query)) { - builder.appendPath(""); - } else { - builder.appendPath(query); // Builder will encode the query + if (isExtendedDirectory(directoryId)) { + final DirectoryPartition directory = getExtendedDirectoryFromId(directoryId); + final String contentUri = directory.getContentUri(); + if (contentUri == null) { + throw new IllegalStateException("Extended directory must have a content URL: " + + directory); } - builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, - String.valueOf(directoryId)); + final Builder builder = Uri.parse(contentUri).buildUpon(); + builder.appendPath(query); + builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, + String.valueOf(getDirectoryResultLimit(directory))); + loader.setUri(builder.build()); + loader.setProjection(PhoneQuery.PROJECTION_PRIMARY); } else { - final Uri baseUri = mUseCallableUri ? Callable.CONTENT_URI : Phone.CONTENT_URI; - builder = baseUri.buildUpon().appendQueryParameter( - ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT)); - if (isSectionHeaderDisplayEnabled()) { - builder.appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true"); + final boolean isRemoteDirectoryQuery = isRemoteDirectory(directoryId); + final Builder builder; + if (isSearchMode()) { + final Uri baseUri; + if (isRemoteDirectoryQuery) { + baseUri = Phone.CONTENT_FILTER_URI; + } else if (mUseCallableUri) { + baseUri = Callable.CONTENT_FILTER_URI; + } else { + baseUri = Phone.CONTENT_FILTER_URI; + } + builder = baseUri.buildUpon(); + builder.appendPath(query); // Builder will encode the query + builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, + String.valueOf(directoryId)); + if (isRemoteDirectoryQuery) { + builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, + String.valueOf(getDirectoryResultLimit(getDirectoryById(directoryId)))); + } + } else { + final Uri baseUri = mUseCallableUri ? Callable.CONTENT_URI : Phone.CONTENT_URI; + builder = baseUri.buildUpon().appendQueryParameter( + ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT)); + if (isSectionHeaderDisplayEnabled()) { + builder.appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true"); + } + applyFilter(loader, builder, directoryId, getFilter()); } - applyFilter(loader, builder, directoryId, getFilter()); - } - // Remove duplicates when it is possible. - builder.appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true"); - loader.setUri(builder.build()); + // Remove duplicates when it is possible. + builder.appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true"); + loader.setUri(builder.build()); - // TODO a projection that includes the search snippet - if (getContactNameDisplayOrder() == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) { - loader.setProjection(PhoneQuery.PROJECTION_PRIMARY); - } else { - loader.setProjection(PhoneQuery.PROJECTION_ALTERNATIVE); - } + // TODO a projection that includes the search snippet + if (getContactNameDisplayOrder() == + ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) { + loader.setProjection(PhoneQuery.PROJECTION_PRIMARY); + } else { + loader.setProjection(PhoneQuery.PROJECTION_ALTERNATIVE); + } - if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) { - loader.setSortOrder(Phone.SORT_KEY_PRIMARY); - } else { - loader.setSortOrder(Phone.SORT_KEY_ALTERNATIVE); + if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) { + loader.setSortOrder(Phone.SORT_KEY_PRIMARY); + } else { + loader.setSortOrder(Phone.SORT_KEY_ALTERNATIVE); + } } } + protected boolean isExtendedDirectory(long directoryId) { + return directoryId >= mFirstExtendedDirectoryId; + } + + private DirectoryPartition getExtendedDirectoryFromId(long directoryId) { + final int directoryIndex = (int) (directoryId - mFirstExtendedDirectoryId); + return mExtendedDirectories.get(directoryIndex); + } + /** * Configure {@code loader} and {@code uriBuilder} according to {@code directoryId} and {@code * filter}. @@ -188,7 +242,12 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { @Override public String getContactDisplayName(int position) { - return ((Cursor) getItem(position)).getString(PhoneQuery.PHONE_DISPLAY_NAME); + return ((Cursor) getItem(position)).getString(PhoneQuery.DISPLAY_NAME); + } + + public String getPhoneNumber(int position) { + final Cursor item = (Cursor)getItem(position); + return item != null ? item.getString(PhoneQuery.PHONE_NUMBER) : null; } /** @@ -197,14 +256,19 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { * @return Uri for the data. may be null if the cursor is not ready. */ public Uri getDataUri(int position) { - Cursor cursor = ((Cursor)getItem(position)); - if (cursor != null) { - long id = cursor.getLong(PhoneQuery.PHONE_ID); - return ContentUris.withAppendedId(Data.CONTENT_URI, id); - } else { - Log.w(TAG, "Cursor was null in getDataUri() call. Returning null instead."); - return null; + final int partitionIndex = getPartitionForPosition(position); + final Cursor item = (Cursor)getItem(position); + return item != null ? getDataUri(partitionIndex, item) : null; + } + + public Uri getDataUri(int partitionIndex, Cursor cursor) { + final long directoryId = + ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId(); + if (!isRemoteDirectory(directoryId)) { + final long phoneId = cursor.getLong(PhoneQuery.PHONE_ID); + return ContentUris.withAppendedId(Data.CONTENT_URI, phoneId); } + return null; } @Override @@ -217,11 +281,35 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { return view; } + protected void setHighlight(ContactListItemView view, Cursor cursor) { + view.setHighlightedPrefix(isSearchMode() ? getUpperCaseQueryString() : null); + } + + // Override default, which would return number of phone numbers, so we + // instead return number of contacts. + @Override + protected int getResultCount(Cursor cursor) { + if (cursor == null) { + return 0; + } + cursor.moveToPosition(-1); + long curContactId = -1; + int numContacts = 0; + while(cursor.moveToNext()) { + final long contactId = cursor.getLong(PhoneQuery.CONTACT_ID); + if (contactId != curContactId) { + curContactId = contactId; + ++numContacts; + } + } + return numContacts; + } + @Override protected void bindView(View itemView, int partition, Cursor cursor, int position) { ContactListItemView view = (ContactListItemView)itemView; - view.setHighlightedPrefix(isSearchMode() ? getUpperCaseQueryString() : null); + setHighlight(view, cursor); // Look at elements before and after this position, checking if contact IDs are same. // If they have one same contact ID, it means they can be grouped. @@ -231,16 +319,16 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { cursor.moveToPosition(position); boolean isFirstEntry = true; boolean showBottomDivider = true; - final long currentContactId = cursor.getLong(PhoneQuery.PHONE_CONTACT_ID); + final long currentContactId = cursor.getLong(PhoneQuery.CONTACT_ID); if (cursor.moveToPrevious() && !cursor.isBeforeFirst()) { - final long previousContactId = cursor.getLong(PhoneQuery.PHONE_CONTACT_ID); + final long previousContactId = cursor.getLong(PhoneQuery.CONTACT_ID); if (currentContactId == previousContactId) { isFirstEntry = false; } } cursor.moveToPosition(position); if (cursor.moveToNext() && !cursor.isAfterLast()) { - final long nextContactId = cursor.getLong(PhoneQuery.PHONE_CONTACT_ID); + final long nextContactId = cursor.getLong(PhoneQuery.CONTACT_ID); if (currentContactId == nextContactId) { // The following entry should be in the same group, which means we don't want a // divider between them. @@ -255,25 +343,28 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { if (isFirstEntry) { bindName(view, cursor); if (isQuickContactEnabled()) { - // No need for photo uri here, because we can not have directory results. If we - // ever do, we need to add photo uri to the query - bindQuickContact(view, partition, cursor, PhoneQuery.PHONE_PHOTO_ID, -1, - PhoneQuery.PHONE_CONTACT_ID, PhoneQuery.PHONE_LOOKUP_KEY); + bindQuickContact(view, partition, cursor, PhoneQuery.PHOTO_ID, + PhoneQuery.PHOTO_URI, PhoneQuery.CONTACT_ID, + PhoneQuery.LOOKUP_KEY); } else { - bindPhoto(view, cursor); + if (getDisplayPhotos()) { + bindPhoto(view, partition, cursor); + } } } else { unbindName(view); view.removePhotoView(true, false); } - bindPhoneNumber(view, cursor); + + final DirectoryPartition directory = (DirectoryPartition) getPartition(partition); + bindPhoneNumber(view, cursor, directory.isDisplayNumber()); view.setDividerVisible(showBottomDivider); } - protected void bindPhoneNumber(ContactListItemView view, Cursor cursor) { + protected void bindPhoneNumber(ContactListItemView view, Cursor cursor, boolean displayNumber) { CharSequence label = null; - if (!cursor.isNull(PhoneQuery.PHONE_TYPE)) { + if (displayNumber && !cursor.isNull(PhoneQuery.PHONE_TYPE)) { final int type = cursor.getInt(PhoneQuery.PHONE_TYPE); final String customLabel = cursor.getString(PhoneQuery.PHONE_LABEL); @@ -281,7 +372,20 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { label = Phone.getTypeLabel(getContext().getResources(), type, customLabel); } view.setLabel(label); - view.showData(cursor, PhoneQuery.PHONE_NUMBER); + final String text; + if (displayNumber) { + text = cursor.getString(PhoneQuery.PHONE_NUMBER); + } else { + // Display phone label. If that's null, display geocoded location for the number + final String phoneLabel = cursor.getString(PhoneQuery.PHONE_LABEL); + if (phoneLabel != null) { + text = phoneLabel; + } else { + final String phoneNumber = cursor.getString(PhoneQuery.PHONE_NUMBER); + text = GeoUtil.getGeocodedLocationFor(mContext, phoneNumber); + } + } + view.setPhoneNumber(text); } protected void bindSectionHeaderAndDivider(final ContactListItemView view, int position) { @@ -296,7 +400,7 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { } protected void bindName(final ContactListItemView view, Cursor cursor) { - view.showDisplayName(cursor, PhoneQuery.PHONE_DISPLAY_NAME, getContactNameDisplayOrder()); + view.showDisplayName(cursor, PhoneQuery.DISPLAY_NAME, getContactNameDisplayOrder()); // Note: we don't show phonetic names any more (see issue 5265330) } @@ -304,13 +408,24 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { view.hideDisplayName(); } - protected void bindPhoto(final ContactListItemView view, Cursor cursor) { + protected void bindPhoto(final ContactListItemView view, int partitionIndex, Cursor cursor) { + if (!isPhotoSupported(partitionIndex)) { + view.removePhotoView(); + return; + } + long photoId = 0; - if (!cursor.isNull(PhoneQuery.PHONE_PHOTO_ID)) { - photoId = cursor.getLong(PhoneQuery.PHONE_PHOTO_ID); + if (!cursor.isNull(PhoneQuery.PHOTO_ID)) { + photoId = cursor.getLong(PhoneQuery.PHOTO_ID); } - getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false); + if (photoId != 0) { + getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false); + } else { + final String photoUriString = cursor.getString(PhoneQuery.PHOTO_URI); + final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString); + getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, false); + } } public void setPhotoPosition(ContactListItemView.PhotoPosition photoPosition) { @@ -328,4 +443,75 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { public boolean usesCallableUri() { return mUseCallableUri; } + + /** + * Override base implementation to inject extended directories between local & remote + * directories. This is done in the following steps: + * 1. Call base implementation to add directories from the cursor. + * 2. Iterate all base directories and establish the following information: + * a. The highest directory id so that we can assign unused id's to the extended directories. + * b. The index of the last non-remote directory. This is where we will insert extended + * directories. + * 3. Iterate the extended directories and for each one, assign an ID and insert it in the + * proper location. + */ + @Override + public void changeDirectories(Cursor cursor) { + super.changeDirectories(cursor); + if (getDirectorySearchMode() == DirectoryListLoader.SEARCH_MODE_NONE) { + return; + } + final int numExtendedDirectories = mExtendedDirectories.size(); + if (getPartitionCount() == cursor.getCount() + numExtendedDirectories) { + // already added all directories; + return; + } + // + mFirstExtendedDirectoryId = Long.MAX_VALUE; + if (numExtendedDirectories > 0) { + // The Directory.LOCAL_INVISIBLE is not in the cursor but we can't reuse it's + // "special" ID. + long maxId = Directory.LOCAL_INVISIBLE; + int insertIndex = 0; + for (int i = 0, n = getPartitionCount(); i < n; i++) { + final DirectoryPartition partition = (DirectoryPartition) getPartition(i); + final long id = partition.getDirectoryId(); + if (id > maxId) { + maxId = id; + } + if (!isRemoteDirectory(id)) { + // assuming remote directories come after local, we will end up with the index + // where we should insert extended directories. This also works if there are no + // remote directories at all. + insertIndex = i + 1; + } + } + // Extended directories ID's cannot collide with base directories + mFirstExtendedDirectoryId = maxId + 1; + for (int i = 0; i < numExtendedDirectories; i++) { + final long id = mFirstExtendedDirectoryId + i; + final DirectoryPartition directory = mExtendedDirectories.get(i); + if (getPartitionByDirectoryId(id) == -1) { + addPartition(insertIndex, directory); + directory.setDirectoryId(id); + } + } + } + } + + protected Uri getContactUri(int partitionIndex, Cursor cursor, + int contactIdColumn, int lookUpKeyColumn) { + final DirectoryPartition directory = (DirectoryPartition) getPartition(partitionIndex); + final long directoryId = directory.getDirectoryId(); + if (!isExtendedDirectory(directoryId)) { + return super.getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn); + } + return Contacts.CONTENT_LOOKUP_URI.buildUpon() + .appendPath(Constants.LOOKUP_URI_ENCODED) + .appendQueryParameter(Directory.DISPLAY_NAME, directory.getLabel()) + .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, + String.valueOf(directoryId)) + .encodedFragment(cursor.getString(lookUpKeyColumn)) + .build(); + } } |