diff options
Diffstat (limited to 'java/com/android/contacts/common/list/ContactEntryListAdapter.java')
-rw-r--r-- | java/com/android/contacts/common/list/ContactEntryListAdapter.java | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/java/com/android/contacts/common/list/ContactEntryListAdapter.java b/java/com/android/contacts/common/list/ContactEntryListAdapter.java new file mode 100644 index 000000000..18bbae382 --- /dev/null +++ b/java/com/android/contacts/common/list/ContactEntryListAdapter.java @@ -0,0 +1,742 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.contacts.common.list; + +import android.content.Context; +import android.content.CursorLoader; +import android.content.res.Resources; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.Directory; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.QuickContactBadge; +import android.widget.SectionIndexer; +import android.widget.TextView; +import com.android.contacts.common.ContactPhotoManager; +import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest; +import com.android.contacts.common.ContactsUtils; +import com.android.contacts.common.R; +import com.android.contacts.common.compat.DirectoryCompat; +import com.android.contacts.common.util.SearchUtil; +import com.android.dialer.compat.CompatUtils; +import java.util.HashSet; + +/** + * Common base class for various contact-related lists, e.g. contact list, phone number list etc. + */ +public abstract class ContactEntryListAdapter extends IndexerListAdapter { + + /** + * Indicates whether the {@link Directory#LOCAL_INVISIBLE} directory should be included in the + * search. + */ + public static final boolean LOCAL_INVISIBLE_DIRECTORY_ENABLED = false; + + private static final String TAG = "ContactEntryListAdapter"; + private int mDisplayOrder; + private int mSortOrder; + + private boolean mDisplayPhotos; + private boolean mCircularPhotos = true; + private boolean mQuickContactEnabled; + private boolean mAdjustSelectionBoundsEnabled; + + /** The root view of the fragment that this adapter is associated with. */ + private View mFragmentRootView; + + private ContactPhotoManager mPhotoLoader; + + private String mQueryString; + private String mUpperCaseQueryString; + private boolean mSearchMode; + private int mDirectorySearchMode; + private int mDirectoryResultLimit = Integer.MAX_VALUE; + + private boolean mEmptyListEnabled = true; + + private boolean mSelectionVisible; + + private ContactListFilter mFilter; + private boolean mDarkTheme = false; + + /** Resource used to provide header-text for default filter. */ + private CharSequence mDefaultFilterHeaderText; + + public ContactEntryListAdapter(Context context) { + super(context); + setDefaultFilterHeaderText(R.string.local_search_label); + addPartitions(); + } + + /** + * @param fragmentRootView Root view of the fragment. This is used to restrict the scope of image + * loading requests that get cancelled on cursor changes. + */ + protected void setFragmentRootView(View fragmentRootView) { + mFragmentRootView = fragmentRootView; + } + + protected void setDefaultFilterHeaderText(int resourceId) { + mDefaultFilterHeaderText = getContext().getResources().getText(resourceId); + } + + @Override + protected ContactListItemView newView( + Context context, int partition, Cursor cursor, int position, ViewGroup parent) { + final ContactListItemView view = new ContactListItemView(context, null); + view.setIsSectionHeaderEnabled(isSectionHeaderDisplayEnabled()); + view.setAdjustSelectionBoundsEnabled(isAdjustSelectionBoundsEnabled()); + return view; + } + + @Override + protected void bindView(View itemView, int partition, Cursor cursor, int position) { + final ContactListItemView view = (ContactListItemView) itemView; + view.setIsSectionHeaderEnabled(isSectionHeaderDisplayEnabled()); + bindWorkProfileIcon(view, partition); + } + + @Override + protected View createPinnedSectionHeaderView(Context context, ViewGroup parent) { + return new ContactListPinnedHeaderView(context, null, parent); + } + + @Override + protected void setPinnedSectionTitle(View pinnedHeaderView, String title) { + ((ContactListPinnedHeaderView) pinnedHeaderView).setSectionHeaderTitle(title); + } + + protected void addPartitions() { + addPartition(createDefaultDirectoryPartition()); + } + + protected DirectoryPartition createDefaultDirectoryPartition() { + DirectoryPartition partition = new DirectoryPartition(true, true); + partition.setDirectoryId(Directory.DEFAULT); + partition.setDirectoryType(getContext().getString(R.string.contactsList)); + partition.setPriorityDirectory(true); + partition.setPhotoSupported(true); + partition.setLabel(mDefaultFilterHeaderText.toString()); + return partition; + } + + /** + * Remove all directories after the default directory. This is typically used when contacts list + * screens are asked to exit the search mode and thus need to remove all remote directory results + * for the search. + * + * <p>This code assumes that the default directory and directories before that should not be + * deleted (e.g. Join screen has "suggested contacts" directory before the default director, and + * we should not remove the directory). + */ + public void removeDirectoriesAfterDefault() { + final int partitionCount = getPartitionCount(); + for (int i = partitionCount - 1; i >= 0; i--) { + final Partition partition = getPartition(i); + if ((partition instanceof DirectoryPartition) + && ((DirectoryPartition) partition).getDirectoryId() == Directory.DEFAULT) { + break; + } else { + removePartition(i); + } + } + } + + protected int getPartitionByDirectoryId(long id) { + int count = getPartitionCount(); + for (int i = 0; i < count; i++) { + Partition partition = getPartition(i); + if (partition instanceof DirectoryPartition) { + if (((DirectoryPartition) partition).getDirectoryId() == id) { + return i; + } + } + } + return -1; + } + + protected DirectoryPartition getDirectoryById(long id) { + int count = getPartitionCount(); + for (int i = 0; i < count; i++) { + Partition partition = getPartition(i); + if (partition instanceof DirectoryPartition) { + final DirectoryPartition directoryPartition = (DirectoryPartition) partition; + if (directoryPartition.getDirectoryId() == id) { + return directoryPartition; + } + } + } + return null; + } + + public abstract void configureLoader(CursorLoader loader, long directoryId); + + /** Marks all partitions as "loading" */ + public void onDataReload() { + boolean notify = false; + int count = getPartitionCount(); + for (int i = 0; i < count; i++) { + Partition partition = getPartition(i); + if (partition instanceof DirectoryPartition) { + DirectoryPartition directoryPartition = (DirectoryPartition) partition; + if (!directoryPartition.isLoading()) { + notify = true; + } + directoryPartition.setStatus(DirectoryPartition.STATUS_NOT_LOADED); + } + } + if (notify) { + notifyDataSetChanged(); + } + } + + @Override + public void clearPartitions() { + int count = getPartitionCount(); + for (int i = 0; i < count; i++) { + Partition partition = getPartition(i); + if (partition instanceof DirectoryPartition) { + DirectoryPartition directoryPartition = (DirectoryPartition) partition; + directoryPartition.setStatus(DirectoryPartition.STATUS_NOT_LOADED); + } + } + super.clearPartitions(); + } + + public boolean isSearchMode() { + return mSearchMode; + } + + public void setSearchMode(boolean flag) { + mSearchMode = flag; + } + + public String getQueryString() { + return mQueryString; + } + + public void setQueryString(String queryString) { + mQueryString = queryString; + if (TextUtils.isEmpty(queryString)) { + mUpperCaseQueryString = null; + } else { + mUpperCaseQueryString = SearchUtil.cleanStartAndEndOfSearchQuery(queryString.toUpperCase()); + } + } + + public String getUpperCaseQueryString() { + return mUpperCaseQueryString; + } + + public int getDirectorySearchMode() { + return mDirectorySearchMode; + } + + public void setDirectorySearchMode(int mode) { + mDirectorySearchMode = mode; + } + + public int getDirectoryResultLimit() { + return mDirectoryResultLimit; + } + + public void setDirectoryResultLimit(int limit) { + this.mDirectoryResultLimit = limit; + } + + public int getDirectoryResultLimit(DirectoryPartition directoryPartition) { + final int limit = directoryPartition.getResultLimit(); + return limit == DirectoryPartition.RESULT_LIMIT_DEFAULT ? mDirectoryResultLimit : limit; + } + + public int getContactNameDisplayOrder() { + return mDisplayOrder; + } + + public void setContactNameDisplayOrder(int displayOrder) { + mDisplayOrder = displayOrder; + } + + public int getSortOrder() { + return mSortOrder; + } + + public void setSortOrder(int sortOrder) { + mSortOrder = sortOrder; + } + + protected ContactPhotoManager getPhotoLoader() { + return mPhotoLoader; + } + + public void setPhotoLoader(ContactPhotoManager photoLoader) { + mPhotoLoader = photoLoader; + } + + public boolean getDisplayPhotos() { + return mDisplayPhotos; + } + + public void setDisplayPhotos(boolean displayPhotos) { + mDisplayPhotos = displayPhotos; + } + + public boolean getCircularPhotos() { + return mCircularPhotos; + } + + public boolean isSelectionVisible() { + return mSelectionVisible; + } + + public void setSelectionVisible(boolean flag) { + this.mSelectionVisible = flag; + } + + public boolean isQuickContactEnabled() { + return mQuickContactEnabled; + } + + public void setQuickContactEnabled(boolean quickContactEnabled) { + mQuickContactEnabled = quickContactEnabled; + } + + public boolean isAdjustSelectionBoundsEnabled() { + return mAdjustSelectionBoundsEnabled; + } + + public void setAdjustSelectionBoundsEnabled(boolean enabled) { + mAdjustSelectionBoundsEnabled = enabled; + } + + public void setProfileExists(boolean exists) { + // Stick the "ME" header for the profile + if (exists) { + setSectionHeader(R.string.user_profile_contacts_list_header, /* # of ME */ 1); + } + } + + private void setSectionHeader(int resId, int numberOfItems) { + SectionIndexer indexer = getIndexer(); + if (indexer != null) { + ((ContactsSectionIndexer) indexer) + .setProfileAndFavoritesHeader(getContext().getString(resId), numberOfItems); + } + } + + public void setDarkTheme(boolean value) { + mDarkTheme = value; + } + + /** Updates partitions according to the directory meta-data contained in the supplied cursor. */ + public void changeDirectories(Cursor cursor) { + if (cursor.getCount() == 0) { + // Directory table must have at least local directory, without which this adapter will + // enter very weird state. + Log.e( + TAG, + "Directory search loader returned an empty cursor, which implies we have " + + "no directory entries.", + new RuntimeException()); + return; + } + HashSet<Long> directoryIds = new HashSet<Long>(); + + int idColumnIndex = cursor.getColumnIndex(Directory._ID); + int directoryTypeColumnIndex = cursor.getColumnIndex(DirectoryListLoader.DIRECTORY_TYPE); + int displayNameColumnIndex = cursor.getColumnIndex(Directory.DISPLAY_NAME); + int photoSupportColumnIndex = cursor.getColumnIndex(Directory.PHOTO_SUPPORT); + + // TODO preserve the order of partition to match those of the cursor + // Phase I: add new directories + cursor.moveToPosition(-1); + while (cursor.moveToNext()) { + long id = cursor.getLong(idColumnIndex); + directoryIds.add(id); + if (getPartitionByDirectoryId(id) == -1) { + DirectoryPartition partition = new DirectoryPartition(false, true); + partition.setDirectoryId(id); + if (DirectoryCompat.isRemoteDirectoryId(id)) { + if (DirectoryCompat.isEnterpriseDirectoryId(id)) { + partition.setLabel(mContext.getString(R.string.directory_search_label_work)); + } else { + partition.setLabel(mContext.getString(R.string.directory_search_label)); + } + } else { + if (DirectoryCompat.isEnterpriseDirectoryId(id)) { + partition.setLabel(mContext.getString(R.string.list_filter_phones_work)); + } else { + partition.setLabel(mDefaultFilterHeaderText.toString()); + } + } + partition.setDirectoryType(cursor.getString(directoryTypeColumnIndex)); + partition.setDisplayName(cursor.getString(displayNameColumnIndex)); + int photoSupport = cursor.getInt(photoSupportColumnIndex); + partition.setPhotoSupported( + photoSupport == Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY + || photoSupport == Directory.PHOTO_SUPPORT_FULL); + addPartition(partition); + } + } + + // Phase II: remove deleted directories + int count = getPartitionCount(); + for (int i = count; --i >= 0; ) { + Partition partition = getPartition(i); + if (partition instanceof DirectoryPartition) { + long id = ((DirectoryPartition) partition).getDirectoryId(); + if (!directoryIds.contains(id)) { + removePartition(i); + } + } + } + + invalidate(); + notifyDataSetChanged(); + } + + @Override + public void changeCursor(int partitionIndex, Cursor cursor) { + if (partitionIndex >= getPartitionCount()) { + // There is no partition for this data + return; + } + + Partition partition = getPartition(partitionIndex); + if (partition instanceof DirectoryPartition) { + ((DirectoryPartition) partition).setStatus(DirectoryPartition.STATUS_LOADED); + } + + if (mDisplayPhotos && mPhotoLoader != null && isPhotoSupported(partitionIndex)) { + mPhotoLoader.refreshCache(); + } + + super.changeCursor(partitionIndex, cursor); + + if (isSectionHeaderDisplayEnabled() && partitionIndex == getIndexedPartition()) { + updateIndexer(cursor); + } + + // When the cursor changes, cancel any pending asynchronous photo loads. + mPhotoLoader.cancelPendingRequests(mFragmentRootView); + } + + public void changeCursor(Cursor cursor) { + changeCursor(0, cursor); + } + + /** Updates the indexer, which is used to produce section headers. */ + private void updateIndexer(Cursor cursor) { + if (cursor == null || cursor.isClosed()) { + setIndexer(null); + return; + } + + Bundle bundle = cursor.getExtras(); + if (bundle.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES) + && bundle.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS)) { + String[] sections = bundle.getStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES); + int[] counts = bundle.getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS); + + if (getExtraStartingSection()) { + // Insert an additional unnamed section at the top of the list. + String[] allSections = new String[sections.length + 1]; + int[] allCounts = new int[counts.length + 1]; + for (int i = 0; i < sections.length; i++) { + allSections[i + 1] = sections[i]; + allCounts[i + 1] = counts[i]; + } + allCounts[0] = 1; + allSections[0] = ""; + setIndexer(new ContactsSectionIndexer(allSections, allCounts)); + } else { + setIndexer(new ContactsSectionIndexer(sections, counts)); + } + } else { + setIndexer(null); + } + } + + protected boolean getExtraStartingSection() { + return false; + } + + @Override + public int getViewTypeCount() { + // We need a separate view type for each item type, plus another one for + // each type with header, plus one for "other". + return getItemViewTypeCount() * 2 + 1; + } + + @Override + public int getItemViewType(int partitionIndex, int position) { + int type = super.getItemViewType(partitionIndex, position); + if (!isUserProfile(position) + && isSectionHeaderDisplayEnabled() + && partitionIndex == getIndexedPartition()) { + Placement placement = getItemPlacementInSection(position); + return placement.firstInSection ? type : getItemViewTypeCount() + type; + } else { + return type; + } + } + + @Override + public boolean isEmpty() { + // TODO + // if (contactsListActivity.mProviderStatus != ProviderStatus.STATUS_NORMAL) { + // return true; + // } + + if (!mEmptyListEnabled) { + return false; + } else if (isSearchMode()) { + return TextUtils.isEmpty(getQueryString()); + } else { + return super.isEmpty(); + } + } + + public boolean isLoading() { + int count = getPartitionCount(); + for (int i = 0; i < count; i++) { + Partition partition = getPartition(i); + if (partition instanceof DirectoryPartition && ((DirectoryPartition) partition).isLoading()) { + return true; + } + } + return false; + } + + /** Changes visibility parameters for the default directory partition. */ + public void configureDefaultPartition(boolean showIfEmpty, boolean hasHeader) { + int defaultPartitionIndex = -1; + int count = getPartitionCount(); + for (int i = 0; i < count; i++) { + Partition partition = getPartition(i); + if (partition instanceof DirectoryPartition + && ((DirectoryPartition) partition).getDirectoryId() == Directory.DEFAULT) { + defaultPartitionIndex = i; + break; + } + } + if (defaultPartitionIndex != -1) { + setShowIfEmpty(defaultPartitionIndex, showIfEmpty); + setHasHeader(defaultPartitionIndex, hasHeader); + } + } + + @Override + protected View newHeaderView(Context context, int partition, Cursor cursor, ViewGroup parent) { + LayoutInflater inflater = LayoutInflater.from(context); + View view = inflater.inflate(R.layout.directory_header, parent, false); + if (!getPinnedPartitionHeadersEnabled()) { + // If the headers are unpinned, there is no need for their background + // color to be non-transparent. Setting this transparent reduces maintenance for + // non-pinned headers. We don't need to bother synchronizing the activity's + // background color with the header background color. + view.setBackground(null); + } + return view; + } + + protected void bindWorkProfileIcon(final ContactListItemView view, int partitionId) { + final Partition partition = getPartition(partitionId); + if (partition instanceof DirectoryPartition) { + final DirectoryPartition directoryPartition = (DirectoryPartition) partition; + final long directoryId = directoryPartition.getDirectoryId(); + final long userType = ContactsUtils.determineUserType(directoryId, null); + view.setWorkProfileIconEnabled(userType == ContactsUtils.USER_TYPE_WORK); + } + } + + @Override + protected void bindHeaderView(View view, int partitionIndex, Cursor cursor) { + Partition partition = getPartition(partitionIndex); + if (!(partition instanceof DirectoryPartition)) { + return; + } + + DirectoryPartition directoryPartition = (DirectoryPartition) partition; + long directoryId = directoryPartition.getDirectoryId(); + TextView labelTextView = (TextView) view.findViewById(R.id.label); + TextView displayNameTextView = (TextView) view.findViewById(R.id.display_name); + labelTextView.setText(directoryPartition.getLabel()); + if (!DirectoryCompat.isRemoteDirectoryId(directoryId)) { + displayNameTextView.setText(null); + } else { + String directoryName = directoryPartition.getDisplayName(); + String displayName = + !TextUtils.isEmpty(directoryName) ? directoryName : directoryPartition.getDirectoryType(); + displayNameTextView.setText(displayName); + } + + final Resources res = getContext().getResources(); + final int headerPaddingTop = + partitionIndex == 1 && getPartition(0).isEmpty() + ? 0 + : res.getDimensionPixelOffset(R.dimen.directory_header_extra_top_padding); + // There should be no extra padding at the top of the first directory header + view.setPaddingRelative( + view.getPaddingStart(), headerPaddingTop, view.getPaddingEnd(), view.getPaddingBottom()); + } + + /** Checks whether the contact entry at the given position represents the user's profile. */ + protected boolean isUserProfile(int position) { + // The profile only ever appears in the first position if it is present. So if the position + // is anything beyond 0, it can't be the profile. + boolean isUserProfile = false; + if (position == 0) { + int partition = getPartitionForPosition(position); + if (partition >= 0) { + // Save the old cursor position - the call to getItem() may modify the cursor + // position. + int offset = getCursor(partition).getPosition(); + Cursor cursor = (Cursor) getItem(position); + if (cursor != null) { + int profileColumnIndex = cursor.getColumnIndex(Contacts.IS_USER_PROFILE); + if (profileColumnIndex != -1) { + isUserProfile = cursor.getInt(profileColumnIndex) == 1; + } + // Restore the old cursor position. + cursor.moveToPosition(offset); + } + } + } + return isUserProfile; + } + + public boolean isPhotoSupported(int partitionIndex) { + Partition partition = getPartition(partitionIndex); + if (partition instanceof DirectoryPartition) { + return ((DirectoryPartition) partition).isPhotoSupported(); + } + return true; + } + + /** Returns the currently selected filter. */ + public ContactListFilter getFilter() { + return mFilter; + } + + public void setFilter(ContactListFilter filter) { + mFilter = filter; + } + + // TODO: move sharable logic (bindXX() methods) to here with extra arguments + + /** + * Loads the photo for the quick contact view and assigns the contact uri. + * + * @param photoIdColumn Index of the photo id column + * @param photoUriColumn Index of the photo uri column. Optional: Can be -1 + * @param contactIdColumn Index of the contact id column + * @param lookUpKeyColumn Index of the lookup key column + * @param displayNameColumn Index of the display name column + */ + protected void bindQuickContact( + final ContactListItemView view, + int partitionIndex, + Cursor cursor, + int photoIdColumn, + int photoUriColumn, + int contactIdColumn, + int lookUpKeyColumn, + int displayNameColumn) { + long photoId = 0; + if (!cursor.isNull(photoIdColumn)) { + photoId = cursor.getLong(photoIdColumn); + } + + QuickContactBadge quickContact = view.getQuickContact(); + quickContact.assignContactUri( + getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn)); + if (CompatUtils.hasPrioritizedMimeType()) { + // The Contacts app never uses the QuickContactBadge. Therefore, it is safe to assume + // that only Dialer will use this QuickContact badge. This means prioritizing the phone + // mimetype here is reasonable. + quickContact.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE); + } + + if (photoId != 0 || photoUriColumn == -1) { + getPhotoLoader().loadThumbnail(quickContact, photoId, mDarkTheme, mCircularPhotos, null); + } else { + final String photoUriString = cursor.getString(photoUriColumn); + final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString); + DefaultImageRequest request = null; + if (photoUri == null) { + request = getDefaultImageRequestFromCursor(cursor, displayNameColumn, lookUpKeyColumn); + } + getPhotoLoader().loadPhoto(quickContact, photoUri, -1, mDarkTheme, mCircularPhotos, request); + } + } + + @Override + public boolean hasStableIds() { + // Whenever bindViewId() is called, the values passed into setId() are stable or + // stable-ish. For example, when one contact is modified we don't expect a second + // contact's Contact._ID values to change. + return true; + } + + protected void bindViewId(final ContactListItemView view, Cursor cursor, int idColumn) { + // Set a semi-stable id, so that talkback won't get confused when the list gets + // refreshed. There is little harm in inserting the same ID twice. + long contactId = cursor.getLong(idColumn); + view.setId((int) (contactId % Integer.MAX_VALUE)); + } + + protected Uri getContactUri( + int partitionIndex, Cursor cursor, int contactIdColumn, int lookUpKeyColumn) { + long contactId = cursor.getLong(contactIdColumn); + String lookupKey = cursor.getString(lookUpKeyColumn); + long directoryId = ((DirectoryPartition) getPartition(partitionIndex)).getDirectoryId(); + Uri uri = Contacts.getLookupUri(contactId, lookupKey); + if (uri != null && directoryId != Directory.DEFAULT) { + uri = + uri.buildUpon() + .appendQueryParameter( + ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)) + .build(); + } + return uri; + } + + /** + * Retrieves the lookup key and display name from a cursor, and returns a {@link + * DefaultImageRequest} containing these contact details + * + * @param cursor Contacts cursor positioned at the current row to retrieve contact details for + * @param displayNameColumn Column index of the display name + * @param lookupKeyColumn Column index of the lookup key + * @return {@link DefaultImageRequest} with the displayName and identifier fields set to the + * display name and lookup key of the contact. + */ + public DefaultImageRequest getDefaultImageRequestFromCursor( + Cursor cursor, int displayNameColumn, int lookupKeyColumn) { + final String displayName = cursor.getString(displayNameColumn); + final String lookupKey = cursor.getString(lookupKeyColumn); + return new DefaultImageRequest(displayName, lookupKey, mCircularPhotos); + } +} |