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