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