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