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.Paint.FontMetricsInt;
     23 import android.graphics.Typeface;
     24 import android.util.SparseArray;
     25 import android.view.LayoutInflater;
     26 import android.view.View;
     27 import android.view.View.MeasureSpec;
     28 import android.view.ViewGroup;
     29 import android.view.ViewGroup.LayoutParams;
     30 import android.widget.FrameLayout;
     31 import android.widget.TextView;
     32 
     33 import com.android.mail.R;
     34 import com.android.mail.R.dimen;
     35 import com.android.mail.R.id;
     36 import com.android.mail.ui.ViewMode;
     37 import com.android.mail.utils.Utils;
     38 import com.google.common.base.Objects;
     39 
     40 /**
     41  * Represents the coordinates of elements inside a CanvasConversationHeaderView
     42  * (eg, checkmark, star, subject, sender, folders, etc.) It will inflate a view,
     43  * and record the coordinates of each element after layout. This will allows us
     44  * to easily improve performance by creating custom view while still defining
     45  * layout in XML files.
     46  *
     47  * @author phamm
     48  */
     49 public class ConversationItemViewCoordinates {
     50     // Modes
     51     static final int MODE_COUNT = 2;
     52     static final int WIDE_MODE = 0;
     53     static final int NORMAL_MODE = 1;
     54 
     55     // Left-side gadget modes
     56     static final int GADGET_NONE = 0;
     57     static final int GADGET_CONTACT_PHOTO = 1;
     58     static final int GADGET_CHECKBOX = 2;
     59 
     60     // Attachment previews modes
     61     static final int ATTACHMENT_PREVIEW_NONE = 0;
     62     static final int ATTACHMENT_PREVIEW_UNREAD = 1;
     63     static final int ATTACHMENT_PREVIEW_READ = 2;
     64 
     65     // For combined views
     66     private static int COLOR_BLOCK_WIDTH = -1;
     67     private static int COLOR_BLOCK_HEIGHT = -1;
     68 
     69     /**
     70      * Simple holder class for an item's abstract configuration state. ListView binding creates an
     71      * instance per item, and {@link #forConfig(Context, Config, SparseArray)} uses it to hide/show
     72      * optional views and determine the correct coordinates for that item configuration.
     73      */
     74     public static final class Config {
     75         private int mWidth;
     76         private int mViewMode = ViewMode.UNKNOWN;
     77         private int mGadgetMode = GADGET_NONE;
     78         private int mAttachmentPreviewMode = ATTACHMENT_PREVIEW_NONE;
     79         private boolean mShowFolders = false;
     80         private boolean mShowReplyState = false;
     81         private boolean mShowColorBlock = false;
     82         private boolean mShowPersonalIndicator = false;
     83 
     84         public Config setViewMode(int viewMode) {
     85             mViewMode = viewMode;
     86             return this;
     87         }
     88 
     89         public Config withGadget(int gadget) {
     90             mGadgetMode = gadget;
     91             return this;
     92         }
     93 
     94         public Config withAttachmentPreviews(int attachmentPreviewMode) {
     95             mAttachmentPreviewMode = attachmentPreviewMode;
     96             return this;
     97         }
     98 
     99         public Config showFolders() {
    100             mShowFolders = true;
    101             return this;
    102         }
    103 
    104         public Config showReplyState() {
    105             mShowReplyState = true;
    106             return this;
    107         }
    108 
    109         public Config showColorBlock() {
    110             mShowColorBlock = true;
    111             return this;
    112         }
    113 
    114         public Config showPersonalIndicator() {
    115             mShowPersonalIndicator  = true;
    116             return this;
    117         }
    118 
    119         public Config updateWidth(int width) {
    120             mWidth = width;
    121             return this;
    122         }
    123 
    124         public int getWidth() {
    125             return mWidth;
    126         }
    127 
    128         public int getViewMode() {
    129             return mViewMode;
    130         }
    131 
    132         public int getGadgetMode() {
    133             return mGadgetMode;
    134         }
    135 
    136         public int getAttachmentPreviewMode() {
    137             return mAttachmentPreviewMode;
    138         }
    139 
    140         public boolean areFoldersVisible() {
    141             return mShowFolders;
    142         }
    143 
    144         public boolean isReplyStateVisible() {
    145             return mShowReplyState;
    146         }
    147 
    148         public boolean isColorBlockVisible() {
    149             return mShowColorBlock;
    150         }
    151 
    152         public boolean isPersonalIndicatorVisible() {
    153             return mShowPersonalIndicator;
    154         }
    155 
    156         private int getCacheKey() {
    157             // hash the attributes that contribute to item height and child view geometry
    158             return Objects.hashCode(mWidth, mViewMode, mGadgetMode, mAttachmentPreviewMode,
    159                     mShowFolders, mShowReplyState, mShowPersonalIndicator);
    160         }
    161 
    162     }
    163 
    164     public static class CoordinatesCache {
    165         private final SparseArray<ConversationItemViewCoordinates> mCoordinatesCache
    166                 = new SparseArray<ConversationItemViewCoordinates>();
    167         private final SparseArray<View> mViewsCache = new SparseArray<View>();
    168 
    169         public ConversationItemViewCoordinates getCoordinates(final int key) {
    170             return mCoordinatesCache.get(key);
    171         }
    172 
    173         public View getView(final int layoutId) {
    174             return mViewsCache.get(layoutId);
    175         }
    176 
    177         public void put(final int key, final ConversationItemViewCoordinates coords) {
    178             mCoordinatesCache.put(key, coords);
    179         }
    180 
    181         public void put(final int layoutId, final View view) {
    182             mViewsCache.put(layoutId, view);
    183         }
    184     }
    185 
    186     /**
    187      * One of either NORMAL_MODE or WIDE_MODE.
    188      */
    189     private final int mMode;
    190 
    191     final int height;
    192 
    193     // Star.
    194     final int starX;
    195     final int starY;
    196     final int starWidth;
    197 
    198     // Senders.
    199     final int sendersX;
    200     final int sendersY;
    201     final int sendersWidth;
    202     final int sendersHeight;
    203     final int sendersLineCount;
    204     final int sendersLineHeight;
    205     final float sendersFontSize;
    206 
    207     // Subject.
    208     final int subjectX;
    209     final int subjectY;
    210     final int subjectWidth;
    211     final int subjectHeight;
    212     final int subjectLineCount;
    213     final float subjectFontSize;
    214 
    215     // Folders.
    216     final int foldersX;
    217     final int foldersXEnd;
    218     final int foldersY;
    219     final int foldersHeight;
    220     final Typeface foldersTypeface;
    221     final float foldersFontSize;
    222     final int foldersTextBottomPadding;
    223 
    224     // Info icon
    225     final int infoIconXEnd;
    226     final int infoIconY;
    227 
    228     // Date.
    229     final int dateXEnd;
    230     final int dateY;
    231     final int datePaddingLeft;
    232     final float dateFontSize;
    233     final int dateYBaseline;
    234 
    235     // Paperclip.
    236     final int paperclipY;
    237     final int paperclipPaddingLeft;
    238 
    239     // Color block.
    240     final int colorBlockX;
    241     final int colorBlockY;
    242     final int colorBlockWidth;
    243     final int colorBlockHeight;
    244 
    245     // Reply state of a conversation.
    246     final int replyStateX;
    247     final int replyStateY;
    248 
    249     final int personalIndicatorX;
    250     final int personalIndicatorY;
    251 
    252     final int contactImagesHeight;
    253     final int contactImagesWidth;
    254     final int contactImagesX;
    255     final int contactImagesY;
    256 
    257     // Attachment previews
    258     public final int attachmentPreviewsX;
    259     public final int attachmentPreviewsY;
    260     final int attachmentPreviewsWidth;
    261     final int attachmentPreviewsHeight;
    262     public final int attachmentPreviewsDecodeHeight;
    263 
    264     // Attachment previews overflow badge and count
    265     public final int overflowXEnd;
    266     public final int overflowYEnd;
    267     public final int overflowDiameter;
    268     public final float overflowFontSize;
    269     public final Typeface overflowTypeface;
    270 
    271     // Attachment previews placeholder
    272     final int placeholderY;
    273     public final int placeholderWidth;
    274     public final int placeholderHeight;
    275     // Attachment previews progress bar
    276     final int progressBarY;
    277     public final int progressBarWidth;
    278     public final int progressBarHeight;
    279 
    280     /**
    281      * The smallest item width for which we use the "wide" layout.
    282      */
    283     private final int mMinListWidthForWide;
    284     /**
    285      * The smallest item width for which we use the "spacious" variant of the normal layout,
    286      * if the normal version is used at all. Larger than {@link #mMinListWidthForWide}, we use
    287      * wide mode anyway, and this value is unused.
    288      */
    289     private final int mMinListWidthIsSpacious;
    290     private final int mFolderCellWidth;
    291     private final int mFolderMinimumWidth;
    292 
    293     private ConversationItemViewCoordinates(final Context context, final Config config,
    294             final CoordinatesCache cache) {
    295         Utils.traceBeginSection("CIV coordinates constructor");
    296         final Resources res = context.getResources();
    297         mFolderCellWidth = res.getDimensionPixelSize(R.dimen.folder_cell_width);
    298         mMinListWidthForWide = res.getDimensionPixelSize(R.dimen.list_min_width_is_wide);
    299         mMinListWidthIsSpacious = res.getDimensionPixelSize(
    300                 R.dimen.list_normal_mode_min_width_is_spacious);
    301         mFolderMinimumWidth = res.getDimensionPixelSize(R.dimen.folder_minimum_width);
    302 
    303         mMode = calculateMode(res, config);
    304 
    305         final int layoutId;
    306         if (mMode == WIDE_MODE) {
    307             layoutId = R.layout.conversation_item_view_wide;
    308         } else {
    309             if (config.getWidth() >= mMinListWidthIsSpacious) {
    310                 layoutId = R.layout.conversation_item_view_normal_spacious;
    311             } else {
    312                 layoutId = R.layout.conversation_item_view_normal;
    313             }
    314         }
    315 
    316         ViewGroup view = (ViewGroup) cache.getView(layoutId);
    317         if (view == null) {
    318             view = (ViewGroup) LayoutInflater.from(context).inflate(layoutId, null);
    319             cache.put(layoutId, view);
    320         }
    321 
    322         // Show/hide optional views before measure/layout call
    323 
    324         final View attachmentPreviews = view.findViewById(R.id.attachment_previews);;
    325         if (config.getAttachmentPreviewMode() != ATTACHMENT_PREVIEW_NONE) {
    326             final LayoutParams params = attachmentPreviews.getLayoutParams();
    327             attachmentPreviews.setVisibility(View.VISIBLE);
    328             params.height = getAttachmentPreviewsHeight(context, config.getAttachmentPreviewMode());
    329             attachmentPreviews.setLayoutParams(params);
    330         } else {
    331             attachmentPreviews.setVisibility(View.GONE);
    332         }
    333         attachmentPreviewsDecodeHeight = getAttachmentPreviewsHeight(context,
    334                 ATTACHMENT_PREVIEW_UNREAD);
    335 
    336         final TextView folders = (TextView) view.findViewById(R.id.folders);
    337         folders.setVisibility(config.areFoldersVisible() ? View.VISIBLE : View.GONE);
    338 
    339         // Add margin between attachment previews and folders
    340         final View attachmentPreviewsBottomMargin = view
    341                 .findViewById(R.id.attachment_previews_bottom_margin);
    342         final boolean marginVisible = config.getAttachmentPreviewMode() != ATTACHMENT_PREVIEW_NONE
    343                 && config.areFoldersVisible();
    344         attachmentPreviewsBottomMargin.setVisibility(marginVisible ? View.VISIBLE : View.GONE);
    345 
    346         View contactImagesView = view.findViewById(R.id.contact_image);
    347 
    348         switch (config.getGadgetMode()) {
    349             case GADGET_CONTACT_PHOTO:
    350                 contactImagesView.setVisibility(View.VISIBLE);
    351                 break;
    352             case GADGET_CHECKBOX:
    353                 contactImagesView.setVisibility(View.GONE);
    354                 contactImagesView = null;
    355                 break;
    356             default:
    357                 contactImagesView.setVisibility(View.GONE);
    358                 contactImagesView = null;
    359                 break;
    360         }
    361 
    362         final View replyState = view.findViewById(R.id.reply_state);
    363         replyState.setVisibility(config.isReplyStateVisible() ? View.VISIBLE : View.GONE);
    364 
    365         final View personalIndicator = view.findViewById(R.id.personal_indicator);
    366         personalIndicator.setVisibility(
    367                 config.isPersonalIndicatorVisible() ? View.VISIBLE : View.GONE);
    368 
    369         // Layout the appropriate view.
    370         final int widthSpec = MeasureSpec.makeMeasureSpec(config.getWidth(), MeasureSpec.EXACTLY);
    371         final int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
    372 
    373         view.measure(widthSpec, heightSpec);
    374         view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
    375 
    376 //        Utils.dumpViewTree((ViewGroup) view);
    377 
    378         // Records coordinates.
    379 
    380         // Contact images view
    381         if (contactImagesView != null) {
    382             contactImagesWidth = contactImagesView.getWidth();
    383             contactImagesHeight = contactImagesView.getHeight();
    384             contactImagesX = getX(contactImagesView);
    385             contactImagesY = getY(contactImagesView);
    386         } else {
    387             contactImagesX = contactImagesY = contactImagesWidth = contactImagesHeight = 0;
    388         }
    389 
    390         final View star = view.findViewById(R.id.star);
    391         starX = getX(star);
    392         starY = getY(star);
    393         starWidth = star.getWidth();
    394 
    395         final TextView senders = (TextView) view.findViewById(R.id.senders);
    396         final int sendersTopAdjust = getLatinTopAdjustment(senders);
    397         sendersX = getX(senders);
    398         sendersY = getY(senders) + sendersTopAdjust;
    399         sendersWidth = senders.getWidth();
    400         sendersHeight = senders.getHeight();
    401         sendersLineCount = getLineCount(senders);
    402         sendersLineHeight = senders.getLineHeight();
    403         sendersFontSize = senders.getTextSize();
    404 
    405         final TextView subject = (TextView) view.findViewById(R.id.subject);
    406         final int subjectTopAdjust = getLatinTopAdjustment(subject);
    407         subjectX = getX(subject);
    408         if (isWide()) {
    409             subjectY = getY(subject) + subjectTopAdjust;
    410         } else {
    411             subjectY = getY(subject) + sendersTopAdjust;
    412         }
    413         subjectWidth = subject.getWidth();
    414         subjectHeight = subject.getHeight();
    415         subjectLineCount = getLineCount(subject);
    416         subjectFontSize = subject.getTextSize();
    417 
    418         if (config.areFoldersVisible()) {
    419             // vertically align folders min left edge with subject
    420             foldersX = subjectX;
    421             foldersXEnd = getX(folders) + folders.getWidth();
    422             if (isWide()) {
    423                 foldersY = getY(folders);
    424             } else {
    425                 foldersY = getY(folders) + sendersTopAdjust;
    426             }
    427             foldersHeight = folders.getHeight();
    428             foldersTypeface = folders.getTypeface();
    429             foldersTextBottomPadding = res
    430                     .getDimensionPixelSize(R.dimen.folders_text_bottom_padding);
    431             foldersFontSize = folders.getTextSize();
    432         } else {
    433             foldersX = 0;
    434             foldersXEnd = 0;
    435             foldersY = 0;
    436             foldersHeight = 0;
    437             foldersTypeface = null;
    438             foldersTextBottomPadding = 0;
    439             foldersFontSize = 0;
    440         }
    441 
    442         final View colorBlock = view.findViewById(R.id.color_block);
    443         if (config.isColorBlockVisible() && colorBlock != null) {
    444             colorBlockX = getX(colorBlock);
    445             colorBlockY = getY(colorBlock);
    446             colorBlockWidth = colorBlock.getWidth();
    447             colorBlockHeight = colorBlock.getHeight();
    448         } else {
    449             colorBlockX = colorBlockY = colorBlockWidth = colorBlockHeight = 0;
    450         }
    451 
    452         if (config.isReplyStateVisible()) {
    453             replyStateX = getX(replyState);
    454             replyStateY = getY(replyState);
    455         } else {
    456             replyStateX = replyStateY = 0;
    457         }
    458 
    459         if (config.isPersonalIndicatorVisible()) {
    460             personalIndicatorX = getX(personalIndicator);
    461             personalIndicatorY = getY(personalIndicator);
    462         } else {
    463             personalIndicatorX = personalIndicatorY = 0;
    464         }
    465 
    466         final View infoIcon = view.findViewById(R.id.info_icon);
    467         infoIconXEnd = getX(infoIcon) + infoIcon.getWidth();
    468         infoIconY = getY(infoIcon);
    469 
    470         final TextView date = (TextView) view.findViewById(R.id.date);
    471         dateXEnd = getX(date) + date.getWidth();
    472         dateY = getY(date);
    473         datePaddingLeft = date.getPaddingLeft();
    474         dateFontSize = date.getTextSize();
    475         dateYBaseline = dateY + getLatinTopAdjustment(date) + date.getBaseline();
    476 
    477         final View paperclip = view.findViewById(R.id.paperclip);
    478         paperclipY = getY(paperclip);
    479         paperclipPaddingLeft = paperclip.getPaddingLeft();
    480 
    481         if (attachmentPreviews != null) {
    482             attachmentPreviewsX = subjectX;
    483             attachmentPreviewsY = getY(attachmentPreviews) + sendersTopAdjust;
    484             attachmentPreviewsWidth = subjectWidth;
    485             attachmentPreviewsHeight = attachmentPreviews.getHeight();
    486 
    487             // We only care about the right and bottom of the overflow count
    488             final TextView overflow = (TextView) view.findViewById(id.ap_overflow);
    489             final FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) overflow
    490                     .getLayoutParams();
    491             overflowXEnd = attachmentPreviewsX + attachmentPreviewsWidth - params.rightMargin;
    492             overflowYEnd = attachmentPreviewsY + attachmentPreviewsHeight - params.bottomMargin;
    493             overflowDiameter = overflow.getWidth();
    494             overflowFontSize = overflow.getTextSize();
    495             overflowTypeface = overflow.getTypeface();
    496 
    497             final View placeholder = view.findViewById(id.ap_placeholder);
    498             placeholderWidth = placeholder.getWidth();
    499             placeholderHeight = placeholder.getHeight();
    500             placeholderY = attachmentPreviewsY + attachmentPreviewsHeight / 2
    501                     - placeholderHeight / 2;
    502 
    503             final View progressBar = view.findViewById(id.ap_progress_bar);
    504             progressBarWidth = progressBar.getWidth();
    505             progressBarHeight = progressBar.getHeight();
    506             progressBarY = attachmentPreviewsY + attachmentPreviewsHeight / 2
    507                     - progressBarHeight / 2;
    508         } else {
    509             attachmentPreviewsX = 0;
    510             attachmentPreviewsY = 0;
    511             attachmentPreviewsWidth = 0;
    512             attachmentPreviewsHeight = 0;
    513             overflowXEnd = 0;
    514             overflowYEnd = 0;
    515             overflowDiameter = 0;
    516             overflowFontSize = 0;
    517             overflowTypeface = null;
    518             placeholderY = 0;
    519             placeholderWidth = 0;
    520             placeholderHeight = 0;
    521             progressBarY = 0;
    522             progressBarWidth = 0;
    523             progressBarHeight = 0;
    524         }
    525 
    526         height = view.getHeight() + (isWide() ? 0 : sendersTopAdjust);
    527         Utils.traceEndSection();
    528     }
    529 
    530     public int getMode() {
    531         return mMode;
    532     }
    533 
    534     public boolean isWide() {
    535         return mMode == WIDE_MODE;
    536     }
    537 
    538     /**
    539      * Returns a negative corrective value that you can apply to a TextView's vertical dimensions
    540      * that will nudge the first line of text upwards such that uppercase Latin characters are
    541      * truly top-aligned.
    542      * <p>
    543      * N.B. this will cause other characters to draw above the top! only use this if you have
    544      * adequate top margin.
    545      *
    546      */
    547     private static int getLatinTopAdjustment(TextView t) {
    548         final FontMetricsInt fmi = t.getPaint().getFontMetricsInt();
    549         return (fmi.top - fmi.ascent);
    550     }
    551 
    552     /**
    553      * Returns the mode of the header view (Wide/Normal).
    554      */
    555     private int calculateMode(Resources res, Config config) {
    556         switch (config.getViewMode()) {
    557             case ViewMode.CONVERSATION_LIST:
    558                 return config.getWidth() >= mMinListWidthForWide ? WIDE_MODE : NORMAL_MODE;
    559 
    560             case ViewMode.SEARCH_RESULTS_LIST:
    561                 return res.getInteger(R.integer.conversation_list_search_header_mode);
    562 
    563             default:
    564                 return res.getInteger(R.integer.conversation_header_mode);
    565         }
    566     }
    567 
    568     private int getAttachmentPreviewsHeight(final Context context,
    569             final int attachmentPreviewMode) {
    570         final Resources res = context.getResources();
    571         switch (attachmentPreviewMode) {
    572             case ATTACHMENT_PREVIEW_UNREAD:
    573                 return (int) (isWide() ? res.getDimension(dimen.attachment_preview_height_tall_wide)
    574                         : res.getDimension(dimen.attachment_preview_height_tall));
    575             case ATTACHMENT_PREVIEW_READ:
    576                 return (int) res.getDimension(dimen.attachment_preview_height_short);
    577             default:
    578                 return 0;
    579         }
    580     }
    581 
    582     /**
    583      * Returns the x coordinates of a view by tracing up its hierarchy.
    584      */
    585     private static int getX(View view) {
    586         int x = 0;
    587         while (view != null) {
    588             x += (int) view.getX();
    589             view = (View) view.getParent();
    590         }
    591         return x;
    592     }
    593 
    594     /**
    595      * Returns the y coordinates of a view by tracing up its hierarchy.
    596      */
    597     private static int getY(View view) {
    598         int y = 0;
    599         while (view != null) {
    600             y += (int) view.getY();
    601             view = (View) view.getParent();
    602         }
    603         return y;
    604     }
    605 
    606     /**
    607      * Returns the number of lines of this text view. Delegates to built-in TextView logic on JB+.
    608      */
    609     private static int getLineCount(TextView textView) {
    610         if (Utils.isRunningJellybeanOrLater()) {
    611             return textView.getMaxLines();
    612         } else {
    613             return Math.round(((float) textView.getHeight()) / textView.getLineHeight());
    614         }
    615     }
    616 
    617     /**
    618      * Returns the length (maximum of characters) of subject in this mode.
    619      */
    620     public static int getSendersLength(Context context, int mode, boolean hasAttachments) {
    621         final Resources res = context.getResources();
    622         if (hasAttachments) {
    623             return res.getIntArray(R.array.senders_with_attachment_lengths)[mode];
    624         } else {
    625             return res.getIntArray(R.array.senders_lengths)[mode];
    626         }
    627     }
    628 
    629     @Deprecated
    630     public static int getColorBlockWidth(Context context) {
    631         Resources res = context.getResources();
    632         if (COLOR_BLOCK_WIDTH <= 0) {
    633             COLOR_BLOCK_WIDTH = res.getDimensionPixelSize(R.dimen.color_block_width);
    634         }
    635         return COLOR_BLOCK_WIDTH;
    636     }
    637 
    638     @Deprecated
    639     public static int getColorBlockHeight(Context context) {
    640         Resources res = context.getResources();
    641         if (COLOR_BLOCK_HEIGHT <= 0) {
    642             COLOR_BLOCK_HEIGHT = res.getDimensionPixelSize(R.dimen.color_block_height);
    643         }
    644         return COLOR_BLOCK_HEIGHT;
    645     }
    646 
    647     public static boolean displaySendersInline(int mode) {
    648         switch (mode) {
    649             case WIDE_MODE:
    650                 return false;
    651             case NORMAL_MODE:
    652                 return true;
    653             default:
    654                 throw new IllegalArgumentException("Unknown conversation header view mode " + mode);
    655         }
    656     }
    657 
    658     /**
    659      * Returns coordinates for elements inside a conversation header view given
    660      * the view width.
    661      */
    662     public static ConversationItemViewCoordinates forConfig(final Context context,
    663             final Config config, final CoordinatesCache cache) {
    664         final int cacheKey = config.getCacheKey();
    665         ConversationItemViewCoordinates coordinates = cache.getCoordinates(cacheKey);
    666         if (coordinates != null) {
    667             return coordinates;
    668         }
    669 
    670         coordinates = new ConversationItemViewCoordinates(context, config, cache);
    671         cache.put(cacheKey, coordinates);
    672         return coordinates;
    673     }
    674 
    675     /**
    676      * Return the minimum width of a folder cell with no text. Essentially this is the left+right
    677      * intra-cell margin within cells.
    678      *
    679      */
    680     public int getFolderCellWidth() {
    681         return mFolderCellWidth;
    682     }
    683 
    684     /**
    685      * Return the minimum width of a folder cell, period. This will affect the
    686      * maximum number of folders we can display.
    687      */
    688     public int getFolderMinimumWidth() {
    689         return mFolderMinimumWidth;
    690     }
    691 
    692     public static boolean isWideMode(int mode) {
    693         return mode == WIDE_MODE;
    694     }
    695 
    696 }
    697