MessageListItem.java revision fe0faf65c654a87d4d5de8d87713364fcf786a3f
1/*
2 * Copyright (C) 2008 Esmertec AG.
3 * Copyright (C) 2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.mms.ui;
19
20import java.util.Map;
21import java.util.regex.Matcher;
22import java.util.regex.Pattern;
23
24import android.app.AlertDialog;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.Intent;
28import android.graphics.Bitmap;
29import android.graphics.BitmapFactory;
30import android.graphics.Canvas;
31import android.graphics.Paint;
32import android.graphics.Path;
33import android.graphics.Typeface;
34import android.graphics.Paint.FontMetricsInt;
35import android.graphics.drawable.Drawable;
36import android.net.Uri;
37import android.os.Handler;
38import android.os.Message;
39import android.provider.Browser;
40import android.provider.ContactsContract.Profile;
41import android.provider.Telephony.Sms;
42import android.telephony.PhoneNumberUtils;
43import android.text.Html;
44import android.text.SpannableStringBuilder;
45import android.text.TextUtils;
46import android.text.method.HideReturnsTransformationMethod;
47import android.text.style.ForegroundColorSpan;
48import android.text.style.LineHeightSpan;
49import android.text.style.StyleSpan;
50import android.text.style.TextAppearanceSpan;
51import android.text.style.URLSpan;
52import android.util.AttributeSet;
53import android.util.Log;
54import android.view.View;
55import android.view.ViewGroup;
56import android.view.View.OnClickListener;
57import android.widget.ArrayAdapter;
58import android.widget.Button;
59import android.widget.ImageButton;
60import android.widget.ImageView;
61import android.widget.LinearLayout;
62import android.widget.TextView;
63
64import com.android.mms.MmsApp;
65import com.android.mms.R;
66import com.android.mms.data.Contact;
67import com.android.mms.data.WorkingMessage;
68import com.android.mms.transaction.Transaction;
69import com.android.mms.transaction.TransactionBundle;
70import com.android.mms.transaction.TransactionService;
71import com.android.mms.util.DownloadManager;
72import com.android.mms.util.SmileyParser;
73import com.google.android.mms.ContentType;
74import com.google.android.mms.pdu.PduHeaders;
75
76/**
77 * This class provides view of a message in the messages list.
78 */
79public class MessageListItem extends LinearLayout implements
80        SlideViewInterface, OnClickListener {
81    public static final String EXTRA_URLS = "com.android.mms.ExtraUrls";
82
83    private static final String TAG = "MessageListItem";
84    private static final StyleSpan STYLE_BOLD = new StyleSpan(Typeface.BOLD);
85
86    static final int MSG_LIST_EDIT_MMS   = 1;
87    static final int MSG_LIST_EDIT_SMS   = 2;
88
89    private View mMmsView;
90    private ImageView mImageView;
91    private ImageView mLockedIndicator;
92    private ImageView mDeliveredIndicator;
93    private ImageView mDetailsIndicator;
94    private ImageButton mSlideShowButton;
95    private TextView mBodyTextView;
96    private Button mDownloadButton;
97    private TextView mDownloadingLabel;
98    private Handler mHandler;
99    private MessageItem mMessageItem;
100    private String mDefaultCountryIso;
101    private TextView mDateView;
102    public View mMessageBlock;
103    private Path mPath = new Path();
104    private Paint mPaint = new Paint();
105    private QuickContactDivot mAvatar;
106    private boolean mIsLastItemInList;
107    static private Drawable sDefaultContactImage;
108
109    public MessageListItem(Context context) {
110        super(context);
111        mDefaultCountryIso = MmsApp.getApplication().getCurrentCountryIso();
112
113        if (sDefaultContactImage == null) {
114            sDefaultContactImage = context.getResources().getDrawable(R.drawable.ic_contact_picture);
115        }
116    }
117
118    public MessageListItem(Context context, AttributeSet attrs) {
119        super(context, attrs);
120
121        int color = mContext.getResources().getColor(R.color.timestamp_color);
122        mColorSpan = new ForegroundColorSpan(color);
123        mDefaultCountryIso = MmsApp.getApplication().getCurrentCountryIso();
124
125        if (sDefaultContactImage == null) {
126            sDefaultContactImage = context.getResources().getDrawable(R.drawable.ic_contact_picture);
127        }
128    }
129
130    @Override
131    protected void onFinishInflate() {
132        super.onFinishInflate();
133
134        mBodyTextView = (TextView) findViewById(R.id.text_view);
135        mDateView = (TextView) findViewById(R.id.date_view);
136        mLockedIndicator = (ImageView) findViewById(R.id.locked_indicator);
137        mDeliveredIndicator = (ImageView) findViewById(R.id.delivered_indicator);
138        mDetailsIndicator = (ImageView) findViewById(R.id.details_indicator);
139        mAvatar = (QuickContactDivot) findViewById(R.id.avatar);
140        mMessageBlock = findViewById(R.id.message_block);
141    }
142
143    public void bind(MessageItem msgItem, boolean isLastItem) {
144        mMessageItem = msgItem;
145        mIsLastItemInList = isLastItem;
146
147        setLongClickable(false);
148
149        switch (msgItem.mMessageType) {
150            case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
151                bindNotifInd(msgItem);
152                break;
153            default:
154                bindCommonMessage(msgItem);
155                break;
156        }
157    }
158
159    public void unbind() {
160        // Clear all references to the message item, which can contain attachments and other
161        // memory-intensive objects
162        mMessageItem = null;
163        if (mImageView != null) {
164            // Because #setOnClickListener may have set the listener to an object that has the
165            // message item in its closure.
166            mImageView.setOnClickListener(null);
167        }
168        if (mSlideShowButton != null) {
169            // Because #drawPlaybackButton sets the tag to mMessageItem
170            mSlideShowButton.setTag(null);
171        }
172    }
173
174    public MessageItem getMessageItem() {
175        return mMessageItem;
176    }
177
178    public void setMsgListItemHandler(Handler handler) {
179        mHandler = handler;
180    }
181
182    private void bindNotifInd(final MessageItem msgItem) {
183        hideMmsViewIfNeeded();
184
185        String msgSizeText = mContext.getString(R.string.message_size_label)
186                                + String.valueOf((msgItem.mMessageSize + 1023) / 1024)
187                                + mContext.getString(R.string.kilobyte);
188
189        mBodyTextView.setText(formatMessage(msgItem, msgItem.mContact, null, msgItem.mSubject,
190                                            msgItem.mHighlight, msgItem.mTextContentType));
191
192        mDateView.setText(msgSizeText + " " + msgItem.mTimestamp);
193
194        int state = DownloadManager.getInstance().getState(msgItem.mMessageUri);
195        switch (state) {
196            case DownloadManager.STATE_DOWNLOADING:
197                inflateDownloadControls();
198                mDownloadingLabel.setVisibility(View.VISIBLE);
199                mDownloadButton.setVisibility(View.GONE);
200                break;
201            case DownloadManager.STATE_UNSTARTED:
202            case DownloadManager.STATE_TRANSIENT_FAILURE:
203            case DownloadManager.STATE_PERMANENT_FAILURE:
204            default:
205                setLongClickable(true);
206                inflateDownloadControls();
207                mDownloadingLabel.setVisibility(View.GONE);
208                mDownloadButton.setVisibility(View.VISIBLE);
209                mDownloadButton.setOnClickListener(new OnClickListener() {
210                    @Override
211                    public void onClick(View v) {
212                        mDownloadingLabel.setVisibility(View.VISIBLE);
213                        mDownloadButton.setVisibility(View.GONE);
214                        Intent intent = new Intent(mContext, TransactionService.class);
215                        intent.putExtra(TransactionBundle.URI, msgItem.mMessageUri.toString());
216                        intent.putExtra(TransactionBundle.TRANSACTION_TYPE,
217                                Transaction.RETRIEVE_TRANSACTION);
218                        mContext.startService(intent);
219                    }
220                });
221                break;
222        }
223
224        // Hide the indicators.
225        mLockedIndicator.setVisibility(View.GONE);
226        mDeliveredIndicator.setVisibility(View.GONE);
227        mDetailsIndicator.setVisibility(View.GONE);
228        updateAvatarView(msgItem.mAddress, false);
229    }
230
231    private void updateAvatarView(String addr, boolean isSelf) {
232        Drawable avatarDrawable;
233        if (isSelf || !TextUtils.isEmpty(addr)) {
234            Contact contact = isSelf ? Contact.getMe(false) : Contact.get(addr, false);
235            avatarDrawable = contact.getAvatar(mContext, sDefaultContactImage);
236
237            if (isSelf) {
238                mAvatar.assignContactUri(Profile.CONTENT_URI);
239            } else {
240                if (contact.existsInDatabase()) {
241                    mAvatar.assignContactUri(contact.getUri());
242                } else {
243                    mAvatar.assignContactFromPhone(contact.getNumber(), true);
244                }
245            }
246        } else {
247            avatarDrawable = sDefaultContactImage;
248        }
249        mAvatar.setImageDrawable(avatarDrawable);
250    }
251
252    private void bindCommonMessage(final MessageItem msgItem) {
253        if (mDownloadButton != null) {
254            mDownloadButton.setVisibility(View.GONE);
255            mDownloadingLabel.setVisibility(View.GONE);
256        }
257        // Since the message text should be concatenated with the sender's
258        // address(or name), I have to display it here instead of
259        // displaying it by the Presenter.
260        mBodyTextView.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
261
262        boolean isSelf = Sms.isOutgoingFolder(msgItem.mBoxId);
263        String addr = isSelf ? null : msgItem.mAddress;
264        updateAvatarView(addr, isSelf);
265
266        // Get and/or lazily set the formatted message from/on the
267        // MessageItem.  Because the MessageItem instances come from a
268        // cache (currently of size ~50), the hit rate on avoiding the
269        // expensive formatMessage() call is very high.
270        CharSequence formattedMessage = msgItem.getCachedFormattedMessage();
271        if (formattedMessage == null) {
272            formattedMessage = formatMessage(msgItem, msgItem.mContact, msgItem.mBody,
273                                             msgItem.mSubject,
274                                             msgItem.mHighlight, msgItem.mTextContentType);
275        }
276        mBodyTextView.setText(formattedMessage);
277
278        // If we're in the process of sending a message (i.e. pending), then we show a "SENDING..."
279        // string in place of the timestamp.
280        mDateView.setText(msgItem.isSending() ?
281                mContext.getResources().getString(R.string.sending_message) :
282                    msgItem.mTimestamp);
283
284        if (msgItem.isSms()) {
285            hideMmsViewIfNeeded();
286        } else {
287            Presenter presenter = PresenterFactory.getPresenter(
288                    "MmsThumbnailPresenter", mContext,
289                    this, msgItem.mSlideshow);
290            presenter.present();
291
292            if (msgItem.mAttachmentType != WorkingMessage.TEXT) {
293                inflateMmsView();
294                mMmsView.setVisibility(View.VISIBLE);
295                setOnClickListener(msgItem);
296                drawPlaybackButton(msgItem);
297            } else {
298                hideMmsViewIfNeeded();
299            }
300        }
301        drawRightStatusIndicator(msgItem);
302
303        requestLayout();
304    }
305
306    private void hideMmsViewIfNeeded() {
307        if (mMmsView != null) {
308            mMmsView.setVisibility(View.GONE);
309        }
310    }
311
312    @Override
313    public void startAudio() {
314        // TODO Auto-generated method stub
315    }
316
317    @Override
318    public void startVideo() {
319        // TODO Auto-generated method stub
320    }
321
322    @Override
323    public void setAudio(Uri audio, String name, Map<String, ?> extras) {
324        // TODO Auto-generated method stub
325    }
326
327    @Override
328    public void setImage(String name, Bitmap bitmap) {
329        inflateMmsView();
330
331        try {
332            if (null == bitmap) {
333                bitmap = BitmapFactory.decodeResource(getResources(),
334                        R.drawable.ic_missing_thumbnail_picture);
335            }
336            mImageView.setImageBitmap(bitmap);
337            mImageView.setVisibility(VISIBLE);
338        } catch (java.lang.OutOfMemoryError e) {
339            Log.e(TAG, "setImage: out of memory: ", e);
340        }
341    }
342
343    private void inflateMmsView() {
344        if (mMmsView == null) {
345            //inflate the surrounding view_stub
346            findViewById(R.id.mms_layout_view_stub).setVisibility(VISIBLE);
347
348            mMmsView = findViewById(R.id.mms_view);
349            mImageView = (ImageView) findViewById(R.id.image_view);
350            mSlideShowButton = (ImageButton) findViewById(R.id.play_slideshow_button);
351        }
352    }
353
354    private void inflateDownloadControls() {
355        if (mDownloadButton == null) {
356            //inflate the download controls
357            findViewById(R.id.mms_downloading_view_stub).setVisibility(VISIBLE);
358            mDownloadButton = (Button) findViewById(R.id.btn_download_msg);
359            mDownloadingLabel = (TextView) findViewById(R.id.label_downloading);
360        }
361    }
362
363
364    private LineHeightSpan mSpan = new LineHeightSpan() {
365        @Override
366        public void chooseHeight(CharSequence text, int start,
367                int end, int spanstartv, int v, FontMetricsInt fm) {
368            fm.ascent -= 10;
369        }
370    };
371
372    TextAppearanceSpan mTextSmallSpan =
373        new TextAppearanceSpan(mContext, android.R.style.TextAppearance_Small);
374
375    ForegroundColorSpan mColorSpan = null;  // set in ctor
376
377    private CharSequence formatMessage(MessageItem msgItem, String contact, String body,
378                                       String subject, Pattern highlight,
379                                       String contentType) {
380        SpannableStringBuilder buf = new SpannableStringBuilder();
381
382        boolean hasSubject = !TextUtils.isEmpty(subject);
383        SmileyParser parser = SmileyParser.getInstance();
384        if (hasSubject) {
385            CharSequence smilizedSubject = parser.addSmileySpans(subject);
386            // Can't use the normal getString() with extra arguments for string replacement
387            // because it doesn't preserve the SpannableText returned by addSmileySpans.
388            // We have to manually replace the %s with our text.
389            buf.append(TextUtils.replace(mContext.getResources().getString(R.string.inline_subject),
390                    new String[] { "%s" }, new CharSequence[] { smilizedSubject }));
391        }
392
393        if (!TextUtils.isEmpty(body)) {
394            // Converts html to spannable if ContentType is "text/html".
395            if (contentType != null && ContentType.TEXT_HTML.equals(contentType)) {
396                buf.append("\n");
397                buf.append(Html.fromHtml(body));
398            } else {
399                if (hasSubject) {
400                    buf.append(" - ");
401                }
402                buf.append(parser.addSmileySpans(body));
403            }
404        }
405
406        if (highlight != null) {
407            Matcher m = highlight.matcher(buf.toString());
408            while (m.find()) {
409                buf.setSpan(new StyleSpan(Typeface.BOLD), m.start(), m.end(), 0);
410            }
411        }
412        return buf;
413    }
414
415    private void drawPlaybackButton(MessageItem msgItem) {
416        switch (msgItem.mAttachmentType) {
417            case WorkingMessage.SLIDESHOW:
418            case WorkingMessage.AUDIO:
419            case WorkingMessage.VIDEO:
420                // Show the 'Play' button and bind message info on it.
421                mSlideShowButton.setTag(msgItem);
422                // Set call-back for the 'Play' button.
423                mSlideShowButton.setOnClickListener(this);
424                mSlideShowButton.setVisibility(View.VISIBLE);
425                setLongClickable(true);
426
427                // When we show the mSlideShowButton, this list item's onItemClickListener doesn't
428                // get called. (It gets set in ComposeMessageActivity:
429                // mMsgListView.setOnItemClickListener) Here we explicitly set the item's
430                // onClickListener. It allows the item to respond to embedded html links and at the
431                // same time, allows the slide show play button to work.
432                setOnClickListener(new OnClickListener() {
433                    @Override
434                    public void onClick(View v) {
435                        onMessageListItemClick();
436                    }
437                });
438                break;
439            default:
440                mSlideShowButton.setVisibility(View.GONE);
441                break;
442        }
443    }
444
445    // OnClick Listener for the playback button
446    @Override
447    public void onClick(View v) {
448        MessageItem mi = (MessageItem) v.getTag();
449        switch (mi.mAttachmentType) {
450            case WorkingMessage.VIDEO:
451            case WorkingMessage.AUDIO:
452            case WorkingMessage.SLIDESHOW:
453                MessageUtils.viewMmsMessageAttachment(mContext, mi.mMessageUri, mi.mSlideshow);
454                break;
455        }
456    }
457
458    public void onMessageListItemClick() {
459        // If the message is a failed one, clicking it should reload it in the compose view,
460        // regardless of whether it has links in it
461        if (mMessageItem != null &&
462                mMessageItem.isOutgoingMessage() &&
463                mMessageItem.isFailedMessage() ) {
464            recomposeFailedMessage();
465            return;
466        }
467
468        // Check for links. If none, do nothing; if 1, open it; if >1, ask user to pick one
469        URLSpan[] spans = mBodyTextView.getUrls();
470
471        if (spans.length == 0) {
472            // Do nothing.
473        } else if (spans.length == 1) {
474            Uri uri = Uri.parse(spans[0].getURL());
475            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
476            intent.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName());
477            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
478            mContext.startActivity(intent);
479        } else {
480            final java.util.ArrayList<String> urls = MessageUtils.extractUris(spans);
481
482            ArrayAdapter<String> adapter =
483                new ArrayAdapter<String>(mContext, android.R.layout.select_dialog_item, urls) {
484                @Override
485                public View getView(int position, View convertView, ViewGroup parent) {
486                    View v = super.getView(position, convertView, parent);
487                    try {
488                        String url = getItem(position).toString();
489                        TextView tv = (TextView) v;
490                        Drawable d = mContext.getPackageManager().getActivityIcon(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
491                        if (d != null) {
492                            d.setBounds(0, 0, d.getIntrinsicHeight(), d.getIntrinsicHeight());
493                            tv.setCompoundDrawablePadding(10);
494                            tv.setCompoundDrawables(d, null, null, null);
495                        }
496                        final String telPrefix = "tel:";
497                        if (url.startsWith(telPrefix)) {
498                            url = PhoneNumberUtils.formatNumber(
499                                            url.substring(telPrefix.length()), mDefaultCountryIso);
500                        }
501                        tv.setText(url);
502                    } catch (android.content.pm.PackageManager.NameNotFoundException ex) {
503                        // it's ok if we're unable to set the drawable for this view - the user
504                        // can still use it
505                    }
506                    return v;
507                }
508            };
509
510            AlertDialog.Builder b = new AlertDialog.Builder(mContext);
511
512            DialogInterface.OnClickListener click = new DialogInterface.OnClickListener() {
513                @Override
514                public final void onClick(DialogInterface dialog, int which) {
515                    if (which >= 0) {
516                        Uri uri = Uri.parse(urls.get(which));
517                        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
518                        intent.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName());
519                        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
520                        mContext.startActivity(intent);
521                    }
522                    dialog.dismiss();
523                }
524            };
525
526            b.setTitle(R.string.select_link_title);
527            b.setCancelable(true);
528            b.setAdapter(adapter, click);
529
530            b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
531                @Override
532                public final void onClick(DialogInterface dialog, int which) {
533                    dialog.dismiss();
534                }
535            });
536
537            b.show();
538        }
539    }
540
541    private void setOnClickListener(final MessageItem msgItem) {
542        switch(msgItem.mAttachmentType) {
543        case WorkingMessage.IMAGE:
544        case WorkingMessage.VIDEO:
545            mImageView.setOnClickListener(new OnClickListener() {
546                @Override
547                public void onClick(View v) {
548                    MessageUtils.viewMmsMessageAttachment(mContext, null, msgItem.mSlideshow);
549                }
550            });
551            mImageView.setOnLongClickListener(new OnLongClickListener() {
552                @Override
553                public boolean onLongClick(View v) {
554                    return v.showContextMenu();
555                }
556            });
557            break;
558
559        default:
560            mImageView.setOnClickListener(null);
561            break;
562        }
563    }
564
565    /**
566     * Assuming the current message is a failed one, reload it into the compose view so that the
567     * user can resend it.
568     */
569    private void recomposeFailedMessage() {
570        String type = mMessageItem.mType;
571        final int what;
572        if (type.equals("sms")) {
573            what = MSG_LIST_EDIT_SMS;
574        } else {
575            what = MSG_LIST_EDIT_MMS;
576        }
577        if (null != mHandler) {
578            Message msg = Message.obtain(mHandler, what);
579            msg.obj = new Long(mMessageItem.mMsgId);
580            msg.sendToTarget();
581        }
582    }
583
584    private void drawRightStatusIndicator(MessageItem msgItem) {
585        // Locked icon
586        if (msgItem.mLocked) {
587            mLockedIndicator.setImageResource(R.drawable.ic_lock_message_sms);
588            mLockedIndicator.setVisibility(View.VISIBLE);
589        } else {
590            mLockedIndicator.setVisibility(View.GONE);
591        }
592
593        // Delivery icon
594        if ((msgItem.isOutgoingMessage() && msgItem.isFailedMessage()) ||
595                msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.FAILED) {
596            mDeliveredIndicator.setImageResource(R.drawable.ic_list_alert_sms_failed);
597            mDeliveredIndicator.setVisibility(View.VISIBLE);
598        } else if (msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.RECEIVED) {
599            mDeliveredIndicator.setImageResource(R.drawable.ic_sms_mms_delivered);
600            mDeliveredIndicator.setVisibility(View.VISIBLE);
601        } else {
602            mDeliveredIndicator.setVisibility(View.GONE);
603        }
604
605        // Message details icon
606        if (msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.INFO || msgItem.mReadReport) {
607            mDetailsIndicator.setImageResource(R.drawable.ic_sms_mms_details);
608            mDetailsIndicator.setVisibility(View.VISIBLE);
609        } else {
610            mDetailsIndicator.setVisibility(View.GONE);
611        }
612    }
613
614    @Override
615    public void setImageRegionFit(String fit) {
616        // TODO Auto-generated method stub
617    }
618
619    @Override
620    public void setImageVisibility(boolean visible) {
621        // TODO Auto-generated method stub
622    }
623
624    @Override
625    public void setText(String name, String text) {
626        // TODO Auto-generated method stub
627    }
628
629    @Override
630    public void setTextVisibility(boolean visible) {
631        // TODO Auto-generated method stub
632    }
633
634    @Override
635    public void setVideo(String name, Uri video) {
636        inflateMmsView();
637
638        try {
639            Bitmap bitmap = VideoAttachmentView.createVideoThumbnail(mContext, video);
640            if (null == bitmap) {
641                bitmap = BitmapFactory.decodeResource(getResources(),
642                        R.drawable.ic_missing_thumbnail_video);
643            }
644            mImageView.setImageBitmap(bitmap);
645            mImageView.setVisibility(VISIBLE);
646        } catch (java.lang.OutOfMemoryError e) {
647            Log.e(TAG, "setVideo: out of memory: ", e);
648        }
649    }
650
651    @Override
652    public void setVideoVisibility(boolean visible) {
653        // TODO Auto-generated method stub
654    }
655
656    @Override
657    public void stopAudio() {
658        // TODO Auto-generated method stub
659    }
660
661    @Override
662    public void stopVideo() {
663        // TODO Auto-generated method stub
664    }
665
666    @Override
667    public void reset() {
668        if (mImageView != null) {
669            mImageView.setVisibility(GONE);
670        }
671    }
672
673    @Override
674    public void setVisibility(boolean visible) {
675        // TODO Auto-generated method stub
676    }
677
678    @Override
679    public void pauseAudio() {
680        // TODO Auto-generated method stub
681
682    }
683
684    @Override
685    public void pauseVideo() {
686        // TODO Auto-generated method stub
687
688    }
689
690    @Override
691    public void seekAudio(int seekTo) {
692        // TODO Auto-generated method stub
693
694    }
695
696    @Override
697    public void seekVideo(int seekTo) {
698        // TODO Auto-generated method stub
699
700    }
701
702    /**
703     * Override dispatchDraw so that we can put our own background and border in.
704     * This is all complexity to support a shared border from one item to the next.
705     */
706    @Override
707    public void dispatchDraw(Canvas c) {
708        View v = mMessageBlock;
709        if (v != null) {
710            float l = v.getX();
711            float t = v.getY();
712            float r = v.getX() + v.getWidth();
713            float b = v.getY() + v.getHeight();
714
715            Path path = mPath;
716            path.reset();
717
718            super.dispatchDraw(c);
719
720            path.reset();
721
722            r -= 1;
723
724            // This block of code draws the border around the "message block" section
725            // of the layout.  This would normally be a simple rectangle but we omit
726            // the border at the point of the avatar's divot.  Also, the bottom is drawn
727            // 1 pixel below our own bounds to get it to line up with the border of
728            // the next item.
729            //
730            // But for the last item we draw the bottom in our own bounds -- so it will
731            // show up.
732            if (mIsLastItemInList) {
733                b -= 1;
734            }
735            if (mAvatar.getPosition() == Divot.RIGHT_UPPER) {
736                path.moveTo(l, t + mAvatar.getCloseOffset());
737                path.lineTo(l, t);
738                path.lineTo(r, t);
739                path.lineTo(r, b);
740                path.lineTo(l, b);
741                path.lineTo(l, t + mAvatar.getFarOffset());
742            } else if (mAvatar.getPosition() == Divot.LEFT_UPPER) {
743                path.moveTo(r, t + mAvatar.getCloseOffset());
744                path.lineTo(r, t);
745                path.lineTo(l, t);
746                path.lineTo(l, b);
747                path.lineTo(r, b);
748                path.lineTo(r, t + mAvatar.getFarOffset());
749            }
750
751            Paint paint = mPaint;
752//            paint.setColor(0xff00ff00);
753            paint.setColor(0xffcccccc);
754            paint.setStrokeWidth(1F);
755            paint.setStyle(Paint.Style.STROKE);
756            c.drawPath(path, paint);
757        } else {
758            super.dispatchDraw(c);
759        }
760    }
761}
762