Home | History | Annotate | Download | only in browse
      1 /*
      2  * Copyright (C) 2012 Google Inc.
      3  * Licensed to 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.mail.browse;
     19 
     20 import android.content.Context;
     21 import android.content.res.Resources;
     22 import android.graphics.Canvas;
     23 import android.graphics.Paint;
     24 import android.graphics.drawable.Drawable;
     25 import android.support.v4.text.BidiFormatter;
     26 import android.text.Layout;
     27 import android.text.SpannableStringBuilder;
     28 import android.text.Spanned;
     29 import android.text.method.LinkMovementMethod;
     30 import android.text.style.ReplacementSpan;
     31 import android.util.AttributeSet;
     32 import android.widget.TextView;
     33 
     34 import com.android.mail.R;
     35 import com.android.mail.browse.ConversationViewHeader.ConversationViewHeaderCallbacks;
     36 import com.android.mail.providers.Account;
     37 import com.android.mail.providers.Conversation;
     38 import com.android.mail.providers.Folder;
     39 import com.android.mail.providers.Settings;
     40 import com.android.mail.text.ChangeLabelsSpan;
     41 import com.android.mail.text.FolderSpan;
     42 import com.android.mail.ui.FolderDisplayer;
     43 
     44 /**
     45  * A TextView that displays the conversation subject and list of folders for the message.
     46  * The view knows the widest that any of its containing {@link com.android.mail.text.FolderSpan}s
     47  * can be. They cannot exceed the TextView line width, or else {@link Layout} will split up the
     48  * spans in strange places.
     49  */
     50 public class SubjectAndFolderView extends TextView implements FolderSpan.FolderSpanDimensions {
     51     private final String mNoFolderChipName;
     52     private final int mNoFolderBgColor;
     53     private final int mNoFolderFgColor;
     54     private final Drawable mImportanceMarkerDrawable;
     55     private final int mChipVerticalOffset;
     56 
     57     private int mMaxSpanWidth;
     58 
     59     private ConversationFolderDisplayer mFolderDisplayer;
     60 
     61     private String mSubject;
     62 
     63     private boolean mVisibleFolders;
     64 
     65     private ConversationViewAdapter.ConversationHeaderItem mHeaderItem;
     66 
     67     private BidiFormatter mBidiFormatter;
     68 
     69     public SubjectAndFolderView(Context context) {
     70         this(context, null);
     71     }
     72 
     73     public SubjectAndFolderView(Context context, AttributeSet attrs) {
     74         super(context, attrs);
     75 
     76         final Resources res = getResources();
     77         mNoFolderChipName = res.getString(R.string.add_label);
     78         mNoFolderBgColor = res.getColor(R.color.conv_header_add_label_background);
     79         mNoFolderFgColor = res.getColor(R.color.conv_header_add_label_text);
     80         mImportanceMarkerDrawable = res.getDrawable(
     81                 R.drawable.ic_email_caret_none_important_unread);
     82         mImportanceMarkerDrawable.setBounds(0, 0, mImportanceMarkerDrawable.getIntrinsicWidth(),
     83                 mImportanceMarkerDrawable.getIntrinsicHeight());
     84         mChipVerticalOffset = res.getDimensionPixelOffset(R.dimen.folder_cv_vertical_offset);
     85 
     86         mVisibleFolders = false;
     87         mFolderDisplayer = new ConversationFolderDisplayer(getContext());
     88     }
     89 
     90     @Override
     91     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     92         mMaxSpanWidth = MeasureSpec.getSize(widthMeasureSpec) - getTotalPaddingLeft()
     93                 - getTotalPaddingRight();
     94 
     95         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     96     }
     97 
     98     public void setSubject(String subject) {
     99         mSubject = Conversation.getSubjectForDisplay(getContext(), null /* badgeText */, subject);
    100 
    101         if (!mVisibleFolders) {
    102             setText(mSubject);
    103         }
    104     }
    105 
    106     public void setFolders(ConversationViewHeaderCallbacks callbacks, Account account,
    107             Conversation conv) {
    108         mVisibleFolders = true;
    109         final BidiFormatter bidiFormatter = getBidiFormatter();
    110         final String wrappedSubject = mSubject == null ? "" : bidiFormatter.unicodeWrap(mSubject);
    111         final SpannableStringBuilder sb = new SpannableStringBuilder(wrappedSubject);
    112         sb.append('\u0020');
    113         final Settings settings = account.settings;
    114         final int start = sb.length();
    115         if (settings.importanceMarkersEnabled && conv.isImportant()) {
    116             sb.append(".\u0020");
    117             sb.setSpan(new ReplacementSpan() {
    118                 @Override
    119                 public int getSize(Paint paint, CharSequence text, int start, int end,
    120                         Paint.FontMetricsInt fm) {
    121                     return mImportanceMarkerDrawable.getIntrinsicWidth();
    122                 }
    123 
    124                 @Override
    125                 public void draw(Canvas canvas, CharSequence text, int start, int end, float x,
    126                         int top, int baseline, int bottom, Paint paint) {
    127                     canvas.save();
    128                     final int transY = baseline + mChipVerticalOffset -
    129                             mImportanceMarkerDrawable.getIntrinsicHeight();
    130                     canvas.translate(x, transY);
    131                     mImportanceMarkerDrawable.draw(canvas);
    132                     canvas.restore();
    133                 }
    134             }, start, start + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
    135         }
    136 
    137         mFolderDisplayer.loadConversationFolders(conv, null /* ignoreFolder */,
    138                 -1 /* ignoreFolderType */);
    139         mFolderDisplayer.constructFolderChips(sb);
    140 
    141         final int end = sb.length();
    142         sb.setSpan(new ChangeLabelsSpan(callbacks), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    143 
    144         setText(sb);
    145         setMovementMethod(LinkMovementMethod.getInstance());
    146     }
    147 
    148     public void bind(ConversationViewAdapter.ConversationHeaderItem headerItem) {
    149         mHeaderItem = headerItem;
    150     }
    151 
    152     private BidiFormatter getBidiFormatter() {
    153         if (mBidiFormatter == null) {
    154             final ConversationViewAdapter adapter = mHeaderItem != null
    155                     ? mHeaderItem.getAdapter() : null;
    156             if (adapter == null) {
    157                 mBidiFormatter = BidiFormatter.getInstance();
    158             } else {
    159                 mBidiFormatter = adapter.getBidiFormatter();
    160             }
    161         }
    162         return mBidiFormatter;
    163     }
    164 
    165     public String getSubject() {
    166         return mSubject;
    167     }
    168 
    169     @Override
    170     public int getMaxChipWidth() {
    171         return mMaxSpanWidth;
    172     }
    173 
    174     private class ConversationFolderDisplayer extends FolderDisplayer {
    175 
    176         public ConversationFolderDisplayer(Context context) {
    177             super(context);
    178         }
    179 
    180         @Override
    181         protected void initializeDrawableResources() {
    182             super.initializeDrawableResources();
    183             final Resources res = mContext.getResources();
    184             mFolderDrawableResources.overflowGradientPadding = 0;   // not applicable
    185             mFolderDrawableResources.folderHorizontalPadding =
    186                     res.getDimensionPixelOffset(R.dimen.folder_cv_cell_content_padding);
    187             mFolderDrawableResources.folderFontSize =
    188                     res.getDimensionPixelOffset(R.dimen.folder_cv_font_size);
    189             mFolderDrawableResources.folderVerticalOffset = mChipVerticalOffset;
    190         }
    191 
    192         private void constructFolderChips(SpannableStringBuilder sb) {
    193             for (final Folder f : mFoldersSortedSet) {
    194                 addSpan(sb, f.name, f.getForegroundColor(mFolderDrawableResources.defaultFgColor),
    195                         f.getBackgroundColor(mFolderDrawableResources.defaultBgColor));
    196             }
    197 
    198             if (mFoldersSortedSet.isEmpty()) {
    199                 addSpan(sb, mNoFolderChipName, mNoFolderFgColor, mNoFolderBgColor);
    200             }
    201         }
    202 
    203         private void addSpan(SpannableStringBuilder sb, String name, int fgColor, int bgColor) {
    204             final FolderSpan span = new FolderSpan(name, fgColor, bgColor, mFolderDrawableResources,
    205                     getBidiFormatter(), SubjectAndFolderView.this);
    206             final int start = sb.length();
    207             sb.append(name);
    208             sb.setSpan(span, start, start + name.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    209             sb.append(" ");
    210         }
    211     }
    212 }
    213