MessageListItem.java revision 8eed706474910ccb978acda03e85d3261037da6e
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 com.android.mms.R; 21import com.android.mms.transaction.Transaction; 22import com.android.mms.transaction.TransactionBundle; 23import com.android.mms.transaction.TransactionService; 24import com.android.mms.util.DownloadManager; 25import com.google.android.mms.pdu.PduHeaders; 26 27import android.content.Context; 28import android.content.Intent; 29import android.graphics.Bitmap; 30import android.graphics.Typeface; 31import android.media.MediaPlayer; 32import android.net.Uri; 33import android.os.Handler; 34import android.os.Message; 35import android.provider.Telephony.Mms; 36import android.provider.Telephony.MmsSms; 37import android.provider.Telephony.Sms; 38import android.text.Spannable; 39import android.text.SpannableStringBuilder; 40import android.text.TextUtils; 41import android.text.method.HideReturnsTransformationMethod; 42import android.text.style.StyleSpan; 43import android.text.style.URLSpan; 44import android.util.AttributeSet; 45import android.util.Log; 46import android.view.View; 47import android.view.View.OnClickListener; 48import android.widget.Button; 49import android.widget.ImageButton; 50import android.widget.ImageView; 51import android.widget.LinearLayout; 52import android.widget.TextView; 53 54import java.io.IOException; 55import java.util.ArrayList; 56import java.util.Map; 57 58/** 59 * This class provides view of a message in the messages list. 60 */ 61public class MessageListItem extends LinearLayout implements 62 SlideViewInterface, OnClickListener { 63 public static final String EXTRA_URLS = "com.android.mms.ExtraUrls"; 64 65 private static final String TAG = "MessageListItem"; 66 private static final StyleSpan STYLE_BOLD = new StyleSpan(Typeface.BOLD); 67 68 static final int MSG_LIST_EDIT_MMS = 1; 69 static final int MSG_LIST_EDIT_SMS = 2; 70 71 private View mMsgListItem; 72 private View mMmsView; 73 private ImageView mImageView; 74 private ImageView mLeftStatusIndicator; 75 private ImageView mRightStatusIndicator; 76 private ImageButton mSlideShowButton; 77 private TextView mBodyTextView; 78 private Button mDownloadButton; 79 private TextView mDownloadingLabel; 80 private Handler mHandler; 81 82 public MessageListItem(Context context) { 83 super(context); 84 } 85 86 public MessageListItem(Context context, AttributeSet attrs) { 87 super(context, attrs); 88 } 89 90 @Override 91 protected void onFinishInflate() { 92 super.onFinishInflate(); 93 94 mMsgListItem = findViewById(R.id.msg_list_item); 95 mLeftStatusIndicator = (ImageView) findViewById(R.id.left_status_indicator); 96 mBodyTextView = (TextView) findViewById(R.id.text_view); 97 mRightStatusIndicator = (ImageView) findViewById(R.id.right_status_indicator); 98 mDownloadButton = (Button) findViewById(R.id.btn_download_msg); 99 mDownloadingLabel = (TextView) findViewById(R.id.label_downloading); 100 } 101 102 public void bind(MessageItem msgItem) { 103 setLongClickable(false); 104 105 switch (msgItem.mMessageType) { 106 case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: 107 bindNotifInd(msgItem); 108 break; 109 default: 110 bindCommonMessage(msgItem); 111 break; 112 } 113 } 114 115 public void setMsgListItemHandler(Handler handler) { 116 mHandler = handler; 117 } 118 119 private void bindNotifInd(final MessageItem msgItem) { 120 hideMmsViewIfNeeded(); 121 122 String msgSizeText = mContext.getString(R.string.message_size_label) 123 + String.valueOf((msgItem.mMessageSize + 1023) / 1024) 124 + mContext.getString(R.string.kilobyte); 125 126 mBodyTextView.setText(formatMessage(msgItem.mContact, null, msgItem.mSubject, 127 msgSizeText + "\n" + msgItem.mTimestamp)); 128 129 int state = DownloadManager.getInstance().getState(msgItem.mMessageUri); 130 switch (state) { 131 case DownloadManager.STATE_DOWNLOADING: 132 mDownloadingLabel.setVisibility(View.VISIBLE); 133 mDownloadButton.setVisibility(View.GONE); 134 break; 135 case DownloadManager.STATE_UNSTARTED: 136 case DownloadManager.STATE_TRANSIENT_FAILURE: 137 case DownloadManager.STATE_PERMANENT_FAILURE: 138 default: 139 setLongClickable(true); 140 mDownloadingLabel.setVisibility(View.GONE); 141 mDownloadButton.setVisibility(View.VISIBLE); 142 mDownloadButton.setOnClickListener(new OnClickListener() { 143 public void onClick(View v) { 144 mDownloadingLabel.setVisibility(View.VISIBLE); 145 mDownloadButton.setVisibility(View.GONE); 146 Intent intent = new Intent(mContext, TransactionService.class); 147 intent.putExtra(TransactionBundle.URI, msgItem.mMessageUri.toString()); 148 intent.putExtra(TransactionBundle.TRANSACTION_TYPE, 149 Transaction.RETRIEVE_TRANSACTION); 150 mContext.startService(intent); 151 } 152 }); 153 break; 154 } 155 156 // Hide the error indicator. 157 mRightStatusIndicator.setVisibility(View.GONE); 158 159 drawLeftStatusIndicator(msgItem.mBoxId); 160 } 161 162 private void bindCommonMessage(final MessageItem msgItem) { 163 mDownloadButton.setVisibility(View.GONE); 164 mDownloadingLabel.setVisibility(View.GONE); 165 166 // Since the message text should be concatenated with the sender's 167 // address(or name), I have to display it here instead of 168 // displaying it by the Presenter. 169 mBodyTextView.setTransformationMethod(HideReturnsTransformationMethod.getInstance()); 170 171 mBodyTextView.setText(formatMessage(msgItem.mContact, msgItem.mBody, 172 msgItem.mSubject, msgItem.mTimestamp)); 173 // TODO part of changing contact names to links 174 //mBodyTextView.setText(formatMessage(msgItem.mAddress, msgItem.mBody)); 175 176 if (msgItem.isSms()) { 177 hideMmsViewIfNeeded(); 178 } else { 179 Presenter presenter = PresenterFactory.getPresenter( 180 "MmsThumbnailPresenter", mContext, 181 this, msgItem.mSlideshow); 182 presenter.present(); 183 184 if (msgItem.mAttachmentType != AttachmentEditor.TEXT_ONLY) { 185 inflateMmsView(); 186 mMmsView.setVisibility(View.VISIBLE); 187 setOnClickListener(msgItem); 188 drawPlaybackButton(msgItem); 189 } else { 190 hideMmsViewIfNeeded(); 191 } 192 } 193 194 drawLeftStatusIndicator(msgItem.mBoxId); 195 drawRightStatusIndicator(msgItem); 196 } 197 198 private void hideMmsViewIfNeeded() { 199 if (mMmsView != null) { 200 mMmsView.setVisibility(View.GONE); 201 } 202 } 203 204 public void startAudio() { 205 // TODO Auto-generated method stub 206 } 207 208 public void startVideo() { 209 // TODO Auto-generated method stub 210 } 211 212 public void setAudio(Uri audio, String name, Map<String, ?> extras) { 213 // TODO Auto-generated method stub 214 } 215 216 public void setImage(String name, Bitmap bitmap) { 217 inflateMmsView(); 218 219 mImageView.setImageBitmap(bitmap); 220 mImageView.setVisibility(VISIBLE); 221 } 222 223 private void inflateMmsView() { 224 if (mMmsView == null) { 225 //inflate the surrounding view_stub 226 findViewById(R.id.mms_layout_view_stub).setVisibility(VISIBLE); 227 228 mMmsView = findViewById(R.id.mms_view); 229 mImageView = (ImageView) findViewById(R.id.image_view); 230 mSlideShowButton = (ImageButton) findViewById(R.id.play_slideshow_button); 231 } 232 } 233 234 private CharSequence formatMessage(String contact, String body, String subject, 235 String timestamp) { 236 SpannableStringBuilder buf = new SpannableStringBuilder(contact); 237 buf.append(": "); 238 buf.setSpan(STYLE_BOLD, 0, buf.length(), 239 Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 240 241 boolean hasSubject = !TextUtils.isEmpty(subject); 242 if (hasSubject) { 243 buf.append(mContext.getResources().getString(R.string.inline_subject, subject)); 244 } 245 246 if (!TextUtils.isEmpty(body)) { 247 if (hasSubject) { 248 buf.append(" - "); 249 } 250 buf.append(body); 251 } 252 253 buf.append("\n"); 254 buf.append(timestamp); 255 256 return buf; 257 } 258 259 private void drawPlaybackButton(MessageItem msgItem) { 260 switch (msgItem.mAttachmentType) { 261 case AttachmentEditor.SLIDESHOW_ATTACHMENT: 262 case AttachmentEditor.AUDIO_ATTACHMENT: 263 case AttachmentEditor.VIDEO_ATTACHMENT: 264 // Show the 'Play' button and bind message info on it. 265 mSlideShowButton.setTag(msgItem); 266 // Set call-back for the 'Play' button. 267 mSlideShowButton.setOnClickListener(this); 268 mSlideShowButton.setVisibility(View.VISIBLE); 269 setLongClickable(true); 270 break; 271 default: 272 mSlideShowButton.setVisibility(View.GONE); 273 break; 274 } 275 } 276 277 // OnClick Listener for the playback button 278 public void onClick(View v) { 279 MessageItem mi = (MessageItem) v.getTag(); 280 switch (mi.mAttachmentType) { 281 case AttachmentEditor.AUDIO_ATTACHMENT: 282 case AttachmentEditor.VIDEO_ATTACHMENT: 283 case AttachmentEditor.SLIDESHOW_ATTACHMENT: 284 Intent intent = new Intent( 285 mContext, SlideshowActivity.class); 286 intent.setData(mi.mMessageUri); 287 mContext.startActivity(intent); 288 break; 289 } 290 } 291 292 public void onMessageListItemClick() { 293 URLSpan[] spans = mBodyTextView.getUrls(); 294 295 if (spans.length == 0) { 296 // Do nothing. 297 } else if (spans.length == 1) { 298 Uri uri = Uri.parse(spans[0].getURL()); 299 Intent intent = new Intent(Intent.ACTION_VIEW, uri); 300 301 mContext.startActivity(intent); 302 } else { 303 Intent intent = new Intent( 304 mContext, UrlListActivity.class); 305 306 intent.putStringArrayListExtra( 307 EXTRA_URLS, extractUris(spans)); 308 mContext.startActivity(intent); 309 } 310 } 311 312 313 private void setOnClickListener(final MessageItem msgItem) { 314 if (msgItem.mAttachmentType == AttachmentEditor.IMAGE_ATTACHMENT) { 315 mImageView.setOnClickListener(new OnClickListener() { 316 public void onClick(View v) { 317 // FIXME: Use SlideshowActivity to view image for the time being. 318 // As described in UI spec, pressing an inline attachment will 319 // open up the full view of the attachment in its associated app 320 // (here should the pictures app). 321 // But the <ViewImage> would only show images in MediaStore. 322 // Should we save a copy to MediaStore temporarily for displaying? 323 Intent intent = new Intent(mContext, SlideshowActivity.class); 324 intent.setData(msgItem.mMessageUri); 325 mContext.startActivity(intent); 326 } 327 }); 328 } else { 329 mImageView.setOnClickListener(null); 330 } 331 } 332 333 private ArrayList<String> extractUris(URLSpan[] spans) { 334 int size = spans.length; 335 ArrayList<String> accumulator = new ArrayList<String>(); 336 337 for (int i = 0; i < size; i++) { 338 accumulator.add(spans[i].getURL()); 339 } 340 return accumulator; 341 } 342 343 private void drawLeftStatusIndicator(int msgBoxId) { 344 switch (msgBoxId) { 345 case Mms.MESSAGE_BOX_INBOX: 346 mLeftStatusIndicator.setVisibility(VISIBLE); 347 mLeftStatusIndicator.setImageResource(R.drawable.textfield_im_left_indicator_blue); 348 mMsgListItem.setBackgroundResource(R.drawable.light_blue_background); 349 break; 350 351 case Mms.MESSAGE_BOX_DRAFTS: 352 case Sms.MESSAGE_TYPE_FAILED: 353 case Sms.MESSAGE_TYPE_QUEUED: 354 case Mms.MESSAGE_BOX_OUTBOX: 355 mLeftStatusIndicator.setVisibility(VISIBLE); 356 mLeftStatusIndicator.setImageResource(R.drawable.textfield_im_left_indicator_red); 357 mMsgListItem.setBackgroundResource(R.drawable.white_background); 358 break; 359 360 default: 361 // reserve space for one indicator to preserve alignment 362 mLeftStatusIndicator.setVisibility(INVISIBLE); 363 mMsgListItem.setBackgroundResource(R.drawable.white_background); 364 break; 365 } 366 } 367 368 private boolean isFailedMessage(MessageItem msgItem) { 369 boolean isFailedMms = msgItem.isMms() 370 && (msgItem.mErrorType >= MmsSms.ERR_TYPE_GENERIC_PERMANENT); 371 boolean isFailedSms = msgItem.isSms() 372 && (msgItem.mBoxId == Sms.MESSAGE_TYPE_FAILED); 373 return isFailedMms || isFailedSms; 374 } 375 376 private void setErrorIndicatorClickListener(final MessageItem msgItem) { 377 String type = msgItem.mType; 378 final int what; 379 if (type.equals("sms")) { 380 what = MSG_LIST_EDIT_SMS; 381 } else { 382 what = MSG_LIST_EDIT_MMS; 383 } 384 mRightStatusIndicator.setOnClickListener(new OnClickListener() { 385 public void onClick(View v) { 386 if (null != mHandler) { 387 Message msg = Message.obtain(mHandler, what); 388 msg.obj = new Long(msgItem.mMsgId); 389 msg.sendToTarget(); 390 } 391 } 392 }); 393 } 394 395 private void drawRightStatusIndicator(MessageItem msgItem) { 396 if (msgItem.isOutgoingMessage()) { 397 if (isFailedMessage(msgItem)) { 398 mRightStatusIndicator.setImageResource(R.drawable.ic_sms_error); 399 setErrorIndicatorClickListener(msgItem); 400 } else { 401 mRightStatusIndicator.setImageResource(R.drawable.ic_email_pending); 402 } 403 mRightStatusIndicator.setVisibility(View.VISIBLE); 404 } else if (msgItem.mDeliveryReport || msgItem.mReadReport) { 405 mRightStatusIndicator.setImageResource(R.drawable.ic_mms_message_details); 406 mRightStatusIndicator.setVisibility(View.VISIBLE); 407 } else { 408 mRightStatusIndicator.setVisibility(View.GONE); 409 } 410 } 411 412 public void setImageRegionFit(String fit) { 413 // TODO Auto-generated method stub 414 } 415 416 public void setImageVisibility(boolean visible) { 417 // TODO Auto-generated method stub 418 } 419 420 public void setText(String name, String text) { 421 // TODO Auto-generated method stub 422 } 423 424 public void setTextVisibility(boolean visible) { 425 // TODO Auto-generated method stub 426 } 427 428 public void setVideo(String name, Uri video) { 429 inflateMmsView(); 430 431 MediaPlayer mp = new MediaPlayer(); 432 433 try { 434 mp.setDataSource(mContext, video); 435 mImageView.setImageBitmap(mp.getFrameAt(1000)); 436 mImageView.setVisibility(VISIBLE); 437 } catch (IOException e) { 438 Log.e(TAG, "Unexpected IOException.", e); 439 } finally { 440 mp.release(); 441 } 442 } 443 444 public void setVideoVisibility(boolean visible) { 445 // TODO Auto-generated method stub 446 } 447 448 public void stopAudio() { 449 // TODO Auto-generated method stub 450 } 451 452 public void stopVideo() { 453 // TODO Auto-generated method stub 454 } 455 456 public void reset() { 457 if (mImageView != null) { 458 mImageView.setVisibility(GONE); 459 } 460 } 461 462 public void setVisibility(boolean visible) { 463 // TODO Auto-generated method stub 464 } 465 466 public void pauseAudio() { 467 // TODO Auto-generated method stub 468 469 } 470 471 public void pauseVideo() { 472 // TODO Auto-generated method stub 473 474 } 475 476 public void seekAudio(int seekTo) { 477 // TODO Auto-generated method stub 478 479 } 480 481 public void seekVideo(int seekTo) { 482 // TODO Auto-generated method stub 483 484 } 485} 486