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