com.tct.mail.browse.MessageHeaderView.java Source code

Java tutorial

Introduction

Here is the source code for com.tct.mail.browse.MessageHeaderView.java

Source

/**
 * Copyright (c) 2011, Google Inc.
 *
 * 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.
 */

/******************************************************************************/
/* ========================================================================== */
/*     Modifications on Features list / Changes Request / Problems Report     */
/* -------------------------------------------------------------------------- */
/*    date   |        author        |         Key          |     comment      */
/* ----------|----------------------|----------------------|----------------- */
/* ----------|----------------------|----------------------|----------------- */
/* 04/22/2014|     Chao Zhang       |      FR 631895       |bcc and auto dow- */
/*           |                      |porting from  FR487417|nload remaining   */
/* ----------|----------------------|----------------------|----------------- */
/* 04/17/2013|     Chao Zhang       |      FR 631895       |[HOMO][HOMO][Ora- */
/*           |                      |porting from FR514398 |nge][Homologatio- */
/*           |                      |                      |n] Exchange Acti- */
/*           |                      |                      |ve Sync Priority  */
/******************************************************************************/
/*
 ==========================================================================
 *HISTORY
 *
 *Tag            Date         Author          Description
 *============== ============ =============== ==============================
 *CONFLICT-20001 2014/10/24   wenggangjin     Modify the package conflict
 *BUGFIX-869494  2014/12/31   zhaotianyong    [Android5.0][Email][UE] Show attachments on top screen.
 *BUGFIX-884329  2014/01/19   zhaotianyong    [Clone][Email]Download vcs-attachments display "Load iCalendar error"
 *BUGFIX-891429  2014/01/19   zhaotianyong    [Android5.0][Email][UI] The arrow direction is wrong after expanding attachments.
 *BUGFIX-901690  2014/01/19   zhaotianyong    [Android5.0][Email]Can't download attachment again successfully
 *BUGFIX-881446  2015-01-15   wenggangjin     [Email]All attachment disappear after delete one attachment of Email in outbox
 *BUGFIX-913854  2015-01-28   zhaotianyong    [Android5.0][Email] Show a large area after expanding attachments.
 *BUGFIX-920071  2015-02-02   gengkexue       [Monkey][Crash]com.tct.email java.lang.NullPointerException.
 *BUGFIX-936358  2015-03-02   chenyanhua      [Email]The mail attachment doesn't update after edit
 *BUGFIX-907154  2015-03-04   wenggangjin     [Android5.0][Exchange]There is no prompt when we forward an email
 *BUGFIX_938279  2015-03-04   gengkexue       [Android5.0][Email][Arabic] No 'receive time' and 'View details' when open a mail.
 *BUGFIX_954064  2015-03-25   ZhangChao       [Android5.0][Email] Attachment not hide when tap the email title.
 *BUGFIX-962222  2015-03-31   junwei-xu       [Android5.0][Email]There will always be a "!" in eml file
 *BUGFIX-957218  2015/4/1     junwei-xu    [Android5.0][Email]Email can forward before download the attachment.
 *BUGFIX-971901  2015/4/16    junwei-xu       [Android5.0][Email][UE]The toast when we forward an email will last for a long time
 *BUGFIX-940964  2015/4/20    gangjin.weng    [Email] Set Dwonload Head Only by default
 *BUGFIX-988459  2015/05/08   zhaotianyong    [Email]Mail can not be forwarded if the attachment is not supported
 *BUGFIX-998470  2015/05/11   zhaotianyong    [Android5.0][Email]Update the ergo when reply/reply-all/forward email under header only mode.
 *BUGFIX-998526  2015/05/22   zhaotianyong    [Email]Email attachment will overlap the email body during downloading remaining
 *BUGFIX-998526  2015/06/02   Gantao           [Email]Email attachment will overlap the email body during downloading remaining
 *BUGFIX-1001086  2015/06/15   Gantao         [Android5.0][Email] Cannot extend attachments after touch down-arrow icon.
 *BUGFIX-965608  2015/06/19   junwei-xu       [REG][Android5.0][Email]Priority doesn't saved after edit one draft
 *BUGFIX-1030014  2015/06/19   chao-zhang      [HOMO][ALWE] [Email] Email app closed suddenly when reading email (but apparently no crash)
 *BUGFIX-1031646  2015/07/01   xujian         [Android5.0][Email] Still display expand/hide arrow after remove all attachments in Draft.
 *BUGFIX-964831  2015/07/16   xujian          [Android5.0][Email][REG] Hidden content show out after rorate device
 *BUGFIX-964831  2015/08/28   kaifeng.lu      [Android L][Email][Monkey][ANR]ANR in com.tct.email (com.tct.email/.activity.Welcome)
 *BUGFIX-526192  2015-09-01   junwei-xu       From and reply all must be auto display.
 *FEATURE-ID      2015/08/12   Gantao         FEATURE--Always show pictures
 *FEATURE-ID     2015/08/27   Gantao         Horizontal attachment
 *CR_585337      2015-09-29  chao.zhang       Exchange Email resend mechanism
 *BUGFIX-858353c 2015/11/03  zheng.zou       [Email]Optimize Exchange smart-forward/smart-reply
 *BUGFIX-1000731 2015/12/01  jin.dong        [Email][Force close][Monitor]It will pop up email force close when hung up a call when received mails.
 *BUGFIX-1595495 2016/02/29  tianjing.su     [Ergo][Email]Have no the save group icon when enter mail details
 ============================================================================
 */
package com.tct.mail.browse;

import android.annotation.SuppressLint;
import android.app.FragmentManager;
import android.app.LoaderManager;
import android.content.AsyncQueryHandler;
import android.content.Context;
import android.content.Loader;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.text.BidiFormatter;
import android.text.Html;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import android.widget.QuickContactBadge;
import android.widget.TextView;
import android.widget.Toast;

import com.tct.email.activity.setup.MessageSaveGroupDialog;
import com.tct.emailcommon.mail.Address;
import com.tct.email.R;
import com.tct.mail.utils.LogTag;
import com.tct.mail.utils.LogUtils;
import com.tct.email.activity.ConnectionAlertDialog;
//TS: MOD by wenggangjin for CONFLICT_20001 START
//import com.google.common.annotations.VisibleForTesting;
import com.tct.fw.google.common.annotations.VisibleForTesting;
import com.tct.fw.google.common.base.Objects;
import com.tct.fw.google.common.collect.Lists;
import com.tct.mail.ContactInfo;
import com.tct.mail.ContactInfoSource;
import com.tct.mail.analytics.Analytics;
import com.tct.mail.browse.AttachmentLoader.AttachmentCursor;
import com.tct.mail.browse.ConfirmDialogFragment.ForwardDialogFragment;
import com.tct.mail.browse.ConversationViewAdapter.MessageHeaderItem;
import com.tct.mail.compose.ComposeActivity;
import com.tct.mail.perf.Timer;
import com.tct.mail.photomanager.LetterTileProvider;
import com.tct.mail.print.PrintUtils;
import com.tct.mail.providers.Account;
import com.tct.mail.providers.Attachment;
import com.tct.mail.providers.Conversation;
import com.tct.mail.providers.Message;
import com.tct.mail.providers.Settings;
import com.tct.mail.providers.UIProvider;
import com.tct.mail.text.EmailAddressSpan;
import com.tct.mail.ui.AbstractConversationViewFragment;
import com.tct.mail.ui.ImageCanvas;
import com.tct.mail.utils.MimeType;
import com.tct.mail.utils.PLFUtils;
import com.tct.mail.utils.Utils;
import com.tct.mail.utils.VeiledAddressMatcher;
//TS: MOD by wenggangjin for CONFLICT_20001 END
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

//[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import com.tct.emailcommon.provider.EmailContent;
import com.tct.emailcommon.provider.HostAuth;
import com.tct.emailcommon.utility.Utility;

