Home | History | Annotate | Download | only in activity
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 
     18 package com.android.email.activity;
     19 
     20 import android.content.Context;
     21 import android.content.res.Resources;
     22 import android.graphics.Typeface;
     23 import android.text.TextPaint;
     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.ViewParent;
     30 import android.widget.TextView;
     31 
     32 import com.android.email.R;
     33 
     34 /**
     35  * Represents the coordinates of elements inside a CanvasConversationHeaderView
     36  * (eg, checkmark, star, subject, sender, labels, etc.) It will inflate a view,
     37  * and record the coordinates of each element after layout. This will allows us
     38  * to easily improve performance by creating custom view while still defining
     39  * layout in XML files.
     40  */
     41 public class MessageListItemCoordinates {
     42     // Modes.
     43     public static final int WIDE_MODE = 0;
     44     public static final int NORMAL_MODE = 1;
     45 
     46     // Static threshold.
     47     private static int MINIMUM_WIDTH_WIDE_MODE = -1;
     48     private static int[] SUBJECT_LENGTHS;
     49 
     50     // Checkmark.
     51     int checkmarkX;
     52     int checkmarkY;
     53     int checkmarkWidthIncludingMargins;
     54 
     55     // Reply and forward state.
     56     int stateX;
     57     int stateY;
     58 
     59     // Star.
     60     int starX;
     61     int starY;
     62 
     63     // Senders.
     64     int sendersX;
     65     int sendersY;
     66     int sendersWidth;
     67     int sendersLineCount;
     68     int sendersFontSize;
     69     int sendersAscent;
     70 
     71     // Subject.
     72     int subjectX;
     73     int subjectY;
     74     int subjectWidth;
     75     int subjectLineCount;
     76     int subjectFontSize;
     77     int subjectAscent;
     78 
     79     // Color chip.
     80     int chipX;
     81     int chipY;
     82     int chipWidth;
     83     int chipHeight;
     84 
     85     // Date.
     86     int dateXEnd;
     87     int dateY;
     88     int dateFontSize;
     89     int dateAscent;
     90 
     91     // Paperclip.
     92     int paperclipY;
     93 
     94     // Cache to save Coordinates based on view width.
     95     private static SparseArray<MessageListItemCoordinates> mCache =
     96             new SparseArray<MessageListItemCoordinates>();
     97 
     98     private static TextPaint sPaint = new TextPaint();
     99 
    100     static {
    101         sPaint.setTypeface(Typeface.DEFAULT);
    102         sPaint.setAntiAlias(true);
    103     }
    104 
    105     // Not directly instantiable.
    106     private MessageListItemCoordinates() {}
    107 
    108     /**
    109      * Returns the mode of the header view (Wide/Normal/Narrow) given the its
    110      * measured width.
    111      */
    112     public static int getMode(Context context, int width) {
    113         Resources res = context.getResources();
    114         if (MINIMUM_WIDTH_WIDE_MODE <= 0) {
    115             MINIMUM_WIDTH_WIDE_MODE = res.getDimensionPixelSize(R.dimen.minimum_width_wide_mode);
    116         }
    117 
    118         // Choose the correct mode based on view width.
    119         int mode = NORMAL_MODE;
    120         if (width > MINIMUM_WIDTH_WIDE_MODE) {
    121             mode = WIDE_MODE;
    122         }
    123         return mode;
    124     }
    125 
    126     public static boolean isMultiPane(Context context) {
    127         return UiUtilities.useTwoPane(context);
    128     }
    129 
    130     /**
    131      * Returns the layout id to be inflated in this mode.
    132      */
    133     private static int getLayoutId(int mode) {
    134         switch (mode) {
    135             case WIDE_MODE:
    136                 return R.layout.message_list_item_wide;
    137             case NORMAL_MODE:
    138                 return R.layout.message_list_item_normal;
    139             default:
    140                 throw new IllegalArgumentException("Unknown conversation header view mode " + mode);
    141         }
    142     }
    143 
    144     /**
    145      * Returns a value array multiplied by the specified density.
    146      */
    147     public static int[] getDensityDependentArray(int[] values, float density) {
    148         int result[] = new int[values.length];
    149         for (int i = 0; i < values.length; ++i) {
    150             result[i] = (int) (values[i] * density);
    151         }
    152         return result;
    153     }
    154 
    155     /**
    156      * Returns the height of the view in this mode.
    157      */
    158     public static int getHeight(Context context, int mode) {
    159         return context.getResources().getDimensionPixelSize(
    160                 (mode == WIDE_MODE)
    161                         ? R.dimen.message_list_item_height_wide
    162                         : R.dimen.message_list_item_height_normal);
    163     }
    164 
    165     /**
    166      * Returns the x coordinates of a view by tracing up its hierarchy.
    167      */
    168     private static int getX(View view) {
    169         int x = 0;
    170         while (view != null) {
    171             x += (int) view.getX();
    172             ViewParent parent = view.getParent();
    173             view = parent != null ? (View) parent : null;
    174         }
    175         return x;
    176     }
    177 
    178     /**
    179      * Returns the y coordinates of a view by tracing up its hierarchy.
    180      */
    181     private static int getY(View view) {
    182         int y = 0;
    183         while (view != null) {
    184             y += (int) view.getY();
    185             ViewParent parent = view.getParent();
    186             view = parent != null ? (View) parent : null;
    187         }
    188         return y;
    189     }
    190 
    191     /**
    192      * Returns the width of a view.
    193      *
    194      * @param includeMargins whether or not to include margins when calculating
    195      *            width.
    196      */
    197     public static int getWidth(View view, boolean includeMargins) {
    198         ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
    199         return view.getWidth() + (includeMargins ? params.leftMargin + params.rightMargin : 0);
    200     }
    201 
    202     /**
    203      * Returns the height of a view.
    204      *
    205      * @param includeMargins whether or not to include margins when calculating
    206      *            height.
    207      */
    208     public static int getHeight(View view, boolean includeMargins) {
    209         ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
    210         return view.getHeight() + (includeMargins ? params.topMargin + params.bottomMargin : 0);
    211     }
    212 
    213     /**
    214      * Returns the number of lines of this text view.
    215      */
    216     private static int getLineCount(TextView textView) {
    217         return textView.getHeight() / textView.getLineHeight();
    218     }
    219 
    220     /**
    221      * Returns the length (maximum of characters) of subject in this mode.
    222      */
    223     public static int getSubjectLength(Context context, int mode) {
    224         Resources res = context.getResources();
    225         if (SUBJECT_LENGTHS == null) {
    226             SUBJECT_LENGTHS = res.getIntArray(R.array.subject_lengths);
    227         }
    228         return SUBJECT_LENGTHS[mode];
    229     }
    230 
    231     /**
    232      * Reset the caches associated with the coordinate layouts.
    233      */
    234     static void resetCaches() {
    235         mCache.clear();
    236     }
    237 
    238     /**
    239      * Returns coordinates for elements inside a conversation header view given
    240      * the view width.
    241      */
    242     public static MessageListItemCoordinates forWidth(Context context, int width) {
    243         MessageListItemCoordinates coordinates = mCache.get(width);
    244         if (coordinates == null) {
    245             coordinates = new MessageListItemCoordinates();
    246             mCache.put(width, coordinates);
    247             // TODO: make the field computation done inside of the constructor and mark fields final
    248 
    249             // Layout the appropriate view.
    250             int mode = getMode(context, width);
    251             int height = getHeight(context, mode);
    252             View view = LayoutInflater.from(context).inflate(getLayoutId(mode), null);
    253             int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
    254             int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    255             view.measure(widthSpec, heightSpec);
    256             view.layout(0, 0, width, height);
    257 
    258             // Records coordinates.
    259             View checkmark = view.findViewById(R.id.checkmark);
    260             coordinates.checkmarkX = getX(checkmark);
    261             coordinates.checkmarkY = getY(checkmark);
    262             coordinates.checkmarkWidthIncludingMargins = getWidth(checkmark, true);
    263 
    264             View star = view.findViewById(R.id.star);
    265             coordinates.starX = getX(star);
    266             coordinates.starY = getY(star);
    267 
    268             View state = view.findViewById(R.id.reply_state);
    269             coordinates.stateX = getX(state);
    270             coordinates.stateY = getY(state);
    271 
    272             TextView senders = (TextView) view.findViewById(R.id.senders);
    273             coordinates.sendersX = getX(senders);
    274             coordinates.sendersY = getY(senders);
    275             coordinates.sendersWidth = getWidth(senders, false);
    276             coordinates.sendersLineCount = getLineCount(senders);
    277             coordinates.sendersFontSize = (int) senders.getTextSize();
    278             coordinates.sendersAscent = Math.round(senders.getPaint().ascent());
    279 
    280             TextView subject = (TextView) view.findViewById(R.id.subject);
    281             coordinates.subjectX = getX(subject);
    282             coordinates.subjectY = getY(subject);
    283             coordinates.subjectWidth = getWidth(subject, false);
    284             coordinates.subjectLineCount = getLineCount(subject);
    285             coordinates.subjectFontSize = (int) subject.getTextSize();
    286             coordinates.subjectAscent = Math.round(subject.getPaint().ascent());
    287 
    288             View chip = view.findViewById(R.id.color_chip);
    289             coordinates.chipX = getX(chip);
    290             coordinates.chipY = getY(chip);
    291             coordinates.chipWidth = getWidth(chip, false);
    292             coordinates.chipHeight = getHeight(chip, false);
    293 
    294             TextView date = (TextView) view.findViewById(R.id.date);
    295             coordinates.dateXEnd = getX(date) + date.getWidth();
    296             coordinates.dateY = getY(date);
    297             coordinates.dateFontSize = (int) date.getTextSize();
    298             coordinates.dateAscent = Math.round(date.getPaint().ascent());
    299 
    300             // The x-value is computed relative to the date.
    301             View paperclip = view.findViewById(R.id.paperclip);
    302             coordinates.paperclipY = getY(paperclip);
    303         }
    304         return coordinates;
    305     }
    306 }
    307