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