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