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