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