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