com.tct.mail.compose.ComposeActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.tct.mail.compose.ComposeActivity.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      */
/* ----------|----------------------|----------------------|----------------- */
/* 06/09/2014|     Zhenhua Fan      |      FR 622609       |[Orange][Android  */
/*           |                      |                      |guidelines]IMAP   */
/*           |                      |                      |support           */
/* ----------|----------------------|----------------------|----------------- */
/* 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  */
/* ----------|----------------------|----------------------|----------------- */
/* 11/25/2014|       Hu Yang        |      PR-840141       |No respond click send */
/* ----------|----------------------|----------------------|----------------- */
/******************************************************************************/
/*
 ========================================================================================================
 *HISTORY
 *
 *Tag            Date         Author          Description
 *============== ============ =============== ===========================================================
 *CONFLICT-20001 2014/10/24   wenggangjin     Modify the package conflict
 *CONFLICT-20002 2014/10/24   wenggangjin     Modify the package conflict
 *CONFLICT-50012 2014/10/24   zhaotianyong    Modify the package conflict
 *BUGFIX-855270  2014/12/02   zhaotianyong    [Android5.0][Email] No Always bcc myself option in settings
 *BUGFIX-862341  2014/12/11   zhaotianyong    [Android5.0][Email] No Contacts entrance when creating a new mail
 *BUGFIX-881538  2015/01/03   chenyanhua      [Clone][Email]Replaced by another sender,have two bbc
 *BUGFIX-879794  2015/01/05   chenyanhua      [Email]Recipient smart add display error
 *BUGFIX-886455  2015/01/07   chenyanhua      [Android5.0][Email] Lost Bcc info when replace Reply with Reply all
 *BUGFIX-882161  2015-01-06   wenggangjin     [Email]Can't reply/forword/reply all some advertisement mail
 *BUGFIX-893877  2015/01/08   xiaolin.li      [Stability][Email] Cannot Send Email with monkeyrunner
 *BUGFIX-890424  2015/01/12   chenyanhua      [Android5.0][Email][Monitor]Display Sender as 'Combined view' when forward a mail
 *BUGFIX-886976  2015/01/26   xinlei.sheng    Pop up toast "Message saved as draft" after click contact icon after "TO"
 *BUGFIX-917401  2015/01/29   xiaolin.li      [Android5.0][Email][Monkey][Crash] com.tct.email crash caused by android.content.ActivityNotFoundException
 *BUGFIX-874020  2015/2/3     junwei-xu       [Android5.0][Email] Reply/Replay all/Forward does not work in landscape mode
 *BUGFIX-920087  2015-02-09   wenggangjin     [Clone][5][Email]DRM files can be added as an attachment to the message
 *BUGFIX-932308  2015/02/015   chenyanhua     [REG][Email]Priority always display previous select one
 *BUGFIX-930453  2015/02/15   peng-zhang      [Monkey][Crash]com.tct.email java.lang.IllegalStateException.
 *BUGFIX-933509  2015-02-16   zheng.zou       [REG][FC][Camera]History dimensional code sharing to email, email discovery prompted FC
 *BUGFIX-935421  2015-02-28   wenggangjin     [REG][Force close][Email]Email force close when add file attachment from Drive
 *BUGFIX-926303  2015/02/28   zhaotianyong    [ANR][Monitor][Email]Happen email ANR when add contact.
 *BUGFIX-927727  2015/03/02   chenyanhua      [REG][Email]No save notification when add recipients from contacts
 *BUGFIX-938039  2015/03/05   zhonghua.tuo    [Android5.0][Exchange] Editor interface will appear black screen after add a large number of recipients 
 *BUGFIX-927828  2015/03/06   gengkexue       [Email]The default account can't be marked
 *BUGFIX-948927  2015/03/16   zheng.zou       [Monitor][Email]Email Force Close after edit draft sometimes
 *BUGFIX-951700  2015/03/17   zhonghua.tuo    [Android5.0][Exchange][Monitor]One email in outbox and always cant sent out
 *BUGFIX-949589  2015/03/21   zheng.zou       [Email]Share video over 5M by email has some iaaue
 *BUGFIX-957057  2015/03/17   zhonghua.tuo    [Android5.0][Email]We still can forward an email when attachments are not fully downloaded
 *BUGFIX-954496  2015/03/25   zhaotianyong    [Android5.0][Email]"Failed to send mail" not display when no network.
 *BUGFIX-958270  2015/03/25   junwen-xu       [Android5.0][Email]Always CC himself when reply all under combined view.
 *BUGFIX-958270  2015/03/27   peng-zhang      [Android5.0][Email]The address in bcc field will appear again when we remove it and rotate the screen.
 *BUGFIX-963249  2015/03/31   zhaotianyong    [REG][Android5.0][Email]"Failed to send mail" display when save an email as drafts.
 *BUGFIX-968060  2015/04/03   peng-zhang      [REG][Force close][Android5.0][Email]Share files when there is no account,email FC.
 *BUGFIX-963186  2015/4/16    yanhua.chen     Android5.0][Email] [UI] Status bar does not change when selecting characters when editing a mail
 *BUGFIX-978954  2015/04/16   zhaotianyong    [Email]Can forword some unsupport attachfile when it isn't downloaded
 *BUGFIX-974962  2015/04/17   gangjin.weng    Default account option not working at Email settings.
 *BUGFIX_980239  2015/04/23   junwei-xu       [REG][Email]"From" account is defult account when tap "add" button on another account widget
 *BUGFIX-989399  2015/4/30    yanhua.chen     [Android5.0][Email]The step of adding attach file is repetitive
 *BUGFIX-991264  2015/05/06   zhaotianyong    [Email] Fail to forward mail with inner picture
 *BUGFIX-995343  2015/05/07   zhaotianyong    [Android5.0][Email]Can forward email before download remaining content.
 *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_1000343 2015/05/13   junwei-xu       [Vodafone][VF13850][2 - Serious][Email]MS_Exchange: Recipients are wrongly shown under "Cc"
 *BUGFIX_998884 2015/05/20    zhaotianyong    [Android5.0][Email][REG] Attachments are lost after switched from reply to forward a mail from Status bar
 *BUGFIX_998884 2015/05/28    zheng.zou       [Email]Attachment auto add more when change between reply/reply all/forward after rotate phone
 *BUGFIX_996919 2015/06/04    zheng.zou       [Email](new) draft auto saving & discard ui change
 *BUGFIX_1006499 2015/06/05   jian.xu         [Email]Display half keyboard on contact match list UI
 *BUGFIX_1009174 2015/06/08   zheng.zou       [Android5.0][Email]The order of To field on Reply All screen is wrong.
 *CR-996908      2015/6/8     yanhua.chen     [Email]attachment size limit unblock with toast
 *BUGFIX_1019278 2015/06/09   Gantao       [Android5.0][Email]Reply myself when on combined view.
 *BUGFIX-1024081 2015/6/15    yanhua.chen     [Android5.0][Email]Change sound when "email sent"
 *BUGFIX_1015669 2015/6/15    jian.xu         [Monitor][Force Close][Email]Happen FC when add picture attachment.
 *BUGFIX_996919 2015/06/04    zheng.zou       [Email](new) draft auto saving & discard ui change
 *BUGFIX_1025192 2015/6/17    jian.xu         [SW][Email]Interface beat when writing email
 *BUGFIX-965608  2015/06/19   junwei-xu       [REG][Android5.0][Email]Priority doesn't saved after edit one draft
 *BUGFIX_1029027 2015/6/24    Gantao            [Android 5.0][Email][REG]Lost To info when change Reply to Reply all
 *BUGFIX_1013067 2015/6/26    xujian          [Android5.0][Email]In landscape the matched email addresses searched by initial search will overlap with status bar when dragging the searched email address
 *BUGFIX-1030195 2015/06/30   junwei-xu       [GAPP][Email]The priority icon disappear after rotate MS
 *BUGFIX-1034971 2015/07/02   junwei-xu       [Android 5.0][Email]Priority icon displayed after changing "Forward" to "Reply/Reply all"
 *BUGFIX-1037000 2015/07/10   xujian          [GAPP][Email]Quick response and signature diaplay connection after rotate MS
 *FEATURE-1033148  2015/7/13  Gantao          [Android5.0][Email]"Add attachment" not display when long touch on attachment icon.s
 *BUGFIX-1029180 2015/07/17   junwei-xu       [Android L][Email] 'Message saved as draft.' toast not pops up after exiting composing mail
 *BUGFIX-1053132 2015/7/29    yanhua.chen     [Android5.0][Email]Toast display abnormally when forward an email more than 20M
 *BUGFIX-1054431 2015/7/30    jin.dong        [Email]The screen will flash unfriendly when delete the attachemnts in forward edit screen.
 *BUGFIX-1063281  2015/8/7   kaifeng.lu       [Android5.0][Email][Monkey][Crash][Monitor]com.tct.email crash:java.lang.IndexOutOfBoundsException
 *BUGFIX-1067957 2015/8/14   kaifeng.lu      [Monitor][Force close][Email]Email force close when reply email from notification
 *BUGFIX-526192  2015-09-01   junwei-xu       From and reply all must be auto display.
 *CR-540045      2015/9/1     yanhua.chen     Compose ui adjustment
 *CD_551912      2015/9/1     yanhua.chen     Compose e-mail Hint
 *BUGFIX_568681  2015/9/11    yanhua.chen     [Android L][Email]"..." appear if user don't have a signature Edit Notification
 *BUGFIX-1047612 2015/07/27   zheng.zou       [Email]Can't display all pictures of the picture attachment after send it.
 *BUGFIX-622679  2015/09/19   junwei-xu       [Android][Email]Not bcc myself when forward an email.
 *BUGFIX_569665  2015/9/19    yanhua.chen     [Android][Email]"..." also display when the signature display
 *BUGFIX_1065369 2015/8/28    lin-zhou        [UE][Email]"There's no text in the message subject" doesn't pop up when there is no suject with attachments attachted
 *BUGFIX_666151  2015/9/28    lin-zhou        [Android L][Email]The signature is displayed after rotating the handset
 *BUGFIX_571435  2015/9/28    jin.dong        [Android L][Email][Force close]Share a file via POP/IMAP account Email force close hanpped
 *BUGFIX_673904  2015/9/30    kaifeng.lu      [Android L][Email]"Message saved as draft" display after adding mail address from contact
 *BUGFIX-708877  2015/07/10   xujian          [Android L][Email]The draft not save when share files as attachment with not change the sender
 *BUGFIX_1085945 2015/9/17    jian.xu         [Monkey][Crash] com.tct.email
 *BUGFIX_718388  2015/10/15   lin-zhou        [Android L][Email][Monitor]Domain disappeared after pop up can not connect server
 *BUGFIX_1100033 2015/10/19   jin.dong        [Android L][Email] [Monkey][Crash]com.tct.email crash by java.lang.NullPointerException
 *BUGFIX-894294  2015/10/28   kaifeng.lu      [Email][VDF]MTK Platform CompatibilityEmail compose recipients contacts
 *BUGFIX-858353  2015/11/03   zheng.zou       [Email]Optimize Exchange smart-forward/smart-reply
 *BUGFIX-864427  2015/11/10   junwei-xu       [Android L][Email]Share a file via Email "From" not display current account
 *BUGFIX_861373  2015/11/11   yanhua.chen     [Android 5.0][Email]The signature display as three points display wrong place when respond inline
 *BUGFIX_821332  2015/11/12   jian.xu         [Android L][Email]The priority of draft not change in time
 *BUGFIX-863678  2015/11/13   jian.xu         [Android 5.0][Email]No detail information in foward message when forward the mail
 *TASK-869664    2015/11/30   zheng.zou       [Email]Android M Permission Upgrade
 *BUGFIX-1106881 2015/11/16   jian.xu         [Android L][Email][UE]The cursor back to beginning again after rotating screen when editing mail
 *BUGFIX-1030520 2015/12/02   zheng.zou       [Android 6.0][Email][Force close]Email force close when share contact with no contact permission if no account created
 *BUGFIX-1035228 2015/11/10   junwei-xu       [Android L][Email][SBS]The priority popup will disappear after rotate phone from portrait mode to landscape mode.
 *BUGFIX-1059178  2015/12/09  zheng.zou       [Email]"Save draft" is not gray in menu when compose email from widget.
 *BUGFIX-1181863 2015/12/22   junwei-xu       [Android 6.0][Email]The email addresses are invaild if add from contact on MTK platform
 *BUGFIX-1307962 2016/01/11   jian.xu         [Android 6.0][Email][Force close]Email will force close when share in download without storage and phone/contact permission
 *BUGFIX-1355979 2016/01/13   chao-zhang      [Android M][Email][Force close]Mutiple press UNDO Email force close
 *BUGFIX_1441004 2015/1/19    yang.mei        [Android 6.0][Email]Pop up "Message saved as draft" when add attachment without storage permission
 *BUGFIX-1496954 2016/01/25   junwei-xu       [Android 6.0][Email]contact with special character in name cannot be select
 *BUGFIX-1612750 2016/03/01   junwei-xu       [VF17110][2 - Serious][CTC][Email]'MS_Exchange: Adding contacts from phonebook as email recipients works faulty.
 *BUGFIX-1712549 2016/03/02   rong-tang       [Email][Force Close]Email happens stopped when opening one folder from image
 *BUGFIX-1761777 2016/03/16   tao.gan         [Monkey][ANR][Email]NOT RESPONDING: com.tct.email during system test
 *BUGFIX_1814252 2015/3/16    yanhua.chen     [Email]There is no prompt when share a file exceeds 10M via Email]  Edit Notification
 *BUGFIX-1783199 2016/03/23   xiangnan.zhou   [Email][Broswer]The screen will appears half black when sharing browser page by email
 *BUGFIX_1841392 2016/3/25    kaifeng.lu      [Email][Ergo]Email actual result not consistent with ergo designed.
 *BUGFIX-1863457 2016/03/28   rong-tang       [Email]The number of receivers is incorrect when reply all.
 *BUGFIX-1877378 2016/03/31   yanhua.chen     [Email]Reply/Reply all/Forward function confusion
 *BUGFIX-1840992 2016/04/06   rong-tang       [Email]The number of receivers is incorrect when reply
 *BUGFIX-1909143 2016/04/06   kaifeng.lu      [Email]DUT prompt two signature when select "Photos".
========================================================================================================
 */
package com.tct.mail.compose;

import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.LoaderManager;
import android.content.ClipData;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.CursorLoader;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.content.ActivityNotFoundException;
import android.database.Cursor;
import android.graphics.Rect;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import com.tct.emailcommon.utility.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.provider.BaseColumns;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.RemoteInput;
import android.support.v7.app.ActionBar;
import android.text.Editable;
import android.text.Html;
import android.text.SpanWatcher;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
//TS: junwei-xu 2015-2-3 EMAIL BUGFIX_874020 ADD_S
import android.view.inputmethod.InputMethodManager;
//TS: junwei-xu 2015-2-3 EMAIL BUGFIX_874020 ADD_E
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import android.provider.ContactsContract;
import android.widget.MultiAutoCompleteTextView;
import com.tct.drm.api.TctDrmManager;
import com.tct.email.EmailApplication;
import com.tct.email.Preferences;
//TS: MOD by zhaotianyong for CONFLICT_50012 START
//import com.android.common.Rfc822Validator;
//import com.android.common.contacts.DataUsageStatUpdater;
import com.tct.fw.ex.common.Rfc822Validator;
import com.tct.fw.ex.common.contacts.DataUsageStatUpdater;
//TS: MOD by zhaotianyong for CONFLICT_50012 END
import com.tct.emailcommon.mail.Address;
import com.tct.emailcommon.provider.EmailContent;
import com.tct.emailcommon.provider.EmailContent.Body;
import com.tct.emailcommon.utility.Utility;
//TS: MOD by wenggangjin for CONFLICT_20002 START
//import com.tct.ex.chips.BaseRecipientAdapter;
//import com.tct.ex.chips.DropdownChipLayouter;
//import com.tct.ex.chips.RecipientEditTextView;
import com.tct.fw.ex.chips.BaseRecipientAdapter;
import com.tct.fw.ex.chips.DropdownChipLayouter;
import com.tct.fw.ex.chips.RecipientEditTextView;
//TS: MOD by wenggangjin for CONFLICT_20002 END
import com.tct.email.R;
import com.tct.mail.ui.ActionableToastBar;
import com.tct.mail.ui.ProgressDialogFragment;
import com.tct.mail.ui.ToastBarOperation;
import com.tct.mail.utils.LogTag;
import com.tct.mail.utils.LogUtils;
import com.google.android.mail.common.html.parser.HtmlTree;
//TS: MOD by wenggangjin for CONFLICT_20001 START
//import com.google.common.annotations.VisibleForTesting;
//import com.google.common.collect.Lists;
//import com.google.common.collect.Sets;
import com.tct.fw.google.common.annotations.VisibleForTesting;
import com.tct.fw.google.common.collect.Lists;
import com.tct.mail.MailIntentService;
import com.tct.mail.analytics.Analytics;
import com.tct.mail.browse.ConfirmDialogFragment.ChangeForwardDialogFragment;
import com.tct.mail.browse.ConfirmDialogFragment.ChangeForwardDialogFragment.ChangeForwardCallback;
import com.tct.mail.browse.MessageHeaderView;
import com.tct.mail.compose.AttachmentsView.AttachmentAddedOrDeletedListener;
import com.tct.mail.compose.AttachmentsView.AttachmentFailureException;
import com.tct.mail.compose.FromAddressSpinner.OnAccountChangedListener;
import com.tct.mail.compose.QuotedTextView.RespondInlineListener;
import com.tct.mail.preferences.MailPrefs;
import com.tct.mail.providers.Account;
import com.tct.mail.providers.Attachment;
import com.tct.mail.providers.Folder;
import com.tct.mail.providers.MailAppProvider;
import com.tct.mail.providers.Message;
import com.tct.mail.providers.MessageModification;
import com.tct.mail.providers.ReplyFromAccount;
import com.tct.mail.providers.Settings;
import com.tct.mail.providers.UIProvider;
import com.tct.mail.providers.UIProvider.AccountCapabilities;
import com.tct.mail.providers.UIProvider.DraftType;
import com.tct.mail.ui.MailActivity;
import com.tct.mail.ui.WaitFragment;
import com.tct.mail.ui.AttachmentTile.AttachmentPreview;
import com.tct.mail.utils.AccountUtils;
import com.tct.mail.utils.AttachmentUtils;
import com.tct.mail.utils.ContentProviderTask;
import com.tct.mail.utils.HtmlUtils;
import com.tct.mail.utils.NotificationActionUtils;
import com.tct.mail.utils.PLFUtils;
import com.tct.mail.utils.Utils;
//TS: MOD by wenggangjin for CONFLICT_20001 END
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
//[FEATURE]-Add-BEGIN by  TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
import android.widget.ImageView;
//[FEATURE]-Add-END by TSCD.chao zhang

//[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/21/2014,FR631895(porting from FR487417)
import android.widget.ImageButton;
import android.provider.ContactsContract.Contacts;

import java.util.Iterator;
//[FEATURE]-Add-END by TSCD.chao zhang

//TS: yanhua.chen 2015-4-16 EMAIL BUGFIX_963186 ADD_S
import android.view.Window;
//TS: yanhua.chen 2015-4-16 EMAIL BUGFIX_963186 ADD_E

//TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_S
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.LinearLayout;
import com.tct.permission.BaseActivity;
import com.tct.permission.PermissionUtil;
//TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_E

//[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/22/2014,FR 631895(porting from  FR487417)

