• 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 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 ConversationHeaderView extends RelativeLayout implements Contact.UpdateListener {
53     private static final String TAG = "ConversationHeaderView";
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 ConversationHeader mConversationHeader;
70 
71     private static final StyleSpan STYLE_BOLD = new StyleSpan(Typeface.BOLD);
72 
ConversationHeaderView(Context context)73     public ConversationHeaderView(Context context) {
74         super(context);
75     }
76 
ConversationHeaderView(Context context, AttributeSet attrs)77     public ConversationHeaderView(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
onFinishInflate()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 
setPresenceIcon(int iconId)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 
getConversationHeader()108     public ConversationHeader getConversationHeader() {
109         return mConversationHeader;
110     }
111 
setConversationHeader(ConversationHeader header)112     private void setConversationHeader(ConversationHeader header) {
113         mConversationHeader = header;
114     }
115 
116     /**
117      * Only used for header binding.
118      */
bind(String title, String explain)119     public void bind(String title, String explain) {
120         mFromView.setText(title);
121         mSubjectView.setText(explain);
122     }
123 
formatMessage(ConversationHeader ch)124     private CharSequence formatMessage(ConversationHeader 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 
updateAvatarView()154     private void updateAvatarView() {
155         ConversationHeader ch = mConversationHeader;
156 
157         Drawable avatarDrawable;
158         if (ch.getContacts().size() == 1) {
159             Contact contact = ch.getContacts().get(0);
160             avatarDrawable = contact.getAvatar(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 
updateFromView()176     private void updateFromView() {
177         ConversationHeader ch = mConversationHeader;
178         ch.updateRecipients();
179         mFromView.setText(formatMessage(ch));
180         setPresenceIcon(ch.getContacts().getPresenceResId());
181         updateAvatarView();
182     }
183 
onUpdate(Contact updated)184     public void onUpdate(Contact updated) {
185         mHandler.post(new Runnable() {
186             public void run() {
187                 updateFromView();
188             }
189         });
190     }
191 
bind(Context context, final ConversationHeader ch)192     public final void bind(Context context, final ConversationHeader 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         contacts.addListeners(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 
unbind()243     public final void unbind() {
244         if (DEBUG) Log.v(TAG, "unbind: contacts.removeListeners " + this);
245         // Unregister contact update callbacks.
246         mConversationHeader.getContacts().removeListeners(this);
247     }
248 }
249