diff options
author | Andy Huang <ath@google.com> | 2012-04-06 18:08:53 -0700 |
---|---|---|
committer | Andy Huang <ath@google.com> | 2012-04-06 18:09:00 -0700 |
commit | 65fe28fa88daad08f3be4c084ca5b4eaa366d1a7 (patch) | |
tree | c9650f077867e4706c353050160832131fee91e2 | |
parent | 68ab9316b8ec08a24f12dc16c626a78b77728d3e (diff) | |
download | android_packages_apps_UnifiedEmail-65fe28fa88daad08f3be4c084ca5b4eaa366d1a7.tar.gz android_packages_apps_UnifiedEmail-65fe28fa88daad08f3be4c084ca5b4eaa366d1a7.tar.bz2 android_packages_apps_UnifiedEmail-65fe28fa88daad08f3be4c084ca5b4eaa366d1a7.zip |
optimize low-hanging fruit found using traceview
* View.getTag/setTag is a slow way of finding an overlay view;
switch to a SparseArray
* this also obsoletes mChildrenToRemove since we can immediately
remove overlays from the SparseArray
* bring back the Email Address cache to avoid repeated parsing
* avoid repeatedly calling findViewById for each header
* cache expensive timestamps and recipient summary strings
Change-Id: Ic76265848e4956a28a44c4a2e3e726ec9ef6bfaf
-rw-r--r-- | res/values/ids.xml | 19 | ||||
-rw-r--r-- | src/com/android/mail/browse/ConversationContainer.java | 71 | ||||
-rw-r--r-- | src/com/android/mail/browse/ConversationViewAdapter.java | 15 | ||||
-rw-r--r-- | src/com/android/mail/browse/MessageHeaderView.java | 132 | ||||
-rw-r--r-- | src/com/android/mail/ui/ConversationViewFragment.java | 8 | ||||
-rw-r--r-- | tests/src/com/android/mail/browse/MessageHeaderViewTest.java | 8 |
6 files changed, 133 insertions, 120 deletions
diff --git a/res/values/ids.xml b/res/values/ids.xml deleted file mode 100644 index b2d77dbb7..000000000 --- a/res/values/ids.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 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. ---> - -<resources> - <item type="id" name="view_tag_conversation_index" /> -</resources> diff --git a/src/com/android/mail/browse/ConversationContainer.java b/src/com/android/mail/browse/ConversationContainer.java index e563bdd87..531d31c41 100644 --- a/src/com/android/mail/browse/ConversationContainer.java +++ b/src/com/android/mail/browse/ConversationContainer.java @@ -19,6 +19,7 @@ package com.android.mail.browse; import android.content.Context; import android.util.AttributeSet; +import android.util.SparseArray; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -33,9 +34,6 @@ import com.android.mail.browse.ConversationViewAdapter.ConversationItem; import com.android.mail.browse.ScrollNotifier.ScrollListener; import com.android.mail.utils.DequeMap; import com.android.mail.utils.LogUtils; -import com.google.common.collect.Sets; - -import java.util.Set; /** * A specialized ViewGroup container for conversation view. It is designed to contain a single @@ -112,16 +110,16 @@ public class ConversationContainer extends ViewGroup implements ScrollListener { private final DequeMap<Integer, View> mScrapViews = new DequeMap<Integer, View>(); /** - * The set of children queued for later removal by a Runnable posted to the UI thread (no - * synchronization required). Scroll changes cause children to be added to this set, and the - * Runnable later removes the children when it safely detaches them outside of a - * draw/getDisplayList operation. + * The current set of overlay views in the view hierarchy. Looking through this map is faster + * than traversing the view hierarchy. * <p> * WebView sometimes notifies of scroll changes during a draw (or display list generation), when * it's not safe to detach view children because ViewGroup is in the middle of iterating over - * its child array. + * its child array. So we remove any child from this list immediately and queue up a task to + * detach it later. Since nobody other than the detach task references that view in the + * meantime, we don't need any further checks or synchronization. */ - private final Set<View> mChildrenToRemove; + private final SparseArray<View> mOverlayViews; private final float mDensity; @@ -129,8 +127,6 @@ public class ConversationContainer extends ViewGroup implements ScrollListener { private boolean mDisableLayoutTracing; - private static final int VIEW_TAG_CONVERSATION_INDEX = R.id.view_tag_conversation_index; - /** * Child views of this container should implement this interface to be notified when they are * being detached. @@ -151,7 +147,7 @@ public class ConversationContainer extends ViewGroup implements ScrollListener { public ConversationContainer(Context c, AttributeSet attrs) { super(c, attrs); - mChildrenToRemove = Sets.newHashSet(); + mOverlayViews = new SparseArray<View>(); mDensity = c.getResources().getDisplayMetrics().density; mScale = mDensity; @@ -182,14 +178,6 @@ public class ConversationContainer extends ViewGroup implements ScrollListener { return mOverlayAdapter; } - private int getOverlayCount() { - return Math.max(0, getChildCount() - 1); - } - - private View getOverlayAt(int i) { - return getChildAt(i + 1); - } - private void forwardFakeMotionEvent(MotionEvent original, int newAction) { MotionEvent newEvent = MotionEvent.obtain(original); newEvent.setAction(newAction); @@ -372,18 +360,14 @@ public class ConversationContainer extends ViewGroup implements ScrollListener { child.measure(childWidthSpec, childHeightSpec); } - private void onOverlayScrolledOff(final View overlayView, final int itemType, - int overlayTop, int overlayBottom) { - // do it asynchronously, as scroll notification can happen during a draw, when it's not - // safe to remove children + private void onOverlayScrolledOff(final int adapterIndex, final View overlayView, + final int itemType, int overlayTop, int overlayBottom) { + // detach the view asynchronously, as scroll notification can happen during a draw, when + // it's not safe to remove children - // ensure that repeated scroll events that want to remove the same header only do it - // once - if (mChildrenToRemove.contains(overlayView)) { - return; - } + // but immediately remove this view from the view set so future lookups don't find it + mOverlayViews.remove(adapterIndex); - mChildrenToRemove.add(overlayView); post(new Runnable() { @Override public void run() { @@ -407,7 +391,6 @@ public class ConversationContainer extends ViewGroup implements ScrollListener { private void detachOverlay(View overlayView, int itemType) { detachViewFromParent(overlayView); mScrapViews.add(itemType, overlayView); - mChildrenToRemove.remove(overlayView); if (overlayView instanceof DetachListener) { ((DetachListener) overlayView).onDetachedFromParent(); } @@ -461,7 +444,7 @@ public class ConversationContainer extends ViewGroup implements ScrollListener { } private void positionOverlay(int adapterIndex, int overlayTopY, int overlayBottomY) { - View overlayView = findExistingOverlayView(adapterIndex); + View overlayView = mOverlayViews.get(adapterIndex); final ConversationItem item = mOverlayAdapter.getItem(adapterIndex); // is the overlay visible and does it have non-zero height? @@ -484,22 +467,19 @@ public class ConversationContainer extends ViewGroup implements ScrollListener { } traceLayout("laying out overlay %d with h=%d", adapterIndex, overlayView.getMeasuredHeight()); - layoutOverlay(overlayView, overlayTopY); + layoutOverlay(overlayView, overlayTopY, overlayTopY + overlayView.getMeasuredHeight()); } else { // hide overlay if (overlayView != null) { traceLayout("hide overlay %d", adapterIndex); - onOverlayScrolledOff(overlayView, item.getType(), overlayTopY, overlayBottomY); + onOverlayScrolledOff(adapterIndex, overlayView, item.getType(), overlayTopY, + overlayBottomY); } else { traceLayout("ignore non-visible overlay %d", adapterIndex); } } } - private void layoutOverlay(View child, int childTop) { - layoutOverlay(child, childTop, childTop + child.getMeasuredHeight()); - } - // layout an existing view // need its top offset into the conversation, its height, and the scroll offset private void layoutOverlay(View child, int childTop, int childBottom) { @@ -513,7 +493,7 @@ public class ConversationContainer extends ViewGroup implements ScrollListener { final View convertView = mScrapViews.poll(itemType); View view = mOverlayAdapter.getView(adapterIndex, convertView, this); - view.setTag(VIEW_TAG_CONVERSATION_INDEX, adapterIndex); + mOverlayViews.put(adapterIndex, view); // Only re-attach if the view had previously been added to a view hierarchy. // Since external components can contribute to the scrap heap (addScrapView), we can't @@ -530,19 +510,6 @@ public class ConversationContainer extends ViewGroup implements ScrollListener { return view; } - private View findExistingOverlayView(int adapterIndex) { - for (int i = 0, count = getOverlayCount(); i < count; i++) { - final View overlay = getOverlayAt(i); - final Integer tag = (Integer) overlay.getTag(VIEW_TAG_CONVERSATION_INDEX); - // ignore children queued to be removed - // otherwise we'll re-use and lay out this view and then just throw it away - if (tag != null && tag == adapterIndex && !mChildrenToRemove.contains(overlay)) { - return overlay; - } - } - return null; - } - /** * Prevents any layouts from happening until the next time {@link #onGeometryChange(int[])} is * called. Useful when you know the HTML spacer coordinates are inconsistent with adapter items. diff --git a/src/com/android/mail/browse/ConversationViewAdapter.java b/src/com/android/mail/browse/ConversationViewAdapter.java index 2d09d22fe..686262294 100644 --- a/src/com/android/mail/browse/ConversationViewAdapter.java +++ b/src/com/android/mail/browse/ConversationViewAdapter.java @@ -31,6 +31,7 @@ import com.android.mail.R; import com.android.mail.browse.ConversationViewHeader.ConversationViewHeaderCallbacks; import com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks; import com.android.mail.providers.Account; +import com.android.mail.providers.Address; import com.android.mail.providers.Conversation; import com.android.mail.providers.Message; import com.android.mail.providers.UIProvider; @@ -38,6 +39,7 @@ import com.android.mail.utils.LogUtils; import com.google.common.collect.Lists; import java.util.List; +import java.util.Map; /** * A specialized adapter that contains overlay views to draw on top of the underlying conversation @@ -58,6 +60,7 @@ public class ConversationViewAdapter extends BaseAdapter { private final LoaderManager mLoaderManager; private final MessageHeaderViewCallbacks mMessageCallbacks; private ConversationViewHeaderCallbacks mConversationCallbacks; + private Map<String, Address> mAddressCache; private final LayoutInflater mInflater; private boolean mDefaultReplyAll; @@ -171,9 +174,16 @@ public class ConversationViewAdapter extends BaseAdapter { public class MessageHeaderItem extends ConversationItem { public final Message message; + + // view state variables private boolean mExpanded; public boolean detailsExpanded; + // cached values to speed up re-rendering during view recycling + public CharSequence timestampShort; + public CharSequence timestampLong; + public CharSequence recipientSummaryText; + private MessageHeaderItem(Message message, boolean expanded) { this.message = message; mExpanded = expanded; @@ -190,7 +200,7 @@ public class ConversationViewAdapter extends BaseAdapter { public View createView(Context context, LayoutInflater inflater, ViewGroup parent) { final MessageHeaderView v = (MessageHeaderView) inflater.inflate( R.layout.conversation_message_header, parent, false); - v.initialize(mDateBuilder, mAccount); + v.initialize(mDateBuilder, mAccount, mAddressCache); v.setCallbacks(mMessageCallbacks); return v; } @@ -265,13 +275,14 @@ public class ConversationViewAdapter extends BaseAdapter { public ConversationViewAdapter(Context context, Account account, LoaderManager loaderManager, MessageHeaderViewCallbacks messageCallbacks, - ConversationViewHeaderCallbacks convCallbacks) { + ConversationViewHeaderCallbacks convCallbacks, Map<String, Address> addressCache) { mContext = context; mDateBuilder = new FormattedDateBuilder(context); mAccount = account; mLoaderManager = loaderManager; mMessageCallbacks = messageCallbacks; mConversationCallbacks = convCallbacks; + mAddressCache = addressCache; mInflater = LayoutInflater.from(context); mItems = Lists.newArrayList(); diff --git a/src/com/android/mail/browse/MessageHeaderView.java b/src/com/android/mail/browse/MessageHeaderView.java index d9c325997..c39bdeea7 100644 --- a/src/com/android/mail/browse/MessageHeaderView.java +++ b/src/com/android/mail/browse/MessageHeaderView.java @@ -56,6 +56,7 @@ import com.google.common.annotations.VisibleForTesting; import java.io.IOException; import java.io.StringReader; +import java.util.Map; public class MessageHeaderView extends LinearLayout implements OnClickListener, OnMenuItemClickListener, HeaderBlock { @@ -94,6 +95,15 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, private ViewGroup mImagePromptView; private View mBottomBorderView; private ImageView mPresenceView; + private View mPhotoSpacerView; + private View mForwardButton; + private View mOverflowButton; + private View mDraftIcon; + private View mEditDraftButton; + private TextView mUpperDateView; + private View mReplyButton; + private View mReplyAllButton; + private View mAttachmentIcon; // temporary fields to reference raw data between initial render and details // expansion @@ -108,8 +118,6 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, private boolean mIsSending; - private boolean mDetailsExpanded; - /** * The snappy header has special visibility rules (i.e. no details header, * even though it has an expanded appearance) @@ -126,6 +134,8 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, private Account mAccount; + private Map<String, Address> mAddressCache; + private boolean mShowImagePrompt; private boolean mDefaultReplyAll; @@ -190,9 +200,18 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, mSenderNameView = (TextView) findViewById(R.id.sender_name); mSenderEmailView = (TextView) findViewById(R.id.sender_email); mPhotoView = (QuickContactBadge) findViewById(R.id.photo); + mPhotoSpacerView = findViewById(R.id.photo_spacer); + mReplyButton = findViewById(R.id.reply); + mReplyAllButton = findViewById(R.id.reply_all); + mForwardButton = findViewById(R.id.forward); mStarView = (ImageView) findViewById(R.id.star); mPresenceView = (ImageView) findViewById(R.id.presence); mTitleContainerView = (ViewGroup) findViewById(R.id.title_container); + mOverflowButton = findViewById(R.id.overflow); + mDraftIcon = findViewById(R.id.draft); + mEditDraftButton = findViewById(R.id.edit_draft); + mUpperDateView = (TextView) findViewById(R.id.upper_date); + mAttachmentIcon = findViewById(R.id.attachment); mCollapsedStarVis = mStarView.getVisibility(); mTitleContainerCollapsedMarginRight = ((MarginLayoutParams) mTitleContainerView @@ -302,9 +321,11 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, updateChildVisibility(); } - public void initialize(FormattedDateBuilder dateBuilder, Account account) { + public void initialize(FormattedDateBuilder dateBuilder, Account account, + Map<String, Address> addressCache) { mDateBuilder = dateBuilder; mAccount = account; + mAddressCache = addressCache; } public void bind(MessageHeaderItem headerItem, boolean defaultReplyAll) { @@ -321,8 +342,10 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, setExpanded(headerItem.isExpanded()); mTimestampMs = mMessage.dateReceivedMs; - if (mDateBuilder != null) { + mTimestampShort = headerItem.timestampShort; + if (mTimestampShort == null) { mTimestampShort = mDateBuilder.formatShortDate(mTimestampMs); + headerItem.timestampShort = mTimestampShort; } mTo = mMessage.getToAddresses(); @@ -357,14 +380,13 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, if (TextUtils.isEmpty(from)) { from = mAccount.name; } - mSender = Address.getEmailAddress(from); + mSender = getAddress(from); mSenderNameView.setText(getHeaderTitle()); mSenderEmailView.setText(getHeaderSubtitle()); - TextView upperDateView = (TextView) findViewById(R.id.upper_date); - if (upperDateView != null) { - upperDateView.setText(mTimestampShort); + if (mUpperDateView != null) { + mUpperDateView.setText(mTimestampShort); } mStarView.setSelected(mMessage.starred); @@ -376,6 +398,24 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, t.pause(HEADER_RENDER_TAG); } + private Address getAddress(String emailStr) { + return getAddress(mAddressCache, emailStr); + } + + private static Address getAddress(Map<String, Address> cache, String emailStr) { + Address addr = null; + if (cache != null) { + addr = cache.get(emailStr); + } + if (addr == null) { + addr = Address.getEmailAddress(emailStr); + if (cache != null) { + cache.put(emailStr, addr); + } + } + return addr; + } + private boolean isInOutbox() { // TODO: what should this read? Folder info? return false; @@ -430,8 +470,8 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, * Return the name, if known, or just the address. */ private static CharSequence getSenderName(Address sender) { - String displayName = sender == null ? "" : sender.getName(); - return TextUtils.isEmpty(displayName) && sender != null ? sender.getAddress() : displayName; + final String displayName = sender.getName(); + return TextUtils.isEmpty(displayName) ? sender.getAddress() : displayName; } /** @@ -442,9 +482,8 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, return TextUtils.isEmpty(displayName) ? null : sender.getAddress(); } - private void setChildVisibility(int visibility, int... resources) { - for (int res : resources) { - View v = findViewById(res); + private void setChildVisibility(int visibility, View... children) { + for (View v : children) { if (v != null) { v.setVisibility(visibility); } @@ -481,38 +520,39 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, } setReplyOrReplyAllVisible(); - setChildVisibility(normalVis, R.id.photo, R.id.photo_spacer, R.id.forward, - R.id.sender_email, R.id.overflow); - setChildVisibility(draftVis, R.id.draft, R.id.edit_draft); - setChildVisibility(GONE, R.id.attachment, R.id.upper_date); - setChildVisibility(VISIBLE, R.id.star); + setChildVisibility(normalVis, mPhotoView, mPhotoSpacerView, mForwardButton, + mSenderEmailView, mOverflowButton); + setChildVisibility(draftVis, mDraftIcon, mEditDraftButton); + setChildVisibility(GONE, mAttachmentIcon, mUpperDateView); + setChildVisibility(VISIBLE, mStarView); setChildMarginRight(mTitleContainerView, 0); } else { setMessageDetailsVisibility(GONE); - setChildVisibility(VISIBLE, R.id.sender_email, R.id.upper_date); + setChildVisibility(VISIBLE, mSenderEmailView, mUpperDateView); - setChildVisibility(GONE, R.id.edit_draft, R.id.reply, R.id.reply_all, R.id.forward); - setChildVisibility(GONE, R.id.overflow); + setChildVisibility(GONE, mEditDraftButton, mReplyButton, mReplyAllButton, + mForwardButton); + setChildVisibility(GONE, mOverflowButton); setChildVisibility(mMessage.hasAttachments ? VISIBLE : GONE, - R.id.attachment); + mAttachmentIcon); - setChildVisibility(mCollapsedStarVis, R.id.star); + setChildVisibility(mCollapsedStarVis, mStarView); setChildMarginRight(mTitleContainerView, mTitleContainerCollapsedMarginRight); if (mIsDraft) { - setChildVisibility(VISIBLE, R.id.draft); - setChildVisibility(GONE, R.id.photo, R.id.photo_spacer); + setChildVisibility(VISIBLE, mDraftIcon); + setChildVisibility(GONE, mPhotoView, mPhotoSpacerView); } else { - setChildVisibility(GONE, R.id.draft); - setChildVisibility(VISIBLE, R.id.photo, R.id.photo_spacer); + setChildVisibility(GONE, mDraftIcon); + setChildVisibility(VISIBLE, mPhotoView, mPhotoSpacerView); } } @@ -528,15 +568,15 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, */ private void setReplyOrReplyAllVisible() { if (mIsDraft) { - setChildVisibility(GONE, R.id.reply, R.id.reply_all); + setChildVisibility(GONE, mReplyButton, mReplyAllButton); return; - } else if (findViewById(R.id.overflow) == null) { - setChildVisibility(VISIBLE, R.id.reply, R.id.reply_all); + } else if (mOverflowButton == null) { + setChildVisibility(VISIBLE, mReplyButton, mReplyAllButton); return; } - setChildVisibility(mDefaultReplyAll ? GONE : VISIBLE, R.id.reply); - setChildVisibility(mDefaultReplyAll ? VISIBLE : GONE, R.id.reply_all); + setChildVisibility(mDefaultReplyAll ? GONE : VISIBLE, mReplyButton); + setChildVisibility(mDefaultReplyAll ? VISIBLE : GONE, mReplyAllButton); } private static void setChildMarginRight(View childView, int marginRight) { @@ -551,7 +591,7 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, } String[] formattedEmails = new String[emails.length]; for (int i = 0; i < emails.length; i++) { - Address e = Address.getEmailAddress(emails[i]); + Address e = getAddress(emails[i]); String name = e.getName(); String addr = e.getAddress(); if (name == null || name.length() == 0) { @@ -597,14 +637,17 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, private final String mMe; private final SpannableStringBuilder mBuilder = new SpannableStringBuilder(); private final CharSequence mComma; + private final Map<String, Address> mAddressCache; int mRecipientCount = 0; boolean mFirst = true; - public RecipientListsBuilder(Context context, String me) { + public RecipientListsBuilder(Context context, String me, + Map<String, Address> addressCache) { mContext = context; mMe = me; mComma = mContext.getText(R.string.enumeration_comma); + mAddressCache = addressCache; } public void append(String[] recipients, int headingRes) { @@ -638,7 +681,7 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, final int len = Math.min(maxToCopy, rawAddrs.length); boolean first = true; for (int i = 0; i < len; i++) { - Address email = Address.getEmailAddress(rawAddrs[i]); + Address email = getAddress(mAddressCache, rawAddrs[i]); String name = (mMe.equals(email.getAddress())) ? mContext.getString(R.string.me) : email.getSimplifiedName(); @@ -662,9 +705,9 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, @VisibleForTesting static CharSequence getRecipientSummaryText(Context context, String me, String[] to, - String[] cc, String[] bcc) { + String[] cc, String[] bcc, Map<String, Address> addressCache) { - RecipientListsBuilder builder = new RecipientListsBuilder(context, me); + RecipientListsBuilder builder = new RecipientListsBuilder(context, me, addressCache); builder.append(to, R.string.to_heading); builder.append(cc, R.string.cc_heading); @@ -949,8 +992,12 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, mCollapsedDetailsView.setOnClickListener(this); } if (!mCollapsedDetailsValid) { - ((TextView) findViewById(R.id.recipients_summary)).setText(getRecipientSummaryText( - getContext(), mAccount.name, mTo, mCc, mBcc)); + if (mMessageHeaderItem.recipientSummaryText == null) { + mMessageHeaderItem.recipientSummaryText = getRecipientSummaryText(getContext(), + mAccount.name, mTo, mCc, mBcc, mAddressCache); + } + ((TextView) findViewById(R.id.recipients_summary)) + .setText(mMessageHeaderItem.recipientSummaryText); ((TextView) findViewById(R.id.date_summary)).setText(mTimestampShort); @@ -975,9 +1022,10 @@ public class MessageHeaderView extends LinearLayout implements OnClickListener, mExpandedDetailsView = (ViewGroup) v; } if (!mExpandedDetailsValid) { - CharSequence longTimestamp = mDateBuilder != null ? mDateBuilder - .formatLongDateTime(mTimestampMs) : mTimestampMs + ""; - ((TextView) findViewById(R.id.date_value)).setText(longTimestamp); + if (mMessageHeaderItem.timestampLong == null) { + mMessageHeaderItem.timestampLong = mDateBuilder.formatLongDateTime(mTimestampMs); + } + ((TextView) findViewById(R.id.date_value)).setText(mMessageHeaderItem.timestampLong); renderEmailList(R.id.replyto_row, R.id.replyto_value, mReplyTo); renderEmailList(R.id.to_row, R.id.to_value, mTo); renderEmailList(R.id.cc_row, R.id.cc_value, mCc); diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java index aa2a1bed9..a7e9145bb 100644 --- a/src/com/android/mail/ui/ConversationViewFragment.java +++ b/src/com/android/mail/ui/ConversationViewFragment.java @@ -54,6 +54,7 @@ import com.android.mail.browse.MessageCursor; import com.android.mail.browse.MessageFooterView; import com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks; import com.android.mail.providers.Account; +import com.android.mail.providers.Address; import com.android.mail.providers.Conversation; import com.android.mail.providers.Folder; import com.android.mail.providers.ListParams; @@ -63,6 +64,9 @@ import com.android.mail.providers.UIProvider.AccountCapabilities; import com.android.mail.providers.UIProvider.FolderCapabilities; import com.android.mail.utils.LogUtils; import com.android.mail.utils.Utils; +import com.google.common.collect.Maps; + +import java.util.Map; /** @@ -110,6 +114,8 @@ public final class ConversationViewFragment extends Fragment implements private Folder mFolder; + private final Map<String, Address> mAddressCache = Maps.newHashMap(); + private static final String ARG_ACCOUNT = "account"; private static final String ARG_CONVERSATION = "conversation"; private static final String ARG_FOLDER = "folder"; @@ -159,7 +165,7 @@ public final class ConversationViewFragment extends Fragment implements mTemplates = new HtmlConversationTemplates(mContext); mAdapter = new ConversationViewAdapter(mActivity.getActivityContext(), mAccount, - getLoaderManager(), this, this); + getLoaderManager(), this, this, mAddressCache); mConversationContainer.setOverlayAdapter(mAdapter); mDensity = getResources().getDisplayMetrics().density; diff --git a/tests/src/com/android/mail/browse/MessageHeaderViewTest.java b/tests/src/com/android/mail/browse/MessageHeaderViewTest.java index 0fc8b80ed..48d7f8d9e 100644 --- a/tests/src/com/android/mail/browse/MessageHeaderViewTest.java +++ b/tests/src/com/android/mail/browse/MessageHeaderViewTest.java @@ -26,8 +26,8 @@ public class MessageHeaderViewTest extends AndroidTestCase { public void testRecipientSummaryLongTo() { String[] to = makeRecipientArray("TO", 60); String[] cc = makeRecipientArray("CC", 60); - String summary = MessageHeaderView.getRecipientSummaryText(getContext(), "", to, cc, null) - .toString(); + String summary = MessageHeaderView.getRecipientSummaryText(getContext(), "", to, cc, null, + null).toString(); assertTrue(summary.contains("TO00")); assertTrue(summary.contains("TO49")); @@ -39,8 +39,8 @@ public class MessageHeaderViewTest extends AndroidTestCase { String[] to = makeRecipientArray("TO", 20); String[] cc = makeRecipientArray("CC", 10); String[] bcc = makeRecipientArray("BB", 60); - String summary = MessageHeaderView.getRecipientSummaryText(getContext(), "", to, cc, bcc) - .toString(); + String summary = MessageHeaderView.getRecipientSummaryText(getContext(), "", to, cc, bcc, + null).toString(); assertTrue(summary.contains("TO00")); assertTrue(summary.contains("TO19")); |