public class ComposeActivity extends BaseActivity //TS: zheng.zou 2015-10-17 EMAIL permission check MOD
        implements OnClickListener, ActionBar.OnNavigationListener, RespondInlineListener, TextWatcher,
        AttachmentAddedOrDeletedListener, OnAccountChangedListener, LoaderManager.LoaderCallbacks<Cursor>,
        TextView.OnEditorActionListener, RecipientEditTextView.RecipientEntryItemClickedListener,
        View.OnFocusChangeListener, ChangeForwardCallback {//TS: zhaotianyong 2015-05-19 EMAIL BUGFIX_988459 MOD
    /**
     * An {@link Intent} action that launches {@link ComposeActivity}, but is handled as if the
     * {@link Activity} were launched with no special action.
     */
    private static final String ACTION_LAUNCH_COMPOSE = "com.tct.mail.intent.action.LAUNCH_COMPOSE";

    // Identifiers for which type of composition this is
    public static final int COMPOSE = -1;
    public static final int REPLY = 0;
    public static final int REPLY_ALL = 1;
    public static final int FORWARD = 2;
    public static final int EDIT_DRAFT = 3;

    // Integer extra holding one of the above compose action
    protected static final String EXTRA_ACTION = "action";

    private static final String EXTRA_SHOW_CC = "showCc";
    private static final String EXTRA_SHOW_BCC = "showBcc";
    private static final String EXTRA_RESPONDED_INLINE = "respondedInline";
    private static final String EXTRA_SAVE_ENABLED = "saveEnabled";

    private static final String UTF8_ENCODING_NAME = "UTF-8";

    private static final String MAIL_TO = "mailto";

    private static final String EXTRA_SUBJECT = "subject";

    private static final String EXTRA_BODY = "body";

    /**
     * Expected to be html formatted text.
     */
    private static final String EXTRA_QUOTED_TEXT = "quotedText";

    protected static final String EXTRA_FROM_ACCOUNT_STRING = "fromAccountString";

    private static final String EXTRA_ATTACHMENT_PREVIEWS = "attachmentPreviews";

    // Extra that we can get passed from other activities
    @VisibleForTesting
    protected static final String EXTRA_TO = "to";
    private static final String EXTRA_CC = "cc";
    private static final String EXTRA_BCC = "bcc";

    /**
     * An optional extra containing a {@link ContentValues} of values to be added to
     * {@link SendOrSaveMessage#mValues}.
     */
    public static final String EXTRA_VALUES = "extra-values";

    // List of all the fields
    static final String[] ALL_EXTRAS = { EXTRA_SUBJECT, EXTRA_BODY, EXTRA_TO, EXTRA_CC, EXTRA_BCC,
            EXTRA_QUOTED_TEXT };

    private static final String LEGACY_WEAR_EXTRA = "com.google.android.wearable.extras";

    /**
     * Constant value for the threshold to use for auto-complete suggestions
     * for the to/cc/bcc fields.
     */
    private static final int COMPLETION_THRESHOLD = 1;

    private static SendOrSaveCallback sTestSendOrSaveCallback = null;
    // Map containing information about requests to create new messages, and the id of the
    // messages that were the result of those requests.
    //
    // This map is used when the activity that initiated the save a of a new message, is killed
    // before the save has completed (and when we know the id of the newly created message).  When
    // a save is completed, the service that is running in the background, will update the map
    //
    // When a new ComposeActivity instance is created, it will attempt to use the information in
    // the previously instantiated map.  If ComposeActivity.onCreate() is called, with a bundle
    // (restoring data from a previous instance), and the map hasn't been created, we will attempt
    // to populate the map with data stored in shared preferences.
    // FIXME: values in this map are never read.
    private static ConcurrentHashMap<Integer, Long> sRequestMessageIdMap = null;
    /**
     * Notifies the {@code Activity} that the caller is an Email
     * {@code Activity}, so that the back behavior may be modified accordingly.
     *
     * @see #onAppUpPressed
     */
    public static final String EXTRA_FROM_EMAIL_TASK = "fromemail";
    //TS: zheng.zou 2015-12-09 EMAIL BUGFIX_1059178 ADD_S
    //notify the Intent is from Widget
    public static final String EXTRA_FROM_EMAIL_WIDGET = "fromwidget";
    //TS: zheng.zou 2015-12-09 EMAIL BUGFIX_1059178 ADD_E

    public static final String EXTRA_ATTACHMENTS = "attachments";

    /** If set, we will clear notifications for this folder. */
    public static final String EXTRA_NOTIFICATION_FOLDER = "extra-notification-folder";
    public static final String EXTRA_NOTIFICATION_CONVERSATION = "extra-notification-conversation";

    //  If this is a reply/forward then this extra will hold the original message
    private static final String EXTRA_IN_REFERENCE_TO_MESSAGE = "in-reference-to-message";
    // If this is a reply/forward then this extra will hold a uri we must query
    // to get the original message.
    protected static final String EXTRA_IN_REFERENCE_TO_MESSAGE_URI = "in-reference-to-message-uri";
    // If this is an action to edit an existing draft message, this extra will hold the
    // draft message
    private static final String ORIGINAL_DRAFT_MESSAGE = "original-draft-message";
    private static final String END_TOKEN = ", ";
    private static final String LOG_TAG = LogTag.getLogTag();
    // Request numbers for activities we start
    private static final int RESULT_PICK_ATTACHMENT = 1;
    private static final int RESULT_CREATE_ACCOUNT = 2;
    //[BUGFIX]-Mod-BEGIN by TSCD.chao zhang,05/29/2014,PR 682725 [Email]Can't add mail address from contact option.
    private static final int ACTIVITY_REQUEST_PICK_CONTACT_TO = 3;
    private static final int ACTIVITY_REQUEST_PICK_CONTACT_CC = 4;
    private static final int ACTIVITY_REQUEST_PICK_CONTACT_BCC = 5;

    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/21/2014,FR631895(porting from FR487417)
    private static final int EMAIL_NAME_INDEX = 0;
    private static final int EMAIL_ADDRESS_INDEX = 1;
    private static final char EMAIL_QUOTE_START = '<';
    private static final char EMAIL_QUOTE_END = '>';
    private static final char EMAIL_SPACE = ' ';
    //TS: junwei-xu 2016-03-01 EMAIL BUGFIX-1612750 ADD_S
    private static final String ACTION_MULTI_PICK_EMAIL_QUALCOMM = "com.android.contacts.action.MULTI_PICK_EMAIL";
    private static final String ACTION_MULTI_PICK_EMAIL_MTK = "android.intent.action.contacts.list.PICKMULTIEMAILS";
    //TS: junwei-xu 2016-03-01 EMAIL BUGFIX-1612750 ADD_E
    //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 ADD_S
    public static final String EXTRA_PICK_DATA_RESULT = "com.mediatek.contacts.list.pickdataresult";
    //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 ADD_E
    private static final String ACTION_SAVE_EMAIL_TO_GROUP = "com.tct.email.MessageCompose.peopleActivity";
    public static final String EXTRA_PICK_EMAIL_BUNDLE = "contacts_extra_pick_email_bundle";
    //[FEATURE]-Add-END by TSCD.chao zhang

    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/22/2014,FR 631895(porting from  FR487417)
    private boolean mAddBccBySetting = false;
    //[FEATURE]-Add-END by TSCD.chao zhang

    //[BUGFIX]-Mod-END by TSCD.chao zhang
    // TODO(mindyp) set mime-type for auto send?
    public static final String AUTO_SEND_ACTION = "com.tct.mail.action.AUTO_SEND";

    private static final String EXTRA_SELECTED_REPLY_FROM_ACCOUNT = "replyFromAccount";
    private static final String EXTRA_REQUEST_ID = "requestId";
    private static final String EXTRA_FOCUS_SELECTION_START = "focusSelectionStart";
    private static final String EXTRA_FOCUS_SELECTION_END = "focusSelectionEnd";
    private static final String EXTRA_MESSAGE = "extraMessage";
    private static final int REFERENCE_MESSAGE_LOADER = 0;
    private static final int LOADER_ACCOUNT_CURSOR = 1;
    private static final int INIT_DRAFT_USING_REFERENCE_MESSAGE = 2;
    private static final String EXTRA_SELECTED_ACCOUNT = "selectedAccount";
    private static final String TAG_WAIT = "wait-fragment";
    private static final String MIME_TYPE_ALL = "*/*";
    private static final String MIME_TYPE_PHOTO = "image/*";

    //[FEATURE]-Add-BEGIN by TSNJ,Zhenhua.Fan,06/11/2014,FR-622609
    /** If the intent is sent from draft view screen.  */
    public static final String EXTRA_FROM_DRAFT_VIEW = "from_view";
    //[FEATURE]-Add-END by TSNJ,Zhenhua.Fan
    private static final String KEY_INNER_SAVED_STATE = "compose_state";
    // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 ADD_S
    private static final String KEY_PRIORITY_SAVED_STATE = "compose_priority_state";
    // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 ADD_E

    // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_S
    private static final String KEY_SAVED_STATE_TEXT_CHANGED = "compose_state_text_changed";
    private static final String KEY_SAVED_STATE_ATTACHMENT_CHANGED = "compose_state_attachment_changed";
    private static final String KEY_SAVED_STATE_REPLY_FROM_CHANGED = "compose_state_reply_from_changed";
    private static final String KEY_SAVED_STATE_PRIORITY_CHANGED = "compose_state_priority_changed";
    // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_E
    //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_S
    private static final String KEY_SAVED_STATE_ATTLARGEWARNING_CHANGED = "compose_state_attLargeWarning_changed";
    //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_E
    //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_S
    private static final String KEY_SAVED_STATE_ISCLICKICON_CHANGED = "compose_state_isClickIcon_changed";
    private static final String KEY_SAVED_STATE_CHANGEACCOUNT_CHANGED = "compose_state_changeAccount_changed";
    private static final String KEY_SAVED_STATE_EDITDRAFT_CHANGED = "compose_state_editDraft_changed";
    //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_E
    //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 ADD_S
    private static final String PLATFORM_QUALCOMM = "0";
    private static final String PLATFORM_MTK = "1";
    //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 ADD_E

    /**
     * A single thread for running tasks in the backgnd.
     */
    private final static Handler SEND_SAVE_TASK_HANDLER;
    static {
        HandlerThread handlerThread = new HandlerThread("Send Message Task Thread");
        handlerThread.start();

        SEND_SAVE_TASK_HANDLER = new Handler(handlerThread.getLooper());
    }
    public static final String DRAFT_SAVED_ACTION = "com.tct.mail.action.DRAFT_SAVED_ACTION"; //TS: zheng.zou 2015-03-18 EMAIL FEATURE_996919 ADD

    private ScrollView mScrollView;
    private RecipientEditTextView mTo;
    private RecipientEditTextView mCc;
    private RecipientEditTextView mBcc;
    //TS: rong-tang 2016-04-06 EMAIL-1840992 ADD_S
    private ImageButton mToPicker;
    private ImageButton mCcPicker;
    private ImageButton mBccPicker;
    //TS: rong-tang 2016-04-06 EMAIL-1840992 ADD_E
    private View mCcBccButton;

    //[BUGFIX]-Add-BEGIN by SCDTABLET.weiwei.huang,05/03/2016,2013739,
    //[Email]Add mail contact icon display is not consistent
    private View bccButtonImg;
    private View ccButtonImg;
    //[BUGFIX]-Add-END by SCDTABLET.weiwei.huang

    private CcBccView mCcBccView;
    private AttachmentsView mAttachmentsView;
    protected Account mAccount;
    protected ReplyFromAccount mReplyFromAccount;
    private Settings mCachedSettings;
    private Rfc822Validator mValidator;
    private TextView mSubject;
    // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_S
    private LinearLayout mFromRow;
    private boolean mSupportReplyAll = false;
    // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_E

    private ComposeModeAdapter mComposeModeAdapter;
    protected int mComposeMode = -1;
    private boolean mForward;
    private QuotedTextView mQuotedTextView;
    protected EditText mBodyView;
    protected ImageButton mBodySignature;
    private View mFromStatic;
    private TextView mFromStaticText;
    private View mFromSpinnerWrapper;
    @VisibleForTesting
    protected FromAddressSpinner mFromSpinner;
    protected boolean mAddingAttachment;
    private boolean mAttachmentsChanged;
    private boolean mTextChanged;
    private boolean mReplyFromChanged;
    private MenuItem mSave;
    @VisibleForTesting
    protected Message mRefMessage;
    private long mDraftId = UIProvider.INVALID_MESSAGE_ID;
    private Message mDraft;
    private ReplyFromAccount mDraftAccount;
    private final Object mDraftLock = new Object();

    /**
     * Boolean indicating whether ComposeActivity was launched from a Gmail controlled view.
     */
    private boolean mLaunchedFromEmail = false;
    private RecipientTextWatcher mToListener;
    private RecipientTextWatcher mCcListener;
    private RecipientTextWatcher mBccListener;
    private Uri mRefMessageUri;
    private boolean mShowQuotedText = false;
    protected Bundle mInnerSavedState;
    private ContentValues mExtraValues = null;
    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
    private boolean mProrityChanged;
    //[FEATURE]-Add-END by TSCD.chao zhang
    //[FEATURE]-Add-BEGIN by TSNJ,Zhenhua.Fan,06/11/2014,FR-622609
    private boolean isFromView;
    //[FEATURE]-Add-END by TSNJ,Zhenhua.Fan

    // Array of the outstanding send or save tasks.  Access is synchronized
    // with the object itself
    /* package for testing */
    @VisibleForTesting
    public final ArrayList<SendOrSaveTask> mActiveTasks = Lists.newArrayList();
    // FIXME: this variable is never read. related to sRequestMessageIdMap.
    private int mRequestId;
    private String mSignature;
    private Account[] mAccounts;
    private boolean mRespondedInline;
    private boolean mPerformedSendOrDiscard = false;
    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
    private int mPriorityFlag = Message.FLAG_PRIORITY_NORMAL;
    private ImageView mPriorityIcon;
    //[BUGFIX]-Add-BEGIN by TCTNJ.wenlu.wu,12/03/2014,PR-857886
    private final int ADD_ATTACHMENT_MSG = 1001;
    // TS: jian.xu 2016-01-11 EMAIL BUGFIX-1307962 ADD_S
    private final int ADD_ATTACHMENT_MSG_ERROR = 1002;
    // TS: jian.xu 2016-01-11 EMAIL BUGFIX-1307962 ADD_E
    //TS: xinlei.sheng 2015-01-26 EMAIL FIXBUG_886976 ADD
    private boolean mLaunchContact = false;
    // TS: zhaotianyong 2015-02-28 EMAIL BUGFIX-926303 ADD_S
    public static final int RECIPIENT_MAX_NUMBER = 500;
    // TS: zhaotianyong 2015-02-28 EMAIL BUGFIX-926303 ADD_E
    // TS: zhaotianyong 2015-03-31 EMAIL BUGFIX-963249 ADD_S
    private boolean doSend = false;
    // TS: zhaotianyong 2015-03-31 EMAIL BUGFIX-963249 ADD_E
    // TS: zhaotianyong 2015-05-20 EMAIL BUGFIX-998884 ADD_S
    private boolean allAttachmentsLoad = false;
    // TS: zhaotianyong 2015-05-20 EMAIL BUGFIX-998884 ADD_E
    // TS: Gantao 2015-06-09 EMAIL BUGFIX-1019278 ADD_S
    private boolean mSelectedAccountUnusual = false;
    // TS: Gantao 2015-06-09 EMAIL BUGFIX-1019278 ADD_E

    // TS: junwei-xu 2015-03-25 EMAIL BUGFIX-958270 ADD_S
    private ReplyRecipientsListener mReplyRecipientslistener;
    private ActionableToastBar mToastBar; //TS: jin.dong 2015-12-17 EMAIL BUGFIX_1170083 ADD

    private interface ReplyRecipientsListener {
        public void addCCRecipients();
    }
    // TS: junwei-xu 2015-03-25 EMAIL BUGFIX-958270 ADD_E

    // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_S
    private final int SET_LISTPOPUPWINDOW_HEIGHT = 1003;
    // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_E

    // TS: Gantao 2015-06-16 EMAIL BUGFIX-1019238 ADD_S
    private ReplyToRecipientsListener mReplyToRecipientsLisnter;

    private interface ReplyToRecipientsListener {
        public void addToRecipients();
    }
    // TS: Gantao 2015-06-16 EMAIL BUGFIX-1019238 ADD_E

    //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_S
    private boolean attLargeWarning = true;
    //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_E
    //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_S
    //private boolean mIsClickIcon = false;//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
    private boolean mChangeAccount = false;
    //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_E

    // TS: yanhua.chen 2015-9-19 EMAIL BUGFIX_569665 ADD_S
    private boolean mIsSaveDraft = false;
    private boolean mEditDraft = false;
    // TS: yanhua.chen 2015-9-19 EMAIL BUGFIX_569665 ADD_E

    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(android.os.Message msg) {
            super.handleMessage(msg);
            int id = msg.what;
            switch (id) {
            case ADD_ATTACHMENT_MSG:
                Attachment a = (Attachment) msg.obj;
                if (a != null) {
                    try {
                        long size = mAttachmentsView.addAttachment(mAccount, a, false);//TS: yanhua.chen 2015-6-8 EMAIL CR_996908 MOD
                        //TS: yanhua.chen 2016-3-16 EMAIL BUGFIX_1814252 ADD_S
                        if (size > Settings.DEFAULT_MID_ATTACHMENT_SIZE && attLargeWarning) {
                            attLargeWarning = false;
                            showErrorToast(getResources().getString(R.string.too_large_to_mid_attach_additional,
                                    AttachmentUtils.convertToHumanReadableSize(ComposeActivity.this,
                                            Settings.DEFAULT_MID_ATTACHMENT_SIZE)));
                        }
                        //TS: yanhua.chen 2016-3-16 EMAIL BUGFIX_1814252 ADD_E
                        // TS: jian.xu 2015-10-12 EMAIL BUGFIX-708877 MOD_S
                        if (size > 0) {
                            mAttachmentsChanged = true;
                            updateSaveUi();
                            Analytics.getInstance().sendEvent("send_intent_attachment",
                                    Utils.normalizeMimeType(a.getContentType()), null, size);
                        }
                        // TS: jian.xu 2015-10-12 EMAIL BUGFIX-708877 MOD_E
                    } catch (AttachmentFailureException e) {
                        LogUtils.e(LOG_TAG, e, "Error adding attachment");
                        showAttachmentTooBigToast(e.getErrorRes());
                    }
                }
                break;
            // TS: jian.xu 2016-01-11 EMAIL BUGFIX-1307962 ADD_S
            case ADD_ATTACHMENT_MSG_ERROR:
                int errorRes = (int) msg.obj;
                showAttachmentTooBigToast(errorRes);
                break;
            // TS: jian.xu 2016-01-11 EMAIL BUGFIX-1307962 ADD_E
            // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_S
            case SET_LISTPOPUPWINDOW_HEIGHT:
                // TS: jian.xu 2015-06-17 EMAIL BUGFIX-1025192 ADD_S
                //                int height = getListHeight();
                //                mTo.setDropDownHeight(height);
                //                mCc.setDropDownHeight(height);
                //                mBcc.setDropDownHeight(height);
                //                mTo.getAdapter().notifyDataSetChanged();
                //                mCc.getAdapter().notifyDataSetChanged();
                //                mBcc.getAdapter().notifyDataSetChanged();

                // TS: Gantao 2015-08-26 EMAIL BUGFIX-1075004 ADD_S
                if (mTo == null || mCc == null || mBcc == null) {
                    return;
                }
                // TS: Gantao 2015-08-26 EMAIL BUGFIX-1075004 ADD_E
                RecipientEditTextView textView = null;
                if (mTo.isFocused()) {
                    textView = mTo;
                } else if (mCc.isFocused()) {
                    textView = mCc;
                } else if (mBcc.isFocused()) {
                    textView = mBcc;
                }
                if (textView == null)
                    return;
                //TS: kaifeng.lu 2015-8-14 EMAIL BUGFIX_1067957 ADD_S
                if (textView.getAdapter() == null)
                    return;
                //TS: kaifeng.lu 2015-8-14 EMAIL BUGFIX_1067957 ADD_E
                int count = textView.getAdapter().getCount();
                if (count == 0) {
                    return;
                }
                int height = getListHeight();
                textView.setDropDownHeight(height);
                textView.getAdapter().notifyDataSetChanged();
                // TS: jian.xu 2015-06-17 EMAIL BUGFIX-1025192 ADD_E
                break;
            // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_E
            default:
                break;
            }
        }
    };
    //[BUGFIX]-Add-END by TCTNJ.wenlu.wu,12/03/2014,PR-857886

    //TS: junwei-xu 2015-12-04 EMAIL BUGFIX-1035228 MOD_S
    public static class PrioritySelectFragment extends DialogFragment {

        public static final String TAG = "PRIORITY_SELECT_DIALOG";
        private static final String ARGS_PRIORITY_INDEX = "PRIORITY_INDEX";
        private int mPriorityIndex = 0;

        public static PrioritySelectFragment newInstance(int priorityIndex) {
            Bundle bundle = new Bundle();
            bundle.putInt(ARGS_PRIORITY_INDEX, priorityIndex);
            PrioritySelectFragment dialog = new PrioritySelectFragment();
            dialog.setArguments(bundle);
            return dialog;
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            mPriorityIndex = this.getArguments().getInt(ARGS_PRIORITY_INDEX, 1);
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
                    .setTitle(R.string.set_priority_dialog_title)
                    .setSingleChoiceItems(R.array.set_priority_dialog_options, mPriorityIndex,
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int which) {
                                    if (getActivity() instanceof ComposeActivity) {
                                        ((ComposeActivity) getActivity()).changePriority(dialog, which);
                                    }
                                }
                            })
                    .setNegativeButton(R.string.cancel_action, null);
            return builder.create();
        }
    }

    public void changePriority(DialogInterface dialog, int which) {

        if (mDraft != null) {
            mDraft.mPriority = getPriorityOptionValue(which);
            // TS: Gantao 2015-12-17 EMAIL BUGFIX_1176396 MOD_S
            //Set the icon view by ourself avoid the UI issue
            //TODO : Set the icon view any where use the method.
            //            mPriorityIcon.setImageLevel(mDraft.mPriority);
            setPriorityIcon(mDraft.mPriority);
        } else {
            //            mPriorityIcon.setImageLevel(getPriorityOptionValue(which));
            setPriorityIcon(getPriorityOptionValue(which));
            // TS: Gantao 2015-12-17 EMAIL BUGFIX_1176396 MOD_E
        }
        mPriorityFlag = getPriorityOptionValue(which);
        mProrityChanged = true;
        // TS: jian.xu 2015-11-12 EMAIL BUGFIX-821332 ADD_S
        updateSaveUi();
        // TS: jian.xu 2015-11-12 EMAIL BUGFIX-821332 ADD_E
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 DEL_S
        // Note: we don't use sharedpreferences to save priority, no need to save it
        /*
        //TS: junwei-xu 2015-2-3 EMAIL BUGFIX_874020 ADD_S
        SharedPreferences.Editor editor = getSharedPreferences("PriorityFlag", Context.MODE_PRIVATE).edit();
        editor.putInt("mPriorityFlag", mPriorityFlag);
        editor.commit();
        //TS: junwei-xu 2015-2-3 EMAIL BUGFIX_874020 ADD_E
        */
        //TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 DEL_E
        dialog.dismiss();
    }

    private void setPriority() {
        if (mDraft != null) {
            mPriorityFlag = mDraft.mPriority;
        }
        PrioritySelectFragment.newInstance(getPriorityOptionIndex(mPriorityFlag)).show(getFragmentManager(),
                PrioritySelectFragment.TAG);
    }
    //TS: junwei-xu 2015-12-04 EMAIL BUGFIX-1035228 MOD_E

    private int getPriorityOptionIndex(int value) {
        int[] values = getResources().getIntArray(R.array.set_priority_dialog_options_values);
        for (int i = 0; i < values.length; i++) {
            if (values[i] == value) {
                return i;
            }
        }

        return -1;
    }

    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

    private final HtmlTree.ConverterFactory mSpanConverterFactory = new HtmlTree.ConverterFactory() {
        @Override
        public HtmlTree.Converter<Spanned> createInstance() {
            return getSpanConverter();
        }
    };

    /**
     * Can be called from a non-UI thread.
     */
    public static void editDraft(Context launcher, Account account, Message message) {
        launch(launcher, account, message, EDIT_DRAFT, null, null, null, null, null /* extraValues */);
    }

    //[FEATURE]-Add-BEGIN by TSNJ,Zhenhua.Fan,06/11/2014,FR-622609
    public static void actionEditDraftFromDraftViewScreen(Context launcher, Account account, Message message) {

        Intent intent = new Intent(launcher, ComposeActivity.class);
        intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
        intent.putExtra(EXTRA_ACTION, EDIT_DRAFT);
        intent.putExtra(Utils.EXTRA_ACCOUNT, account);
        intent.putExtra(EXTRA_FROM_DRAFT_VIEW, true);
        intent.putExtra(ORIGINAL_DRAFT_MESSAGE, message);

        launcher.startActivity(intent);
    }

    //[FEATURE]-Add-END by TSNJ,Zhenhua.Fan
    /**
     * Can be called from a non-UI thread.
     */
    public static void compose(Context launcher, Account account) {
        launch(launcher, account, null, COMPOSE, null, null, null, null, null /* extraValues */);
    }

    /**
     * Can be called from a non-UI thread.
     */
    public static void composeToAddress(Context launcher, Account account, String toAddress) {
        launch(launcher, account, null, COMPOSE, toAddress, null, null, null, null /* extraValues */);
    }

    /**
     * Can be called from a non-UI thread.
     */
    public static void composeWithExtraValues(Context launcher, Account account, String subject,
            final ContentValues extraValues) {
        launch(launcher, account, null, COMPOSE, null, null, null, subject, extraValues);
    }

    /**
     * Can be called from a non-UI thread.
     */
    public static Intent createReplyIntent(final Context launcher, final Account account, final Uri messageUri,
            final boolean isReplyAll) {
        return createActionIntent(launcher, account, messageUri, isReplyAll ? REPLY_ALL : REPLY);
    }

    /**
     * Can be called from a non-UI thread.
     */
    public static Intent createForwardIntent(final Context launcher, final Account account, final Uri messageUri) {
        return createActionIntent(launcher, account, messageUri, FORWARD);
    }

    private static Intent createActionIntent(final Context context, final Account account, final Uri messageUri,
            final int action) {
        final Intent intent = new Intent(ACTION_LAUNCH_COMPOSE);
        intent.setPackage(context.getPackageName());

        updateActionIntent(account, messageUri, action, intent);

        return intent;
    }

    @VisibleForTesting
    static Intent updateActionIntent(Account account, Uri messageUri, int action, Intent intent) {
        intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
        intent.putExtra(EXTRA_ACTION, action);
        intent.putExtra(Utils.EXTRA_ACCOUNT, account);
        intent.putExtra(EXTRA_IN_REFERENCE_TO_MESSAGE_URI, messageUri);

        return intent;
    }

    /**
     * Can be called from a non-UI thread.
     */
    public static void reply(Context launcher, Account account, Message message) {
        launch(launcher, account, message, REPLY, null, null, null, null, null /* extraValues */);
    }

    /**
     * Can be called from a non-UI thread.
     */
    public static void replyAll(Context launcher, Account account, Message message) {
        launch(launcher, account, message, REPLY_ALL, null, null, null, null, null /* extraValues */);
    }

    /**
     * Can be called from a non-UI thread.
     */
    public static void forward(Context launcher, Account account, Message message) {
        launch(launcher, account, message, FORWARD, null, null, null, null, null /* extraValues */);
    }

    public static void reportRenderingFeedback(Context launcher, Account account, Message message, String body) {
        launch(launcher, account, message, FORWARD, "android-gmail-readability@google.com", body, null, null,
                null /* extraValues */);
    }

    private static void launch(Context context, Account account, Message message, int action, String toAddress,
            String body, String quotedText, String subject, final ContentValues extraValues) {
        //TS: xujian 2015-06-23 EMAIL BUGFIX_1015657 MOD_S
        if (message != null && message.bodyHtml != null && message.bodyHtml.length() > 1024 * 5) {
            message.bodyHtml = "";
            LogUtils.d("Email", "test---launch---bodyHtml set to empty");
        }
        if (message != null && message.bodyText != null && message.bodyText.length() > 1024 * 5) {
            message.bodyText = "";
            LogUtils.d("Email", "test---launch---bodyText set to empty");
        }
        //TS: xujian 2015-06-23 EMAIL BUGFIX_1015657 MOD_E
        //Note: intent can not take too much extra data, so not take these two string in extra data.
        if (message != null) {
            message.bodyHtmlLinkify = "";
            message.bodyTextLinkify = "";
        }
        Intent intent = new Intent(ACTION_LAUNCH_COMPOSE);
        intent.setPackage(context.getPackageName());
        intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
        intent.putExtra(EXTRA_ACTION, action);
        intent.putExtra(Utils.EXTRA_ACCOUNT, account);
        if (action == EDIT_DRAFT) {
            intent.putExtra(ORIGINAL_DRAFT_MESSAGE, message);
        } else {
            intent.putExtra(EXTRA_IN_REFERENCE_TO_MESSAGE, message);
        }
        if (message != null) {
            LogUtils.d("Email",
                    String.format("test---launch---action=%d messageId=%d conversationUri=%s uri=%s subject=%s",
                            action, message.id, message.conversationUri, message.uri, message.subject));
        }
        //TS: wenggangjin 2015-01-06 EMAIL BUGFIX_882161 MOD_S
        //        if(message != null && message.bodyHtml != null && message.bodyHtml.length() > 1024*10){
        //            message.bodyHtml = "";
        //            Log.d("Email","test---launch---bodyHtml set to empty");
        //        }
        //TS: wenggangjin 2015-01-06 EMAIL BUGFIX_882161 MOD_E
        if (toAddress != null) {
            intent.putExtra(EXTRA_TO, toAddress);
        }
        if (body != null) {
            intent.putExtra(EXTRA_BODY, body);
        }
        if (quotedText != null) {
            intent.putExtra(EXTRA_QUOTED_TEXT, quotedText);
        }
        if (subject != null) {
            intent.putExtra(EXTRA_SUBJECT, subject);
        }
        if (extraValues != null) {
            LogUtils.d(LOG_TAG, "Launching with extraValues: %s", extraValues.toString());
            intent.putExtra(EXTRA_VALUES, extraValues);
        }
        if (action == COMPOSE) {
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
        } else if (message != null) {
            //TS: jin.dong 2015-10-19 EMAIL BUGFIX_1100033 ADD_S
            if (message.uri == null) {
                LogUtils.e(LOG_TAG,
                        "what ? message's uri is null? want launch ComposeActivity ? do nothing is the right thing!!!");
                return;
            }
            //TS: jin.dong 2015-10-19 EMAIL BUGFIX_1100033 ADD_E
            //TS: yanhua.chen 2016-03-31 EMAIL BUGFIX_1877378 ADD_S
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
            //TS: yanhua.chen 2016-03-31 EMAIL BUGFIX_1877378 ADD_E
            intent.setData(Utils.normalizeUri(message.uri));
        }

        // TS: xiaolin.li 2015-01-29 EMAIL BUGFIX-917401 ADD_S
        //context.startActivity(intent);
        try {
            context.startActivity(intent);
        } catch (Exception e) {
            LogUtils.e(LOG_TAG, "Launch compose activity err.");
        }
        // TS: xiaolin.li 2015-01-29 EMAIL BUGFIX-917401 ADD_E
    }

    public static void composeMailto(Context context, Account account, Uri mailto) {
        final Intent intent = new Intent(Intent.ACTION_VIEW, mailto);
        intent.setPackage(context.getPackageName());
        intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
        intent.putExtra(Utils.EXTRA_ACCOUNT, account);
        if (mailto != null) {
            intent.setData(Utils.normalizeUri(mailto));
        }
        context.startActivity(intent);
    }

    //AM: peng-zhang 2015-02-27 EMAIL BUGFIX_955421 MOD_S
    private String bcc_text;
    private Boolean Changed = true;

    //AM: peng-zhang 2015-02-27 EMAIL BUGFIX_955421 MOD_E
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //TS: zheng.zou 2015-12-03 EMAIL BUGFIX_1030520 ADD_S
        if (mHasNoPermission) {
            return;
        }
        //TS: zheng.zou 2015-12-03 EMAIL BUGFIX_1030520 ADD_E
        //AM: peng-zhang 2015-02-27 EMAIL BUGFIX_955421 MOD_S
        if (savedInstanceState != null) {
            bcc_text = savedInstanceState.getString("BCC_SAVE");
            Changed = savedInstanceState.getBoolean("BCC_CHANGED");
            // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 ADD_S
            mPriorityFlag = savedInstanceState.getInt(KEY_PRIORITY_SAVED_STATE);
            // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 ADD_E
            // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_S
            mTextChanged = savedInstanceState.getBoolean(KEY_SAVED_STATE_TEXT_CHANGED);
            mAttachmentsChanged = savedInstanceState.getBoolean(KEY_SAVED_STATE_ATTACHMENT_CHANGED);
            mReplyFromChanged = savedInstanceState.getBoolean(KEY_SAVED_STATE_REPLY_FROM_CHANGED);
            mProrityChanged = savedInstanceState.getBoolean(KEY_SAVED_STATE_PRIORITY_CHANGED);
            // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_E
            //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_S
            attLargeWarning = savedInstanceState.getBoolean(KEY_SAVED_STATE_ATTLARGEWARNING_CHANGED);
            //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_E
            //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_S
            //mIsClickIcon = savedInstanceState.getBoolean(KEY_SAVED_STATE_ISCLICKICON_CHANGED);//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
            mChangeAccount = savedInstanceState.getBoolean(KEY_SAVED_STATE_CHANGEACCOUNT_CHANGED);
            mEditDraft = savedInstanceState.getBoolean(KEY_SAVED_STATE_EDITDRAFT_CHANGED);
            //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_E
        }
        //AM: peng-zhang 2015-02-27 EMAIL BUGFIX_955421 MOD_E
        setContentView(R.layout.compose);
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            // Hide the app icon.
            actionBar.setIcon(null);
            actionBar.setDisplayUseLogoEnabled(false);
        }

        mInnerSavedState = (savedInstanceState != null) ? savedInstanceState.getBundle(KEY_INNER_SAVED_STATE)
                : null;

        //[FEATURE]-Add-BEGIN by TSNJ,Zhenhua.Fan,06/11/2014,FR-622609
        if (EmailApplication.isOrangeImapFeatureOn()) {
            Intent i = getIntent();
            if (i != null && i.getBooleanExtra(EXTRA_FROM_DRAFT_VIEW, false)) {
                isFromView = true;
            }
        }
        //[FEATURE]-Add-END by TSNJ,Zhenhua.Fan
        checkValidAccounts();
        // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_S
        LinearLayout tmp = (LinearLayout) findViewById(R.id.content);
        tmp.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                //get the cutrent visibale disrict.
                ComposeActivity.this.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
                //get the screen height
                int screenHeight = ComposeActivity.this.getWindow().getDecorView().getRootView().getHeight();

                Rect textRect = new Rect();
                int keyboardHeight = screenHeight - r.bottom;
                // the number 100 means that the keyboard is showing.
                if (keyboardHeight > 100) {
                    android.os.Message msg = mHandler.obtainMessage(SET_LISTPOPUPWINDOW_HEIGHT);
                    mHandler.sendMessage(msg);
                }
            }
        });
        // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_E
    }

    // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_S
    private int getListHeight() {
        Rect r = new Rect();
        //get the cutrent visibale disrict.
        ComposeActivity.this.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
        // TS: jian.xu 2015-06-26 EMAIL BUGFIX-1013067 MOD_S
        View textView = null;
        if (mTo.isFocused()) {
            textView = findViewById(R.id.to_content);
        } else if (mCc.isFocused()) {
            textView = findViewById(R.id.cc_content);
        } else if (mBcc.isFocused()) {
            textView = findViewById(R.id.bcc_content);
        }
        if (textView == null) {
            return 0;
        }
        // TS: jian.xu 2015-06-26 EMAIL BUGFIX-1013067 MOD_E
        Rect textRect = new Rect();
        textView.getGlobalVisibleRect(textRect);
        int listHeight = r.bottom - textRect.bottom;
        return listHeight;
    }
    // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_E

    private void finishCreate() {
        final Bundle savedState = mInnerSavedState;
        findViews();
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_S
        updateFromRowByAccounts();
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_E
        final Intent intent = getIntent();
        final Message message;
        final ArrayList<AttachmentPreview> previews;
        mShowQuotedText = false;
        final CharSequence quotedText;
        int action;
        // Check for any of the possibly supplied accounts.;
        final Account account;
        if (hadSavedInstanceStateMessage(savedState)) {
            action = savedState.getInt(EXTRA_ACTION, COMPOSE);
            account = savedState.getParcelable(Utils.EXTRA_ACCOUNT);
            message = savedState.getParcelable(EXTRA_MESSAGE);

            previews = savedState.getParcelableArrayList(EXTRA_ATTACHMENT_PREVIEWS);
            mRefMessage = savedState.getParcelable(EXTRA_IN_REFERENCE_TO_MESSAGE);
            quotedText = savedState.getCharSequence(EXTRA_QUOTED_TEXT);

            mExtraValues = savedState.getParcelable(EXTRA_VALUES);
        } else {
            account = obtainAccount(intent);
            action = intent.getIntExtra(EXTRA_ACTION, COMPOSE);
            // Initialize the message from the message in the intent
            message = intent.getParcelableExtra(ORIGINAL_DRAFT_MESSAGE);
            previews = intent.getParcelableArrayListExtra(EXTRA_ATTACHMENT_PREVIEWS);
            mRefMessage = intent.getParcelableExtra(EXTRA_IN_REFERENCE_TO_MESSAGE);
            //TS: wenggangjin 2015-01-06 EMAIL BUGFIX_882161 MOD_S
            if (mRefMessage != null && "".equals(mRefMessage.bodyHtml)) {
                String htmlbody = Body.restoreBodyHtmlWithMessageId(this, mRefMessage.getId());
                mRefMessage.bodyHtml = htmlbody;
                //TS: xujian 2015-06-23 EMAIL BUGFIX_1015657 MOD_S
            } else if (message != null) {
                if ("".equals(message.bodyHtml)) {
                    String htmlbody = Body.restoreBodyHtmlWithMessageId(this, message.getId());
                    message.bodyHtml = htmlbody;
                }
                if ("".equals(message.bodyText)) {
                    String body = Body.restoreBodyTextWithMessageId(this, message.getId());
                    message.bodyText = body;
                }
                //TS: xujian 2015-06-23 EMAIL BUGFIX_1015657 MOD_S
            }
            //TS: wenggangjin 2015-01-06 EMAIL BUGFIX_882161 MOD_E
            mRefMessageUri = intent.getParcelableExtra(EXTRA_IN_REFERENCE_TO_MESSAGE_URI);
            quotedText = null;

            if (Analytics.isLoggable()) {
                if (intent.getBooleanExtra(Utils.EXTRA_FROM_NOTIFICATION, false)) {
                    Analytics.getInstance().sendEvent("notification_action", "compose", getActionString(action), 0);
                }
            }
        }
        //TS: Gantao 2015-08-27 EMAIL FEATURE_ID DEL_S
        //        mAttachmentsView.setAttachmentPreviews(previews);
        //TS: Gantao 2015-08-27 EMAIL FEATURE_ID DEL_E
        // TS: yanhua.chen 2015-9-19 EMAIL BUGFIX_569665 ADD_S
        if (action == EDIT_DRAFT) {
            //mIsClickIcon = true;//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
            mEditDraft = true;
        }
        // TS: yanhua.chen 2015-9-19 EMAIL BUGFIX_569665 ADD_E

        setAccount(account);
        if (mAccount == null) {
            return;
        }
        // TS: chenyanhua 2015-01-12 EMAIL BUGFIX-890424 MOD_S
        //        initRecipients();
        // TS: chenyanhua 2015-01-12 EMAIL BUGFIX-890424 MOD_S
        // Clear the notification and mark the conversation as seen, if necessary
        final Folder notificationFolder = intent.getParcelableExtra(EXTRA_NOTIFICATION_FOLDER);

        if (notificationFolder != null) {
            final Uri conversationUri = intent.getParcelableExtra(EXTRA_NOTIFICATION_CONVERSATION);
            Intent actionIntent;
            if (conversationUri != null) {
                actionIntent = new Intent(MailIntentService.ACTION_RESEND_NOTIFICATIONS_WEAR);
                actionIntent.putExtra(Utils.EXTRA_CONVERSATION, conversationUri);
            } else {
                actionIntent = new Intent(MailIntentService.ACTION_CLEAR_NEW_MAIL_NOTIFICATIONS);
                actionIntent.setData(Utils.appendVersionQueryParameter(this, notificationFolder.folderUri.fullUri));
            }
            actionIntent.setPackage(getPackageName());
            actionIntent.putExtra(Utils.EXTRA_ACCOUNT, account);
            actionIntent.putExtra(Utils.EXTRA_FOLDER, notificationFolder);

            startService(actionIntent);
        }

        if (intent.getBooleanExtra(EXTRA_FROM_EMAIL_TASK, false)) {
            mLaunchedFromEmail = true;
        } else if (Intent.ACTION_SEND.equals(intent.getAction())) {
            final Uri dataUri = intent.getData();
            if (dataUri != null) {
                final String dataScheme = intent.getData().getScheme();
                final String accountScheme = mAccount.composeIntentUri.getScheme();
                mLaunchedFromEmail = TextUtils.equals(dataScheme, accountScheme);
            }
        }

        if (mRefMessageUri != null) {
            mShowQuotedText = true;
            mComposeMode = action;

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
                String wearReply = null;
                if (remoteInput != null) {
                    LogUtils.d(LOG_TAG, "Got remote input from new api");
                    CharSequence input = remoteInput.getCharSequence(NotificationActionUtils.WEAR_REPLY_INPUT);
                    if (input != null) {
                        wearReply = input.toString();
                    }
                } else {
                    // TODO: remove after legacy code has been removed.
                    LogUtils.d(LOG_TAG, "No remote input from new api, falling back to compatibility mode");
                    ClipData clipData = intent.getClipData();
                    if (clipData != null && LEGACY_WEAR_EXTRA.equals(clipData.getDescription().getLabel())) {
                        Bundle extras = clipData.getItemAt(0).getIntent().getExtras();
                        if (extras != null) {
                            wearReply = extras.getString(NotificationActionUtils.WEAR_REPLY_INPUT);
                        }
                    }
                }

                if (!TextUtils.isEmpty(wearReply)) {
                    createWearReplyTask(this, mRefMessageUri, UIProvider.MESSAGE_PROJECTION, mComposeMode,
                            wearReply).execute();
                    finish();
                    return;
                } else {
                    LogUtils.w(LOG_TAG, "remote input string is null");
                }
            }

            getLoaderManager().initLoader(INIT_DRAFT_USING_REFERENCE_MESSAGE, null, this);
            return;
        } else if (message != null && action != EDIT_DRAFT) {
            initFromDraftMessage(message);
            initQuotedTextFromRefMessage(mRefMessage, action);
            mShowQuotedText = message.appendRefMessageContent;
            // if we should be showing quoted text but mRefMessage is null
            // and we have some quotedText, display that
            if (mShowQuotedText && mRefMessage == null) {
                if (quotedText != null) {
                    initQuotedText(quotedText, false /* shouldQuoteText */);
                } else if (mExtraValues != null) {
                    initExtraValues(mExtraValues);
                    return;
                }
            }
        } else if (action == EDIT_DRAFT) {
            if (message == null) {
                throw new IllegalStateException("Message must not be null to edit draft");
            }
            initFromDraftMessage(message);
            // Update the action to the draft type of the previous draft
            switch (message.draftType) {
            case UIProvider.DraftType.REPLY:
                action = REPLY;
                break;
            case UIProvider.DraftType.REPLY_ALL:
                action = REPLY_ALL;
                break;
            case UIProvider.DraftType.FORWARD:
                action = FORWARD;
                break;
            case UIProvider.DraftType.COMPOSE:
            default:
                action = COMPOSE;
                break;
            }
            LogUtils.d(LOG_TAG, "Previous draft had action type: %d", action);

            mShowQuotedText = message.appendRefMessageContent;
            //TS: Gantao 2015-07-28 EMAIL BUGFIX_1053829 MOD_S
            //Terrible original design,refMessageUri did not save to db,the value is always 0 here.
            //but the body's sourceKey is saved ,it points to the refMessage's id,so we can get
            //the refMessage from the body's sourceKey.
            long sourceKey = Body.restoreBodySourceKey(this, message.id);
            if (sourceKey != 0) {
                // If we're editing an existing draft that was in reference to an existing message,
                // still need to load that original message since we might need to refer to the
                // original sender and recipients if user switches "reply <-> reply-all".
                mRefMessageUri = Uri.parse("content://" + EmailContent.AUTHORITY + "/uimessage/" + sourceKey);
                //TS: Gantao 2015-07-28 EMAIL BUGFIX_1053829 MOD_E
                mComposeMode = action;
                getLoaderManager().initLoader(REFERENCE_MESSAGE_LOADER, null, this);
                return;
            }
        } else if ((action == REPLY || action == REPLY_ALL || action == FORWARD)) {
            if (mRefMessage != null) {
                initFromRefMessage(action);
                mShowQuotedText = true;
            }
        } else {
            if (initFromExtras(intent)) {
                return;
            }
        }

        mComposeMode = action;
        finishSetup(action, intent, savedState);
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private static AsyncTask<Void, Void, Message> createWearReplyTask(final ComposeActivity composeActivity,
            final Uri refMessageUri, final String[] projection, final int action, final String wearReply) {
        return new AsyncTask<Void, Void, Message>() {
            private Intent mEmptyServiceIntent = new Intent(composeActivity, EmptyService.class);

            @Override
            protected void onPreExecute() {
                // Start service so we won't be killed if this app is put in the background.
                composeActivity.startService(mEmptyServiceIntent);
            }

            @Override
            protected Message doInBackground(Void... params) {
                Cursor cursor = composeActivity.getContentResolver().query(refMessageUri, projection, null, null,
                        null, null);
                if (cursor != null) {
                    try {
                        cursor.moveToFirst();
                        return new Message(cursor);
                    } finally {
                        cursor.close();
                    }
                }
                return null;
            }

            @Override
            protected void onPostExecute(Message message) {
                composeActivity.stopService(mEmptyServiceIntent);

                composeActivity.mRefMessage = message;
                composeActivity.initFromRefMessage(action);
                composeActivity.setBody(wearReply, false);
                composeActivity.finishSetup(action, composeActivity.getIntent(), null);
                composeActivity.sendOrSaveWithSanityChecks(false /* save */, true /* show  toast */,
                        false /* orientationChanged */, true /* autoSend */);
            }
        };
    }

    private void checkValidAccounts() {
        final Account[] allAccounts = AccountUtils.getAccounts(this);
        if (allAccounts == null || allAccounts.length == 0) {
            final Intent noAccountIntent = MailAppProvider.getNoAccountIntent(this);
            if (noAccountIntent != null) {
                mAccounts = null;
                startActivityForResult(noAccountIntent, RESULT_CREATE_ACCOUNT);
            }
        } else {
            // If none of the accounts are syncing, setup a watcher.
            boolean anySyncing = false;
            for (Account a : allAccounts) {
                if (a.isAccountReady()) {
                    anySyncing = true;
                    break;
                }
            }
            if (!anySyncing) {
                // There are accounts, but none are sync'd, which is just like having no accounts.
                mAccounts = null;
                getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, null, this);
                return;
            }
            //  AM: zhiqiang.shao 2015-03-30 EMAIL BUGFIX_960434 MOD_S
            Account[] accounts = AccountUtils.getSyncingAccounts(this);
            ArrayList<Account> listAccount = new ArrayList<Account>(accounts.length);
            for (Account acct : accounts) {
                if (!acct.supportsCapability(UIProvider.AccountCapabilities.VIRTUAL_ACCOUNT)) {
                    listAccount.add(acct);
                }
            }
            mAccounts = listAccount.toArray(new Account[listAccount.size()]);
            //  AM: zhiqiang.shao 2015-03-30 EMAIL BUGFIX_960434 MOD_E
            finishCreate();
        }
    }

    private Account obtainAccount(Intent intent) {
        Account account = null;
        Object accountExtra = null;
        if (intent != null && intent.getExtras() != null) {
            accountExtra = intent.getExtras().get(Utils.EXTRA_ACCOUNT);
            if (accountExtra instanceof Account) {
                account = (Account) accountExtra;
                //TS: gangjin.weng 2015-04-17 EMAIL BUGFIX_974962 Add_S
                if (account != null && !account.getAccountId().equalsIgnoreCase("Account Id")) {
                    return account;
                } else {
                    account = null;
                }
                //TS: gangjin.weng 2015-04-17 EMAIL BUGFIX_974962 Add_E
            } else if (accountExtra instanceof String) {
                // This is the Account attached to the widget compose intent.
                account = Account.newInstance((String) accountExtra);
                //TS: gangjin.weng 2015-04-17 EMAIL BUGFIX_974962 Add_S
                if (account != null && !account.getAccountId().equalsIgnoreCase("Account Id")) {
                    return account;
                } else {
                    account = null;
                }
                //TS: gangjin.weng 2015-04-17 EMAIL BUGFIX_974962 Add_E
                //  AM: zhiqiang.shao 2015-03-30 EMAIL BUGFIX_957619,960434 MOD_S
                //                if (account != null) {
                //                    return account;
                //                }
            }
            if (account != null) {
                if (!Address.isAllValid(account.getEmailAddress()) && mAccounts.length > 0) {
                    return account = mAccounts[0];
                }
                return account;
            }
            // AM: zhiqiang.shao 2015-03-30 EMAIL BUGFIX_957619,960434 MOD_E
            accountExtra = intent.hasExtra(Utils.EXTRA_ACCOUNT) ? intent.getStringExtra(Utils.EXTRA_ACCOUNT)
                    : intent.getStringExtra(EXTRA_SELECTED_ACCOUNT);
        }

        // AM: Kexue.Geng 2015-03-06 EMAIL BUGFIX_927828 MOD_S
        /*
        MailAppProvider provider = MailAppProvider.getInstance();
        String lastAccountUri = provider.getLastSentFromAccount();
        if (TextUtils.isEmpty(lastAccountUri)) {
        lastAccountUri = provider.getLastViewedAccount();
        }
        if (!TextUtils.isEmpty(lastAccountUri)) {
        accountExtra = Uri.parse(lastAccountUri);
        }
        */

        boolean goOriginalPath = true;
        boolean defaultAccountEnable = PLFUtils.getBoolean(getApplicationContext(),
                "feature_email_defaultAccount_on");
        if (defaultAccountEnable) {
            // If we still have no account, try the default
            if (account == null) {
                long accountId = com.tct.emailcommon.provider.Account.getDefaultAccountId(this);
                if (accountId != com.tct.emailcommon.provider.Account.NO_ACCOUNT) {
                    // Make sure it exists...
                    com.tct.emailcommon.provider.Account defaultAccount = com.tct.emailcommon.provider.Account
                            .restoreAccountWithId(getApplicationContext(), accountId);
                    if (defaultAccount != null) {
                        accountExtra = defaultAccount.getEmailAddress();
                        goOriginalPath = false;
                    }
                }
            }
        }

        if (goOriginalPath) {
            MailAppProvider provider = MailAppProvider.getInstance();
            // TS: junwei-xu 2015-11-10 EMAIL BUGFIX-864427 MOD_S
            //NOTE: We should use last seleceted account as current account.
            //String lastAccountUri = provider.getLastSentFromAccount();
            String lastAccountUri = provider.getLastViewedAccount();
            // TS: junwei-xu 2015-11-10 EMAIL BUGFIX-864427 MOD_E
            if (TextUtils.isEmpty(lastAccountUri)) {
                lastAccountUri = provider.getLastViewedAccount();
            }
            if (!TextUtils.isEmpty(lastAccountUri)) {
                accountExtra = Uri.parse(lastAccountUri);
            }
        }
        // AM: Kexue.Geng 2015-03-06 EMAIL BUGFIX_927828 MOD_E

        // TS: Gantao 2015-06-09 EMAIL BUGFIX-1019278 ADD_S
        // Note: If we go here,it says that the selected account is "combined view" or null,
        // anyway ,not normal selected,add a boolean var to avoid the issue when we reply a
        // mail from combined view.
        mSelectedAccountUnusual = true;
        if (mAccounts != null && mAccounts.length > 0) {
            if (accountExtra instanceof String && !TextUtils.isEmpty((String) accountExtra)) {
                // For backwards compatibility, we need to check account
                // names.
                for (Account a : mAccounts) {
                    if (a.getEmailAddress().equals(accountExtra)) {
                        account = a;
                    }
                }
            } else if (accountExtra instanceof Uri) {
                // The uri of the last viewed account is what is stored in
                // the current code base.
                for (Account a : mAccounts) {
                    if (a.uri.equals(accountExtra)) {
                        account = a;
                    }
                }
            }
            if (account == null) {
                account = mAccounts[0];
            }
        }
        return account;
    }

    protected void finishSetup(int action, Intent intent, Bundle savedInstanceState) {
        setFocus(action);
        // Don't bother with the intent if we have procured a message from the
        // intent already.
        if (!hadSavedInstanceStateMessage(savedInstanceState)) {
            initAttachmentsFromIntent(intent);
        }
        initActionBar();
        initFromSpinner(savedInstanceState != null ? savedInstanceState : intent.getExtras(), action);

        // If this is a draft message, the draft account is whatever account was
        // used to open the draft message in Compose.
        if (mDraft != null) {
            mDraftAccount = mReplyFromAccount;
        }

        initChangeListeners();

        // These two should be identical since we check CC and BCC the same way
        boolean showCc = !TextUtils.isEmpty(mCc.getText())
                || (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_SHOW_CC));
        boolean showBcc = !TextUtils.isEmpty(mBcc.getText())
                || (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_SHOW_BCC));
        mCcBccView.show(false /* animate */, showCc, showBcc);
        updateHideOrShowCcBcc();
        updateHideOrShowQuotedText(mShowQuotedText);

        mRespondedInline = mInnerSavedState != null && mInnerSavedState.getBoolean(EXTRA_RESPONDED_INLINE);
        if (mRespondedInline) {
            mQuotedTextView.setVisibility(View.GONE);
        }
    }

    private static boolean hadSavedInstanceStateMessage(final Bundle savedInstanceState) {
        return savedInstanceState != null && savedInstanceState.containsKey(EXTRA_MESSAGE);
    }

    private void updateHideOrShowQuotedText(boolean showQuotedText) {
        mQuotedTextView.updateCheckedState(showQuotedText);
        mQuotedTextView.setUpperDividerVisible(mAttachmentsView.getAttachments().size() > 0);
    }

    private void setFocus(int action) {
        if (action == EDIT_DRAFT) {
            int type = mDraft.draftType;
            switch (type) {
            case UIProvider.DraftType.COMPOSE:
            case UIProvider.DraftType.FORWARD:
                action = COMPOSE;
                break;
            case UIProvider.DraftType.REPLY:
            case UIProvider.DraftType.REPLY_ALL:
            default:
                action = REPLY;
                break;
            }
        }
        switch (action) {
        case FORWARD:
        case COMPOSE:
            if (TextUtils.isEmpty(mTo.getText())) {
                mTo.requestFocus();
                break;
            }
            //$FALL-THROUGH$
        case REPLY:
        case REPLY_ALL:
        default:
            focusBody();
            break;
        }
    }

    /**
     * Focus the body of the message.
     */
    private void focusBody() {
        mBodyView.requestFocus();
        resetBodySelection();
    }

    private void resetBodySelection() {
        int length = mBodyView.getText().length();
        int signatureStartPos = getSignatureStartPosition(mSignature, mBodyView.getText().toString());
        if (signatureStartPos > -1) {
            // In case the user deleted the newlines...
            mBodyView.setSelection(signatureStartPos);
        } else if (length >= 0) {
            // Move cursor to the end.
            mBodyView.setSelection(length);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();

        Analytics.getInstance().activityStart(this);
    }

    @Override
    protected void onStop() {
        super.onStop();

        Analytics.getInstance().activityStop(this);
        hideInputSoftKeyboard();
    }

    //TS: xiangnan.zhou 2016-03-23 EMAIL BUGFIX-1783199 ADD_S
    /** hide input soft keyboard */
    private void hideInputSoftKeyboard() {
        View focusView = getCurrentFocus();
        if (focusView != null) {
            final InputMethodManager imm = (InputMethodManager) getApplicationContext()
                    .getSystemService(Context.INPUT_METHOD_SERVICE);
            if (imm != null) {
                imm.hideSoftInputFromWindow(focusView.getWindowToken(), 0);
            }
        }
    }
    //TS: xiangnan.zhou 2016-03-23 EMAIL BUGFIX-1783199 ADD_E

    @Override
    protected void onResume() {
        super.onResume();
        //[FEATURE]-Add-BEGIN by TSNJ,Zhenhua.Fan,06/11/2014,FR-622609
        if (isFromView) {
            Intent intent = new Intent();
            intent.setAction("destroyDraftViewScreen");
            this.sendBroadcast(intent);
            isFromView = false;
        }
        //[FEATURE]-Add-END by TSNJ,Zhenhua.Fan
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_S
        //Note: check valid accounts and update ui
        updateFromRowByAccounts();
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_E
        // Update the from spinner as other accounts
        // may now be available.
        if (mFromSpinner != null && mAccount != null) {
            mFromSpinner.initialize(mComposeMode, mAccount, mAccounts, mRefMessage);
        }
        //TS: yanhua.chen 2015-9-22 EMAIL CD_551912 ADD_S
        //Note:when onResume,should restore the flag
        mIsSaveDraft = false;
        //TS: yanhua.chen 2015-9-22 EMAIL CD_551912 ADD_E
    }

    @Override
    protected void onPause() {
        super.onPause();

        // When the user exits the compose view, see if this draft needs saving.
        // Don't save unnecessary drafts if we are only changing the orientation.
        if (!isChangingConfigurations()) {
            saveIfNeeded();

            if (isFinishing() && !mPerformedSendOrDiscard && !isBlank()) {
                // log saving upon backing out of activity. (we avoid logging every sendOrSave()
                // because that method can be invoked many times in a single compose session.)
                logSendOrSave(true /* save */);
            }
        }
    }

    @Override
    protected void onActivityResult(int request, int result, Intent data) {
        if (request == RESULT_PICK_ATTACHMENT) {
            mAddingAttachment = false;
            if (result == RESULT_OK) {
                addAttachmentAndUpdateView(data);
            }
        } else if (request == RESULT_CREATE_ACCOUNT) {
            // We were waiting for the user to create an account
            if (result != RESULT_OK) {
                finish();
            } else {
                // Watch for accounts to show up!
                // restart the loader to get the updated list of accounts
                getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, null, this);
                showWaitFragment(null);
            } //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 MOD_S
        } else if (result == RESULT_OK && request == ACTIVITY_REQUEST_PICK_CONTACT_TO) {
            //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 MOD_E
            // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_S
            mLaunchContact = false;
            // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_E
            //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 MOD_S
            String platform = PLFUtils.getString(this, "feature_email_platform");
            if (platform.equals(PLATFORM_QUALCOMM)) {
                addAddressFromData(mTo, data);
            } else if (platform.equals(PLATFORM_MTK)) {
                final long[] ids = data.getLongArrayExtra(EXTRA_PICK_DATA_RESULT);
                if (ids == null || ids.length <= 0) {
                    return;
                }
                addAddressesToList(ids, mTo);
            }
        } else if (result == RESULT_OK && request == ACTIVITY_REQUEST_PICK_CONTACT_CC) {
            //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 MOD_E
            // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_S
            mLaunchContact = false;
            // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_E
            //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 MOD_S
            String platform = PLFUtils.getString(this, "feature_email_platform");
            if (platform.equals(PLATFORM_QUALCOMM)) {
                addAddressFromData(mCc, data);
            } else if (platform.equals(PLATFORM_MTK)) {
                final long[] ids = data.getLongArrayExtra(EXTRA_PICK_DATA_RESULT);
                if (ids == null || ids.length <= 0) {
                    return;
                }
                addAddressesToList(ids, mCc);
            }
        } else if (result == RESULT_OK && request == ACTIVITY_REQUEST_PICK_CONTACT_BCC) {
            //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 MOD_E
            // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_S
            mLaunchContact = false;
            // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_E
            //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 MOD_S
            String platform = PLFUtils.getString(this, "feature_email_platform");
            if (platform.equals(PLATFORM_QUALCOMM)) {
                addAddressFromData(mBcc, data);
            } else if (platform.equals(PLATFORM_MTK)) {
                final long[] ids = data.getLongArrayExtra(EXTRA_PICK_DATA_RESULT);
                if (ids == null || ids.length <= 0) {
                    return;
                }
                addAddressesToList(ids, mBcc);
            }
            //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 MOD_E
        }
    }

    //TS: jin.dong 2015-12-17 EMAIL BUGFIX_1170083 ADD_S
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PermissionUtil.REQ_CODE_PERMISSION_ADD_ATTACHMENT) {
            if (!PermissionUtil.checkPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                showNeedPermissionToast(R.string.permission_needed_to_add_attachments);
            } else if (ACTION_LAUNCH_COMPOSE.equals(getIntent().getAction())) {
                doAttach(MIME_TYPE_ALL);
            } else {
                Intent intent = getIntent();
                int size = getShareAttachmentSize(intent);
                if (size > 0) {
                    mAttachmentsChanged = true;
                    updateSaveUi();

                    Analytics.getInstance().sendEvent("send_intent_with_attachments",
                            Integer.toString(getAttachments().size()), null, size);
                }
            }
        }
    }
    //TS: jin.dong 2015-12-17 EMAIL BUGFIX_1170083 ADD_E

    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/21/2014,FR631895(porting from FR487417)
    private void addAddressFromData(RecipientEditTextView view, Intent data) {
        //[FEATURE]-Mod-BEGIN by TSCD.chao zhang,04/22/2014,fix email crash during press back key during choosing nothing in Contacts.
        if (data == null) {
            return;
        }
        //[FEATURE]-Mod-END  by TSCD.chao zhang
        Bundle b = data.getExtras();
        Bundle choiceSet = b.getBundle("result");
        Set<String> set = choiceSet.keySet();
        Iterator<String> i = set.iterator();
        //TS: junwei-xu 2016-01-25 EMAIL BUGFIX-1496954 MOD_S
        String itemName, itemAddress;
        Rfc822Token token;
        // TS: zhaotianyong 2015-02-28 EMAIL BUGFIX-926303 ADD_S
        int recipientsCounts = 0;
        // TS: zhaotianyong 2015-02-28 EMAIL BUGFIX-926303 ADD_E
        while (i.hasNext()) {
            // TS: zhaotianyong 2015-02-28 EMAIL BUGFIX-926303 ADD_S
            recipientsCounts++;
            if (recipientsCounts > RECIPIENT_MAX_NUMBER) {
                Utility.showToast(this, this.getString(R.string.not_add_more_recipients, RECIPIENT_MAX_NUMBER));
                return;
            }
            // TS: zhaotianyong 2015-02-28 EMAIL BUGFIX-926303 ADD_E
            String key = i.next();
            String[] emails = choiceSet.getStringArray(key);
            itemName = emails[EMAIL_NAME_INDEX];
            itemAddress = emails[EMAIL_ADDRESS_INDEX];
            //TS: rong-tang 2016-03-28 EMAIL BUGFIX-1863457 MOD_S
            itemName = Rfc822Validator.fixInvalidName(itemName);
            //TS: rong-tang 2016-03-28 EMAIL BUGFIX-1863457 MOD_E
            token = new Rfc822Token(itemName, itemAddress, null);
            addAddressToList(token.toString(), view);
        }
        //TS: junwei-xu 2016-01-25 EMAIL BUGFIX-1496954 MOD_E
    }

    //[FEATURE]-Add-END by TSCD.chao zhang
    private static void addAddress(RecipientEditTextView view, String address) {
        // TS: chenyanhua 2015-01-03 EMAIL BUGFIX-881538 ADD_S
        view.setText("");
        // TS: chenyanhua 2015-01-03 EMAIL BUGFIX_881538 ADD_E
        view.append(address + ", ");
    }

    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/21/2014,FR631895(porting from FR487417)
    private com.tct.emailcommon.mail.Address[] getAddressesIgnoreValid(TextView view) {
        com.tct.emailcommon.mail.Address[] addresses = com.tct.emailcommon.mail.Address
                .parseIgnoreValid(view.getText().toString().trim());
        return addresses;
    }
    //[FEATURE]-Add-END by TSCD.chao zhang

    @Override
    protected final void onRestoreInstanceState(Bundle savedInstanceState) {
        final boolean hasAccounts = mAccounts != null && mAccounts.length > 0;
        if (hasAccounts) {
            clearChangeListeners();
        }
        super.onRestoreInstanceState(savedInstanceState);
        if (mInnerSavedState != null) {
            if (mInnerSavedState.containsKey(EXTRA_FOCUS_SELECTION_START)) {
                int selectionStart = mInnerSavedState.getInt(EXTRA_FOCUS_SELECTION_START);
                int selectionEnd = mInnerSavedState.getInt(EXTRA_FOCUS_SELECTION_END);
                // There should be a focus and it should be an EditText since we
                // only save these extras if these conditions are true.
                EditText focusEditText = (EditText) getCurrentFocus();
                final int length = focusEditText.getText().length();
                if (selectionStart < length && selectionEnd < length) {
                    focusEditText.setSelection(selectionStart, selectionEnd);
                }
            }
        }
        if (hasAccounts) {
            initChangeListeners();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle state) {
        super.onSaveInstanceState(state);
        final Bundle inner = new Bundle();
        saveState(inner);
        state.putBundle(KEY_INNER_SAVED_STATE, inner);
        //AM: peng-zhang 2015-02-27 EMAIL BUGFIX_955421 MOD_S
        //AM: peng-zhang 2015-04-03 EMAIL BUGFIX_968060 MOD_S
        if (null != mBcc) {
            String bcc_save = mBcc.getText().toString();
            boolean bcc_changed = mBcc.getText().toString().contains(mAccount.getEmailAddress());
            state.putString("BCC_SAVE", bcc_save);
            state.putBoolean("BCC_CHANGED", bcc_changed);
        }
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 ADD_S
        state.putInt(KEY_PRIORITY_SAVED_STATE, mPriorityFlag);
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 ADD_E
        // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_S
        state.putBoolean(KEY_SAVED_STATE_TEXT_CHANGED, mTextChanged);
        state.putBoolean(KEY_SAVED_STATE_ATTACHMENT_CHANGED, mAttachmentsChanged);
        state.putBoolean(KEY_SAVED_STATE_REPLY_FROM_CHANGED, mReplyFromChanged);
        state.putBoolean(KEY_SAVED_STATE_PRIORITY_CHANGED, mProrityChanged);
        // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 ADD_E
        //AM: peng-zhang 2015-04-03 EMAIL BUGFIX_968060 MOD_E
        //AM: peng-zhang 2015-02-27 EMAIL BUGFIX_955421 MOD_E
        //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_S
        state.putBoolean(KEY_SAVED_STATE_ATTLARGEWARNING_CHANGED, attLargeWarning);
        //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_E
        //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_S
        //state.putBoolean(KEY_SAVED_STATE_ISCLICKICON_CHANGED, mIsClickIcon);//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
        state.putBoolean(KEY_SAVED_STATE_CHANGEACCOUNT_CHANGED, mChangeAccount);
        state.putBoolean(KEY_SAVED_STATE_EDITDRAFT_CHANGED, mEditDraft);
        //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_E
    }

    private void saveState(Bundle state) {
        //TS: lin-zhou 2015-9-28 EMAIL BUGFIX_666151 ADD_S
        //Note:When click doSend but not send and turn screen,restore the flag
        doSend = false;
        //TS: lin-zhou 2015-9-28 EMAIL BUGFIX_666151 ADD_E
        // We have no accounts so there is nothing to compose, and therefore, nothing to save.
        if (mAccounts == null || mAccounts.length == 0) {
            return;
        }
        // The framework is happy to save and restore the selection but only if it also saves and
        // restores the contents of the edit text. That's a lot of text to put in a bundle so we do
        // this manually.
        View focus = getCurrentFocus();
        if (focus != null && focus instanceof EditText) {
            EditText focusEditText = (EditText) focus;
            state.putInt(EXTRA_FOCUS_SELECTION_START, focusEditText.getSelectionStart());
            state.putInt(EXTRA_FOCUS_SELECTION_END, focusEditText.getSelectionEnd());
        }

        final List<ReplyFromAccount> replyFromAccounts = mFromSpinner.getReplyFromAccounts();
        final int selectedPos = mFromSpinner.getSelectedItemPosition();
        final ReplyFromAccount selectedReplyFromAccount = (replyFromAccounts != null && replyFromAccounts.size() > 0
                && replyFromAccounts.size() > selectedPos) ? replyFromAccounts.get(selectedPos) : null;
        if (selectedReplyFromAccount != null) {
            state.putString(EXTRA_SELECTED_REPLY_FROM_ACCOUNT, selectedReplyFromAccount.serialize().toString());
            state.putParcelable(Utils.EXTRA_ACCOUNT, selectedReplyFromAccount.account);
        } else {
            state.putParcelable(Utils.EXTRA_ACCOUNT, mAccount);
        }

        if (mDraftId == UIProvider.INVALID_MESSAGE_ID && mRequestId != 0) {
            // We don't have a draft id, and we have a request id,
            // save the request id.
            state.putInt(EXTRA_REQUEST_ID, mRequestId);
        }

        // We want to restore the current mode after a pause
        // or rotation.
        int mode = getMode();
        state.putInt(EXTRA_ACTION, mode);

        final Message message = createMessage(selectedReplyFromAccount, mRefMessage, mode,
                removeComposingSpans(mBodyView.getText()));
        if (mDraft != null) {
            message.id = mDraft.id;
            message.serverId = mDraft.serverId;
            message.uri = mDraft.uri;
        }
        state.putParcelable(EXTRA_MESSAGE, message);

        if (mRefMessage != null) {
            state.putParcelable(EXTRA_IN_REFERENCE_TO_MESSAGE, mRefMessage);
        } else if (message.appendRefMessageContent) {
            // If we have no ref message but should be appending
            // ref message content, we have orphaned quoted text. Save it.
            state.putCharSequence(EXTRA_QUOTED_TEXT, mQuotedTextView.getQuotedTextIfIncluded());
        }
        state.putBoolean(EXTRA_SHOW_CC, mCcBccView.isCcVisible());
        state.putBoolean(EXTRA_SHOW_BCC, mCcBccView.isBccVisible());
        state.putBoolean(EXTRA_RESPONDED_INLINE, mRespondedInline);
        state.putBoolean(EXTRA_SAVE_ENABLED, mSave != null && mSave.isEnabled());
        //TS: Gantao 2015-08-27 EMAIL FEATURE_ID DEL_S
        //        state.putParcelableArrayList(
        //                EXTRA_ATTACHMENT_PREVIEWS, mAttachmentsView.getAttachmentPreviews());
        //TS: Gantao 2015-08-27 EMAIL FEATURE_ID DEL_E

        state.putParcelable(EXTRA_VALUES, mExtraValues);
    }

    private int getMode() {
        int mode = ComposeActivity.COMPOSE;
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar != null && actionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_LIST) {
            // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 MOD_S
            //Note: get the truth compose mode from position
            //mode = actionBar.getSelectedNavigationIndex();
            mode = getComposeModeFromListPosition(actionBar.getSelectedNavigationIndex(), mSupportReplyAll);
            // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 MOD_E
        }
        return mode;
    }

    /**
     * This function might be called from a background thread, so be sure to move everything that
     * can potentially modify the UI to the main thread (e.g. removeComposingSpans for body).
     */
    private Message createMessage(ReplyFromAccount selectedReplyFromAccount, Message refMessage, int mode,
            Spanned body) {
        Message message = new Message();
        message.id = UIProvider.INVALID_MESSAGE_ID;
        message.serverId = null;
        message.uri = null;
        message.conversationUri = null;
        message.subject = mSubject.getText().toString();
        message.snippet = null;
        message.setTo(formatSenders(mTo.getText().toString()));
        message.setCc(formatSenders(mCc.getText().toString()));
        message.setBcc(formatSenders(mBcc.getText().toString()));
        // TS: tao.gan 2015-12-25 EMAIL FEATURE-1239148 ADD_S
        //message.setReplyTo(null);
        message.setReplyTo(getReplyToAddress());
        // TS: tao.gan 2015-12-25 EMAIL FEATURE-1239148 ADD_E
        message.dateReceivedMs = 0;
        // TS: xujian 2015-11-16 EMAIL BUGFIX-1106881 MOD_S
        // TS: xujian 2015-07-10 EMAIL BUGFIX-1037000 ADD_S
        //        message.bodyHtml = spannedBodyToHtml(body, true);
        String body1 = mBodyView.getText().toString().replace("\n", "\n\r");
        SpannableString spannableString = new SpannableString(body1);
        message.bodyHtml = spannedBodyToHtml(spannableString, true);
        // TS: xujian 2015-07-10 EMAIL BUGFIX-1037000 ADD_E
        // TS: xujian 2015-11-16 EMAIL BUGFIX-1106881 MOD_E
        message.bodyText = mBodyView.getText().toString();
        message.embedsExternalResources = false;
        message.refMessageUri = mRefMessage != null ? mRefMessage.uri : null;
        message.appendRefMessageContent = mQuotedTextView.getQuotedTextIfIncluded() != null;
        ArrayList<Attachment> attachments = mAttachmentsView.getAttachments();
        message.hasAttachments = attachments != null && attachments.size() > 0;
        message.attachmentListUri = null;
        message.messageFlags = 0;
        message.alwaysShowImages = false;
        // TS: xujian 2015-06-15 EMAIL BUGFIX_1015669 MOD_S
        synchronized (attachments) {
            message.attachmentsJson = Attachment.toJSONArray(attachments);
        }
        // TS: xujian 2015-06-15 EMAIL BUGFIX_1015669 MOD_E
        CharSequence quotedText = mQuotedTextView.getQuotedText();
        message.quotedTextOffset = -1; // Just a default value.
        if (refMessage != null && !TextUtils.isEmpty(quotedText)) {
            if (!TextUtils.isEmpty(refMessage.bodyHtml)) {
                // We want the index to point to just the quoted text and not the
                // "On December 25, 2014..." part of it.
                message.quotedTextOffset = QuotedTextView.getQuotedTextOffset(quotedText.toString());
            } else if (!TextUtils.isEmpty(refMessage.bodyText)) {
                // We want to point to the entire quoted text.
                message.quotedTextOffset = QuotedTextView.findQuotedTextIndex(quotedText);
            }
        }
        message.accountUri = null;
        message.setFrom(computeFromForAccount(selectedReplyFromAccount));
        message.draftType = getDraftType(mode);
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 DEL_S
        // Note: we don't use sharedpreferences to save priority, no need to get it from sharedpreferences
        /*
        //TS: junwei-xu 2015-2-3 EMAIL BUGFIX_874020 ADD_S
        SharedPreferences perPreferences = getSharedPreferences("PriorityFlag", Context.MODE_PRIVATE);
        mPriorityFlag = perPreferences.getInt("mPriorityFlag", mPriorityFlag);
        //TS: junwei-xu 2015-2-3 EMAIL BUGFIX_874020 ADD_E
        */
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 DEL_E
        message.mPriority = mPriorityFlag;//[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
        return message;
    }

    protected String computeFromForAccount(ReplyFromAccount selectedReplyFromAccount) {
        final String email = selectedReplyFromAccount != null ? selectedReplyFromAccount.address
                : mAccount != null ? mAccount.getEmailAddress() : null;
        final String senderName = selectedReplyFromAccount != null ? selectedReplyFromAccount.name
                : mAccount != null ? mAccount.getSenderName() : null;
        final Address address = new Address(email, senderName);
        return address.toHeader();
    }

    private static String formatSenders(final String string) {
        if (!TextUtils.isEmpty(string) && string.charAt(string.length() - 1) == ',') {
            return string.substring(0, string.length() - 1);
        }
        return string;
    }

    @VisibleForTesting
    protected void setAccount(Account account) {
        if (account == null) {
            return;
        }
        if (!account.equals(mAccount)) {
            mAccount = account;
            mCachedSettings = mAccount.settings;
            //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 MOD_S
            //Note:when change the account and have been clicked icon,set icon visible
            if (/*!mIsClickIcon&&*/mChangeAccount) {//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
                mBodySignature.setVisibility(View.VISIBLE);
            }
            // TS: yanhua.chen 2015-9-19 EMAIL BUGFIX_569665 MOD_S
            if (mEditDraft) {
                appendSignature();
                mBodySignature.setVisibility(View.GONE);
            } else {
                //Note:when change the account,set mSignature the value to currentAccount's signature
                mSignature = mCachedSettings.signature;
            }

            //[BUGFIX]-MOD begin by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
            if (mBodySignature != null && !TextUtils.isEmpty(mSignature)) {
                mBodySignature.setVisibility(View.GONE);
                //Note:when click icon,append the signature to body
                appendSignature();
            }
            //[BUGFIX]-MOD end by SCDTABLET.shujing.jin

            // TS: yanhua.chen 2015-9-19 EMAIL BUGFIX_569665 MOD_E
            //TS: yanhua.chen 2015-9-11 EMAIL BUGFIX_568681 ADD_S
            //Note:when account hasn't signature,set icon gone
            if (TextUtils.isEmpty(mSignature)) {
                mBodySignature.setVisibility(View.GONE);
            }
            //TS: yanhua.chen 2015-9-11 EMAIL BUGFIX_568681 ADD_E
            //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 MOD_E
        }
        if (mAccount != null) {
            MailActivity.setNfcMessage(mAccount.getEmailAddress());
        }
    }

    private void initFromSpinner(Bundle bundle, int action) {
        if (action == EDIT_DRAFT && mDraft.draftType == UIProvider.DraftType.COMPOSE) {
            action = COMPOSE;
        }
        mFromSpinner.initialize(action, mAccount, mAccounts, mRefMessage);

        if (bundle != null) {
            if (bundle.containsKey(EXTRA_SELECTED_REPLY_FROM_ACCOUNT)) {
                mReplyFromAccount = ReplyFromAccount.deserialize(mAccount,
                        bundle.getString(EXTRA_SELECTED_REPLY_FROM_ACCOUNT));
            } else if (bundle.containsKey(EXTRA_FROM_ACCOUNT_STRING)) {
                final String accountString = bundle.getString(EXTRA_FROM_ACCOUNT_STRING);
                mReplyFromAccount = mFromSpinner.getMatchingReplyFromAccount(accountString);
            }
        }
        if (mReplyFromAccount == null) {
            if (mDraft != null) {
                mReplyFromAccount = getReplyFromAccountFromDraft(mAccount, mDraft);
            } else if (mRefMessage != null) {
                mReplyFromAccount = getReplyFromAccountForReply(mAccount, mRefMessage);
            }
        }
        if (mReplyFromAccount == null) {
            mReplyFromAccount = getDefaultReplyFromAccount(mAccount);
        }

        mFromSpinner.setCurrentAccount(mReplyFromAccount);

        // TS: chenyanhua 2015-01-12 EMAIL BUGFIX-890424 ADD_S
        setAccount(mReplyFromAccount.account);
        // TS: Gantao 2015-06-24 EMAIL BUGFIX-1029207 ADD_S
        mSelectedAccountUnusual = false;
        // TS: Gantao 2015-06-24 EMAIL BUGFIX-1029207 ADD_E
        // TS: Gantao 2015-06-16 EMAIL BUGFIX-1019238 ADD_S
        if (mReplyToRecipientsLisnter != null) {
            mReplyToRecipientsLisnter.addToRecipients();
        }
        // TS: Gantao 2015-06-16 EMAIL BUGFIX-1019238 ADD_E
        // TS: junwei-xu 2015-03-25 EMAIL BUGFIX-958270 ADD_S
        if (mReplyRecipientslistener != null) {
            mReplyRecipientslistener.addCCRecipients();
        }
        // TS: junwei-xu 2015-03-25 EMAIL BUGFIX-958270 ADD_E
        initRecipients();
        // TS: chenyanhua 2015-01-12 EMAIL BUGFIX-890424 ADD_E
        if (mFromSpinner.getCount() > 1) {
            // If there is only 1 account, just show that account.
            // Otherwise, give the user the ability to choose which account to
            // send mail from / save drafts to.
            mFromStatic.setVisibility(View.GONE);
            mFromStaticText.setText(mReplyFromAccount.address);
            mFromSpinnerWrapper.setVisibility(View.VISIBLE);
        } else {
            mFromStatic.setVisibility(View.VISIBLE);
            mFromStaticText.setText(mReplyFromAccount.address);
            mFromSpinnerWrapper.setVisibility(View.GONE);
        }
    }

    // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_S
    /**
     * auto display From row according to mAccounts's size
     */
    private void updateFromRowByAccounts() {
        Account[] accounts = AccountUtils.getSyncingAccounts(this);
        ArrayList<Account> listAccount = new ArrayList<Account>(accounts.length);
        for (Account account : accounts) {
            if (!account.supportsCapability(UIProvider.AccountCapabilities.VIRTUAL_ACCOUNT)) {
                listAccount.add(account);
            }
        }
        mAccounts = listAccount.toArray(new Account[listAccount.size()]);
        // TS: jin.dong 2015-09-01 EMAIL BUGFIX-571435  MOD_S
        //NOTE: may launch from Gallery/fileManager shareing,so mFromRow not initialized.
        if (mAccounts == null || mAccounts.length == 0 || mFromRow == null) {
            return;
        }
        // TS: jin.dong 2015-09-01 EMAIL BUGFIX-571435  MOD_E
        boolean showFromRow = mAccounts.length > 1 ? true : false;
        if (showFromRow) {
            mFromRow.setVisibility(View.VISIBLE);
        } else {
            mFromRow.setVisibility(View.GONE);
        }
    }

    /**
     * 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;
    }

    /**
     * According to whether support reply all, return the truth list position.
     */
    private int getListPositionFromComposeMode(int composeMode, boolean isSupportReplyall) {
        int selectItem = 0;
        switch (composeMode) {
        case ComposeActivity.REPLY:
            selectItem = 0;
            break;
        case ComposeActivity.REPLY_ALL:
            selectItem = isSupportReplyall ? 1 : 0;
            break;
        case ComposeActivity.FORWARD:
            selectItem = isSupportReplyall ? 2 : 1;
            break;
        }
        return selectItem;
    }

    /**
     * According to whether support reply all, return the truth compose mode.
     */
    private int getComposeModeFromListPosition(int position, boolean isSupportReplyall) {
        int mode = ComposeActivity.REPLY;
        switch (position) {
        case 0:
            mode = ComposeActivity.REPLY;
            break;
        case 1:
            mode = isSupportReplyall ? ComposeActivity.REPLY_ALL : ComposeActivity.FORWARD;
            break;
        case 2:
            mode = ComposeActivity.FORWARD;
            break;
        }
        return mode;
    }
    // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_E

    private ReplyFromAccount getReplyFromAccountForReply(Account account, Message refMessage) {
        if (refMessage.accountUri != null) {
            // This must be from combined inbox.
            List<ReplyFromAccount> replyFromAccounts = mFromSpinner.getReplyFromAccounts();
            for (ReplyFromAccount from : replyFromAccounts) {
                if (from.account.uri.equals(refMessage.accountUri)) {
                    return from;
                }
            }
            return null;
        } else {
            return getReplyFromAccount(account, refMessage);
        }
    }

    /**
     * Given an account and the message we're replying to,
     * return who the message should be sent from.
     * @param account Account in which the message arrived.
     * @param refMessage Message to analyze for account selection
     * @return the address from which to reply.
     */
    public ReplyFromAccount getReplyFromAccount(Account account, Message refMessage) {
        // First see if we are supposed to use the default address or
        // the address it was sentTo.
        if (mCachedSettings.forceReplyFromDefault) {
            return getDefaultReplyFromAccount(account);
        } else {
            // If we aren't explicitly told which account to look for, look at
            // all the message recipients and find one that matches
            // a custom from or account.
            List<String> allRecipients = new ArrayList<String>();
            allRecipients.addAll(Arrays.asList(refMessage.getToAddressesUnescaped()));
            allRecipients.addAll(Arrays.asList(refMessage.getCcAddressesUnescaped()));
            return getMatchingRecipient(account, allRecipients);
        }
    }

    /**
     * Compare all the recipients of an email to the current account and all
     * custom addresses associated with that account. Return the match if there
     * is one, or the default account if there isn't.
     */
    protected ReplyFromAccount getMatchingRecipient(Account account, List<String> sentTo) {
        // Tokenize the list and place in a hashmap.
        ReplyFromAccount matchingReplyFrom = null;
        Rfc822Token[] tokens;
        HashSet<String> recipientsMap = new HashSet<String>();
        for (String address : sentTo) {
            tokens = Rfc822Tokenizer.tokenize(address);
            for (final Rfc822Token token : tokens) {
                recipientsMap.add(token.getAddress());
            }
        }

        int matchingAddressCount = 0;
        List<ReplyFromAccount> customFroms;
        customFroms = account.getReplyFroms();
        if (customFroms != null) {
            for (ReplyFromAccount entry : customFroms) {
                if (recipientsMap.contains(entry.address)) {
                    matchingReplyFrom = entry;
                    matchingAddressCount++;
                }
            }
        }
        if (matchingAddressCount > 1) {
            matchingReplyFrom = getDefaultReplyFromAccount(account);
        }
        return matchingReplyFrom;
    }

    private static ReplyFromAccount getDefaultReplyFromAccount(final Account account) {
        for (final ReplyFromAccount from : account.getReplyFroms()) {
            if (from.isDefault) {
                return from;
            }
        }
        return new ReplyFromAccount(account, account.uri, account.getEmailAddress(), account.getSenderName(),
                account.getEmailAddress(), true, false);
    }

    private ReplyFromAccount getReplyFromAccountFromDraft(final Account account, final Message msg) {
        final Address[] draftFroms = Address.parse(msg.getFrom());
        final String sender = draftFroms.length > 0 ? draftFroms[0].getAddress() : "";
        ReplyFromAccount replyFromAccount = null;
        List<ReplyFromAccount> replyFromAccounts = mFromSpinner.getReplyFromAccounts();
        if (TextUtils.equals(account.getEmailAddress(), sender)) {
            replyFromAccount = getDefaultReplyFromAccount(account);
        } else {
            for (ReplyFromAccount fromAccount : replyFromAccounts) {
                if (TextUtils.equals(fromAccount.address, sender)) {
                    replyFromAccount = fromAccount;
                    break;
                }
            }
        }
        return replyFromAccount;
    }

    private void findViews() {
        mScrollView = (ScrollView) findViewById(R.id.compose);
        mScrollView.setVisibility(View.VISIBLE);
        mCcBccButton = findViewById(R.id.add_cc_bcc);

        //[BUGFIX]-Add-BEGIN by SCDTABLET.weiwei.huang,05/03/2016,2013739,
        //[Email]Add mail contact icon display is not consistent
        bccButtonImg = findViewById(R.id.bcc_img);
        ccButtonImg = findViewById(R.id.cc_img);
        //[BUGFIX]-Add-END by SCDTABLET.weiwei.huang

        if (mCcBccButton != null) {
            mCcBccButton.setOnClickListener(this);
        }
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_S
        mFromRow = (LinearLayout) findViewById(R.id.compose_from_row);
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_E
        mCcBccView = (CcBccView) findViewById(R.id.cc_bcc_wrapper);
        mAttachmentsView = (AttachmentsView) findViewById(R.id.attachments);
        mTo = (RecipientEditTextView) findViewById(R.id.to);
        initializeRecipientEditTextView(mTo);
        mTo.setAlternatePopupAnchor(findViewById(R.id.compose_to_dropdown_anchor));
        mCc = (RecipientEditTextView) findViewById(R.id.cc);
        initializeRecipientEditTextView(mCc);
        mBcc = (RecipientEditTextView) findViewById(R.id.bcc);
        initializeRecipientEditTextView(mBcc);
        // TODO: add special chips text change watchers before adding
        // this as a text changed watcher to the to, cc, bcc fields.
        mSubject = (TextView) findViewById(R.id.subject);
        mSubject.setOnEditorActionListener(this);
        mSubject.setOnFocusChangeListener(this);
        mQuotedTextView = (QuotedTextView) findViewById(R.id.quoted_text_view);
        mQuotedTextView.setRespondInlineListener(this);
        mBodyView = (EditText) findViewById(R.id.body);
        mBodyView.setOnFocusChangeListener(this);
        //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_S
        mBodySignature = (ImageButton) findViewById(R.id.body_signature);
        //Note:When turn screen and has been click icon,set icon gone
        //if(mIsClickIcon){//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
        mBodySignature.setVisibility(View.GONE);
        //}
        mBodySignature.setOnClickListener(this);
        //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_E
        mFromStatic = findViewById(R.id.static_from_content);
        mFromStaticText = (TextView) findViewById(R.id.from_account_name);
        mFromSpinnerWrapper = findViewById(R.id.spinner_from_content);
        mFromSpinner = (FromAddressSpinner) findViewById(R.id.from_picker);
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
        mPriorityIcon = (ImageView) findViewById(R.id.priority_icon);
        setPriorityIcon(Message.FLAG_PRIORITY_NORMAL);
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_S
        mPriorityIcon.setOnClickListener(this);
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_E
        //[FEATURE]-Add-END by TSCD.chao zhang

        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/21/2014,FR631895(porting from FR487417)
        //TS: rong-tang 2016-04-06 EMAIL-1840992 MOD_S
        mToPicker = (ImageButton) findViewById(R.id.to_recipients_picker);
        mCcPicker = (ImageButton) findViewById(R.id.cc_recipients_picker);
        mBccPicker = (ImageButton) findViewById(R.id.bcc_recipients_picker);
        if (mToPicker != null && mCcPicker != null && mBccPicker != null) {
            // TS: xiaolin.li 2014-11-25 EMAIL READ_PLF MOD_S
            //if(getResources().getBoolean(R.bool.feature_email_recipients_picker_on)){
            if (PLFUtils.getBoolean(this, "feature_email_recipients_picker_on")) {
                // TS: xiaolin.li 2014-11-25 EMAIL READ_PLF MOD_E
                mToPicker.setVisibility(View.VISIBLE);
                mToPicker.setOnClickListener(this);
                /**we handle the visibility of cc and bcc in another class(CcBccView.java)**/
                // TS: zhaotianyong 2014-12-11 EMAIL BUGFIX-862341 ADD_S
                mCcPicker.setVisibility(View.VISIBLE);
                mCcPicker.setOnClickListener(this);
                mBccPicker.setVisibility(View.VISIBLE);
                mBccPicker.setOnClickListener(this);
                // TS: zhaotianyong 2014-12-11 EMAIL BUGFIX-862341 ADD_E
            } else {
                mToPicker.setVisibility(View.GONE);
                // TS: zhaotianyong 2014-12-11 EMAIL BUGFIX-862341 ADD_S
                mCcPicker.setVisibility(View.GONE);
                mBccPicker.setVisibility(View.GONE);
                // TS: zhaotianyong 2014-12-11 EMAIL BUGFIX-862341 ADD_E
            }
        }
        //TS: rong-tang 2016-04-06 EMAIL-1840992 MOD_E
        //[FEATURE]-Add-END by TSCD.chao zhang
        mToastBar = (ActionableToastBar) findViewById(R.id.toast_bar); //TS: jin.dong 2015-12-17 EMAIL BUGFIX_1170083 ADD
        //TS: jin.dong 2015-12-28 EMAIL BUGFIX_1193689 ADD_S
        //hide the compose btn
        View floatingComposeButton = findViewById(R.id.compose_button);
        if (floatingComposeButton != null) {
            floatingComposeButton.setVisibility(View.GONE);
        }
        //TS: jin.dong 2015-12-28 EMAIL BUGFIX_1193689 ADD_E
    }

    private void initializeRecipientEditTextView(RecipientEditTextView view) {
        view.setTokenizer(new Rfc822Tokenizer());
        view.setThreshold(COMPLETION_THRESHOLD);
        // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_S
        view.setDropDownVerticalOffset(0);
        // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_E
    }

    @Override
    public boolean onEditorAction(TextView view, int action, KeyEvent keyEvent) {
        if (action == EditorInfo.IME_ACTION_DONE) {
            focusBody();
            return true;
        }
        return false;
    }

    /**
     * Convert the body text (in {@link Spanned} form) to ready-to-send HTML format as a plain
     * String.
     *
     * @param body the body text including fancy style spans
     * @param removedComposing whether the function already removed composingSpans. Necessary
     *   because we cannot call removeComposingSpans from a background thread.
     * @return HTML formatted body that's suitable for sending or saving
     */
    private String spannedBodyToHtml(Spanned body, boolean removedComposing) {
        if (!removedComposing) {
            body = removeComposingSpans(body);
        }
        final HtmlifyBeginResult r = onHtmlifyBegin(body);
        return onHtmlifyEnd(Html.toHtml(r.result), r.extras);
    }

    /**
     * A hook for subclasses to convert custom spans in the body text prior to system HTML
     * conversion. That HTML conversion is lossy, so anything above and beyond its capability
     * has to be handled here.
     *
     * @param body
     * @return a copy of the body text with custom spans replaced with HTML
     */
    protected HtmlifyBeginResult onHtmlifyBegin(Spanned body) {
        return new HtmlifyBeginResult(body, null /* extras */);
    }

    protected String onHtmlifyEnd(String html, Object extras) {
        return html;
    }

    protected TextView getBody() {
        return mBodyView;
    }

    @VisibleForTesting
    public String getBodyHtml() {
        return spannedBodyToHtml(mBodyView.getText(), false);
    }

    @VisibleForTesting
    public Account getFromAccount() {
        return mReplyFromAccount != null && mReplyFromAccount.account != null ? mReplyFromAccount.account
                : mAccount;
    }

    private void clearChangeListeners() {
        mSubject.removeTextChangedListener(this);
        mBodyView.removeTextChangedListener(this);
        mTo.removeTextChangedListener(mToListener);
        mCc.removeTextChangedListener(mCcListener);
        mBcc.removeTextChangedListener(mBccListener);
        mFromSpinner.setOnAccountChangedListener(null);
        mAttachmentsView.setAttachmentChangesListener(null);
    }

    // Now that the message has been initialized from any existing draft or
    // ref message data, set up listeners for any changes that occur to the
    // message.
    private void initChangeListeners() {
        // Make sure we only add text changed listeners once!
        clearChangeListeners();
        mSubject.addTextChangedListener(this);
        mBodyView.addTextChangedListener(this);
        if (mToListener == null) {
            mToListener = new RecipientTextWatcher(mTo, this);
        }
        mTo.addTextChangedListener(mToListener);
        if (mCcListener == null) {
            mCcListener = new RecipientTextWatcher(mCc, this);
        }
        mCc.addTextChangedListener(mCcListener);
        if (mBccListener == null) {
            mBccListener = new RecipientTextWatcher(mBcc, this);
        }
        mBcc.addTextChangedListener(mBccListener);
        mFromSpinner.setOnAccountChangedListener(this);
        mAttachmentsView.setAttachmentChangesListener(this);
    }

    private void initActionBar() {
        LogUtils.d(LOG_TAG, "initializing action bar in ComposeActivity");
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar == null) {
            return;
        }
        if (mComposeMode == ComposeActivity.COMPOSE) {
            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
            actionBar.setTitle(R.string.compose);
        } else {
            actionBar.setTitle(null);
            // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 MOD_S
            if (mComposeModeAdapter == null) {
                mSupportReplyAll = isSupportReplyAll(mRefMessage);
                //mComposeModeAdapter = new ComposeModeAdapter(actionBar.getThemedContext());
                mComposeModeAdapter = new ComposeModeAdapter(actionBar.getThemedContext(), mSupportReplyAll);
            }
            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
            actionBar.setListNavigationCallbacks(mComposeModeAdapter, this);
            actionBar.setSelectedNavigationItem(getListPositionFromComposeMode(mComposeMode, mSupportReplyAll));
            /*
            switch (mComposeMode) {
            case ComposeActivity.REPLY:
                actionBar.setSelectedNavigationItem(0);
                break;
            case ComposeActivity.REPLY_ALL:
                actionBar.setSelectedNavigationItem(1);
                break;
            case ComposeActivity.FORWARD:
                actionBar.setSelectedNavigationItem(2);
                break;
            }
            */
            // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 MOD_E
        }
        actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
        actionBar.setHomeButtonEnabled(true);
    }

    private void initFromRefMessage(int action) {
        setFieldsFromRefMessage(action);

        // Check if To: address and email body needs to be prefilled based on extras.
        // This is used for reporting rendering feedback.
        if (MessageHeaderView.ENABLE_REPORT_RENDERING_PROBLEM) {
            Intent intent = getIntent();
            if (intent.getExtras() != null) {
                String toAddresses = intent.getStringExtra(EXTRA_TO);
                if (toAddresses != null) {
                    addToAddresses(Arrays.asList(TextUtils.split(toAddresses, ",")));
                }
                String body = intent.getStringExtra(EXTRA_BODY);
                if (body != null) {
                    setBody(body, false /* withSignature */);
                }
            }
        }
    }

    private void setFieldsFromRefMessage(int action) {
        setSubject(mRefMessage, action);
        // Setup recipients
        if (action == FORWARD) {
            mForward = true;
            mPriorityFlag = mRefMessage.mPriority;
            setPriorityIcon(mPriorityFlag);
        }
        initRecipientsFromRefMessage(mRefMessage, action);
        //TS: jian.xu 2015-11-13 EMAIL BUGFIX-863678 MOD_S
        //NOTE: Also need init quoted text when switch from compose mode.
        //TS: Gantao 2015-07-28 EMAIL BUGFIX_1053829 MOD_S
        //If we are editing a draft,we don't init quoted text.
        //if (mDraft == null ) {
        //TS: chaozhang 2016-01-13 EMAIL BUGFIX_1355979 MOD_S
        try {
            initQuotedTextFromRefMessage(mRefMessage, action);
        } catch (OutOfMemoryError oom) {
            LogUtils.e(LOG_TAG, "OOM happen during initRefMessage!!!", oom);
        }
        //TS: chaozhang 2015-01-13 EMAIL BUGFIX_1355979  MOD_E
        //}
        //TS: Gantao 2015-07-28 EMAIL BUGFIX_1053829 MOD_E
        //TS: jian.xu 2015-11-13 EMAIL BUGFIX-863678 MOD_E
        // TS: Gantao 2015-07-21 EMAIL BUGFIX_1047618 MOD_S
        //Note:very serious problem,only change to forward should initAttachments.
        if (action == ComposeActivity.FORWARD/* || mAttachmentsChanged*/) {
            // TS: Gantao 2015-07-21 EMAIL BUGFIX_1047618 MOD_E
            // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_S
            initAttachments(mRefMessage, action == ComposeActivity.FORWARD);
            // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_E
        }
    }

    protected HtmlTree.Converter<Spanned> getSpanConverter() {
        return new HtmlUtils.SpannedConverter();
    }

    private void initFromDraftMessage(Message message) {
        LogUtils.d(LOG_TAG, "Intializing draft from previous draft message: %s", message);

        mDraft = message;
        mDraftId = message.id;
        mSubject.setText(message.subject);
        mForward = message.draftType == UIProvider.DraftType.FORWARD;
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
        //[FEATURE]-Add-END by TSCD.chao zhang
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 ADD_S
        // Note: we should initialize priority flag from draf message
        mPriorityFlag = message.mPriority;
        setPriorityIcon(mPriorityFlag);
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 ADD_E
        final List<String> toAddresses = Arrays.asList(message.getToAddressesUnescaped());
        addToAddresses(toAddresses);
        addCcAddresses(Arrays.asList(message.getCcAddressesUnescaped()), toAddresses);
        addBccAddresses(Arrays.asList(message.getBccAddressesUnescaped()));
        if (message.hasAttachments) {
            List<Attachment> attachments = message.getAttachments();
            for (Attachment a : attachments) {
                addAttachmentAndUpdateView(a, true); //TS: zheng.zou 2015-05-28 EMAIL BUGFIX_-997631 MOD
            }
        }
        int quotedTextIndex = message.appendRefMessageContent ? message.quotedTextOffset : -1;
        // Set the body
        CharSequence quotedText = null;
        if (!TextUtils.isEmpty(message.bodyHtml)) {
            String body = message.bodyHtml;
            if (quotedTextIndex > -1) {
                // Find the offset in the html text of the actual quoted text and strip it out.
                // Note that the actual quotedTextOffset in the message has not changed as
                // this different offset is used only for display purposes. They point to different
                // parts of the original message.  Please see the comments in QuoteTextView
                // to see the differences.
                quotedTextIndex = QuotedTextView.findQuotedTextIndex(message.bodyHtml);
                if (quotedTextIndex > -1) {
                    body = message.bodyHtml.substring(0, quotedTextIndex);
                    quotedText = message.bodyHtml.subSequence(quotedTextIndex, message.bodyHtml.length());
                }
            }
            new HtmlToSpannedTask().execute(body);
        } else {
            final String body = message.bodyText;
            final CharSequence bodyText;
            if (TextUtils.isEmpty(body)) {
                bodyText = "";
                quotedText = null;
            } else {
                if (quotedTextIndex > body.length()) {
                    // Sanity check to guarantee that we will not over index the String.
                    // If this happens there is a bigger problem. This should never happen hence
                    // the wtf logging.
                    quotedTextIndex = -1;
                    LogUtils.wtf(LOG_TAG, "quotedTextIndex (%d) > body.length() (%d)", quotedTextIndex,
                            body.length());
                }
                bodyText = quotedTextIndex > -1 ? body.substring(0, quotedTextIndex) : body;
                if (quotedTextIndex > -1) {
                    quotedText = body.substring(quotedTextIndex);
                }
            }
            mBodyView.setText(bodyText);
        }
        if (quotedTextIndex > -1 && quotedText != null) {
            mQuotedTextView.setQuotedTextFromDraft(quotedText, mForward);
        }
    }

    //[BUGFIX]-ADD-BEGIN by TSNJ,wenlu.wu,10/20/2014,FR-739335
    private boolean mBodyAlreadySet = false;
    //[BUGFIX]-ADD-END by TSNJ,wenlu.wu,10/20/2014,FR-739335

    /**
     * Fill all the widgets with the content found in the Intent Extra, if any.
     * Also apply the same style to all widgets. Note: if initFromExtras is
     * called as a result of switching between reply, reply all, and forward per
     * the latest revision of Gmail, and the user has already made changes to
     * attachments on a previous incarnation of the message (as a reply, reply
     * all, or forward), the original attachments from the message will not be
     * re-instantiated. The user's changes will be respected. This follows the
     * web gmail interaction.
     * @return {@code true} if the activity should not call {@link #finishSetup}.
     */
    public boolean initFromExtras(Intent intent) {
        // If we were invoked with a SENDTO intent, the value
        // should take precedence
        final Uri dataUri = intent.getData();
        if (dataUri != null) {
            if (MAIL_TO.equals(dataUri.getScheme())) {
                initFromMailTo(dataUri.toString());
            } else {
                if (!mAccount.composeIntentUri.equals(dataUri)) {
                    String toText = dataUri.getSchemeSpecificPart();
                    // TS: junwei-xu 2015-03-23 EMAIL BUGFIX_980239 MOD_S
                    //if (toText != null) {
                    if (Address.isAllValid(toText)) {
                        // TS: junwei-xu 2015-04-23 EMAIL BUGFIX_980239 MOD_E
                        mTo.setText("");
                        addToAddresses(Arrays.asList(TextUtils.split(toText, ",")));
                    }
                }
            }
        }

        String[] extraStrings = intent.getStringArrayExtra(Intent.EXTRA_EMAIL);
        if (extraStrings != null) {
            addToAddresses(Arrays.asList(extraStrings));
        }
        extraStrings = intent.getStringArrayExtra(Intent.EXTRA_CC);
        if (extraStrings != null) {
            addCcAddresses(Arrays.asList(extraStrings), null);
        }
        extraStrings = intent.getStringArrayExtra(Intent.EXTRA_BCC);
        if (extraStrings != null) {
            addBccAddresses(Arrays.asList(extraStrings));
        }

        String extraString = intent.getStringExtra(Intent.EXTRA_SUBJECT);
        if (extraString != null) {
            mSubject.setText(extraString);
        }

        for (String extra : ALL_EXTRAS) {
            if (intent.hasExtra(extra)) {
                String value = intent.getStringExtra(extra);
                if (EXTRA_TO.equals(extra)) {
                    addToAddresses(Arrays.asList(TextUtils.split(value, ",")));
                } else if (EXTRA_CC.equals(extra)) {
                    addCcAddresses(Arrays.asList(TextUtils.split(value, ",")), null);
                } else if (EXTRA_BCC.equals(extra)) {
                    addBccAddresses(Arrays.asList(TextUtils.split(value, ",")));
                } else if (EXTRA_SUBJECT.equals(extra)) {
                    mSubject.setText(value);
                } else if (EXTRA_BODY.equals(extra)) {
                    //[BUGFIX]-Add-BEGINbySCDTABLET.yafang.wei,07/21/2016,2565329
                    // Modifytofixsignatureshowsbeforebodyissuewhensharewebsitebyemail
                    if (mBodyView.getText().toString().trim()
                            .equals(convertToPrintableSignature(mSignature).trim())) {
                        mBodyView.setText("");
                        setBody(value, true /* with signature */);
                        appendSignature();
                    } else {
                        setBody(value, true /* with signature */);
                    }
                    //[BUGFIX]-Add-ENDbySCDTABLET.yafang.wei
                } else if (EXTRA_QUOTED_TEXT.equals(extra)) {
                    initQuotedText(value, true /* shouldQuoteText */);
                }
            }
        }

        Bundle extras = intent.getExtras();
        //[BUGFIX]-MOD-BEGIN by TSNJ,wenlu.wu,10/20/2014,FR-739335
        if (extras != null && !mBodyAlreadySet) {
            //[BUGFIX]-MOD-END by TSNJ,wenlu.wu,10/20/2014,FR-739335
            CharSequence text = extras.getCharSequence(Intent.EXTRA_TEXT);
            //[BUGFIX]-Add-BEGINbySCDTABLET.yafang.wei,07/21/2016,2565329
            // Modifytofixsignatureshowsbeforebodyissuewhensharewebsitebyemail
            if (mBodyView.getText().toString().trim().equals(convertToPrintableSignature(mSignature).trim())) {
                mBodyView.setText("");
                setBody((text != null) ? text : "", true /* with signature */);
                appendSignature();
            } else {
                setBody((text != null) ? text : "", true /* with signature */);
            }
            //[BUGFIX]-Add-ENDbySCDTABLET.yafang.wei

            // TODO - support EXTRA_HTML_TEXT
        }

        mExtraValues = intent.getParcelableExtra(EXTRA_VALUES);
        if (mExtraValues != null) {
            LogUtils.d(LOG_TAG, "Launched with extra values: %s", mExtraValues.toString());
            initExtraValues(mExtraValues);
            return true;
        }

        return false;
    }

    protected void initExtraValues(ContentValues extraValues) {
        // DO NOTHING - Gmail will override
    }

    @VisibleForTesting
    protected String decodeEmailInUri(String s) throws UnsupportedEncodingException {
        // TODO: handle the case where there are spaces in the display name as
        // well as the email such as "Guy with spaces <guy+with+spaces@gmail.com>"
        // as they could be encoded ambiguously.
        // Since URLDecode.decode changes + into ' ', and + is a valid
        // email character, we need to find/ replace these ourselves before
        // decoding.
        try {
            return URLDecoder.decode(replacePlus(s), UTF8_ENCODING_NAME);
        } catch (IllegalArgumentException e) {
            if (LogUtils.isLoggable(LOG_TAG, LogUtils.VERBOSE)) {
                LogUtils.e(LOG_TAG, "%s while decoding '%s'", e.getMessage(), s);
            } else {
                LogUtils.e(LOG_TAG, e, "Exception  while decoding mailto address");
            }
            return null;
        }
    }

    /**
     * Replaces all occurrences of '+' with "%2B", to prevent URLDecode.decode from
     * changing '+' into ' '
     *
     * @param toReplace Input string
     * @return The string with all "+" characters replaced with "%2B"
     */
    private static String replacePlus(String toReplace) {
        return toReplace.replace("+", "%2B");
    }

    /**
     * Replaces all occurrences of '%' with "%25", to prevent URLDecode.decode from
     * crashing on decoded '%' symbols
     *
     * @param toReplace Input string
     * @return The string with all "%" characters replaced with "%25"
     */
    private static String replacePercent(String toReplace) {
        return toReplace.replace("%", "%25");
    }

    /**
     * Helper function to encapsulate encoding/decoding string from Uri.getQueryParameters
     * @param content Input string
     * @return The string that's properly escaped to be shown in mail subject/content
     */
    private static String decodeContentFromQueryParam(String content) {
        try {
            return URLDecoder.decode(replacePlus(replacePercent(content)), UTF8_ENCODING_NAME);
        } catch (UnsupportedEncodingException e) {
            LogUtils.e(LOG_TAG, "%s while decoding '%s'", e.getMessage(), content);
            return ""; // Default to empty string so setText/setBody has same behavior as before.
        }
    }

    /**
     * Initialize the compose view from a String representing a mailTo uri.
     * @param mailToString The uri as a string.
     */
    public void initFromMailTo(String mailToString) {
        // We need to disguise this string as a URI in order to parse it
        // TODO:  Remove this hack when http://b/issue?id=1445295 gets fixed
        Uri uri = Uri.parse("foo://" + mailToString);
        int index = mailToString.indexOf("?");
        int length = "mailto".length() + 1;
        String to;
        try {
            // Extract the recipient after mailto:
            if (index == -1) {
                to = decodeEmailInUri(mailToString.substring(length));
            } else {
                to = decodeEmailInUri(mailToString.substring(length, index));
            }
            if (!TextUtils.isEmpty(to)) {
                addToAddresses(Arrays.asList(TextUtils.split(to, ",")));
            }
        } catch (UnsupportedEncodingException e) {
            if (LogUtils.isLoggable(LOG_TAG, LogUtils.VERBOSE)) {
                LogUtils.e(LOG_TAG, "%s while decoding '%s'", e.getMessage(), mailToString);
            } else {
                LogUtils.e(LOG_TAG, e, "Exception  while decoding mailto address");
            }
        }

        List<String> cc = uri.getQueryParameters("cc");
        addCcAddresses(Arrays.asList(cc.toArray(new String[cc.size()])), null);

        List<String> otherTo = uri.getQueryParameters("to");
        addToAddresses(Arrays.asList(otherTo.toArray(new String[otherTo.size()])));

        List<String> bcc = uri.getQueryParameters("bcc");
        addBccAddresses(Arrays.asList(bcc.toArray(new String[bcc.size()])));

        // NOTE: Uri.getQueryParameters already decodes % encoded characters
        List<String> subject = uri.getQueryParameters("subject");
        if (subject.size() > 0) {
            mSubject.setText(decodeContentFromQueryParam(subject.get(0)));
        }

        List<String> body = uri.getQueryParameters("body");
        if (body.size() > 0) {
            setBody(decodeContentFromQueryParam(body.get(0)), true /* with signature */);
            mBodyAlreadySet = true;//[BUGFIX]-ADD by TSNJ,wenlu.wu,10/20/2014,FR-739335
        }
    }

    // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_S
    @VisibleForTesting
    protected void initAttachments(Message refMessage) {
        addAttachments(refMessage.getAttachments(), false);
    }

    protected void initAttachments(Message refMessage, boolean dropUnload) {
        addAttachments(refMessage.getAttachments(), dropUnload);
    }

    public long addAttachments(List<Attachment> attachments, boolean dropUnload) {
        long size = 0;
        AttachmentFailureException error = null;
        for (Attachment a : attachments) {
            try {
                // TS: zhaotianyong 2015-05-20 EMAIL BUGFIX-998884 ADD_S
                if (!supportSmartForward(mAccount) && !allAttachmentsLoad && dropUnload && !a.isPresentLocally()) { //TS: zheng.zou 2015-11-03 EMAIL BUGFIX_858353 MOD
                    // TS: zhaotianyong 2015-05-20 EMAIL BUGFIX-998884 ADD_E
                    continue;
                }
                size += mAttachmentsView.addAttachment(mAccount, a, false);//TS: yanhua.chen 2015-6-8 EMAIL CR_996908 MOD
                //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_S
                if (size > Settings.DEFAULT_MID_ATTACHMENT_SIZE && attLargeWarning) {
                    attLargeWarning = false;
                    showErrorToast(
                            getResources().getString(R.string.too_large_to_mid_attach_additional, AttachmentUtils
                                    .convertToHumanReadableSize(this, Settings.DEFAULT_MID_ATTACHMENT_SIZE)));
                }
                //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_E
            } catch (AttachmentFailureException e) {
                error = e;
            }
        }
        if (error != null) {
            LogUtils.e(LOG_TAG, error, "Error adding attachment");
            if (attachments.size() > 1) {
                showAttachmentTooBigToast(R.string.too_large_to_attach_multiple);
            } else {
                showAttachmentTooBigToast(error.getErrorRes());
            }
        }
        return size;
    }
    // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_E

    /**
     * When an attachment is too large to be added to a message, show a toast.
     * This method also updates the position of the toast so that it is shown
     * clearly above they keyboard if it happens to be open.
     */
    private void showAttachmentTooBigToast(int errorRes) {
        String maxSize = AttachmentUtils.convertToHumanReadableSize(getApplicationContext(),
                mAccount.settings.getMaxAttachmentSize());
        showErrorToast(getString(errorRes, maxSize));
    }

    //TS: kaifeng.lu 2016-3-25 EMAIL BUGFIX_1841392 MOD_S
    private void showErrorToast(String message) {
        //        Toast t = Toast.makeText(this, message, Toast.LENGTH_LONG);
        //        t.setText(message);
        //        t.show();
        if (mToastBar != null) {
            mToastBar.show(null, message, 0, true, null);
        }
    }
    //TS: kaifeng.lu 2016-3-25 EMAIL BUGFIX_1841392 MOD_E

    private void initAttachmentsFromIntent(Intent intent) {
        Bundle extras = intent.getExtras();
        if (extras == null) {
            extras = Bundle.EMPTY;
        }
        final String action = intent.getAction();
        if (!mAttachmentsChanged) {
            long totalSize = 0;
            if (extras.containsKey(EXTRA_ATTACHMENTS)) {
                String[] uris = (String[]) extras.getSerializable(EXTRA_ATTACHMENTS);
                for (String uriString : uris) {
                    final Uri uri = Uri.parse(uriString);
                    long size = 0;
                    try {
                        if (handleSpecialAttachmentUri(uri)) {
                            continue;
                        }

                        final Attachment a = mAttachmentsView.generateLocalAttachment(uri);
                        //TS: rong-tang 2016-03-02 EMAIL BUGFIX-1712549 ADD_S
                        if (a == null) {
                            continue;
                        }
                        //TS: rong-tang 2016-03-02 EMAIL BUGFIX-1712549 ADD_E
                        size = mAttachmentsView.addAttachment(mAccount, a, true);//TS: yanhua.chen 2015-6-8 EMAIL CR_996908 MOD

                        Analytics.getInstance().sendEvent("send_intent_attachment",
                                Utils.normalizeMimeType(a.getContentType()), null, size);

                    } catch (AttachmentFailureException e) {
                        LogUtils.e(LOG_TAG, e, "Error adding attachment");
                        showAttachmentTooBigToast(e.getErrorRes());
                    }
                    totalSize += size;
                }
            }
            if (extras.containsKey(Intent.EXTRA_STREAM)) {
                if (!PermissionUtil.checkAndRequestPermissionForResult(this,
                        Manifest.permission.READ_EXTERNAL_STORAGE,
                        PermissionUtil.REQ_CODE_PERMISSION_ADD_ATTACHMENT)) {
                    return;
                }
                if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
                    final ArrayList<Uri> uris = extras.getParcelableArrayList(Intent.EXTRA_STREAM);
                    ArrayList<Attachment> attachments = new ArrayList<Attachment>();
                    for (Uri uri : uris) {
                        if (uri == null) {
                            continue;
                        }
                        try {
                            if (handleSpecialAttachmentUri(uri)) {
                                continue;
                            }

                            final Attachment a = mAttachmentsView.generateLocalAttachment(uri);
                            //TS: rong-tang 2016-03-02 EMAIL BUGFIX-1712549 ADD_S
                            if (a == null) {
                                continue;
                            }
                            //TS: rong-tang 2016-03-02 EMAIL BUGFIX-1712549 ADD_E
                            attachments.add(a);

                            Analytics.getInstance().sendEvent("send_intent_attachment",
                                    Utils.normalizeMimeType(a.getContentType()), null, a.size);

                        } catch (AttachmentFailureException e) {
                            LogUtils.e(LOG_TAG, e, "Error adding attachment");
                            String maxSize = AttachmentUtils.convertToHumanReadableSize(getApplicationContext(),
                                    mAccount.settings.getMaxAttachmentSize());
                            showErrorToast(getString(R.string.generic_attachment_problem, maxSize));
                        }
                    }
                    // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_S
                    totalSize += addAttachments(attachments, false);
                    // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_E
                } else {
                    final Uri uri = extras.getParcelable(Intent.EXTRA_STREAM);
                    if (uri != null) {
                        long size = 0;
                        //[BUGFIX]-Modified-BEGIN by TCTNJ.wenlu.wu,12/03/2014,PR-857886
                        if (!handleSpecialAttachmentUri(uri)) {

                            new AsyncTask<Void, Void, Void>() {
                                @Override
                                protected Void doInBackground(Void... params) {
                                    try {
                                        Attachment mAttachment = mAttachmentsView.generateLocalAttachment(uri);
                                        android.os.Message msg = new android.os.Message();
                                        msg.what = 1001;
                                        msg.obj = mAttachment;
                                        mHandler.sendMessage(msg);
                                    } catch (AttachmentFailureException e) {
                                        LogUtils.e(LOG_TAG, e, "Error adding attachment");
                                        showAttachmentTooBigToast(e.getErrorRes());
                                    }
                                    return null;
                                }

                                @Override
                                protected void onPostExecute(Void result) {

                                }
                            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                        }
                        //[BUGFIX]-Modified-END by TCTNJ.wenlu.wu,12/03/2014,PR-857886

                        totalSize += size;
                    }
                }
            }

            if (totalSize > 0) {
                mAttachmentsChanged = true;
                updateSaveUi();

                Analytics.getInstance().sendEvent("send_intent_with_attachments",
                        Integer.toString(getAttachments().size()), null, totalSize);
            }
        }
    }

    private int getShareAttachmentSize(Intent intent) {
        Bundle extras = intent.getExtras();
        if (extras == null) {
            extras = Bundle.EMPTY;
        }
        final String action = intent.getAction();
        int totalSize = 0;
        if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
            final ArrayList<Uri> uris = extras.getParcelableArrayList(Intent.EXTRA_STREAM);
            ArrayList<Attachment> attachments = new ArrayList<Attachment>();
            for (Uri uri : uris) {
                if (uri == null) {
                    continue;
                }
                try {
                    if (handleSpecialAttachmentUri(uri)) {
                        continue;
                    }

                    final Attachment a = mAttachmentsView.generateLocalAttachment(uri);
                    //TS: rong-tang 2016-03-02 EMAIL BUGFIX-1712549 ADD_S
                    if (a == null) {
                        continue;
                    }
                    //TS: rong-tang 2016-03-02 EMAIL BUGFIX-1712549 ADD_E
                    attachments.add(a);

                    Analytics.getInstance().sendEvent("send_intent_attachment",
                            Utils.normalizeMimeType(a.getContentType()), null, a.size);

                } catch (AttachmentFailureException e) {
                    LogUtils.e(LOG_TAG, e, "Error adding attachment");
                    String maxSize = AttachmentUtils.convertToHumanReadableSize(getApplicationContext(),
                            mAccount.settings.getMaxAttachmentSize());
                    showErrorToast(getString(R.string.generic_attachment_problem, maxSize));
                }
            }
            // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_S
            totalSize += addAttachments(attachments, false);
            // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_E
        } else {
            final Uri uri = extras.getParcelable(Intent.EXTRA_STREAM);
            if (uri != null) {
                long size = 0;
                //[BUGFIX]-Modified-BEGIN by TCTNJ.wenlu.wu,12/03/2014,PR-857886
                if (!handleSpecialAttachmentUri(uri)) {

                    new AsyncTask<Void, Void, Void>() {
                        @Override
                        protected Void doInBackground(Void... params) {
                            try {
                                Attachment mAttachment = mAttachmentsView.generateLocalAttachment(uri);
                                android.os.Message msg = new android.os.Message();
                                msg.what = ADD_ATTACHMENT_MSG;
                                msg.obj = mAttachment;
                                mHandler.sendMessage(msg);
                            } catch (AttachmentFailureException e) {
                                LogUtils.e(LOG_TAG, e, "Error adding attachment");
                                // TS: jian.xu 2016-01-11 EMAIL BUGFIX-1307962 MOD_S
                                //Note: show toast must be on ui thread.
                                android.os.Message errorMsg = new android.os.Message();
                                errorMsg.what = ADD_ATTACHMENT_MSG_ERROR;
                                errorMsg.obj = e.getErrorRes();
                                mHandler.sendMessage(errorMsg);
                                //showAttachmentTooBigToast(e.getErrorRes());
                                // TS: jian.xu 2016-01-11 EMAIL BUGFIX-1307962 MOD_E
                            }
                            return null;
                        }

                        @Override
                        protected void onPostExecute(Void result) {

                        }
                    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                }
                //[BUGFIX]-Modified-END by TCTNJ.wenlu.wu,12/03/2014,PR-857886

                totalSize += size;
            }
        }
        return totalSize;
    }

    protected void initQuotedText(CharSequence quotedText, boolean shouldQuoteText) {
        mQuotedTextView.setQuotedTextFromHtml(quotedText, shouldQuoteText);
        mShowQuotedText = true;
    }

    private void initQuotedTextFromRefMessage(Message refMessage, int action) {
        if (mRefMessage != null && (action == REPLY || action == REPLY_ALL || action == FORWARD)) {
            mQuotedTextView.setQuotedText(action, refMessage, action != FORWARD);
        }
    }

    private void updateHideOrShowCcBcc() {
        // Its possible there is a menu item OR a button.
        boolean ccVisible = mCcBccView.isCcVisible();
        boolean bccVisible = mCcBccView.isBccVisible();
        if (mCcBccButton != null) {
            if (!ccVisible || !bccVisible) {
                mCcBccButton.setVisibility(View.VISIBLE);

                //[BUGFIX]-Add-BEGIN by SCDTABLET.weiwei.huang,05/03/2016,2013739,
                //[Email]Add mail contact icon display is not consistent
                bccButtonImg.setVisibility(View.VISIBLE);
                ccButtonImg.setVisibility(View.VISIBLE);
                //[BUGFIX]-Add-END by SCDTABLET.weiwei.huang

            } else {
                mCcBccButton.setVisibility(View.GONE);

                //[BUGFIX]-Add-BEGIN by SCDTABLET.weiwei.huang,05/03/2016,2013739,
                //[Email]Add mail contact icon display is not consistent
                bccButtonImg.setVisibility(View.GONE);
                ccButtonImg.setVisibility(View.GONE);
                //[BUGFIX]-Add-END by SCDTABLET.weiwei.huang
            }
        }
    }

    /**
     * Add attachment and update the compose area appropriately.
     */
    private void addAttachmentAndUpdateView(Intent data) {
        if (data == null) {
            return;
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            final ClipData clipData = data.getClipData();
            if (clipData != null) {
                for (int i = 0, size = clipData.getItemCount(); i < size; i++) {
                    addAttachmentAndUpdateView(clipData.getItemAt(i).getUri());
                }
                return;
            }
        }

        addAttachmentAndUpdateView(data.getData());
    }

    private void addAttachmentAndUpdateView(Uri contentUri) {
        if (contentUri == null) {
            return;
        }
        try {
            //TS: zheng.zou 2015-3-21 EMAIL BUGFIX_949589 ADD_S
            if (handleLongRunAttachmentUri(contentUri)) {
                return;
            }
            //TS: zheng.zou 2015-3-21 EMAIL BUGFIX_949589 ADD_E

            if (handleSpecialAttachmentUri(contentUri)) {
                return;
            }
            //TS: wenggangjin 2015-02-03 EMAIL BUGFIX_-920087 MOD_S
            //TS: wenggangjin 2015-02-28 EMAIL BUGFIX_-935421 MOD_S
            //            if(!TctDrmManager.isAllowForward(getFilePath(contentUri))){
            if (getFilePath(contentUri) != null && !"".equals(getFilePath(contentUri))
                    && !TctDrmManager.isAllowForward(getFilePath(contentUri))) {
                //TS: kaifeng.lu 2016-3-25 EMAIL BUGFIX_1841392 MOD_S
                //                Toast.makeText(this, R.string.drm_file_remind, Toast.LENGTH_LONG).show();
                showErrorToast(getResources().getString(R.string.drm_file_remind));
                //TS: kaifeng.lu 2016-3-25 EMAIL BUGFIX_1841392 MOD_E
                return;
            }
            //TS: wenggangjin 2015-02-28 EMAIL BUGFIX_-935421 MOD_E
            //TS: wenggangjin 2015-02-03 EMAIL BUGFIX_-920087 MOD_S
            addAttachmentAndUpdateView(mAttachmentsView.generateLocalAttachment(contentUri), false); //TS: zheng.zou 2015-05-28 EMAIL BUGFIX_-997631 MOD
        } catch (AttachmentFailureException e) {
            LogUtils.e(LOG_TAG, e, "Error adding attachment");
            showErrorToast(getResources().getString(e.getErrorRes(), AttachmentUtils.convertToHumanReadableSize(
                    getApplicationContext(), mAccount.settings.getMaxAttachmentSize())));
        }
    }

    /**
     * Allow subclasses to implement custom handling of attachments.
     *
     * @param contentUri a passed-in URI from a pick intent
     * @return true iff handled
     */
    protected boolean handleSpecialAttachmentUri(final Uri contentUri) {
        return false;
    }

    //TS: zheng.zou 2015-3-21 EMAIL BUGFIX_949589 ADD_S
    protected boolean handleLongRunAttachmentUri(final Uri contentUri) {
        if ("com.google.android.apps.photos.content".equals(contentUri.getAuthority())) {
            new LoadAttachmentTask().execute(contentUri);
            return true;
        }
        return false;
    }

    class LoadAttachmentTask extends AsyncTask<Uri, Void, Integer> {

        private boolean isCanceled;
        private static final int RESULT_SUCCESS = 0;
        private static final int RESULT_CANCELED = -1;
        private static final int RESULT_FAIL_DRM = -2;

        @Override
        protected void onPreExecute() {
            ProgressDialogFragment dialog = ProgressDialogFragment.showDialog(getFragmentManager(),
                    getString(R.string.add_file_attachment));
            dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialog) {
                    isCanceled = true;
                }
            });
        }

        @Override
        protected Integer doInBackground(Uri... params) {
            Uri contentUri = params[0];
            if (contentUri != null) {
                try {
                    if (getFilePath(contentUri) != null && !"".equals(getFilePath(contentUri))
                            && !TctDrmManager.isAllowForward(getFilePath(contentUri))) {
                        return RESULT_FAIL_DRM;
                    }
                    Attachment mAttachment = mAttachmentsView.generateLocalAttachment(contentUri);
                    if (isCanceled) {
                        return RESULT_CANCELED;
                    }
                    android.os.Message msg = new android.os.Message();
                    msg.what = 1001;
                    msg.obj = mAttachment;
                    mHandler.sendMessage(msg);
                } catch (AttachmentFailureException e) {
                    LogUtils.e(LOG_TAG, e, "Error adding attachment");
                    showAttachmentTooBigToast(e.getErrorRes());
                }
            }
            return RESULT_SUCCESS;
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);
            ProgressDialogFragment.dismissDialog(getFragmentManager(), getString(R.string.add_file_attachment));
            if (result == RESULT_FAIL_DRM) {
                //TS: kaifeng.lu 2016-3-25 EMAIL BUGFIX_1841392 MOD_S
                //                Toast.makeText(ComposeActivity.this, R.string.drm_file_remind, Toast.LENGTH_LONG).show();
                showErrorToast(getResources().getString(R.string.drm_file_remind));
                //TS: kaifeng.lu 2016-3-25 EMAIL BUGFIX_1841392 MOD_E
            }

        }
    }
    //TS: zheng.zou 2015-3-21 EMAIL BUGFIX_949589 ADD_E

    private void addAttachmentAndUpdateView(Attachment attachment, boolean isInit) { //TS: zheng.zou 2015-05-28 EMAIL BUGFIX_-997631 MOD
        try {
            //TS: rong-tang 2016-03-02 EMAIL BUGFIX-1712549 ADD_S
            if (attachment == null) {
                return;
            }
            //TS: rong-tang 2016-03-02 EMAIL BUGFIX-1712549 ADD_E
            long size = mAttachmentsView.addAttachment(mAccount, attachment, isInit);//TS: yanhua.chen 2015-6-8 EMAIL CR_996908 MOD
            if (size > 0) {
                //TS: zheng.zou 2015-05-28 EMAIL BUGFIX_-997631 MOD_S
                if (!isInit) {
                    mAttachmentsChanged = true;
                }
                //TS: zheng.zou 2015-05-28 EMAIL BUGFIX_-997631 MOD_E
                updateSaveUi();
            }
            //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_S
            long total = getTotalSize();
            if (total > Settings.DEFAULT_MID_ATTACHMENT_SIZE && attLargeWarning) {
                attLargeWarning = false;
                showErrorToast(getResources().getString(R.string.too_large_to_mid_attach_additional,
                        AttachmentUtils.convertToHumanReadableSize(this, Settings.DEFAULT_MID_ATTACHMENT_SIZE)));
            }
            //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_E
        } catch (AttachmentFailureException e) {
            LogUtils.e(LOG_TAG, e, "Error adding attachment");
            showAttachmentTooBigToast(e.getErrorRes());
        }
    }

    void initRecipientsFromRefMessage(Message refMessage, int action) {
        // Don't populate the address if this is a forward.
        if (action == ComposeActivity.FORWARD) {
            return;
        }
        initReplyRecipients(refMessage, action);
    }

    // TODO: This should be private.  This method shouldn't be used by ComposeActivityTests, as
    // it doesn't setup the state of the activity correctly
    @VisibleForTesting
    void initReplyRecipients(final Message refMessage, final int action) {
        // TS: junwei-xu 2015-03-25 EMAIL BUGFIX-958270 ADD_S
        final String[] sentToAddresses = refMessage.getToAddressesUnescaped();
        // TS: junwei-xu 2015-03-25 EMAIL BUGFIX-958270 ADD_E
        final Collection<String> toAddresses;
        final String[] fromAddresses = refMessage.getFromAddressesUnescaped();
        final String fromAddress = fromAddresses.length > 0 ? fromAddresses[0] : null;
        final String[] replyToAddresses = getReplyToAddresses(refMessage.getReplyToAddressesUnescaped(),
                fromAddress);

        // If this is a reply, the Cc list is empty. If this is a reply-all, the
        // Cc list is the union of the To and Cc recipients of the original
        // message, excluding the current user's email address and any addresses
        // already on the To list.
        // TS: junwei-xu 2015-05-13 EMAIL BUGFIX-1000343 MOD_S
        if (action == ComposeActivity.REPLY) {
            toAddresses = initToRecipients(fromAddress, replyToAddresses, sentToAddresses, action);
            addToAddresses(toAddresses);
            // TS: chenyanhua 2015-01-07 EMAIL BUGFIX-879794 ADD_S
            addBccMySelf(mAccount);
            // TS: chenyanhua 2015-01-07 EMAIL BUGFIX-879794 ADD_E
        } else if (action == ComposeActivity.REPLY_ALL) {
            // TS: zheng.zou 2015-06-08 EMAIL BUGFIX-1009174 MOD_S
            //note: use list instead of set to keep the add order.
            //            final Set<String> ccAddresses = Sets.newHashSet();
            final List<String> ccAddresses = new ArrayList<>();
            // TS: zheng.zou 2015-06-08 EMAIL BUGFIX-1009174 MOD_E
            toAddresses = initToRecipients(fromAddress, replyToAddresses, sentToAddresses, action);
            // TS: Gantao 2015-06-16 EMAIL BUGFIX-1019238 ADD_S
            // TS: Gantao 2015-06-24 EMAIL BUGFIX-1029207 MOD_S
            if (mSelectedAccountUnusual) {
                // delay to add to list
                mReplyToRecipientsLisnter = new ReplyToRecipientsListener() {
                    @Override
                    public void addToRecipients() {
                        addRecipients(toAddresses, sentToAddresses);
                        addToAddresses(toAddresses);
                    }
                };
            } else {
                addRecipients(toAddresses, sentToAddresses);
                addToAddresses(toAddresses);
            }
            // TS: Gantao 2015-06-24 EMAIL BUGFIX-1029207 MOD_E
            // TS: Gantao 2015-06-16 EMAIL BUGFIX-1019238 ADD_E
            // TS: junwei-xu 2015-03-25 EMAIL BUGFIX-958270 ADD_S
            //          addRecipients(ccAddresses, sentToAddresses);
            //          addRecipients(ccAddresses, refMessage.getCcAddressesUnescaped());
            //          addCcAddresses(ccAddresses, toAddresses);
            if (mAccount != null && mSelectedAccountUnusual) {// TS: Gantao 2015-06-18 EMAIL BUGFIX-1020548 MOD
                // delay to add cc list
                mReplyRecipientslistener = new ReplyRecipientsListener() {
                    public void addCCRecipients() {
                        //addRecipients(ccAddresses, sentToAddresses);
                        addRecipients(ccAddresses, refMessage.getCcAddressesUnescaped());
                        addCcAddresses(ccAddresses, toAddresses);
                    }
                };
            } else {
                //addRecipients(ccAddresses, sentToAddresses);
                addRecipients(ccAddresses, refMessage.getCcAddressesUnescaped());
                addCcAddresses(ccAddresses, toAddresses);
            }
            // TS: junwei-xu 2015-03-25 EMAIL BUGFIX-958270 ADD_E
            // TS: chenyanhua 2015-01-07 EMAIL BUGFIX-879794 ADD_S
            addBccMySelf(mAccount);
            // TS: chenyanhua 2015-01-07 EMAIL BUGFIX-879794 ADD_E
        }
        // TS: junwei-xu 2015-05-13 EMAIL BUGFIX-1000343 MOD_E
    }

    // If there is no reply to address, the reply to address is the sender.
    private static String[] getReplyToAddresses(String[] replyTo, String from) {
        boolean hasReplyTo = false;
        for (final String replyToAddress : replyTo) {
            if (!TextUtils.isEmpty(replyToAddress)) {
                hasReplyTo = true;
            }
        }
        return hasReplyTo ? replyTo : new String[] { from };
    }

    private void addToAddresses(Collection<String> addresses) {
        addAddressesToList(addresses, mTo);
    }

    private void addCcAddresses(Collection<String> addresses, Collection<String> toAddresses) {
        addCcAddressesToList(tokenizeAddressList(addresses),
                toAddresses != null ? tokenizeAddressList(toAddresses) : null, mCc);
    }

    private void addBccAddresses(Collection<String> addresses) {
        addAddressesToList(addresses, mBcc);
    }

    @VisibleForTesting
    protected void addCcAddressesToList(List<Rfc822Token[]> addresses, List<Rfc822Token[]> compareToList,
            RecipientEditTextView list) {
        String address;

        if (compareToList == null) {
            for (final Rfc822Token[] tokens : addresses) {
                for (final Rfc822Token token : tokens) {
                    address = token.toString();
                    list.append(address + END_TOKEN);
                }
            }
        } else {
            HashSet<String> compareTo = convertToHashSet(compareToList);
            for (final Rfc822Token[] tokens : addresses) {
                for (final Rfc822Token token : tokens) {
                    address = token.toString();
                    // Check if this is a duplicate:
                    if (!compareTo.contains(token.getAddress())) {
                        // Get the address here
                        list.append(address + END_TOKEN);
                    }
                }
            }
        }
    }

    private static HashSet<String> convertToHashSet(final List<Rfc822Token[]> list) {
        final HashSet<String> hash = new HashSet<String>();
        for (final Rfc822Token[] tokens : list) {
            for (final Rfc822Token token : tokens) {
                hash.add(token.getAddress());
            }
        }
        return hash;
    }

    protected List<Rfc822Token[]> tokenizeAddressList(Collection<String> addresses) {
        @VisibleForTesting
        List<Rfc822Token[]> tokenized = new ArrayList<Rfc822Token[]>();

        for (String address : addresses) {
            tokenized.add(Rfc822Tokenizer.tokenize(address));
        }
        return tokenized;
    }

    @VisibleForTesting
    void addAddressesToList(Collection<String> addresses, RecipientEditTextView list) {
        for (String address : addresses) {
            addAddressToList(address, list);
        }
    }

    //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 ADD_S
    void addAddressesToList(final long[] contactIds, final RecipientEditTextView list) {
        list.requestFocus(); // request the focus
        new EmailContactTask(list, contactIds).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    class EmailContactTask extends AsyncTask<Long, ArrayList<String>, ArrayList<String>> {
        RecipientEditTextView mView;
        long[] mids;

        EmailContactTask(RecipientEditTextView view, long[] ids) {
            this.mView = view;
            this.mids = ids;
        }

        @Override
        protected ArrayList<String> doInBackground(Long... params) {
            // TODO Auto-generated method stub         1266
            if (mids == null || mids.length <= 0 || this.mView == null) {
                return null;
            }
            ArrayList<String> StringAddress = new ArrayList<String>();
            ArrayList<Rfc822Token> addresses = getEmailAddressesFromContacts(mids);

            for (Rfc822Token token : addresses) {
                if (token != null)
                    //TS: junwei-xu 2015-12-22 EMAIL BUGFIX-1181863 MOD_S
                    //Note: use structure of name<address>
                    StringAddress.add(token.toString());
                //TS: junwei-xu 2015-12-22 EMAIL BUGFIX-1181863 MOD_E
            }

            return StringAddress;
        }

        @Override
        protected void onPostExecute(ArrayList<String> result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            if (result == null || result.size() <= 0) {
                return;
            }

            for (int i = 0; i < result.size(); i++) {
                if (this.mView instanceof MultiAutoCompleteTextView)
                    addAddressToList(result.get(i), mView);
            }

        }

        private ArrayList<Rfc822Token> getEmailAddressesFromContacts(long[] contactIds) {
            ArrayList<Rfc822Token> addresses = new ArrayList<Rfc822Token>();
            if (contactIds == null || contactIds.length <= 0) {
                return addresses;
            }
            StringBuilder selection = new StringBuilder();
            selection.append(ContactsContract.CommonDataKinds.Email._ID);
            selection.append(" IN (");
            selection.append(contactIds[0]);
            for (int i = 1; i < contactIds.length; i++) {
                selection.append(",");
                selection.append(contactIds[i]);
            }
            selection.append(")");
            Cursor c = null;
            try {
                c = getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, new String[] {
                        ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.Data.DISPLAY_NAME },
                        selection.toString(), null, null);
                if (c == null) {
                    return addresses;
                }
                String itemName, itemAddress;
                Rfc822Token token;
                while (c.moveToNext()) {
                    //TS: junwei-xu 2015-12-22 EMAIL BUGFIX-1181863 MOD_S
                    itemName = c.getString(1);
                    itemAddress = c.getString(0);
                    //TS: rong-tang 2016-03-28 EMAIL BUGFIX-1863457 MOD_S
                    itemName = Rfc822Validator.fixInvalidName(itemName);
                    //TS: rong-tang 2016-03-28 EMAIL BUGFIX-1863457 MOD_E
                    token = new Rfc822Token(itemName, itemAddress, null);
                    addresses.add(token);
                    //TS: junwei-xu 2015-12-22 EMAIL BUGFIX-1181863 MOD_E
                }
                return addresses;
            } finally {
                if (c != null) {
                    c.close();
                }
            }
        }

    }
    //TS:kaifeng.lu 2015-10-28 EMAIL BUGFIX_824294 ADD_E

    private static void addAddressToList(final String address, final RecipientEditTextView list) {
        if (address == null || list == null)
            return;

        final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);

        for (final Rfc822Token token : tokens) {
            list.append(token + END_TOKEN);
        }
    }

    // TS: junwei-xu 2015-05-13 EMAIL BUGFIX-1000343 ADD_S
    @VisibleForTesting
    protected Collection<String> initToRecipients(final String fullSenderAddress, final String[] replyToAddresses,
            final String[] inToAddresses, final int action) {
        // The To recipient is the reply-to address specified in the original
        // message, unless it is:
        // the current user OR a custom from of the current user, in which case
        // it's the To recipient list of the original message.
        // OR missing, in which case use the sender of the original message
        // TS: zheng.zou 2015-06-08 EMAIL BUGFIX-1009174 MOD_S
        //note: use list instead of set to keep the add order.
        //        Set<String> toAddresses = Sets.newHashSet();
        List<String> toAddresses = new ArrayList<>();
        // TS: zheng.zou 2015-06-08 EMAIL BUGFIX-1009174 MOD_E
        for (final String replyToAddress : replyToAddresses) {
            if (!TextUtils.isEmpty(replyToAddress)
                    // TS: Gantao 2015-06-09 EMAIL BUGFIX-1019278 MOD
                    && !recipientMatchesThisAccount(replyToAddress) || mSelectedAccountUnusual) {
                toAddresses.add(replyToAddress);
            }
        }
        // TS: Gantao 2015-06-16 EMAIL BUGFIX-1019238 DEL_S
        //        if (action == ComposeActivity.REPLY_ALL) {
        //            for (String address : inToAddresses) {
        //                if (!TextUtils.isEmpty(address)
        //                        && !recipientMatchesThisAccount(address)) {
        //                    toAddresses.add(address);
        //                }
        //            }
        //        }
        // TS: Gantao 2015-06-16 EMAIL BUGFIX-1019238 DEL_E
        if (toAddresses.size() == 0) {
            // In this case, the user is replying to a message in which their
            // current account or some of their custom from addresses are the only
            // recipients and they sent the original message.
            if (inToAddresses.length == 1 && recipientMatchesThisAccount(fullSenderAddress)
                    && recipientMatchesThisAccount(inToAddresses[0])) {
                toAddresses.add(inToAddresses[0]);
                return toAddresses;
            }
            // This happens if the user replies to a message they originally
            // wrote. In this case, "reply" really means "re-send," so we
            // target the original recipients. This works as expected even
            // if the user sent the original message to themselves.
            for (String address : inToAddresses) {
                if (!recipientMatchesThisAccount(address)) {
                    toAddresses.add(address);
                }
            }
        }
        return toAddresses;
    }
    // TS: junwei-xu 2015-05-13 EMAIL BUGFIX-1000343 ADD_E

    private void addRecipients(final Collection<String> recipients, final String[] addresses) { // TS: zheng.zou 2015-06-08 EMAIL BUGFIX-1009174 MOD
        for (final String email : addresses) {
            // Do not add this account, or any of its custom from addresses, to
            // the list of recipients.
            final String recipientAddress = Address.getEmailAddress(email).getAddress();
            if (!recipientMatchesThisAccount(recipientAddress)) {
                recipients.add(email.replace("\"\"", ""));
            }
        }
    }

    /**
     * A recipient matches this account if it has the same address as the
     * currently selected account OR one of the custom from addresses associated
     * with the currently selected account.
     * @param recipientAddress address we are comparing with the currently selected account
     */
    protected boolean recipientMatchesThisAccount(String recipientAddress) {
        return ReplyFromAccount.matchesAccountOrCustomFrom(mAccount, recipientAddress, mAccount.getReplyFroms());
    }

    /**
     * Returns a formatted subject string with the appropriate prefix for the action type.
     * E.g., "FWD: " is prepended if action is {@link ComposeActivity#FORWARD}.
     */
    public static String buildFormattedSubject(Resources res, String subject, int action) {
        final String prefix;
        final String correctedSubject;
        if (action == ComposeActivity.COMPOSE) {
            prefix = "";
        } else if (action == ComposeActivity.FORWARD) {
            prefix = res.getString(R.string.forward_subject_label);
        } else {
            prefix = res.getString(R.string.reply_subject_label);
        }

        if (TextUtils.isEmpty(subject)) {
            correctedSubject = prefix;
        } else {
            // Don't duplicate the prefix
            if (subject.toLowerCase().startsWith(prefix.toLowerCase())) {
                correctedSubject = subject;
            } else {
                correctedSubject = String.format(res.getString(R.string.formatted_subject), prefix, subject);
            }
        }

        return correctedSubject;
    }

    private void setSubject(Message refMessage, int action) {
        mSubject.setText(buildFormattedSubject(getResources(), refMessage.subject, action));
    }

    private void initRecipients() {
        setupRecipients(mTo);
        setupRecipients(mCc);
        setupRecipients(mBcc);
    }

    private void setupRecipients(RecipientEditTextView view) {
        final DropdownChipLayouter layouter = getDropdownChipLayouter();
        if (layouter != null) {
            view.setDropdownChipLayouter(layouter);
        }
        view.setAdapter(getRecipientAdapter());
        view.setRecipientEntryItemClickedListener(this);
        if (mValidator == null) {
            final String accountName = mAccount.getEmailAddress();
            int offset = accountName.indexOf("@") + 1;
            String account = accountName;
            if (offset > 0) {
                account = account.substring(offset);
            }
            mValidator = new Rfc822Validator(account);
        }
        view.setValidator(mValidator);
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
        addBccMySelf(mAccount);
        //after added the address,we display the cc and bcc field.
        if (mAddBccBySetting && (mBcc != null && view == mBcc) && !mCcBccView.isBccVisible()) {
            showCcBccViews();
        }
        //[FEATURE]-Add-END by TSCD.chao zhang
    }

    /**
     * Derived classes should override if they wish to provide their own autocomplete behavior.
     */
    public BaseRecipientAdapter getRecipientAdapter() {
        return new RecipientAdapter(this, mAccount);
    }

    /**
     * Derived classes should override this to provide their own dropdown behavior.
     * If the result is null, the default {@link com.tct.ex.chips.DropdownChipLayouter}
     * is used.
     */
    public DropdownChipLayouter getDropdownChipLayouter() {
        return null;
    }

    @Override
    public void onClick(View v) {
        int requestCode = -1;//[FEATURE]-Add by TCTNB.chen caixia,09/11/2013,FR 487417
        final int id = v.getId();
        if (id == R.id.add_cc_bcc) {
            // Verify that cc/ bcc aren't showing.
            // Animate in cc/bcc.
            showCcBccViews();
            // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_S
        } else if (id == R.id.priority_icon) {
            setPriority();
        }
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_E
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/21/2014,FR631895(porting from FR487417)
        else if (id == R.id.to_recipients_picker) {
            //TS: rong-tang 2016-04-06 EMAIL-1840992 ADD_S
            //Note: when click picker, request focus to picker, recipient edit text view need to submit change.
            //[BUGFIX]-Mod-BEGINbySCDTABLET.aijian.shi,09/07/2016,2848507,
            // UseclearFocustoavoidtheimproperfocus afterenteringtherecipientedit textview.
            //mToPicker.requestFocusFromTouch();
            mTo.clearFocus();
            //[BUGFIX]-Mod-ENDbySCDTABLET.aijian.shi
            //TS: rong-tang 2016-04-06 EMAIL-1840992 ADD_E
            mLaunchContact = true; //TS: xinlei.sheng 2015-01-26 EMAIL FIXBUG_886976 ADD
            requestCode = ACTIVITY_REQUEST_PICK_CONTACT_TO;
            Intent toPickEmail = createAddContactIntent(mTo);
            //TS: jian.xu 2015-09-17 EMAIL-1085945 MOD_S
            try {
                startActivityForResult(toPickEmail, requestCode);
            } catch (ActivityNotFoundException e) {
                LogUtils.e(LOG_TAG, e, "Exception attempting to choose person from contact app");
            }
        } else if (id == R.id.cc_recipients_picker) {
            //TS: rong-tang 2016-04-06 EMAIL-1840992 ADD_S
            //Note: when click picker, request focus to picker, recipient edit text view need to submit change.
            //[BUGFIX]-Mod-BEGINbySCDTABLET.aijian.shi,09/07/2016,2848507,
            // UseclearFocustoavoidtheimproperfocus afterenteringtherecipientedit textview.
            //mCcPicker.requestFocusFromTouch();
            mCc.clearFocus();
            //[BUGFIX]-Mod-ENDbySCDTABLET.aijian.shi
            //TS: rong-tang 2016-04-06 EMAIL-1840992 ADD_E
            mLaunchContact = true; //TS: xinlei.sheng 2015-01-26 EMAIL FIXBUG_886976 ADD
            requestCode = ACTIVITY_REQUEST_PICK_CONTACT_CC;
            Intent ccPickEmail = createAddContactIntent(mCc);
            //TS: jian.xu 2015-09-17 EMAIL-1085945 MOD_S
            try {
                startActivityForResult(ccPickEmail, requestCode);
            } catch (ActivityNotFoundException e) {
                LogUtils.e(LOG_TAG, e, "Exception attempting to choose person from contact app");
            }
            //TS: jian.xu 2015-09-17 EMAIL-1085945 MOD_E
        } else if (id == R.id.bcc_recipients_picker) {
            //TS: rong-tang 2016-04-06 EMAIL-1840992 ADD_S
            //Note: when click picker, request focus to picker, recipient edit text view need to submit change.
            //[BUGFIX]-Mod-BEGINbySCDTABLET.aijian.shi,09/07/2016,2848507,
            // UseclearFocustoavoidtheimproperfocus afterenteringtherecipientedit textview.
            //mBccPicker.requestFocusFromTouch();
            mBcc.clearFocus();
            //[BUGFIX]-Mod-ENDbySCDTABLET.aijian.shi
            //TS: rong-tang 2016-04-06 EMAIL-1840992 ADD_E
            mLaunchContact = true; //TS: xinlei.sheng 2015-01-26 EMAIL FIXBUG_886976 ADD
            requestCode = ACTIVITY_REQUEST_PICK_CONTACT_BCC;
            Intent bccPickEmail = createAddContactIntent(mBcc);
            //TS: jian.xu 2015-09-17 EMAIL-1085945 MOD_S
            try {
                startActivityForResult(bccPickEmail, requestCode);
            } catch (ActivityNotFoundException e) {
                LogUtils.e(LOG_TAG, e, "Exception attempting to choose person from contact app");
            }
            //TS: jian.xu 2015-09-17 EMAIL-1085945 MOD_E
        }
        //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_S
        else if (id == R.id.body_signature) {
            //mIsClickIcon = true;//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
            mBodySignature.setVisibility(View.GONE);
            //Note:when click icon,append the signature to body
            appendSignature();
        }
        //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_E
        //[FEATURE]-Add-END by TSCD.chao zhang
    }

    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/21/2014,FR631895(porting from FR487417)
    private Intent createAddContactIntent(RecipientEditTextView textview) {
        //TS: junwei-xu 2016-03-01 EMAIL BUGFIX-1612750 ADD_S
        //Note: different intent for different platform.
        Intent intent = new Intent();
        String platform = PLFUtils.getString(this, "feature_email_platform");
        if (platform.equals(PLATFORM_QUALCOMM)) {
            intent.setAction(ACTION_MULTI_PICK_EMAIL_QUALCOMM);
            intent.setData(Contacts.CONTENT_URI);
        } else if (platform.equals(PLATFORM_MTK)) {
            intent.setAction(ACTION_MULTI_PICK_EMAIL_MTK);
            intent.setType(ContactsContract.CommonDataKinds.Email.CONTENT_TYPE);
        }
        //TS: junwei-xu 2016-03-01 EMAIL BUGFIX-1612750 ADD_E

        // We have to wait for the constructing complete.
        com.tct.emailcommon.mail.Address[] addresses = getAddressesIgnoreValid(textview);
        int addressCount = addresses.length;

        if (addressCount > 0) {
            Bundle bundle = new Bundle();
            Bundle mChoiceSet = new Bundle();
            com.tct.emailcommon.mail.Address address;
            for (int i = 0; i < addressCount; i++) {
                address = addresses[i];
                //[BUGFIX]-Mod-BEGIN by TSCD.chao zhang,05/29/2014,PR 682725,[Email]Can't add mail address from contact option.
                //fix Email address change to invalid when replay mail.
                String name = address.getPersonal();
                if (name != null && !name.equals(address.getAddress())) {
                    if (name.matches(".*[\\(\\)<>@,;:\\\\\".\\[\\]].*")) {
                        name = com.tct.emailcommon.mail.Address.ensureQuotedString(name);
                    }
                }
                mChoiceSet.putStringArray(String.valueOf(i), new String[] { name, address.getAddress() });
                //[BUGFIX]-Mod-END by TSCD.chao zhang
            }
            bundle.putBundle(EXTRA_PICK_EMAIL_BUNDLE, mChoiceSet);
            intent.putExtras(bundle);
        }
        return intent;
    }

    //[FEATURE]-Add-END by TSCD.chao zhang
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        final int id = v.getId();
        if (hasFocus && (id == R.id.subject || id == R.id.body)) {
            //PR961829 zhiqiang.shao begin
            boolean showCcBccEmpty = TextUtils.isEmpty(mCc.getText()) && TextUtils.isEmpty(mBcc.getText());
            if (showCcBccEmpty) {
                mCcBccView.show(false /* animate */, false, false);
                mCcBccButton.setVisibility(View.VISIBLE);

                //[BUGFIX]-Add-BEGIN by SCDTABLET.weiwei.huang,05/03/2016,2013739,
                //[Email]Add mail contact icon display is not consistent
                bccButtonImg.setVisibility(View.VISIBLE);
                ccButtonImg.setVisibility(View.VISIBLE);
                //[BUGFIX]-Add-END by SCDTABLET.weiwei.huang

            }
            // Collapse cc/bcc iff both are empty
            final boolean showCcBccFields = !TextUtils.isEmpty(mCc.getText()) || !TextUtils.isEmpty(mBcc.getText());
            //            mCcBccView.show(false /* animate */, showCcBccFields, showCcBccFields);
            //            mCcBccButton.setVisibility(showCcBccFields ? View.GONE : View.VISIBLE);
            //PR961829 zhiqiang.shao end
            // On phones autoscroll down so that Cc aligns to the top if we are showing cc/bcc.
            if (getResources().getBoolean(R.bool.auto_scroll_cc) && showCcBccFields) {
                final int[] coords = new int[2];
                mCc.getLocationOnScreen(coords);

                // Subtract status bar and action bar height from y-coord.
                final Rect rect = new Rect();
                getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
                final int deltaY = coords[1] - getSupportActionBar().getHeight() - rect.top;

                // Only scroll down
                if (deltaY > 0) {
                    mScrollView.smoothScrollBy(0, deltaY);
                }
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        final boolean superCreated = super.onCreateOptionsMenu(menu);
        // Don't render any menu items when there are no accounts.
        if (mAccounts == null || mAccounts.length == 0) {
            return superCreated;
        }
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.compose_menu, menu);

        /*
         * Start save in the correct enabled state.
         * 1) If a user launches compose from within gmail, save is disabled
         * until they add something, at which point, save is enabled, auto save
         * on exit; if the user empties everything, save is disabled, exiting does not
         * auto-save
         * 2) if a user replies/ reply all/ forwards from within gmail, save is
         * disabled until they change something, at which point, save is
         * enabled, auto save on exit; if the user empties everything, save is
         * disabled, exiting does not auto-save.
         * 3) If a user launches compose from another application and something
         * gets populated (attachments, recipients, body, subject, etc), save is
         * enabled, auto save on exit; if the user empties everything, save is
         * disabled, exiting does not auto-save
         */
        mSave = menu.findItem(R.id.save);
        String action = getIntent() != null ? getIntent().getAction() : null;
        boolean fromWidget = getIntent() != null && getIntent().getBooleanExtra(EXTRA_FROM_EMAIL_WIDGET, false);//TS: zheng.zou 2015-12-09 EMAIL BUGFIX_1059178 ADD
        enableSave(mInnerSavedState != null ? mInnerSavedState.getBoolean(EXTRA_SAVE_ENABLED)
                : ((Intent.ACTION_SEND.equals(action) && !fromWidget) //TS: zheng.zou 2015-12-09 EMAIL BUGFIX_1059178 MOD
                        || Intent.ACTION_SEND_MULTIPLE.equals(action) || Intent.ACTION_SENDTO.equals(action)
                        || shouldSave()));

        final MenuItem helpItem = menu.findItem(R.id.help_info_menu_item);
        final MenuItem sendFeedbackItem = menu.findItem(R.id.feedback_menu_item);
        //TS: Gantao 2015-7-13 EMAIL FEATURE_1033148 DEL_S
        //For feature:long click the attachment icon menu can show the menu discription,
        //we should remove its useless sub menu.
        //        final MenuItem attachFromServiceItem = menu.findItem(R.id.attach_from_service_stub1);
        //TS: Gantao 2015-7-13 EMAIL FEATURE_1033148 DEL_E
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
        MenuItem priority = menu.findItem(R.id.priority);
        if (priority != null) {
            // TS: xiaolin.li 2014-11-25 EMAIL READ_PLF MOD_S
            //priority.setVisible(getResources().getBoolean(R.bool.feature_email_priority_on));
            priority.setVisible(PLFUtils.getBoolean(this, "feature_email_priority_on"));
            // TS: xiaolin.li 2014-11-25 EMAIL READ_PLF MOD_E
        }
        //[FEATURE]-Add-END by TSCD.chao zhang
        if (helpItem != null) {
            helpItem.setVisible(mAccount != null && mAccount.supportsCapability(AccountCapabilities.HELP_CONTENT));
        }
        if (sendFeedbackItem != null) {
            sendFeedbackItem
                    .setVisible(mAccount != null && mAccount.supportsCapability(AccountCapabilities.SEND_FEEDBACK));
        }
        //TS: Gantao 2015-7-13 EMAIL FEATURE_1033148 DEL_S
        //For feature:long click the attachment icon menu can show the menu discription,
        //we should remove its useless sub menu.
        //        if (attachFromServiceItem != null) {
        //            attachFromServiceItem.setVisible(shouldEnableAttachFromServiceMenu(mAccount));
        //        }
        //TS: Gantao 2015-7-13 EMAIL FEATURE_1033148 DEL_E

        //TS: yanhua.chen 2015-4-30 EMAIL BUGFIX_989399 MOD_S
        // Show attach picture on pre-K devices.
        //menu.findItem(R.id.add_photo_attachment).setVisible(!Utils.isRunningKitkatOrLater());
        //TS: yanhua.chen 2015-4-30 EMAIL BUGFIX_989399 MOD_E

        // TS: Gantao 2015-10-30 EMAIL FEATURE_1104470 ADD_S
        MenuItem saveGroupItem = menu.findItem(R.id.group);
        if (saveGroupItem != null) {
            saveGroupItem.setVisible(PLFUtils.getBoolean(this, "feature_email_save_group"));
        }
        // TS: Gantao 2015-10-30 EMAIL FEATURE_1104470 ADD_E

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        final int id = item.getItemId();

        Analytics.getInstance().sendMenuItemEvent(Analytics.EVENT_CATEGORY_MENU_ITEM, id, "compose", 0);

        boolean handled = true;
        //TS: yanhua.chen 2015-4-30 EMAIL BUGFIX_989399 MOD_S
        //if (id == R.id.add_file_attachment) {
        //    doAttach(MIME_TYPE_ALL);
        //} else if (id == R.id.add_photo_attachment) {
        //    doAttach(MIME_TYPE_PHOTO);
        if (id == R.id.add_attachment) {
            doAttach(MIME_TYPE_ALL);
            //TS: yanhua.chen 2015-4-30 EMAIL BUGFIX_989399 MOD_E
        } else if (id == R.id.save) {
            doSave(true);
        } else if (id == R.id.send) {
            doSend();
        } else if (id == R.id.discard) {
            doDiscard();
        } else if (id == R.id.settings) {
            Utils.showSettings(this, mAccount);
        } else if (id == android.R.id.home) {
            onAppUpPressed();
        } else if (id == R.id.help_info_menu_item) {
            Utils.showHelp(this, mAccount, getString(R.string.compose_help_context));
            //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
        } else if (id == R.id.priority) {
            setPriority();
            //[FEATURE]-Add-END by TSCD.chao zhang
            // TS: Gantao 2015-10-30 EMAIL FEATURE_1104470 ADD_S
        } else if (id == R.id.group) {
            showSaveGroupDialog();
            // TS: Gantao 2015-10-30 EMAIL FEATURE_1104470 ADD_E
        } else {
            handled = false;
        }
        return handled || super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        // If we are showing the wait fragment, just exit.
        if (getWaitFragment() != null) {
            finish();
        } else {
            super.onBackPressed();
        }
    }

    /**
     * Carries out the "up" action in the action bar.
     */
    private void onAppUpPressed() {
        if (mLaunchedFromEmail) {
            // If this was started from Gmail, simply treat app up as the system back button, so
            // that the last view is restored.
            onBackPressed();
            return;
        }

        // Fire the main activity to ensure it launches the "top" screen of mail.
        // Since the main Activity is singleTask, it should revive that task if it was already
        // started.
        final Intent mailIntent = Utils.createViewInboxIntent(mAccount);
        mailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
        startActivity(mailIntent);
        finish();
    }

    private void doSend() {
        // TS: zhaotianyong 2015-03-31 EMAIL BUGFIX-963249 ADD_S
        doSend = true;
        // TS: zhaotianyong 2015-03-31 EMAIL BUGFIX-963249 ADD_E
        //TS: xiangnan.zhou 2016-03-23 EMAIL BUGFIX-1783199 ADD_S
        hideInputSoftKeyboard();
        //TS: xiangnan.zhou 2016-03-23 EMAIL BUGFIX-1783199 ADD_E
        sendOrSaveWithSanityChecks(false, true, false, false);
        logSendOrSave(false /* save */);
        mPerformedSendOrDiscard = true;
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 DEL_S
        // Note: we don't use sharedpreferences to save priority, no need to reset it
        /*
        // TS: chenyanhua 2015-02-15 EMAIL BUGFIX-932308 ADD_S
        resetPriorityFlag();
        // TS: chenyanhua 2015-02-15 EMAIL BUGFIX-932308 ADD_E
        */
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 DEL_E
    }

    // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 DEL_S
    // Note: we don't use sharedpreferences to save priority, no need it
    /*
    // TS: chenyanhua 2015-02-15 EMAIL BUGFIX-932308 ADD_S
    private void resetPriorityFlag(){
    SharedPreferences.Editor editor = getSharedPreferences("PriorityFlag", Context.MODE_PRIVATE).edit();
    editor.putInt("mPriorityFlag", Message.FLAG_PRIORITY_NORMAL);
    editor.commit();
    }
    // TS: chenyanhua 2015-02-15 EMAIL BUGFIX-932308 ADD_E
    */
    // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 DEL_E
    private void doSave(boolean showToast) {
        sendOrSaveWithSanityChecks(true, showToast, false, false);
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 DEL_S
        // Note: we don't use sharedpreferences to save priority, no need to reset it
        /*
        // TS: chenyanhua 2015-02-15 EMAIL BUGFIX-932308 ADD_S
        resetPriorityFlag();
        // TS: chenyanhua 2015-02-15 EMAIL BUGFIX-932308 ADD_E
        */
        // TS: junwei-xu 2015-06-30 EMAIL BUGFIX-1030195 DEL_E
    }

    @Override
    public void onRecipientEntryItemClicked(int charactersTyped, int position) {
        // Send analytics of characters typed and position in dropdown selected.
        Analytics.getInstance().sendEvent("suggest_click", Integer.toString(charactersTyped),
                Integer.toString(position), 0);
    }

    @VisibleForTesting
    public interface SendOrSaveCallback {
        void initializeSendOrSave(SendOrSaveTask sendOrSaveTask);

        void notifyMessageIdAllocated(SendOrSaveMessage sendOrSaveMessage, Message message);

        Message getMessage();

        void sendOrSaveFinished(SendOrSaveTask sendOrSaveTask, boolean success);

        void incrementRecipientsTimesContacted(List<String> recipients);
    }

    @VisibleForTesting
    public static class SendOrSaveTask implements Runnable {
        private final Context mContext;
        @VisibleForTesting
        public final SendOrSaveCallback mSendOrSaveCallback;
        @VisibleForTesting
        public final SendOrSaveMessage mSendOrSaveMessage;
        private ReplyFromAccount mExistingDraftAccount;

        public SendOrSaveTask(Context context, SendOrSaveMessage message, SendOrSaveCallback callback,
                ReplyFromAccount draftAccount) {
            mContext = context;
            mSendOrSaveCallback = callback;
            mSendOrSaveMessage = message;
            mExistingDraftAccount = draftAccount;
        }

        @Override
        public void run() {
            final SendOrSaveMessage sendOrSaveMessage = mSendOrSaveMessage;

            final ReplyFromAccount selectedAccount = sendOrSaveMessage.mAccount;
            Message message = mSendOrSaveCallback.getMessage();
            long messageId = message != null ? message.id : UIProvider.INVALID_MESSAGE_ID;
            // If a previous draft has been saved, in an account that is different
            // than what the user wants to send from, remove the old draft, and treat this
            // as a new message
            if (mExistingDraftAccount != null
                    && !selectedAccount.account.uri.equals(mExistingDraftAccount.account.uri)) {
                if (messageId != UIProvider.INVALID_MESSAGE_ID) {
                    ContentResolver resolver = mContext.getContentResolver();
                    ContentValues values = new ContentValues();
                    values.put(BaseColumns._ID, messageId);
                    if (mExistingDraftAccount.account.expungeMessageUri != null) {
                        new ContentProviderTask.UpdateTask().run(resolver,
                                mExistingDraftAccount.account.expungeMessageUri, values, null, null);
                    } else {
                        // TODO(mindyp) delete the conversation.
                    }
                    // reset messageId to 0, so a new message will be created
                    messageId = UIProvider.INVALID_MESSAGE_ID;
                }
            }

            final long messageIdToSave = messageId;
            sendOrSaveMessage(messageIdToSave, sendOrSaveMessage, selectedAccount);

            if (!sendOrSaveMessage.mSave) {
                incrementRecipientsTimesContacted(
                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.TO),
                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.CC),
                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.BCC));
            }
            mSendOrSaveCallback.sendOrSaveFinished(SendOrSaveTask.this, true);
        }

        private void incrementRecipientsTimesContacted(final String toAddresses, final String ccAddresses,
                final String bccAddresses) {
            final List<String> recipients = Lists.newArrayList();
            addAddressesToRecipientList(recipients, toAddresses);
            addAddressesToRecipientList(recipients, ccAddresses);
            addAddressesToRecipientList(recipients, bccAddresses);
            mSendOrSaveCallback.incrementRecipientsTimesContacted(recipients);
        }

        private void addAddressesToRecipientList(final List<String> recipients, final String addressString) {
            if (recipients == null) {
                throw new IllegalArgumentException("recipientList cannot be null");
            }
            if (TextUtils.isEmpty(addressString)) {
                return;
            }
            final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(addressString);
            for (final Rfc822Token token : tokens) {
                recipients.add(token.getAddress());
            }
        }

        /**
         * Send or Save a message.
         */
        private void sendOrSaveMessage(final long messageIdToSave, final SendOrSaveMessage sendOrSaveMessage,
                final ReplyFromAccount selectedAccount) {
            final ContentResolver resolver = mContext.getContentResolver();
            final boolean updateExistingMessage = messageIdToSave != UIProvider.INVALID_MESSAGE_ID;

            final String accountMethod = sendOrSaveMessage.mSave ? UIProvider.AccountCallMethods.SAVE_MESSAGE
                    : UIProvider.AccountCallMethods.SEND_MESSAGE;

            try {
                if (updateExistingMessage) {
                    sendOrSaveMessage.mValues.put(BaseColumns._ID, messageIdToSave);

                    callAccountSendSaveMethod(resolver, selectedAccount.account, accountMethod, sendOrSaveMessage);
                } else {
                    Uri messageUri = null;
                    final Bundle result = callAccountSendSaveMethod(resolver, selectedAccount.account,
                            accountMethod, sendOrSaveMessage);
                    if (result != null) {
                        // If a non-null value was returned, then the provider handled the call
                        // method
                        messageUri = result.getParcelable(UIProvider.MessageColumns.URI);
                    }
                    if (sendOrSaveMessage.mSave && messageUri != null) {
                        final Cursor messageCursor = resolver.query(messageUri, UIProvider.MESSAGE_PROJECTION, null,
                                null, null);
                        if (messageCursor != null) {
                            try {
                                if (messageCursor.moveToFirst()) {
                                    // Broadcast notification that a new message has
                                    // been allocated
                                    mSendOrSaveCallback.notifyMessageIdAllocated(sendOrSaveMessage,
                                            new Message(messageCursor));
                                }
                            } finally {
                                messageCursor.close();
                            }
                        }
                    }
                }
            } finally {
                // Close any opened file descriptors
                closeOpenedAttachmentFds(sendOrSaveMessage);
            }
        }

        private static void closeOpenedAttachmentFds(final SendOrSaveMessage sendOrSaveMessage) {
            final Bundle openedFds = sendOrSaveMessage.attachmentFds();
            if (openedFds != null) {
                final Set<String> keys = openedFds.keySet();
                for (final String key : keys) {
                    final ParcelFileDescriptor fd = openedFds.getParcelable(key);
                    if (fd != null) {
                        try {
                            fd.close();
                        } catch (IOException e) {
                            // Do nothing
                        }
                    }
                }
            }
        }

        /**
         * Use the {@link ContentResolver#call} method to send or save the message.
         *
         * If this was successful, this method will return an non-null Bundle instance
         */
        private static Bundle callAccountSendSaveMethod(final ContentResolver resolver, final Account account,
                final String method, final SendOrSaveMessage sendOrSaveMessage) {
            // Copy all of the values from the content values to the bundle
            final Bundle methodExtras = new Bundle(sendOrSaveMessage.mValues.size());
            final Set<Entry<String, Object>> valueSet = sendOrSaveMessage.mValues.valueSet();

            for (Entry<String, Object> entry : valueSet) {
                final Object entryValue = entry.getValue();
                final String key = entry.getKey();
                if (entryValue instanceof String) {
                    methodExtras.putString(key, (String) entryValue);
                } else if (entryValue instanceof Boolean) {
                    methodExtras.putBoolean(key, (Boolean) entryValue);
                } else if (entryValue instanceof Integer) {
                    methodExtras.putInt(key, (Integer) entryValue);
                } else if (entryValue instanceof Long) {
                    methodExtras.putLong(key, (Long) entryValue);
                } else {
                    LogUtils.wtf(LOG_TAG, "Unexpected object type: %s", entryValue.getClass().getName());
                }
            }

            // If the SendOrSaveMessage has some opened fds, add them to the bundle
            final Bundle fdMap = sendOrSaveMessage.attachmentFds();
            if (fdMap != null) {
                methodExtras.putParcelable(UIProvider.SendOrSaveMethodParamKeys.OPENED_FD_MAP, fdMap);
            }

            return resolver.call(account.uri, method, account.uri.toString(), methodExtras);
        }
    }

    /**
     * Reports recipients that have been contacted in order to improve auto-complete
     * suggestions. Default behavior updates usage statistics in ContactsProvider.
     * @param recipients addresses
     */
    protected void incrementRecipientsTimesContacted(List<String> recipients) {
        final DataUsageStatUpdater statsUpdater = new DataUsageStatUpdater(this);
        statsUpdater.updateWithAddress(recipients);
    }

    @VisibleForTesting
    public static class SendOrSaveMessage {
        final ReplyFromAccount mAccount;
        final ContentValues mValues;
        final String mRefMessageId;
        @VisibleForTesting
        public final boolean mSave;
        final int mRequestId;
        private final Bundle mAttachmentFds;

        public SendOrSaveMessage(Context context, ReplyFromAccount account, ContentValues values,
                String refMessageId, List<Attachment> attachments, boolean save) {
            mAccount = account;
            mValues = values;
            mRefMessageId = refMessageId;
            mSave = save;
            mRequestId = mValues.hashCode() ^ hashCode();

            mAttachmentFds = initializeAttachmentFds(context, attachments);
        }

        int requestId() {
            return mRequestId;
        }

        Bundle attachmentFds() {
            return mAttachmentFds;
        }

        /**
         * Opens {@link ParcelFileDescriptor} for each of the attachments.  This method must be
         * called before the ComposeActivity finishes.
         * Note: The caller is responsible for closing these file descriptors.
         */
        private static Bundle initializeAttachmentFds(final Context context, final List<Attachment> attachments) {
            if (attachments == null || attachments.size() == 0) {
                return null;
            }

            final Bundle result = new Bundle(attachments.size());
            final ContentResolver resolver = context.getContentResolver();

            for (Attachment attachment : attachments) {
                if (attachment == null || Utils.isEmpty(attachment.contentUri)
                        || result.getParcelable(attachment.contentUri.toString()) != null) { //TS: zheng.zou 2015-7-27 EMAIL BUGFIX_1047612 MOD
                    continue;
                }

                ParcelFileDescriptor fileDescriptor;
                try {
                    fileDescriptor = resolver.openFileDescriptor(attachment.contentUri, "r");
                } catch (FileNotFoundException e) {
                    LogUtils.e(LOG_TAG, e, "Exception attempting to open attachment");
                    fileDescriptor = null;
                } catch (SecurityException e) {
                    // We have encountered a security exception when attempting to open the file
                    // specified by the content uri.  If the attachment has been cached, this
                    // isn't a problem, as even through the original permission may have been
                    // revoked, we have cached the file.  This will happen when saving/sending
                    // a previously saved draft.
                    // TODO(markwei): Expose whether the attachment has been cached through the
                    // attachment object.  This would allow us to limit when the log is made, as
                    // if the attachment has been cached, this really isn't an error
                    LogUtils.e(LOG_TAG, e, "Security Exception attempting to open attachment");
                    // Just set the file descriptor to null, as the underlying provider needs
                    // to handle the file descriptor not being set.
                    fileDescriptor = null;
                } catch (IllegalArgumentException e) {
                    LogUtils.e(LOG_TAG, e, "IllegalArgumentException Exception attempting to open attachment");
                    fileDescriptor = null;
                    //TS: rong-tang 2016-03-02 EMAIL BUGFIX-1712549 ADD_S
                } catch (UnsupportedOperationException e) {
                    LogUtils.e(LOG_TAG, e, "UnsupportedOperationException while opening file to obtain size.");
                    fileDescriptor = null;
                }
                //TS: rong-tang 2016-03-02 EMAIL BUGFIX-1712549 ADD_E

                if (fileDescriptor != null) {
                    result.putParcelable(attachment.contentUri.toString(), fileDescriptor);
                }
            }

            return result;
        }
    }

    /**
     * Get the to recipients.
     */
    public String[] getToAddresses() {
        return getAddressesFromList(mTo);
    }

    /**
     * Get the cc recipients.
     */
    public String[] getCcAddresses() {
        return getAddressesFromList(mCc);
    }

    /**
     * Get the bcc recipients.
     */
    public String[] getBccAddresses() {
        return getAddressesFromList(mBcc);
    }

    public String[] getAddressesFromList(RecipientEditTextView list) {
        if (list == null) {
            return new String[0];
        }
        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(list.getText());
        int count = tokens.length;
        String[] result = new String[count];
        for (int i = 0; i < count; i++) {
            result[i] = tokens[i].toString();
        }
        return result;
    }

    /**
     * Check for invalid email addresses.
     * @param to String array of email addresses to check.
     * @param wrongEmailsOut Emails addresses that were invalid.
     */
    public void checkInvalidEmails(final String[] to, final List<String> wrongEmailsOut) {
        if (mValidator == null) {
            return;
        }
        for (final String email : to) {
            if (!mValidator.isValid(email)) {
                wrongEmailsOut.add(email);
            }
        }
    }

    public static class RecipientErrorDialogFragment extends DialogFragment {
        // Public no-args constructor needed for fragment re-instantiation
        public RecipientErrorDialogFragment() {
        }

        public static RecipientErrorDialogFragment newInstance(final String message) {
            final RecipientErrorDialogFragment frag = new RecipientErrorDialogFragment();
            final Bundle args = new Bundle(1);
            args.putString("message", message);
            frag.setArguments(args);
            return frag;
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final String message = getArguments().getString("message");
            return new AlertDialog.Builder(getActivity()).setMessage(message)
                    .setPositiveButton(R.string.ok, new Dialog.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            ((ComposeActivity) getActivity()).finishRecipientErrorDialog();
                        }
                    }).create();
        }
    }

    private void finishRecipientErrorDialog() {
        // after the user dismisses the recipient error
        // dialog we want to make sure to refocus the
        // recipient to field so they can fix the issue
        // easily
        if (mTo != null) {
            mTo.requestFocus();
        }
    }

    /**
     * Show an error because the user has entered an invalid recipient.
     */
    private void showRecipientErrorDialog(final String message) {
        final DialogFragment frag = RecipientErrorDialogFragment.newInstance(message);
        frag.show(getFragmentManager(), "recipient error");
    }

    /**
     * Update the state of the UI based on whether or not the current draft
     * needs to be saved and the message is not empty.
     */
    public void updateSaveUi() {
        if (mSave != null) {
            mSave.setEnabled((shouldSave() && !isBlank()));
        }
    }

    /**
     * Returns true if we need to save the current draft.
     */
    private boolean shouldSave() {
        synchronized (mDraftLock) {
            // The message should only be saved if:
            // It hasn't been sent AND
            // Some text has been added to the message OR
            // an attachment has been added or removed
            // AND there is actually something in the draft to save.
            //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
            return (mTextChanged || mAttachmentsChanged || mReplyFromChanged || mProrityChanged) && !isBlank();
        }
    }

    /**
     * Returns whether the "Attach from Drive" menu item should be visible.
     */
    protected boolean shouldEnableAttachFromServiceMenu(Account mAccount) {
        return false;
    }

    /**
     * Check if all fields are blank.
     * @return boolean
     */
    public boolean isBlank() {
        // Need to check for null since isBlank() can be called from onPause()
        // before findViews() is called
        if (mSubject == null || mBodyView == null || mTo == null || mCc == null || mAttachmentsView == null) {
            LogUtils.w(LOG_TAG, "null views in isBlank check");
            return true;
        }
        return mSubject.getText().length() == 0
                && (mBodyView.getText().length() == 0
                        || getSignatureStartPosition(mSignature, mBodyView.getText().toString()) == 0)
                && mTo.length() == 0
                //[FEATURE]-Mod-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
                && mCc.length() == 0 && isBccViewEmpty()//mBcc.length() == 0
                //[FEATURE]-Mod-END by TSCD.chao zhang
                && mAttachmentsView.getAttachments().size() == 0;
    }

    //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
    private boolean isBccViewEmpty() {
        if (mBcc.length() == 0) {
            return true;
        } else {
            // TS: zhaotianyong 2014-12-02 EMAIL BUGFIX_855270 MOD_S
            //            boolean bccMySelf = Preferences.getSharedPreferences(this)
            //                    .getBoolean(Preferences.BCC_MYSELF_KEY, Preferences.BCC_MYSELF_DEFAULT);
            boolean bccMySelf = getSharedPreferences(MailPrefs.get(this).getSharedPreferencesName(),
                    Context.MODE_PRIVATE).getBoolean(Preferences.BCC_MYSELF_KEY, Preferences.BCC_MYSELF_DEFAULT);
            // TS: zhaotianyong 2014-12-02 EMAIL BUGFIX_855270 MOD_E
            String bccText = mBcc.getText().toString().trim();
            if (bccMySelf) {
                com.tct.emailcommon.mail.Address[] bcc = com.tct.emailcommon.mail.Address.parse(bccText);
                if (bcc.length == 1 && bcc[0].getAddress().equals(mAccount.getEmailAddress())) {
                    return true;
                }
            }
        }
        return false;
    }
    //[FEATURE]-Add-END by TSCD.chao zhang

    //[FEATURE]-Add-BEGIN by TTSCD.chao zhang,04/25/2014,FR 631895(porting from  FR487417)
    private void addBccMySelf(Account account) {
        // TS: zhaotianyong 2014-12-02 EMAIL BUGFIX_855270 MOD_S
        //        boolean bccMySelf = Preferences.getSharedPreferences(this).getBoolean(
        //                Preferences.BCC_MYSELF_KEY, Preferences.BCC_MYSELF_DEFAULT);
        boolean bccMySelf = getSharedPreferences(MailPrefs.get(this).getSharedPreferencesName(),
                Context.MODE_PRIVATE).getBoolean(Preferences.BCC_MYSELF_KEY, Preferences.BCC_MYSELF_DEFAULT);
        // TS: zhaotianyong 2014-12-02 EMAIL BUGFIX_855270 MOD_E
        if (account == null) {
            return;
        }
        if (bccMySelf) {
            if (!mBcc.getText().toString().contains(mAccount.getEmailAddress())) {
                com.tct.emailcommon.mail.Address a = new com.tct.emailcommon.mail.Address(
                        mAccount.getEmailAddress());
                a.setPersonal(mAccount.getSenderName());
                //AM: peng-zhang 2015-02-27 EMAIL BUGFIX_955421 MOD_S
                //addAddress(mBcc, a.toString());
                if (Changed) {
                    addAddress(mBcc, a.toString());
                } else {
                    addAddressToList(bcc_text, mBcc);
                }
                //AM: peng-zhang 2015-02-27 EMAIL BUGFIX_955421 MOD_E
                mAddBccBySetting = true;
            }
        } else {
            mAddBccBySetting = false;
        }
    }
    //[FEATURE]-Add-END by TSCD.chao zhang

    @VisibleForTesting
    protected int getSignatureStartPosition(String signature, String bodyText) {
        int startPos = -1;

        if (TextUtils.isEmpty(signature) || TextUtils.isEmpty(bodyText)) {
            return startPos;
        }

        int bodyLength = bodyText.length();
        int signatureLength = signature.length();
        String printableVersion = convertToPrintableSignature(signature);
        int printableLength = printableVersion.length();

        if (bodyLength >= printableLength
                && bodyText.substring(bodyLength - printableLength).equals(printableVersion)) {
            startPos = bodyLength - printableLength;
        } else if (bodyLength >= signatureLength
                && bodyText.substring(bodyLength - signatureLength).equals(signature)) {
            startPos = bodyLength - signatureLength;
        }
        return startPos;
    }

    /**
     * Allows any changes made by the user to be ignored. Called when the user
     * decides to discard a draft.
     */
    private void discardChanges() {
        mTextChanged = false;
        mAttachmentsChanged = false;
        mReplyFromChanged = false;
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
        mProrityChanged = false;
        //[FEATURE]-Add-END by TSCD.chao zhang
    }

    /**
     * @param save True to save, false to send
     * @param showToast True to show a toast once the message is sent/saved
     */
    protected void sendOrSaveWithSanityChecks(final boolean save, final boolean showToast,
            final boolean orientationChanged, final boolean autoSend) {
        if (mAccounts == null || mAccount == null) {
            //[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,08/05/2016,2635083
            Utility.showShortToast(this, R.string.send_failed);
            //Toast.makeText(this, R.string.send_failed, Toast.LENGTH_SHORT).show();
            if (autoSend) {
                finish();
            }
            return;
        }

        final String[] to, cc, bcc;
        if (orientationChanged) {
            to = cc = bcc = new String[0];
        } else {
            to = getToAddresses();
            cc = getCcAddresses();
            bcc = getBccAddresses();
        }
        // TS: tao.gan 2016-03-16 EMAIL BUGFIX-1761777 MOD_S
        //NOTE: NO USED,but will increase timeconsume with lots of address data.
        /*final ArrayList<String> recipients = buildEmailAddressList(to);
        recipients.addAll(buildEmailAddressList(cc));
        recipients.addAll(buildEmailAddressList(bcc));*/

        final ArrayList<String> recipients = new ArrayList<String>();
        // TS: tao.gan 2015-03-16 EMAIL BUGFIX-1761777 MOD_E
        // Don't let the user send to nobody (but it's okay to save a message
        // with no recipients)
        if (!save && (to.length == 0 && cc.length == 0 && bcc.length == 0)) {
            showRecipientErrorDialog(getString(R.string.recipient_needed));
            return;
        }

        List<String> wrongEmails = new ArrayList<String>();
        if (!save) {
            checkInvalidEmails(to, wrongEmails);
            checkInvalidEmails(cc, wrongEmails);
            checkInvalidEmails(bcc, wrongEmails);
        }

        // Don't let the user send an email with invalid recipients
        if (wrongEmails.size() > 0) {
            String errorText = String.format(getString(R.string.invalid_recipient), wrongEmails.get(0));
            showRecipientErrorDialog(errorText);
            return;
        }

        if (!save) {
            if (autoSend) {
                // Skip all further checks during autosend. This flow is used by Android Wear
                // and Google Now.
                sendOrSave(save, showToast);
                return;
            }

            //TS: lin-zhou 2015-8-28 EMAIL BUGFIX_1065369 ADD_S
            boolean warnAboutEmptySubject = isSubjectEmpty();
            // When we bring up a dialog warning the user about a send,
            // assume that they accept sending the message. If they do not,
            // the dialog listener is required to enable sending again.
            if (warnAboutEmptySubject) {
                showSendConfirmDialog(R.string.confirm_send_message_with_no_subject, showToast, recipients);
                return;
            }
            //TS: lin-zhou 2015-8-28 EMAIL BUGFIX_1065369 ADD_E
            // Show a warning before sending only if there are no attachments, body, or subject.
            if (mAttachmentsView.getAttachments().isEmpty() && showEmptyTextWarnings()) {
                //TS: lin-zhou 2015-8-28 EMAIL BUGFIX_1065369 DEL_S
                //boolean warnAboutEmptySubject = isSubjectEmpty();
                //TS: lin-zhou 2015-8-28 EMAIL BUGFIX_1065369 DEL_E
                boolean emptyBody = TextUtils.getTrimmedLength(mBodyView.getEditableText()) == 0;

                // A warning about an empty body may not be warranted when
                // forwarding mails, since a common use case is to forward
                // quoted text and not append any more text.
                boolean warnAboutEmptyBody = emptyBody && (!mForward || isBodyEmpty());

                //TS: lin-zhou 2015-8-28 EMAIL BUGFIX_1065369 DEL_S
                /*// When we bring up a dialog warning the user about a send,
                // assume that they accept sending the message. If they do not,
                // the dialog listener is required to enable sending again.
                if (warnAboutEmptySubject) {
                showSendConfirmDialog(R.string.confirm_send_message_with_no_subject,
                        showToast, recipients);
                return;
                }*/
                //TS: lin-zhou 2015-8-28 EMAIL BUGFIX_1065369 DEL_E

                if (warnAboutEmptyBody) {
                    showSendConfirmDialog(R.string.confirm_send_message_with_no_body, showToast, recipients);
                    return;
                }
            }
            // Ask for confirmation to send.
            if (showSendConfirmation()) {
                showSendConfirmDialog(R.string.confirm_send_message, showToast, recipients);
                return;
            }
        }

        performAdditionalSendOrSaveSanityChecks(save, showToast, recipients);
    }

    /**
     * Returns a boolean indicating whether warnings should be shown for empty
     * subject and body fields
     *
     * @return True if a warning should be shown for empty text fields
     */
    protected boolean showEmptyTextWarnings() {
        return mAttachmentsView.getAttachments().size() == 0;
    }

    /**
     * Returns a boolean indicating whether the user should confirm each send
     *
     * @return True if a warning should be on each send
     */
    protected boolean showSendConfirmation() {
        return mCachedSettings != null && mCachedSettings.confirmSend;
    }

    public static class SendConfirmDialogFragment extends DialogFragment
            implements DialogInterface.OnClickListener {

        private static final String MESSAGE_ID = "messageId";
        private static final String SHOW_TOAST = "showToast";
        private static final String RECIPIENTS = "recipients";

        private boolean mShowToast;

        private ArrayList<String> mRecipients;

        // Public no-args constructor needed for fragment re-instantiation
        public SendConfirmDialogFragment() {
        }

        public static SendConfirmDialogFragment newInstance(final int messageId, final boolean showToast,
                final ArrayList<String> recipients) {
            final SendConfirmDialogFragment frag = new SendConfirmDialogFragment();
            final Bundle args = new Bundle(3);
            args.putInt(MESSAGE_ID, messageId);
            args.putBoolean(SHOW_TOAST, showToast);
            args.putStringArrayList(RECIPIENTS, recipients);
            frag.setArguments(args);
            return frag;
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final int messageId = getArguments().getInt(MESSAGE_ID);
            mShowToast = getArguments().getBoolean(SHOW_TOAST);
            mRecipients = getArguments().getStringArrayList(RECIPIENTS);

            final int confirmTextId = (messageId == R.string.confirm_send_message) ? R.string.ok : R.string.send;

            return new AlertDialog.Builder(getActivity()).setMessage(messageId)
                    .setPositiveButton(confirmTextId, this).setNegativeButton(R.string.cancel, null).create();
        }

        @Override
        public void onClick(DialogInterface dialog, int which) {
            if (which == DialogInterface.BUTTON_POSITIVE) {
                ((ComposeActivity) getActivity()).finishSendConfirmDialog(mShowToast, mRecipients);
            }
        }
    }

    private void finishSendConfirmDialog(final boolean showToast, final ArrayList<String> recipients) {
        performAdditionalSendOrSaveSanityChecks(false /* save */, showToast, recipients);
    }

    // The list of recipients are used by the additional sendOrSave checks.
    // However, the send confirm dialog may be shown before performing
    // the additional checks. As a result, we need to plumb the recipient
    // list through the send confirm dialog so that
    // performAdditionalSendOrSaveChecks can be performed properly.
    private void showSendConfirmDialog(final int messageId, final boolean showToast,
            final ArrayList<String> recipients) {
        final DialogFragment frag = SendConfirmDialogFragment.newInstance(messageId, showToast, recipients);
        frag.show(getFragmentManager(), "send confirm");
    }

    /**
     * Returns whether the ComposeArea believes there is any text in the body of
     * the composition. TODO: When ComposeArea controls the Body as well, add
     * that here.
     */
    public boolean isBodyEmpty() {
        return !mQuotedTextView.isTextIncluded();
    }

    /**
     * Test to see if the subject is empty.
     *
     * @return boolean.
     */
    // TODO: this will likely go away when composeArea.focus() is implemented
    // after all the widget control is moved over.
    public boolean isSubjectEmpty() {
        return TextUtils.getTrimmedLength(mSubject.getText()) == 0;
    }

    @VisibleForTesting
    public String getSubject() {
        return mSubject.getText().toString();
    }

    private int sendOrSaveInternal(Context context, ReplyFromAccount replyFromAccount, Message message,
            final Message refMessage, final CharSequence quotedText, SendOrSaveCallback callback, Handler handler,
            boolean save, int composeMode, ReplyFromAccount draftAccount, final ContentValues extraValues) {
        final ContentValues values = new ContentValues();

        final String refMessageId = refMessage != null ? refMessage.uri.toString() : "";

        MessageModification.putToAddresses(values, message.getToAddresses());
        MessageModification.putCcAddresses(values, message.getCcAddresses());
        MessageModification.putBccAddresses(values, message.getBccAddresses());
        MessageModification.putCustomFromAddress(values, message.getFrom());
        //[FEATURE]-Add-BEGIN by TSCD.chao zhang,04/17/2014,FR 631895(porting from FR514398)
        MessageModification.putPriority(values, message.mPriority);
        //[FEATURE]-Add-END by TSCD.chao zhang
        MessageModification.putSubject(values, message.subject);
        // TS: tao.gan 2015-12-25 EMAIL FEATURE-1239148 ADD_S
        MessageModification.putRepylToAddress(values, message.getReplyTo());
        // TS: tao.gan 2015-12-25 EMAIL FEATURE-1239148 ADD_E

        // bodyHtml already have the composing spans removed.
        final String htmlBody = message.bodyHtml;
        final String textBody = message.bodyText;
        // fullbody will contain the actual body plus the quoted text.
        final String fullBody;
        final String quotedString;
        final boolean hasQuotedText = !TextUtils.isEmpty(quotedText);
        if (hasQuotedText) {
            // The quoted text is HTML at this point.
            quotedString = quotedText.toString();
            fullBody = htmlBody + quotedString;
            MessageModification.putForward(values, composeMode == ComposeActivity.FORWARD);
            MessageModification.putAppendRefMessageContent(values, true /* include quoted */);
        } else {
            fullBody = htmlBody;
            quotedString = null;
        }
        // Only take refMessage into account if either one of its html/text is not empty.
        if (refMessage != null
                && !(TextUtils.isEmpty(refMessage.bodyHtml) && TextUtils.isEmpty(refMessage.bodyText))) {
            // The code below might need to be revisited. The quoted text position is different
            // between text/html and text/plain parts and they should be stored seperately and
            // the right version should be used in the UI. text/html should have preference
            // if both exist.  Issues like this made me file b/14256940 to make sure that we
            // properly handle the existing of both text/html and text/plain parts and to verify
            // that we are not making some assumptions that break if there is no text/html part.
            int quotedTextPos = -1;
            //[FEATURE]-Add-BEGIN by TSNJ,Zhenhua.Fan,06/11/2014,FR-622609 1471
            if (EmailApplication.isOrangeImapFeatureOn() && message.serverId != null) {
                values.put(UIProvider.MessageColumns.SERVER_ID, message.serverId);
            }
            //[FEATURE]-Add-END by TSNJ,Zhenhua.Fan
            if (!TextUtils.isEmpty(refMessage.bodyHtml)) {
                MessageModification.putBodyHtml(values, fullBody.toString());
                if (hasQuotedText) {
                    quotedTextPos = htmlBody.length() + QuotedTextView.getQuotedTextOffset(quotedString);
                }
            }
            if (!TextUtils.isEmpty(refMessage.bodyText)) {
                MessageModification.putBody(values, Utils.convertHtmlToPlainText(fullBody.toString()));
                if (hasQuotedText && (quotedTextPos == -1)) {
                    quotedTextPos = textBody.length();
                }
            }
            if (quotedTextPos != -1) {
                // The quoted text pos is the text/html version first and the text/plan version
                // if there is no text/html part. The reason for this is because preference
                // is given to text/html in the compose window if it exists. In the future, we
                // should calculate the index for both since the user could choose to compose
                // explicitly in text/plain.
                MessageModification.putQuoteStartPos(values, quotedTextPos);
            }
        } else {
            MessageModification.putBodyHtml(values, fullBody.toString());
            MessageModification.putBody(values, Utils.convertHtmlToPlainText(fullBody.toString()));
        }
        int draftType = getDraftType(composeMode);
        MessageModification.putDraftType(values, draftType);
        MessageModification.putAttachments(values, message.getAttachments());
        if (!TextUtils.isEmpty(refMessageId)) {
            MessageModification.putRefMessageId(values, refMessageId);
        }
        if (extraValues != null) {
            values.putAll(extraValues);
        }
        SendOrSaveMessage sendOrSaveMessage = new SendOrSaveMessage(context, replyFromAccount, values, refMessageId,
                message.getAttachments(), save);
        SendOrSaveTask sendOrSaveTask = new SendOrSaveTask(context, sendOrSaveMessage, callback, draftAccount);

        callback.initializeSendOrSave(sendOrSaveTask);
        // Do the send/save action on the specified handler to avoid possible
        // ANRs
        handler.post(sendOrSaveTask);

        return sendOrSaveMessage.requestId();
    }

    /**
     * Removes any composing spans from the specified string.  This will create a new
     * SpannableString instance, as to not modify the behavior of the EditText view.
     */
    private static SpannableString removeComposingSpans(Spanned body) {
        // TS: tao.gan 2015-09-18 EMAIL BUGFIX_1088747 MOD_S
        //Why init the SpanableString would throw IndexOutOfBoundsException?
        //Only catch it...
        SpannableString messageBody;
        try {
            messageBody = new SpannableString(body);
        } catch (IndexOutOfBoundsException e) {
            messageBody = new SpannableString("");
            LogUtils.e(LogUtils.TAG, "IndexOutOfBoundsException while init the spannableString");
        }
        // TS: tao.gan 2015-09-18 EMAIL BUGFIX_1088747 MOD_S
        //TS: kaifeng.lu 2015-8-7 EMAIL BUGFIX_1063281 MOD_S
        try {
            BaseInputConnection.removeComposingSpans(messageBody);
        } catch (IndexOutOfBoundsException e) {
            LogUtils.e(LOG_TAG, "Occur IndexOutOfBoundsException");
        }
        //TS: kaifeng.lu 2015-8-7 EMAIL BUGFIX_1063281 MOD_E

        // Remove watcher spans while we're at it, so any off-UI thread manipulation of these
        // spans doesn't trigger unexpected side-effects. This copy is essentially 100% detached
        // from the EditText.
        //
        // (must remove SpanWatchers first to avoid triggering them as we remove other spans)
        removeSpansOfType(messageBody, SpanWatcher.class);
        removeSpansOfType(messageBody, TextWatcher.class);

        return messageBody;
    }

    private static void removeSpansOfType(SpannableString str, Class<?> cls) {
        for (Object span : str.getSpans(0, str.length(), cls)) {
            str.removeSpan(span);
        }
    }

    private static int getDraftType(int mode) {
        int draftType = -1;
        switch (mode) {
        case ComposeActivity.COMPOSE:
            draftType = DraftType.COMPOSE;
            break;
        case ComposeActivity.REPLY:
            draftType = DraftType.REPLY;
            break;
        case ComposeActivity.REPLY_ALL:
            draftType = DraftType.REPLY_ALL;
            break;
        case ComposeActivity.FORWARD:
            draftType = DraftType.FORWARD;
            break;
        }
        return draftType;
    }

    /**
     * Derived classes should override this step to perform additional checks before
     * send or save. The default implementation simply calls {@link #sendOrSave(boolean, boolean)}.
     */
    protected void performAdditionalSendOrSaveSanityChecks(final boolean save, final boolean showToast,
            ArrayList<String> recipients) {
        sendOrSave(save, showToast);
    }

    protected void sendOrSave(final boolean save, final boolean showToast) {
        // Check if user is a monkey. Monkeys can compose and hit send
        // button but are not allowed to send anything off the device.
        // TS: xiaolin.li 2015-01-08 EMAIL BUGFIX-893877 DEL_S
        /*if (ActivityManager.isUserAMonkey()) {
        return;
        }*/
        // TS: xiaolin.li 2015-01-08 EMAIL BUGFIX-893877 DEL_E
        final SendOrSaveCallback callback = new SendOrSaveCallback() {
            // FIXME: unused
            private int mRestoredRequestId;

            @Override
            public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask) {
                synchronized (mActiveTasks) {
                    int numTasks = mActiveTasks.size();
                    if (numTasks == 0) {
                        // Start service so we won't be killed if this app is
                        // put in the background.
                        startService(new Intent(ComposeActivity.this, EmptyService.class));
                    }

                    mActiveTasks.add(sendOrSaveTask);
                }
                if (sTestSendOrSaveCallback != null) {
                    sTestSendOrSaveCallback.initializeSendOrSave(sendOrSaveTask);
                }
            }

            @Override
            public void notifyMessageIdAllocated(SendOrSaveMessage sendOrSaveMessage, Message message) {
                synchronized (mDraftLock) {
                    mDraftAccount = sendOrSaveMessage.mAccount;
                    mDraftId = message.id;
                    mDraft = message;
                    if (sRequestMessageIdMap != null) {
                        sRequestMessageIdMap.put(sendOrSaveMessage.requestId(), mDraftId);
                    }
                    // Cache request message map, in case the process is killed
                    saveRequestMap();
                }
                if (sTestSendOrSaveCallback != null) {
                    sTestSendOrSaveCallback.notifyMessageIdAllocated(sendOrSaveMessage, message);
                }
            }

            @Override
            public Message getMessage() {
                synchronized (mDraftLock) {
                    return mDraft;
                }
            }

            @Override
            public void sendOrSaveFinished(SendOrSaveTask task, boolean success) {
                // Update the last sent from account.
                if (mAccount != null) {
                    MailAppProvider.getInstance().setLastSentFromAccount(mAccount.uri.toString());
                }
                // TS: zhaotianyong 2015-03-25 EMAIL BUGFIX-954496 ADD_S
                // TS: zhaotianyong 2015-03-31 EMAIL BUGFIX-963249 ADD_S
                if (doSend) {
                    ConnectivityManager mConnectivityManager = (ConnectivityManager) ComposeActivity.this
                            .getSystemService(Context.CONNECTIVITY_SERVICE);
                    NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
                    if (info == null) {
                        Utility.showToast(ComposeActivity.this, R.string.send_failed);
                    }
                }
                // TS: zhaotianyong 2015-03-31 EMAIL BUGFIX-963249 ADD_E
                // TS: zhaotianyong 2015-03-25 EMAIL BUGFIX-954496 ADD_E
                if (success) {
                    // Successfully sent or saved so reset change markers
                    discardChanges();
                    //TS: zheng.zou 2015-03-18 EMAIL FEATURE_996919 ADD_S
                    if (!doSend && showToast) {
                        Intent intent = new Intent(DRAFT_SAVED_ACTION);
                        intent.setPackage(getString(R.string.email_package_name));
                        intent.putExtra(BaseColumns._ID, mDraftId);
                        //send ordered broadcast to execute the event in sequence in different receivers,
                        //ordered by priority
                        sendOrderedBroadcast(intent, null);
                    }
                    //TS: zheng.zou 2015-03-18 EMAIL FEATURE_996919 ADD_E
                } else {
                    // A failure happened with saving/sending the draft
                    // TODO(pwestbro): add a better string that should be used
                    // when failing to send or save
                    //[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,08/05/2016,2635083
                    Utility.showShortToast(ComposeActivity.this, R.string.send_failed);
                    //Toast.makeText(ComposeActivity.this, R.string.send_failed, Toast.LENGTH_SHORT)
                    //        .show();
                }

                int numTasks;
                synchronized (mActiveTasks) {
                    // Remove the task from the list of active tasks
                    mActiveTasks.remove(task);
                    numTasks = mActiveTasks.size();
                }

                if (numTasks == 0) {
                    // Stop service so we can be killed.
                    stopService(new Intent(ComposeActivity.this, EmptyService.class));
                }
                if (sTestSendOrSaveCallback != null) {
                    sTestSendOrSaveCallback.sendOrSaveFinished(task, success);
                }
            }

            @Override
            public void incrementRecipientsTimesContacted(final List<String> recipients) {
                ComposeActivity.this.incrementRecipientsTimesContacted(recipients);
            }
        };
        //TS: zheng.zou 2015-3-16 EMAIL BUGFIX_948927 Mod_S
        if (mReplyFromAccount == null && mAccount != null) {
            mReplyFromAccount = getDefaultReplyFromAccount(mAccount);
        }
        if (mReplyFromAccount != null) {
            setAccount(mReplyFromAccount.account);
        }
        //TS: zheng.zou 2015-3-16 EMAIL BUGFIX_948927 Mod_E
        // TS: yanhua.chen 2015-9-19 EMAIL BUGFIX_569665 ADD_S
        mIsSaveDraft = save;
        // TS: yanhua.chen 2015-9-19 EMAIL BUGFIX_569665 ADD_E

        final Spanned body = removeComposingSpans(mBodyView.getText());
        SEND_SAVE_TASK_HANDLER.post(new Runnable() {
            @Override
            public void run() {
                final Message msg = createMessage(mReplyFromAccount, mRefMessage, getMode(), body);
                // TS: kaifeng.lu 2016-4-6 EMAIL BUGFIX_1909143 ADD_S
                if (/*!mIsClickIcon &&*/ !mEditDraft && (mIsSaveDraft || doSend)) {//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
                    String body1 = mBodyView.getText().toString().replace("\n", "\n\r");
                    SpannableString spannableString = new SpannableString(body1);
                    StringBuffer bodySignature = new StringBuffer(body1);
                    //[BUGFIX]-DEL begin by SCDTABLET.shujing.jin@tcl.com,05/17/2016,2013535,2148647
                    //if(mCachedSettings != null){
                    //    bodySignature.append(convertToPrintableSignature(mCachedSettings.signature));
                    //}
                    //[BUGFIX]-DEL end by SCDTABLET.shujing.jin
                    spannableString = new SpannableString(bodySignature.toString());
                    msg.bodyHtml = spannedBodyToHtml(spannableString, true);
                    msg.bodyText = bodySignature.toString();
                }
                // TS: kaifeng.lu 2016-4-6 EMAIL BUGFIX_1909143 ADD_E
                mRequestId = sendOrSaveInternal(ComposeActivity.this, mReplyFromAccount, msg, mRefMessage,
                        mQuotedTextView.getQuotedTextIfIncluded(), callback, SEND_SAVE_TASK_HANDLER, save,
                        mComposeMode, mDraftAccount, mExtraValues);
            }
        });

        // Don't display the toast if the user is just changing the orientation,
        // but we still need to save the draft to the cursor because this is how we restore
        // the attachments when the configuration change completes.
        if (showToast && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) {
            //TS: xinlei.sheng 2015-01-26 EMAIL FIXBUG_886976 MOD_S
            if (mLaunchContact) {
                mLaunchContact = false;
            } else {
                //TS: zheng.zou 2015-03-18 EMAIL FEATURE_996919 MDD_S
                if (!save) {
                    //[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,08/05/2016,2635083
                    Utility.showToast(this, R.string.sending_message);
                    //Toast.makeText(this, R.string.sending_message,
                    //        Toast.LENGTH_LONG).show();
                }
                //                Toast.makeText(this, save ? R.string.message_saved : R.string.sending_message,
                //                        Toast.LENGTH_LONG).show();
                //TS: zheng.zou 2015-03-18 EMAIL FEATURE_996919 MOD_E
            }
            //TS: xinlei.sheng 2015-01-26 EMAIL FIXBUG_886976 MOD_E
        }

        // Need to update variables here because the send or save completes
        // asynchronously even though the toast shows right away.
        discardChanges();
        updateSaveUi();

        // If we are sending, finish the activity
        if (!save) {
            finish();
            //TS: yanhua.chen 2015-6-15 EMAIL BUGFIX_1024081 ADD_S
            //TS: lin-zhou 2015-10-15 EMAIL BUGFIX_718388 MOD_S
            Uri soundUri = Uri.parse(
                    "android.resource://" + getApplicationContext().getPackageName() + "/" + R.raw.email_sent);
            MediaPlayer player = new MediaPlayer();
            try {
                if (soundUri != null) {
                    player.setDataSource(getApplicationContext(), soundUri);
                }
                player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
                player.prepare();
                player.start();
            } catch (IllegalArgumentException e) {
                LogUtils.e(LOG_TAG, "Send mail mediaPlayer get dataSource occur IllegalArgumentException");
            } catch (SecurityException e) {
                LogUtils.e(LOG_TAG, "Send mail mediaPlayer get dataSource occur SecurityException");
            } catch (IllegalStateException e) {
                LogUtils.e(LOG_TAG, "Send mail mediaPlayer get dataSource occur IllegalStateException");
            } catch (IOException e) {
                LogUtils.e(LOG_TAG, "Send mail mediaPlayer get dataSource occur IOException");
            } catch (NullPointerException e) {
                LogUtils.e(LOG_TAG, "Send mail mediaPlayer get dataSource occur NullPointerException");
            }
            //TS: lin-zhou 2015-10-15 EMAIL BUGFIX_718388 MOD_E
            //TS: yanhua.chen 2015-6-15 EMAIL BUGFIX_1024081 ADD_E
        }
    }

    /**
     * Save the state of the request messageid map. This allows for the Gmail
     * process to be killed, but and still allow for ComposeActivity instances
     * to be recreated correctly.
     */
    private void saveRequestMap() {
        // TODO: store the request map in user preferences.
    }

    @SuppressLint("NewApi")
    private void doAttach(String type) {
        mAddingAttachment = true;//TS: yang.mei 2015-1-19 EMAIL BUGFIX_1441004 MOD
        //TS: zheng.zou 2015-11-30 EMAIL TASK_869664 ADD_S
        if (PermissionUtil.checkAndRequestPermissionForResult(this, Manifest.permission.READ_EXTERNAL_STORAGE,
                PermissionUtil.REQ_CODE_PERMISSION_ADD_ATTACHMENT)) { //TS: jin.dong 2015-12-17 EMAIL BUGFIX_1170083 MOD
            Intent i = new Intent(Intent.ACTION_GET_CONTENT);
            i.addCategory(Intent.CATEGORY_OPENABLE);
            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
            i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
            i.setType(type);
            startActivityForResult(Intent.createChooser(i, getText(R.string.select_attachment_type)),
                    RESULT_PICK_ATTACHMENT);

        }
        //TS: zheng.zou 2015-11-30 EMAIL TASK_869664 ADD_E
    }

    private void showCcBccViews() {
        mCcBccView.show(true, true, true);
        if (mCcBccButton != null) {
            mCcBccButton.setVisibility(View.GONE);

            //[BUGFIX]-Add-BEGIN by SCDTABLET.weiwei.huang,05/03/2016,2013739,
            //[Email]Add mail contact icon display is not consistent
            bccButtonImg.setVisibility(View.GONE);
            ccButtonImg.setVisibility(View.GONE);
            //[BUGFIX]-Add-END by SCDTABLET.weiwei.huang
        }
    }

    private static String getActionString(int action) {
        final String msgType;
        switch (action) {
        case COMPOSE:
            msgType = "new_message";
            break;
        case REPLY:
            msgType = "reply";
            break;
        case REPLY_ALL:
            msgType = "reply_all";
            break;
        case FORWARD:
            msgType = "forward";
            break;
        default:
            msgType = "unknown";
            break;
        }
        return msgType;
    }

    private void logSendOrSave(boolean save) {
        if (!Analytics.isLoggable() || mAttachmentsView == null) {
            return;
        }

        final String category = (save) ? "message_save" : "message_send";
        final int attachmentCount = getAttachments().size();
        final String msgType = getActionString(mComposeMode);
        final String label;
        final long value;
        if (mComposeMode == COMPOSE) {
            label = Integer.toString(attachmentCount);
            value = attachmentCount;
        } else {
            label = null;
            value = 0;
        }
        Analytics.getInstance().sendEvent(category, msgType, label, value);
    }

    // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_S
    @Override
    public boolean onNavigationItemSelected(int position, long itemId) {
        int initialComposeMode = mComposeMode;
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_S
        //Note: get the truth compose mode from position
        position = getComposeModeFromListPosition(position, mSupportReplyAll);
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_E
        if (position == ComposeActivity.REPLY) {
            mComposeMode = ComposeActivity.REPLY;
            return afterNavigationItemSelected(initialComposeMode, mComposeMode);
        } else if (position == ComposeActivity.REPLY_ALL) {
            mComposeMode = ComposeActivity.REPLY_ALL;
            return afterNavigationItemSelected(initialComposeMode, mComposeMode);
        } else if (position == ComposeActivity.FORWARD) {
            // TS: zhaotianyong 2015-05-07 EMAIL BUGFIX_995343 MOD_S
            if (mRefMessage == null) {
                mComposeMode = ComposeActivity.FORWARD;
                return afterNavigationItemSelected(initialComposeMode, mComposeMode);
            } else {
                // TS: zhaotianyong 2015-05-11 EMAIL BUGFIX_998470 MOD_S
                // TS: zhaotianyong 2015-05-06 EMAIL BUGFIX_991264 MOD_S
                // TS: zhaotianyong 2015-04-16 EMAIL BUGFIX_978954 MOD_S
                EmailContent.Attachment[] atts = EmailContent.Attachment.restoreAttachmentsWithMessageId(this,
                        mRefMessage.id);
                if (initialComposeMode != FORWARD && !allAttachmentIsDownload(atts)
                        && !supportSmartForward(mAccount)) { //TS: zheng.zou 2015-11-03 EMAIL BUGFIX_858353 MOD
                    // We go here,set the bool allAttachmentsLoad false.
                    allAttachmentsLoad = false;
                    //TS: zhaotianyong 2015-05-19 EMAIL BUGFIX_988459 MOD_S
                    ChangeForwardDialogFragment f = ChangeForwardDialogFragment.newInstance(initialComposeMode);
                    //TS: zhaotianyong 2015-05-19 EMAIL BUGFIX_988459 MOD_E
                    f.displayDialog(getFragmentManager());
                } else {
                    // TS: zhaotianyong 2015-05-20 EMAIL BUGFIX-998884 ADD_S
                    allAttachmentsLoad = true;
                    // TS: zhaotianyong 2015-05-20 EMAIL BUGFIX-998884 ADD_E
                    mComposeMode = ComposeActivity.FORWARD;
                    return afterNavigationItemSelected(initialComposeMode, mComposeMode);
                }
                // TS: zhaotianyong 2015-04-16 EMAIL BUGFIX_978954 MOD_E
                // TS: zhaotianyong 2015-05-06 EMAIL BUGFIX_991264 MOD_E
            }
            // TS: zhaotianyong 2015-05-11 EMAIL BUGFIX_998470 MOD_E
            // TS: zhaotianyong 2015-05-07 EMAIL BUGFIX_995343 MOD_E
        }
        return true;
    }

    private boolean afterNavigationItemSelected(int initialComposeMode, int composeMode) {
        clearChangeListeners();
        if (initialComposeMode != composeMode) {
            //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_S
            attLargeWarning = true;
            //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_E
            resetMessageForModeChange();
            if (mRefMessage != null) {
                setFieldsFromRefMessage(composeMode);
            }
            //TS: junwei-xu 2015-09-19 EMAIL BUGFIX-622679 ADD_S
            //Note: we need to initialize recipients after change reply mode
            initRecipients();
            //TS: junwei-xu 2015-09-19 EMAIL BUGFIX-622679 ADD_E
            boolean showCc = false;
            boolean showBcc = false;
            if (mDraft != null) {
                // Following desktop behavior, if the user has added a BCC
                // field to a draft, we show it regardless of compose mode.
                showBcc = !TextUtils.isEmpty(mDraft.getBcc());
                // Use the draft to determine what to populate.
                // If the Bcc field is showing, show the Cc field whether it is populated or not.
                showCc = showBcc || (!TextUtils.isEmpty(mDraft.getCc()) && composeMode == REPLY_ALL);
            }
            if (mRefMessage != null) {
                showCc = !TextUtils.isEmpty(mCc.getText());
                showBcc = !TextUtils.isEmpty(mBcc.getText());
            }
            mCcBccView.show(false /* animate */, showCc, showBcc);
        }
        updateHideOrShowCcBcc();
        initChangeListeners();
        return true;
    }
    // TS: zhaotianyong 2015-05-08 EMAIL BUGFIX_988459 MOD_E

    @VisibleForTesting
    protected void resetMessageForModeChange() {
        // When switching between reply, reply all, forward,
        // follow the behavior of webview.
        // The contents of the following fields are cleared
        // so that they can be populated directly from the
        // ref message:
        // 1) Any recipient fields
        // 2) The subject
        // 3) The priority
        mTo.setText("");
        mCc.setText("");
        mBcc.setText("");
        // Any edits to the subject are replaced with the original subject.
        mSubject.setText("");
        //TS: junwei-xu 2015-07-02 EMAIL BUGFIX_1034971 ADD_S
        // clear the priority when switching between reply, reply all, forward
        mPriorityFlag = Message.FLAG_PRIORITY_NORMAL;
        setPriorityIcon(mPriorityFlag);
        //TS: junwei-xu 2015-07-02 EMAIL BUGFIX_1034971 ADD_E

        // Any changes to the contents of the following fields are kept:
        // 1) Body
        // 2) Attachments
        // If the user made changes to attachments, keep their changes.
        // TS: Gantao 2015-07-21 EMAIL BUGFIX_1047618 MOD_S
        // Note:cause very serious problem when switch between reply,reply all, forward,
        // we shouled delete All attachments if the mode changed.
        //        if (!mAttachmentsChanged) {
        mAttachmentsView.deleteAllAttachments();
        //        }
        // TS: Gantao 2015-07-21 EMAIL BUGFIX_1047618 MOD_E
    }

    private class ComposeModeAdapter extends ArrayAdapter<String> {

        private Context mContext;
        private LayoutInflater mInflater;

        public ComposeModeAdapter(Context context) {
            super(context, R.layout.compose_mode_item, R.id.mode,
                    getResources().getStringArray(R.array.compose_modes));
            mContext = context;
        }

        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_S
        public ComposeModeAdapter(Context context, boolean isSupportReplyAll) {
            super(context, R.layout.compose_mode_item, R.id.mode, getResources().getStringArray(
                    isSupportReplyAll ? R.array.compose_modes : R.array.compose_modes_without_reply_all));
            mContext = context;
        }
        // TS: junwei-xu 2015-09-01 EMAIL BUGFIX-526192 ADD_E

        private LayoutInflater getInflater() {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(mContext);
            }
            return mInflater;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = getInflater().inflate(R.layout.compose_mode_display_item, null);
            }
            ((TextView) convertView.findViewById(R.id.mode)).setText(getItem(position));
            return super.getView(position, convertView, parent);
        }
    }

    @Override
    public void onRespondInline(String text) {
        //TS: yanhua.chen 2015-11-11 EMAIL BUGFIX_861373 MOD_S
        mRespondedInline = true;
        appendToBody(text, false);
        //hidden "..." icon
        //mIsClickIcon = true;//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
        mBodySignature.setVisibility(View.GONE);
        //TS: yanhua.chen 2015-11-11 EMAIL BUGFIX_861373 MOD_E
        mQuotedTextView.setUpperDividerVisible(false);
        if (!mBodyView.hasFocus()) {
            mBodyView.requestFocus();
        }
    }

    /**
     * Append text to the body of the message. If there is no existing body
     * text, just sets the body to text.
     *
     * @param text Text to append
     * @param withSignature True to append a signature.
     */
    public void appendToBody(CharSequence text, boolean withSignature) {
        Editable bodyText = mBodyView.getEditableText();
        if (bodyText != null && bodyText.length() > 0) {
            //TS: yanhua.chen 2015-11-11 EMAIL BUGFIX_861373 ADD_S
            //if click respone inline,directly show signature,hidden "..." icon
            if (mRespondedInline && /*!mIsClickIcon &&*/ !TextUtils.isEmpty(mSignature)) {//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
                bodyText.append(convertToPrintableSignature(mSignature));
            }
            //TS: yanhua.chen 2015-11-11 EMAIL BUGFIX_861373 ADD_E
            bodyText.append(text);
        } else {
            setBody(text, withSignature);
        }
    }

    /**
     * Set the body of the message.
     *
     * @param text text to set
     * @param withSignature True to append a signature.
     */
    public void setBody(CharSequence text, boolean withSignature) {
        //TS: yanhua.chen 2015-11-11 EMAIL BUGFIX_861373 ADD_S
        //if click respone inline,directly show signature
        if (mRespondedInline && /*!mIsClickIcon &&*/ !TextUtils.isEmpty(mSignature)) {//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
            mBodyView.append(convertToPrintableSignature(mSignature));
        }
        //TS: yanhua.chen 2015-11-11 EMAIL BUGFIX_861373 ADD_E
        mBodyView.append(text);
        if (withSignature) {
            //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 MOD_S
            //Note:when from widget compose and mailtoUri,don't append signature
            //Note:when change the account,set mSignature the value to currentAccount's signature
            //appendSignature();
            mSignature = mCachedSettings.signature;
            //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 MOD_E
        }
    }

    private void appendSignature() {
        final String newSignature = mCachedSettings != null ? mCachedSettings.signature : null;
        final int signaturePos = getSignatureStartPosition(mSignature, mBodyView.getText().toString());
        if (!TextUtils.equals(newSignature, mSignature) || signaturePos < 0) {
            mSignature = newSignature;
            if (!TextUtils.isEmpty(mSignature)) {
                // Appending a signature does not count as changing text.
                mBodyView.removeTextChangedListener(this);
                mBodyView.append(convertToPrintableSignature(mSignature));
                mBodyView.addTextChangedListener(this);
            }
            resetBodySelection();
        }
    }

    private String convertToPrintableSignature(String signature) {
        String signatureResource = getResources().getString(R.string.signature);
        if (signature == null) {
            signature = "";
        }
        return String.format(signatureResource, signature);
    }

    @Override
    public void onAccountChanged() {
        mReplyFromAccount = mFromSpinner.getCurrentAccount();
        // TS: chenyanhua 2015-01-05 EMAIL BUGFIX-879794 ADD_S
        mValidator = null;
        // TS: chenyanhua 2015-01-05 EMAIL BUGFIX_879794 ADD_E
        if (!mAccount.equals(mReplyFromAccount.account)) {
            //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_S
            //Note:when change the account,set the mIsClickIcon flag to the initial value
            mChangeAccount = true;
            //mIsClickIcon = false;//[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,05/06/2016,2013535
            //TS: yanhua.chen 2015-9-1 EMAIL CD_551912 ADD_E
            // Clear a signature, if there was one.
            mBodyView.removeTextChangedListener(this);
            String oldSignature = mSignature;
            String bodyText = getBody().getText().toString();
            if (!TextUtils.isEmpty(oldSignature)) {
                int pos = getSignatureStartPosition(oldSignature, bodyText);
                if (pos > -1) {
                    mBodyView.setText(bodyText.substring(0, pos));
                }
            }
            setAccount(mReplyFromAccount.account);
            mBodyView.addTextChangedListener(this);
            // TODO: handle discarding attachments when switching accounts.
            // Only enable save for this draft if there is any other content
            // in the message.
            if (!isBlank()) {
                enableSave(true);
            }
            mReplyFromChanged = true;
            initRecipients();

            invalidateOptionsMenu();
        }
    }

    public void enableSave(boolean enabled) {
        if (mSave != null) {
            mSave.setEnabled(enabled);
        }
    }

    public static class DiscardConfirmDialogFragment extends DialogFragment {
        // Public no-args constructor needed for fragment re-instantiation
        public DiscardConfirmDialogFragment() {
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            return new AlertDialog.Builder(getActivity()).setMessage(R.string.confirm_discard_text)
                    .setPositiveButton(R.string.discard, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            ((ComposeActivity) getActivity()).doDiscardWithoutConfirmation();
                        }
                    }).setNegativeButton(R.string.cancel, null).create();
        }
    }

    private void doDiscard() {
        final DialogFragment frag = new DiscardConfirmDialogFragment();
        //AM: peng-zhang 2015-02-15 EMAIL BUGFIX_930453 MOD_S
        //frag.show(getFragmentManager(), "discard confirm");
        try {
            frag.show(getFragmentManager(), "discard confirm");
        } catch (IllegalStateException e) {
            LogUtils.e(LOG_TAG, "FragmentManager checkStateLoss!");
            e.printStackTrace();
        }
        //AM: peng-zhang 2015-02-15 EMAIL BUGFIX_930453 MOD_E
    }

    /**
     * Effectively discard the current message.
     *
     * This method is either invoked from the menu or from the dialog
     * once the user has confirmed that they want to discard the message.
     */
    private void doDiscardWithoutConfirmation() {
        synchronized (mDraftLock) {
            if (mDraftId != UIProvider.INVALID_MESSAGE_ID) {
                ContentValues values = new ContentValues();
                values.put(BaseColumns._ID, mDraftId);
                if (!mAccount.expungeMessageUri.equals(Uri.EMPTY)) {
                    getContentResolver().update(mAccount.expungeMessageUri, values, null, null);
                } else {
                    getContentResolver().delete(mDraft.uri, null, null);
                }
                // This is not strictly necessary (since we should not try to
                // save the draft after calling this) but it ensures that if we
                // do save again for some reason we make a new draft rather than
                // trying to resave an expunged draft.
                mDraftId = UIProvider.INVALID_MESSAGE_ID;
            }
        }

        // Display a toast to let the user know
        //[BUGFIX]-MOD by SCDTABLET.shujing.jin@tcl.com,08/05/2016,2635083
        Utility.showShortToast(this, R.string.message_discarded);
        //Toast.makeText(this, R.string.message_discarded, Toast.LENGTH_SHORT).show();

        // This prevents the draft from being saved in onPause().
        discardChanges();
        mPerformedSendOrDiscard = true;
        finish();
    }

    private void saveIfNeeded() {
        if (mAccount == null) {
            // We have not chosen an account yet so there's no way that we can save. This is ok,
            // though, since we are saving our state before AccountsActivity is activated. Thus, the
            // user has not interacted with us yet and there is no real state to save.
            return;
        }

        if (shouldSave()) {
            //TS:kaifeng.lu 2015-9-30 EMAIL BUGFIX_673904 MOD_S
            //add a judgment condition "!mLaunchContact"
            doSave(!mAddingAttachment && !mLaunchContact /* show toast */);
            //TS:kaifeng.lu 2015-9-30 EMAIL BUGFIX_673904 MOD_E
        }
    }

    @Override
    public void onAttachmentDeleted() {
        mAttachmentsChanged = true;
        // If we are showing any attachments, make sure we have an upper
        // divider.
        mQuotedTextView.setUpperDividerVisible(mAttachmentsView.getAttachments().size() > 0);
        updateSaveUi();
        //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_S
        long total = getTotalSize();
        if (total < Settings.DEFAULT_MID_ATTACHMENT_SIZE) {
            attLargeWarning = true;
        }
        //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_E

        //TS: jin.dong 2015-7-30 EMAIL BUGFIX_1053132 ADD_S
        mAttachmentsView.requestFocusFromTouch();
        //TS: jin.dong 2015-7-30 EMAIL BUGFIX_1053132 ADD_E
    }

    //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_S
    //get the attachment total size
    private long getTotalSize() {
        long totalSize = 0;
        if (mAttachmentsView.getAttachments().size() > 0) {
            for (Attachment att : mAttachmentsView.getAttachments()) {
                totalSize += att.size;
            }
        }
        return totalSize;
    }
    //TS: yanhua.chen 2015-7-29 EMAIL BUGFIX_1053132 ADD_E

    @Override
    public void onAttachmentAdded() {
        mQuotedTextView.setUpperDividerVisible(mAttachmentsView.getAttachments().size() > 0);
        mAttachmentsView.focusLastAttachment();
    }

    /**
     * This is called any time one of our text fields changes.
     */
    @Override
    public void afterTextChanged(Editable s) {
        mTextChanged = true;
        updateSaveUi();
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // Do nothing.
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // Do nothing.
    }

    // There is a big difference between the text associated with an address changing
    // to add the display name or to format properly and a recipient being added or deleted.
    // Make sure we only notify of changes when a recipient has been added or deleted.
    private class RecipientTextWatcher implements TextWatcher {
        private HashMap<String, Integer> mContent = new HashMap<String, Integer>();

        private RecipientEditTextView mView;

        private TextWatcher mListener;

        public RecipientTextWatcher(RecipientEditTextView view, TextWatcher listener) {
            mView = view;
            mListener = listener;
        }

        @Override
        public void afterTextChanged(Editable s) {
            if (hasChanged()) {
                mListener.afterTextChanged(s);
            }
            // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_S
            //TODO: this code is used to make sure that the popupwindow can also shows rightly
            //when the view's location was changed by keyboard's effect. it may be not a good way
            //to fix this issue. but now we can't found other way to fix this, so just use this code.
            //if there is better way, we will change the code.
            for (int i = 1; i < 5; i++) {
                android.os.Message msg1 = mHandler.obtainMessage(SET_LISTPOPUPWINDOW_HEIGHT);
                mHandler.sendMessageDelayed(msg1, i * 50);
            }
            // TS: jian.xu 2015-06-05 EMAIL BUGFIX-1006499 ADD_E
        }

        private boolean hasChanged() {
            final ArrayList<String> currRecips = buildEmailAddressList(getAddressesFromList(mView));
            int totalCount = currRecips.size();
            int totalPrevCount = 0;
            for (Entry<String, Integer> entry : mContent.entrySet()) {
                totalPrevCount += entry.getValue();
            }
            if (totalCount != totalPrevCount) {
                return true;
            }

            for (String recip : currRecips) {
                if (!mContent.containsKey(recip)) {
                    return true;
                } else {
                    int count = mContent.get(recip) - 1;
                    if (count < 0) {
                        return true;
                    } else {
                        mContent.put(recip, count);
                    }
                }
            }
            return false;
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            final ArrayList<String> recips = buildEmailAddressList(getAddressesFromList(mView));
            for (String recip : recips) {
                if (!mContent.containsKey(recip)) {
                    mContent.put(recip, 1);
                } else {
                    mContent.put(recip, (mContent.get(recip)) + 1);
                }
            }
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // Do nothing.
        }
    }

    /**
     * Returns a list of email addresses from the recipients. List only contains
     * email addresses strips additional info like the recipient's name.
     */
    private static ArrayList<String> buildEmailAddressList(String[] recips) {
        // Tokenize them all and put them in the list.
        final ArrayList<String> recipAddresses = Lists.newArrayListWithCapacity(recips.length);
        for (int i = 0; i < recips.length; i++) {
            recipAddresses.add(Rfc822Tokenizer.tokenize(recips[i])[0].getAddress());
        }
        return recipAddresses;
    }

    public static void registerTestSendOrSaveCallback(SendOrSaveCallback testCallback) {
        if (sTestSendOrSaveCallback != null && testCallback != null) {
            throw new IllegalStateException("Attempting to register more than one test callback");
        }
        sTestSendOrSaveCallback = testCallback;
    }

    @VisibleForTesting
    protected ArrayList<Attachment> getAttachments() {
        return mAttachmentsView.getAttachments();
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        switch (id) {
        case INIT_DRAFT_USING_REFERENCE_MESSAGE:
            return new CursorLoader(this, mRefMessageUri, UIProvider.MESSAGE_PROJECTION, null, null, null);
        case REFERENCE_MESSAGE_LOADER:
            return new CursorLoader(this, mRefMessageUri, UIProvider.MESSAGE_PROJECTION, null, null, null);
        case LOADER_ACCOUNT_CURSOR:
            return new CursorLoader(this, MailAppProvider.getAccountsUri(), UIProvider.ACCOUNTS_PROJECTION, null,
                    null, null);
        }
        return null;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        int id = loader.getId();
        switch (id) {
        case INIT_DRAFT_USING_REFERENCE_MESSAGE:
            if (data != null && data.moveToFirst()) {
                mRefMessage = new Message(data);
                Intent intent = getIntent();
                initFromRefMessage(mComposeMode);
                finishSetup(mComposeMode, intent, null);
                if (mComposeMode != FORWARD) {
                    String to = intent.getStringExtra(EXTRA_TO);
                    if (!TextUtils.isEmpty(to)) {
                        mRefMessage.setTo(null);
                        mRefMessage.setFrom(null);
                        clearChangeListeners();
                        mTo.append(to);
                        initChangeListeners();
                    }
                }
            } else {
                finish();
            }
            break;
        case REFERENCE_MESSAGE_LOADER:
            // Only populate mRefMessage and leave other fields untouched.
            if (data != null && data.moveToFirst()) {
                mRefMessage = new Message(data);
            }
            finishSetup(mComposeMode, getIntent(), mInnerSavedState);
            break;
        case LOADER_ACCOUNT_CURSOR:
            if (data != null && data.moveToFirst()) {
                // there are accounts now!
                Account account;
                final ArrayList<Account> accounts = new ArrayList<Account>();
                final ArrayList<Account> initializedAccounts = new ArrayList<Account>();
                do {
                    account = Account.builder().buildFrom(data);
                    if (account.isAccountReady()) {
                        initializedAccounts.add(account);
                    }
                    accounts.add(account);
                } while (data.moveToNext());
                if (initializedAccounts.size() > 0) {
                    findViewById(R.id.wait).setVisibility(View.GONE);
                    getLoaderManager().destroyLoader(LOADER_ACCOUNT_CURSOR);
                    findViewById(R.id.compose).setVisibility(View.VISIBLE);
                    mAccounts = initializedAccounts.toArray(new Account[initializedAccounts.size()]);

                    finishCreate();
                    invalidateOptionsMenu();
                } else {
                    // Show "waiting"
                    account = accounts.size() > 0 ? accounts.get(0) : null;
                    showWaitFragment(account);
                }
            }
            break;
        }
    }

    private void showWaitFragment(Account account) {
        WaitFragment fragment = getWaitFragment();
        if (fragment != null) {
            fragment.updateAccount(account);
        } else {
            findViewById(R.id.wait).setVisibility(View.VISIBLE);
            replaceFragment(WaitFragment.newInstance(account, false /* expectingMessages */),
                    FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_WAIT);
        }
    }

    private WaitFragment getWaitFragment() {
        return (WaitFragment) getFragmentManager().findFragmentByTag(TAG_WAIT);
    }

    private int replaceFragment(Fragment fragment, int transition, String tag) {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.setTransition(transition);
        fragmentTransaction.replace(R.id.wait, fragment, tag);
        final int transactionId = fragmentTransaction.commitAllowingStateLoss();
        return transactionId;
    }

    @Override
    public void onLoaderReset(Loader<Cursor> arg0) {
        // Do nothing.
    }

    /**
     * Background task to convert the message's html to Spanned.
     */
    private class HtmlToSpannedTask extends AsyncTask<String, Void, Spanned> {

        @Override
        protected Spanned doInBackground(String... input) {
            return HtmlUtils.htmlToSpan(input[0], mSpanConverterFactory);
        }

        @Override
        protected void onPostExecute(Spanned spanned) {
            mBodyView.removeTextChangedListener(ComposeActivity.this);
            mBodyView.setText(spanned);
            // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 DEL_S
            //mTextChanged = false;
            // TS: junwei-xu 2015-07-17 EMAIL BUGFIX-1029180 DEL_E
            mBodyView.addTextChangedListener(ComposeActivity.this);
            // TS: xujian 2015-11-16 EMAIL BUGFIX-1106881 ADD_S
            resetBodySelection();
            // TS: xujian 2015-11-16 EMAIL BUGFIX-1106881 ADD_E
        }
    }

    //TS: wenggangjin 2015-02-03 EMAIL BUGFIX_-920087 ADD_S
    @SuppressLint("NewApi")
    public String getFilePath(Uri uri) {
        Uri thisUri = null;
        String path = "";

        ContentResolver cr = this.getContentResolver();
        if (uri == null) {
            return path;
        }
        thisUri = uri;

        try {

            String scheme = thisUri.getScheme();

            if (scheme == null) {
                path = thisUri.toString();
            } else if (scheme.equals("file")) {
                path = thisUri.getPath();
                path = changeDrmFileSuffix(path);
            }

            else if (DocumentsContract.isDocumentUri(this, uri)) {
                //ExternalStorageProvider
                if (isExternalStorageDocument(uri)) {
                    final String docId = DocumentsContract.getDocumentId(uri);
                    final String[] split = docId.split(":");
                    final String type = split[0];
                    if ("primary".equalsIgnoreCase(type)) {
                        return Environment.getExternalStorageDirectory() + "/" + split[1];
                    }
                }
                // DownloadsProvider
                else if (isDownloadsDocument(uri)) {
                    final String id = DocumentsContract.getDocumentId(uri);
                    final Uri contentUri = ContentUris
                            .withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                    return getDataColumn(this, contentUri, null, null);
                }
                // MediaProvider
                else if (isMediaDocument(uri)) {
                    final String docId = DocumentsContract.getDocumentId(uri);
                    final String[] split = docId.split(":");
                    final String type = split[0];
                    Uri contentUri = null;
                    if ("image".equals(type)) {
                        contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                    } else if ("video".equals(type)) {
                        contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                    } else if ("audio".equals(type)) {
                        contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                    }
                    contentUri = contentUri.withAppendedPath(contentUri, split[1]);
                    return getDataColumn(this, contentUri, null, null);
                }
            }

            else if (scheme.equals("content")) {
                String[] projection = { "_data" };
                Cursor c = cr.query(thisUri, projection, null, null, null);
                if (c != null) {
                    try {
                        if (c.moveToFirst()) {
                            path = c.getString(0);
                        }
                    } finally {
                        c.close();
                    }
                }

                if (path.endsWith("RAW")) {
                    List<String> segments = thisUri.getPathSegments();
                    String dbName = segments.get(0);
                    String id = segments.get(1);
                    path = this.getDatabasePath(dbName + "_att") + "/" + id;
                }

            }
        } catch (Exception e) {
        }
        return path;
    }

    private String changeDrmFileSuffix(String filePath) {
        String constTmp = "/storage/sdcard0/Download/";
        String tmpEmulated = "/storage/emulated/0/Download/";
        if (null != filePath && filePath.startsWith("/storage/emulated/0/")) {
            String tmpStr = filePath.substring(tmpEmulated.length(), filePath.length());
            String tmp = constTmp + tmpStr;
            return tmp;
        }
        return filePath;
    }

    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = { column };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                String path = cursor.getString(index);
                //[BUGFIX]-Add by TCTNJ,chuang.wang, 2014-07-12,PR728605 Begin
                if (path != null && path.endsWith("RAW")) {
                    //[BUGFIX]-Add by TCTNJ,chuang.wang, 2014-07-12,PR728605 END
                    List<String> segments = uri.getPathSegments();
                    String dbName = segments.get(0);
                    String id = segments.get(1);
                    path = context.getDatabasePath(dbName + "_att") + "/" + id;
                }
                return path;
            }
            //[BUGFIX]-Mod-BEGIN by TSNJ,yong.tao,1/27/2015, PR-913357
        } catch (Exception e) {
            //[BUGFIX]-Mod-END by TSNJ,yong.tao
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }
    //TS: wenggangjin 2015-02-03 EMAIL BUGFIX_-920087 ADD_E

    //TS: yanhua.chen 2015-4-16 EMAIL BUGFIX_963186 ADD_S
    @Override
    public void onSupportActionModeStarted(android.support.v7.view.ActionMode mode) {
        changeStatusBarColor();
        super.onSupportActionModeStarted(mode);
    }

    @Override
    public void onSupportActionModeFinished(android.support.v7.view.ActionMode mode) {
        restoreStatusBarColor();
        super.onSupportActionModeFinished(mode);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void changeStatusBarColor() {
        Window window = ComposeActivity.this.getWindow();
        if (window != null) {
            window.setStatusBarColor(getResources().getColor(R.color.change_status_bar));
        }
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void restoreStatusBarColor() {
        Window window = ComposeActivity.this.getWindow();
        if (window != null) {
            window.setStatusBarColor(getResources().getColor(R.color.restore_status_bar));
        }
    }
    //TS: yanhua.chen 2015-4-16 EMAIL BUGFIX_963186 ADD_E

    //TS: zhaotianyong 2015-05-06 EMAIL BUGFIX_991264 MOD_S
    //TS: zhaotianyong 2015-04-16 EMAIL BUGFIX_978954 ADD_S
    /**
     * determine whether the attachments is all downloaded.
     */
    public static boolean allAttachmentIsDownload(EmailContent.Attachment[] atts) {
        for (EmailContent.Attachment attachment : atts) {
            if (attachment == null || TextUtils.isEmpty(attachment.getContentUri())
                    || !(attachment.isSaved() && attachment.mUiState == UIProvider.AttachmentState.SAVED)) {
                return false;
            }
        }
        return true;
    }
    //TS: zhaotianyong 2015-04-16 EMAIL BUGFIX_978954 ADD_E
    //TS: zhaotianyong 2015-05-06 EMAIL BUGFIX_991264 MOD_E

    //TS: zheng.zou 2015-11-03 EMAIL BUGFIX_858353 ADD_S
    public static boolean supportSmartForward(Account account) {
        return account != null && account.supportsCapability(AccountCapabilities.SMART_FORWARD);
    }
    //TS: zheng.zou 2015-11-03 EMAIL BUGFIX_858353 ADD_E

    //TS: zhaotianyong 2015-05-19 EMAIL BUGFIX_988459 MOD_S
    @Override
    public void setComposeMode(int composeMode) {
        mComposeMode = composeMode;
        afterNavigationItemSelected(0, mComposeMode);
    }

    @Override
    public void setNavigationItem(int selectedItem) {
        getSupportActionBar().setSelectedNavigationItem(selectedItem);
    }
    //TS: zhaotianyong 2015-05-19 EMAIL BUGFIX_988459 MOD_E

    // TS: Gantao 2015-10-30 EMAIL FEATURE_1104470 ADD_S
    /*
     * Show save group dialogFragment
     */
    private void showSaveGroupDialog() {
        //Get string array from to/cc/bcc field
        String[] toMails = Address.getMailsArray(formatSenders(mTo.getText().toString()));
        String[] ccMails = Address.getMailsArray(formatSenders(mCc.getText().toString()));
        String[] BccMails = Address.getMailsArray(formatSenders(mBcc.getText().toString()));
        SaveGroupDialog dialog = SaveGroupDialog.newInstance(toMails, ccMails, BccMails);
        dialog.displayDialog(getFragmentManager());
    }
    // TS: Gantao 2015-10-30 EMAIL FEATURE_1104470 ADD_E

    // TS: Gantao 2015-12-17 EMAIL BUGFIX_1176396 ADD_S
    /**
     * Set image view according to the priority level
     * @param imageLevel  the priority level
     */
    private void setPriorityIcon(int imageLevel) {
        switch (imageLevel) {
        case Message.FLAG_PRIORITY_HIGH:
            mPriorityIcon.setImageResource(R.drawable.ic_high_priority);
            break;
        case Message.FLAG_PRIORITY_NORMAL:
            mPriorityIcon.setImageResource(R.drawable.ic_normal_priority);
            break;
        case Message.FLAG_PRIORITY_LOW:
            mPriorityIcon.setImageResource(R.drawable.ic_low_priority);
            break;
        default:
            mPriorityIcon.setImageResource(R.drawable.ic_normal_priority);
            break;
        }
    }

    // TS: Gantao 2015-12-17 EMAIL BUGFIX_1176396 ADD_E
    //TS: jin.dong 2015-12-17 EMAIL BUGFIX_1170083 ADD_S
    private void showNeedPermissionToast(int descId) {
        final ActionableToastBar.ActionClickedListener listener = new ActionableToastBar.ActionClickedListener() {
            @Override
            public void onActionClicked(Context context, int undoNum) {
                PermissionUtil.gotoSettings(context);
            }
        };
        mToastBar.show(listener, getString(descId), R.string.permission_grant_go_setting, true,
                new ToastBarOperation(1, 0, ToastBarOperation.INFO, false, null));

    }
    //TS: jin.dong 2015-12-17 EMAIL BUGFIX_1170083 ADD_E

    // TS: tao.gan 2015-12-25 EMAIL FEATURE-1239148 ADD_S

    /**
     * Get reply to address from account ,which is set in account settings
     * @return reply to address
     */
    private String getReplyToAddress() {
        //TS: zheng.zou 2016-03-21 EMAIL BUGFIX_1801683 MOD_S
        if (mAccount == null || mAccount.settings == null) {
            LogUtils.e(LOG_TAG, "Null account while get reply to address");
            return "";
        }
        return mAccount.settings.replyTo;
        //TS: zheng.zou 2016-03-21 EMAIL BUGFIX_1801683 MOD_E
    }
    // TS: tao.gan 2015-12-25 EMAIL FEATURE-1239148 ADD_E

}