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