ConversationViewHeader.java revision b53fe05fb867685d0d3f2f37daf0f93bc6488ded
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.text.Spannable;
23import android.text.SpannableStringBuilder;
24import android.text.TextUtils;
25import android.text.style.BackgroundColorSpan;
26import android.text.style.ForegroundColorSpan;
27import android.util.AttributeSet;
28import android.view.View;
29import android.view.ViewGroup;
30import android.view.View.OnClickListener;
31import android.widget.RelativeLayout;
32import android.widget.TextView;
33
34import com.android.mail.R;
35import com.android.mail.browse.ConversationViewAdapter.ConversationHeaderItem;
36import com.android.mail.browse.FolderSpan.FolderSpanDimensions;
37import com.android.mail.providers.Conversation;
38import com.android.mail.providers.Folder;
39import com.android.mail.providers.Settings;
40import com.android.mail.ui.FolderDisplayer;
41import com.android.mail.utils.LogTag;
42import com.android.mail.utils.LogUtils;
43import com.android.mail.utils.Utils;
44
45/**
46 * A view for the subject and folders in the conversation view. This container
47 * makes an attempt to combine subject and folders on the same horizontal line if
48 * there is enough room to fit both without wrapping. If they overlap, it
49 * adjusts the layout to position the folders below the subject.
50 */
51public class ConversationViewHeader extends RelativeLayout implements OnClickListener {
52
53    public interface ConversationViewHeaderCallbacks {
54        /**
55         * Called in response to a click on the folders region.
56         */
57        void onFoldersClicked();
58
59        /**
60         * Called when the height of the {@link ConversationViewHeader} changes.
61         *
62         * @param newHeight the new height in px
63         */
64        void onConversationViewHeaderHeightChange(int newHeight);
65    }
66
67    private static final String LOG_TAG = LogTag.getLogTag();
68    private TextView mSubjectView;
69    private FolderSpanTextView mFoldersView;
70    private ConversationViewHeaderCallbacks mCallbacks;
71    private ConversationAccountController mAccountController;
72    private ConversationFolderDisplayer mFolderDisplayer;
73    private ConversationHeaderItem mHeaderItem;
74
75    /**
76     * Instantiated from this layout: conversation_view_header.xml
77     * @param context
78     */
79    public ConversationViewHeader(Context context) {
80        this(context, null);
81    }
82
83    public ConversationViewHeader(Context context, AttributeSet attrs) {
84        super(context, attrs);
85    }
86
87    @Override
88    protected void onFinishInflate() {
89        super.onFinishInflate();
90
91        mSubjectView = (TextView) findViewById(R.id.subject);
92        mFoldersView = (FolderSpanTextView) findViewById(R.id.folders);
93
94        mFoldersView.setOnClickListener(this);
95        mFolderDisplayer = new ConversationFolderDisplayer(getContext(), mFoldersView);
96    }
97
98    @Override
99    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
100        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
101
102        // reposition the folders if they don't fit horizontally next to the
103        // subject
104        // (taking into account child margins and parent padding)
105        final int childWidthSum = getTotalMeasuredChildWidth(mSubjectView)
106                + getTotalMeasuredChildWidth(mFoldersView) + getPaddingLeft() + getPaddingRight();
107
108        if (childWidthSum > getMeasuredWidth()) {
109            LayoutParams params = (LayoutParams) mFoldersView.getLayoutParams();
110            params.addRule(RelativeLayout.BELOW, R.id.subject);
111            params.addRule(RelativeLayout.ALIGN_BASELINE, 0);
112            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
113        }
114    }
115
116    private static int getTotalMeasuredChildWidth(View child) {
117        final LayoutParams p = (LayoutParams) child.getLayoutParams();
118        return child.getMeasuredWidth() + p.leftMargin + p.rightMargin;
119    }
120
121    public void setCallbacks(ConversationViewHeaderCallbacks callbacks,
122            ConversationAccountController accountController) {
123        mCallbacks = callbacks;
124        mAccountController = accountController;
125    }
126
127    public void setSubject(final String subject) {
128        mSubjectView.setText(subject);
129        if (TextUtils.isEmpty(subject)) {
130            mSubjectView.setVisibility(GONE);
131        }
132    }
133
134    public void setFoldersVisible(boolean show) {
135        mFoldersView.setVisibility(show ? View.VISIBLE : View.GONE);
136    }
137
138    public void setFolders(Conversation conv) {
139        setFoldersVisible(true);
140        SpannableStringBuilder sb = new SpannableStringBuilder();
141        final Settings settings = mAccountController.getAccount().settings;
142        if (settings.priorityArrowsEnabled && conv.isImportant()) {
143            sb.append('.');
144            sb.setSpan(new PriorityIndicatorSpan(getContext(),
145                    R.drawable.ic_email_caret_none_important_unread, mFoldersView.getPadding(), 0),
146                    0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
147        }
148
149        mFolderDisplayer.loadConversationFolders(conv, null /* ignoreFolder */,
150                -1 /* ignoreFolderType */);
151        mFolderDisplayer.appendFolderSpans(sb);
152
153        mFoldersView.setText(sb);
154    }
155
156    public void bind(ConversationHeaderItem headerItem) {
157        mHeaderItem = headerItem;
158    }
159
160    private int measureHeight() {
161        ViewGroup parent = (ViewGroup) getParent();
162        if (parent == null) {
163            LogUtils.e(LOG_TAG, "Unable to measure height of conversation header");
164            return getHeight();
165        }
166        final int h = Utils.measureViewHeight(this, parent);
167        return h;
168    }
169
170    /**
171     * Update the conversation view header to reflect the updated conversation.
172     */
173    public void onConversationUpdated(Conversation conv) {
174        // The only things we have to worry about when the conversation changes
175        // in the conversation header are the folders and priority indicators.
176        // Updating these will resize the space for the header.
177        setFolders(conv);
178        if (mHeaderItem != null) {
179            final int h = measureHeight();
180            if (mHeaderItem.setHeight(h)) {
181                mCallbacks.onConversationViewHeaderHeightChange(h);
182            }
183        }
184    }
185
186    @Override
187    public void onClick(View v) {
188        if (R.id.folders == v.getId()) {
189            if (mCallbacks != null) {
190                mCallbacks.onFoldersClicked();
191            }
192        }
193    }
194
195    private static class ConversationFolderDisplayer extends FolderDisplayer {
196
197        private FolderSpanDimensions mDims;
198
199        public ConversationFolderDisplayer(Context context, FolderSpanDimensions dims) {
200            super(context);
201            mDims = dims;
202        }
203
204        public void appendFolderSpans(SpannableStringBuilder sb) {
205            for (final Folder f : mFoldersSortedSet) {
206                final int bgColor = f.getBackgroundColor(mDefaultBgColor);
207                final int fgColor = f.getForegroundColor(mDefaultFgColor);
208                addSpan(sb, f.name, bgColor, fgColor);
209            }
210
211            if (mFoldersSortedSet.isEmpty()) {
212                final Resources r = mContext.getResources();
213                final String name = r.getString(R.string.add_label);
214                final int bgColor = r.getColor(R.color.conv_header_add_label_background);
215                final int fgColor = r.getColor(R.color.conv_header_add_label_text);
216                addSpan(sb, name, bgColor, fgColor);
217            }
218        }
219
220        private void addSpan(SpannableStringBuilder sb, String name, int bgColor,
221                             int fgColor) {
222            final int start = sb.length();
223            sb.append(name);
224            final int end = sb.length();
225
226            sb.setSpan(new BackgroundColorSpan(bgColor), start, end,
227                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
228            sb.setSpan(new ForegroundColorSpan(fgColor), start, end,
229                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
230            sb.setSpan(new FolderSpan(sb, mDims), start, end,
231                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
232        }
233
234    }
235}
236