ContactListItemView.java revision 97c281fd4b6a9724f79cfd7dbaf4b4b07300c53c
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 17package com.android.contacts.list; 18 19import com.android.contacts.ContactPresenceIconUtil; 20import com.android.contacts.R; 21import com.android.contacts.widget.TextWithHighlighting; 22import com.android.contacts.widget.TextWithHighlightingFactory; 23 24import android.content.Context; 25import android.content.res.Resources; 26import android.content.res.TypedArray; 27import android.database.CharArrayBuffer; 28import android.database.Cursor; 29import android.graphics.Canvas; 30import android.graphics.Typeface; 31import android.graphics.drawable.Drawable; 32import android.provider.ContactsContract.Contacts; 33import android.provider.ContactsContract.CommonDataKinds.Email; 34import android.provider.ContactsContract.CommonDataKinds.Nickname; 35import android.provider.ContactsContract.CommonDataKinds.Organization; 36import android.text.TextUtils; 37import android.text.TextUtils.TruncateAt; 38import android.util.AttributeSet; 39import android.view.Gravity; 40import android.view.View; 41import android.view.ViewGroup; 42import android.widget.ImageView; 43import android.widget.QuickContactBadge; 44import android.widget.TextView; 45import android.widget.ImageView.ScaleType; 46 47/** 48 * A custom view for an item in the contact list. 49 */ 50public class ContactListItemView extends ViewGroup { 51 52 private static final int QUICK_CONTACT_BADGE_STYLE = 53 com.android.internal.R.attr.quickContactBadgeStyleWindowMedium; 54 55 protected final Context mContext; 56 57 private final int mPreferredHeight; 58 private final int mVerticalDividerMargin; 59 private final int mPaddingTop; 60 private final int mPaddingRight; 61 private final int mPaddingBottom; 62 private final int mPaddingLeft; 63 private final int mGapBetweenImageAndText; 64 private final int mGapBetweenLabelAndData; 65 private final int mCallButtonPadding; 66 private final int mPresenceIconMargin; 67 private final int mHeaderTextWidth; 68 69 private boolean mHorizontalDividerVisible = true; 70 private Drawable mHorizontalDividerDrawable; 71 private int mHorizontalDividerHeight; 72 73 private boolean mVerticalDividerVisible; 74 private Drawable mVerticalDividerDrawable; 75 private int mVerticalDividerWidth; 76 77 private boolean mHeaderVisible; 78 private Drawable mHeaderBackgroundDrawable; 79 private int mHeaderBackgroundHeight; 80 private TextView mHeaderTextView; 81 82 private QuickContactBadge mQuickContact; 83 private ImageView mPhotoView; 84 private TextView mNameTextView; 85 private TextView mPhoneticNameTextView; 86 private DontPressWithParentImageView mCallButton; 87 private TextView mLabelView; 88 private TextView mDataView; 89 private TextView mSnippetView; 90 private ImageView mPresenceIcon; 91 92 private int mPhotoViewWidth; 93 private int mPhotoViewHeight; 94 private int mLine1Height; 95 private int mLine2Height; 96 private int mLine3Height; 97 private int mLine4Height; 98 99 private OnClickListener mCallButtonClickListener; 100 private TextWithHighlightingFactory mTextWithHighlightingFactory; 101 public CharArrayBuffer nameBuffer = new CharArrayBuffer(128); 102 public CharArrayBuffer dataBuffer = new CharArrayBuffer(128); 103 public CharArrayBuffer highlightedTextBuffer = new CharArrayBuffer(128); 104 public TextWithHighlighting textWithHighlighting; 105 public CharArrayBuffer phoneticNameBuffer = new CharArrayBuffer(128); 106 107 private CharSequence mUnknownNameText; 108 109 /** 110 * Special class to allow the parent to be pressed without being pressed itself. 111 * This way the line of a tab can be pressed, but the image itself is not. 112 */ 113 // TODO: understand this 114 private static class DontPressWithParentImageView extends ImageView { 115 116 public DontPressWithParentImageView(Context context, AttributeSet attrs) { 117 super(context, attrs); 118 } 119 120 @Override 121 public void setPressed(boolean pressed) { 122 // If the parent is pressed, do not set to pressed. 123 if (pressed && ((View) getParent()).isPressed()) { 124 return; 125 } 126 super.setPressed(pressed); 127 } 128 } 129 130 public ContactListItemView(Context context, AttributeSet attrs) { 131 super(context, attrs); 132 mContext = context; 133 134 // Obtain preferred item height from the current theme 135 TypedArray a = context.obtainStyledAttributes(null, com.android.internal.R.styleable.Theme); 136 mPreferredHeight = 137 a.getDimensionPixelSize(android.R.styleable.Theme_listPreferredItemHeight, 0); 138 a.recycle(); 139 140 Resources resources = context.getResources(); 141 mVerticalDividerMargin = 142 resources.getDimensionPixelOffset(R.dimen.list_item_vertical_divider_margin); 143 mPaddingTop = 144 resources.getDimensionPixelOffset(R.dimen.list_item_padding_top); 145 mPaddingBottom = 146 resources.getDimensionPixelOffset(R.dimen.list_item_padding_bottom); 147 mPaddingLeft = 148 resources.getDimensionPixelOffset(R.dimen.list_item_padding_left); 149 mPaddingRight = 150 resources.getDimensionPixelOffset(R.dimen.list_item_padding_right); 151 mGapBetweenImageAndText = 152 resources.getDimensionPixelOffset(R.dimen.list_item_gap_between_image_and_text); 153 mGapBetweenLabelAndData = 154 resources.getDimensionPixelOffset(R.dimen.list_item_gap_between_label_and_data); 155 mCallButtonPadding = 156 resources.getDimensionPixelOffset(R.dimen.list_item_call_button_padding); 157 mPresenceIconMargin = 158 resources.getDimensionPixelOffset(R.dimen.list_item_presence_icon_margin); 159 mHeaderTextWidth = 160 resources.getDimensionPixelOffset(R.dimen.list_item_header_text_width); 161 } 162 163 /** 164 * Installs a call button listener. 165 */ 166 public void setOnCallButtonClickListener(OnClickListener callButtonClickListener) { 167 mCallButtonClickListener = callButtonClickListener; 168 } 169 170 public void setTextWithHighlightingFactory(TextWithHighlightingFactory factory) { 171 mTextWithHighlightingFactory = factory; 172 } 173 174 public void setUnknownNameText(CharSequence unknownNameText) { 175 mUnknownNameText = unknownNameText; 176 } 177 178 @Override 179 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 180 // We will match parent's width and wrap content vertically, but make sure 181 // height is no less than listPreferredItemHeight. 182 int width = resolveSize(0, widthMeasureSpec); 183 int height = 0; 184 185 mLine1Height = 0; 186 mLine2Height = 0; 187 mLine3Height = 0; 188 mLine4Height = 0; 189 190 // Obtain the natural dimensions of the name text (we only care about height) 191 mNameTextView.measure(0, 0); 192 193 mLine1Height = mNameTextView.getMeasuredHeight(); 194 195 if (isVisible(mPhoneticNameTextView)) { 196 mPhoneticNameTextView.measure(0, 0); 197 mLine2Height = mPhoneticNameTextView.getMeasuredHeight(); 198 } 199 200 if (isVisible(mLabelView)) { 201 mLabelView.measure(0, 0); 202 mLine3Height = mLabelView.getMeasuredHeight(); 203 } 204 205 if (isVisible(mDataView)) { 206 mDataView.measure(0, 0); 207 mLine3Height = Math.max(mLine3Height, mDataView.getMeasuredHeight()); 208 } 209 210 if (isVisible(mSnippetView)) { 211 mSnippetView.measure(0, 0); 212 mLine4Height = mSnippetView.getMeasuredHeight(); 213 } 214 215 height += mLine1Height + mLine2Height + mLine3Height + mLine4Height 216 + mPaddingTop + mPaddingBottom; 217 218 if (isVisible(mCallButton)) { 219 mCallButton.measure(0, 0); 220 } 221 if (isVisible(mPresenceIcon)) { 222 mPresenceIcon.measure(0, 0); 223 } 224 225 ensurePhotoViewSize(); 226 227 height = Math.max(height, mPhotoViewHeight); 228 height = Math.max(height, mPreferredHeight); 229 230 if (mHeaderVisible) { 231 ensureHeaderBackground(); 232 mHeaderTextView.measure( 233 MeasureSpec.makeMeasureSpec(mHeaderTextWidth, MeasureSpec.EXACTLY), 234 MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY)); 235 height += mHeaderBackgroundHeight; 236 } 237 238 if (mHorizontalDividerVisible) { 239 ensureHorizontalDivider(); 240 height += mHorizontalDividerHeight; 241 } 242 243 setMeasuredDimension(width, height); 244 } 245 246 @Override 247 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 248 int height = bottom - top; 249 int width = right - left; 250 251 // Determine the vertical bounds by laying out the header first. 252 int topBound = 0; 253 254 if (mHeaderVisible) { 255 mHeaderBackgroundDrawable.setBounds( 256 0, 257 0, 258 width, 259 mHeaderBackgroundHeight); 260 mHeaderTextView.layout(0, 0, width, mHeaderBackgroundHeight); 261 topBound += mHeaderBackgroundHeight; 262 } 263 264 // Positions of views on the left are fixed and so are those on the right side. 265 // The stretchable part of the layout is in the middle. So, we will start off 266 // by laying out the left and right sides. Then we will allocate the remainder 267 // to the text fields in the middle. 268 269 int leftBound = layoutLeftSide(height, topBound, mPaddingLeft); 270 int rightBound = layoutRightSide(height, topBound, right); 271 272 if (mHorizontalDividerVisible) { 273 ensureHorizontalDivider(); 274 mHorizontalDividerDrawable.setBounds( 275 0, 276 height - mHorizontalDividerHeight, 277 width, 278 height); 279 } 280 281 topBound += mPaddingTop; 282 int bottomBound = height - mPaddingBottom; 283 284 // Text lines, centered vertically 285 rightBound -= mPaddingRight; 286 287 // Center text vertically 288 int totalTextHeight = mLine1Height + mLine2Height + mLine3Height + mLine4Height; 289 int textTopBound = (bottomBound + topBound - totalTextHeight) / 2; 290 291 mNameTextView.layout(leftBound, 292 textTopBound, 293 rightBound, 294 textTopBound + mLine1Height); 295 296 int dataLeftBound = leftBound; 297 if (isVisible(mPhoneticNameTextView)) { 298 mPhoneticNameTextView.layout(leftBound, 299 textTopBound + mLine1Height, 300 rightBound, 301 textTopBound + mLine1Height + mLine2Height); 302 } 303 304 if (isVisible(mLabelView)) { 305 dataLeftBound = leftBound + mLabelView.getMeasuredWidth(); 306 mLabelView.layout(leftBound, 307 textTopBound + mLine1Height + mLine2Height, 308 dataLeftBound, 309 textTopBound + mLine1Height + mLine2Height + mLine3Height); 310 dataLeftBound += mGapBetweenLabelAndData; 311 } 312 313 if (isVisible(mDataView)) { 314 mDataView.layout(dataLeftBound, 315 textTopBound + mLine1Height + mLine2Height, 316 rightBound, 317 textTopBound + mLine1Height + mLine2Height + mLine3Height); 318 } 319 320 if (isVisible(mSnippetView)) { 321 mSnippetView.layout(leftBound, 322 textTopBound + mLine1Height + mLine2Height + mLine3Height, 323 rightBound, 324 textTopBound + mLine1Height + mLine2Height + mLine3Height + mLine4Height); 325 } 326 } 327 328 /** 329 * Performs layout of the left side of the view 330 * 331 * @return new left boundary 332 */ 333 protected int layoutLeftSide(int height, int topBound, int leftBound) { 334 View photoView = mQuickContact != null ? mQuickContact : mPhotoView; 335 if (photoView != null) { 336 // Center the photo vertically 337 int photoTop = topBound + (height - topBound - mPhotoViewHeight) / 2; 338 photoView.layout( 339 leftBound, 340 photoTop, 341 leftBound + mPhotoViewWidth, 342 photoTop + mPhotoViewHeight); 343 leftBound += mPhotoViewWidth + mGapBetweenImageAndText; 344 } 345 return leftBound; 346 } 347 348 /** 349 * Performs layout of the right side of the view 350 * 351 * @return new right boundary 352 */ 353 protected int layoutRightSide(int height, int topBound, int rightBound) { 354 if (isVisible(mCallButton)) { 355 int buttonWidth = mCallButton.getMeasuredWidth(); 356 rightBound -= buttonWidth; 357 mCallButton.layout( 358 rightBound, 359 topBound, 360 rightBound + buttonWidth, 361 height - mHorizontalDividerHeight); 362 mVerticalDividerVisible = true; 363 ensureVerticalDivider(); 364 rightBound -= mVerticalDividerWidth; 365 mVerticalDividerDrawable.setBounds( 366 rightBound, 367 topBound + mVerticalDividerMargin, 368 rightBound + mVerticalDividerWidth, 369 height - mVerticalDividerMargin); 370 } else { 371 mVerticalDividerVisible = false; 372 } 373 374 if (isVisible(mPresenceIcon)) { 375 int iconWidth = mPresenceIcon.getMeasuredWidth(); 376 rightBound -= mPresenceIconMargin + iconWidth; 377 mPresenceIcon.layout( 378 rightBound, 379 topBound, 380 rightBound + iconWidth, 381 height); 382 } 383 return rightBound; 384 } 385 386 protected boolean isVisible(View view) { 387 return view != null && view.getVisibility() == View.VISIBLE; 388 } 389 390 /** 391 * Loads the drawable for the vertical divider if it has not yet been loaded. 392 */ 393 private void ensureVerticalDivider() { 394 if (mVerticalDividerDrawable == null) { 395 mVerticalDividerDrawable = mContext.getResources().getDrawable( 396 R.drawable.divider_vertical_dark); 397 mVerticalDividerWidth = mVerticalDividerDrawable.getIntrinsicWidth(); 398 } 399 } 400 401 /** 402 * Loads the drawable for the horizontal divider if it has not yet been loaded. 403 */ 404 private void ensureHorizontalDivider() { 405 if (mHorizontalDividerDrawable == null) { 406 mHorizontalDividerDrawable = mContext.getResources().getDrawable( 407 com.android.internal.R.drawable.divider_horizontal_dark_opaque); 408 mHorizontalDividerHeight = mHorizontalDividerDrawable.getIntrinsicHeight(); 409 } 410 } 411 412 /** 413 * Loads the drawable for the header background if it has not yet been loaded. 414 */ 415 private void ensureHeaderBackground() { 416 if (mHeaderBackgroundDrawable == null) { 417 mHeaderBackgroundDrawable = mContext.getResources().getDrawable( 418 android.R.drawable.dark_header); 419 mHeaderBackgroundHeight = mHeaderBackgroundDrawable.getIntrinsicHeight(); 420 } 421 } 422 423 /** 424 * Extracts width and height from the style 425 */ 426 private void ensurePhotoViewSize() { 427 if (mPhotoViewWidth == 0 && mPhotoViewHeight == 0) { 428 TypedArray a = mContext.obtainStyledAttributes(null, 429 com.android.internal.R.styleable.ViewGroup_Layout, 430 QUICK_CONTACT_BADGE_STYLE, 0); 431 mPhotoViewWidth = a.getLayoutDimension( 432 android.R.styleable.ViewGroup_Layout_layout_width, 433 ViewGroup.LayoutParams.WRAP_CONTENT); 434 mPhotoViewHeight = a.getLayoutDimension( 435 android.R.styleable.ViewGroup_Layout_layout_height, 436 ViewGroup.LayoutParams.WRAP_CONTENT); 437 a.recycle(); 438 } 439 } 440 441 @Override 442 public void dispatchDraw(Canvas canvas) { 443 if (mHeaderVisible) { 444 mHeaderBackgroundDrawable.draw(canvas); 445 } 446 if (mHorizontalDividerVisible) { 447 mHorizontalDividerDrawable.draw(canvas); 448 } 449 if (mVerticalDividerVisible) { 450 mVerticalDividerDrawable.draw(canvas); 451 } 452 super.dispatchDraw(canvas); 453 } 454 455 /** 456 * Sets the flag that determines whether a divider should drawn at the bottom 457 * of the view. 458 */ 459 public void setDividerVisible(boolean visible) { 460 mHorizontalDividerVisible = visible; 461 } 462 463 /** 464 * Sets section header or makes it invisible if the title is null. 465 */ 466 public void setSectionHeader(String title) { 467 if (!TextUtils.isEmpty(title)) { 468 if (mHeaderTextView == null) { 469 mHeaderTextView = new TextView(mContext); 470 mHeaderTextView.setTypeface(mHeaderTextView.getTypeface(), Typeface.BOLD); 471 mHeaderTextView.setTextColor(mContext.getResources() 472 .getColor(com.android.internal.R.color.dim_foreground_dark)); 473 mHeaderTextView.setTextSize(14); 474 mHeaderTextView.setGravity(Gravity.CENTER); 475 addView(mHeaderTextView); 476 } 477 mHeaderTextView.setText(title); 478 mHeaderTextView.setVisibility(View.VISIBLE); 479 mHeaderVisible = true; 480 } else { 481 if (mHeaderTextView != null) { 482 mHeaderTextView.setVisibility(View.GONE); 483 } 484 mHeaderVisible = false; 485 } 486 } 487 488 /** 489 * Returns the quick contact badge, creating it if necessary. 490 */ 491 public QuickContactBadge getQuickContact() { 492 if (mQuickContact == null) { 493 mQuickContact = new QuickContactBadge(mContext, null, QUICK_CONTACT_BADGE_STYLE); 494 mQuickContact.setExcludeMimes(new String[] { Contacts.CONTENT_ITEM_TYPE }); 495 addView(mQuickContact); 496 } 497 return mQuickContact; 498 } 499 500 /** 501 * Returns the photo view, creating it if necessary. 502 */ 503 public ImageView getPhotoView() { 504 if (mPhotoView == null) { 505 mPhotoView = new ImageView(mContext, null, QUICK_CONTACT_BADGE_STYLE); 506 // Quick contact style used above will set a background - remove it 507 mPhotoView.setBackgroundDrawable(null); 508 addView(mPhotoView); 509 } 510 return mPhotoView; 511 } 512 513 /** 514 * Removes the photo view. Should not be needed once we start handling different 515 * types of views as different types of views from the List's perspective. 516 * 517 * @deprecated 518 */ 519 @Deprecated 520 public void removePhotoView() { 521 if (mPhotoView != null) { 522 removeView(mPhotoView); 523 mPhotoView = null; 524 } 525 if (mQuickContact != null) { 526 removeView(mQuickContact); 527 mQuickContact = null; 528 } 529 } 530 531 /** 532 * Returns the text view for the contact name, creating it if necessary. 533 */ 534 public TextView getNameTextView() { 535 if (mNameTextView == null) { 536 mNameTextView = new TextView(mContext); 537 mNameTextView.setSingleLine(true); 538 mNameTextView.setEllipsize(TruncateAt.MARQUEE); 539 mNameTextView.setTextAppearance(mContext, android.R.style.TextAppearance_Large); 540 mNameTextView.setGravity(Gravity.CENTER_VERTICAL); 541 addView(mNameTextView); 542 } 543 return mNameTextView; 544 } 545 546 /** 547 * Adds a call button using the supplied arguments as an id and tag. 548 */ 549 public void showCallButton(int id, int tag) { 550 if (mCallButton == null) { 551 mCallButton = new DontPressWithParentImageView(mContext, null); 552 mCallButton.setId(id); 553 mCallButton.setOnClickListener(mCallButtonClickListener); 554 mCallButton.setBackgroundResource(R.drawable.call_background); 555 mCallButton.setImageResource(android.R.drawable.sym_action_call); 556 mCallButton.setPadding(mCallButtonPadding, 0, mCallButtonPadding, 0); 557 mCallButton.setScaleType(ScaleType.CENTER); 558 addView(mCallButton); 559 } 560 561 mCallButton.setTag(tag); 562 mCallButton.setVisibility(View.VISIBLE); 563 } 564 565 public void hideCallButton() { 566 if (mCallButton != null) { 567 mCallButton.setVisibility(View.GONE); 568 } 569 } 570 571 /** 572 * Adds or updates a text view for the phonetic name. 573 */ 574 public void setPhoneticName(char[] text, int size) { 575 if (text == null || size == 0) { 576 if (mPhoneticNameTextView != null) { 577 mPhoneticNameTextView.setVisibility(View.GONE); 578 } 579 } else { 580 getPhoneticNameTextView(); 581 mPhoneticNameTextView.setText(text, 0, size); 582 mPhoneticNameTextView.setVisibility(VISIBLE); 583 } 584 } 585 586 /** 587 * Returns the text view for the phonetic name, creating it if necessary. 588 */ 589 public TextView getPhoneticNameTextView() { 590 if (mPhoneticNameTextView == null) { 591 mPhoneticNameTextView = new TextView(mContext); 592 mPhoneticNameTextView.setSingleLine(true); 593 mPhoneticNameTextView.setEllipsize(TruncateAt.MARQUEE); 594 mPhoneticNameTextView.setTextAppearance(mContext, android.R.style.TextAppearance_Small); 595 mPhoneticNameTextView.setTypeface(mPhoneticNameTextView.getTypeface(), Typeface.BOLD); 596 addView(mPhoneticNameTextView); 597 } 598 return mPhoneticNameTextView; 599 } 600 601 /** 602 * Adds or updates a text view for the data label. 603 */ 604 public void setLabel(CharSequence text) { 605 if (TextUtils.isEmpty(text)) { 606 if (mLabelView != null) { 607 mLabelView.setVisibility(View.GONE); 608 } 609 } else { 610 getLabelView(); 611 mLabelView.setText(text); 612 mLabelView.setVisibility(VISIBLE); 613 } 614 } 615 616 /** 617 * Adds or updates a text view for the data label. 618 */ 619 public void setLabel(char[] text, int size) { 620 if (text == null || size == 0) { 621 if (mLabelView != null) { 622 mLabelView.setVisibility(View.GONE); 623 } 624 } else { 625 getLabelView(); 626 mLabelView.setText(text, 0, size); 627 mLabelView.setVisibility(VISIBLE); 628 } 629 } 630 631 /** 632 * Returns the text view for the data label, creating it if necessary. 633 */ 634 public TextView getLabelView() { 635 if (mLabelView == null) { 636 mLabelView = new TextView(mContext); 637 mLabelView.setSingleLine(true); 638 mLabelView.setEllipsize(TruncateAt.MARQUEE); 639 mLabelView.setTextAppearance(mContext, android.R.style.TextAppearance_Small); 640 mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD); 641 addView(mLabelView); 642 } 643 return mLabelView; 644 } 645 646 /** 647 * Adds or updates a text view for the data element. 648 */ 649 public void setData(char[] text, int size) { 650 if (text == null || size == 0) { 651 if (mDataView != null) { 652 mDataView.setVisibility(View.GONE); 653 } 654 return; 655 } else { 656 getDataView(); 657 mDataView.setText(text, 0, size); 658 mDataView.setVisibility(VISIBLE); 659 } 660 } 661 662 /** 663 * Returns the text view for the data text, creating it if necessary. 664 */ 665 public TextView getDataView() { 666 if (mDataView == null) { 667 mDataView = new TextView(mContext); 668 mDataView.setSingleLine(true); 669 mDataView.setEllipsize(TruncateAt.MARQUEE); 670 mDataView.setTextAppearance(mContext, android.R.style.TextAppearance_Small); 671 addView(mDataView); 672 } 673 return mDataView; 674 } 675 676 /** 677 * Adds or updates a text view for the search snippet. 678 */ 679 public void setSnippet(CharSequence text) { 680 if (TextUtils.isEmpty(text)) { 681 if (mSnippetView != null) { 682 mSnippetView.setVisibility(View.GONE); 683 } 684 } else { 685 getSnippetView(); 686 mSnippetView.setText(text); 687 mSnippetView.setVisibility(VISIBLE); 688 } 689 } 690 691 /** 692 * Returns the text view for the search snippet, creating it if necessary. 693 */ 694 public TextView getSnippetView() { 695 if (mSnippetView == null) { 696 mSnippetView = new TextView(mContext); 697 mSnippetView.setSingleLine(true); 698 mSnippetView.setEllipsize(TruncateAt.MARQUEE); 699 mSnippetView.setTextAppearance(mContext, android.R.style.TextAppearance_Small); 700 mSnippetView.setTypeface(mSnippetView.getTypeface(), Typeface.BOLD); 701 addView(mSnippetView); 702 } 703 return mSnippetView; 704 } 705 706 /** 707 * Adds or updates the presence icon view. 708 */ 709 public void setPresence(Drawable icon) { 710 if (icon != null) { 711 if (mPresenceIcon == null) { 712 mPresenceIcon = new ImageView(mContext); 713 addView(mPresenceIcon); 714 } 715 mPresenceIcon.setImageDrawable(icon); 716 mPresenceIcon.setScaleType(ScaleType.CENTER); 717 mPresenceIcon.setVisibility(View.VISIBLE); 718 } else { 719 if (mPresenceIcon != null) { 720 mPresenceIcon.setVisibility(View.GONE); 721 } 722 } 723 } 724 725 public void showDisplayName(Cursor cursor, int nameColumnIndex, boolean highlightingEnabled, 726 int alternativeNameColumnIndex) { 727 cursor.copyStringToBuffer(nameColumnIndex, nameBuffer); 728 TextView nameView = getNameTextView(); 729 int size = nameBuffer.sizeCopied; 730 if (size != 0) { 731 if (highlightingEnabled) { 732 if (textWithHighlighting == null) { 733 textWithHighlighting = 734 mTextWithHighlightingFactory.createTextWithHighlighting(); 735 } 736 cursor.copyStringToBuffer(alternativeNameColumnIndex, highlightedTextBuffer); 737 textWithHighlighting.setText(nameBuffer, highlightedTextBuffer); 738 nameView.setText(textWithHighlighting); 739 } else { 740 nameView.setText(nameBuffer.data, 0, size); 741 } 742 } else { 743 nameView.setText(mUnknownNameText); 744 } 745 } 746 747 public void showPhoneticName(Cursor cursor, int phoneticNameColumnIndex) { 748 cursor.copyStringToBuffer(phoneticNameColumnIndex, phoneticNameBuffer); 749 int phoneticNameSize = phoneticNameBuffer.sizeCopied; 750 if (phoneticNameSize != 0) { 751 setPhoneticName(phoneticNameBuffer.data, phoneticNameSize); 752 } else { 753 setPhoneticName(null, 0); 754 } 755 } 756 757 /** 758 * Sets the proper icon (star or presence or nothing) 759 */ 760 public void showPresence(Cursor cursor, int presenceColumnIndex) { 761 int serverStatus; 762 if (!cursor.isNull(presenceColumnIndex)) { 763 serverStatus = cursor.getInt(presenceColumnIndex); 764 765 // TODO consider caching these drawables 766 Drawable icon = ContactPresenceIconUtil.getPresenceIcon(getContext(), serverStatus); 767 if (icon != null) { 768 setPresence(icon); 769 } else { 770 setPresence(null); 771 } 772 } else { 773 setPresence(null); 774 } 775 } 776 777 /** 778 * Shows search snippet. 779 */ 780 public void showSnippet(Cursor cursor, int summarySnippetMimetypeColumnIndex, 781 int summarySnippetData1ColumnIndex, int summarySnippetData4ColumnIndex) { 782 String snippet = null; 783 String snippetMimeType = cursor.getString(summarySnippetMimetypeColumnIndex); 784 if (Email.CONTENT_ITEM_TYPE.equals(snippetMimeType)) { 785 String email = cursor.getString(summarySnippetData1ColumnIndex); 786 if (!TextUtils.isEmpty(email)) { 787 snippet = email; 788 } 789 } else if (Organization.CONTENT_ITEM_TYPE.equals(snippetMimeType)) { 790 String company = cursor.getString(summarySnippetData1ColumnIndex); 791 String title = cursor.getString(summarySnippetData4ColumnIndex); 792 if (!TextUtils.isEmpty(company)) { 793 if (!TextUtils.isEmpty(title)) { 794 snippet = company + " / " + title; 795 } else { 796 snippet = company; 797 } 798 } else if (!TextUtils.isEmpty(title)) { 799 snippet = title; 800 } 801 } else if (Nickname.CONTENT_ITEM_TYPE.equals(snippetMimeType)) { 802 String nickname = cursor.getString(summarySnippetData1ColumnIndex); 803 if (!TextUtils.isEmpty(nickname)) { 804 snippet = nickname; 805 } 806 } 807 808 setSnippet(snippet); 809 } 810 811 /** 812 * Shows data element (e.g. phone number). 813 */ 814 public void showData(Cursor cursor, int dataColumnIndex) { 815 cursor.copyStringToBuffer(dataColumnIndex, dataBuffer); 816 setData(dataBuffer.data, dataBuffer.sizeCopied); 817 } 818} 819