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.Paint.FontMetricsInt;
     30 import android.graphics.Typeface;
     31 import android.graphics.drawable.Drawable;
     32 import android.net.Uri;
     33 import android.os.Handler;
     34 import android.os.Message;
     35 import android.provider.ContactsContract.Profile;
     36 import android.provider.Telephony.Sms;
     37 import android.telephony.PhoneNumberUtils;
     38 import android.telephony.TelephonyManager;
     39 import android.text.Html;
     40 import android.text.SpannableStringBuilder;
     41 import android.text.TextUtils;
     42 import android.text.method.HideReturnsTransformationMethod;
     43 import android.text.style.ForegroundColorSpan;
     44 import android.text.style.LineHeightSpan;
     45 import android.text.style.StyleSpan;
     46 import android.text.style.TextAppearanceSpan;
     47 import android.text.style.URLSpan;
     48 import android.util.AttributeSet;
     49 import android.util.Log;
     50 import android.view.View;
     51 import android.view.View.OnClickListener;
     52 import android.view.ViewGroup;
     53 import android.widget.ArrayAdapter;
     54 import android.widget.Button;
     55 import android.widget.ImageButton;
     56 import android.widget.ImageView;
     57 import android.widget.LinearLayout;
     58 import android.widget.TextView;
     59 
     60 import com.android.mms.MmsApp;
     61 import com.android.mms.R;
     62 import com.android.mms.data.Contact;
     63 import com.android.mms.data.WorkingMessage;
     64 import com.android.mms.model.SlideModel;
     65 import com.android.mms.model.SlideshowModel;
     66 import com.android.mms.transaction.Transaction;
     67 import com.android.mms.transaction.TransactionBundle;
     68 import com.android.mms.transaction.TransactionService;
     69 import com.android.mms.util.DownloadManager;
     70 import com.android.mms.util.ItemLoadedCallback;
     71 import com.android.mms.util.SmileyParser;
     72 import com.android.mms.util.ThumbnailManager.ImageLoaded;
     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 boolean DEBUG = false;
     85     private static final boolean DEBUG_DONT_LOAD_IMAGES = false;
     86 
     87     static final int MSG_LIST_EDIT    = 1;
     88     static final int MSG_LIST_PLAY    = 2;
     89     static final int MSG_LIST_DETAILS = 3;
     90 
     91     private View mMmsView;
     92     private ImageView mImageView;
     93     private ImageView mLockedIndicator;
     94     private ImageView mDeliveredIndicator;
     95     private ImageView mDetailsIndicator;
     96     private ImageButton mSlideShowButton;
     97     private TextView mBodyTextView;
     98     private Button mDownloadButton;
     99     private TextView mDownloadingLabel;
    100     private Handler mHandler;
    101     private MessageItem mMessageItem;
    102     private String mDefaultCountryIso;
    103     private TextView mDateView;
    104     public View mMessageBlock;
    105     private QuickContactDivot mAvatar;
    106     static private Drawable sDefaultContactImage;
    107     private Presenter mPresenter;
    108     private int mPosition;      // for debugging
    109     private ImageLoadedCallback mImageLoadedCallback;
    110     private boolean mMultiRecipients;
    111 
    112     public MessageListItem(Context context) {
    113         super(context);
    114         mDefaultCountryIso = MmsApp.getApplication().getCurrentCountryIso();
    115 
    116         if (sDefaultContactImage == null) {
    117             sDefaultContactImage = context.getResources().getDrawable(R.drawable.ic_contact_picture);
    118         }
    119     }
    120 
    121     public MessageListItem(Context context, AttributeSet attrs) {
    122         super(context, attrs);
    123 
    124         int color = mContext.getResources().getColor(R.color.timestamp_color);
    125         mColorSpan = new ForegroundColorSpan(color);
    126         mDefaultCountryIso = MmsApp.getApplication().getCurrentCountryIso();
    127 
    128         if (sDefaultContactImage == null) {
    129             sDefaultContactImage = context.getResources().getDrawable(R.drawable.ic_contact_picture);
    130         }
    131     }
    132 
    133     @Override
    134     protected void onFinishInflate() {
    135         super.onFinishInflate();
    136 
    137         mBodyTextView = (TextView) findViewById(R.id.text_view);
    138         mDateView = (TextView) findViewById(R.id.date_view);
    139         mLockedIndicator = (ImageView) findViewById(R.id.locked_indicator);
    140         mDeliveredIndicator = (ImageView) findViewById(R.id.delivered_indicator);
    141         mDetailsIndicator = (ImageView) findViewById(R.id.details_indicator);
    142         mAvatar = (QuickContactDivot) findViewById(R.id.avatar);
    143         mMessageBlock = findViewById(R.id.message_block);
    144     }
    145 
    146     public void bind(MessageItem msgItem, boolean convHasMultiRecipients, int position) {
    147         if (DEBUG) {
    148             Log.v(TAG, "bind for item: " + position + " old: " +
    149                    (mMessageItem != null ? mMessageItem.toString() : "NULL" ) +
    150                     " new " + msgItem.toString());
    151         }
    152         boolean sameItem = mMessageItem != null && mMessageItem.mMsgId == msgItem.mMsgId;
    153         mMessageItem = msgItem;
    154 
    155         mPosition = position;
    156         mMultiRecipients = convHasMultiRecipients;
    157 
    158         setLongClickable(false);
    159         setClickable(false);    // let the list view handle clicks on the item normally. When
    160                                 // clickable is true, clicks bypass the listview and go straight
    161                                 // to this listitem. We always want the listview to handle the
    162                                 // clicks first.
    163 
    164         switch (msgItem.mMessageType) {
    165             case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
    166                 bindNotifInd();
    167                 break;
    168             default:
    169                 bindCommonMessage(sameItem);
    170                 break;
    171         }
    172     }
    173 
    174     public void unbind() {
    175         // Clear all references to the message item, which can contain attachments and other
    176         // memory-intensive objects
    177         if (mImageView != null) {
    178             // Because #setOnClickListener may have set the listener to an object that has the
    179             // message item in its closure.
    180             mImageView.setOnClickListener(null);
    181         }
    182         if (mSlideShowButton != null) {
    183             // Because #drawPlaybackButton sets the tag to mMessageItem
    184             mSlideShowButton.setTag(null);
    185         }
    186         // leave the presenter in case it's needed when rebound to a different MessageItem.
    187         if (mPresenter != null) {
    188             mPresenter.cancelBackgroundLoading();
    189         }
    190     }
    191 
    192     public MessageItem getMessageItem() {
    193         return mMessageItem;
    194     }
    195 
    196     public void setMsgListItemHandler(Handler handler) {
    197         mHandler = handler;
    198     }
    199 
    200     private void bindNotifInd() {
    201         showMmsView(false);
    202 
    203         String msgSizeText = mContext.getString(R.string.message_size_label)
    204                                 + String.valueOf((mMessageItem.mMessageSize + 1023) / 1024)
    205                                 + mContext.getString(R.string.kilobyte);
    206 
    207         mBodyTextView.setText(formatMessage(mMessageItem, null,
    208                                             mMessageItem.mSubject,
    209                                             mMessageItem.mHighlight,
    210                                             mMessageItem.mTextContentType));
    211 
    212         mDateView.setText(buildTimestampLine(msgSizeText + " " + mMessageItem.mTimestamp));
    213 
    214         switch (mMessageItem.getMmsDownloadStatus()) {
    215             case DownloadManager.STATE_DOWNLOADING:
    216                 showDownloadingAttachment();
    217                 break;
    218             case DownloadManager.STATE_UNKNOWN:
    219             case DownloadManager.STATE_UNSTARTED:
    220                 DownloadManager downloadManager = DownloadManager.getInstance();
    221                 boolean autoDownload = downloadManager.isAuto();
    222                 boolean dataSuspended = (MmsApp.getApplication().getTelephonyManager()
    223                         .getDataState() == TelephonyManager.DATA_SUSPENDED);
    224 
    225                 // If we're going to automatically start downloading the mms attachment, then
    226                 // don't bother showing the download button for an instant before the actual
    227                 // download begins. Instead, show downloading as taking place.
    228                 if (autoDownload && !dataSuspended) {
    229                     showDownloadingAttachment();
    230                     break;
    231                 }
    232             case DownloadManager.STATE_TRANSIENT_FAILURE:
    233             case DownloadManager.STATE_PERMANENT_FAILURE:
    234             default:
    235                 setLongClickable(true);
    236                 inflateDownloadControls();
    237                 mDownloadingLabel.setVisibility(View.GONE);
    238                 mDownloadButton.setVisibility(View.VISIBLE);
    239                 mDownloadButton.setOnClickListener(new OnClickListener() {
    240                     @Override
    241                     public void onClick(View v) {
    242                         mDownloadingLabel.setVisibility(View.VISIBLE);
    243                         mDownloadButton.setVisibility(View.GONE);
    244                         Intent intent = new Intent(mContext, TransactionService.class);
    245                         intent.putExtra(TransactionBundle.URI, mMessageItem.mMessageUri.toString());
    246                         intent.putExtra(TransactionBundle.TRANSACTION_TYPE,
    247                                 Transaction.RETRIEVE_TRANSACTION);
    248                         mContext.startService(intent);
    249                     }
    250                 });
    251                 break;
    252         }
    253 
    254         // Hide the indicators.
    255         mLockedIndicator.setVisibility(View.GONE);
    256         mDeliveredIndicator.setVisibility(View.GONE);
    257         mDetailsIndicator.setVisibility(View.GONE);
    258         updateAvatarView(mMessageItem.mAddress, false);
    259     }
    260 
    261     private String buildTimestampLine(String timestamp) {
    262         if (!mMultiRecipients || mMessageItem.isMe() || TextUtils.isEmpty(mMessageItem.mContact)) {
    263             // Never show "Me" for messages I sent.
    264             return timestamp;
    265         }
    266         // This is a group conversation, show the sender's name on the same line as the timestamp.
    267         return mContext.getString(R.string.message_timestamp_format, mMessageItem.mContact,
    268                 timestamp);
    269     }
    270 
    271     private void showDownloadingAttachment() {
    272         inflateDownloadControls();
    273         mDownloadingLabel.setVisibility(View.VISIBLE);
    274         mDownloadButton.setVisibility(View.GONE);
    275     }
    276 
    277     private void updateAvatarView(String addr, boolean isSelf) {
    278         Drawable avatarDrawable;
    279         if (isSelf || !TextUtils.isEmpty(addr)) {
    280             Contact contact = isSelf ? Contact.getMe(false) : Contact.get(addr, false);
    281             avatarDrawable = contact.getAvatar(mContext, sDefaultContactImage);
    282 
    283             if (isSelf) {
    284                 mAvatar.assignContactUri(Profile.CONTENT_URI);
    285             } else {
    286                 if (contact.existsInDatabase()) {
    287                     mAvatar.assignContactUri(contact.getUri());
    288                 } else {
    289                     mAvatar.assignContactFromPhone(contact.getNumber(), true);
    290                 }
    291             }
    292         } else {
    293             avatarDrawable = sDefaultContactImage;
    294         }
    295         mAvatar.setImageDrawable(avatarDrawable);
    296     }
    297 
    298     private void bindCommonMessage(final boolean sameItem) {
    299         if (mDownloadButton != null) {
    300             mDownloadButton.setVisibility(View.GONE);
    301             mDownloadingLabel.setVisibility(View.GONE);
    302         }
    303         // Since the message text should be concatenated with the sender's
    304         // address(or name), I have to display it here instead of
    305         // displaying it by the Presenter.
    306         mBodyTextView.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
    307 
    308         boolean haveLoadedPdu = mMessageItem.isSms() || mMessageItem.mSlideshow != null;
    309         // Here we're avoiding reseting the avatar to the empty avatar when we're rebinding
    310         // to the same item. This happens when there's a DB change which causes the message item
    311         // cache in the MessageListAdapter to get cleared. When an mms MessageItem is newly
    312         // created, it has no info in it except the message id. The info is eventually loaded
    313         // and bindCommonMessage is called again (see onPduLoaded below). When we haven't loaded
    314         // the pdu, we don't want to call updateAvatarView because it
    315         // will set the avatar to the generic avatar then when this method is called again
    316         // from onPduLoaded, it will reset to the real avatar. This test is to avoid that flash.
    317         if (!sameItem || haveLoadedPdu) {
    318             boolean isSelf = Sms.isOutgoingFolder(mMessageItem.mBoxId);
    319             String addr = isSelf ? null : mMessageItem.mAddress;
    320             updateAvatarView(addr, isSelf);
    321         }
    322 
    323         // Get and/or lazily set the formatted message from/on the
    324         // MessageItem.  Because the MessageItem instances come from a
    325         // cache (currently of size ~50), the hit rate on avoiding the
    326         // expensive formatMessage() call is very high.
    327         CharSequence formattedMessage = mMessageItem.getCachedFormattedMessage();
    328         if (formattedMessage == null) {
    329             formattedMessage = formatMessage(mMessageItem,
    330                                              mMessageItem.mBody,
    331                                              mMessageItem.mSubject,
    332                                              mMessageItem.mHighlight,
    333                                              mMessageItem.mTextContentType);
    334             mMessageItem.setCachedFormattedMessage(formattedMessage);
    335         }
    336         if (!sameItem || haveLoadedPdu) {
    337             mBodyTextView.setText(formattedMessage);
    338         }
    339 
    340         // Debugging code to put the URI of the image attachment in the body of the list item.
    341         if (DEBUG) {
    342             String debugText = null;
    343             if (mMessageItem.mSlideshow == null) {
    344                 debugText = "NULL slideshow";
    345             } else {
    346                 SlideModel slide = ((SlideshowModel) mMessageItem.mSlideshow).get(0);
    347                 if (slide == null) {
    348                     debugText = "NULL first slide";
    349                 } else if (!slide.hasImage()) {
    350                     debugText = "Not an image";
    351                 } else {
    352                     debugText = slide.getImage().getUri().toString();
    353                 }
    354             }
    355             mBodyTextView.setText(mPosition + ": " + debugText);
    356         }
    357 
    358         // If we're in the process of sending a message (i.e. pending), then we show a "SENDING..."
    359         // string in place of the timestamp.
    360         if (!sameItem || haveLoadedPdu) {
    361             mDateView.setText(buildTimestampLine(mMessageItem.isSending() ?
    362                     mContext.getResources().getString(R.string.sending_message) :
    363                         mMessageItem.mTimestamp));
    364         }
    365         if (mMessageItem.isSms()) {
    366             showMmsView(false);
    367             mMessageItem.setOnPduLoaded(null);
    368         } else {
    369             if (DEBUG) {
    370                 Log.v(TAG, "bindCommonMessage for item: " + mPosition + " " +
    371                         mMessageItem.toString() +
    372                         " mMessageItem.mAttachmentType: " + mMessageItem.mAttachmentType +
    373                         " sameItem: " + sameItem);
    374             }
    375             if (mMessageItem.mAttachmentType != WorkingMessage.TEXT) {
    376                 if (!sameItem) {
    377                     setImage(null, null);
    378                 }
    379                 setOnClickListener(mMessageItem);
    380                 drawPlaybackButton(mMessageItem);
    381             } else {
    382                 showMmsView(false);
    383             }
    384             if (mMessageItem.mSlideshow == null) {
    385                 mMessageItem.setOnPduLoaded(new MessageItem.PduLoadedCallback() {
    386                     public void onPduLoaded(MessageItem messageItem) {
    387                         if (DEBUG) {
    388                             Log.v(TAG, "PduLoadedCallback in MessageListItem for item: " + mPosition +
    389                                     " " + (mMessageItem == null ? "NULL" : mMessageItem.toString()) +
    390                                     " passed in item: " +
    391                                     (messageItem == null ? "NULL" : messageItem.toString()));
    392                         }
    393                         if (messageItem != null && mMessageItem != null &&
    394                                 messageItem.getMessageId() == mMessageItem.getMessageId()) {
    395                             mMessageItem.setCachedFormattedMessage(null);
    396                             bindCommonMessage(true);
    397                         }
    398                     }
    399                 });
    400             } else {
    401                 if (mPresenter == null) {
    402                     mPresenter = PresenterFactory.getPresenter(
    403                             "MmsThumbnailPresenter", mContext,
    404                             this, mMessageItem.mSlideshow);
    405                 } else {
    406                     mPresenter.setModel(mMessageItem.mSlideshow);
    407                     mPresenter.setView(this);
    408                 }
    409                 if (mImageLoadedCallback == null) {
    410                     mImageLoadedCallback = new ImageLoadedCallback(this);
    411                 } else {
    412                     mImageLoadedCallback.reset(this);
    413                 }
    414                 mPresenter.present(mImageLoadedCallback);
    415             }
    416         }
    417         drawRightStatusIndicator(mMessageItem);
    418 
    419         requestLayout();
    420     }
    421 
    422     static private class ImageLoadedCallback implements ItemLoadedCallback<ImageLoaded> {
    423         private long mMessageId;
    424         private final MessageListItem mListItem;
    425 
    426         public ImageLoadedCallback(MessageListItem listItem) {
    427             mListItem = listItem;
    428             mMessageId = listItem.getMessageItem().getMessageId();
    429         }
    430 
    431         public void reset(MessageListItem listItem) {
    432             mMessageId = listItem.getMessageItem().getMessageId();
    433         }
    434 
    435         public void onItemLoaded(ImageLoaded imageLoaded, Throwable exception) {
    436             if (DEBUG_DONT_LOAD_IMAGES) {
    437                 return;
    438             }
    439             // Make sure we're still pointing to the same message. The list item could have
    440             // been recycled.
    441             MessageItem msgItem = mListItem.mMessageItem;
    442             if (msgItem != null && msgItem.getMessageId() == mMessageId) {
    443                 if (imageLoaded.mIsVideo) {
    444                     mListItem.setVideoThumbnail(null, imageLoaded.mBitmap);
    445                 } else {
    446                     mListItem.setImage(null, imageLoaded.mBitmap);
    447                 }
    448             }
    449         }
    450     }
    451 
    452     @Override
    453     public void startAudio() {
    454         // TODO Auto-generated method stub
    455     }
    456 
    457     @Override
    458     public void startVideo() {
    459         // TODO Auto-generated method stub
    460     }
    461 
    462     @Override
    463     public void setAudio(Uri audio, String name, Map<String, ?> extras) {
    464         // TODO Auto-generated method stub
    465     }
    466 
    467     @Override
    468     public void setImage(String name, Bitmap bitmap) {
    469         showMmsView(true);
    470 
    471         try {
    472             mImageView.setImageBitmap(bitmap);
    473             mImageView.setVisibility(VISIBLE);
    474         } catch (java.lang.OutOfMemoryError e) {
    475             Log.e(TAG, "setImage: out of memory: ", e);
    476         }
    477     }
    478 
    479     private void showMmsView(boolean visible) {
    480         if (mMmsView == null) {
    481             mMmsView = findViewById(R.id.mms_view);
    482             // if mMmsView is still null here, that mean the mms section hasn't been inflated
    483 
    484             if (visible && mMmsView == null) {
    485                 //inflate the mms view_stub
    486                 View mmsStub = findViewById(R.id.mms_layout_view_stub);
    487                 mmsStub.setVisibility(View.VISIBLE);
    488                 mMmsView = findViewById(R.id.mms_view);
    489             }
    490         }
    491         if (mMmsView != null) {
    492             if (mImageView == null) {
    493                 mImageView = (ImageView) findViewById(R.id.image_view);
    494             }
    495             if (mSlideShowButton == null) {
    496                 mSlideShowButton = (ImageButton) findViewById(R.id.play_slideshow_button);
    497             }
    498             mMmsView.setVisibility(visible ? View.VISIBLE : View.GONE);
    499             mImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
    500         }
    501     }
    502 
    503     private void inflateDownloadControls() {
    504         if (mDownloadButton == null) {
    505             //inflate the download controls
    506             findViewById(R.id.mms_downloading_view_stub).setVisibility(VISIBLE);
    507             mDownloadButton = (Button) findViewById(R.id.btn_download_msg);
    508             mDownloadingLabel = (TextView) findViewById(R.id.label_downloading);
    509         }
    510     }
    511 
    512 
    513     private LineHeightSpan mSpan = new LineHeightSpan() {
    514         @Override
    515         public void chooseHeight(CharSequence text, int start,
    516                 int end, int spanstartv, int v, FontMetricsInt fm) {
    517             fm.ascent -= 10;
    518         }
    519     };
    520 
    521     TextAppearanceSpan mTextSmallSpan =
    522         new TextAppearanceSpan(mContext, android.R.style.TextAppearance_Small);
    523 
    524     ForegroundColorSpan mColorSpan = null;  // set in ctor
    525 
    526     private CharSequence formatMessage(MessageItem msgItem, String body,
    527                                        String subject, Pattern highlight,
    528                                        String contentType) {
    529         SpannableStringBuilder buf = new SpannableStringBuilder();
    530 
    531         boolean hasSubject = !TextUtils.isEmpty(subject);
    532         SmileyParser parser = SmileyParser.getInstance();
    533         if (hasSubject) {
    534             CharSequence smilizedSubject = parser.addSmileySpans(subject);
    535             // Can't use the normal getString() with extra arguments for string replacement
    536             // because it doesn't preserve the SpannableText returned by addSmileySpans.
    537             // We have to manually replace the %s with our text.
    538             buf.append(TextUtils.replace(mContext.getResources().getString(R.string.inline_subject),
    539                     new String[] { "%s" }, new CharSequence[] { smilizedSubject }));
    540         }
    541 
    542         if (!TextUtils.isEmpty(body)) {
    543             // Converts html to spannable if ContentType is "text/html".
    544             if (contentType != null && ContentType.TEXT_HTML.equals(contentType)) {
    545                 buf.append("\n");
    546                 buf.append(Html.fromHtml(body));
    547             } else {
    548                 if (hasSubject) {
    549                     buf.append(" - ");
    550                 }
    551                 buf.append(parser.addSmileySpans(body));
    552             }
    553         }
    554 
    555         if (highlight != null) {
    556             Matcher m = highlight.matcher(buf.toString());
    557             while (m.find()) {
    558                 buf.setSpan(new StyleSpan(Typeface.BOLD), m.start(), m.end(), 0);
    559             }
    560         }
    561         return buf;
    562     }
    563 
    564     private void drawPlaybackButton(MessageItem msgItem) {
    565         switch (msgItem.mAttachmentType) {
    566             case WorkingMessage.SLIDESHOW:
    567             case WorkingMessage.AUDIO:
    568             case WorkingMessage.VIDEO:
    569                 // Show the 'Play' button and bind message info on it.
    570                 mSlideShowButton.setTag(msgItem);
    571                 // Set call-back for the 'Play' button.
    572                 mSlideShowButton.setOnClickListener(this);
    573                 mSlideShowButton.setVisibility(View.VISIBLE);
    574                 setLongClickable(true);
    575 
    576                 // When we show the mSlideShowButton, this list item's onItemClickListener doesn't
    577                 // get called. (It gets set in ComposeMessageActivity:
    578                 // mMsgListView.setOnItemClickListener) Here we explicitly set the item's
    579                 // onClickListener. It allows the item to respond to embedded html links and at the
    580                 // same time, allows the slide show play button to work.
    581                 setOnClickListener(new OnClickListener() {
    582                     @Override
    583                     public void onClick(View v) {
    584                         onMessageListItemClick();
    585                     }
    586                 });
    587                 break;
    588             default:
    589                 mSlideShowButton.setVisibility(View.GONE);
    590                 break;
    591         }
    592     }
    593 
    594     // OnClick Listener for the playback button
    595     @Override
    596     public void onClick(View v) {
    597         sendMessage(mMessageItem, MSG_LIST_PLAY);
    598     }
    599 
    600     private void sendMessage(MessageItem messageItem, int message) {
    601         if (mHandler != null) {
    602             Message msg = Message.obtain(mHandler, message);
    603             msg.obj = messageItem;
    604             msg.sendToTarget(); // See ComposeMessageActivity.mMessageListItemHandler.handleMessage
    605         }
    606     }
    607 
    608     public void onMessageListItemClick() {
    609         // If the message is a failed one, clicking it should reload it in the compose view,
    610         // regardless of whether it has links in it
    611         if (mMessageItem != null &&
    612                 mMessageItem.isOutgoingMessage() &&
    613                 mMessageItem.isFailedMessage() ) {
    614 
    615             // Assuming the current message is a failed one, reload it into the compose view so
    616             // the user can resend it.
    617             sendMessage(mMessageItem, MSG_LIST_EDIT);
    618             return;
    619         }
    620 
    621         // Check for links. If none, do nothing; if 1, open it; if >1, ask user to pick one
    622         final URLSpan[] spans = mBodyTextView.getUrls();
    623 
    624         if (spans.length == 0) {
    625             sendMessage(mMessageItem, MSG_LIST_DETAILS);    // show the message details dialog
    626         } else if (spans.length == 1) {
    627             spans[0].onClick(mBodyTextView);
    628         } else {
    629             ArrayAdapter<URLSpan> adapter =
    630                 new ArrayAdapter<URLSpan>(mContext, android.R.layout.select_dialog_item, spans) {
    631                 @Override
    632                 public View getView(int position, View convertView, ViewGroup parent) {
    633                     View v = super.getView(position, convertView, parent);
    634                     try {
    635                         URLSpan span = getItem(position);
    636                         String url = span.getURL();
    637                         Uri uri = Uri.parse(url);
    638                         TextView tv = (TextView) v;
    639                         Drawable d = mContext.getPackageManager().getActivityIcon(
    640                                 new Intent(Intent.ACTION_VIEW, uri));
    641                         if (d != null) {
    642                             d.setBounds(0, 0, d.getIntrinsicHeight(), d.getIntrinsicHeight());
    643                             tv.setCompoundDrawablePadding(10);
    644                             tv.setCompoundDrawables(d, null, null, null);
    645                         }
    646                         final String telPrefix = "tel:";
    647                         if (url.startsWith(telPrefix)) {
    648                             if ((mDefaultCountryIso == null) || mDefaultCountryIso.isEmpty()) {
    649                                 url = url.substring(telPrefix.length());
    650                             }
    651                             else {
    652                                 url = PhoneNumberUtils.formatNumber(
    653                                         url.substring(telPrefix.length()), mDefaultCountryIso);
    654                             }
    655                         }
    656                         tv.setText(url);
    657                     } catch (android.content.pm.PackageManager.NameNotFoundException ex) {
    658                         // it's ok if we're unable to set the drawable for this view - the user
    659                         // can still use it
    660                     }
    661                     return v;
    662                 }
    663             };
    664 
    665             AlertDialog.Builder b = new AlertDialog.Builder(mContext);
    666 
    667             DialogInterface.OnClickListener click = new DialogInterface.OnClickListener() {
    668                 @Override
    669                 public final void onClick(DialogInterface dialog, int which) {
    670                     if (which >= 0) {
    671                         spans[which].onClick(mBodyTextView);
    672                     }
    673                     dialog.dismiss();
    674                 }
    675             };
    676 
    677             b.setTitle(R.string.select_link_title);
    678             b.setCancelable(true);
    679             b.setAdapter(adapter, click);
    680 
    681             b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
    682                 @Override
    683                 public final void onClick(DialogInterface dialog, int which) {
    684                     dialog.dismiss();
    685                 }
    686             });
    687 
    688             b.show();
    689         }
    690     }
    691 
    692     private void setOnClickListener(final MessageItem msgItem) {
    693         switch(msgItem.mAttachmentType) {
    694             case WorkingMessage.IMAGE:
    695             case WorkingMessage.VIDEO:
    696                 mImageView.setOnClickListener(new OnClickListener() {
    697                     @Override
    698                     public void onClick(View v) {
    699                         sendMessage(msgItem, MSG_LIST_PLAY);
    700                     }
    701                 });
    702                 mImageView.setOnLongClickListener(new OnLongClickListener() {
    703                     @Override
    704                     public boolean onLongClick(View v) {
    705                         return v.showContextMenu();
    706                     }
    707                 });
    708                 break;
    709 
    710             default:
    711                 mImageView.setOnClickListener(null);
    712                 break;
    713             }
    714     }
    715 
    716     private void drawRightStatusIndicator(MessageItem msgItem) {
    717         // Locked icon
    718         if (msgItem.mLocked) {
    719             mLockedIndicator.setImageResource(R.drawable.ic_lock_message_sms);
    720             mLockedIndicator.setVisibility(View.VISIBLE);
    721         } else {
    722             mLockedIndicator.setVisibility(View.GONE);
    723         }
    724 
    725         // Delivery icon - we can show a failed icon for both sms and mms, but for an actual
    726         // delivery, we only show the icon for sms. We don't have the information here in mms to
    727         // know whether the message has been delivered. For mms, msgItem.mDeliveryStatus set
    728         // to MessageItem.DeliveryStatus.RECEIVED simply means the setting requesting a
    729         // delivery report was turned on when the message was sent. Yes, it's confusing!
    730         if ((msgItem.isOutgoingMessage() && msgItem.isFailedMessage()) ||
    731                 msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.FAILED) {
    732             mDeliveredIndicator.setImageResource(R.drawable.ic_list_alert_sms_failed);
    733             mDeliveredIndicator.setVisibility(View.VISIBLE);
    734         } else if (msgItem.isSms() &&
    735                 msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.RECEIVED) {
    736             mDeliveredIndicator.setImageResource(R.drawable.ic_sms_mms_delivered);
    737             mDeliveredIndicator.setVisibility(View.VISIBLE);
    738         } else {
    739             mDeliveredIndicator.setVisibility(View.GONE);
    740         }
    741 
    742         // Message details icon - this icon is shown both for sms and mms messages. For mms,
    743         // we show the icon if the read report or delivery report setting was set when the
    744         // message was sent. Showing the icon tells the user there's more information
    745         // by selecting the "View report" menu.
    746         if (msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.INFO || msgItem.mReadReport
    747                 || (msgItem.isMms() &&
    748                         msgItem.mDeliveryStatus == MessageItem.DeliveryStatus.RECEIVED)) {
    749             mDetailsIndicator.setImageResource(R.drawable.ic_sms_mms_details);
    750             mDetailsIndicator.setVisibility(View.VISIBLE);
    751         } else {
    752             mDetailsIndicator.setVisibility(View.GONE);
    753         }
    754     }
    755 
    756     @Override
    757     public void setImageRegionFit(String fit) {
    758         // TODO Auto-generated method stub
    759     }
    760 
    761     @Override
    762     public void setImageVisibility(boolean visible) {
    763         // TODO Auto-generated method stub
    764     }
    765 
    766     @Override
    767     public void setText(String name, String text) {
    768         // TODO Auto-generated method stub
    769     }
    770 
    771     @Override
    772     public void setTextVisibility(boolean visible) {
    773         // TODO Auto-generated method stub
    774     }
    775 
    776     @Override
    777     public void setVideo(String name, Uri uri) {
    778     }
    779 
    780     @Override
    781     public void setVideoThumbnail(String name, Bitmap bitmap) {
    782         showMmsView(true);
    783 
    784         try {
    785             mImageView.setImageBitmap(bitmap);
    786             mImageView.setVisibility(VISIBLE);
    787         } catch (java.lang.OutOfMemoryError e) {
    788             Log.e(TAG, "setVideo: out of memory: ", e);
    789         }
    790     }
    791 
    792     @Override
    793     public void setVideoVisibility(boolean visible) {
    794         // TODO Auto-generated method stub
    795     }
    796 
    797     @Override
    798     public void stopAudio() {
    799         // TODO Auto-generated method stub
    800     }
    801 
    802     @Override
    803     public void stopVideo() {
    804         // TODO Auto-generated method stub
    805     }
    806 
    807     @Override
    808     public void reset() {
    809     }
    810 
    811     @Override
    812     public void setVisibility(boolean visible) {
    813         // TODO Auto-generated method stub
    814     }
    815 
    816     @Override
    817     public void pauseAudio() {
    818         // TODO Auto-generated method stub
    819 
    820     }
    821 
    822     @Override
    823     public void pauseVideo() {
    824         // TODO Auto-generated method stub
    825 
    826     }
    827 
    828     @Override
    829     public void seekAudio(int seekTo) {
    830         // TODO Auto-generated method stub
    831 
    832     }
    833 
    834     @Override
    835     public void seekVideo(int seekTo) {
    836         // TODO Auto-generated method stub
    837 
    838     }
    839 }
    840