MessageListItem.java revision 44e86cc021e0656602ecdb6afa38051cfebea68d
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        if (hasSubject) {
384            buf.append(mContext.getResources().getString(R.string.inline_subject, subject));
385        }
386
387        if (!TextUtils.isEmpty(body)) {
388            // Converts html to spannable if ContentType is "text/html".
389            if (contentType != null && ContentType.TEXT_HTML.equals(contentType)) {
390                buf.append("\n");
391                buf.append(Html.fromHtml(body));
392            } else {
393                if (hasSubject) {
394                    buf.append(" - ");
395                }
396                SmileyParser parser = SmileyParser.getInstance();
397                buf.append(parser.addSmileySpans(body));
398            }
399        }
400
401        if (highlight != null) {
402            Matcher m = highlight.matcher(buf.toString());
403            while (m.find()) {
404                buf.setSpan(new StyleSpan(Typeface.BOLD), m.start(), m.end(), 0);
405            }
406        }
407        return buf;
408    }
409
410    private void drawPlaybackButton(MessageItem msgItem) {
411        switch (msgItem.mAttachmentType) {
412            case WorkingMessage.SLIDESHOW:
413            case WorkingMessage.AUDIO:
414            case WorkingMessage.VIDEO:
415                // Show the 'Play' button and bind message info on it.
416                mSlideShowButton.setTag(msgItem);
417                // Set call-back for the 'Play' button.
418                mSlideShowButton.setOnClickListener(this);
419                mSlideShowButton.setVisibility(View.VISIBLE);
420                setLongClickable(true);
421
422                // When we show the mSlideShowButton, this list item's onItemClickListener doesn't
423                // get called. (It gets set in ComposeMessageActivity:
424                // mMsgListView.setOnItemClickListener) Here we explicitly set the item's
425                // onClickListener. It allows the item to respond to embedded html links and at the
426                // same time, allows the slide show play button to work.
427                setOnClickListener(new OnClickListener() {
428                    @Override
429                    public void onClick(View v) {
430                        onMessageListItemClick();
431                    }
432                });
433                break;
434            default:
435                mSlideShowButton.setVisibility(View.GONE);
436                break;
437        }
438    }
439
440    // OnClick Listener for the playback button
441    @Override
442    public void onClick(View v) {
443        MessageItem mi = (MessageItem) v.getTag();
444        switch (mi.mAttachmentType) {
445            case WorkingMessage.VIDEO:
446            case WorkingMessage.AUDIO:
447            case WorkingMessage.SLIDESHOW:
448                MessageUtils.viewMmsMessageAttachment(mContext, mi.mMessageUri, mi.mSlideshow);
449                break;
450        }
451    }
452
453    public void onMessageListItemClick() {
454        // If the message is a failed one, clicking it should reload it in the compose view,
455        // regardless of whether it has links in it
456        if (mMessageItem != null &&
457                mMessageItem.isOutgoingMessage() &&
458                mMessageItem.isFailedMessage() ) {
459            recomposeFailedMessage();
460            return;
461        }
462
463        // Check for links. If none, do nothing; if 1, open it; if >1, ask user to pick one
464        URLSpan[] spans = mBodyTextView.getUrls();
465
466        if (spans.length == 0) {
467            // Do nothing.
468        } else if (spans.length == 1) {
469            Uri uri = Uri.parse(spans[0].getURL());
470            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
471            intent.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName());
472            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
473            mContext.startActivity(intent);
474        } else {
475            final java.util.ArrayList<String> urls = MessageUtils.extractUris(spans);
476
477            ArrayAdapter<String> adapter =
478                new ArrayAdapter<String>(mContext, android.R.layout.select_dialog_item, urls) {
479                @Override
480                public View getView(int position, View convertView, ViewGroup parent) {
481                    View v = super.getView(position, convertView, parent);
482                    try {
483                        String url = getItem(position).toString();
484                        TextView tv = (TextView) v;
485                        Drawable d = mContext.getPackageManager().getActivityIcon(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
486                        if (d != null) {
487                            d.setBounds(0, 0, d.getIntrinsicHeight(), d.getIntrinsicHeight());
488                            tv.setCompoundDrawablePadding(10);
489                            tv.setCompoundDrawables(d, null, null, null);
490                        }
491                        final String telPrefix = "tel:";
492                        if (url.startsWith(telPrefix)) {
493                            url = PhoneNumberUtils.formatNumber(
494                                            url.substring(telPrefix.length()), mDefaultCountryIso);
495                        }
496                        tv.setText(url);
497                    } catch (android.content.pm.PackageManager.NameNotFoundException ex) {
498                        // it's ok if we're unable to set the drawable for this view - the user
499                        // can still use it
500                    }
501                    return v;
502                }
503            };
504
505            AlertDialog.Builder b = new AlertDialog.Builder(mContext);
506
507            DialogInterface.OnClickListener click = new DialogInterface.OnClickListener() {
508                @Override
509                public final void onClick(DialogInterface dialog, int which) {
510                    if (which >= 0) {
511                        Uri uri = Uri.parse(urls.get(which));
512                        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
513                        intent.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName());
514                        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
515                        mContext.startActivity(intent);
516                    }
517                    dialog.dismiss();
518                }
519            };
520
521            b.setTitle(R.string.select_link_title);
522            b.setCancelable(true);
523            b.setAdapter(adapter, click);
524
525            b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
526                @Override
527                public final void onClick(DialogInterface dialog, int which) {
528                    dialog.dismiss();
529                }
530            });
531
532            b.show();
533        }
534    }
535
536    private void setOnClickListener(final MessageItem msgItem) {
537        switch(msgItem.mAttachmentType) {
538        case WorkingMessage.IMAGE:
539        case WorkingMessage.VIDEO:
540            mImageView.setOnClickListener(new OnClickListener() {
541                @Override
542                public void onClick(View v) {
543                    MessageUtils.viewMmsMessageAttachment(mContext, null, msgItem.mSlideshow);
544                }
545            });
546            mImageView.setOnLongClickListener(new OnLongClickListener() {
547                @Override
548                public boolean onLongClick(View v) {
549                    return v.showContextMenu();
550                }
551            });
552            break;
553
554        default:
555            mImageView.setOnClickListener(null);
556            break;
557        }
558    }
559
560    /**
561     * Assuming the current message is a failed one, reload it into the compose view so that the
562     * user can resend it.
563     */
564    private void recomposeFailedMessage() {
565        String type = mMessageItem.mType;
566        final int what;
567        if (type.equals("sms")) {
568            what = MSG_LIST_EDIT_SMS;
569        } else {
570            what = MSG_LIST_EDIT_MMS;
571        }
572        if (null != mHandler) {
573            Message msg = Message.obtain(mHandler, what);
574            msg.obj = new Long(mMessageItem.mMsgId);
575            msg.sendToTarget();
576        }
577    }
578
579    private void drawRightStatusIndicator(MessageItem msgItem) {
580        // Locked icon
581        if (msgItem.mLocked) {
582            mLockedIndicator.setImageResource(R.drawable.ic_lock_message_sms);
583            mLockedIndicator.setVisibility(View.VISIBLE);
584        } else {
585            mLockedIndicator.setVisibility(View.GONE);
586        }
587
588        // Delivery icon
589        if ((msgItem.isOutgoingMessage() && msgItem.isFailedMessage()) ||
590                msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.FAILED) {
591            mDeliveredIndicator.setImageResource(R.drawable.ic_list_alert_sms_failed);
592            mDeliveredIndicator.setVisibility(View.VISIBLE);
593        } else if (msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.RECEIVED) {
594            mDeliveredIndicator.setImageResource(R.drawable.ic_sms_mms_delivered);
595            mDeliveredIndicator.setVisibility(View.VISIBLE);
596        } else {
597            mDeliveredIndicator.setVisibility(View.GONE);
598        }
599
600        // Message details icon
601        if (msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.INFO || msgItem.mReadReport) {
602            mDetailsIndicator.setImageResource(R.drawable.ic_sms_mms_details);
603            mDetailsIndicator.setVisibility(View.VISIBLE);
604        } else {
605            mDetailsIndicator.setVisibility(View.GONE);
606        }
607    }
608
609    @Override
610    public void setImageRegionFit(String fit) {
611        // TODO Auto-generated method stub
612    }
613
614    @Override
615    public void setImageVisibility(boolean visible) {
616        // TODO Auto-generated method stub
617    }
618
619    @Override
620    public void setText(String name, String text) {
621        // TODO Auto-generated method stub
622    }
623
624    @Override
625    public void setTextVisibility(boolean visible) {
626        // TODO Auto-generated method stub
627    }
628
629    @Override
630    public void setVideo(String name, Uri video) {
631        inflateMmsView();
632
633        try {
634            Bitmap bitmap = VideoAttachmentView.createVideoThumbnail(mContext, video);
635            if (null == bitmap) {
636                bitmap = BitmapFactory.decodeResource(getResources(),
637                        R.drawable.ic_missing_thumbnail_video);
638            }
639            mImageView.setImageBitmap(bitmap);
640            mImageView.setVisibility(VISIBLE);
641        } catch (java.lang.OutOfMemoryError e) {
642            Log.e(TAG, "setVideo: out of memory: ", e);
643        }
644    }
645
646    @Override
647    public void setVideoVisibility(boolean visible) {
648        // TODO Auto-generated method stub
649    }
650
651    @Override
652    public void stopAudio() {
653        // TODO Auto-generated method stub
654    }
655
656    @Override
657    public void stopVideo() {
658        // TODO Auto-generated method stub
659    }
660
661    @Override
662    public void reset() {
663        if (mImageView != null) {
664            mImageView.setVisibility(GONE);
665        }
666    }
667
668    @Override
669    public void setVisibility(boolean visible) {
670        // TODO Auto-generated method stub
671    }
672
673    @Override
674    public void pauseAudio() {
675        // TODO Auto-generated method stub
676
677    }
678
679    @Override
680    public void pauseVideo() {
681        // TODO Auto-generated method stub
682
683    }
684
685    @Override
686    public void seekAudio(int seekTo) {
687        // TODO Auto-generated method stub
688
689    }
690
691    @Override
692    public void seekVideo(int seekTo) {
693        // TODO Auto-generated method stub
694
695    }
696
697    /**
698     * Override dispatchDraw so that we can put our own background and border in.
699     * This is all complexity to support a shared border from one item to the next.
700     */
701    @Override
702    public void dispatchDraw(Canvas c) {
703        View v = mMessageBlock;
704        if (v != null) {
705            float l = v.getX();
706            float t = v.getY();
707            float r = v.getX() + v.getWidth();
708            float b = v.getY() + v.getHeight();
709
710            Path path = mPath;
711            path.reset();
712
713            // This block of code draws our own background but omits the top pixel so that
714            // if the previous item draws it's border there we don't overwrite it.
715            path.moveTo(l, t + 1);
716            path.lineTo(r, t + 1);
717            path.lineTo(r, b);
718            path.lineTo(l, b);
719            path.close();
720
721            Paint paint = mPaint;
722            paint.setStyle(Paint.Style.FILL);
723            paint.setColor(0xffffffff);
724            c.drawPath(path, paint);
725
726            super.dispatchDraw(c);
727
728            path.reset();
729
730            r -= 1;
731
732
733            // This block of code draws the border around the "message block" section
734            // of the layout.  This would normally be a simple rectangle but we omit
735            // the border at the point of the avatar's divot.  Also, the bottom is drawn
736            // 1 pixel below our own bounds to get it to line up with the border of
737            // the next item.
738            //
739            // But for the last item we draw the bottom in our own bounds -- so it will
740            // show up.
741            if (mIsLastItemInList) {
742                b -= 1;
743            }
744            if (mAvatar.getPosition() == Divot.RIGHT_UPPER) {
745                path.moveTo(l, t + mAvatar.getCloseOffset());
746                path.lineTo(l, t);
747                path.lineTo(r, t);
748                path.lineTo(r, b);
749                path.lineTo(l, b);
750                path.lineTo(l, t + mAvatar.getFarOffset());
751            } else if (mAvatar.getPosition() == Divot.LEFT_UPPER) {
752                path.moveTo(r, t + mAvatar.getCloseOffset());
753                path.lineTo(r, t);
754                path.lineTo(l, t);
755                path.lineTo(l, b);
756                path.lineTo(r, b);
757                path.lineTo(r, t + mAvatar.getFarOffset());
758            }
759
760//            paint.setColor(0xff00ff00);
761            paint.setColor(0xffcccccc);
762            paint.setStrokeWidth(1F);
763            paint.setStyle(Paint.Style.STROKE);
764            c.drawPath(path, paint);
765        } else {
766            super.dispatchDraw(c);
767        }
768    }
769}
770