ConversationListItem.java revision 3fb271abe0c478d0ea8432830589774182079c86
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.data.Contact; 22import com.android.mms.data.ContactList; 23 24import android.content.Context; 25import android.graphics.Typeface; 26import android.graphics.drawable.Drawable; 27 28import android.os.Handler; 29import android.text.Spannable; 30import android.text.SpannableStringBuilder; 31import android.text.style.ForegroundColorSpan; 32import android.text.style.StyleSpan; 33import android.text.style.TextAppearanceSpan; 34import android.util.AttributeSet; 35import android.util.Log; 36import android.view.View; 37import android.widget.QuickContactBadge; 38import android.widget.ImageView; 39import android.widget.RelativeLayout; 40import android.widget.TextView; 41 42/** 43 * This class manages the view for given conversation. 44 */ 45public class ConversationListItem extends RelativeLayout implements Contact.UpdateListener { 46 private static final String TAG = "ConversationListItem"; 47 private static final boolean DEBUG = false; 48 49 private TextView mSubjectView; 50 private TextView mFromView; 51 private TextView mDateView; 52 private View mAttachmentView; 53 private View mErrorIndicator; 54 private QuickContactBadge mAvatarView; 55 56 static private Drawable sDefaultContactImage; 57 58 // For posting UI update Runnables from other threads: 59 private Handler mHandler = new Handler(); 60 61 private ConversationListItemData mConversationHeader; 62 63 private static final StyleSpan STYLE_BOLD = new StyleSpan(Typeface.BOLD); 64 65 public ConversationListItem(Context context) { 66 super(context); 67 } 68 69 public ConversationListItem(Context context, AttributeSet attrs) { 70 super(context, attrs); 71 72 if (sDefaultContactImage == null) { 73 sDefaultContactImage = context.getResources().getDrawable(R.drawable.ic_contact_picture); 74 } 75 } 76 77 @Override 78 protected void onFinishInflate() { 79 super.onFinishInflate(); 80 81 mFromView = (TextView) findViewById(R.id.from); 82 mSubjectView = (TextView) findViewById(R.id.subject); 83 84 mDateView = (TextView) findViewById(R.id.date); 85 mAttachmentView = findViewById(R.id.attachment); 86 mErrorIndicator = findViewById(R.id.error); 87 mAvatarView = (QuickContactBadge) findViewById(R.id.avatar); 88 } 89 90 public ConversationListItemData getConversationHeader() { 91 return mConversationHeader; 92 } 93 94 private void setConversationHeader(ConversationListItemData header) { 95 mConversationHeader = header; 96 } 97 98 /** 99 * Only used for header binding. 100 */ 101 public void bind(String title, String explain) { 102 mFromView.setText(title); 103 mSubjectView.setText(explain); 104 } 105 106 private CharSequence formatMessage(ConversationListItemData ch) { 107 final int color = android.R.styleable.Theme_textColorSecondary; 108 String from = ch.getFrom(); 109 110 SpannableStringBuilder buf = new SpannableStringBuilder(from); 111 112 if (ch.getMessageCount() > 1) { 113 buf.append(mContext.getResources().getString(R.string.message_count_format, 114 ch.getMessageCount())); 115 } 116 if (ch.hasDraft()) { 117 buf.append(mContext.getResources().getString(R.string.draft_separator)); 118 int before = buf.length(); 119 int size; 120 buf.append(mContext.getResources().getString(R.string.has_draft)); 121 size = android.R.style.TextAppearance_Small; 122 buf.setSpan(new TextAppearanceSpan(mContext, size, color), before, 123 buf.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 124 buf.setSpan(new ForegroundColorSpan( 125 mContext.getResources().getColor(R.drawable.text_color_blue)), 126 before, buf.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 127 } 128 129 // Unread messages are shown in bold 130 if (!ch.isRead()) { 131 buf.setSpan(STYLE_BOLD, 0, buf.length(), 132 Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 133 } 134 return buf; 135 } 136 137 private void updateAvatarView() { 138 ConversationListItemData ch = mConversationHeader; 139 140 Drawable avatarDrawable; 141 if (ch.getContacts().size() == 1) { 142 Contact contact = ch.getContacts().get(0); 143 avatarDrawable = contact.getAvatar(mContext, sDefaultContactImage); 144 145 if (contact.existsInDatabase()) { 146 mAvatarView.assignContactUri(contact.getUri()); 147 } else { 148 mAvatarView.assignContactFromPhone(contact.getNumber(), true); 149 } 150 } else { 151 // TODO get a multiple recipients asset (or do something else) 152 avatarDrawable = sDefaultContactImage; 153 mAvatarView.assignContactUri(null); 154 } 155 mAvatarView.setImageDrawable(avatarDrawable); 156 mAvatarView.setVisibility(View.VISIBLE); 157 } 158 159 private void updateFromView() { 160 ConversationListItemData ch = mConversationHeader; 161 ch.updateRecipients(); 162 mFromView.setText(formatMessage(ch)); 163 updateAvatarView(); 164 } 165 166 public void onUpdate(Contact updated) { 167 mHandler.post(new Runnable() { 168 public void run() { 169 updateFromView(); 170 } 171 }); 172 } 173 174 public final void bind(Context context, final ConversationListItemData ch) { 175 //if (DEBUG) Log.v(TAG, "bind()"); 176 177 setConversationHeader(ch); 178 179 Drawable background = ch.isRead()? 180 mContext.getResources().getDrawable(R.drawable.conversation_item_background_read) : 181 mContext.getResources().getDrawable(R.drawable.conversation_item_background_unread); 182 183 setBackgroundDrawable(background); 184 185 LayoutParams attachmentLayout = (LayoutParams)mAttachmentView.getLayoutParams(); 186 boolean hasError = ch.hasError(); 187 // When there's an error icon, the attachment icon is left of the error icon. 188 // When there is not an error icon, the attachment icon is left of the date text. 189 // As far as I know, there's no way to specify that relationship in xml. 190 if (hasError) { 191 attachmentLayout.addRule(RelativeLayout.LEFT_OF, R.id.error); 192 } else { 193 attachmentLayout.addRule(RelativeLayout.LEFT_OF, R.id.date); 194 } 195 196 boolean hasAttachment = ch.hasAttachment(); 197 mAttachmentView.setVisibility(hasAttachment ? VISIBLE : GONE); 198 199 // Date 200 mDateView.setText(ch.getDate()); 201 202 // From. 203 mFromView.setText(formatMessage(ch)); 204 205 // Register for updates in changes of any of the contacts in this conversation. 206 ContactList contacts = ch.getContacts(); 207 208 if (DEBUG) Log.v(TAG, "bind: contacts.addListeners " + this); 209 Contact.addListener(this); 210 211 // Subject 212 mSubjectView.setText(ch.getSubject()); 213 LayoutParams subjectLayout = (LayoutParams)mSubjectView.getLayoutParams(); 214 // We have to make the subject left of whatever optional items are shown on the right. 215 subjectLayout.addRule(RelativeLayout.LEFT_OF, hasAttachment ? R.id.attachment : 216 (hasError ? R.id.error : R.id.date)); 217 218 // Transmission error indicator. 219 mErrorIndicator.setVisibility(hasError ? VISIBLE : GONE); 220 221 updateAvatarView(); 222 } 223 224 public final void unbind() { 225 if (DEBUG) Log.v(TAG, "unbind: contacts.removeListeners " + this); 226 // Unregister contact update callbacks. 227 Contact.removeListener(this); 228 } 229} 230