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