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