Home | History | Annotate | Download | only in ui
      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 
     18 package com.android.mms.ui;
     19 
     20 import java.util.Map;
     21 import java.util.regex.Matcher;
     22 import java.util.regex.Pattern;
     23 
     24 import android.app.AlertDialog;
     25 import android.content.Context;
     26 import android.content.DialogInterface;
     27 import android.content.Intent;
     28 import android.graphics.Bitmap;
     29 import android.graphics.BitmapFactory;
     30 import android.graphics.Canvas;
     31 import android.graphics.Paint;
     32 import android.graphics.Path;
     33 import android.graphics.Typeface;
     34 import android.graphics.Paint.FontMetricsInt;
     35 import android.graphics.drawable.Drawable;
     36 import android.net.Uri;
     37 import android.os.Handler;
     38 import android.os.Message;
     39 import android.provider.Browser;
     40 import android.provider.ContactsContract.Profile;
     41 import android.provider.Telephony.Sms;
     42 import android.telephony.PhoneNumberUtils;
     43 import android.text.Html;
     44 import android.text.SpannableStringBuilder;
     45 import android.text.TextUtils;
     46 import android.text.method.HideReturnsTransformationMethod;
     47 import android.text.style.ForegroundColorSpan;
     48 import android.text.style.LineHeightSpan;
     49 import android.text.style.StyleSpan;
     50 import android.text.style.TextAppearanceSpan;
     51 import android.text.style.URLSpan;
     52 import android.util.AttributeSet;
     53 import android.util.Log;
     54 import android.view.View;
     55 import android.view.ViewGroup;
     56 import android.view.View.OnClickListener;
     57 import android.widget.ArrayAdapter;
     58 import android.widget.Button;
     59 import android.widget.ImageButton;
     60 import android.widget.ImageView;
     61 import android.widget.LinearLayout;
     62 import android.widget.TextView;
     63 
     64 import com.android.mms.MmsApp;
     65 import com.android.mms.R;
     66 import com.android.mms.data.Contact;
     67 import com.android.mms.data.WorkingMessage;
     68 import com.android.mms.transaction.Transaction;
     69 import com.android.mms.transaction.TransactionBundle;
     70 import com.android.mms.transaction.TransactionService;
     71 import com.android.mms.util.DownloadManager;
     72 import com.android.mms.util.SmileyParser;
     73 import com.google.android.mms.ContentType;
     74 import com.google.android.mms.pdu.PduHeaders;
     75 
     76 /**
     77  * This class provides view of a message in the messages list.
     78  */
     79 public 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