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 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 
     68     public ConversationListItem(Context context) {
     69         super(context);
     70     }
     71 
     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
     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 
     93     public Conversation getConversation() {
     94         return mConversation;
     95     }
     96 
     97     /**
     98      * Only used for header binding.
     99      */
    100     public void bind(String title, String explain) {
    101         mFromView.setText(title);
    102         mSubjectView.setText(explain);
    103     }
    104 
    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 
    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 
    160     private void updateFromView() {
    161         mFromView.setText(formatMessage());
    162         updateAvatarView();
    163     }
    164 
    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 
    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 
    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 
    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 
    246     public void setChecked(boolean checked) {
    247         mConversation.setIsChecked(checked);
    248         updateBackground();
    249     }
    250 
    251     public boolean isChecked() {
    252         return mConversation.isChecked();
    253     }
    254 
    255     public void toggle() {
    256         mConversation.setIsChecked(!mConversation.isChecked());
    257     }
    258 }
    259