MessageListItem.java revision 9549e0f231f524440c6a9112fe07ac898f82c88a
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        setClickable(false);    // let the list view handle clicks on the item normally. When
149                                // clickable is true, clicks bypass the listview and go straight
150                                // to this listitem. We always want the listview to handle the
151                                // clicks first.
152
153        switch (msgItem.mMessageType) {
154            case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
155                bindNotifInd(msgItem);
156                break;
157            default:
158                bindCommonMessage(msgItem);
159                break;
160        }
161    }
162
163    public void unbind() {
164        // Clear all references to the message item, which can contain attachments and other
165        // memory-intensive objects
166        mMessageItem = null;
167        if (mImageView != null) {
168            // Because #setOnClickListener may have set the listener to an object that has the
169            // message item in its closure.
170            mImageView.setOnClickListener(null);
171        }
172        if (mSlideShowButton != null) {
173            // Because #drawPlaybackButton sets the tag to mMessageItem
174            mSlideShowButton.setTag(null);
175        }
176    }
177
178    public MessageItem getMessageItem() {
179        return mMessageItem;
180    }
181
182    public void setMsgListItemHandler(Handler handler) {
183        mHandler = handler;
184    }
185
186    private void bindNotifInd(final MessageItem msgItem) {
187        hideMmsViewIfNeeded();
188
189        String msgSizeText = mContext.getString(R.string.message_size_label)
190                                + String.valueOf((msgItem.mMessageSize + 1023) / 1024)
191                                + mContext.getString(R.string.kilobyte);
192
193        mBodyTextView.setText(formatMessage(msgItem, msgItem.mContact, null, msgItem.mSubject,
194                                            msgItem.mHighlight, msgItem.mTextContentType));
195
196        mDateView.setText(msgSizeText + " " + msgItem.mTimestamp);
197
198        int state = DownloadManager.getInstance().getState(msgItem.mMessageUri);
199        switch (state) {
200            case DownloadManager.STATE_DOWNLOADING:
201                inflateDownloadControls();
202                mDownloadingLabel.setVisibility(View.VISIBLE);
203                mDownloadButton.setVisibility(View.GONE);
204                break;
205            case DownloadManager.STATE_UNSTARTED:
206            case DownloadManager.STATE_TRANSIENT_FAILURE:
207            case DownloadManager.STATE_PERMANENT_FAILURE:
208            default:
209                setLongClickable(true);
210                inflateDownloadControls();
211                mDownloadingLabel.setVisibility(View.GONE);
212                mDownloadButton.setVisibility(View.VISIBLE);
213                mDownloadButton.setOnClickListener(new OnClickListener() {
214                    @Override
215                    public void onClick(View v) {
216                        mDownloadingLabel.setVisibility(View.VISIBLE);
217                        mDownloadButton.setVisibility(View.GONE);
218                        Intent intent = new Intent(mContext, TransactionService.class);
219                        intent.putExtra(TransactionBundle.URI, msgItem.mMessageUri.toString());
220                        intent.putExtra(TransactionBundle.TRANSACTION_TYPE,
221                                Transaction.RETRIEVE_TRANSACTION);
222                        mContext.startService(intent);
223                    }
224                });
225                break;
226        }
227
228        // Hide the indicators.
229        mLockedIndicator.setVisibility(View.GONE);
230        mDeliveredIndicator.setVisibility(View.GONE);
231        mDetailsIndicator.setVisibility(View.GONE);
232        updateAvatarView(msgItem.mAddress, false);
233    }
234
235    private void updateAvatarView(String addr, boolean isSelf) {
236        Drawable avatarDrawable;
237        if (isSelf || !TextUtils.isEmpty(addr)) {
238            Contact contact = isSelf ? Contact.getMe(false) : Contact.get(addr, false);
239            avatarDrawable = contact.getAvatar(mContext, sDefaultContactImage);
240
241            if (isSelf) {
242                mAvatar.assignContactUri(Profile.CONTENT_URI);
243            } else {
244                if (contact.existsInDatabase()) {
245                    mAvatar.assignContactUri(contact.getUri());
246                } else {
247                    mAvatar.assignContactFromPhone(contact.getNumber(), true);
248                }
249            }
250        } else {
251            avatarDrawable = sDefaultContactImage;
252        }
253        mAvatar.setImageDrawable(avatarDrawable);
254    }
255
256    private void bindCommonMessage(final MessageItem msgItem) {
257        if (mDownloadButton != null) {
258            mDownloadButton.setVisibility(View.GONE);
259            mDownloadingLabel.setVisibility(View.GONE);
260        }
261        // Since the message text should be concatenated with the sender's
262        // address(or name), I have to display it here instead of
263        // displaying it by the Presenter.
264        mBodyTextView.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
265
266        boolean isSelf = Sms.isOutgoingFolder(msgItem.mBoxId);
267        String addr = isSelf ? null : msgItem.mAddress;
268        updateAvatarView(addr, isSelf);
269
270        // Get and/or lazily set the formatted message from/on the
271        // MessageItem.  Because the MessageItem instances come from a
272        // cache (currently of size ~50), the hit rate on avoiding the
273        // expensive formatMessage() call is very high.
274        CharSequence formattedMessage = msgItem.getCachedFormattedMessage();
275        if (formattedMessage == null) {
276            formattedMessage = formatMessage(msgItem, msgItem.mContact, msgItem.mBody,
277                                             msgItem.mSubject,
278                                             msgItem.mHighlight, msgItem.mTextContentType);
279        }
280        mBodyTextView.setText(formattedMessage);
281
282        // If we're in the process of sending a message (i.e. pending), then we show a "SENDING..."
283        // string in place of the timestamp.
284        mDateView.setText(msgItem.isSending() ?
285                mContext.getResources().getString(R.string.sending_message) :
286                    msgItem.mTimestamp);
287
288        if (msgItem.isSms()) {
289            hideMmsViewIfNeeded();
290        } else {
291            Presenter presenter = PresenterFactory.getPresenter(
292                    "MmsThumbnailPresenter", mContext,
293                    this, msgItem.mSlideshow);
294            presenter.present();
295
296            if (msgItem.mAttachmentType != WorkingMessage.TEXT) {
297                inflateMmsView();
298                mMmsView.setVisibility(View.VISIBLE);
299                setOnClickListener(msgItem);
300                drawPlaybackButton(msgItem);
301            } else {
302                hideMmsViewIfNeeded();
303            }
304        }
305        drawRightStatusIndicator(msgItem);
306
307        requestLayout();
308    }
309
310    private void hideMmsViewIfNeeded() {
311        if (mMmsView != null) {
312            mMmsView.setVisibility(View.GONE);
313        }
314    }
315
316    @Override
317    public void startAudio() {
318        // TODO Auto-generated method stub
319    }
320
321    @Override
322    public void startVideo() {
323        // TODO Auto-generated method stub
324    }
325
326    @Override
327    public void setAudio(Uri audio, String name, Map<String, ?> extras) {
328        // TODO Auto-generated method stub
329    }
330
331    @Override
332    public void setImage(String name, Bitmap bitmap) {
333        inflateMmsView();
334
335        try {
336            if (null == bitmap) {
337                bitmap = BitmapFactory.decodeResource(getResources(),
338                        R.drawable.ic_missing_thumbnail_picture);
339            }
340            mImageView.setImageBitmap(bitmap);
341            mImageView.setVisibility(VISIBLE);
342        } catch (java.lang.OutOfMemoryError e) {
343            Log.e(TAG, "setImage: out of memory: ", e);
344        }
345    }
346
347    private void inflateMmsView() {
348        if (mMmsView == null) {
349            //inflate the surrounding view_stub
350            findViewById(R.id.mms_layout_view_stub).setVisibility(VISIBLE);
351
352            mMmsView = findViewById(R.id.mms_view);
353            mImageView = (ImageView) findViewById(R.id.image_view);
354            mSlideShowButton = (ImageButton) findViewById(R.id.play_slideshow_button);
355        }
356    }
357
358    private void inflateDownloadControls() {
359        if (mDownloadButton == null) {
360            //inflate the download controls
361            findViewById(R.id.mms_downloading_view_stub).setVisibility(VISIBLE);
362            mDownloadButton = (Button) findViewById(R.id.btn_download_msg);
363            mDownloadingLabel = (TextView) findViewById(R.id.label_downloading);
364        }
365    }
366
367
368    private LineHeightSpan mSpan = new LineHeightSpan() {
369        @Override
370        public void chooseHeight(CharSequence text, int start,
371                int end, int spanstartv, int v, FontMetricsInt fm) {
372            fm.ascent -= 10;
373        }
374    };
375
376    TextAppearanceSpan mTextSmallSpan =
377        new TextAppearanceSpan(mContext, android.R.style.TextAppearance_Small);
378
379    ForegroundColorSpan mColorSpan = null;  // set in ctor
380
381    private CharSequence formatMessage(MessageItem msgItem, String contact, String body,
382                                       String subject, Pattern highlight,
383                                       String contentType) {
384        SpannableStringBuilder buf = new SpannableStringBuilder();
385
386        boolean hasSubject = !TextUtils.isEmpty(subject);
387        SmileyParser parser = SmileyParser.getInstance();
388        if (hasSubject) {
389            CharSequence smilizedSubject = parser.addSmileySpans(subject);
390            // Can't use the normal getString() with extra arguments for string replacement
391            // because it doesn't preserve the SpannableText returned by addSmileySpans.
392            // We have to manually replace the %s with our text.
393            buf.append(TextUtils.replace(mContext.getResources().getString(R.string.inline_subject),
394                    new String[] { "%s" }, new CharSequence[] { smilizedSubject }));
395        }
396
397        if (!TextUtils.isEmpty(body)) {
398            // Converts html to spannable if ContentType is "text/html".
399            if (contentType != null && ContentType.TEXT_HTML.equals(contentType)) {
400                buf.append("\n");
401                buf.append(Html.fromHtml(body));
402            } else {
403                if (hasSubject) {
404                    buf.append(" - ");
405                }
406                buf.append(parser.addSmileySpans(body));
407            }
408        }
409
410        if (highlight != null) {
411            Matcher m = highlight.matcher(buf.toString());
412            while (m.find()) {
413                buf.setSpan(new StyleSpan(Typeface.BOLD), m.start(), m.end(), 0);
414            }
415        }
416        return buf;
417    }
418
419    private void drawPlaybackButton(MessageItem msgItem) {
420        switch (msgItem.mAttachmentType) {
421            case WorkingMessage.SLIDESHOW:
422            case WorkingMessage.AUDIO:
423            case WorkingMessage.VIDEO:
424                // Show the 'Play' button and bind message info on it.
425                mSlideShowButton.setTag(msgItem);
426                // Set call-back for the 'Play' button.
427                mSlideShowButton.setOnClickListener(this);
428                mSlideShowButton.setVisibility(View.VISIBLE);
429                setLongClickable(true);
430
431                // When we show the mSlideShowButton, this list item's onItemClickListener doesn't
432                // get called. (It gets set in ComposeMessageActivity:
433                // mMsgListView.setOnItemClickListener) Here we explicitly set the item's
434                // onClickListener. It allows the item to respond to embedded html links and at the
435                // same time, allows the slide show play button to work.
436                setOnClickListener(new OnClickListener() {
437                    @Override
438                    public void onClick(View v) {
439                        onMessageListItemClick();
440                    }
441                });
442                break;
443            default:
444                mSlideShowButton.setVisibility(View.GONE);
445                break;
446        }
447    }
448
449    // OnClick Listener for the playback button
450    @Override
451    public void onClick(View v) {
452        MessageItem mi = (MessageItem) v.getTag();
453        switch (mi.mAttachmentType) {
454            case WorkingMessage.VIDEO:
455            case WorkingMessage.AUDIO:
456            case WorkingMessage.SLIDESHOW:
457                MessageUtils.viewMmsMessageAttachment(mContext, mi.mMessageUri, mi.mSlideshow);
458                break;
459        }
460    }
461
462    public void onMessageListItemClick() {
463        // If the message is a failed one, clicking it should reload it in the compose view,
464        // regardless of whether it has links in it
465        if (mMessageItem != null &&
466                mMessageItem.isOutgoingMessage() &&
467                mMessageItem.isFailedMessage() ) {
468            recomposeFailedMessage();
469            return;
470        }
471
472        // Check for links. If none, do nothing; if 1, open it; if >1, ask user to pick one
473        URLSpan[] spans = mBodyTextView.getUrls();
474
475        if (spans.length == 0) {
476            // Do nothing.
477        } else if (spans.length == 1) {
478            Uri uri = Uri.parse(spans[0].getURL());
479            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
480            intent.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName());
481            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
482            mContext.startActivity(intent);
483        } else {
484            final java.util.ArrayList<String> urls = MessageUtils.extractUris(spans);
485
486            ArrayAdapter<String> adapter =
487                new ArrayAdapter<String>(mContext, android.R.layout.select_dialog_item, urls) {
488                @Override
489                public View getView(int position, View convertView, ViewGroup parent) {
490                    View v = super.getView(position, convertView, parent);
491                    try {
492                        String url = getItem(position).toString();
493                        TextView tv = (TextView) v;
494                        Drawable d = mContext.getPackageManager().getActivityIcon(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
495                        if (d != null) {
496                            d.setBounds(0, 0, d.getIntrinsicHeight(), d.getIntrinsicHeight());
497                            tv.setCompoundDrawablePadding(10);
498                            tv.setCompoundDrawables(d, null, null, null);
499                        }
500                        final String telPrefix = "tel:";
501                        if (url.startsWith(telPrefix)) {
502                            url = PhoneNumberUtils.formatNumber(
503                                            url.substring(telPrefix.length()), mDefaultCountryIso);
504                        }
505                        tv.setText(url);
506                    } catch (android.content.pm.PackageManager.NameNotFoundException ex) {
507                        // it's ok if we're unable to set the drawable for this view - the user
508                        // can still use it
509                    }
510                    return v;
511                }
512            };
513
514            AlertDialog.Builder b = new AlertDialog.Builder(mContext);
515
516            DialogInterface.OnClickListener click = new DialogInterface.OnClickListener() {
517                @Override
518                public final void onClick(DialogInterface dialog, int which) {
519                    if (which >= 0) {
520                        Uri uri = Uri.parse(urls.get(which));
521                        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
522                        intent.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName());
523                        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
524                        mContext.startActivity(intent);
525                    }
526                    dialog.dismiss();
527                }
528            };
529
530            b.setTitle(R.string.select_link_title);
531            b.setCancelable(true);
532            b.setAdapter(adapter, click);
533
534            b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
535                @Override
536                public final void onClick(DialogInterface dialog, int which) {
537                    dialog.dismiss();
538                }
539            });
540
541            b.show();
542        }
543    }
544
545    private void setOnClickListener(final MessageItem msgItem) {
546        switch(msgItem.mAttachmentType) {
547        case WorkingMessage.IMAGE:
548        case WorkingMessage.VIDEO:
549            mImageView.setOnClickListener(new OnClickListener() {
550                @Override
551                public void onClick(View v) {
552                    MessageUtils.viewMmsMessageAttachment(mContext, null, msgItem.mSlideshow);
553                }
554            });
555            mImageView.setOnLongClickListener(new OnLongClickListener() {
556                @Override
557                public boolean onLongClick(View v) {
558                    return v.showContextMenu();
559                }
560            });
561            break;
562
563        default:
564            mImageView.setOnClickListener(null);
565            break;
566        }
567    }
568
569    /**
570     * Assuming the current message is a failed one, reload it into the compose view so that the
571     * user can resend it.
572     */
573    private void recomposeFailedMessage() {
574        String type = mMessageItem.mType;
575        final int what;
576        if (type.equals("sms")) {
577            what = MSG_LIST_EDIT_SMS;
578        } else {
579            what = MSG_LIST_EDIT_MMS;
580        }
581        if (null != mHandler) {
582            Message msg = Message.obtain(mHandler, what);
583            msg.obj = new Long(mMessageItem.mMsgId);
584            msg.sendToTarget();
585        }
586    }
587
588    private void drawRightStatusIndicator(MessageItem msgItem) {
589        // Locked icon
590        if (msgItem.mLocked) {
591            mLockedIndicator.setImageResource(R.drawable.ic_lock_message_sms);
592            mLockedIndicator.setVisibility(View.VISIBLE);
593        } else {
594            mLockedIndicator.setVisibility(View.GONE);
595        }
596
597        // Delivery icon - we can show a failed icon for both sms and mms, but for an actual
598        // delivery, we only show the icon for sms. We don't have the information here in mms to
599        // know whether the message has been delivered. For mms, msgItem.mDeliveryStatus set
600        // to MessageItem.DeliveryStatus.RECEIVED simply means the setting requesting a
601        // delivery report was turned on when the message was sent. Yes, it's confusing!
602        if ((msgItem.isOutgoingMessage() && msgItem.isFailedMessage()) ||
603                msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.FAILED) {
604            mDeliveredIndicator.setImageResource(R.drawable.ic_list_alert_sms_failed);
605            mDeliveredIndicator.setVisibility(View.VISIBLE);
606        } else if (msgItem.isSms() &&
607                msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.RECEIVED) {
608            mDeliveredIndicator.setImageResource(R.drawable.ic_sms_mms_delivered);
609            mDeliveredIndicator.setVisibility(View.VISIBLE);
610        } else {
611            mDeliveredIndicator.setVisibility(View.GONE);
612        }
613
614        // Message details icon - this icon is shown both for sms and mms messages. For mms,
615        // we show the icon if the read report or delivery report setting was set when the
616        // message was sent. Showing the icon tells the user there's more information
617        // by selecting the "View report" menu.
618        if (msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.INFO || msgItem.mReadReport
619                || (msgItem.isMms() &&
620                        msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.RECEIVED)) {
621            mDetailsIndicator.setImageResource(R.drawable.ic_sms_mms_details);
622            mDetailsIndicator.setVisibility(View.VISIBLE);
623        } else {
624            mDetailsIndicator.setVisibility(View.GONE);
625        }
626    }
627
628    @Override
629    public void setImageRegionFit(String fit) {
630        // TODO Auto-generated method stub
631    }
632
633    @Override
634    public void setImageVisibility(boolean visible) {
635        // TODO Auto-generated method stub
636    }
637
638    @Override
639    public void setText(String name, String text) {
640        // TODO Auto-generated method stub
641    }
642
643    @Override
644    public void setTextVisibility(boolean visible) {
645        // TODO Auto-generated method stub
646    }
647
648    @Override
649    public void setVideo(String name, Uri video) {
650        inflateMmsView();
651
652        try {
653            Bitmap bitmap = VideoAttachmentView.createVideoThumbnail(mContext, video);
654            if (null == bitmap) {
655                bitmap = BitmapFactory.decodeResource(getResources(),
656                        R.drawable.ic_missing_thumbnail_video);
657            }
658            mImageView.setImageBitmap(bitmap);
659            mImageView.setVisibility(VISIBLE);
660        } catch (java.lang.OutOfMemoryError e) {
661            Log.e(TAG, "setVideo: out of memory: ", e);
662        }
663    }
664
665    @Override
666    public void setVideoVisibility(boolean visible) {
667        // TODO Auto-generated method stub
668    }
669
670    @Override
671    public void stopAudio() {
672        // TODO Auto-generated method stub
673    }
674
675    @Override
676    public void stopVideo() {
677        // TODO Auto-generated method stub
678    }
679
680    @Override
681    public void reset() {
682        if (mImageView != null) {
683            mImageView.setVisibility(GONE);
684        }
685    }
686
687    @Override
688    public void setVisibility(boolean visible) {
689        // TODO Auto-generated method stub
690    }
691
692    @Override
693    public void pauseAudio() {
694        // TODO Auto-generated method stub
695
696    }
697
698    @Override
699    public void pauseVideo() {
700        // TODO Auto-generated method stub
701
702    }
703
704    @Override
705    public void seekAudio(int seekTo) {
706        // TODO Auto-generated method stub
707
708    }
709
710    @Override
711    public void seekVideo(int seekTo) {
712        // TODO Auto-generated method stub
713
714    }
715
716    /**
717     * Override dispatchDraw so that we can put our own background and border in.
718     * This is all complexity to support a shared border from one item to the next.
719     */
720    @Override
721    public void dispatchDraw(Canvas c) {
722        View v = mMessageBlock;
723        if (v != null) {
724            float l = v.getX();
725            float t = v.getY();
726            float r = v.getX() + v.getWidth();
727            float b = v.getY() + v.getHeight();
728
729            Path path = mPath;
730            path.reset();
731
732            super.dispatchDraw(c);
733
734            path.reset();
735
736            r -= 1;
737
738            // This block of code draws the border around the "message block" section
739            // of the layout.  This would normally be a simple rectangle but we omit
740            // the border at the point of the avatar's divot.  Also, the bottom is drawn
741            // 1 pixel below our own bounds to get it to line up with the border of
742            // the next item.
743            //
744            // But for the last item we draw the bottom in our own bounds -- so it will
745            // show up.
746            if (mIsLastItemInList) {
747                b -= 1;
748            }
749            if (mAvatar.getPosition() == Divot.RIGHT_UPPER) {
750                path.moveTo(l, t + mAvatar.getCloseOffset());
751                path.lineTo(l, t);
752                path.lineTo(r, t);
753                path.lineTo(r, b);
754                path.lineTo(l, b);
755                path.lineTo(l, t + mAvatar.getFarOffset());
756            } else if (mAvatar.getPosition() == Divot.LEFT_UPPER) {
757                path.moveTo(r, t + mAvatar.getCloseOffset());
758                path.lineTo(r, t);
759                path.lineTo(l, t);
760                path.lineTo(l, b);
761                path.lineTo(r, b);
762                path.lineTo(r, t + mAvatar.getFarOffset());
763            }
764
765            Paint paint = mPaint;
766//            paint.setColor(0xff00ff00);
767            paint.setColor(0xffcccccc);
768            paint.setStrokeWidth(1F);
769            paint.setStyle(Paint.Style.STROKE);
770            c.drawPath(path, paint);
771        } else {
772            super.dispatchDraw(c);
773        }
774    }
775}
776