1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.contacts.list; 17 18 import com.android.contacts.ContactPhotoManager; 19 import com.android.contacts.R; 20 import com.android.contacts.widget.IndexerListAdapter; 21 22 import android.content.Context; 23 import android.content.CursorLoader; 24 import android.database.Cursor; 25 import android.net.Uri; 26 import android.os.Bundle; 27 import android.provider.ContactsContract; 28 import android.provider.ContactsContract.ContactCounts; 29 import android.provider.ContactsContract.Contacts; 30 import android.provider.ContactsContract.Directory; 31 import android.text.TextUtils; 32 import android.util.Log; 33 import android.view.LayoutInflater; 34 import android.view.View; 35 import android.view.ViewGroup; 36 import android.widget.QuickContactBadge; 37 import android.widget.SectionIndexer; 38 import android.widget.TextView; 39 40 import java.util.HashSet; 41 42 /** 43 * Common base class for various contact-related lists, e.g. contact list, phone number list 44 * etc. 45 */ 46 public abstract class ContactEntryListAdapter extends IndexerListAdapter { 47 48 private static final String TAG = "ContactEntryListAdapter"; 49 50 /** 51 * Indicates whether the {@link Directory#LOCAL_INVISIBLE} directory should 52 * be included in the search. 53 */ 54 private static final boolean LOCAL_INVISIBLE_DIRECTORY_ENABLED = false; 55 56 private int mDisplayOrder; 57 private int mSortOrder; 58 59 private boolean mDisplayPhotos; 60 private boolean mQuickContactEnabled; 61 62 /** 63 * indicates if contact queries include profile 64 */ 65 private boolean mIncludeProfile; 66 67 /** 68 * indicates if query results includes a profile 69 */ 70 private boolean mProfileExists; 71 72 private ContactPhotoManager mPhotoLoader; 73 74 private String mQueryString; 75 private char[] mUpperCaseQueryString; 76 private boolean mSearchMode; 77 private int mDirectorySearchMode; 78 private int mDirectoryResultLimit = Integer.MAX_VALUE; 79 80 private boolean mLoading = true; 81 private boolean mEmptyListEnabled = true; 82 83 private boolean mSelectionVisible; 84 85 private ContactListFilter mFilter; 86 private String mContactsCount = ""; 87 private boolean mDarkTheme = false; 88 89 /** Resource used to provide header-text for default filter. */ 90 private CharSequence mDefaultFilterHeaderText; 91 ContactEntryListAdapter(Context context)92 public ContactEntryListAdapter(Context context) { 93 super(context); 94 addPartitions(); 95 setDefaultFilterHeaderText(R.string.local_search_label); 96 } 97 setDefaultFilterHeaderText(int resourceId)98 protected void setDefaultFilterHeaderText(int resourceId) { 99 mDefaultFilterHeaderText = getContext().getResources().getText(resourceId); 100 } 101 102 @Override createPinnedSectionHeaderView(Context context, ViewGroup parent)103 protected View createPinnedSectionHeaderView(Context context, ViewGroup parent) { 104 return new ContactListPinnedHeaderView(context, null); 105 } 106 107 @Override setPinnedSectionTitle(View pinnedHeaderView, String title)108 protected void setPinnedSectionTitle(View pinnedHeaderView, String title) { 109 ((ContactListPinnedHeaderView)pinnedHeaderView).setSectionHeader(title); 110 } 111 112 @Override setPinnedHeaderContactsCount(View header)113 protected void setPinnedHeaderContactsCount(View header) { 114 // Update the header with the contacts count only if a profile header exists 115 // otherwise, the contacts count are shown in the empty profile header view 116 if (mProfileExists) { 117 ((ContactListPinnedHeaderView)header).setCountView(mContactsCount); 118 } else { 119 clearPinnedHeaderContactsCount(header); 120 } 121 } 122 123 @Override clearPinnedHeaderContactsCount(View header)124 protected void clearPinnedHeaderContactsCount(View header) { 125 ((ContactListPinnedHeaderView)header).setCountView(null); 126 } 127 addPartitions()128 protected void addPartitions() { 129 addPartition(createDefaultDirectoryPartition()); 130 } 131 createDefaultDirectoryPartition()132 protected DirectoryPartition createDefaultDirectoryPartition() { 133 DirectoryPartition partition = new DirectoryPartition(true, true); 134 partition.setDirectoryId(Directory.DEFAULT); 135 partition.setDirectoryType(getContext().getString(R.string.contactsList)); 136 partition.setPriorityDirectory(true); 137 partition.setPhotoSupported(true); 138 return partition; 139 } 140 141 /** 142 * Remove all directories after the default directory. This is typically used when contacts 143 * list screens are asked to exit the search mode and thus need to remove all remote directory 144 * results for the search. 145 * 146 * This code assumes that the default directory and directories before that should not be 147 * deleted (e.g. Join screen has "suggested contacts" directory before the default director, 148 * and we should not remove the directory). 149 */ removeDirectoriesAfterDefault()150 /* package */ void removeDirectoriesAfterDefault() { 151 final int partitionCount = getPartitionCount(); 152 for (int i = partitionCount - 1; i >= 0; i--) { 153 final Partition partition = getPartition(i); 154 if ((partition instanceof DirectoryPartition) 155 && ((DirectoryPartition) partition).getDirectoryId() == Directory.DEFAULT) { 156 break; 157 } else { 158 removePartition(i); 159 } 160 } 161 } 162 getPartitionByDirectoryId(long id)163 private int getPartitionByDirectoryId(long id) { 164 int count = getPartitionCount(); 165 for (int i = 0; i < count; i++) { 166 Partition partition = getPartition(i); 167 if (partition instanceof DirectoryPartition) { 168 if (((DirectoryPartition)partition).getDirectoryId() == id) { 169 return i; 170 } 171 } 172 } 173 return -1; 174 } 175 getContactDisplayName(int position)176 public abstract String getContactDisplayName(int position); configureLoader(CursorLoader loader, long directoryId)177 public abstract void configureLoader(CursorLoader loader, long directoryId); 178 179 /** 180 * Marks all partitions as "loading" 181 */ onDataReload()182 public void onDataReload() { 183 boolean notify = false; 184 int count = getPartitionCount(); 185 for (int i = 0; i < count; i++) { 186 Partition partition = getPartition(i); 187 if (partition instanceof DirectoryPartition) { 188 DirectoryPartition directoryPartition = (DirectoryPartition)partition; 189 if (!directoryPartition.isLoading()) { 190 notify = true; 191 } 192 directoryPartition.setStatus(DirectoryPartition.STATUS_NOT_LOADED); 193 } 194 } 195 if (notify) { 196 notifyDataSetChanged(); 197 } 198 } 199 200 @Override clearPartitions()201 public void clearPartitions() { 202 int count = getPartitionCount(); 203 for (int i = 0; i < count; i++) { 204 Partition partition = getPartition(i); 205 if (partition instanceof DirectoryPartition) { 206 DirectoryPartition directoryPartition = (DirectoryPartition)partition; 207 directoryPartition.setStatus(DirectoryPartition.STATUS_NOT_LOADED); 208 } 209 } 210 super.clearPartitions(); 211 } 212 isSearchMode()213 public boolean isSearchMode() { 214 return mSearchMode; 215 } 216 setSearchMode(boolean flag)217 public void setSearchMode(boolean flag) { 218 mSearchMode = flag; 219 } 220 getQueryString()221 public String getQueryString() { 222 return mQueryString; 223 } 224 setQueryString(String queryString)225 public void setQueryString(String queryString) { 226 mQueryString = queryString; 227 if (TextUtils.isEmpty(queryString)) { 228 mUpperCaseQueryString = null; 229 } else { 230 mUpperCaseQueryString = queryString.toUpperCase().toCharArray(); 231 } 232 } 233 getUpperCaseQueryString()234 public char[] getUpperCaseQueryString() { 235 return mUpperCaseQueryString; 236 } 237 getDirectorySearchMode()238 public int getDirectorySearchMode() { 239 return mDirectorySearchMode; 240 } 241 setDirectorySearchMode(int mode)242 public void setDirectorySearchMode(int mode) { 243 mDirectorySearchMode = mode; 244 } 245 getDirectoryResultLimit()246 public int getDirectoryResultLimit() { 247 return mDirectoryResultLimit; 248 } 249 setDirectoryResultLimit(int limit)250 public void setDirectoryResultLimit(int limit) { 251 this.mDirectoryResultLimit = limit; 252 } 253 getContactNameDisplayOrder()254 public int getContactNameDisplayOrder() { 255 return mDisplayOrder; 256 } 257 setContactNameDisplayOrder(int displayOrder)258 public void setContactNameDisplayOrder(int displayOrder) { 259 mDisplayOrder = displayOrder; 260 } 261 getSortOrder()262 public int getSortOrder() { 263 return mSortOrder; 264 } 265 setSortOrder(int sortOrder)266 public void setSortOrder(int sortOrder) { 267 mSortOrder = sortOrder; 268 } 269 setPhotoLoader(ContactPhotoManager photoLoader)270 public void setPhotoLoader(ContactPhotoManager photoLoader) { 271 mPhotoLoader = photoLoader; 272 } 273 getPhotoLoader()274 protected ContactPhotoManager getPhotoLoader() { 275 return mPhotoLoader; 276 } 277 getDisplayPhotos()278 public boolean getDisplayPhotos() { 279 return mDisplayPhotos; 280 } 281 setDisplayPhotos(boolean displayPhotos)282 public void setDisplayPhotos(boolean displayPhotos) { 283 mDisplayPhotos = displayPhotos; 284 } 285 isEmptyListEnabled()286 public boolean isEmptyListEnabled() { 287 return mEmptyListEnabled; 288 } 289 setEmptyListEnabled(boolean flag)290 public void setEmptyListEnabled(boolean flag) { 291 mEmptyListEnabled = flag; 292 } 293 isSelectionVisible()294 public boolean isSelectionVisible() { 295 return mSelectionVisible; 296 } 297 setSelectionVisible(boolean flag)298 public void setSelectionVisible(boolean flag) { 299 this.mSelectionVisible = flag; 300 } 301 isQuickContactEnabled()302 public boolean isQuickContactEnabled() { 303 return mQuickContactEnabled; 304 } 305 setQuickContactEnabled(boolean quickContactEnabled)306 public void setQuickContactEnabled(boolean quickContactEnabled) { 307 mQuickContactEnabled = quickContactEnabled; 308 } 309 shouldIncludeProfile()310 public boolean shouldIncludeProfile() { 311 return mIncludeProfile; 312 } 313 setIncludeProfile(boolean includeProfile)314 public void setIncludeProfile(boolean includeProfile) { 315 mIncludeProfile = includeProfile; 316 } 317 setProfileExists(boolean exists)318 public void setProfileExists(boolean exists) { 319 mProfileExists = exists; 320 // Stick the "ME" header for the profile 321 if (exists) { 322 SectionIndexer indexer = getIndexer(); 323 if (indexer != null) { 324 ((ContactsSectionIndexer) indexer).setProfileHeader( 325 getContext().getString(R.string.user_profile_contacts_list_header)); 326 } 327 } 328 } 329 hasProfile()330 public boolean hasProfile() { 331 return mProfileExists; 332 } 333 setDarkTheme(boolean value)334 public void setDarkTheme(boolean value) { 335 mDarkTheme = value; 336 } 337 configureDirectoryLoader(DirectoryListLoader loader)338 public void configureDirectoryLoader(DirectoryListLoader loader) { 339 loader.setDirectorySearchMode(mDirectorySearchMode); 340 loader.setLocalInvisibleDirectoryEnabled(LOCAL_INVISIBLE_DIRECTORY_ENABLED); 341 } 342 343 /** 344 * Updates partitions according to the directory meta-data contained in the supplied 345 * cursor. 346 */ changeDirectories(Cursor cursor)347 public void changeDirectories(Cursor cursor) { 348 if (cursor.getCount() == 0) { 349 // Directory table must have at least local directory, without which this adapter will 350 // enter very weird state. 351 Log.e(TAG, "Directory search loader returned an empty cursor, which implies we have " + 352 "no directory entries.", new RuntimeException()); 353 return; 354 } 355 HashSet<Long> directoryIds = new HashSet<Long>(); 356 357 int idColumnIndex = cursor.getColumnIndex(Directory._ID); 358 int directoryTypeColumnIndex = cursor.getColumnIndex(DirectoryListLoader.DIRECTORY_TYPE); 359 int displayNameColumnIndex = cursor.getColumnIndex(Directory.DISPLAY_NAME); 360 int photoSupportColumnIndex = cursor.getColumnIndex(Directory.PHOTO_SUPPORT); 361 362 // TODO preserve the order of partition to match those of the cursor 363 // Phase I: add new directories 364 cursor.moveToPosition(-1); 365 while (cursor.moveToNext()) { 366 long id = cursor.getLong(idColumnIndex); 367 directoryIds.add(id); 368 if (getPartitionByDirectoryId(id) == -1) { 369 DirectoryPartition partition = new DirectoryPartition(false, true); 370 partition.setDirectoryId(id); 371 partition.setDirectoryType(cursor.getString(directoryTypeColumnIndex)); 372 partition.setDisplayName(cursor.getString(displayNameColumnIndex)); 373 int photoSupport = cursor.getInt(photoSupportColumnIndex); 374 partition.setPhotoSupported(photoSupport == Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY 375 || photoSupport == Directory.PHOTO_SUPPORT_FULL); 376 addPartition(partition); 377 } 378 } 379 380 // Phase II: remove deleted directories 381 int count = getPartitionCount(); 382 for (int i = count; --i >= 0; ) { 383 Partition partition = getPartition(i); 384 if (partition instanceof DirectoryPartition) { 385 long id = ((DirectoryPartition)partition).getDirectoryId(); 386 if (!directoryIds.contains(id)) { 387 removePartition(i); 388 } 389 } 390 } 391 392 invalidate(); 393 notifyDataSetChanged(); 394 } 395 396 @Override changeCursor(int partitionIndex, Cursor cursor)397 public void changeCursor(int partitionIndex, Cursor cursor) { 398 if (partitionIndex >= getPartitionCount()) { 399 // There is no partition for this data 400 return; 401 } 402 403 Partition partition = getPartition(partitionIndex); 404 if (partition instanceof DirectoryPartition) { 405 ((DirectoryPartition)partition).setStatus(DirectoryPartition.STATUS_LOADED); 406 } 407 408 if (mDisplayPhotos && mPhotoLoader != null && isPhotoSupported(partitionIndex)) { 409 mPhotoLoader.refreshCache(); 410 } 411 412 super.changeCursor(partitionIndex, cursor); 413 414 if (isSectionHeaderDisplayEnabled() && partitionIndex == getIndexedPartition()) { 415 updateIndexer(cursor); 416 } 417 } 418 changeCursor(Cursor cursor)419 public void changeCursor(Cursor cursor) { 420 changeCursor(0, cursor); 421 } 422 423 /** 424 * Updates the indexer, which is used to produce section headers. 425 */ updateIndexer(Cursor cursor)426 private void updateIndexer(Cursor cursor) { 427 if (cursor == null) { 428 setIndexer(null); 429 return; 430 } 431 432 Bundle bundle = cursor.getExtras(); 433 if (bundle.containsKey(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES)) { 434 String sections[] = 435 bundle.getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES); 436 int counts[] = bundle.getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS); 437 setIndexer(new ContactsSectionIndexer(sections, counts)); 438 } else { 439 setIndexer(null); 440 } 441 } 442 443 @Override getViewTypeCount()444 public int getViewTypeCount() { 445 // We need a separate view type for each item type, plus another one for 446 // each type with header, plus one for "other". 447 return getItemViewTypeCount() * 2 + 1; 448 } 449 450 @Override getItemViewType(int partitionIndex, int position)451 public int getItemViewType(int partitionIndex, int position) { 452 int type = super.getItemViewType(partitionIndex, position); 453 if (!isUserProfile(position) 454 && isSectionHeaderDisplayEnabled() 455 && partitionIndex == getIndexedPartition()) { 456 Placement placement = getItemPlacementInSection(position); 457 return placement.firstInSection ? type : getItemViewTypeCount() + type; 458 } else { 459 return type; 460 } 461 } 462 463 @Override isEmpty()464 public boolean isEmpty() { 465 // TODO 466 // if (contactsListActivity.mProviderStatus != ProviderStatus.STATUS_NORMAL) { 467 // return true; 468 // } 469 470 if (!mEmptyListEnabled) { 471 return false; 472 } else if (isSearchMode()) { 473 return TextUtils.isEmpty(getQueryString()); 474 } else if (mLoading) { 475 // We don't want the empty state to show when loading. 476 return false; 477 } else { 478 return super.isEmpty(); 479 } 480 } 481 isLoading()482 public boolean isLoading() { 483 int count = getPartitionCount(); 484 for (int i = 0; i < count; i++) { 485 Partition partition = getPartition(i); 486 if (partition instanceof DirectoryPartition 487 && ((DirectoryPartition) partition).isLoading()) { 488 return true; 489 } 490 } 491 return false; 492 } 493 areAllPartitionsEmpty()494 public boolean areAllPartitionsEmpty() { 495 int count = getPartitionCount(); 496 for (int i = 0; i < count; i++) { 497 if (!isPartitionEmpty(i)) { 498 return false; 499 } 500 } 501 return true; 502 } 503 504 /** 505 * Changes visibility parameters for the default directory partition. 506 */ configureDefaultPartition(boolean showIfEmpty, boolean hasHeader)507 public void configureDefaultPartition(boolean showIfEmpty, boolean hasHeader) { 508 int defaultPartitionIndex = -1; 509 int count = getPartitionCount(); 510 for (int i = 0; i < count; i++) { 511 Partition partition = getPartition(i); 512 if (partition instanceof DirectoryPartition && 513 ((DirectoryPartition)partition).getDirectoryId() == Directory.DEFAULT) { 514 defaultPartitionIndex = i; 515 break; 516 } 517 } 518 if (defaultPartitionIndex != -1) { 519 setShowIfEmpty(defaultPartitionIndex, showIfEmpty); 520 setHasHeader(defaultPartitionIndex, hasHeader); 521 } 522 } 523 524 @Override newHeaderView(Context context, int partition, Cursor cursor, ViewGroup parent)525 protected View newHeaderView(Context context, int partition, Cursor cursor, 526 ViewGroup parent) { 527 LayoutInflater inflater = LayoutInflater.from(context); 528 return inflater.inflate(R.layout.directory_header, parent, false); 529 } 530 531 @Override bindHeaderView(View view, int partitionIndex, Cursor cursor)532 protected void bindHeaderView(View view, int partitionIndex, Cursor cursor) { 533 Partition partition = getPartition(partitionIndex); 534 if (!(partition instanceof DirectoryPartition)) { 535 return; 536 } 537 538 DirectoryPartition directoryPartition = (DirectoryPartition)partition; 539 long directoryId = directoryPartition.getDirectoryId(); 540 TextView labelTextView = (TextView)view.findViewById(R.id.label); 541 TextView displayNameTextView = (TextView)view.findViewById(R.id.display_name); 542 if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) { 543 labelTextView.setText(mDefaultFilterHeaderText); 544 displayNameTextView.setText(null); 545 } else { 546 labelTextView.setText(R.string.directory_search_label); 547 String directoryName = directoryPartition.getDisplayName(); 548 String displayName = !TextUtils.isEmpty(directoryName) 549 ? directoryName 550 : directoryPartition.getDirectoryType(); 551 displayNameTextView.setText(displayName); 552 } 553 554 TextView countText = (TextView)view.findViewById(R.id.count); 555 if (directoryPartition.isLoading()) { 556 countText.setText(R.string.search_results_searching); 557 } else { 558 int count = cursor == null ? 0 : cursor.getCount(); 559 if (directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE 560 && count >= getDirectoryResultLimit()) { 561 countText.setText(mContext.getString( 562 R.string.foundTooManyContacts, getDirectoryResultLimit())); 563 } else { 564 countText.setText(getQuantityText( 565 count, R.string.listFoundAllContactsZero, R.plurals.searchFoundContacts)); 566 } 567 } 568 } 569 570 /** 571 * Checks whether the contact entry at the given position represents the user's profile. 572 */ isUserProfile(int position)573 protected boolean isUserProfile(int position) { 574 // The profile only ever appears in the first position if it is present. So if the position 575 // is anything beyond 0, it can't be the profile. 576 boolean isUserProfile = false; 577 if (position == 0) { 578 int partition = getPartitionForPosition(position); 579 if (partition >= 0) { 580 // Save the old cursor position - the call to getItem() may modify the cursor 581 // position. 582 int offset = getCursor(partition).getPosition(); 583 Cursor cursor = (Cursor) getItem(position); 584 if (cursor != null) { 585 int profileColumnIndex = cursor.getColumnIndex(Contacts.IS_USER_PROFILE); 586 if (profileColumnIndex != -1) { 587 isUserProfile = cursor.getInt(profileColumnIndex) == 1; 588 } 589 // Restore the old cursor position. 590 cursor.moveToPosition(offset); 591 } 592 } 593 } 594 return isUserProfile; 595 } 596 597 // TODO: fix PluralRules to handle zero correctly and use Resources.getQuantityText directly getQuantityText(int count, int zeroResourceId, int pluralResourceId)598 public String getQuantityText(int count, int zeroResourceId, int pluralResourceId) { 599 if (count == 0) { 600 return getContext().getString(zeroResourceId); 601 } else { 602 String format = getContext().getResources() 603 .getQuantityText(pluralResourceId, count).toString(); 604 return String.format(format, count); 605 } 606 } 607 isPhotoSupported(int partitionIndex)608 public boolean isPhotoSupported(int partitionIndex) { 609 Partition partition = getPartition(partitionIndex); 610 if (partition instanceof DirectoryPartition) { 611 return ((DirectoryPartition) partition).isPhotoSupported(); 612 } 613 return true; 614 } 615 616 /** 617 * Returns the currently selected filter. 618 */ getFilter()619 public ContactListFilter getFilter() { 620 return mFilter; 621 } 622 setFilter(ContactListFilter filter)623 public void setFilter(ContactListFilter filter) { 624 mFilter = filter; 625 } 626 627 // TODO: move sharable logic (bindXX() methods) to here with extra arguments 628 629 /** 630 * Loads the photo for the quick contact view and assigns the contact uri. 631 * @param photoIdColumn Index of the photo id column 632 * @param photoUriColumn Index of the photo uri column. Optional: Can be -1 633 * @param contactIdColumn Index of the contact id column 634 * @param lookUpKeyColumn Index of the lookup key column 635 */ bindQuickContact(final ContactListItemView view, int partitionIndex, Cursor cursor, int photoIdColumn, int photoUriColumn, int contactIdColumn, int lookUpKeyColumn)636 protected void bindQuickContact(final ContactListItemView view, int partitionIndex, 637 Cursor cursor, int photoIdColumn, int photoUriColumn, int contactIdColumn, 638 int lookUpKeyColumn) { 639 long photoId = 0; 640 if (!cursor.isNull(photoIdColumn)) { 641 photoId = cursor.getLong(photoIdColumn); 642 } 643 644 QuickContactBadge quickContact = view.getQuickContact(); 645 quickContact.assignContactUri( 646 getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn)); 647 648 if (photoId != 0 || photoUriColumn == -1) { 649 getPhotoLoader().loadThumbnail(quickContact, photoId, mDarkTheme); 650 } else { 651 final String photoUriString = cursor.getString(photoUriColumn); 652 final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString); 653 getPhotoLoader().loadPhoto(quickContact, photoUri, -1, mDarkTheme); 654 } 655 656 } 657 getContactUri(int partitionIndex, Cursor cursor, int contactIdColumn, int lookUpKeyColumn)658 protected Uri getContactUri(int partitionIndex, Cursor cursor, 659 int contactIdColumn, int lookUpKeyColumn) { 660 long contactId = cursor.getLong(contactIdColumn); 661 String lookupKey = cursor.getString(lookUpKeyColumn); 662 Uri uri = Contacts.getLookupUri(contactId, lookupKey); 663 long directoryId = ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId(); 664 if (directoryId != Directory.DEFAULT) { 665 uri = uri.buildUpon().appendQueryParameter( 666 ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build(); 667 } 668 return uri; 669 } 670 setContactsCount(String count)671 public void setContactsCount(String count) { 672 mContactsCount = count; 673 } 674 getContactsCount()675 public String getContactsCount() { 676 return mContactsCount; 677 } 678 } 679