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