public class MessageHeaderView extends SnapHeader implements OnClickListener, OnMenuItemClickListener,
        ConversationContainer.DetachListener, LoaderManager.LoaderCallbacks<Cursor> {

    /**
     * Cap very long recipient lists during summary construction for efficiency.
     */
    private static final int SUMMARY_MAX_RECIPIENTS = 50;

    private static final int MAX_SNIPPET_LENGTH = 100;

    private static final int SHOW_IMAGE_PROMPT_ONCE = 1;
    private static final int SHOW_IMAGE_PROMPT_ALWAYS = 2;

    private static final String HEADER_RENDER_TAG = "message header render";
    private static final String LAYOUT_TAG = "message header layout";
    private static final String MEASURE_TAG = "message header measure";
    private static final String DIALOG_FRAGMENT_TAG = "save_group_dialog_tag";
    private static final String LOG_TAG = LogTag.getLogTag();

    // This is a debug only feature
    public static final boolean ENABLE_REPORT_RENDERING_PROBLEM = false;

    private static final String DETAILS_DIALOG_TAG = "details-dialog";
    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
    public static final int LOAD_REMAIN_MESSAGE_SUCCESS = 1;
    public static final int LOAD_REMAIN_MESSAGE_FAIL = 0;
    //[FEATURE]-Add-END by TSCD.chao zhang
    private MessageHeaderViewCallbacks mCallbacks;
    //TS: zhaotianyong 2014-12-31 EMAIL BUGFIX_869494 ADD_S
    private LoaderManager mLoaderManager;
    private Integer mOldAttachmentLoaderId;
    private AttachmentCursor mAttachmentsCursor;
    private FragmentManager mFragmentManager;
    //TS: zhaotianyong 2014-12-31 EMAIL BUGFIX_869494 ADD_E

    private View mBorderView;
    private ViewGroup mUpperHeaderView;
    private View mTitleContainer;
    private View mSnapHeaderBottomBorder;
    private TextView mSenderNameView;
    private TextView mRecipientSummary;
    private TextView mDateView;
    private View mHideDetailsView;
    private TextView mSnippetView;
    private MessageHeaderContactBadge mPhotoView;
    private ViewGroup mExtraContentView;
    private ViewGroup mExpandedDetailsView;
    private SpamWarningView mSpamWarningView;
    private TextView mImagePromptView;
    private MessageInviteView mInviteView;
    private View mForwardButton;
    private View mOverflowButton;
    private View mDraftIcon;
    private View mEditDraftButton;
    private TextView mUpperDateView;
    private View mReplyButton;
    private View mReplyAllButton;
    private View mAttachmentIcon;
    private final EmailCopyContextMenu mEmailCopyMenu;
    // TS: junwei-xu 2015-09-06 EMAIL BUGFIX-526192 DEL_S
    /*
    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
    private ImageView mPriority;
    //[FEATURE]-Add-END by TSCD.chao zhang
    */
    // TS: junwei-xu 2015-09-06 EMAIL BUGFIX-526192 DEL_E
    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)7
    private long mId;
    private boolean mPartialDownload = false;
    private LinearLayout mRemainBtnView;
    //[FEATURE]-Add-END by TSCD.chao zhang
    //TS: Gantao 2015-06-16 EMAIL BUGFIX_1013206 ADD_S
    private TextView msgRemainBtn;
    private ProgressBar msgProgress;
    private TextView msgLoading;
    //TS: Gantao 2015-06-16 EMAIL BUGFIX_1013206 ADD_E

    //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_S
    private LinearLayout mAttachmentBarListLayout;
    private HorizontalScrollView mAttachmentBarScorll;
    //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_E

    // temporary fields to reference raw data between initial render and details
    // expansion
    private String[] mFrom;
    private String[] mTo;
    private String[] mCc;
    private String[] mBcc;
    private String[] mReplyTo;

    private boolean mIsDraft = false;

    private int mSendingState;

    private String mSnippet;

    private Address mSender;

    private ContactInfoSource mContactInfoSource;

    private boolean mPreMeasuring;

    private ConversationAccountController mAccountController;

    private Map<String, Address> mAddressCache;

    private boolean mShowImagePrompt;

    private PopupMenu mPopup;

    private MessageHeaderItem mMessageHeaderItem;
    private ConversationMessage mMessage;

    private boolean mRecipientSummaryValid;
    private boolean mExpandedDetailsValid;

    private final LayoutInflater mInflater;

    private AsyncQueryHandler mQueryHandler;

    private boolean mObservingContactInfo;

    /**
     * What I call myself? "me" in English, and internationalized correctly.
     */
    private final String mMyName;

    private final DataSetObserver mContactInfoObserver = new DataSetObserver() {
        @Override
        public void onChanged() {
            updateContactInfo();
        }
    };

    private boolean mExpandable = true;

    private VeiledAddressMatcher mVeiledMatcher;

    private boolean mIsViewOnlyMode = false;

    private LetterTileProvider mLetterTileProvider;
    private final int mContactPhotoWidth;
    private final int mContactPhotoHeight;
    private final int mTitleContainerMarginEnd;
    private final int mLinkColorBlue; // AM: Kexue.Geng 2015-03-04 EMAIL BUGFIX_938279

    /**
     * The snappy header has special visibility rules (i.e. no details header,
     * even though it has an expanded appearance)
     */
    private boolean mIsSnappy;

    private BidiFormatter mBidiFormatter;

    public interface MessageHeaderViewCallbacks {
        void setMessageSpacerHeight(MessageHeaderItem item, int newSpacerHeight);

        void setMessageExpanded(MessageHeaderItem item, int newSpacerHeight);

        void setMessageDetailsExpanded(MessageHeaderItem messageHeaderItem, boolean expanded,
                int previousMessageHeaderItemHeight);

        void showExternalResources(Message msg);

        void showExternalResources(String senderRawAddress);

        boolean supportsMessageTransforms();

        String getMessageTransforms(Message msg);

        FragmentManager getFragmentManager();

        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
        int loadSingleMessageBackground(Message msg);
        //[FEATURE]-Add-END by TSCD.chao zhang

        // TS: Gantao 2016-02-17 EMAIL BUGFIX-1554314 ADD_S
        boolean isDownloadRemaining();
        // TS: Gantao 2016-02-17 EMAIL BUGFIX-1554314 ADD_E

        /**
         * @return <tt>true</tt> if this header is contained within a SecureConversationViewFragment
         * and cannot assume the content is <strong>not</strong> malicious
         */
        boolean isSecure();
    }

    public MessageHeaderView(Context context) {
        this(context, null);
    }

    public MessageHeaderView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public MessageHeaderView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        context = context;
        mIsSnappy = false;
        mEmailCopyMenu = new EmailCopyContextMenu(getContext());
        mInflater = LayoutInflater.from(context);
        mMyName = context.getString(R.string.me_object_pronoun);

        final Resources res = getResources();
        mContactPhotoWidth = res.getDimensionPixelSize(R.dimen.contact_image_width);
        mContactPhotoHeight = res.getDimensionPixelSize(R.dimen.contact_image_height);
        mTitleContainerMarginEnd = res.getDimensionPixelSize(R.dimen.conversation_view_margin_side);
        mLinkColorBlue = res.getColor(R.color.conversation_view_text_color_link_blue); // AM: Kexue.Geng 2015-03-04 EMAIL BUGFIX_938279
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mBorderView = findViewById(R.id.message_header_border);
        mUpperHeaderView = (ViewGroup) findViewById(R.id.upper_header);
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
        mRemainBtnView = (LinearLayout) findViewById(R.id.msg_remain);
        //[FEATURE]-Add-END by TSCD.chao zhang
        //TS: Gantao 2015-06-16 EMAIL BUGFIX_1013206 ADD_S
        msgRemainBtn = (TextView) findViewById(R.id.msg_remain_btn);
        msgRemainBtn.setOnClickListener(this);
        msgProgress = (ProgressBar) findViewById(R.id.msg_remain_loading_progress);
        msgLoading = (TextView) findViewById(R.id.msg_remain_loading_text);
        //TS: Gantao 2015-06-16 EMAIL BUGFIX_1013206 ADD_E
        mTitleContainer = findViewById(R.id.title_container);
        mSnapHeaderBottomBorder = findViewById(R.id.snap_header_bottom_border);
        mSenderNameView = (TextView) findViewById(R.id.sender_name);
        mRecipientSummary = (TextView) findViewById(R.id.recipient_summary);
        mDateView = (TextView) findViewById(R.id.send_date);
        mDateView.setOnLongClickListener(null); // AM: Kexue.Geng 2015-03-04 EMAIL BUGFIX_938279
        mHideDetailsView = findViewById(R.id.hide_details);
        mSnippetView = (TextView) findViewById(R.id.email_snippet);
        mPhotoView = (MessageHeaderContactBadge) findViewById(R.id.photo);
        mPhotoView.setQuickContactBadge((QuickContactBadge) findViewById(R.id.invisible_quick_contact));
        mReplyButton = findViewById(R.id.reply);
        mReplyAllButton = findViewById(R.id.reply_all);
        mForwardButton = findViewById(R.id.forward);
        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);
        mExtraContentView = (ViewGroup) findViewById(R.id.header_extra_content);
        // TS: junwei-xu 2015-09-06 EMAIL BUGFIX-526192 DEL_S
        /*
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
        mPriority = (ImageView)findViewById(R.id.priority_icon);
        //[FEATURE]-Add-END by TSCD.chao zhang
        // TS: junwei-xu 2015-03-31 EMAIL BUGFIX-962222 MOD_S
        if (mPriority != null) {
        mPriority.setImageLevel(Message.FLAG_PRIORITY_NORMAL);
        }
        // TS: junwei-xu 2015-03-31 EMAIL BUGFIX-962222 MOD_E
        */
        // TS: junwei-xu 2015-09-06 EMAIL BUGFIX-526192 DEL_E
        //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_S
        mAttachmentBarListLayout = (LinearLayout) findViewById(R.id.attachment_bar_lists);
        mAttachmentBarScorll = (HorizontalScrollView) findViewById(R.id.attachment_bar_scroll);
        //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_E
        setExpanded(true);

        registerMessageClickTargets(mReplyButton, mReplyAllButton, mForwardButton, mEditDraftButton,
                mOverflowButton, mUpperHeaderView, mDateView, mHideDetailsView);

        mUpperHeaderView.setOnCreateContextMenuListener(mEmailCopyMenu);
    }

    private void registerMessageClickTargets(View... views) {
        for (View v : views) {
            if (v != null) {
                v.setOnClickListener(this);
            }
        }
    }

    @Override
    public void initialize(ConversationAccountController accountController, Map<String, Address> addressCache,
            MessageHeaderViewCallbacks callbacks, ContactInfoSource contactInfoSource,
            VeiledAddressMatcher veiledAddressMatcher) {
        //TS: zhaotianyong 2014-12-31 EMAIL BUGFIX_869494 MOD_S
        initialize(mLoaderManager, mFragmentManager, accountController, addressCache);
        //TS: zhaotianyong 2014-12-31 EMAIL BUGFIX_869494 MOD_E
        setCallbacks(callbacks);
        setContactInfoSource(contactInfoSource);
        setVeiledMatcher(veiledAddressMatcher);
    }

    /**
     * Associate the header with a contact info source for later contact
     * presence/photo lookup.
     */
    public void setContactInfoSource(ContactInfoSource contactInfoSource) {
        mContactInfoSource = contactInfoSource;
    }

    public void setCallbacks(MessageHeaderViewCallbacks callbacks) {
        mCallbacks = callbacks;
    }

    public void setVeiledMatcher(VeiledAddressMatcher matcher) {
        mVeiledMatcher = matcher;
    }

    public boolean isExpanded() {
        // (let's just arbitrarily say that unbound views are expanded by default)
        return mMessageHeaderItem == null || mMessageHeaderItem.isExpanded();
    }

    @Override
    public void onDetachedFromParent() {
        unbind();
    }

    /**
     * Headers that are unbound will not match any rendered header (matches()
     * will return false). Unbinding is not guaranteed to *hide* the view's old
     * data, though. To re-bind this header to message data, call render() or
     * renderUpperHeaderFrom().
     */
    @Override
    public void unbind() {
        mMessageHeaderItem = null;
        mMessage = null;

        if (mObservingContactInfo) {
            mContactInfoSource.unregisterObserver(mContactInfoObserver);
            mObservingContactInfo = false;
        }
    }

    //TS: zhaotianyong 2014-12-31 EMAIL BUGFIX_869494 MOD_S
    public void initialize(LoaderManager loaderManager, FragmentManager fragmentManager,
            ConversationAccountController accountController, Map<String, Address> addressCache) {
        mLoaderManager = loaderManager;
        mFragmentManager = fragmentManager;
        mAccountController = accountController;
        mAddressCache = addressCache;
    }
    //TS: zhaotianyong 2014-12-31 EMAIL BUGFIX_869494 MOD_E

    private Account getAccount() {
        return mAccountController != null ? mAccountController.getAccount() : null;
    }

    public void bind(MessageHeaderItem headerItem, boolean measureOnly) {
        if (mMessageHeaderItem != null && mMessageHeaderItem == headerItem) {
            return;
        }

        mMessageHeaderItem = headerItem;
        render(measureOnly);
    }

    // TS: Gantao 2015-06-02 EMAIL BUGFIX-998526 ADD_S
    public void updateHeight() {
        ViewGroup parent = (ViewGroup) getParent();
        if (parent != null) {
            updateSpacerHeight();
        }
    }
    // TS: Gantao 2015-06-02 EMAIL BUGFIX-998526 ADD_E

    /**
     * Rebinds the view to its data. This will only update the view
     * if the {@link MessageHeaderItem} sent as a parameter is the
     * same as the view's current {@link MessageHeaderItem} and the
     * view's expanded state differs from the item's expanded state.
     */
    public void rebind(MessageHeaderItem headerItem) {
        if (mMessageHeaderItem == null || mMessageHeaderItem != headerItem || isActivated() == isExpanded()) {
            return;
        }

        render(false /* measureOnly */);
    }

    @Override
    public void refresh() {
        render(false);
    }

    private BidiFormatter getBidiFormatter() {
        if (mBidiFormatter == null) {
            final ConversationViewAdapter adapter = mMessageHeaderItem != null ? mMessageHeaderItem.getAdapter()
                    : null;
            if (adapter == null) {
                mBidiFormatter = BidiFormatter.getInstance();
            } else {
                mBidiFormatter = adapter.getBidiFormatter();
            }
        }
        return mBidiFormatter;
    }

    private void render(boolean measureOnly) {
        if (mMessageHeaderItem == null) {
            return;
        }

        Timer t = new Timer();
        t.start(HEADER_RENDER_TAG);

        mRecipientSummaryValid = false;
        mExpandedDetailsValid = false;

        mMessage = mMessageHeaderItem.getMessage();

        final Account account = getAccount();
        final boolean alwaysShowImagesForAccount = (account != null)
                && (account.settings.showImages == Settings.ShowImages.ALWAYS);

        final boolean alwaysShowImagesForMessage = mMessage.shouldShowImagePrompt();

        if (!alwaysShowImagesForMessage) {
            // we don't need the "Show picture" prompt if the user allows images for this message
            mShowImagePrompt = false;
        } else if (mCallbacks.isSecure()) {
            // in a secure view we always display the "Show picture" prompt
            mShowImagePrompt = true;
        } else {
            // otherwise honor the account setting for automatically showing pictures
            mShowImagePrompt = !alwaysShowImagesForAccount;
        }

        setExpanded(mMessageHeaderItem.isExpanded());

        mFrom = mMessage.getFromAddresses();
        mTo = mMessage.getToAddresses();
        mCc = mMessage.getCcAddresses();
        mBcc = mMessage.getBccAddresses();
        mReplyTo = mMessage.getReplyToAddresses();
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
        mId = mMessage.getId();
        //[FEATURE]-Add-END by TSCD.chao zhang
        /**
         * Turns draft mode on or off. Draft mode hides message operations other
         * than "edit", hides contact photo, hides presence, and changes the
         * sender name to "Draft".
         */
        mIsDraft = mMessage.draftType != UIProvider.DraftType.NOT_A_DRAFT;
        mSendingState = mMessage.sendingState;
        // TS: chao.zhang 2015-09-29 EMAIL FEATURE-585337 ADD_S
        //NOTE: Only display draft mode during message send failed.
        if (!mIsDraft || (mSendingState != UIProvider.ConversationSendingState.OTHER
                && mSendingState != UIProvider.ConversationSendingState.SEND_ERROR)) {
            mIsDraft = false;
        }
        // TS: chao.zhang 2015-09-29 EMAIL FEATURE-585337 ADD_E
        // If this was a sent message AND:
        // 1. the account has a custom from, the cursor will populate the
        // selected custom from as the fromAddress when a message is sent but
        // not yet synced.
        // 2. the account has no custom froms, fromAddress will be empty, and we
        // can safely fall back and show the account name as sender since it's
        // the only possible fromAddress.
        String from = mMessage.getFrom();
        if (TextUtils.isEmpty(from)) {
            from = (account != null) ? account.getEmailAddress() : "";
        }
        mSender = getAddress(from);

        // TS: junwei-xu 2015-09-06 EMAIL BUGFIX-526192 DEL_S
        // TS: junwei-xu 2015-06-19 EMAIL BUGFIX_965608 ADD_S
        /*
        if (mPriority != null) {
        mPriority.setImageLevel(getPriorityOptionValue(mMessage.mPriority));
        }
        */
        /*
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
        final Conversation conversation = mMessage.getConversation();
        if (conversation != null) {
          mMessage.mPriority=conversation.flagPriority;
        if(mPriority!=null){
            mPriority.setImageLevel(getPriorityOptionValue(conversation.flagPriority));
        }
         //[FEATURE]-Add-END by TSCD.chao zhang
        }
        */
        // TS: junwei-xu 2015-06-19 EMAIL BUGFIX_965608 ADD_E
        // TS: junwei-xu 2015-09-06 EMAIL BUGFIX-526192 DEL_E
        //TS: Gantao 2015-06-15 EMAIL BUGFIX_1001086 MOD_S
        updateChildVisibility(false);
        //TS: Gantao 2015-06-15 EMAIL BUGFIX_1001086 MOD_E

        final String snippet;
        if (mIsDraft || mSendingState != UIProvider.ConversationSendingState.OTHER) {
            snippet = makeSnippet(mMessage.snippet);
        } else {
            snippet = mMessage.snippet;
        }
        mSnippet = snippet == null ? null : getBidiFormatter().unicodeWrap(snippet);

        mSenderNameView.setText(getHeaderTitle());
        setRecipientSummary();
        setDateText();
        mSnippetView.setText(mSnippet);
        setAddressOnContextMenu();

        if (mUpperDateView != null) {
            mUpperDateView.setText(mMessageHeaderItem.getTimestampShort());
        }

        //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_S
        final Integer attachmentLoaderId = getAttachmentLoaderId();

        // Destroy the loader if we are attempting to load a different
        // attachment
        // TS: zhaotianyong 2015-02-17 EMAIL BUGFIX_933187 MOD_S
        // TS: wenggangjin 2015-01-15 EMAIL BUGFIX_881446 MOD_S
        if (mOldAttachmentLoaderId != null
                && (!Objects.equal(mOldAttachmentLoaderId, attachmentLoaderId) || mIsDraft)) {// TS:
                                                                                                                                 // chenyanhua
                                                                                                                                 // 2015-03-02
                                                                                                                                 // EMAIL
                                                                                                                                 // BUGFIX_936358
                                                                                                                                 // MOD
                                                                                                                                 // TS: wenggangjin 2015-01-15 EMAIL BUGFIX_881446 MOD_E
                                                                                                                                 // TS: zhaotianyong 2015-02-17 EMAIL BUGFIX_933187 MOD_E // reback
                                                                                                                                 // the BUGFIX_881446
            mLoaderManager.destroyLoader(mOldAttachmentLoaderId);

            // Resets the footer view. This step is only done if the
            // attachmentsListUri changes so that we don't
            // repeat the work of layout and measure when
            // we're only updating the attachments.
            // TS: Gantao 2015-06-15 EMAIL BUGFIX_1001086 MOD_S
            mAttachmentBarListLayout.removeAllViewsInLayout();
            mAttachmentBarScorll.setVisibility(View.GONE);
        }
        mOldAttachmentLoaderId = attachmentLoaderId;

        // kick off load of Attachment objects in background thread
        // but don't do any Loader work if we're only measuring
        if (!measureOnly && attachmentLoaderId != null) {
            LogUtils.i(LOG_TAG, "binding footer view, calling initLoader for message %d", attachmentLoaderId);
            mLoaderManager.initLoader(attachmentLoaderId, Bundle.EMPTY, this);
        }
        //[Defect]-Del-BEGINbySCDTABLET.qiao-yang@tcl.com,05/12/2016,2004459,
        if (mAttachmentBarListLayout.getChildCount() == 0 /*&& mMessage.hasAttachments*/) { // TS:
            // Gantao
            // 2016-02-23
            // EMAIL
            // BUGFIX_1649841
            // MOD
            renderAttachments(false);
        }
        //[Defect]-Del-ENDbySCDTABLET.qiao-yang@tcl.com
        //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_E

        if (measureOnly) {
            // avoid leaving any state around that would interfere with future regular bind() calls
            unbind();
        } else {
            updateContactInfo();
            if (!mObservingContactInfo) {
                mContactInfoSource.registerObserver(mContactInfoObserver);
                mObservingContactInfo = true;
            }
        }

        t.pause(HEADER_RENDER_TAG);
    }

    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
    private int getPriorityOptionValue(int index) {
        int[] values = getResources().getIntArray(R.array.set_priority_dialog_options_values);
        if ((index >= 0) && (index < values.length)) {
            return values[index];
        } else {
            return -1;
        }
    }
    //[FEATURE]-Add-END by TSCD.chao zhang

    // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_S
    /**
     * check whether the message support Reply all
     */
    private boolean isSupportReplyAll(Message message) {
        boolean supprot = false;

        if (message != null) {
            String[] sendToAddresses = message.getToAddressesUnescaped();
            String[] ccAddresses = message.getCcAddressesUnescaped();
            String[] bccAddresses = message.getBccAddressesUnescaped();
            int sizeSendTo = sendToAddresses != null ? sendToAddresses.length : 0;
            int sizeCc = ccAddresses != null ? ccAddresses.length : 0;
            int sizeBcc = bccAddresses != null ? bccAddresses.length : 0;

            //when the summation for sendToAddresses, ccAddresses and bccAddresses's size over 1.
            //we think it supprot reply all.
            if (sizeSendTo + sizeCc + sizeBcc > 1) {
                supprot = true;
            }
        }

        return supprot;
    }
    // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_E

    /**
     * Update context menu's address field for when the user long presses
     * on the message header and attempts to copy/send email.
     */
    private void setAddressOnContextMenu() {
        if (mSender != null) {
            mEmailCopyMenu.setAddress(mSender.getAddress());
        }
    }

    @Override
    public boolean isBoundTo(ConversationOverlayItem item) {
        return item == mMessageHeaderItem;
    }

    public Address getAddress(String emailStr) {
        return Utils.getAddress(mAddressCache, emailStr);
    }

    protected void updateSpacerHeight() {
        final int h = measureHeight();
        //TS: chaozhang 2015-06-24 EMAIL BUGFIX_1030014 ADD_S
        //After ChromeWebView loaded finished,a callBack will notify to update UI.
        //in some special case,during calutate the MessageHeaderView,
        //nullPointerException will thrown,which made libwebviewchromium crashed
        if (mMessageHeaderItem == null) {
            return;
        }
        //TS: chaozhang 2015-06-24 EMAIL BUGFIX_1030014 ADD_E
        mMessageHeaderItem.setHeight(h);
        if (mCallbacks != null) {
            mCallbacks.setMessageSpacerHeight(mMessageHeaderItem, h);
        }
    }

    private int measureHeight() {
        ViewGroup parent = (ViewGroup) getParent();
        if (parent == null) {
            LogUtils.e(LOG_TAG, new Error(), "Unable to measure height of detached header");
            return getHeight();
        }
        mPreMeasuring = true;
        final int h = Utils.measureViewHeight(this, parent);
        mPreMeasuring = false;
        return h;
    }

    private CharSequence getHeaderTitle() {
        CharSequence title;
        switch (mSendingState) {
        case UIProvider.ConversationSendingState.QUEUED:
            title = getResources().getString(R.string.message_in_queued_status);
            break;
        case UIProvider.ConversationSendingState.SENDING:
            title = getResources().getString(R.string.sending);
            break;
        case UIProvider.ConversationSendingState.RETRYING:
            title = getResources().getString(R.string.message_retrying);
            break;
        case UIProvider.ConversationSendingState.SEND_ERROR:
            title = getResources().getString(R.string.message_failed);
            break;
        default:
            if (mIsDraft) {
                title = SendersView.getSingularDraftString(getContext());
            } else {
                title = getBidiFormatter().unicodeWrap(getSenderName(mSender));
            }
        }

        return title;
    }

    private void setRecipientSummary() {
        if (!mRecipientSummaryValid) {
            if (mMessageHeaderItem.recipientSummaryText == null) {
                final Account account = getAccount();
                final String meEmailAddress = (account != null) ? account.getEmailAddress() : "";
                mMessageHeaderItem.recipientSummaryText = getRecipientSummaryText(getContext(), meEmailAddress,
                        mMyName, mTo, mCc, mBcc, mAddressCache, mVeiledMatcher, getBidiFormatter());
            }
            mRecipientSummary.setText(mMessageHeaderItem.recipientSummaryText);
            mRecipientSummaryValid = true;
        }
    }

    private void setDateText() {
        if (mIsSnappy) {
            mDateView.setText(mMessageHeaderItem.getTimestampLong());
            mDateView.setOnClickListener(null);
        } else {
            // AM: Kexue.Geng 2015-03-04 EMAIL BUGFIX_938279 MOD_S
            /*
            mDateView.setMovementMethod(LinkMovementMethod.getInstance());
            mDateView.setText(Html.fromHtml(getResources().getString(
                R.string.date_and_view_details, mMessageHeaderItem.getTimestampLong())));
            StyleUtils.stripUnderlinesAndUrl(mDateView);
             */
            mDateView.setText(Html.fromHtml(getResources().getString(R.string.date_and_view_details_another,
                    mMessageHeaderItem.getTimestampLong(), mLinkColorBlue)));
            // AM: Kexue.Geng 2015-03-04 EMAIL BUGFIX_938279 MOD_E
        }
    }

    /**
     * Return the name, if known, or just the address.
     */
    private static String getSenderName(Address sender) {
        if (sender == null) {
            return "";
        }
        final String displayName = sender.getPersonal();
        return TextUtils.isEmpty(displayName) ? sender.getAddress() : displayName;
    }

    private static void setChildVisibility(int visibility, View... children) {
        for (View v : children) {
            if (v != null) {
                v.setVisibility(visibility);
            }
        }
    }

    private void setExpanded(final boolean expanded) {
        // use View's 'activated' flag to store expanded state
        // child view state lists can use this to toggle drawables
        setActivated(expanded);
        if (mMessageHeaderItem != null) {
            mMessageHeaderItem.setExpanded(expanded);
        }
    }

    //TS: Gantao 2015-06-15 EMAIL BUGFIX_1001086 MOD
    /**
     * Update the visibility of the many child views based on expanded/collapsed
     * and draft/normal state.
     * @param TS:add param isToggleExpanded to indicate if is called by toggleExpanded()
     */
    private void updateChildVisibility(boolean isToggleExpanded) {
        // Too bad this can't be done with an XML state list...

        if (mIsViewOnlyMode) {
            setMessageDetailsVisibility(VISIBLE);
            setChildVisibility(GONE, mSnapHeaderBottomBorder);

            setChildVisibility(GONE, mReplyButton, mReplyAllButton, mForwardButton, mOverflowButton, mDraftIcon,
                    mEditDraftButton, mAttachmentIcon, mUpperDateView, mSnippetView);
            setChildVisibility(VISIBLE, mPhotoView, mRecipientSummary);

            setChildMarginEnd(mTitleContainer, 0);
        } else if (isExpanded()) {
            int normalVis, draftVis;

            final boolean isSnappy = isSnappy();
            setMessageDetailsVisibility((isSnappy) ? GONE : VISIBLE);
            setChildVisibility(isSnappy ? VISIBLE : GONE, mSnapHeaderBottomBorder);

            if (mIsDraft) {
                normalVis = GONE;
                draftVis = VISIBLE;
            } else {
                normalVis = VISIBLE;
                draftVis = GONE;
            }

            setReplyOrReplyAllVisible();
            setChildVisibility(normalVis, mPhotoView, mForwardButton, mOverflowButton);
            setChildVisibility(draftVis, mDraftIcon, mEditDraftButton);
            setChildVisibility(VISIBLE, mRecipientSummary);
            setChildVisibility(GONE, mAttachmentIcon, mUpperDateView, mSnippetView);
            setChildMarginEnd(mTitleContainer, 0);
            //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_S
            //[Defect]-Del-BEGINbySCDTABLET.qiao-yang@tcl.com,05/12/2016,2004459,
            //if (mMessage.hasAttachments) {
            setChildVisibility(VISIBLE, mAttachmentBarScorll);
            //}
            //[Defect]-Del-ENDbySCDTABLET.qiao-yang@tcl.com
            //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_E
        } else {
            setMessageDetailsVisibility(GONE);
            setChildVisibility(GONE, mSnapHeaderBottomBorder);
            setChildVisibility(VISIBLE, mSnippetView, mUpperDateView);

            setChildVisibility(GONE, mEditDraftButton, mReplyButton, mReplyAllButton, mForwardButton,
                    mOverflowButton, mRecipientSummary, mDateView, mHideDetailsView);

            setChildVisibility(mMessage.hasAttachments ? VISIBLE : GONE, mAttachmentIcon);
            //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_S
            //[Defect]-Del-BEGINbySCDTABLET.qiao-yang@tcl.com,05/12/2016,2004459,
            //if (mMessage.hasAttachments) {
            setChildVisibility(GONE, mAttachmentBarScorll);
            //}
            //[Defect]-Del-ENDbySCDTABLET.qiao-yang@tcl.com
            //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_E

            if (mIsDraft) {
                setChildVisibility(VISIBLE, mDraftIcon);
                setChildVisibility(GONE, mPhotoView);
            } else {
                setChildVisibility(GONE, mDraftIcon);
                setChildVisibility(VISIBLE, mPhotoView);
            }
            setChildMarginEnd(mTitleContainer, mTitleContainerMarginEnd);
        }
        final ConversationViewAdapter adapter = mMessageHeaderItem.getAdapter();
        if (adapter != null) {
            mBorderView.setVisibility(adapter.isPreviousItemSuperCollapsed(mMessageHeaderItem) ? GONE : VISIBLE);
        } else {
            mBorderView.setVisibility(VISIBLE);
        }
    }

    /**
     * If an overflow menu is present in this header's layout, set the
     * visibility of "Reply" and "Reply All" actions based on a user preference.
     * Only one of those actions will be visible when an overflow is present. If
     * no overflow is present (e.g. big phone or tablet), it's assumed we have
     * plenty of screen real estate and can show both.
     */
    private void setReplyOrReplyAllVisible() {
        if (mIsDraft) {
            setChildVisibility(GONE, mReplyButton, mReplyAllButton);
            return;
        } else if (mOverflowButton == null) {
            setChildVisibility(VISIBLE, mReplyButton, mReplyAllButton);
            return;
        }

        final Account account = getAccount();
        final boolean defaultReplyAll = (account != null)
                ? account.settings.replyBehavior == UIProvider.DefaultReplyBehavior.REPLY_ALL
                : false;
        setChildVisibility(defaultReplyAll ? GONE : VISIBLE, mReplyButton);
        setChildVisibility(defaultReplyAll ? VISIBLE : GONE, mReplyAllButton);
    }

    @SuppressLint("NewApi")
    private static void setChildMarginEnd(View childView, int marginEnd) {
        MarginLayoutParams mlp = (MarginLayoutParams) childView.getLayoutParams();
        if (Utils.isRunningJBMR1OrLater()) {
            mlp.setMarginEnd(marginEnd);
        } else {
            mlp.rightMargin = marginEnd;
        }
        childView.setLayoutParams(mlp);
    }

    @VisibleForTesting
    static CharSequence getRecipientSummaryText(Context context, String meEmailAddress, String myName, String[] to,
            String[] cc, String[] bcc, Map<String, Address> addressCache, VeiledAddressMatcher matcher,
            BidiFormatter bidiFormatter) {

        final RecipientListsBuilder builder = new RecipientListsBuilder(context, meEmailAddress, myName,
                addressCache, matcher, bidiFormatter);

        builder.append(to);
        builder.append(cc);
        builder.append(bcc);

        return builder.build();
    }

    /**
     * Utility class to build a list of recipient lists.
     */
    private static class RecipientListsBuilder {
        private final Context mContext;
        private final String mMeEmailAddress;
        private final String mMyName;
        private final StringBuilder mBuilder = new StringBuilder();
        private final CharSequence mComma;
        private final Map<String, Address> mAddressCache;
        private final VeiledAddressMatcher mMatcher;
        private final BidiFormatter mBidiFormatter;

        int mRecipientCount = 0;
        boolean mFirst = true;

        public RecipientListsBuilder(Context context, String meEmailAddress, String myName,
                Map<String, Address> addressCache, VeiledAddressMatcher matcher, BidiFormatter bidiFormatter) {
            mContext = context;
            mMeEmailAddress = meEmailAddress;
            mMyName = myName;
            mComma = mContext.getText(R.string.enumeration_comma);
            mAddressCache = addressCache;
            mMatcher = matcher;
            mBidiFormatter = bidiFormatter;
        }

        public void append(String[] recipients) {
            final int addLimit = SUMMARY_MAX_RECIPIENTS - mRecipientCount;
            final boolean hasRecipients = appendRecipients(recipients, addLimit);
            if (hasRecipients) {
                mRecipientCount += Math.min(addLimit, recipients.length);
            }
        }

        /**
         * Appends formatted recipients of the message to the recipient list,
         * as long as there are recipients left to append and the maximum number
         * of addresses limit has not been reached.
         * @param rawAddrs The addresses to append.
         * @param maxToCopy The maximum number of addresses to append.
         * @return {@code true} if a recipient has been appended. {@code false}, otherwise.
         */
        private boolean appendRecipients(String[] rawAddrs, int maxToCopy) {
            if (rawAddrs == null || rawAddrs.length == 0 || maxToCopy == 0) {
                return false;
            }

            final int len = Math.min(maxToCopy, rawAddrs.length);
            for (int i = 0; i < len; i++) {
                final Address email = Utils.getAddress(mAddressCache, rawAddrs[i]);
                final String emailAddress = email.getAddress();
                final String name;
                if (mMatcher != null && mMatcher.isVeiledAddress(emailAddress)) {
                    if (TextUtils.isEmpty(email.getPersonal())) {
                        // Let's write something more readable.
                        name = mContext.getString(VeiledAddressMatcher.VEILED_SUMMARY_UNKNOWN);
                    } else {
                        name = email.getSimplifiedName();
                    }
                } else {
                    // Not a veiled address, show first part of email, or "me".
                    name = mMeEmailAddress.equals(emailAddress) ? mMyName : email.getSimplifiedName();
                }

                // duplicate TextUtils.join() logic to minimize temporary allocations
                if (mFirst) {
                    mFirst = false;
                } else {
                    mBuilder.append(mComma);
                }
                mBuilder.append(mBidiFormatter.unicodeWrap(name));
            }

            return true;
        }

        public CharSequence build() {
            return mContext.getString(R.string.to_message_header, mBuilder);
        }
    }

    private void updateContactInfo() {
        if (mContactInfoSource == null || mSender == null) {
            mPhotoView.setImageToDefault();
            mPhotoView.setContentDescription(getResources().getString(R.string.contact_info_string_default));
            return;
        }

        // Set the photo to either a found Bitmap or the default
        // and ensure either the contact URI or email is set so the click
        // handling works
        String contentDesc = getResources().getString(R.string.contact_info_string,
                !TextUtils.isEmpty(mSender.getPersonal()) ? mSender.getPersonal() : mSender.getAddress());
        mPhotoView.setContentDescription(contentDesc);
        boolean photoSet = false;
        final String email = mSender.getAddress();
        final ContactInfo info = mContactInfoSource.getContactInfo(email);
        final Resources res = getResources();
        if (info != null) {
            if (info.contactUri != null) {
                mPhotoView.assignContactUri(info.contactUri);
            } else {
                mPhotoView.assignContactFromEmail(email, true /* lazyLookup */);
            }

            if (info.photo != null) {
                mPhotoView.setImageBitmap(frameBitmapInCircle(info.photo));
                photoSet = true;
            }
        } else {
            mPhotoView.assignContactFromEmail(email, true /* lazyLookup */);
        }

        if (!photoSet) {
            mPhotoView.setImageBitmap(frameBitmapInCircle(makeLetterTile(mSender.getPersonal(), email)));
        }
    }

    private Bitmap makeLetterTile(String displayName, String senderAddress) {
        if (mLetterTileProvider == null) {
            mLetterTileProvider = new LetterTileProvider(getContext());
        }

        final ImageCanvas.Dimensions dimensions = new ImageCanvas.Dimensions(mContactPhotoWidth,
                mContactPhotoHeight, ImageCanvas.Dimensions.SCALE_ONE);
        return mLetterTileProvider.getLetterTile(dimensions, displayName, senderAddress);
    }

    /**
     * Frames the input bitmap in a circle.
     */
    private static Bitmap frameBitmapInCircle(Bitmap input) {
        if (input == null) {
            return null;
        }

        // Crop the image if not squared.
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        int targetX, targetY, targetSize;
        if (inputWidth >= inputHeight) {
            targetX = inputWidth / 2 - inputHeight / 2;
            targetY = 0;
            targetSize = inputHeight;
        } else {
            targetX = 0;
            targetY = inputHeight / 2 - inputWidth / 2;
            targetSize = inputWidth;
        }

        // Create an output bitmap and a canvas to draw on it.
        Bitmap output = Bitmap.createBitmap(targetSize, targetSize, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        // Create a black paint to draw the mask.
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.BLACK);

        // Draw a circle.
        canvas.drawCircle(targetSize / 2, targetSize / 2, targetSize / 2, paint);

        // Replace the black parts of the mask with the input image.
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(input, targetX /* left */, targetY /* top */, paint);

        return output;
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        mPopup.dismiss();
        return onClick(null, item.getItemId());
    }

    @Override
    public void onClick(View v) {
        onClick(v, v.getId());
    }

    /**
     * Handles clicks on either views or menu items. View parameter can be null
     * for menu item clicks.
     */
    public boolean onClick(final View v, final int id) {
        if (mMessage == null) {
            LogUtils.i(LOG_TAG, "ignoring message header tap on unbound view");
            return false;
        }

        boolean handled = true;

        // TS: zhaotianyong 2015-05-11 EMAIL BUGFIX_998470 MOD_S
        boolean mailDownloadPartial = (mMessage != null
                && mMessage.msgFlagLoaded == EmailContent.Message.FLAG_LOADED_PARTIAL);
        if (id == R.id.reply) {
            if (mailDownloadPartial) {
                //[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,08/05/2016,2635083
                Utility.showShortToast(getContext(),
                        getResources().getString(R.string.mail_not_download_completely));
                return true;
            }
            ComposeActivity.reply(getContext(), getAccount(), mMessage);
        } else if (id == R.id.reply_all) {
            if (mailDownloadPartial) {
                //[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,08/05/2016,2635083
                Utility.showShortToast(getContext(),
                        getResources().getString(R.string.mail_not_download_completely));
                return true;
            }
            ComposeActivity.replyAll(getContext(), getAccount(), mMessage);
        } else if (id == R.id.forward) {
            if (mailDownloadPartial) {
                //[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,08/05/2016,2635083
                Utility.showShortToast(getContext(),
                        getResources().getString(R.string.mail_not_download_completely));
                return true;
            }
            // TS: zhaotianyong 2015-05-11 EMAIL BUGFIX_998470 MOD_E
            // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_S
            EmailContent.Attachment[] att = EmailContent.Attachment.restoreAttachmentsWithMessageId(getContext(),
                    mMessage.id);
            boolean allAttachmentsDownloaded = ComposeActivity.allAttachmentIsDownload(att);
            //TS: zheng.zou 2015-11-03 EMAIL BUGFIX_858353 MOD_S
            boolean supportSmartForward = ComposeActivity.supportSmartForward(getAccount());
            if (!allAttachmentsDownloaded && !supportSmartForward) {
                //TS: zheng.zou 2015-11-03 EMAIL BUGFIX_858353 MOD_E
                ForwardDialogFragment f = ForwardDialogFragment.newInstance(getAccount(), mMessage);
                f.displayDialog(mCallbacks.getFragmentManager());
            } else {
                ComposeActivity.forward(getContext(), getAccount(), mMessage);
            }
            // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_E
            // TS: tianjing.su 2016-02-29 EMAIL BUGFIX-1595495 MOD_S
        } else if (id == R.id.save_group) {
            String[] fromMails = Address.addressParse(mFrom);
            String[] toMails = Address.addressParse(mTo);
            String[] ccMails = Address.addressParse(mCc);
            String[] bccMails = Address.addressParse(mBcc);
            MessageSaveGroupDialog.newInstance(fromMails, toMails, ccMails, bccMails).show(mFragmentManager,
                    DIALOG_FRAGMENT_TAG);
        }
        // TS: tianjing.su 2016-02-29 EMAIL BUGFIX-1595495 MOD_E
        else if (id == R.id.print_message) {
            printMessage();
        } else if (id == R.id.report_rendering_problem) {
            final String text = getContext().getString(R.string.report_rendering_problem_desc);
            ComposeActivity.reportRenderingFeedback(getContext(), getAccount(), mMessage,
                    text + "\n\n" + mCallbacks.getMessageTransforms(mMessage));
        } else if (id == R.id.report_rendering_improvement) {
            final String text = getContext().getString(R.string.report_rendering_improvement_desc);
            ComposeActivity.reportRenderingFeedback(getContext(), getAccount(), mMessage,
                    text + "\n\n" + mCallbacks.getMessageTransforms(mMessage));
        } else if (id == R.id.edit_draft) {
            ComposeActivity.editDraft(getContext(), getAccount(), mMessage);
        } else if (id == R.id.overflow) {
            if (mPopup == null) {
                mPopup = new PopupMenu(getContext(), v);
                mPopup.getMenuInflater().inflate(R.menu.message_header_overflow_menu, mPopup.getMenu());
                // TS: tianjing.su 2016-02-29 EMAIL BUGFIX-1595495 ADD_S
                mPopup.getMenu().findItem(R.id.save_group)
                        .setVisible(PLFUtils.getBoolean(getContext(), "feature_email_save_group"));
                // TS: tianjing.su 2016-02-29 EMAIL BUGFIX-1595495 ADD_E
                mPopup.setOnMenuItemClickListener(this);
            }
            final boolean defaultReplyAll = getAccount().settings.replyBehavior == UIProvider.DefaultReplyBehavior.REPLY_ALL;
            final Menu m = mPopup.getMenu();
            // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 MOD_S
            //Note: check whether support Reply all
            //if support, show it, not support, hide it.
            //m.findItem(R.id.reply).setVisible(defaultReplyAll);
            //m.findItem(R.id.reply_all).setVisible(!defaultReplyAll);
            // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 MOD_E
            m.findItem(R.id.reply_all).setVisible(isSupportReplyAll(mMessage));
            m.findItem(R.id.print_message).setVisible(Utils.isRunningKitkatOrLater());

            final boolean reportRendering = ENABLE_REPORT_RENDERING_PROBLEM
                    && mCallbacks.supportsMessageTransforms();
            m.findItem(R.id.report_rendering_improvement).setVisible(reportRendering);
            m.findItem(R.id.report_rendering_problem).setVisible(reportRendering);

            mPopup.show();
        } else if (id == R.id.send_date || id == R.id.hide_details || id == R.id.details_expanded_content) {
            toggleMessageDetails();
        } else if (id == R.id.upper_header) {
            toggleExpanded();
        } else if (id == R.id.show_pictures_text) {
            handleShowImagePromptClick(v);
            //[FEATURE]-MOD-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
        } else if (id == R.id.msg_remain_btn) {
            doLoadMsgBackground(mMessage);
            //[FEATURE]-MOD-END by TSCD.chao zhang
        } else {
            LogUtils.i(LOG_TAG, "unrecognized header tap: %d", id);
            handled = false;
        }

        if (handled && id != R.id.overflow) {
            Analytics.getInstance().sendMenuItemEvent(Analytics.EVENT_CATEGORY_MENU_ITEM, id, "message_header", 0);
        }

        return handled;
    }

    private void printMessage() {
        // Secure conversation view does not use a conversation view adapter
        // so it's safe to test for existence as a signal to use javascript or not.
        final boolean useJavascript = mMessageHeaderItem.getAdapter() != null;
        final Account account = getAccount();
        final Conversation conversation = mMessage.getConversation();
        final String baseUri = AbstractConversationViewFragment.buildBaseUri(getContext(), account, conversation);
        PrintUtils.printMessage(getContext(), mMessage, conversation.subject, mAddressCache,
                conversation.getBaseUri(baseUri), useJavascript);
    }

    /**
     * Set to true if the user should not be able to perform message actions
     * on the message such as reply/reply all/forward/star/etc.
     *
     * Default is false.
     */
    public void setViewOnlyMode(boolean isViewOnlyMode) {
        mIsViewOnlyMode = isViewOnlyMode;
    }

    public void setExpandable(boolean expandable) {
        mExpandable = expandable;
    }

    public void toggleExpanded() {
        if (!mExpandable) {
            return;
        }
        setExpanded(!isExpanded());

        // The snappy header will disappear; no reason to update text.
        if (!isSnappy()) {
            mSenderNameView.setText(getHeaderTitle());
            setRecipientSummary();
            setDateText();
            mSnippetView.setText(mSnippet);
        }

        //TS: Gantao 2015-06-15 EMAIL BUGFIX_1001086 MOD_S
        updateChildVisibility(true);
        //TS: Gantao 2015-06-15 EMAIL BUGFIX_1001086 MOD_E

        // Force-measure the new header height so we can set the spacer size and
        // reveal the message div in one pass. Force-measuring makes it unnecessary to set
        // mSizeChanged.
        int h = measureHeight();
        mMessageHeaderItem.setHeight(h);
        if (mCallbacks != null) {
            mCallbacks.setMessageExpanded(mMessageHeaderItem, h);
        }
    }

    private static boolean isValidPosition(int position, int size) {
        return position >= 0 && position < size;
    }

    @Override
    public void setSnappy() {
        mIsSnappy = true;
        hideMessageDetails();
    }

    private boolean isSnappy() {
        return mIsSnappy;
    }

    private void toggleMessageDetails() {
        int heightBefore = measureHeight();
        final boolean expand = (mExpandedDetailsView == null || mExpandedDetailsView.getVisibility() == GONE);
        setMessageDetailsExpanded(expand);
        updateSpacerHeight();
        if (mCallbacks != null) {
            mCallbacks.setMessageDetailsExpanded(mMessageHeaderItem, expand, heightBefore);
        }
    }

    private void setMessageDetailsExpanded(boolean expand) {
        if (expand) {
            showExpandedDetails();
        } else {
            hideExpandedDetails();
        }

        if (mMessageHeaderItem != null) {
            mMessageHeaderItem.detailsExpanded = expand;
        }
    }

    public void setMessageDetailsVisibility(int vis) {
        if (vis == GONE) {
            hideExpandedDetails();
            hideSpamWarning();
            hideShowImagePrompt();
            hideInvite();
            //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
            hideOrShowRemainMsgInfo(false);
            //[FEATURE]-Add-END by TSCD.chao zhang
            mUpperHeaderView.setOnCreateContextMenuListener(null);
        } else {
            setMessageDetailsExpanded(mMessageHeaderItem.detailsExpanded);
            // TS: xiaolin.li 2014-11-25 EMAIL READ_PLF MOD_S
            //if(getResources().getBoolean(R.bool.feature_email_account_list_on)){
            //            if(PLFUtils.getBoolean(this.getContext(), "feature_email_downloadOptions_on")){
            // TS: xiaolin.li 2014-11-25 EMAIL READ_PLF MOD_E
            // TS: GanTao 2015-12-1 EMAIL BUGFIX_1117109 MOD_S
            //No need to query databases,Just get the data when we query UI message
            //EmailContent.Message msg = EmailContent.Message.restoreMessageWithId(getContext(), mId);
            showRemainMsgInfo();
            // TS: GanTao 2015-12-1 EMAIL BUGFIX_1117109 MOD_E
            //            }
            if (mMessage.spamWarningString == null) {
                hideSpamWarning();
            } else {
                showSpamWarning();
            }
            //TS: Gantao 2015-09-18 EMAIL BUGFIX-571112 MOD
            //"Always show picture form the sender" should show for the feature <Always show images>
            if (mShowImagePrompt) {
                if (mMessageHeaderItem.getShowImages()) {
                    showImagePromptAlways(true);
                } else {
                    showImagePromptOnce();
                }
            } else {
                hideShowImagePrompt();
            }
            if (mMessage.isFlaggedCalendarInvite()) {
                showInvite();
            } else {
                hideInvite();
            }
            mUpperHeaderView.setOnCreateContextMenuListener(mEmailCopyMenu);
        }
    }

    private void hideMessageDetails() {
        setMessageDetailsVisibility(GONE);
    }

    private void hideExpandedDetails() {
        if (mExpandedDetailsView != null) {
            mExpandedDetailsView.setVisibility(GONE);
        }
        mDateView.setVisibility(VISIBLE);
        mHideDetailsView.setVisibility(GONE);
    }

    private void hideInvite() {
        if (mInviteView != null) {
            mInviteView.setVisibility(GONE);
        }
    }

    private void showInvite() {
        if (mInviteView == null) {
            mInviteView = (MessageInviteView) mInflater.inflate(R.layout.conversation_message_invite, this, false);
            mExtraContentView.addView(mInviteView);
        }
        mInviteView.bind(mMessage);
        mInviteView.setVisibility(VISIBLE);
    }

    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
    public void showRemainProgress(boolean flag) {
        // TS: Gantao 2015-06-11 EMAIL BUGFIX-1013206 MOD_S
        //        mRemainBtnView.removeAllViews();

        //        View v = mInflater.inflate(R.layout.conversation_view_fragment_remain_btn, null);
        //        TextView msgRemainBtn = (TextView) v.findViewById(R.id.msg_remain_btn);
        //        msgRemainBtn.setOnClickListener(this);  // TS: gangjin.weng 2015-04-20 EMAIL BUGFIX_940964 ADD
        //        //msgRemainBtn.setVisibility(View.GONE);
        //
        //        ProgressBar msgProgress = (ProgressBar) v.findViewById(R.id.msg_remain_loading_progress);
        //        TextView msgLoading = (TextView) v.findViewById(R.id.msg_remain_loading_text);

        makeVisible(msgRemainBtn, !flag);
        makeVisible(msgProgress, flag);
        makeVisible(msgLoading, flag);

        //        mRemainBtnView.addView(v);
        // TS: Gantao 2015-06-11 EMAIL BUGFIX-1013206 MOD_E
        hideOrShowRemainMsgInfo(true);
    }

    public void hideOrShowRemainMsgInfo(boolean show) {
        if (mRemainBtnView != null) {
            if (show) {
                mRemainBtnView.setVisibility(VISIBLE);
            } else
                mRemainBtnView.setVisibility(GONE);
        }
    }

    public void hideRemainView() {
        if (mRemainBtnView != null) {
            // TS: Gantao 2015-06-11 EMAIL BUGFIX-1013206 DEL_S
            //            mRemainBtnView.removeAllViews();
            // TS: Gantao 2015-06-11 EMAIL BUGFIX-1013206 DEL_E
            mRemainBtnView.setVisibility(GONE);
        }
    }

    public void showRemainMsgInfo() {
        // TS: Gantao 2015-06-02 EMAIL BUGFIX-998526 DEL_S
        //[BUGFIX]-Add-BEGIN by TSCD.zhangyang,08/22/2014,PR772845,
        //[Email]"Mark starred" button and "Download Remaining" button are conflicted.
        //        ProgressBar msgProgress = (ProgressBar) mRemainBtnView.findViewById(R.id.msg_remain_loading_progress);
        //        if (msgProgress != null && msgProgress.getVisibility() == View.VISIBLE) {
        //            hideOrShowRemainMsgInfo(true);  // TS: gangjin.weng 2015-04-20 EMAIL BUGFIX_940964 ADD
        //            return;
        //        }
        //[BUGFIX]-Add-END by TSCD.zhangyang
        // TS: Gantao 2015-06-02 EMAIL BUGFIX-998526 DEL_E
        // TS: Gantao 2015-06-11 EMAIL BUGFIX-1013206 MOD_S
        //        mRemainBtnView.removeAllViews();
        if (mMessage == null) {
            return;
        }
        //com.tct.emailcommon.provider.Account account = com.tct.emailcommon.provider.Account.getAccountForMessageId(getContext(),message.mId);
        if (getAccount() == null) {
            return;
        }
        if (mMessage.msgFlagLoaded == EmailContent.Message.FLAG_LOADED_PARTIAL) {
            //            View v = mInflater.inflate(R.layout.conversation_view_fragment_remain_btn, null);
            //            TextView msgRemainBtn = (TextView) v.findViewById(R.id.msg_remain_btn);
            //            TextView msgRemainBtn = (TextView) findViewById(R.id.msg_remain_btn);
            msgRemainBtn.setOnClickListener(this);

            // TS: Gantao 2016-02-17 EMAIL BUGFIX-1554314 MOD_S
            boolean isDownloadRemaining = false;
            if (mCallbacks != null) {
                isDownloadRemaining = mCallbacks.isDownloadRemaining();
            }
            makeVisible(msgRemainBtn, !isDownloadRemaining);
            makeVisible(msgProgress, isDownloadRemaining);
            makeVisible(msgLoading, isDownloadRemaining);
            // TS: Gantao 2016-02-17 EMAIL BUGFIX-1554314 MOD_E
            hideOrShowRemainMsgInfo(true);
            mPartialDownload = true;
        } else if (mMessage.msgFlagLoaded == EmailContent.Message.FLAG_LOADED_COMPLETE) {
            showRemainProgress(false);
            hideOrShowRemainMsgInfo(false);
            mPartialDownload = false;
        }
    }

    private static void makeVisible(View v, boolean visible) {
        final int visibility = visible ? View.VISIBLE : View.GONE;
        if ((v != null) && (v.getVisibility() != visibility)) {
            v.setVisibility(visibility);
        }
    }

    //[FEATURE]-Add-BEGIN by TSNJ.(li.yu),09/02/2015 for FR917870
    Handler romainHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 0:
                showRemainProgress(false);
                break;
            case 1:
                showRemainProgress(false);
                hideOrShowRemainMsgInfo(false);
                break;
            }
        }
    };

    //[FEATURE]-END by TSNJ.(li.yu),
    private void doLoadMsgBackground(final Message msg) {
        boolean isEas = false;
        Context context = getContext();
        if (!Utility.hasConnectivity(getContext())) {
            mPartialDownload = false;
            ConnectionAlertDialog.newInstance().show(mCallbacks.getFragmentManager(), "connectionalertdialog");
            return;
        }
        mPartialDownload = true;
        //[BUGFIX]-Del-BEGIN by TSCD.chaozhang,06/04/2014,PR689959
        //[Force Close][Email]The Email stop running after download the body.
        //we update the UI in another class.here is not good.
        /**showRemainProgress(true);
        int status = mCallbacks.loadSingleMessageBackground(msg);
        switch (status) {
        case LOAD_REMAIN_MESSAGE_FAIL:
         showRemainProgress(false);
         break;
        case LOAD_REMAIN_MESSAGE_SUCCESS:
         showRemainProgress(false);
         hideOrShowRemainMsgInfo(false);
         break;
        } **/
        //[BUGFIX]-Del-END by TSCD.chaozhang
        //showRemainProgress(true);
        //com.tct.emailcommon.provider.Account account = com.tct.emailcommon.provider.Account
        //      .getAccountForMessageId(context, msg.getId());
        if (getAccount() == null) {
            return;
        }
        mCallbacks.loadSingleMessageBackground(msg);
    }

    private void hideShowImagePrompt() {
        if (mImagePromptView != null) {
            mImagePromptView.setVisibility(GONE);
        }
    }

    private void showImagePromptOnce() {
        if (mImagePromptView == null) {
            mImagePromptView = (TextView) mInflater.inflate(R.layout.conversation_message_show_pics, this, false);
            mExtraContentView.addView(mImagePromptView);
            mImagePromptView.setOnClickListener(this);
        }
        mImagePromptView.setVisibility(VISIBLE);
        mImagePromptView.setText(R.string.show_images);
        mImagePromptView.setTag(SHOW_IMAGE_PROMPT_ONCE);
    }

    /**
     * Shows the "Always show pictures" message
     *
     * @param initialShowing <code>true</code> if this is the first time we are showing the prompt
     *        for "show images", <code>false</code> if we are transitioning from "Show pictures"
     */
    private void showImagePromptAlways(final boolean initialShowing) {
        if (initialShowing) {
            // Initialize the view
            showImagePromptOnce();
        }

        mImagePromptView.setText(R.string.always_show_images);
        mImagePromptView.setTag(SHOW_IMAGE_PROMPT_ALWAYS);

        if (!initialShowing) {
            // the new text's line count may differ, so update the spacer height
            updateSpacerHeight();
        }
    }

    private void hideSpamWarning() {
        if (mSpamWarningView != null) {
            mSpamWarningView.setVisibility(GONE);
        }
    }

    private void showSpamWarning() {
        if (mSpamWarningView == null) {
            mSpamWarningView = (SpamWarningView) mInflater.inflate(R.layout.conversation_message_spam_warning, this,
                    false);
            mExtraContentView.addView(mSpamWarningView);
        }

        mSpamWarningView.showSpamWarning(mMessage, mSender);
    }

    private void handleShowImagePromptClick(View v) {
        Integer state = (Integer) v.getTag();
        if (state == null) {
            return;
        }
        switch (state) {
        case SHOW_IMAGE_PROMPT_ONCE:
            if (mCallbacks != null) {
                mCallbacks.showExternalResources(mMessage);
            }
            if (mMessageHeaderItem != null) {
                mMessageHeaderItem.setShowImages(true);
            }
            //TS: Gantao 2015-09-18 EMAIL BUGFIX-571112 MOD
            //"Always show picture form the sender" should show for the feature <Always show images>
            if (mIsViewOnlyMode) {
                hideShowImagePrompt();
            } else {
                showImagePromptAlways(false);
            }
            break;
        case SHOW_IMAGE_PROMPT_ALWAYS:
            mMessage.markAlwaysShowImages(getQueryHandler(), 0 /* token */, null /* cookie */);

            if (mCallbacks != null) {
                mCallbacks.showExternalResources(mMessage.getFrom());
            }

            mShowImagePrompt = false;
            v.setTag(null);
            v.setVisibility(GONE);
            updateSpacerHeight();
            //[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,08/05/2016,2635083
            Utility.showShortToast(getContext(), R.string.always_show_images_toast);
            //Toast.makeText(getContext(), R.string.always_show_images_toast, Toast.LENGTH_SHORT)
            //        .show();
        }
    }

    private AsyncQueryHandler getQueryHandler() {
        if (mQueryHandler == null) {
            mQueryHandler = new AsyncQueryHandler(getContext().getContentResolver()) {
            };
        }
        return mQueryHandler;
    }

    /**
     * Makes expanded details visible. If necessary, will inflate expanded
     * details layout and render using saved-off state (senders, timestamp,
     * etc).
     */
    private void showExpandedDetails() {
        // lazily create expanded details view
        final boolean expandedViewCreated = ensureExpandedDetailsView();
        if (expandedViewCreated) {
            mExtraContentView.addView(mExpandedDetailsView, 0);
        }
        mExpandedDetailsView.setVisibility(VISIBLE);
        mDateView.setVisibility(GONE);
        mHideDetailsView.setVisibility(VISIBLE);
    }

    private boolean ensureExpandedDetailsView() {
        boolean viewCreated = false;
        if (mExpandedDetailsView == null) {
            View v = inflateExpandedDetails(mInflater);
            v.setOnClickListener(this);

            mExpandedDetailsView = (ViewGroup) v;
            viewCreated = true;
        }
        if (!mExpandedDetailsValid) {
            renderExpandedDetails(getResources(), mExpandedDetailsView, mMessage.viaDomain, mAddressCache,
                    getAccount(), mVeiledMatcher, mFrom, mReplyTo, mTo, mCc, mBcc,
                    mMessageHeaderItem.getTimestampFull(), getBidiFormatter());

            mExpandedDetailsValid = true;
        }
        return viewCreated;
    }

    public static View inflateExpandedDetails(LayoutInflater inflater) {
        return inflater.inflate(R.layout.conversation_message_header_details, null, false);
    }

    public void renderExpandedDetails(Resources res, View detailsView, String viaDomain,
            Map<String, Address> addressCache, Account account, VeiledAddressMatcher veiledMatcher, String[] from,
            String[] replyTo, String[] to, String[] cc, String[] bcc, CharSequence receivedTimestamp,
            BidiFormatter bidiFormatter) {
        renderEmailList(res, R.id.from_heading, R.id.from_details, from, viaDomain, detailsView, addressCache,
                account, veiledMatcher, bidiFormatter);
        renderEmailList(res, R.id.replyto_heading, R.id.replyto_details, replyTo, viaDomain, detailsView,
                addressCache, account, veiledMatcher, bidiFormatter);
        renderEmailList(res, R.id.to_heading, R.id.to_details, to, viaDomain, detailsView, addressCache, account,
                veiledMatcher, bidiFormatter);
        renderEmailList(res, R.id.cc_heading, R.id.cc_details, cc, viaDomain, detailsView, addressCache, account,
                veiledMatcher, bidiFormatter);
        renderEmailList(res, R.id.bcc_heading, R.id.bcc_details, bcc, viaDomain, detailsView, addressCache, account,
                veiledMatcher, bidiFormatter);

        // Render date
        detailsView.findViewById(R.id.date_heading).setVisibility(VISIBLE);
        final TextView date = (TextView) detailsView.findViewById(R.id.date_details);
        date.setText(receivedTimestamp);
        date.setVisibility(VISIBLE);
    }

    /**
     * Render an email list for the expanded message details view.
     */
    private void renderEmailList(Resources res, int headerId, int detailsId, String[] emails, String viaDomain,
            View rootView, Map<String, Address> addressCache, Account account, VeiledAddressMatcher veiledMatcher,
            BidiFormatter bidiFormatter) {
        if (emails == null || emails.length == 0) {
            return;
        }
        final String[] formattedEmails = new String[emails.length];
        for (int i = 0; i < emails.length; i++) {
            final Address email = Utils.getAddress(addressCache, emails[i]);
            String name = email.getPersonal();
            final String address = email.getAddress();
            // Check if the address here is a veiled address.  If it is, we need to display an
            // alternate layout
            final boolean isVeiledAddress = veiledMatcher != null && veiledMatcher.isVeiledAddress(address);
            final String addressShown;
            if (isVeiledAddress) {
                // Add the warning at the end of the name, and remove the address.  The alternate
                // text cannot be put in the address part, because the address is made into a link,
                // and the alternate human-readable text is not a link.
                addressShown = "";
                if (TextUtils.isEmpty(name)) {
                    // Empty name and we will block out the address. Let's write something more
                    // readable.
                    name = res.getString(VeiledAddressMatcher.VEILED_ALTERNATE_TEXT_UNKNOWN_PERSON);
                } else {
                    name = name + res.getString(VeiledAddressMatcher.VEILED_ALTERNATE_TEXT);
                }
            } else {
                addressShown = address;
            }
            if (name == null || name.length() == 0 || name.equalsIgnoreCase(addressShown)) {
                formattedEmails[i] = bidiFormatter.unicodeWrap(addressShown);
            } else {
                // The one downside to having the showViaDomain here is that
                // if the sender does not have a name, it will not show the via info
                if (viaDomain != null) {
                    formattedEmails[i] = res.getString(R.string.address_display_format_with_via_domain,
                            bidiFormatter.unicodeWrap(name), bidiFormatter.unicodeWrap(addressShown),
                            bidiFormatter.unicodeWrap(viaDomain));
                } else {
                    formattedEmails[i] = res.getString(R.string.address_display_format,
                            bidiFormatter.unicodeWrap(name), bidiFormatter.unicodeWrap(addressShown));
                }
            }
        }

        rootView.findViewById(headerId).setVisibility(VISIBLE);
        final TextView detailsText = (TextView) rootView.findViewById(detailsId);
        detailsText.setText(TextUtils.join("\n", formattedEmails));
        stripUnderlines(detailsText, account);
        detailsText.setVisibility(VISIBLE);
    }

    private void stripUnderlines(TextView textView, Account account) {
        final Spannable spannable = (Spannable) textView.getText();
        final URLSpan[] urls = textView.getUrls();

        for (URLSpan span : urls) {
            final int start = spannable.getSpanStart(span);
            final int end = spannable.getSpanEnd(span);
            spannable.removeSpan(span);
            //TS: rong-tang 2016-04-19 EMAIL BUGFIX-1951808 MOD_S
            span = new EmailAddressSpan(getContext(), account, span.getURL().substring(7), mContactInfoSource);
            ((EmailAddressSpan) span).setFragmentManager(mFragmentManager);
            //TS: rong-tang 2016-04-19 EMAIL BUGFIX-1951808 MOD_E
            spannable.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }

    /**
     * Returns a short plaintext snippet generated from the given HTML message
     * body. Collapses whitespace, ignores '&lt;' and '&gt;' characters and
     * everything in between, and truncates the snippet to no more than 100
     * characters.
     *
     * @return Short plaintext snippet
     */
    @VisibleForTesting
    static String makeSnippet(final String messageBody) {
        if (TextUtils.isEmpty(messageBody)) {
            return null;
        }

        final StringBuilder snippet = new StringBuilder(MAX_SNIPPET_LENGTH);

        final StringReader reader = new StringReader(messageBody);
        try {
            int c;
            while ((c = reader.read()) != -1 && snippet.length() < MAX_SNIPPET_LENGTH) {
                // Collapse whitespace.
                if (Character.isWhitespace(c)) {
                    snippet.append(' ');
                    do {
                        c = reader.read();
                    } while (Character.isWhitespace(c));
                    if (c == -1) {
                        break;
                    }
                }

                if (c == '<') {
                    // Ignore everything up to and including the next '>'
                    // character.
                    while ((c = reader.read()) != -1) {
                        if (c == '>') {
                            break;
                        }
                    }

                    // If we reached the end of the message body, exit.
                    if (c == -1) {
                        break;
                    }
                } else if (c == '&') {
                    // Read HTML entity.
                    StringBuilder sb = new StringBuilder();

                    while ((c = reader.read()) != -1) {
                        if (c == ';') {
                            break;
                        }
                        sb.append((char) c);
                    }

                    String entity = sb.toString();
                    if ("nbsp".equals(entity)) {
                        snippet.append(' ');
                    } else if ("lt".equals(entity)) {
                        snippet.append('<');
                    } else if ("gt".equals(entity)) {
                        snippet.append('>');
                    } else if ("amp".equals(entity)) {
                        snippet.append('&');
                    } else if ("quot".equals(entity)) {
                        snippet.append('"');
                    } else if ("apos".equals(entity) || "#39".equals(entity)) {
                        snippet.append('\'');
                    } else {
                        // Unknown entity; just append the literal string.
                        snippet.append('&').append(entity);
                        if (c == ';') {
                            snippet.append(';');
                        }
                    }

                    // If we reached the end of the message body, exit.
                    if (c == -1) {
                        break;
                    }
                } else {
                    // The current character is a non-whitespace character that
                    // isn't inside some
                    // HTML tag and is not part of an HTML entity.
                    snippet.append((char) c);
                }
            }
        } catch (IOException e) {
            LogUtils.wtf(LOG_TAG, e, "Really? IOException while reading a freaking string?!? ");
        }

        return snippet.toString();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Timer perf = new Timer();
        perf.start(LAYOUT_TAG);
        super.onLayout(changed, l, t, r, b);
        perf.pause(LAYOUT_TAG);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Timer t = new Timer();
        if (Timer.ENABLE_TIMER && !mPreMeasuring) {
            t.count("header measure id=" + mMessage.id);
            t.start(MEASURE_TAG);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!mPreMeasuring) {
            t.pause(MEASURE_TAG);
        }
    }

    //TS: zhaotianyong 2014-12-31 EMAIL BUGFIX_869494 ADD_S
    private void renderAttachments(boolean loaderResult) {
        final List<Attachment> attachments;
        if (mAttachmentsCursor != null && !mAttachmentsCursor.isClosed()) {
            int i = -1;
            attachments = Lists.newArrayList();
            while (mAttachmentsCursor.moveToPosition(++i)) {
                attachments.add(mAttachmentsCursor.get());
            }
        } else {
            // before the attachment loader results are in, we can still render immediately using
            // the basic info in the message's attachmentsJSON
            attachments = mMessageHeaderItem.getMessage().getAttachments();
        }
        //TS: Gantao 2015-11-10 EMAIL BUGFIX_871525 ADD_S
        //In case of NullPointerException
        if (mMessageHeaderItem == null) {
            return;
        }
        //TS: Gantao 2015-11-10 EMAIL BUGFIX_871525 ADD_E
        renderAttachments(attachments, loaderResult);
        // if message is no expanded, hide the attachment bar scroll view.
        if (!isExpanded()) {
            setChildVisibility(GONE, mAttachmentBarScorll);
        }
    }

    //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_S
    private void renderAttachments(List<Attachment> attachments, boolean loaderResult) {
        if (attachments == null || attachments.isEmpty()) {
            return;
        }

        // filter the attachments into tiled and non-tiled
        final int maxSize = attachments.size();
        final List<Attachment> barAttachments = new ArrayList<Attachment>(maxSize);
        for (Attachment attachment : attachments) {
            // TS: tao.gan 2015-08-12 EMAIL FEATURE-ID MOD_S
            //We don't show inline attachments now.
            //[Defect]-Del-BEGINbySCDTABLET.qiao-yang@tcl.com,05/12/2016,2004459,
            //if (TextUtils.isEmpty(attachment.contentId) || attachment.isStandardAttachment()) {
            barAttachments.add(attachment);
            //}
            //[Defect]-Del-ENDbySCDTABLET.qiao-yang@tcl.com
            // TS: tao.gan 2015-08-12 EMAIL FEATURE-ID MOD_E
        }

        // AM: Kexue.Geng 2015-01-02 EMAIL BUGFIX_920071 MOD_S
        // mMessageHeaderItem.getMessage().attachmentsJson = Attachment.toJSONArray(attachments);
        ConversationMessage cmg = mMessageHeaderItem.getMessage();
        if (cmg != null) {
            cmg.attachmentsJson = Attachment.toJSONArray(attachments);
        }
        // AM: Kexue.Geng 2015-01-02 EMAIL BUGFIX_920071 MOD_E
        // All attachments are inline, don't display anything.
        if (barAttachments.isEmpty()) {
            return;
        }
        renderBarAttachments(barAttachments, loaderResult);
    }

    private void renderBarAttachments(List<Attachment> barAttachments, boolean loaderResult) {
        mAttachmentBarScorll.setVisibility(VISIBLE);

        final Account account = getAccount();
        for (Attachment attachment : barAttachments) {
            renderBarAttachments(attachment, account, loaderResult);
        }
    }

    private void renderBarAttachments(Attachment attachment, Account account, boolean loaderResult) {
        final Uri id = attachment.getIdentifierUri();
        MessageAttachmentBar barAttachmentView = (MessageAttachmentBar) mAttachmentBarListLayout
                .findViewWithTag(id);

        if (barAttachmentView == null) {
            barAttachmentView = MessageAttachmentBar.inflate(mInflater, this);
            barAttachmentView.setTag(id);
            barAttachmentView.initialize(mFragmentManager);
            barAttachmentView.setThumbnailDefault(true);
            MimeType.setThumbnailBackground(barAttachmentView.getThunmbnailView(), attachment.getContentType());
            mAttachmentBarListLayout.addView(barAttachmentView);
        }
        //TS: zheng.zou 2015-12-01 EMAIL BUGFIX_1000731 MOD_S
        if (mMessageHeaderItem != null) {
            barAttachmentView.render(attachment, account, mMessageHeaderItem.getMessage(), loaderResult,
                    getBidiFormatter());
        }
        //TS: zheng.zou 2015-12-01 EMAIL BUGFIX_1000731 MOD_E
    }
    //TS: Gantao 2015-08-27 EMAIL FEATURE_ID MOD_E

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new AttachmentLoader(getContext(), mMessageHeaderItem.getMessage().attachmentListUri);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> arg0, Cursor data) {
        mAttachmentsCursor = (AttachmentCursor) data;

        if (mAttachmentsCursor == null || mAttachmentsCursor.isClosed()) {
            return;
        }

        renderAttachments(true);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> arg0) {
        mAttachmentsCursor = null;
    }

    private Integer getAttachmentLoaderId() {
        Integer id = null;
        final Message msg = mMessageHeaderItem == null ? null : mMessageHeaderItem.getMessage();
        if (msg != null && /*msg.hasAttachments && */msg.attachmentListUri != null) {//[Defect]-Del-BEGINbySCDTABLET.qiao-yang@tcl.com,05/12/2016,2004459,
            id = msg.attachmentListUri.hashCode();
        }
        return id;
    }
    //TS: zhaotianyong 2014-12-31 EMAIL BUGFIX_869494 ADD_E

}