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
18package com.android.mail.browse;
19
20import android.content.Context;
21import android.content.res.Resources;
22import android.graphics.drawable.Drawable;
23import android.support.v4.text.BidiFormatter;
24import android.text.Layout;
25import android.text.Spannable;
26import android.text.SpannableStringBuilder;
27import android.text.Spanned;
28import android.text.method.LinkMovementMethod;
29import android.text.style.BackgroundColorSpan;
30import android.text.style.DynamicDrawableSpan;
31import android.text.style.ForegroundColorSpan;
32import android.util.AttributeSet;
33import android.widget.TextView;
34
35import com.android.mail.R;
36import com.android.mail.browse.ConversationViewHeader.ConversationViewHeaderCallbacks;
37import com.android.mail.providers.Account;
38import com.android.mail.providers.Conversation;
39import com.android.mail.providers.Folder;
40import com.android.mail.providers.Settings;
41import com.android.mail.text.ChangeLabelsSpan;
42import com.android.mail.ui.FolderDisplayer;
43import com.android.mail.utils.ViewUtils;
44
45/**
46 * A TextView that displays the conversation subject and list of folders for the message.
47 * The view knows the widest that any of its containing {@link FolderSpan}s can be.
48 * They cannot exceed the TextView line width, or else {@link Layout}
49 * will split up the spans in strange places.
50 */
51public class SubjectAndFolderView extends TextView
52        implements FolderSpan.FolderSpanDimensions {
53
54    private final int mFolderPadding;
55    private final int mFolderPaddingExtraWidth;
56    private final int mFolderPaddingAfter;
57    private final int mRoundedCornerRadius;
58    private final float mFolderSpanTextSize;
59    private final int mFolderMarginTop;
60
61    private int mMaxSpanWidth;
62
63    private ConversationFolderDisplayer mFolderDisplayer;
64
65    private String mSubject;
66
67    private boolean mVisibleFolders;
68
69    private ConversationViewAdapter.ConversationHeaderItem mHeaderItem;
70
71    private BidiFormatter mBidiFormatter;
72
73    public SubjectAndFolderView(Context context) {
74        this(context, null);
75    }
76
77    public SubjectAndFolderView(Context context, AttributeSet attrs) {
78        super(context, attrs);
79
80        mVisibleFolders = false;
81        mFolderDisplayer = new ConversationFolderDisplayer(getContext(), this);
82
83        final Resources r = getResources();
84        mFolderPadding = r.getDimensionPixelOffset(R.dimen.conversation_folder_padding);
85        mFolderPaddingExtraWidth = r.getDimensionPixelOffset(
86                R.dimen.conversation_folder_padding_extra_width);
87        mFolderPaddingAfter = r.getDimensionPixelOffset(
88                R.dimen.conversation_folder_padding_after);
89        mRoundedCornerRadius = r.getDimensionPixelOffset(
90                R.dimen.folder_rounded_corner_radius);
91        mFolderSpanTextSize = r.getDimension(R.dimen.conversation_folder_font_size);
92        mFolderMarginTop = r.getDimensionPixelOffset(R.dimen.conversation_folder_margin_top);
93    }
94
95    @Override
96    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
97        mMaxSpanWidth = MeasureSpec.getSize(widthMeasureSpec) - getTotalPaddingLeft()
98                - getTotalPaddingRight();
99
100        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
101    }
102
103    @Override
104    public int getPadding() {
105        return mFolderPadding;
106    }
107
108    @Override
109    public int getPaddingExtraWidth() {
110        return mFolderPaddingExtraWidth;
111    }
112
113    @Override
114    public int getPaddingAfter() {
115        return mFolderPaddingAfter;
116    }
117
118    @Override
119    public int getMaxWidth() {
120        return mMaxSpanWidth;
121    }
122
123    @Override
124    public float getRoundedCornerRadius() {
125        return mRoundedCornerRadius;
126    }
127
128    @Override
129    public float getFolderSpanTextSize() {
130        return mFolderSpanTextSize;
131    }
132
133    @Override
134    public int getMarginTop() {
135        return mFolderMarginTop;
136    }
137
138    @Override
139    public boolean isRtl() {
140        return ViewUtils.isViewRtl(this);
141    }
142
143    public void setSubject(String subject) {
144        mSubject = Conversation.getSubjectForDisplay(getContext(), null /* badgeText */, subject);
145
146        if (!mVisibleFolders) {
147            setText(mSubject);
148        }
149    }
150
151    public void setFolders(
152            ConversationViewHeaderCallbacks callbacks, Account account, Conversation conv) {
153        mVisibleFolders = true;
154        final BidiFormatter bidiFormatter = getBidiFormatter();
155        final SpannableStringBuilder sb =
156                new SpannableStringBuilder(bidiFormatter.unicodeWrap(mSubject));
157        sb.append('\u0020');
158        final Settings settings = account.settings;
159        final int start = sb.length();
160        if (settings.importanceMarkersEnabled && conv.isImportant()) {
161            sb.append(".\u0020");
162            sb.setSpan(new DynamicDrawableSpan(DynamicDrawableSpan.ALIGN_BASELINE) {
163                           @Override
164                           public Drawable getDrawable() {
165                               Drawable d = getContext().getResources().getDrawable(
166                                       R.drawable.ic_email_caret_none_important_unread);
167                               d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
168                               return d;
169                           }
170                       },
171                    start, start + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
172        }
173
174        mFolderDisplayer.loadConversationFolders(conv, null /* ignoreFolder */,
175                -1 /* ignoreFolderType */);
176        mFolderDisplayer.appendFolderSpans(sb, bidiFormatter);
177
178        final int end = sb.length();
179        sb.setSpan(new ChangeLabelsSpan(callbacks), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
180
181        setText(sb);
182        setMovementMethod(LinkMovementMethod.getInstance());
183    }
184
185    public void bind(ConversationViewAdapter.ConversationHeaderItem headerItem) {
186        mHeaderItem = headerItem;
187    }
188
189    private BidiFormatter getBidiFormatter() {
190        if (mBidiFormatter == null) {
191            final ConversationViewAdapter adapter = mHeaderItem != null
192                    ? mHeaderItem.getAdapter() : null;
193            if (adapter == null) {
194                mBidiFormatter = BidiFormatter.getInstance();
195            } else {
196                mBidiFormatter = adapter.getBidiFormatter();
197            }
198        }
199        return mBidiFormatter;
200    }
201
202    private static class ConversationFolderDisplayer extends FolderDisplayer {
203
204        private final FolderSpan.FolderSpanDimensions mDims;
205
206        public ConversationFolderDisplayer(Context context, FolderSpan.FolderSpanDimensions dims) {
207            super(context);
208            mDims = dims;
209        }
210
211        public void appendFolderSpans(SpannableStringBuilder sb, BidiFormatter bidiFormatter) {
212            for (final Folder f : mFoldersSortedSet) {
213                final int bgColor = f.getBackgroundColor(mDefaultBgColor);
214                final int fgColor = f.getForegroundColor(mDefaultFgColor);
215                addSpan(sb, f.name, bgColor, fgColor, bidiFormatter);
216            }
217
218            if (mFoldersSortedSet.isEmpty()) {
219                final Resources r = mContext.getResources();
220                final String name = r.getString(R.string.add_label);
221                final int bgColor = r.getColor(R.color.conv_header_add_label_background);
222                final int fgColor = r.getColor(R.color.conv_header_add_label_text);
223                addSpan(sb, name, bgColor, fgColor, bidiFormatter);
224            }
225        }
226
227        private void addSpan(SpannableStringBuilder sb, String name,
228                int bgColor, int fgColor, BidiFormatter bidiFormatter) {
229            final int start = sb.length();
230            sb.append(bidiFormatter.unicodeWrap(name));
231            final int end = sb.length();
232
233            sb.setSpan(new BackgroundColorSpan(bgColor), start, end,
234                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
235            sb.setSpan(new ForegroundColorSpan(fgColor), start, end,
236                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
237            sb.setSpan(new FolderSpan(sb, mDims), start, end,
238                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
239        }
240    }
241}
242