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