311 lines
10 KiB
Java
311 lines
10 KiB
Java
/*
|
|
* Copyright (C) 2011 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
|
|
package com.android.email.activity;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.Resources;
|
|
import android.graphics.Typeface;
|
|
import android.text.TextPaint;
|
|
import android.util.SparseArray;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.View.MeasureSpec;
|
|
import android.view.ViewGroup;
|
|
import android.view.ViewParent;
|
|
import android.widget.TextView;
|
|
|
|
import com.android.email.R;
|
|
|
|
/**
|
|
* Represents the coordinates of elements inside a CanvasConversationHeaderView
|
|
* (eg, checkmark, star, subject, sender, labels, etc.) It will inflate a view,
|
|
* and record the coordinates of each element after layout. This will allows us
|
|
* to easily improve performance by creating custom view while still defining
|
|
* layout in XML files.
|
|
*/
|
|
public class MessageListItemCoordinates {
|
|
// Modes.
|
|
public static final int WIDE_MODE = 0;
|
|
public static final int NORMAL_MODE = 1;
|
|
|
|
// Static threshold.
|
|
private static int MINIMUM_WIDTH_WIDE_MODE = -1;
|
|
private static int[] SUBJECT_LENGTHS;
|
|
|
|
// Checkmark.
|
|
int checkmarkX;
|
|
int checkmarkY;
|
|
int checkmarkWidthIncludingMargins;
|
|
|
|
// Reply and forward state.
|
|
int stateX;
|
|
int stateY;
|
|
|
|
// Star.
|
|
int starX;
|
|
int starY;
|
|
|
|
// Senders.
|
|
int sendersX;
|
|
int sendersY;
|
|
int sendersWidth;
|
|
int sendersLineCount;
|
|
int sendersFontSize;
|
|
int sendersAscent;
|
|
|
|
// Subject.
|
|
int subjectX;
|
|
int subjectY;
|
|
int subjectWidth;
|
|
int subjectLineCount;
|
|
int subjectFontSize;
|
|
int subjectAscent;
|
|
|
|
// Color chip.
|
|
int chipX;
|
|
int chipY;
|
|
int chipWidth;
|
|
int chipHeight;
|
|
|
|
// Date.
|
|
int dateXEnd;
|
|
int dateY;
|
|
int dateFontSize;
|
|
int dateAscent;
|
|
|
|
// Paperclip.
|
|
int paperclipY;
|
|
|
|
// Cache to save Coordinates based on view width.
|
|
private static SparseArray<MessageListItemCoordinates> mCache =
|
|
new SparseArray<MessageListItemCoordinates>();
|
|
|
|
private static TextPaint sPaint = new TextPaint();
|
|
|
|
static {
|
|
sPaint.setTypeface(Typeface.DEFAULT);
|
|
sPaint.setAntiAlias(true);
|
|
}
|
|
|
|
// Not directly instantiable.
|
|
private MessageListItemCoordinates() {}
|
|
|
|
/**
|
|
* Returns the mode of the header view (Wide/Normal/Narrow) given the its
|
|
* measured width.
|
|
*/
|
|
public static int getMode(Context context, int width, boolean isSearch) {
|
|
Resources res = context.getResources();
|
|
if (isSearch) {
|
|
return res.getInteger(R.integer.message_search_list_header_mode);
|
|
}
|
|
if (MINIMUM_WIDTH_WIDE_MODE <= 0) {
|
|
MINIMUM_WIDTH_WIDE_MODE = res.getDimensionPixelSize(R.dimen.minimum_width_wide_mode);
|
|
}
|
|
|
|
// Choose the correct mode based on view width.
|
|
int mode = NORMAL_MODE;
|
|
if (width > MINIMUM_WIDTH_WIDE_MODE) {
|
|
mode = WIDE_MODE;
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
public static boolean isMultiPane(Context context) {
|
|
return UiUtilities.useTwoPane(context);
|
|
}
|
|
|
|
/**
|
|
* Returns the layout id to be inflated in this mode.
|
|
*/
|
|
private static int getLayoutId(int mode) {
|
|
switch (mode) {
|
|
case WIDE_MODE:
|
|
return R.layout.message_list_item_wide;
|
|
case NORMAL_MODE:
|
|
return R.layout.message_list_item_normal;
|
|
default:
|
|
throw new IllegalArgumentException("Unknown conversation header view mode " + mode);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a value array multiplied by the specified density.
|
|
*/
|
|
public static int[] getDensityDependentArray(int[] values, float density) {
|
|
int result[] = new int[values.length];
|
|
for (int i = 0; i < values.length; ++i) {
|
|
result[i] = (int) (values[i] * density);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns the height of the view in this mode.
|
|
*/
|
|
public static int getHeight(Context context, int mode) {
|
|
return context.getResources().getDimensionPixelSize(
|
|
(mode == WIDE_MODE)
|
|
? R.dimen.message_list_item_height_wide
|
|
: R.dimen.message_list_item_height_normal);
|
|
}
|
|
|
|
/**
|
|
* Returns the x coordinates of a view by tracing up its hierarchy.
|
|
*/
|
|
private static int getX(View view) {
|
|
int x = 0;
|
|
while (view != null) {
|
|
x += (int) view.getX();
|
|
ViewParent parent = view.getParent();
|
|
view = parent != null ? (View) parent : null;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/**
|
|
* Returns the y coordinates of a view by tracing up its hierarchy.
|
|
*/
|
|
private static int getY(View view) {
|
|
int y = 0;
|
|
while (view != null) {
|
|
y += (int) view.getY();
|
|
ViewParent parent = view.getParent();
|
|
view = parent != null ? (View) parent : null;
|
|
}
|
|
return y;
|
|
}
|
|
|
|
/**
|
|
* Returns the width of a view.
|
|
*
|
|
* @param includeMargins whether or not to include margins when calculating
|
|
* width.
|
|
*/
|
|
public static int getWidth(View view, boolean includeMargins) {
|
|
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
|
|
return view.getWidth() + (includeMargins ? params.leftMargin + params.rightMargin : 0);
|
|
}
|
|
|
|
/**
|
|
* Returns the height of a view.
|
|
*
|
|
* @param includeMargins whether or not to include margins when calculating
|
|
* height.
|
|
*/
|
|
public static int getHeight(View view, boolean includeMargins) {
|
|
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
|
|
return view.getHeight() + (includeMargins ? params.topMargin + params.bottomMargin : 0);
|
|
}
|
|
|
|
/**
|
|
* Returns the number of lines of this text view.
|
|
*/
|
|
private static int getLineCount(TextView textView) {
|
|
return textView.getHeight() / textView.getLineHeight();
|
|
}
|
|
|
|
/**
|
|
* Returns the length (maximum of characters) of subject in this mode.
|
|
*/
|
|
public static int getSubjectLength(Context context, int mode) {
|
|
Resources res = context.getResources();
|
|
if (SUBJECT_LENGTHS == null) {
|
|
SUBJECT_LENGTHS = res.getIntArray(R.array.subject_lengths);
|
|
}
|
|
return SUBJECT_LENGTHS[mode];
|
|
}
|
|
|
|
/**
|
|
* Reset the caches associated with the coordinate layouts.
|
|
*/
|
|
static void resetCaches() {
|
|
mCache.clear();
|
|
}
|
|
|
|
/**
|
|
* Returns coordinates for elements inside a conversation header view given
|
|
* the view width.
|
|
*/
|
|
public static MessageListItemCoordinates forWidth(Context context, int width,
|
|
boolean isSearchResult) {
|
|
MessageListItemCoordinates coordinates = mCache.get(width);
|
|
if (coordinates == null) {
|
|
coordinates = new MessageListItemCoordinates();
|
|
mCache.put(width, coordinates);
|
|
// TODO: make the field computation done inside of the constructor and mark fields final
|
|
|
|
// Layout the appropriate view.
|
|
int mode = getMode(context, width, isSearchResult);
|
|
int height = getHeight(context, mode);
|
|
View view = LayoutInflater.from(context).inflate(getLayoutId(mode), null);
|
|
int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
|
|
int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
|
|
view.measure(widthSpec, heightSpec);
|
|
view.layout(0, 0, width, height);
|
|
|
|
// Records coordinates.
|
|
View checkmark = view.findViewById(R.id.checkmark);
|
|
coordinates.checkmarkX = getX(checkmark);
|
|
coordinates.checkmarkY = getY(checkmark);
|
|
coordinates.checkmarkWidthIncludingMargins = getWidth(checkmark, true);
|
|
|
|
View star = view.findViewById(R.id.star);
|
|
coordinates.starX = getX(star);
|
|
coordinates.starY = getY(star);
|
|
|
|
View state = view.findViewById(R.id.reply_state);
|
|
coordinates.stateX = getX(state);
|
|
coordinates.stateY = getY(state);
|
|
|
|
TextView senders = (TextView) view.findViewById(R.id.senders);
|
|
coordinates.sendersX = getX(senders);
|
|
coordinates.sendersY = getY(senders);
|
|
coordinates.sendersWidth = getWidth(senders, false);
|
|
coordinates.sendersLineCount = getLineCount(senders);
|
|
coordinates.sendersFontSize = (int) senders.getTextSize();
|
|
coordinates.sendersAscent = Math.round(senders.getPaint().ascent());
|
|
|
|
TextView subject = (TextView) view.findViewById(R.id.subject);
|
|
coordinates.subjectX = getX(subject);
|
|
coordinates.subjectY = getY(subject);
|
|
coordinates.subjectWidth = getWidth(subject, false);
|
|
coordinates.subjectLineCount = getLineCount(subject);
|
|
coordinates.subjectFontSize = (int) subject.getTextSize();
|
|
coordinates.subjectAscent = Math.round(subject.getPaint().ascent());
|
|
|
|
View chip = view.findViewById(R.id.color_chip);
|
|
coordinates.chipX = getX(chip);
|
|
coordinates.chipY = getY(chip);
|
|
coordinates.chipWidth = getWidth(chip, false);
|
|
coordinates.chipHeight = getHeight(chip, false);
|
|
|
|
TextView date = (TextView) view.findViewById(R.id.date);
|
|
coordinates.dateXEnd = getX(date) + date.getWidth();
|
|
coordinates.dateY = getY(date);
|
|
coordinates.dateFontSize = (int) date.getTextSize();
|
|
coordinates.dateAscent = Math.round(date.getPaint().ascent());
|
|
|
|
// The x-value is computed relative to the date.
|
|
View paperclip = view.findViewById(R.id.paperclip);
|
|
coordinates.paperclipY = getY(paperclip);
|
|
}
|
|
return coordinates;
|
|
}
|
|
}
|