ComposeActivity.java revision d90f7ac9b1a62e8beefdac3ceda9e4cb75ef000c
18e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira/**
28e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira * Copyright (c) 2011, Google Inc.
38e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira *
48e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
58e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira * you may not use this file except in compliance with the License.
68e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira * You may obtain a copy of the License at
78e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira *
88e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira *     http://www.apache.org/licenses/LICENSE-2.0
98e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira *
108e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira * Unless required by applicable law or agreed to in writing, software
118e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
128e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira * See the License for the specific language governing permissions and
148e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira * limitations under the License.
158e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira */
168e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
1730e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangpackage com.android.mail.compose;
188e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
19326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.app.ActionBar;
205c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huangimport android.app.ActionBar.OnNavigationListener;
215c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huangimport android.app.Activity;
2282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.app.ActivityManager;
2382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.app.AlertDialog;
2482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.app.Dialog;
256349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.content.ContentResolver;
2682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.content.ContentValues;
276349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.content.Context;
2882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.content.DialogInterface;
296349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.content.Intent;
3082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.content.pm.ActivityInfo;
317ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereiraimport android.database.Cursor;
326349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.net.Uri;
338e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.os.Bundle;
3482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.os.Handler;
3582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.os.HandlerThread;
36f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrookimport android.os.Parcelable;
3782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.provider.BaseColumns;
3846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport android.text.Editable;
3982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.Html;
4082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.Spanned;
41c182762df95fd382faf8e4a25e5cf7783ea10176Paul Westbrookimport android.text.TextUtils;
4282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.TextWatcher;
4346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport android.text.util.Rfc822Token;
44c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport android.text.util.Rfc822Tokenizer;
45326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.LayoutInflater;
46b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.Menu;
47b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.MenuInflater;
48b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.MenuItem;
498e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.view.View;
508e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.view.View.OnClickListener;
512ea06183aa826b28fe6ebdb454da49ba3878bc4fAndrew Sappersteinimport android.view.View.OnFocusChangeListener;
525c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huangimport android.view.ViewGroup;
53eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereiraimport android.view.inputmethod.BaseInputConnection;
54326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.widget.ArrayAdapter;
558e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.widget.Button;
56433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereiraimport android.widget.EditText;
571f93668e1186d48b507207841c1ca0529c3de292Mindy Pereiraimport android.widget.ImageView;
586349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.widget.TextView;
59013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereiraimport android.widget.Toast;
607b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira
61c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport com.android.common.Rfc822Validator;
625c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huangimport com.android.ex.chips.RecipientEditTextView;
635c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huangimport com.android.mail.R;
64eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereiraimport com.android.mail.compose.AttachmentsView.AttachmentDeletedListener;
659932dee248718d2cec993d93b5115973d1389d0aMindy Pereiraimport com.android.mail.compose.AttachmentsView.AttachmentFailureException;
665a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereiraimport com.android.mail.compose.FromAddressSpinner.OnAccountChangedListener;
6730e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.compose.QuotedTextView.RespondInlineListener;
6833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereiraimport com.android.mail.providers.Account;
6930e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.Address;
7030e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.Attachment;
713ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereiraimport com.android.mail.providers.Message;
7282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.android.mail.providers.MessageModification;
7392551d057965689f1952faeb06763f0762bc717fMindy Pereiraimport com.android.mail.providers.ReplyFromAccount;
74181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereiraimport com.android.mail.providers.Settings;
7530e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.UIProvider;
763ca5bad62c075184920c5e331870280fc6acca16Mindy Pereiraimport com.android.mail.providers.UIProvider.AccountCapabilities;
7712575866e5526e45eaf773c6c98c93c3a06fe156Mindy Pereiraimport com.android.mail.providers.UIProvider.DraftType;
7892227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrookimport com.android.mail.utils.AccountUtils;
79b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
8030e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.utils.LogUtils;
8130e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.utils.Utils;
8246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport com.google.common.annotations.VisibleForTesting;
8382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.google.common.collect.Lists;
844a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereiraimport com.google.common.collect.Sets;
858e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
8662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereiraimport org.json.JSONException;
8762de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira
888eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereiraimport java.io.UnsupportedEncodingException;
898eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereiraimport java.net.URLDecoder;
9046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.ArrayList;
91bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrookimport java.util.Arrays;
9246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.Collection;
9375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereiraimport java.util.HashMap;
9446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.HashSet;
9546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.List;
961c078cff1958b29e280100bfa6221103ec46b555Paul Westbrookimport java.util.Map.Entry;
97bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrookimport java.util.Set;
9882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport java.util.concurrent.ConcurrentHashMap;
9946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
10046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereirapublic class ComposeActivity extends Activity implements OnClickListener, OnNavigationListener,
1015a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        RespondInlineListener, DialogInterface.OnClickListener, TextWatcher,
102226e4fd316954167654b207aba998b7a7e43ade9Andrew Sapperstein        AttachmentDeletedListener, OnAccountChangedListener {
1036349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    // Identifiers for which type of composition this is
10436bbcaeff2a6e57189e4f4818e0ccdf6317ce836Mindy Pereira    static final int COMPOSE = -1;
10536bbcaeff2a6e57189e4f4818e0ccdf6317ce836Mindy Pereira    static final int REPLY = 0;
10636bbcaeff2a6e57189e4f4818e0ccdf6317ce836Mindy Pereira    static final int REPLY_ALL = 1;
10736bbcaeff2a6e57189e4f4818e0ccdf6317ce836Mindy Pereira    static final int FORWARD = 2;
10836bbcaeff2a6e57189e4f4818e0ccdf6317ce836Mindy Pereira    static final int EDIT_DRAFT = 3;
1096349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1106349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    // Integer extra holding one of the above compose action
11136bbcaeff2a6e57189e4f4818e0ccdf6317ce836Mindy Pereira    private static final String EXTRA_ACTION = "action";
1126349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
113326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira    private static final String EXTRA_SHOW_CC = "showCc";
114326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira    private static final String EXTRA_SHOW_BCC = "showBcc";
115a34c9a054c9aef6f83abe56515c132a31d71b3c8Mindy Pereira
1168eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    private static final String UTF8_ENCODING_NAME = "UTF-8";
1178eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
1188eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    private static final String MAIL_TO = "mailto";
1198eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
1208eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    private static final String EXTRA_SUBJECT = "subject";
1218eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
1228eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    private static final String EXTRA_BODY = "body";
1238eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
1249a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira    private static final String EXTRA_FROM_ACCOUNT_STRING = "fromAccountString";
1259a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira
1268eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    // Extra that we can get passed from other activities
1278eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    private static final String EXTRA_TO = "to";
1288eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    private static final String EXTRA_CC = "cc";
1298eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    private static final String EXTRA_BCC = "bcc";
1308eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
1318eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    // List of all the fields
1328eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    static final String[] ALL_EXTRAS = { EXTRA_SUBJECT, EXTRA_BODY, EXTRA_TO, EXTRA_CC, EXTRA_BCC };
1338eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
13482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static SendOrSaveCallback sTestSendOrSaveCallback = null;
13582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Map containing information about requests to create new messages, and the id of the
13682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // messages that were the result of those requests.
13782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    //
13882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // This map is used when the activity that initiated the save a of a new message, is killed
13982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // before the save has completed (and when we know the id of the newly created message).  When
14082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // a save is completed, the service that is running in the background, will update the map
14182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    //
14282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // When a new ComposeActivity instance is created, it will attempt to use the information in
14382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // the previously instantiated map.  If ComposeActivity.onCreate() is called, with a bundle
14482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // (restoring data from a previous instance), and the map hasn't been created, we will attempt
14582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // to populate the map with data stored in shared preferences.
14682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static ConcurrentHashMap<Integer, Long> sRequestMessageIdMap = null;
14782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Key used to store the above map
14882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static final String CACHED_MESSAGE_REQUEST_IDS_KEY = "cache-message-request-ids";
1496349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1506349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Notifies the {@code Activity} that the caller is an Email
1516349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * {@code Activity}, so that the back behavior may be modified accordingly.
1526349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     *
1536349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * @see #onAppUpPressed
1546349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
155daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook    public static final String EXTRA_FROM_EMAIL_TASK = "fromemail";
1566349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
157e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    public static final String EXTRA_ATTACHMENTS = "attachments";
158f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook
1593ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    //  If this is a reply/forward then this extra will hold the original message
16036bbcaeff2a6e57189e4f4818e0ccdf6317ce836Mindy Pereira    private static final String EXTRA_IN_REFERENCE_TO_MESSAGE = "in-reference-to-message";
161bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook    // If this is an action to edit an existing draft messagge, this extra will hold the
162bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook    // draft message
163bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook    private static final String ORIGINAL_DRAFT_MESSAGE = "original-draft-message";
1644a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private static final String END_TOKEN = ", ";
165b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
166013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    // Request numbers for activities we start
167013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final int RESULT_PICK_ATTACHMENT = 1;
168013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final int RESULT_CREATE_ACCOUNT = 2;
1698eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    // TODO(mindyp) set mime-type for auto send?
170e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    public static final String AUTO_SEND_ACTION = "com.android.mail.action.AUTO_SEND";
1718eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
1728eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    // Max size for attachments (5 megs). Will be overridden by account settings if found.
1738eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    // TODO(mindyp): read this from account settings?
1748eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    private static final int DEFAULT_MAX_ATTACHMENT_SIZE = 25 * 1024 * 1024;
175e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private static final String EXTRA_SELECTED_REPLY_FROM_ACCOUNT = "replyFromAccount";
176e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private static final String EXTRA_REQUEST_ID = "requestId";
177e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private static final String EXTRA_FOCUS_SELECTION_START = "focusSelectionStart";
178e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private static final String EXTRA_FOCUS_SELECTION_END = null;
179e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private static final String EXTRA_MESSAGE = "extraMessage";
1808e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
18182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
18282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * A single thread for running tasks in the background.
18382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
18482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Handler mSendSaveTaskHandler = null;
185c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mTo;
186c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mCc;
187c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mBcc;
1888e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    private Button mCcBccButton;
1898e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    private CcBccView mCcBccView;
1907b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira    private AttachmentsView mAttachmentsView;
19133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private Account mAccount;
19292551d057965689f1952faeb06763f0762bc717fMindy Pereira    private ReplyFromAccount mReplyFromAccount;
193181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private Settings mCachedSettings;
19482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Rfc822Validator mValidator;
1956349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private TextView mSubject;
1966349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
197326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private ComposeModeAdapter mComposeModeAdapter;
198326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private int mComposeMode = -1;
19946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private boolean mForward;
20046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private String mRecipient;
20146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private QuotedTextView mQuotedTextView;
202433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira    private EditText mBodyView;
2031a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    private View mFromStatic;
2042eb173211741adf7454bbf1990ba1b9d1e1f6a64Mindy Pereira    private TextView mFromStaticText;
205eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private View mFromSpinnerWrapper;
2061883b346928322a9f045bc5b7426ddcd6af9beb3Mindy Pereira    @VisibleForTesting
2071883b346928322a9f045bc5b7426ddcd6af9beb3Mindy Pereira    protected FromAddressSpinner mFromSpinner;
208013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private boolean mAddingAttachment;
209eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private boolean mAttachmentsChanged;
21082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean mTextChanged;
21182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean mReplyFromChanged;
21282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private MenuItem mSave;
21382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private MenuItem mSend;
21482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private AlertDialog mRecipientErrorDialog;
215eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private AlertDialog mSendConfirmDialog;
216b3112a2bf7b805695259e412b0e2bc0e0e73e9d6Mindy Pereira    @VisibleForTesting
217b3112a2bf7b805695259e412b0e2bc0e0e73e9d6Mindy Pereira    protected Message mRefMessage;
2187ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira    private long mDraftId = UIProvider.INVALID_MESSAGE_ID;
2197ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira    private Message mDraft;
2207ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira    private Object mDraftLock = new Object();
2211f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    private ImageView mAttachmentsButton;
2223ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira
223326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    /**
224daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     * Boolean indicating whether ComposeActivity was launched from a Gmail controlled view.
225daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook     */
226daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook    private boolean mLaunchedFromEmail = false;
227cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira    private RecipientTextWatcher mToListener;
228cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira    private RecipientTextWatcher mCcListener;
229cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira    private RecipientTextWatcher mBccListener;
230daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook
231daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook
232daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook    /**
233326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira     * Can be called from a non-UI thread.
234326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira     */
2353ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void editDraft(Context launcher, Account account, Message message) {
236bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        launch(launcher, account, message, EDIT_DRAFT);
237326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
238326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
2396349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
2406349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
2416349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
24233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void compose(Context launcher, Account account) {
2436349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, null, COMPOSE);
2446349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2456349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
2466349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
2476349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
2486349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
2493ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void reply(Context launcher, Account account, Message message) {
2503ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        launch(launcher, account, message, REPLY);
2516349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2526349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
2536349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
2546349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
2556349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
2563ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void replyAll(Context launcher, Account account, Message message) {
2573ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        launch(launcher, account, message, REPLY_ALL);
2586349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2596349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
2606349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
2616349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
2626349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
2633ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void forward(Context launcher, Account account, Message message) {
2643ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        launch(launcher, account, message, FORWARD);
2656349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2666349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
2673ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private static void launch(Context launcher, Account account, Message message, int action) {
2686349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        Intent intent = new Intent(launcher, ComposeActivity.class);
2696349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
2706349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_ACTION, action);
2716349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account);
272bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        if (action == EDIT_DRAFT) {
273bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook            intent.putExtra(ORIGINAL_DRAFT_MESSAGE, message);
274bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        } else {
275bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook            intent.putExtra(EXTRA_IN_REFERENCE_TO_MESSAGE, message);
276bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        }
2776349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launcher.startActivity(intent);
2786349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2798e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
2808e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    @Override
2818e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    public void onCreate(Bundle savedInstanceState) {
2828e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        super.onCreate(savedInstanceState);
2838e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        setContentView(R.layout.compose);
28446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        findViews();
285818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        Intent intent = getIntent();
286e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        Account account = null;
287e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        Message message;
28871c9e569c06ee362840c14093cf1973ecf297059Mindy Pereira        boolean showQuotedText = false;
289e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        int action;
290e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        Object accountExtra = intent != null && intent.getExtras() != null ? intent.getExtras()
291e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                .get(Utils.EXTRA_ACCOUNT) : null;
292e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        final Account[] syncingAccounts = AccountUtils.getSyncingAccounts(this);
293f7fc6c339da7f73755041cceb9b8f8447680728aMindy Pereira        if (hadSavedInstanceStateMessage(savedInstanceState)) {
294e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            action = savedInstanceState.getInt(EXTRA_ACTION, COMPOSE);
295e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            account = savedInstanceState.getParcelable(Utils.EXTRA_ACCOUNT);
296e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            message = (Message) savedInstanceState.getParcelable(EXTRA_MESSAGE);
297e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            mRefMessage = (Message) savedInstanceState.getParcelable(EXTRA_IN_REFERENCE_TO_MESSAGE);
298e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        } else {
299e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira            if (accountExtra instanceof Account) {
300e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                account = (Account) intent.getExtras().get(Utils.EXTRA_ACCOUNT);
301e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira            } else if (accountExtra instanceof String) {
302e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                // For backwards compatibility
303e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                String extraAccount = intent.getStringExtra(Utils.EXTRA_ACCOUNT);
304e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                if (syncingAccounts.length > 0) {
305e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                    if (!TextUtils.isEmpty(extraAccount)) {
306e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                        for (Account a : syncingAccounts) {
307e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                            if (a.name.equals(extraAccount)) {
308e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                                account = a;
309e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                            }
310e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                        }
311e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                    }
312e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                }
313e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira            }
314e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            action = intent.getIntExtra(EXTRA_ACTION, COMPOSE);
315e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            // Initialize the message from the message in the intent
316e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            message = (Message) intent.getParcelableExtra(ORIGINAL_DRAFT_MESSAGE);
317e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            mRefMessage = (Message) intent.getParcelableExtra(EXTRA_IN_REFERENCE_TO_MESSAGE);
318e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        }
31992227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook        if (account == null) {
320e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira            if (syncingAccounts != null && syncingAccounts.length > 0) {
321e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                    account = syncingAccounts[0];
32292227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook            }
32392227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook        }
32492227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook
32592227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook        setAccount(account);
326818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        if (mAccount == null) {
327818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira            return;
328818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        }
329bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook
330daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        if (intent.getBooleanExtra(EXTRA_FROM_EMAIL_TASK, false)) {
331daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook            mLaunchedFromEmail = true;
332daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        } else if (Intent.ACTION_SEND.equals(intent.getAction())) {
333daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook            final Uri dataUri = intent.getData();
334daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook            if (dataUri != null) {
335daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook                final String dataScheme = intent.getData().getScheme();
336daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook                final String accountScheme = mAccount.composeIntentUri.getScheme();
337daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook                mLaunchedFromEmail = TextUtils.equals(dataScheme, accountScheme);
338daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook            }
339daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        }
340daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook
341e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        if (message != null && action != EDIT_DRAFT) {
342e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            initFromDraftMessage(message);
343e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            initQuotedTextFromRefMessage(mRefMessage, action);
344a34c9a054c9aef6f83abe56515c132a31d71b3c8Mindy Pereira            showCcBcc(savedInstanceState);
34571c9e569c06ee362840c14093cf1973ecf297059Mindy Pereira            showQuotedText = message.appendRefMessageContent;
346e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        } else if (action == EDIT_DRAFT) {
347e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            initFromDraftMessage(message);
348ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira            boolean showBcc = !TextUtils.isEmpty(message.bcc);
349ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira            boolean showCc = showBcc || !TextUtils.isEmpty(message.cc);
350ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira            mCcBccView.show(false, showCc, showBcc);
351bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook            // Update the action to the draft type of the previous draft
352bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook            switch (message.draftType) {
353bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                case UIProvider.DraftType.REPLY:
354bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                    action = REPLY;
355bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                    break;
356bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                case UIProvider.DraftType.REPLY_ALL:
357bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                    action = REPLY_ALL;
358bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                    break;
359bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                case UIProvider.DraftType.FORWARD:
360bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                    action = FORWARD;
361bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                    break;
362bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                case UIProvider.DraftType.COMPOSE:
363bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                default:
364bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                    action = COMPOSE;
365bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook                    break;
366bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook            }
367e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            initQuotedTextFromRefMessage(mRefMessage, action);
36871c9e569c06ee362840c14093cf1973ecf297059Mindy Pereira            showQuotedText = message.appendRefMessageContent;
369e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        } else if ((action == REPLY || action == REPLY_ALL || action == FORWARD)) {
370e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            if (mRefMessage != null) {
371e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                initFromRefMessage(action, mAccount.name);
372ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                if (mRefMessage != null) {
373ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                    // CC field only gets populated when doing REPLY_ALL.
374ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                    // BCC never gets auto-populated, unless the user is editing
375ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                    // a draft with one.
376ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                    if (!TextUtils.isEmpty(mRefMessage.cc) && action == REPLY_ALL) {
377ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                        mCcBccView.show(false, true, false);
378ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                    }
379ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                }
380ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                updateHideOrShowCcBcc();
38171c9e569c06ee362840c14093cf1973ecf297059Mindy Pereira                showQuotedText = true;
382e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            }
3838eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        } else {
3848eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            initFromExtras(intent);
385bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        }
386bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook
387bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        if (action == COMPOSE) {
388eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mQuotedTextView.setVisibility(View.GONE);
38946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
390818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        initRecipients();
391f7fc6c339da7f73755041cceb9b8f8447680728aMindy Pereira        // Don't bother with the intent if we have procured a message from the
392f7fc6c339da7f73755041cceb9b8f8447680728aMindy Pereira        // intent already.
393f7fc6c339da7f73755041cceb9b8f8447680728aMindy Pereira        if (!hadSavedInstanceStateMessage(savedInstanceState)) {
394f7fc6c339da7f73755041cceb9b8f8447680728aMindy Pereira            initAttachmentsFromIntent(intent);
395f7fc6c339da7f73755041cceb9b8f8447680728aMindy Pereira        }
3961a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        initActionBar(action);
397e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        initFromSpinner(savedInstanceState != null ? savedInstanceState : intent.getExtras(),
398e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                action);
39975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        initChangeListeners();
400433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        setFocus(action);
401326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira        updateHideOrShowCcBcc();
40271c9e569c06ee362840c14093cf1973ecf297059Mindy Pereira        updateHideOrShowQuotedText(showQuotedText);
40371c9e569c06ee362840c14093cf1973ecf297059Mindy Pereira    }
40471c9e569c06ee362840c14093cf1973ecf297059Mindy Pereira
405f7fc6c339da7f73755041cceb9b8f8447680728aMindy Pereira    private boolean hadSavedInstanceStateMessage(Bundle savedInstanceState) {
406f7fc6c339da7f73755041cceb9b8f8447680728aMindy Pereira        return savedInstanceState != null && savedInstanceState.containsKey(EXTRA_MESSAGE);
407f7fc6c339da7f73755041cceb9b8f8447680728aMindy Pereira    }
408f7fc6c339da7f73755041cceb9b8f8447680728aMindy Pereira
40971c9e569c06ee362840c14093cf1973ecf297059Mindy Pereira    private void updateHideOrShowQuotedText(boolean showQuotedText) {
41071c9e569c06ee362840c14093cf1973ecf297059Mindy Pereira        mQuotedTextView.updateCheckedState(showQuotedText);
411433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira    }
412433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira
413433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira    private void setFocus(int action) {
414433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        if (action == EDIT_DRAFT) {
415433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            int type = mDraft.draftType;
416433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            switch (type) {
417433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                case UIProvider.DraftType.COMPOSE:
418433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                case UIProvider.DraftType.FORWARD:
419433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                    action = COMPOSE;
420433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                    break;
421433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                case UIProvider.DraftType.REPLY:
422433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                case UIProvider.DraftType.REPLY_ALL:
423433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                default:
424433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                    action = REPLY;
425433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                    break;
426433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            }
427433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        }
428433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        switch (action) {
429433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            case FORWARD:
430433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            case COMPOSE:
431433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                mTo.requestFocus();
432433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                break;
433433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            case REPLY:
434433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            case REPLY_ALL:
435433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            default:
436433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                focusBody();
437433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                break;
438433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        }
439433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira    }
440433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira
441433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira    /**
442433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira     * Focus the body of the message.
443433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira     */
444433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira    public void focusBody() {
445433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        mBodyView.requestFocus();
446433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        int length = mBodyView.getText().length();
447433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira
448433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        int signatureStartPos = getSignatureStartPosition(
449433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                mSignature, mBodyView.getText().toString());
450433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        if (signatureStartPos > -1) {
451433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            // In case the user deleted the newlines...
452433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            mBodyView.setSelection(signatureStartPos);
453433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        } else if (length > 0) {
454433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            // Move cursor to the end.
455433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            mBodyView.setSelection(length);
456433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        }
4571a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
4581a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
4591a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    @Override
4601a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    protected void onResume() {
4611a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        super.onResume();
4621a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        // Update the from spinner as other accounts
4631a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        // may now be available.
464818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        if (mFromSpinner != null && mAccount != null) {
46562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            mFromSpinner.asyncInitFromSpinner(mComposeMode, mAccount);
466818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        }
4671a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
4681a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
469eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
470eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected void onPause() {
471eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        super.onPause();
472eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
473eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mSendConfirmDialog != null) {
474eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mSendConfirmDialog.dismiss();
475eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
476eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mRecipientErrorDialog != null) {
477eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mRecipientErrorDialog.dismiss();
478eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
479eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveIfNeeded();
480eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
481eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
482eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
483eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected final void onActivityResult(int request, int result, Intent data) {
484eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAddingAttachment = false;
485eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
486eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (result == RESULT_OK && request == RESULT_PICK_ATTACHMENT) {
487eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            addAttachmentAndUpdateView(data);
488eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
489eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
490eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
491eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
492e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    public final void onRestoreInstanceState(Bundle savedInstanceState) {
493e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        super.onRestoreInstanceState(savedInstanceState);
494e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        if (savedInstanceState != null) {
495e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            if (savedInstanceState.containsKey(EXTRA_FOCUS_SELECTION_START)) {
496e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                int selectionStart = savedInstanceState.getInt(EXTRA_FOCUS_SELECTION_START);
497e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                int selectionEnd = savedInstanceState.getInt(EXTRA_FOCUS_SELECTION_END);
498e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                // There should be a focus and it should be an EditText since we
499e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                // only save these extras if these conditions are true.
500e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                EditText focusEditText = (EditText) getCurrentFocus();
501e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                final int length = focusEditText.getText().length();
502e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                if (selectionStart < length && selectionEnd < length) {
503e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                    focusEditText.setSelection(selectionStart, selectionEnd);
504e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                }
505e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            }
506e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        }
507e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    }
508e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira
509e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    @Override
510eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public final void onSaveInstanceState(Bundle state) {
511eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        super.onSaveInstanceState(state);
512e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        // The framework is happy to save and restore the selection but only if it also saves and
513e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        // restores the contents of the edit text. That's a lot of text to put in a bundle so we do
514e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        // this manually.
515e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        View focus = getCurrentFocus();
516e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        if (focus != null && focus instanceof EditText) {
517e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            EditText focusEditText = (EditText) focus;
518e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            state.putInt(EXTRA_FOCUS_SELECTION_START, focusEditText.getSelectionStart());
519e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            state.putInt(EXTRA_FOCUS_SELECTION_END, focusEditText.getSelectionEnd());
520e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        }
5216273e96b7204a33ea44823fb36fd7fc8baf5ba20Paul Westbrook
5226273e96b7204a33ea44823fb36fd7fc8baf5ba20Paul Westbrook        final List<ReplyFromAccount> replyFromAccounts = mFromSpinner.getReplyFromAccounts();
523151f1ad1613ecac0207144472872440517172b5aPaul Westbrook        final int selectedPos = mFromSpinner.getSelectedItemPosition();
524d90f7ac9b1a62e8beefdac3ceda9e4cb75ef000cMindy Pereira        final ReplyFromAccount selectedReplyFromAccount = (replyFromAccounts != null
525d90f7ac9b1a62e8beefdac3ceda9e4cb75ef000cMindy Pereira                && replyFromAccounts.size() > 0 && replyFromAccounts.size() > selectedPos) ?
526d90f7ac9b1a62e8beefdac3ceda9e4cb75ef000cMindy Pereira                        replyFromAccounts.get(selectedPos) : null;
527e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        if (selectedReplyFromAccount != null) {
528e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            state.putString(EXTRA_SELECTED_REPLY_FROM_ACCOUNT, selectedReplyFromAccount.serialize()
529e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                    .toString());
530e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            state.putParcelable(Utils.EXTRA_ACCOUNT, selectedReplyFromAccount.account);
531e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        } else {
532e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            state.putParcelable(Utils.EXTRA_ACCOUNT, mAccount);
533e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        }
534eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
535e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        if (mDraftId == UIProvider.INVALID_MESSAGE_ID && mRequestId !=0) {
536e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            // We don't have a draft id, and we have a request id,
537e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            // save the request id.
538e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            state.putInt(EXTRA_REQUEST_ID, mRequestId);
539e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        }
540e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira
541e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        // We want to restore the current mode after a pause
542e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        // or rotation.
543e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        int mode = getMode();
544e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        state.putInt(EXTRA_ACTION, mode);
545e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira
546e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        Message message = createMessage(selectedReplyFromAccount, mode);
547e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        state.putParcelable(EXTRA_MESSAGE, message);
548e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira
549e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        if (mRefMessage != null) {
550e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            state.putParcelable(EXTRA_IN_REFERENCE_TO_MESSAGE, mRefMessage);
551e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        }
552326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira        state.putBoolean(EXTRA_SHOW_CC, mCcBccView.isCcVisible());
553326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira        state.putBoolean(EXTRA_SHOW_BCC, mCcBccView.isBccVisible());
554e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    }
555e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira
556e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private int getMode() {
557e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        int mode = ComposeActivity.COMPOSE;
558e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        ActionBar actionBar = getActionBar();
559e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        if (actionBar != null
560e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                && actionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_LIST) {
561e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            mode = actionBar.getSelectedNavigationIndex();
562e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        }
563e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        return mode;
564e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    }
565e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira
566e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private Message createMessage(ReplyFromAccount selectedReplyFromAccount, int mode) {
567e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        Message message = new Message();
568e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.id = UIProvider.INVALID_MESSAGE_ID;
569e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.serverId =UIProvider.INVALID_MESSAGE_ID;
570e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.uri = null;
571e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.conversationUri = null;
572e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.subject = mSubject.getText().toString();
573e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.snippet = null;
57491906814d08fe6bcfcd97f25f5a01d36d9d5dc73Paul Westbrook        message.from = selectedReplyFromAccount != null ?
57591906814d08fe6bcfcd97f25f5a01d36d9d5dc73Paul Westbrook                selectedReplyFromAccount.name : mAccount.name;
576e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.to = mTo.getText().toString();
5774b1377e9f7677671eda0d5508a0603ec7433d19dMindy Pereira        message.cc = mCc.getText().toString();
5784b1377e9f7677671eda0d5508a0603ec7433d19dMindy Pereira        message.bcc = mBcc.getText().toString();
579e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.replyTo = null;
580e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.dateReceivedMs = 0;
581e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        String htmlBody = Html.toHtml(mBodyView.getText());
582e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        StringBuilder fullBody = new StringBuilder(htmlBody);
583e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.bodyHtml = fullBody.toString();
584e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.bodyText = mBodyView.getText().toString();
585e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.embedsExternalResources = false;
586e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.refMessageId = mRefMessage != null ? mRefMessage.uri.toString() : null;
587d2bef8b371784d95f60dd3f89aa0ac3557c945efMindy Pereira        message.draftType = getDraftType(mode);
588e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.appendRefMessageContent = mQuotedTextView.getQuotedTextIfIncluded() != null;
589e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        ArrayList<Attachment> attachments = mAttachmentsView.getAttachments();
590e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.hasAttachments = attachments != null && attachments.size() > 0;
591e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.attachmentListUri = null;
592e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.messageFlags = 0;
593e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.saveUri = null;
594e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.sendUri = null;
595e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.alwaysShowImages = false;
596e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.attachmentsJson = Attachment.toJSONArray(attachments);
597e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        CharSequence quotedText = mQuotedTextView.getQuotedText();
598e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.quotedTextOffset = !TextUtils.isEmpty(quotedText) ? QuotedTextView
599e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                .getQuotedTextOffset(quotedText.toString()) : -1;
600e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        message.accountUri = null;
601e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        return message;
602eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
603eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
604818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    @VisibleForTesting
605818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    void setAccount(Account account) {
606bb5217ee950c156d761a086bfd0d1d392d224cf4Mindy Pereira        if (account == null) {
607bb5217ee950c156d761a086bfd0d1d392d224cf4Mindy Pereira            return;
608bb5217ee950c156d761a086bfd0d1d392d224cf4Mindy Pereira        }
60923e9fdec6872f9b4d0fbe675745cb25becf7878cMindy Pereira        if (!account.equals(mAccount)) {
61023e9fdec6872f9b4d0fbe675745cb25becf7878cMindy Pereira            mAccount = account;
611b1f573c59ec20f9149443e1e0cc8b64ff3a07feaPaul Westbrook            mCachedSettings = mAccount.settings;
612b1f573c59ec20f9149443e1e0cc8b64ff3a07feaPaul Westbrook            appendSignature();
61323e9fdec6872f9b4d0fbe675745cb25becf7878cMindy Pereira        }
614818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    }
615818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira
616e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private void initFromSpinner(Bundle bundle, int action) {
6179a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira        String accountString = null;
618e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        if (action == EDIT_DRAFT && mDraft.draftType == UIProvider.DraftType.COMPOSE) {
61962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            action = COMPOSE;
62062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        }
62162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        mFromSpinner.asyncInitFromSpinner(action, mAccount);
6229a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira        if (bundle != null) {
6239a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira            if (bundle.containsKey(EXTRA_SELECTED_REPLY_FROM_ACCOUNT)) {
6249a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira                mReplyFromAccount = ReplyFromAccount.deserialize(mAccount,
6259a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira                        bundle.getString(EXTRA_SELECTED_REPLY_FROM_ACCOUNT));
6269a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira            } else if (bundle.containsKey(EXTRA_FROM_ACCOUNT_STRING)) {
6279a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira                accountString = bundle.getString(EXTRA_FROM_ACCOUNT_STRING);
6289a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira                mReplyFromAccount = mFromSpinner.getMatchingReplyFromAccount(accountString);
6299a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira            }
630e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        }
631e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        if (mReplyFromAccount == null) {
632e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            if (mDraft != null) {
633e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                mReplyFromAccount = getReplyFromAccountFromDraft(mAccount, mDraft);
634e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            } else if (mRefMessage != null) {
635e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira                mReplyFromAccount = getReplyFromAccountForReply(mAccount, mRefMessage);
636e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira            }
63762de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        }
63862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        if (mReplyFromAccount == null) {
63962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            mReplyFromAccount = new ReplyFromAccount(mAccount, mAccount.uri, mAccount.name,
640cd970ddaba4e212b668588db2104dc46ad3ad793Mindy Pereira                    mAccount.name, mAccount.name, true, false);
64162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        }
6429a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira
64362de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        mFromSpinner.setCurrentAccount(mReplyFromAccount);
6449a42bb4f11c502dd5c2cf92249ac214a6c2f1a70Mindy Pereira
64562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        if (mFromSpinner.getCount() > 1) {
646a83e708e162ad1f4b04b41738882bf318e217f64Mindy Pereira            // If there is only 1 account, just show that account.
647a83e708e162ad1f4b04b41738882bf318e217f64Mindy Pereira            // Otherwise, give the user the ability to choose which account to
64862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            // send mail from / save drafts to.
64962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            mFromStatic.setVisibility(View.GONE);
650a83e708e162ad1f4b04b41738882bf318e217f64Mindy Pereira            mFromStaticText.setText(mAccount.name);
65162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            mFromSpinnerWrapper.setVisibility(View.VISIBLE);
652a83e708e162ad1f4b04b41738882bf318e217f64Mindy Pereira        } else {
653a83e708e162ad1f4b04b41738882bf318e217f64Mindy Pereira            mFromStatic.setVisibility(View.VISIBLE);
654a83e708e162ad1f4b04b41738882bf318e217f64Mindy Pereira            mFromStaticText.setText(mAccount.name);
655a83e708e162ad1f4b04b41738882bf318e217f64Mindy Pereira            mFromSpinnerWrapper.setVisibility(View.GONE);
656a83e708e162ad1f4b04b41738882bf318e217f64Mindy Pereira        }
65746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
65846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
65962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    private ReplyFromAccount getReplyFromAccountForReply(Account account, Message refMessage) {
66062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        if (refMessage.accountUri != null) {
66162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            // This must be from combined inbox.
66262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            List<ReplyFromAccount> replyFromAccounts = mFromSpinner.getReplyFromAccounts();
66362de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            for (ReplyFromAccount from : replyFromAccounts) {
66462de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                if (from.account.uri.equals(refMessage.accountUri)) {
66562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                    return from;
66662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                }
66762de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            }
66862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            return null;
66962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        } else {
67062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            return getReplyFromAccount(account, refMessage);
67162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        }
67262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    }
67362de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira
67462de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    /**
67562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira     * Given an account and which email address the message was sent to,
67662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira     * return who the message should be sent from.
67762de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira     * @param account Account in which the message arrived.
67862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira     * @param sentTo Email address to which the message was sent.
67962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira     * @return the address from which to reply.
68062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira     */
68162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    public ReplyFromAccount getReplyFromAccount(Account account, Message refMessage) {
68262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        // First see if we are supposed to use the default address or
68362de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        // the address it was sentTo.
684326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira        if (mCachedSettings.forceReplyFromDefault) {
68562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            return getDefaultReplyFromAccount(account);
68662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        } else {
68789bae5713b830ec1182073de22c3091d3613ac3eMindy Pereira            // If we aren't explicitly told which account to look for, look at
68862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            // all the message recipients and find one that matches
68962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            // a custom from or account.
69062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            List<String> allRecipients = new ArrayList<String>();
69162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            allRecipients.addAll(Arrays.asList(Utils.splitCommaSeparatedString(refMessage.to)));
69262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            allRecipients.addAll(Arrays.asList(Utils.splitCommaSeparatedString(refMessage.cc)));
69362de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            return getMatchingRecipient(account, allRecipients);
69462de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        }
69562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    }
69662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira
69762de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    /**
69862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira     * Compare all the recipients of an email to the current account and all
69962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira     * custom addresses associated with that account. Return the match if there
70062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira     * is one, or the default account if there isn't.
70162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira     */
70262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    protected ReplyFromAccount getMatchingRecipient(Account account, List<String> sentTo) {
70362de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        // Tokenize the list and place in a hashmap.
70462de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        ReplyFromAccount matchingReplyFrom = null;
70562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        Rfc822Token[] tokens;
70662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        HashSet<String> recipientsMap = new HashSet<String>();
70762de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        for (String address : sentTo) {
70862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            tokens = Rfc822Tokenizer.tokenize(address);
70962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            for (int i = 0; i < tokens.length; i++) {
71062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                recipientsMap.add(tokens[i].getAddress());
71162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            }
71262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        }
71362de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira
71462de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        int matchingAddressCount = 0;
71562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        List<ReplyFromAccount> customFroms;
71662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        try {
71762de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            customFroms = FromAddressSpinner.getAccountSpecificFroms(account);
71862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            if (customFroms != null) {
71962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                for (ReplyFromAccount entry : customFroms) {
72062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                    if (recipientsMap.contains(entry.address)) {
72162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                        matchingReplyFrom = entry;
72262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                        matchingAddressCount++;
72362de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                    }
72462de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                }
72562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            }
72662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        } catch (JSONException e) {
72762de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            LogUtils.wtf(LOG_TAG, "Exception parsing from addresses for account %s",
72862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                    account.name);
72962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        }
73062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        if (matchingAddressCount > 1) {
73162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            matchingReplyFrom = getDefaultReplyFromAccount(account);
73262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        }
73362de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        return matchingReplyFrom;
73462de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    }
73562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira
73662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    private ReplyFromAccount getDefaultReplyFromAccount(Account account) {
73762de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        List<ReplyFromAccount> replyFromAccounts = mFromSpinner.getReplyFromAccounts();
73862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        for (ReplyFromAccount from : replyFromAccounts) {
73962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            if (from.isDefault) {
74062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                return from;
74162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            }
74262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        }
743cd970ddaba4e212b668588db2104dc46ad3ad793Mindy Pereira        return new ReplyFromAccount(account, account.uri, account.name, account.name, account.name,
744cd970ddaba4e212b668588db2104dc46ad3ad793Mindy Pereira                true, false);
74562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    }
74662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira
747e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private ReplyFromAccount getReplyFromAccountFromDraft(Account account, Message msg) {
748e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        String sender = msg.from;
74962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        ReplyFromAccount replyFromAccount = null;
75062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        List<ReplyFromAccount> replyFromAccounts = mFromSpinner.getReplyFromAccounts();
75162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        if (TextUtils.equals(account.name, sender)) {
75262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            replyFromAccount = new ReplyFromAccount(mAccount, mAccount.uri, mAccount.name,
753cd970ddaba4e212b668588db2104dc46ad3ad793Mindy Pereira                    mAccount.name, mAccount.name, true, false);
75462de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        } else {
75562de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            for (ReplyFromAccount fromAccount : replyFromAccounts) {
75662de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                if (TextUtils.equals(fromAccount.name, sender)) {
75762de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                    replyFromAccount = fromAccount;
75862de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                    break;
75962de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira                }
76062de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira            }
76162de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        }
76262de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira        return replyFromAccount;
76362de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira    }
76462de1b104635b09e7f00b0941268ba1fa5460ddbMindy Pereira
76546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void findViews() {
766a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        mCcBccButton = (Button) findViewById(R.id.add_cc_bcc);
7678e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        if (mCcBccButton != null) {
7688e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira            mCcBccButton.setOnClickListener(this);
7698e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        }
7708e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        mCcBccView = (CcBccView) findViewById(R.id.cc_bcc_wrapper);
7717b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira        mAttachmentsView = (AttachmentsView)findViewById(R.id.attachments);
7721f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        mAttachmentsButton = (ImageView) findViewById(R.id.add_attachment);
7731f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        if (mAttachmentsButton != null) {
7741f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira            mAttachmentsButton.setOnClickListener(this);
7751f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        }
776818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mTo = (RecipientEditTextView) findViewById(R.id.to);
777818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mCc = (RecipientEditTextView) findViewById(R.id.cc);
778818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mBcc = (RecipientEditTextView) findViewById(R.id.bcc);
77982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // TODO: add special chips text change watchers before adding
78082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // this as a text changed watcher to the to, cc, bcc fields.
7816349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        mSubject = (TextView) findViewById(R.id.subject);
78246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mQuotedTextView = (QuotedTextView) findViewById(R.id.quoted_text_view);
78346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mQuotedTextView.setRespondInlineListener(this);
784433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        mBodyView = (EditText) findViewById(R.id.body);
7851a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        mFromStatic = findViewById(R.id.static_from_content);
7862eb173211741adf7454bbf1990ba1b9d1e1f6a64Mindy Pereira        mFromStaticText = (TextView) findViewById(R.id.from_account_name);
787eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromSpinnerWrapper = findViewById(R.id.spinner_from_content);
7885a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner = (FromAddressSpinner) findViewById(R.id.from_picker);
789326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
790326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
791e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    protected TextView getBody() {
792e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        return mBodyView;
793e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    }
794e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira
795e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    @VisibleForTesting
796e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    public Account getFromAccount() {
797e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        return mReplyFromAccount != null && mReplyFromAccount.account != null ?
798e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira                mReplyFromAccount.account : mAccount;
799e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    }
800e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira
801cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira    private void clearChangeListeners() {
802cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        mSubject.removeTextChangedListener(this);
803cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        mBodyView.removeTextChangedListener(this);
804cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        mTo.removeTextChangedListener(mToListener);
805cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        mCc.removeTextChangedListener(mCcListener);
806cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        mBcc.removeTextChangedListener(mBccListener);
807cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        mFromSpinner.setOnAccountChangedListener(null);
808cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        mAttachmentsView.setAttachmentChangesListener(null);
809cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira    }
810cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira
81175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // Now that the message has been initialized from any existing draft or
81275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // ref message data, set up listeners for any changes that occur to the
81375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // message.
81475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    private void initChangeListeners() {
81575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mSubject.addTextChangedListener(this);
81675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mBodyView.addTextChangedListener(this);
817cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        if (mToListener == null) {
818cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira            mToListener = new RecipientTextWatcher(mTo, this);
819cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        }
820cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        mTo.addTextChangedListener(mToListener);
821cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        if (mCcListener == null) {
822cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira            mCcListener = new RecipientTextWatcher(mCc, this);
823cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        }
824cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        mCc.addTextChangedListener(mCcListener);
825cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        if (mBccListener == null) {
826cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira            mBccListener = new RecipientTextWatcher(mBcc, this);
827cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        }
828cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        mBcc.addTextChangedListener(mBccListener);
82975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mFromSpinner.setOnAccountChangedListener(this);
830818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mAttachmentsView.setAttachmentChangesListener(this);
83175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
83275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
833326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private void initActionBar(int action) {
834326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        mComposeMode = action;
835eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        ActionBar actionBar = getActionBar();
836e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        if (actionBar == null) {
837e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira            return;
838e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        }
839326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        if (action == ComposeActivity.COMPOSE) {
840eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
841eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setTitle(R.string.compose);
842326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else {
843eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setTitle(null);
844326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (mComposeModeAdapter == null) {
845326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                mComposeModeAdapter = new ComposeModeAdapter(this);
846326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
847eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
848eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setListNavigationCallbacks(mComposeModeAdapter, this);
849326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            switch (action) {
850326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.REPLY:
851eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(0);
852326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
853326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.REPLY_ALL:
854eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(1);
855326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
856326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.FORWARD:
857eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(2);
858326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
859326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
8606349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        }
861fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME,
862fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME);
863fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        actionBar.setHomeButtonEnabled(true);
8646349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
8656349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
86646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void initFromRefMessage(int action, String recipientAddress) {
8678eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        setSubject(mRefMessage, action);
8688eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        // Setup recipients
8698eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (action == FORWARD) {
8708eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            mForward = true;
8718eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
8728eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        initRecipientsFromRefMessage(recipientAddress, mRefMessage, action);
873e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        initQuotedTextFromRefMessage(mRefMessage, action);
8748eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (action == ComposeActivity.FORWARD || mAttachmentsChanged) {
8758eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            initAttachments(mRefMessage);
8766349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        }
877c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    }
878c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira
879e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private void initFromDraftMessage(Message message) {
880bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        LogUtils.d(LOG_TAG, "Intializing draft from previous draft message");
881bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook
882bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        mDraft = message;
883bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        mDraftId = message.id;
884bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        mSubject.setText(message.subject);
885bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        mForward = message.draftType == UIProvider.DraftType.FORWARD;
886bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        final List<String> toAddresses = Arrays.asList(message.getToAddresses());
887bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        addToAddresses(toAddresses);
888bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        addCcAddresses(Arrays.asList(message.getCcAddresses()), toAddresses);
889bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        addBccAddresses(Arrays.asList(message.getBccAddresses()));
8902421dc8d6d0b4c727ab7ab0e5ce730f415089e86Mindy Pereira        if (message.hasAttachments) {
8912421dc8d6d0b4c727ab7ab0e5ce730f415089e86Mindy Pereira            List<Attachment> attachments = message.getAttachments();
8922421dc8d6d0b4c727ab7ab0e5ce730f415089e86Mindy Pereira            for (Attachment a : attachments) {
8935c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang                addAttachmentAndUpdateView(a);
8942421dc8d6d0b4c727ab7ab0e5ce730f415089e86Mindy Pereira            }
8952421dc8d6d0b4c727ab7ab0e5ce730f415089e86Mindy Pereira        }
896cc8e7db940b2ad6c673e84ca7addae9295c10a33Mindy Pereira        int quotedTextIndex = message.appendRefMessageContent ?
897002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                message.quotedTextOffset : -1;
898bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        // Set the body
899002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira        CharSequence quotedText = null;
900bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        if (!TextUtils.isEmpty(message.bodyHtml)) {
901002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            CharSequence htmlText = Html.fromHtml(message.bodyHtml);
902002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            if (quotedTextIndex > -1) {
903002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                htmlText = htmlText.subSequence(0, quotedTextIndex);
904002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                quotedText = message.bodyHtml.subSequence(quotedTextIndex,
905002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                        message.bodyHtml.length());
906002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            }
907002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            mBodyView.setText(htmlText);
908bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        } else {
909002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            CharSequence bodyText = quotedTextIndex > -1 ?
910002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                    message.bodyText.substring(0, quotedTextIndex) : message.bodyText;
911002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            if (quotedTextIndex > -1) {
912002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                quotedText = message.bodyText.substring(quotedTextIndex);
913002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            }
914002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            mBodyView.setText(bodyText);
915002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira        }
916002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira        if (quotedTextIndex > -1 && quotedText != null) {
9173971323f24b15b541f7460c1a4db42d78eb1873bMindy Pereira            mQuotedTextView.setQuotedTextFromDraft(quotedText, mForward);
918bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        }
919bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook    }
920bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook
9218eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    /**
9228eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     * Fill all the widgets with the content found in the Intent Extra, if any.
9238eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     * Also apply the same style to all widgets. Note: if initFromExtras is
9248eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     * called as a result of switching between reply, reply all, and forward per
9258eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     * the latest revision of Gmail, and the user has already made changes to
9268eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     * attachments on a previous incarnation of the message (as a reply, reply
9278eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     * all, or forward), the original attachments from the message will not be
9288eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     * re-instantiated. The user's changes will be respected. This follows the
9298eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     * web gmail interaction.
9308eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     */
9318eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    public void initFromExtras(Intent intent) {
9328eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        // If we were invoked with a SENDTO intent, the value
9338eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        // should take precedence
9348eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        final Uri dataUri = intent.getData();
9358eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (dataUri != null) {
9368eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            if (MAIL_TO.equals(dataUri.getScheme())) {
9378eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                initFromMailTo(dataUri.toString());
9388eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            } else {
9390b4f28ed53a7f1617dadf3b34de8c743a1733ce5Mindy Pereira                if (!mAccount.composeIntentUri.equals(dataUri)) {
9408eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                    String toText = dataUri.getSchemeSpecificPart();
9418eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                    if (toText != null) {
9428eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                        mTo.setText("");
943dbe8996d8ba382bfe1a23894abbc4711e36c9daeMindy Pereira                        addToAddresses(Arrays.asList(TextUtils.split(toText, ",")));
9448eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                    }
9458eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                }
9468eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            }
9478eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
9488eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
9498eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        String[] extraStrings = intent.getStringArrayExtra(Intent.EXTRA_EMAIL);
9508eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (extraStrings != null) {
9518eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            addToAddresses(Arrays.asList(extraStrings));
9528eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
9538eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        extraStrings = intent.getStringArrayExtra(Intent.EXTRA_CC);
9548eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (extraStrings != null) {
9558eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            addCcAddresses(Arrays.asList(extraStrings), null);
9568eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
9578eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        extraStrings = intent.getStringArrayExtra(Intent.EXTRA_BCC);
9588eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (extraStrings != null) {
9598eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            addBccAddresses(Arrays.asList(extraStrings));
9608eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
9618eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
9628eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        String extraString = intent.getStringExtra(Intent.EXTRA_SUBJECT);
9638eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (extraString != null) {
9648eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            mSubject.setText(extraString);
9658eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
9668eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
9678eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        for (String extra : ALL_EXTRAS) {
9688eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            if (intent.hasExtra(extra)) {
9698eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                String value = intent.getStringExtra(extra);
9708eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                if (EXTRA_TO.equals(extra)) {
971dbe8996d8ba382bfe1a23894abbc4711e36c9daeMindy Pereira                    addToAddresses(Arrays.asList(TextUtils.split(value, ",")));
9728eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                } else if (EXTRA_CC.equals(extra)) {
973dbe8996d8ba382bfe1a23894abbc4711e36c9daeMindy Pereira                    addCcAddresses(Arrays.asList(TextUtils.split(value, ",")), null);
9748eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                } else if (EXTRA_BCC.equals(extra)) {
975dbe8996d8ba382bfe1a23894abbc4711e36c9daeMindy Pereira                    addBccAddresses(Arrays.asList(TextUtils.split(value, ",")));
9768eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                } else if (EXTRA_SUBJECT.equals(extra)) {
9778eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                    mSubject.setText(value);
9788eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                } else if (EXTRA_BODY.equals(extra)) {
9798eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                    setBody(value, true /* with signature */);
9808eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                }
9818eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            }
9828eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
9838eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
9848eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        Bundle extras = intent.getExtras();
9858eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (extras != null) {
9868eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            CharSequence text = extras.getCharSequence(Intent.EXTRA_TEXT);
9878eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            if (text != null) {
9888eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                setBody(text, true /* with signature */);
9898eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            }
9908eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
991e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    }
992e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira
9938eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    @VisibleForTesting
9948eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    protected String decodeEmailInUri(String s) throws UnsupportedEncodingException {
995a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira        // TODO: handle the case where there are spaces in the display name as
996a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira        // well as the email such as "Guy with spaces <guy+with+spaces@gmail.com>"
997a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira        // as they could be encoded ambiguously.
9988eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        // Since URLDecode.decode changes + into ' ', and + is a valid
9998eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        // email character, we need to find/ replace these ourselves before
10008eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        // decoding.
10018eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        String replacePlus = s.replace("+", "%2B");
1002a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira        try {
1003a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira            return URLDecoder.decode(replacePlus, UTF8_ENCODING_NAME);
1004a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira        } catch (IllegalArgumentException e) {
1005a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira            if (LogUtils.isLoggable(LOG_TAG, LogUtils.VERBOSE)) {
1006a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira                LogUtils.e(LOG_TAG, "%s while decoding '%s'", e.getMessage(), s);
1007a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira            } else {
1008a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira                LogUtils.e(LOG_TAG, e, "Exception  while decoding mailto address");
1009a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira            }
1010a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira            return null;
1011a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira        }
10128eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    }
10138eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
10148eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    /**
10158eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     * Initialize the compose view from a String representing a mailTo uri.
10168eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     * @param mailToString The uri as a string.
10178eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira     */
10188eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    public void initFromMailTo(String mailToString) {
10198eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        // We need to disguise this string as a URI in order to parse it
10208eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        // TODO:  Remove this hack when http://b/issue?id=1445295 gets fixed
10218eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        Uri uri = Uri.parse("foo://" + mailToString);
10228eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        int index = mailToString.indexOf("?");
10238eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        int length = "mailto".length() + 1;
10248eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        String to;
10258eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        try {
10268eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            // Extract the recipient after mailto:
10278eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            if (index == -1) {
10288eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                to = decodeEmailInUri(mailToString.substring(length));
10298eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            } else {
10308eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                to = decodeEmailInUri(mailToString.substring(length, index));
10318eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            }
1032a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira            if (!TextUtils.isEmpty(to)) {
1033a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira                addToAddresses(Arrays.asList(TextUtils.split(to, ",")));
1034a4069f2f16c05c6d2948baa39b0c463be4c7a360Mindy Pereira            }
10358eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        } catch (UnsupportedEncodingException e) {
10368eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            if (LogUtils.isLoggable(LOG_TAG, LogUtils.VERBOSE)) {
10378eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                LogUtils.e(LOG_TAG, "%s while decoding '%s'", e.getMessage(), mailToString);
10388eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            } else {
10398eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                LogUtils.e(LOG_TAG, e, "Exception  while decoding mailto address");
10408eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            }
10418eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
10428eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
10438eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        List<String> cc = uri.getQueryParameters("cc");
10448eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        addCcAddresses(Arrays.asList(cc.toArray(new String[cc.size()])), null);
10458eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
10468eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        List<String> otherTo = uri.getQueryParameters("to");
10478eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        addToAddresses(Arrays.asList(otherTo.toArray(new String[otherTo.size()])));
10488eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
10498eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        List<String> bcc = uri.getQueryParameters("bcc");
10508eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        addBccAddresses(Arrays.asList(bcc.toArray(new String[bcc.size()])));
10518eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
10528eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        List<String> subject = uri.getQueryParameters("subject");
10538eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (subject.size() > 0) {
10548eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            try {
10558eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                mSubject.setText(URLDecoder.decode(subject.get(0), UTF8_ENCODING_NAME));
10568eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            } catch (UnsupportedEncodingException e) {
10578eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                LogUtils.e(LOG_TAG, "%s while decoding subject '%s'",
10588eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                        e.getMessage(), subject);
10598eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            }
10608eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
10618eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
10628eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        List<String> body = uri.getQueryParameters("body");
10638eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (body.size() > 0) {
10648eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            try {
10658eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                setBody(URLDecoder.decode(body.get(0), UTF8_ENCODING_NAME),
10668eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                        true /* with signature */);
10678eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            } catch (UnsupportedEncodingException e) {
10688eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                LogUtils.e(LOG_TAG, "%s while decoding body '%s'", e.getMessage(), body);
10698eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            }
10708eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        }
10718eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira    }
10728eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira
1073bddd6f39f34aa15fed2e757f7d3e4163272ca02cMindy Pereira    @VisibleForTesting
1074bddd6f39f34aa15fed2e757f7d3e4163272ca02cMindy Pereira    protected void initAttachments(Message refMessage) {
10757a07fb4955e34250225287212b07d1b6f12bf671Mindy Pereira        mAttachmentsView.addAttachments(mAccount, refMessage);
1076eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1077eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1078f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook    private void initAttachmentsFromIntent(Intent intent) {
107903ee971afd8d766dea81b72fe77593e3be3ab798Paul Westbrook        Bundle extras = intent.getExtras();
108003ee971afd8d766dea81b72fe77593e3be3ab798Paul Westbrook        if (extras == null) {
108103ee971afd8d766dea81b72fe77593e3be3ab798Paul Westbrook            extras = Bundle.EMPTY;
108203ee971afd8d766dea81b72fe77593e3be3ab798Paul Westbrook        }
1083f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook        final String action = intent.getAction();
1084f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook        if (!mAttachmentsChanged) {
1085f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            long totalSize = 0;
1086f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            if (extras.containsKey(EXTRA_ATTACHMENTS)) {
1087f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                String[] uris = (String[]) extras.getSerializable(EXTRA_ATTACHMENTS);
1088f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                for (String uriString : uris) {
1089f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    final Uri uri = Uri.parse(uriString);
1090f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    long size = 0;
1091f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    try {
10925c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang                        size =  mAttachmentsView.addAttachment(mAccount, uri);
1093f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    } catch (AttachmentFailureException e) {
1094f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        // A toast has already been shown to the user,
1095f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        // just break out of the loop.
1096f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        LogUtils.e(LOG_TAG, e, "Error adding attachment");
1097f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    }
1098f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    totalSize += size;
1099f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                }
1100f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            }
1101f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            if (Intent.ACTION_SEND.equals(action) && extras.containsKey(Intent.EXTRA_STREAM)) {
1102f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                final Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);
1103f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                long size = 0;
1104f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                try {
11055c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang                    size =  mAttachmentsView.addAttachment(mAccount, uri);
1106f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                } catch (AttachmentFailureException e) {
1107f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    // A toast has already been shown to the user, so just
1108f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    // exit.
1109f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    LogUtils.e(LOG_TAG, e, "Error adding attachment");
1110f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                }
1111f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                totalSize += size;
1112f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            }
1113f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook
1114f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            if (Intent.ACTION_SEND_MULTIPLE.equals(action)
1115f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    && extras.containsKey(Intent.EXTRA_STREAM)) {
1116f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                ArrayList<Parcelable> uris = extras.getParcelableArrayList(Intent.EXTRA_STREAM);
1117f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                for (Parcelable uri : uris) {
1118f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    long size = 0;
1119f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    try {
11205c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang                        size = mAttachmentsView.addAttachment(mAccount, (Uri) uri);
1121f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    } catch (AttachmentFailureException e) {
1122f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        // A toast has already been shown to the user,
1123f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        // just break out of the loop.
1124f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        LogUtils.e(LOG_TAG, e, "Error adding attachment");
1125f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    }
1126f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    totalSize += size;
1127f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                }
1128f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            }
1129f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook
1130f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            if (totalSize > 0) {
1131f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                mAttachmentsChanged = true;
1132f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                updateSaveUi();
1133f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            }
1134f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook        }
1135f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook    }
1136f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook
1137f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook
1138e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    private void initQuotedTextFromRefMessage(Message refMessage, int action) {
1139e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        if (mRefMessage != null && (action == REPLY || action == REPLY_ALL || action == FORWARD)) {
11409932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            mQuotedTextView.setQuotedText(action, refMessage, action != FORWARD);
11419932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        }
114246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
114346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
114446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void updateHideOrShowCcBcc() {
1145ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        // Its possible there is a menu item OR a button.
1146326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira        boolean ccVisible = mCcBccView.isCcVisible();
1147326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira        boolean bccVisible = mCcBccView.isBccVisible();
1148ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (mCcBccButton != null) {
1149326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira            if (!ccVisible || !bccVisible) {
1150ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setVisibility(View.VISIBLE);
1151326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira                mCcBccButton.setText(getString(!ccVisible ? R.string.add_cc_label
1152ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                        : R.string.add_bcc_label));
1153ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            } else {
1154ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setVisibility(View.GONE);
1155ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            }
1156ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
115746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
115846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
1159a34c9a054c9aef6f83abe56515c132a31d71b3c8Mindy Pereira    private void showCcBcc(Bundle state) {
1160326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira        if (state != null && state.containsKey(EXTRA_SHOW_CC)) {
1161326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira            boolean showCc = state.getBoolean(EXTRA_SHOW_CC);
1162326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira            boolean showBcc = state.getBoolean(EXTRA_SHOW_BCC);
1163326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira            if (showCc || showBcc) {
1164326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira                mCcBccView.show(false, showCc, showBcc);
1165326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira            }
1166326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira        }
1167326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira    }
1168326689d531da1d2579fa7daaf6e307e24a05f993Mindy Pereira
1169013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    /**
1170013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     * Add attachment and update the compose area appropriately.
1171013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     * @param data
1172013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     */
1173013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    public void addAttachmentAndUpdateView(Intent data) {
11742421dc8d6d0b4c727ab7ab0e5ce730f415089e86Mindy Pereira        addAttachmentAndUpdateView(data != null ? data.getData() : (Uri) null);
11752421dc8d6d0b4c727ab7ab0e5ce730f415089e86Mindy Pereira    }
11762421dc8d6d0b4c727ab7ab0e5ce730f415089e86Mindy Pereira
11775c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang    public void addAttachmentAndUpdateView(Uri contentUri) {
11785c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang        if (contentUri == null) {
11792421dc8d6d0b4c727ab7ab0e5ce730f415089e86Mindy Pereira            return;
11802421dc8d6d0b4c727ab7ab0e5ce730f415089e86Mindy Pereira        }
1181013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        try {
11825c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang            addAttachmentAndUpdateView(mAttachmentsView.generateLocalAttachment(contentUri));
11835c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang        } catch (AttachmentFailureException e) {
11845c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang            // A toast has already been shown to the user, no need to do
11855c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang            // anything.
11865c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang            LogUtils.e(LOG_TAG, e, "Error adding attachment");
11875c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang        }
11885c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang    }
11895c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang
11905c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang    public void addAttachmentAndUpdateView(Attachment attachment) {
11915c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang        try {
11925c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang            long size =  mAttachmentsView.addAttachment(mAccount, attachment);
11939932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            if (size > 0) {
11949932dee248718d2cec993d93b5115973d1389d0aMindy Pereira                mAttachmentsChanged = true;
11959932dee248718d2cec993d93b5115973d1389d0aMindy Pereira                updateSaveUi();
1196013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            }
11979932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        } catch (AttachmentFailureException e) {
11989932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            // A toast has already been shown to the user, no need to do
11999932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            // anything.
12009932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            LogUtils.e(LOG_TAG, e, "Error adding attachment");
1201013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        }
1202013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    }
1203013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira
12043ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    void initRecipientsFromRefMessage(String recipientAddress, Message refMessage,
120546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            int action) {
12064a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // Don't populate the address if this is a forward.
12074a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        if (action == ComposeActivity.FORWARD) {
12084a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            return;
12094a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
121033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        initReplyRecipients(mAccount.name, refMessage, action);
12114a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
12124a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
1213818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    @VisibleForTesting
12143ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    void initReplyRecipients(String account, Message refMessage, int action) {
12154a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // This is the email address of the current user, i.e. the one composing
12164a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // the reply.
12174a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira        final String accountEmail = Address.getEmailAddress(account).getAddress();
12181469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira        String fromAddress = getAddress(refMessage.from);
12193ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String[] sentToAddresses = Utils.splitCommaSeparatedString(refMessage.to);
12203ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String replytoAddress = refMessage.replyTo;
1221a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        final Collection<String> toAddresses;
12224a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
12234a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // If this is a reply, the Cc list is empty. If this is a reply-all, the
12244a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // Cc list is the union of the To and Cc recipients of the original
12254a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // message, excluding the current user's email address and any addresses
1226a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        // already on the To list.
1227a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (action == ComposeActivity.REPLY) {
12283ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses = initToRecipients(account, accountEmail, fromAddress, replytoAddress,
12291469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira                    sentToAddresses);
1230a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            addToAddresses(toAddresses);
1231a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        } else if (action == ComposeActivity.REPLY_ALL) {
12324a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            final Set<String> ccAddresses = Sets.newHashSet();
12333ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses = initToRecipients(account, accountEmail, fromAddress, replytoAddress,
12343ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                    new String[0]);
1235154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            addToAddresses(toAddresses);
12364a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addRecipients(accountEmail, ccAddresses, sentToAddresses);
12373ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            addRecipients(accountEmail, ccAddresses,
12383ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                    Utils.splitCommaSeparatedString(refMessage.cc));
12394a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addCcAddresses(ccAddresses, toAddresses);
12404a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
12414a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
12424a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
12431469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira    private String getAddress(String toParse) {
12441469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira        if (!TextUtils.isEmpty(toParse)) {
12451469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira            Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(toParse);
12461469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira            if (tokens.length > 0) {
12471469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira                return tokens[0].getAddress();
12481469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira            }
12491469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira        }
12501469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira        return "";
12511469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira    }
12521469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira
12534a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addToAddresses(Collection<String> addresses) {
12544a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        addAddressesToList(addresses, mTo);
12554a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
12564a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
12574a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addCcAddresses(Collection<String> addresses, Collection<String> toAddresses) {
12588eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        addCcAddressesToList(tokenizeAddressList(addresses),
12598eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                toAddresses != null ? tokenizeAddressList(toAddresses) : null, mCc);
12604a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
12614a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
1262bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook    private void addBccAddresses(Collection<String> addresses) {
1263bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook        addAddressesToList(addresses, mBcc);
1264bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook    }
1265bb87b7fa57f25fc9cee1f572dd253f33f58f81daPaul Westbrook
12664a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
12674a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected void addCcAddressesToList(List<Rfc822Token[]> addresses,
12684a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            List<Rfc822Token[]> compareToList, RecipientEditTextView list) {
12694a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String address;
12704a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
12718eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        if (compareToList == null) {
12728eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            for (Rfc822Token[] tokens : addresses) {
12738eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                for (int i = 0; i < tokens.length; i++) {
12748eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                    address = tokens[i].toString();
12754a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    list.append(address + END_TOKEN);
12764a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                }
12774a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
12788eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira        } else {
12798eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            HashSet<String> compareTo = convertToHashSet(compareToList);
12808eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            for (Rfc822Token[] tokens : addresses) {
12818eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                for (int i = 0; i < tokens.length; i++) {
12828eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                    address = tokens[i].toString();
12838eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                    // Check if this is a duplicate:
12848eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                    if (!compareTo.contains(tokens[i].getAddress())) {
12858eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                        // Get the address here
12868eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                        list.append(address + END_TOKEN);
12878eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                    }
12888eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                }
12898eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            }
12904a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
12914a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
12924a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
12934a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private HashSet<String> convertToHashSet(List<Rfc822Token[]> list) {
12944a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        HashSet<String> hash = new HashSet<String>();
12954a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (Rfc822Token[] tokens : list) {
12964a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            for (int i = 0; i < tokens.length; i++) {
12974a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                hash.add(tokens[i].getAddress());
12984a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
12994a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
13004a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return hash;
13014a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
13024a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
13034a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected List<Rfc822Token[]> tokenizeAddressList(Collection<String> addresses) {
13044a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        @VisibleForTesting
13054a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        List<Rfc822Token[]> tokenized = new ArrayList<Rfc822Token[]>();
13064a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
13074a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String address: addresses) {
13084a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            tokenized.add(Rfc822Tokenizer.tokenize(address));
13094a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
13104a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return tokenized;
13114a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
13124a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
13134a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
13144a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    void addAddressesToList(Collection<String> addresses, RecipientEditTextView list) {
13154a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String address : addresses) {
13164a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addAddressToList(address, list);
13174a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
13184a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
13194a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
13204a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addAddressToList(String address, RecipientEditTextView list) {
13214a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        if (address == null || list == null)
13224a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            return;
13234a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
13244a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);
13254a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
13264a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (int i = 0; i < tokens.length; i++) {
13274a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            list.append(tokens[i] + END_TOKEN);
13284a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
13294a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
13304a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
13314a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
13324a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected Collection<String> initToRecipients(String account, String accountEmail,
13333ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            String senderAddress, String replyToAddress, String[] inToAddresses) {
13344a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // The To recipient is the reply-to address specified in the original
13354a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // message, unless it is:
13364a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // the current user OR a custom from of the current user, in which case
13374a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // it's the To recipient list of the original message.
13384a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // OR missing, in which case use the sender of the original message
13394a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        Set<String> toAddresses = Sets.newHashSet();
13403ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        if (!TextUtils.isEmpty(replyToAddress)) {
13413ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses.add(replyToAddress);
13424a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        } else {
13431883b346928322a9f045bc5b7426ddcd6af9beb3Mindy Pereira            if (!TextUtils.equals(senderAddress, accountEmail)
13441883b346928322a9f045bc5b7426ddcd6af9beb3Mindy Pereira                    && !ReplyFromAccount.isCustomFrom(senderAddress,
13451883b346928322a9f045bc5b7426ddcd6af9beb3Mindy Pereira                            mFromSpinner.getReplyFromAccounts())) {
13461469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira                toAddresses.add(senderAddress);
13471469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira            } else {
13481469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira                // This happens if the user replies to a message they originally
13491883b346928322a9f045bc5b7426ddcd6af9beb3Mindy Pereira                // wrote. In this case, "reply" really means "re-send," so we
13501883b346928322a9f045bc5b7426ddcd6af9beb3Mindy Pereira                // target the original recipients. This works as expected even
13511883b346928322a9f045bc5b7426ddcd6af9beb3Mindy Pereira                // if the user sent the original message to themselves.
13521469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira                toAddresses.addAll(Arrays.asList(inToAddresses));
13531469b4e00ea101bc86adef274cf6b4705425b7fcMindy Pereira            }
13544a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
13554a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return toAddresses;
13564a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
13574a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
13584a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private static void addRecipients(String account, Set<String> recipients, String[] addresses) {
13594a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String email : addresses) {
13604a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // Do not add this account, or any of the custom froms, to the list
13614a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // of recipients.
13624a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira            final String recipientAddress = Address.getEmailAddress(email).getAddress();
13634a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            if (!account.equalsIgnoreCase(recipientAddress)) {
13644a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                recipients.add(email.replace("\"\"", ""));
13654a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
13664a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
136746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
136846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
13693ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private void setSubject(Message refMessage, int action) {
13703ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String subject = refMessage.subject;
137146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String prefix;
137246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String correctedSubject = null;
137346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (action == ComposeActivity.COMPOSE) {
137446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = "";
137546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else if (action == ComposeActivity.FORWARD) {
137646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = getString(R.string.forward_subject_label);
137746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
137846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = getString(R.string.reply_subject_label);
137946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
138046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
138146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        // Don't duplicate the prefix
138246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (subject.toLowerCase().startsWith(prefix.toLowerCase())) {
138346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            correctedSubject = subject;
138446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
138546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            correctedSubject = String
138646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                    .format(getString(R.string.formatted_subject), prefix, subject);
138746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
138846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mSubject.setText(correctedSubject);
138946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
139046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
1391818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    private void initRecipients() {
1392818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        setupRecipients(mTo);
1393818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        setupRecipients(mCc);
1394818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        setupRecipients(mBcc);
1395eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1396eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1397818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    private void setupRecipients(RecipientEditTextView view) {
1398679a8cc895ec75d3b578dbc77db1e9c04dd7f8b0Paul Westbrook        view.setAdapter(new RecipientAdapter(this, mAccount));
1399c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        view.setTokenizer(new Rfc822Tokenizer());
140082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mValidator == null) {
1401679a8cc895ec75d3b578dbc77db1e9c04dd7f8b0Paul Westbrook            final String accountName = mAccount.name;
140233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            int offset = accountName.indexOf("@") + 1;
140333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            String account = accountName;
1404c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira            if (offset > -1) {
140533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                account = account.substring(accountName.indexOf("@") + 1);
1406c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira            }
140782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mValidator = new Rfc822Validator(account);
1408c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        }
140982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        view.setValidator(mValidator);
14108e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    }
14118e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
14128e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    @Override
14138e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    public void onClick(View v) {
14148e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        int id = v.getId();
14158e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        switch (id) {
1416a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            case R.id.add_cc_bcc:
14178e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                // Verify that cc/ bcc aren't showing.
14188e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                // Animate in cc/bcc.
1419a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira                showCcBccViews();
14208e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                break;
14211f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira            case R.id.add_attachment:
14228f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein                openAttachmentTypeSelectionDialog();
14231f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira                break;
14248e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        }
14258e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    }
1426b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira
1427b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    @Override
1428b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    public boolean onCreateOptionsMenu(Menu menu) {
1429b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        super.onCreateOptionsMenu(menu);
1430b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        MenuInflater inflater = getMenuInflater();
1431b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        inflater.inflate(R.menu.compose_menu, menu);
143282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSave = menu.findItem(R.id.save);
143382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSend = menu.findItem(R.id.send);
14343ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        MenuItem helpItem = menu.findItem(R.id.help_info_menu_item);
14353ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        MenuItem sendFeedbackItem = menu.findItem(R.id.feedback_menu_item);
14363ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        if (helpItem != null) {
14373ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira            helpItem.setVisible(mAccount != null
14383ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira                    && mAccount.supportsCapability(AccountCapabilities.HELP_CONTENT));
14393ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        }
14403ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        if (sendFeedbackItem != null) {
14413ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira            sendFeedbackItem.setVisible(mAccount != null
14423ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira                    && mAccount.supportsCapability(AccountCapabilities.SEND_FEEDBACK));
14433ca5bad62c075184920c5e331870280fc6acca16Mindy Pereira        }
1444b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        return true;
1445b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    }
1446b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira
1447b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    @Override
1448ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    public boolean onPrepareOptionsMenu(Menu menu) {
1449ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        MenuItem ccBcc = menu.findItem(R.id.add_cc_bcc);
1450818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        if (ccBcc != null && mCc != null) {
1451ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            // Its possible there is a menu item OR a button.
1452ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            boolean ccFieldVisible = mCc.isShown();
1453ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            boolean bccFieldVisible = mBcc.isShown();
1454ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            if (!ccFieldVisible || !bccFieldVisible) {
1455ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setVisible(true);
1456ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setTitle(getString(!ccFieldVisible ? R.string.add_cc_label
1457ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                        : R.string.add_bcc_label));
1458ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            } else {
1459ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setVisible(false);
1460ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            }
1461ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
146275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        if (mSave != null) {
146375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mSave.setEnabled(shouldSave());
146475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
1465ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        return true;
1466ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    }
1467ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira
1468ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    @Override
1469b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    public boolean onOptionsItemSelected(MenuItem item) {
1470b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        int id = item.getItemId();
147175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        boolean handled = true;
1472b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        switch (id) {
14737b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira            case R.id.add_attachment:
14748f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein                openAttachmentTypeSelectionDialog();
14757b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira                break;
1476ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            case R.id.add_cc_bcc:
1477ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                showCcBccViews();
1478b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira                break;
147933fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            case R.id.save:
148048e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira                doSave(true);
148133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                break;
148233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            case R.id.send:
148333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                doSend();
148475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                break;
1485efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira            case R.id.discard:
1486efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira                doDiscard();
1487efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira                break;
14881f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira            case R.id.settings:
14891f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira                Utils.showSettings(this, mAccount);
14901f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira                break;
1491fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira            case android.R.id.home:
1492daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook                onAppUpPressed();
1493fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                break;
1494fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira            case R.id.help_info_menu_item:
1495fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                // TODO: enable context sensitive help
1496498e76d7efccc92386fe57c65a2ef8d5a0172604Paul Westbrook                Utils.showHelp(this, mAccount, null);
1497fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                break;
1498fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira            case R.id.feedback_menu_item:
1499fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                Utils.sendFeedback(this, mAccount);
1500fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                break;
150175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            default:
150275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                handled = false;
150333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                break;
1504b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        }
1505b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        return !handled ? super.onOptionsItemSelected(item) : handled;
1506b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    }
1507326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1508daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook    private void onAppUpPressed() {
1509daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        if (mLaunchedFromEmail) {
1510daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook            // If this was started from Gmail, simply treat app up as the system back button, so
1511daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook            // that the last view is restored.
1512daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook            onBackPressed();
1513daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook            return;
1514daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        }
1515daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook
1516daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        // Fire the main activity to ensure it launches the "top" screen of mail.
1517daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        // Since the main Activity is singleTask, it should revive that task if it was already
1518daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        // started.
1519daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        final Intent mailIntent =
1520daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook                Utils.createViewFolderIntent(mAccount.settings.defaultInbox, mAccount, null, false);
1521daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook
1522daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        mailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
1523daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook                Intent.FLAG_ACTIVITY_TASK_ON_HOME);
1524daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        startActivity(mailIntent);
1525daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook        finish();
1526daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook    }
1527daecb4b9b20a609b27fc803390ecf4f524b76089Paul Westbrook
152833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private void doSend() {
152982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        sendOrSaveWithSanityChecks(false, true, false);
153033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    }
153133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira
153248e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira    private void doSave(boolean showToast) {
153348e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira        // Clear the IME composing suggestions from the body and subject before saving.
153448e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira        clearImeText(mBodyView);
153548e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira        clearImeText(mSubject);
1536eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        sendOrSaveWithSanityChecks(true, showToast, false);
153748e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira    }
153848e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira
153948e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira    private void clearImeText(TextView v) {
154048e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira        v.clearComposingText();
154148e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira        BaseInputConnection.removeComposingSpans(v.getEditableText());
154282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
154382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1544e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    @VisibleForTesting
1545e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    public interface SendOrSaveCallback {
154682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask);
15477ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        public void notifyMessageIdAllocated(SendOrSaveMessage sendOrSaveMessage, Message message);
15487ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        public Message getMessage();
154982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void sendOrSaveFinished(SendOrSaveTask sendOrSaveTask, boolean success);
155082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
155182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1552e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    @VisibleForTesting
1553e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    public static class SendOrSaveTask implements Runnable {
155482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        private final Context mContext;
155582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        @VisibleForTesting
1556e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        public final SendOrSaveCallback mSendOrSaveCallback;
1557e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        @VisibleForTesting
1558e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        public final SendOrSaveMessage mSendOrSaveMessage;
155982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
156082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public SendOrSaveTask(Context context, SendOrSaveMessage message,
156182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                SendOrSaveCallback callback) {
156282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mContext = context;
156382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveCallback = callback;
156482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveMessage = message;
156582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
156682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
156782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        @Override
156882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void run() {
15697ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            final SendOrSaveMessage sendOrSaveMessage = mSendOrSaveMessage;
157082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
157192551d057965689f1952faeb06763f0762bc717fMindy Pereira            final ReplyFromAccount selectedAccount = sendOrSaveMessage.mAccount;
15727ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            Message message = mSendOrSaveCallback.getMessage();
15737ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            long messageId = message != null ? message.id : UIProvider.INVALID_MESSAGE_ID;
157482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // If a previous draft has been saved, in an account that is different
157582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // than what the user wants to send from, remove the old draft, and treat this
157682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // as a new message
15777ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            if (!selectedAccount.equals(sendOrSaveMessage.mAccount)) {
157882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                if (messageId != UIProvider.INVALID_MESSAGE_ID) {
157982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    ContentResolver resolver = mContext.getContentResolver();
158082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    ContentValues values = new ContentValues();
158182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    values.put(BaseColumns._ID, messageId);
158292551d057965689f1952faeb06763f0762bc717fMindy Pereira                    if (selectedAccount.account.expungeMessageUri != null) {
158392551d057965689f1952faeb06763f0762bc717fMindy Pereira                        resolver.update(selectedAccount.account.expungeMessageUri, values, null,
1584eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                                null);
1585cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    } else {
1586cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                        // TODO(mindyp) delete the conversation.
1587eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    }
158882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    // reset messageId to 0, so a new message will be created
158982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    messageId = UIProvider.INVALID_MESSAGE_ID;
159082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                }
159182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
159282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
159382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            final long messageIdToSave = messageId;
159482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (messageIdToSave != UIProvider.INVALID_MESSAGE_ID) {
15957ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                sendOrSaveMessage.mValues.put(BaseColumns._ID, messageIdToSave);
159682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mContext.getContentResolver().update(
15977ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        Uri.parse(sendOrSaveMessage.mSave ? message.saveUri : message.sendUri),
15987ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        sendOrSaveMessage.mValues, null, null);
159982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            } else {
16007ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                ContentResolver resolver = mContext.getContentResolver();
160192551d057965689f1952faeb06763f0762bc717fMindy Pereira                Uri messageUri = resolver
160292551d057965689f1952faeb06763f0762bc717fMindy Pereira                        .insert(sendOrSaveMessage.mSave ? selectedAccount.account.saveDraftUri
160392551d057965689f1952faeb06763f0762bc717fMindy Pereira                                : selectedAccount.account.sendMessageUri,
160492551d057965689f1952faeb06763f0762bc717fMindy Pereira                                sendOrSaveMessage.mValues);
16057ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (sendOrSaveMessage.mSave && messageUri != null) {
16067ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    Cursor messageCursor = resolver.query(messageUri,
16077ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                            UIProvider.MESSAGE_PROJECTION, null, null, null);
1608ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                    if (messageCursor != null) {
1609ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                        try {
1610ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                            if (messageCursor.moveToFirst()) {
1611ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                                // Broadcast notification that a new message has
1612ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                                // been allocated
1613ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                                mSendOrSaveCallback.notifyMessageIdAllocated(sendOrSaveMessage,
1614ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                                        new Message(messageCursor));
1615ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                            }
1616ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                        } finally {
1617ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                            messageCursor.close();
1618ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                        }
16197ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    }
16207ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
162182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
162282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
16237ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            if (!sendOrSaveMessage.mSave) {
162482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
16257ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.TO));
162682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
16277ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.CC));
162882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
16297ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.BCC));
163082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
163182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveCallback.sendOrSaveFinished(SendOrSaveTask.this, true);
163282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
163382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
163482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
163582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Array of the outstanding send or save tasks.  Access is synchronized
163682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // with the object itself
163782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /* package for testing */
1638e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    @VisibleForTesting
1639e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    public ArrayList<SendOrSaveTask> mActiveTasks = Lists.newArrayList();
164082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private int mRequestId;
1641bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    private String mSignature;
16428f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein    private AttachmentTypeSelectorAdapter mAttachmentTypeSelectorAdapter;
164382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1644e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    @VisibleForTesting
1645e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    public static class SendOrSaveMessage {
164692551d057965689f1952faeb06763f0762bc717fMindy Pereira        final ReplyFromAccount mAccount;
164782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final ContentValues mValues;
16483ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        final String mRefMessageId;
1649e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        @VisibleForTesting
1650e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        public final boolean mSave;
165182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final int mRequestId;
165282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
165392551d057965689f1952faeb06763f0762bc717fMindy Pereira        public SendOrSaveMessage(ReplyFromAccount account, ContentValues values,
16543ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                String refMessageId, boolean save) {
165582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mAccount = account;
165682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mValues = values;
165782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRefMessageId = refMessageId;
165882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave = save;
165982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRequestId = mValues.hashCode() ^ hashCode();
166082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
166182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
166282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        int requestId() {
166382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            return mRequestId;
166482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
166582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
166682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
166782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
166882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the to recipients.
166982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
167082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getToAddresses() {
167182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mTo);
167282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
167382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
167482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
167582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the cc recipients.
167682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
167782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getCcAddresses() {
167882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mCc);
167982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
168082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
168182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
168282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the bcc recipients.
168382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
168482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getBccAddresses() {
168582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mBcc);
168682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
168782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
168882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getAddressesFromList(RecipientEditTextView list) {
168982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (list == null) {
169082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            return new String[0];
169182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
169282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(list.getText());
169382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        int count = tokens.length;
169482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        String[] result = new String[count];
169582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        for (int i = 0; i < count; i++) {
169682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            result[i] = tokens[i].toString();
169782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
169882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return result;
169982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
170082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
170182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
170282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Check for invalid email addresses.
170382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param to String array of email addresses to check.
170482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param wrongEmailsOut Emails addresses that were invalid.
170582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
170682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void checkInvalidEmails(String[] to, List<String> wrongEmailsOut) {
1707e5f20bf1c293497625b0f2865302e6712a854959Mindy Pereira        if (mValidator == null) {
1708e5f20bf1c293497625b0f2865302e6712a854959Mindy Pereira            return;
1709e5f20bf1c293497625b0f2865302e6712a854959Mindy Pereira        }
171082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        for (String email : to) {
171182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (!mValidator.isValid(email)) {
171282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                wrongEmailsOut.add(email);
171382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
171482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
171582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
171682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
171782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
171882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Show an error because the user has entered an invalid recipient.
171982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param message
172082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
172182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void showRecipientErrorDialog(String message) {
172282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // Only 1 invalid recipients error dialog should be allowed up at a
172382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // time.
172482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mRecipientErrorDialog != null) {
172582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRecipientErrorDialog.dismiss();
172682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
172782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mRecipientErrorDialog = new AlertDialog.Builder(this).setMessage(message).setTitle(
172882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                R.string.recipient_error_dialog_title)
172982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setIconAttribute(android.R.attr.alertDialogIcon)
173082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setPositiveButton(
173182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        R.string.ok, new Dialog.OnClickListener() {
17320bbc858506c95005f2840fa5dff495a9216d2665Marc Blank                            @Override
173382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                            public void onClick(DialogInterface dialog, int which) {
173482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // after the user dismisses the recipient error
173582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // dialog we want to make sure to refocus the
173682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // recipient to field so they can fix the issue
173782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // easily
173882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                if (mTo != null) {
173982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                    mTo.requestFocus();
174082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                }
174182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                mRecipientErrorDialog = null;
174282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                            }
174382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        }).show();
174482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
174582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
174682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
174782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Update the state of the UI based on whether or not the current draft
174882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * needs to be saved and the message is not empty.
174982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
1750eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void updateSaveUi() {
175182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSave != null) {
175282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave.setEnabled((shouldSave() && !isBlank()));
175382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
175482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
175582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
175682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
175782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Returns true if we need to save the current draft.
175882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
175982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean shouldSave() {
17607ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        synchronized (mDraftLock) {
176182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // The message should only be saved if:
176282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // It hasn't been sent AND
176382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // Some text has been added to the message OR
176482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // an attachment has been added or removed
1765eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return (mTextChanged || mAttachmentsChanged ||
176682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    (mReplyFromChanged && !isBlank()));
176782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
176882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
176982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
177082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
1771bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira     * Check if all fields are blank.
177282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @return boolean
177382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
177482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public boolean isBlank() {
177582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return mSubject.getText().length() == 0
1776bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && (mBodyView.getText().length() == 0 || getSignatureStartPosition(mSignature,
1777bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                        mBodyView.getText().toString()) == 0)
1778bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && mTo.length() == 0
1779bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && mCc.length() == 0 && mBcc.length() == 0
1780bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && mAttachmentsView.getAttachments().size() == 0;
1781bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    }
1782bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
1783bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    @VisibleForTesting
1784bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    protected int getSignatureStartPosition(String signature, String bodyText) {
1785bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        int startPos = -1;
1786bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
1787bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        if (TextUtils.isEmpty(signature) || TextUtils.isEmpty(bodyText)) {
1788bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            return startPos;
1789bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        }
1790bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
1791bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        int bodyLength = bodyText.length();
1792bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        int signatureLength = signature.length();
1793bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        String printableVersion = convertToPrintableSignature(signature);
1794bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        int printableLength = printableVersion.length();
1795bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
1796bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        if (bodyLength >= printableLength
1797bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && bodyText.substring(bodyLength - printableLength)
1798bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                .equals(printableVersion)) {
1799bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            startPos = bodyLength - printableLength;
1800bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        } else if (bodyLength >= signatureLength
1801bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && bodyText.substring(bodyLength - signatureLength)
1802bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                .equals(signature)) {
1803bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            startPos = bodyLength - signatureLength;
1804bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        }
1805bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        return startPos;
180682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
180782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
180882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
180982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Allows any changes made by the user to be ignored. Called when the user
181082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * decides to discard a draft.
181182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
181282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private void discardChanges() {
181382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mTextChanged = false;
1814eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsChanged = false;
181582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mReplyFromChanged = false;
181633fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    }
181733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira
181882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
1819181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @param body
1820181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @param save
1821181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @param showToast
1822181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return Whether the send or save succeeded.
1823181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1824181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    protected boolean sendOrSaveWithSanityChecks(final boolean save, final boolean showToast,
1825181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            final boolean orientationChanged) {
1826181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        String[] to, cc, bcc;
1827181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        Editable body = mBodyView.getEditableText();
1828181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (orientationChanged) {
1829181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = cc = bcc = new String[0];
1830181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        } else {
1831181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = getToAddresses();
1832181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            cc = getCcAddresses();
1833181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            bcc = getBccAddresses();
1834181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1835181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1836181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Don't let the user send to nobody (but it's okay to save a message
1837181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // with no recipients)
1838181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save && (to.length == 0 && cc.length == 0 && bcc.length == 0)) {
1839181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            showRecipientErrorDialog(getString(R.string.recipient_needed));
1840181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            return false;
1841181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1842181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1843181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        List<String> wrongEmails = new ArrayList<String>();
1844181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save) {
1845181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            checkInvalidEmails(to, wrongEmails);
1846181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            checkInvalidEmails(cc, wrongEmails);
1847181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            checkInvalidEmails(bcc, wrongEmails);
1848181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1849181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1850181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Don't let the user send an email with invalid recipients
1851181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (wrongEmails.size() > 0) {
1852181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            String errorText = String.format(getString(R.string.invalid_recipient),
1853181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    wrongEmails.get(0));
1854181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            showRecipientErrorDialog(errorText);
1855181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            return false;
1856181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1857181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1858181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
18590bbc858506c95005f2840fa5dff495a9216d2665Marc Blank            @Override
1860181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            public void onClick(DialogInterface dialog, int which) {
1861181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                sendOrSave(mBodyView.getEditableText(), save, showToast, orientationChanged);
1862181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1863181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        };
1864181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1865181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Show a warning before sending only if there are no attachments.
1866181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save) {
1867181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (mAttachmentsView.getAttachments().isEmpty() && showEmptyTextWarnings()) {
1868181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                boolean warnAboutEmptySubject = isSubjectEmpty();
1869181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                boolean emptyBody = TextUtils.getTrimmedLength(body) == 0;
1870181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1871181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // A warning about an empty body may not be warranted when
1872181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // forwarding mails, since a common use case is to forward
1873181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // quoted text and not append any more text.
1874181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                boolean warnAboutEmptyBody = emptyBody && (!mForward || isBodyEmpty());
1875181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1876181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // When we bring up a dialog warning the user about a send,
1877181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // assume that they accept sending the message. If they do not,
1878181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // the dialog listener is required to enable sending again.
1879181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (warnAboutEmptySubject) {
1880181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    showSendConfirmDialog(R.string.confirm_send_message_with_no_subject, listener);
1881181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    return true;
1882181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1883181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1884181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (warnAboutEmptyBody) {
1885181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    showSendConfirmDialog(R.string.confirm_send_message_with_no_body, listener);
1886181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    return true;
1887181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1888181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1889181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // Ask for confirmation to send (if always required)
1890181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (showSendConfirmation()) {
1891181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                showSendConfirmDialog(R.string.confirm_send_message, listener);
1892181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                return true;
1893181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1894181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1895181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1896181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        sendOrSave(body, save, showToast, false);
1897181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return true;
1898181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1899181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1900181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1901181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Returns a boolean indicating whether warnings should be shown for empty
1902181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * subject and body fields
19035c5fd571b40a15b95ea5a5d6033f377706ffd0fbAndy Huang     *
1904181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return True if a warning should be shown for empty text fields
1905181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1906181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    protected boolean showEmptyTextWarnings() {
1907181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return mAttachmentsView.getAttachments().size() == 0;
1908181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1909181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1910181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1911181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Returns a boolean indicating whether the user should confirm each send
1912181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     *
1913181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return True if a warning should be on each send
1914181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1915181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    protected boolean showSendConfirmation() {
1916181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return mCachedSettings != null ? mCachedSettings.confirmSend : false;
1917181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1918181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1919181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private void showSendConfirmDialog(int messageId, DialogInterface.OnClickListener listener) {
1920181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (mSendConfirmDialog != null) {
1921181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mSendConfirmDialog.dismiss();
1922181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mSendConfirmDialog = null;
1923181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1924181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        mSendConfirmDialog = new AlertDialog.Builder(this).setMessage(messageId)
1925181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setTitle(R.string.confirm_send_title)
1926181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setIconAttribute(android.R.attr.alertDialogIcon)
1927181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setPositiveButton(R.string.send, listener)
19286edd597b1a6628660284f03442cce1f193af66dcMindy Pereira                .setNegativeButton(R.string.cancel, this)
19296edd597b1a6628660284f03442cce1f193af66dcMindy Pereira                .show();
1930181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1931181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1932181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1933181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Returns whether the ComposeArea believes there is any text in the body of
1934181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * the composition. TODO: When ComposeArea controls the Body as well, add
1935181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * that here.
1936181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1937181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public boolean isBodyEmpty() {
1938181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return !mQuotedTextView.isTextIncluded();
1939181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1940181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1941181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1942181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Test to see if the subject is empty.
1943181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     *
1944181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return boolean.
1945181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1946181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    // TODO: this will likely go away when composeArea.focus() is implemented
1947181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    // after all the widget control is moved over.
1948181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public boolean isSubjectEmpty() {
1949181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return TextUtils.getTrimmedLength(mSubject.getText()) == 0;
1950181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1951181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1952181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /* package */
1953e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira    static int sendOrSaveInternal(Context context, ReplyFromAccount replyFromAccount,
195405b92b8ff275f85edae525623c71b65805ca3a51Paul Westbrook            Message message, final Message refMessage, Spanned body, final CharSequence quotedText,
195505b92b8ff275f85edae525623c71b65805ca3a51Paul Westbrook            SendOrSaveCallback callback, Handler handler, boolean save, int composeMode) {
195629ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        ContentValues values = new ContentValues();
195729ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira
1958c203197908a95f707e93621e5eb5f0fb8bfc7d1fMindy Pereira        String refMessageId = refMessage != null ? refMessage.uri.toString() : "";
1959c203197908a95f707e93621e5eb5f0fb8bfc7d1fMindy Pereira
1960e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        MessageModification.putToAddresses(values, message.getToAddresses());
1961e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        MessageModification.putCcAddresses(values, message.getCcAddresses());
1962e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        MessageModification.putBccAddresses(values, message.getBccAddresses());
196329ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira
1964e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        MessageModification.putCustomFromAddress(values, message.from);
196592551d057965689f1952faeb06763f0762bc717fMindy Pereira
1966e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        MessageModification.putSubject(values, message.subject);
196729ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        String htmlBody = Html.toHtml(body);
196805b92b8ff275f85edae525623c71b65805ca3a51Paul Westbrook
196929ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        boolean includeQuotedText = !TextUtils.isEmpty(quotedText);
197029ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        StringBuilder fullBody = new StringBuilder(htmlBody);
197129ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        if (includeQuotedText) {
1972e8caf1279a1c6ae720215405f678172a5cbf68d7Mindy Pereira            // HTML gets converted to text for now
1973e8caf1279a1c6ae720215405f678172a5cbf68d7Mindy Pereira            final String text = quotedText.toString();
1974e8caf1279a1c6ae720215405f678172a5cbf68d7Mindy Pereira            if (QuotedTextView.containsQuotedText(text)) {
1975e8caf1279a1c6ae720215405f678172a5cbf68d7Mindy Pereira                int pos = QuotedTextView.getQuotedTextOffset(text);
197655271cf624269a5a19bb1989e5904df7adec7ef7Paul Westbrook                final int quoteStartPos = fullBody.length() + pos;
197755271cf624269a5a19bb1989e5904df7adec7ef7Paul Westbrook                fullBody.append(text);
197855271cf624269a5a19bb1989e5904df7adec7ef7Paul Westbrook                MessageModification.putQuoteStartPos(values, quoteStartPos);
197912575866e5526e45eaf773c6c98c93c3a06fe156Mindy Pereira                MessageModification.putForward(values, composeMode == ComposeActivity.FORWARD);
1980e8caf1279a1c6ae720215405f678172a5cbf68d7Mindy Pereira                MessageModification.putAppendRefMessageContent(values, includeQuotedText);
198129ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            } else {
1982e8caf1279a1c6ae720215405f678172a5cbf68d7Mindy Pereira                LogUtils.w(LOG_TAG, "Couldn't find quoted text");
1983e8caf1279a1c6ae720215405f678172a5cbf68d7Mindy Pereira                // This shouldn't happen, but just use what we have,
1984e8caf1279a1c6ae720215405f678172a5cbf68d7Mindy Pereira                // and don't do server-side expansion
1985e8caf1279a1c6ae720215405f678172a5cbf68d7Mindy Pereira                fullBody.append(text);
198629ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            }
198729ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        }
1988002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira        int draftType = getDraftType(composeMode);
198912575866e5526e45eaf773c6c98c93c3a06fe156Mindy Pereira        MessageModification.putDraftType(values, draftType);
1990c6f1e2a8690bd3d4722a2e9386cdc98691135063Mindy Pereira        if (refMessage != null) {
1991c6f1e2a8690bd3d4722a2e9386cdc98691135063Mindy Pereira            if (!TextUtils.isEmpty(refMessage.bodyHtml)) {
1992c6f1e2a8690bd3d4722a2e9386cdc98691135063Mindy Pereira                MessageModification.putBodyHtml(values, fullBody.toString());
1993c6f1e2a8690bd3d4722a2e9386cdc98691135063Mindy Pereira            }
1994c6f1e2a8690bd3d4722a2e9386cdc98691135063Mindy Pereira            if (!TextUtils.isEmpty(refMessage.bodyText)) {
1995c6f1e2a8690bd3d4722a2e9386cdc98691135063Mindy Pereira                MessageModification.putBody(values, Html.fromHtml(fullBody.toString()).toString());
1996c6f1e2a8690bd3d4722a2e9386cdc98691135063Mindy Pereira            }
1997c6f1e2a8690bd3d4722a2e9386cdc98691135063Mindy Pereira        } else {
1998c203197908a95f707e93621e5eb5f0fb8bfc7d1fMindy Pereira            MessageModification.putBodyHtml(values, fullBody.toString());
1999c203197908a95f707e93621e5eb5f0fb8bfc7d1fMindy Pereira            MessageModification.putBody(values, Html.fromHtml(fullBody.toString()).toString());
2000c203197908a95f707e93621e5eb5f0fb8bfc7d1fMindy Pereira        }
2001e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        MessageModification.putAttachments(values, message.getAttachments());
200212575866e5526e45eaf773c6c98c93c3a06fe156Mindy Pereira        if (!TextUtils.isEmpty(refMessageId)) {
200312575866e5526e45eaf773c6c98c93c3a06fe156Mindy Pereira            MessageModification.putRefMessageId(values, refMessageId);
200412575866e5526e45eaf773c6c98c93c3a06fe156Mindy Pereira        }
200582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
200692551d057965689f1952faeb06763f0762bc717fMindy Pereira        SendOrSaveMessage sendOrSaveMessage = new SendOrSaveMessage(replyFromAccount,
2007181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                values, refMessageId, save);
2008181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        SendOrSaveTask sendOrSaveTask = new SendOrSaveTask(context, sendOrSaveMessage, callback);
200982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
2010181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        callback.initializeSendOrSave(sendOrSaveTask);
201182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
2012181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Do the send/save action on the specified handler to avoid possible
2013181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // ANRs
2014181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        handler.post(sendOrSaveTask);
201582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
2016181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return sendOrSaveMessage.requestId();
2017181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
201882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
2019002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira    private static int getDraftType(int mode) {
2020002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira        int draftType = -1;
2021002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira        switch (mode) {
2022002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            case ComposeActivity.COMPOSE:
2023002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                draftType = DraftType.COMPOSE;
2024002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                break;
2025002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            case ComposeActivity.REPLY:
2026002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                draftType = DraftType.REPLY;
2027002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                break;
2028002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            case ComposeActivity.REPLY_ALL:
2029002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                draftType = DraftType.REPLY_ALL;
2030002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                break;
2031002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira            case ComposeActivity.FORWARD:
2032002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                draftType = DraftType.FORWARD;
2033002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira                break;
2034002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira        }
2035002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira        return draftType;
2036002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira    }
2037002ff5244800c32f2d70c96cc1110657020ee65cMindy Pereira
2038181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private void sendOrSave(Spanned body, boolean save, boolean showToast,
2039181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            boolean orientationChanged) {
2040181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Check if user is a monkey. Monkeys can compose and hit send
2041181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // button but are not allowed to send anything off the device.
20423ae824c79ffba25b0cda8b0300f41a28cfbfca7fPaul Westbrook        if (ActivityManager.isUserAMonkey()) {
2043181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            return;
2044181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
204582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
2046181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        String[] to, cc, bcc;
2047181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (orientationChanged) {
2048181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = cc = bcc = new String[0];
2049181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        } else {
2050181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = getToAddresses();
2051181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            cc = getCcAddresses();
2052181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            bcc = getBccAddresses();
2053181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
205482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
2055181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        SendOrSaveCallback callback = new SendOrSaveCallback() {
205682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            private int mRestoredRequestId;
205782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
20580bbc858506c95005f2840fa5dff495a9216d2665Marc Blank            @Override
205982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask) {
2060181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                synchronized (mActiveTasks) {
2061181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    int numTasks = mActiveTasks.size();
2062181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    if (numTasks == 0) {
2063181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        // Start service so we won't be killed if this app is
2064181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        // put in the background.
2065181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        startService(new Intent(ComposeActivity.this, EmptyService.class));
2066181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    }
2067181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
2068181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    mActiveTasks.add(sendOrSaveTask);
2069181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
2070181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (sTestSendOrSaveCallback != null) {
2071181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    sTestSendOrSaveCallback.initializeSendOrSave(sendOrSaveTask);
2072181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
2073181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
207482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
20750bbc858506c95005f2840fa5dff495a9216d2665Marc Blank            @Override
20767ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            public void notifyMessageIdAllocated(SendOrSaveMessage sendOrSaveMessage,
20777ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    Message message) {
2078181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                synchronized (mDraftLock) {
2079181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    mDraftId = message.id;
2080181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    mDraft = message;
20817ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    if (sRequestMessageIdMap != null) {
20827ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        sRequestMessageIdMap.put(sendOrSaveMessage.requestId(), mDraftId);
20837ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    }
2084181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    // Cache request message map, in case the process is killed
2085181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    saveRequestMap();
2086181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
2087181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (sTestSendOrSaveCallback != null) {
20887ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    sTestSendOrSaveCallback.notifyMessageIdAllocated(sendOrSaveMessage, message);
2089181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
2090181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
209182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
20920bbc858506c95005f2840fa5dff495a9216d2665Marc Blank            @Override
20937ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            public Message getMessage() {
20947ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                synchronized (mDraftLock) {
20957ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    return mDraft;
20967ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
20977ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            }
209882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
20990bbc858506c95005f2840fa5dff495a9216d2665Marc Blank            @Override
21007ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            public void sendOrSaveFinished(SendOrSaveTask task, boolean success) {
21017ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (success) {
21027ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // Successfully sent or saved so reset change markers
21037ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    discardChanges();
21047ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                } else {
21057ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // A failure happened with saving/sending the draft
21067ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // TODO(pwestbro): add a better string that should be used
21077ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // when failing to send or save
21087ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    Toast.makeText(ComposeActivity.this, R.string.send_failed, Toast.LENGTH_SHORT)
21097ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                            .show();
21107ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
211182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
21127ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                int numTasks;
21137ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                synchronized (mActiveTasks) {
21147ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // Remove the task from the list of active tasks
21157ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    mActiveTasks.remove(task);
21167ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    numTasks = mActiveTasks.size();
21177ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
211882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
21197ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (numTasks == 0) {
21207ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // Stop service so we can be killed.
21217ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    stopService(new Intent(ComposeActivity.this, EmptyService.class));
21227ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
21237ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (sTestSendOrSaveCallback != null) {
21247ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    sTestSendOrSaveCallback.sendOrSaveFinished(task, success);
21257ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
21267ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            }
2127181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        };
2128181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
2129181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Get the selected account if the from spinner has been setup.
213092551d057965689f1952faeb06763f0762bc717fMindy Pereira        ReplyFromAccount selectedAccount = mReplyFromAccount;
2131181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        String fromAddress = selectedAccount.name;
2132181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (selectedAccount == null || fromAddress == null) {
2133181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // We don't have either the selected account or from address,
2134181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // use mAccount.
213592551d057965689f1952faeb06763f0762bc717fMindy Pereira            selectedAccount = mReplyFromAccount;
2136181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            fromAddress = mAccount.name;
2137181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
2138181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
2139181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (mSendSaveTaskHandler == null) {
2140181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            HandlerThread handlerThread = new HandlerThread("Send Message Task Thread");
2141181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            handlerThread.start();
2142181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
2143181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mSendSaveTaskHandler = new Handler(handlerThread.getLooper());
2144181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
2145181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
2146e8f94dc92567d8efa691860c38720d3ae753f429Mindy Pereira        Message msg = createMessage(mReplyFromAccount, getMode());
214705b92b8ff275f85edae525623c71b65805ca3a51Paul Westbrook        mRequestId = sendOrSaveInternal(this, mReplyFromAccount, msg, mRefMessage, body,
214805b92b8ff275f85edae525623c71b65805ca3a51Paul Westbrook                mQuotedTextView.getQuotedTextIfIncluded(), callback,
214912575866e5526e45eaf773c6c98c93c3a06fe156Mindy Pereira                mSendSaveTaskHandler, save, mComposeMode);
2150181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
2151181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (mRecipient != null && mRecipient.equals(mAccount.name)) {
2152181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mRecipient = selectedAccount.name;
2153181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
2154b1f573c59ec20f9149443e1e0cc8b64ff3a07feaPaul Westbrook        setAccount(selectedAccount.account);
2155181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
2156181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Don't display the toast if the user is just changing the orientation,
2157181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // but we still need to save the draft to the cursor because this is how we restore
2158181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // the attachments when the configuration change completes.
2159181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (showToast && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) {
2160181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            Toast.makeText(this, save ? R.string.message_saved : R.string.sending_message,
2161181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    Toast.LENGTH_LONG).show();
2162181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
2163181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
2164181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Need to update variables here because the send or save completes
2165181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // asynchronously even though the toast shows right away.
2166181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        discardChanges();
2167181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        updateSaveUi();
2168181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
2169181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // If we are sending, finish the activity
2170181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save) {
2171181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            finish();
2172181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
2173181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
2174181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
2175181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
2176181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Save the state of the request messageid map. This allows for the Gmail
2177181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * process to be killed, but and still allow for ComposeActivity instances
2178181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * to be recreated correctly.
2179181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
2180181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private void saveRequestMap() {
2181181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // TODO: store the request map in user preferences.
2182181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
218382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
21848f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein    public void openAttachmentTypeSelectionDialog() {
21858f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein        AlertDialog.Builder builder = new AlertDialog.Builder(this);
21868f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein        builder.setTitle(R.string.add_file_attachment);
21878f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein        builder.setAdapter(new AttachmentTypeSelectorAdapter(this),
21888f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein                new DialogInterface.OnClickListener() {
21898f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein            public void onClick(DialogInterface dialog, int position) {
21908f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein                doAttach(position);
21918f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein            }
21928f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein        });
21938f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein        builder.show();
21948f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein    }
21958f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein
21968f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein    private void doAttach(int position) {
2197013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
2198013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        i.addCategory(Intent.CATEGORY_OPENABLE);
2199d6a9a3f94c69d05f23349a09d71cd36c6fb03613Paul Westbrook        i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
22008f1c01e908d5986e8222dfd45ab82d6895b1ff3bAndrew Sapperstein        i.setType(AttachmentTypeSelectorAdapter.ITEMS.get(position).mMimeType);
2201013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        mAddingAttachment = true;
2202181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        startActivityForResult(Intent.createChooser(i, getText(R.string.select_attachment_type)),
2203181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                RESULT_PICK_ATTACHMENT);
2204013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    }
2205013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira
2206ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    private void showCcBccViews() {
2207a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        mCcBccView.show(true, true, true);
2208ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (mCcBccButton != null) {
2209ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            mCcBccButton.setVisibility(View.GONE);
2210ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
2211ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    }
2212ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira
2213326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    @Override
2214326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    public boolean onNavigationItemSelected(int position, long itemId) {
2215a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        int initialComposeMode = mComposeMode;
2216326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        if (position == ComposeActivity.REPLY) {
2217326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.REPLY;
2218326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else if (position == ComposeActivity.REPLY_ALL) {
2219326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.REPLY_ALL;
2220326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else if (position == ComposeActivity.FORWARD) {
2221326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.FORWARD;
2222326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
2223cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        clearChangeListeners();
2224a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (initialComposeMode != mComposeMode) {
2225154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            resetMessageForModeChange();
2226ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira            if (mDraft == null && mRefMessage != null) {
22278eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira                initFromRefMessage(mComposeMode, mAccount.name);
22288eca57a78ae01d9c8f55226734131aac1ff3c8aeMindy Pereira            }
2229ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira            boolean showCc = false;
2230ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira            boolean showBcc = false;
2231ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira            if (mDraft != null) {
2232ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                // Following desktop behavior, if the user has added a BCC
2233ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                // field to a draft, we show it regardless of compose mode.
2234ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                showBcc = !TextUtils.isEmpty(mDraft.bcc);
2235ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                // Use the draft to determine what to populate.
2236ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                // If the Bcc field is showing, show the Cc field whether it is populated or not.
2237ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                showCc = showBcc || (!TextUtils.isEmpty(mDraft.cc) && mComposeMode == REPLY_ALL);
2238ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira            } else if (mRefMessage != null) {
2239ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira                showCc = mComposeMode == REPLY_ALL && !TextUtils.isEmpty(mRefMessage.cc);
2240ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira            }
2241ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira            mCcBccView.show(false, showCc, showBcc);
2242a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        }
2243ef38830943dac17adb4cade670b5ae0c6b4442c6Mindy Pereira        updateHideOrShowCcBcc();
2244cbfb75a0f92a3ef5cf10ab94b451e4898cc78bfcMindy Pereira        initChangeListeners();
2245326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        return true;
2246326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
2247326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
2248b3112a2bf7b805695259e412b0e2bc0e0e73e9d6Mindy Pereira    @VisibleForTesting
2249b3112a2bf7b805695259e412b0e2bc0e0e73e9d6Mindy Pereira    protected void resetMessageForModeChange() {
2250154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // When switching between reply, reply all, forward,
2251154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // follow the behavior of webview.
2252154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // The contents of the following fields are cleared
2253154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // so that they can be populated directly from the
2254154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // ref message:
2255154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 1) Any recipient fields
2256154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 2) The subject
2257154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mTo.setText("");
2258154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mCc.setText("");
2259154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mBcc.setText("");
2260154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // Any edits to the subject are replaced with the original subject.
2261154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mSubject.setText("");
2262154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira
2263154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // Any changes to the contents of the following fields are kept:
2264154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 1) Body
2265154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 2) Attachments
2266154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // If the user made changes to attachments, keep their changes.
2267154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        if (!mAttachmentsChanged) {
2268154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            mAttachmentsView.deleteAllAttachments();
2269154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        }
2270154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira    }
2271154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira
2272326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private class ComposeModeAdapter extends ArrayAdapter<String> {
2273326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
2274326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        private LayoutInflater mInflater;
2275326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
2276326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        public ComposeModeAdapter(Context context) {
2277326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            super(context, R.layout.compose_mode_item, R.id.mode, getResources()
2278326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    .getStringArray(R.array.compose_modes));
2279326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
2280326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
2281326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        private LayoutInflater getInflater() {
2282326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (mInflater == null) {
2283326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                mInflater = LayoutInflater.from(getContext());
2284326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
2285326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            return mInflater;
2286326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
2287326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
2288326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        @Override
2289326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        public View getView(int position, View convertView, ViewGroup parent) {
2290326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (convertView == null) {
2291326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                convertView = getInflater().inflate(R.layout.compose_mode_display_item, null);
2292326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
2293326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            ((TextView) convertView.findViewById(R.id.mode)).setText(getItem(position));
2294326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            return super.getView(position, convertView, parent);
2295326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
2296326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
229746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
229846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    @Override
229946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void onRespondInline(String text) {
230046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        appendToBody(text, false);
230146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
230246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
230346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
230446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Append text to the body of the message. If there is no existing body
230546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * text, just sets the body to text.
230646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *
230746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param text
230846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param withSignature True to append a signature.
230946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
231046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void appendToBody(CharSequence text, boolean withSignature) {
2311eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        Editable bodyText = mBodyView.getEditableText();
231246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (bodyText != null && bodyText.length() > 0) {
231346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            bodyText.append(text);
231446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
231546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            setBody(text, withSignature);
231646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
231746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
231846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
231946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
232046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Set the body of the message.
2321bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira     *
232246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param text
232346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param withSignature True to append a signature.
232446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
232546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void setBody(CharSequence text, boolean withSignature) {
2326eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBodyView.setText(text);
2327bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        if (withSignature) {
2328bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            appendSignature();
2329bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        }
2330bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    }
2331bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
2332bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    private void appendSignature() {
2333b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira        String newSignature = mCachedSettings != null ? mCachedSettings.signature : null;
2334433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira        boolean hasFocus = mBodyView.hasFocus();
2335b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira        if (!TextUtils.equals(newSignature, mSignature)) {
2336b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira            mSignature = newSignature;
2337b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira            if (!TextUtils.isEmpty(mSignature)
2338b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira                    && getSignatureStartPosition(mSignature,
2339b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira                            mBodyView.getText().toString()) < 0) {
2340b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira                // Appending a signature does not count as changing text.
2341b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira                mBodyView.removeTextChangedListener(this);
2342b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira                mBodyView.append(convertToPrintableSignature(mSignature));
2343b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira                mBodyView.addTextChangedListener(this);
2344b13917c3d753c6674db2a226668a7f3048ddc4b5Mindy Pereira            }
2345433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            if (hasFocus) {
2346433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira                focusBody();
2347433b198e1a9385d1ca1284db0a168734768e0455Mindy Pereira            }
2348bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        }
2349bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    }
2350bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
2351bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    private String convertToPrintableSignature(String signature) {
2352bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        String signatureResource = getResources().getString(R.string.signature);
2353bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        if (signature == null) {
2354bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            signature = "";
2355bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        }
2356bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        return String.format(signatureResource, signature);
235746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
23581a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
23595a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    @Override
23605a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    public void onAccountChanged() {
236192551d057965689f1952faeb06763f0762bc717fMindy Pereira        mReplyFromAccount = mFromSpinner.getCurrentAccount();
236292551d057965689f1952faeb06763f0762bc717fMindy Pereira        if (!mAccount.equals(mReplyFromAccount.account)) {
2363b1f573c59ec20f9149443e1e0cc8b64ff3a07feaPaul Westbrook            setAccount(mReplyFromAccount.account);
2364b1f573c59ec20f9149443e1e0cc8b64ff3a07feaPaul Westbrook
2365181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // TODO: handle discarding attachments when switching accounts.
2366181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // Only enable save for this draft if there is any other content
2367181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // in the message.
2368181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (!isBlank()) {
2369181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                enableSave(true);
2370181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
2371181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mReplyFromChanged = true;
2372181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            initRecipients();
237382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
23741a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
237582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
237682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void enableSave(boolean enabled) {
237782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSave != null) {
237882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave.setEnabled(enabled);
237982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
238082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
238182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
238282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void enableSend(boolean enabled) {
238382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSend != null) {
238482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSend.setEnabled(enabled);
238582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
238682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
238782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
238882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
238982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Handles button clicks from any error dialogs dealing with sending
239082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * a message.
239182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
239282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    @Override
239382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void onClick(DialogInterface dialog, int which) {
239482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        switch (which) {
239582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            case DialogInterface.BUTTON_POSITIVE: {
239682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                doDiscardWithoutConfirmation(true /* show toast */ );
239782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                break;
239882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
239982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            case DialogInterface.BUTTON_NEGATIVE: {
240082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // If the user cancels the send, re-enable the send button.
240182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                enableSend(true);
240282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                break;
240382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
240482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
240582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
240682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
240782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
2408efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira    private void doDiscard() {
2409efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira        new AlertDialog.Builder(this).setMessage(R.string.confirm_discard_text)
2410efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira                .setPositiveButton(R.string.ok, this)
2411efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira                .setNegativeButton(R.string.cancel, null)
2412efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira                .create().show();
2413efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira    }
241482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
241582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Effectively discard the current message.
241682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     *
241782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * This method is either invoked from the menu or from the dialog
241882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * once the user has confirmed that they want to discard the message.
241982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param showToast show "Message discarded" toast if true
242082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
242182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private void doDiscardWithoutConfirmation(boolean showToast) {
24227ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        synchronized (mDraftLock) {
242382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (mDraftId != UIProvider.INVALID_MESSAGE_ID) {
242482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                ContentValues values = new ContentValues();
2425b7050e62aab7133dea300d7fb3266311da99ec80Paul Westbrook                values.put(BaseColumns._ID, mDraftId);
2426cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                if (mAccount.expungeMessageUri != null) {
2427cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    getContentResolver().update(mAccount.expungeMessageUri, values, null, null);
2428cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                } else {
24290bbc858506c95005f2840fa5dff495a9216d2665Marc Blank                    getContentResolver().delete(mDraft.uri, null, null);
2430cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                }
243182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // This is not strictly necessary (since we should not try to
243282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // save the draft after calling this) but it ensures that if we
243382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // do save again for some reason we make a new draft rather than
243482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // trying to resave an expunged draft.
243582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mDraftId = UIProvider.INVALID_MESSAGE_ID;
243682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
243782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
243882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
243982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (showToast) {
244082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // Display a toast to let the user know
244182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            Toast.makeText(this, R.string.message_discarded, Toast.LENGTH_SHORT).show();
244282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
244382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
244482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // This prevents the draft from being saved in onPause().
244582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        discardChanges();
244682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        finish();
244782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
244882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
2449eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void saveIfNeeded() {
2450eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mAccount == null) {
2451eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // We have not chosen an account yet so there's no way that we can save. This is ok,
2452eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // though, since we are saving our state before AccountsActivity is activated. Thus, the
2453eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // user has not interacted with us yet and there is no real state to save.
2454eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return;
2455eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
2456eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
2457eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (shouldSave()) {
245848e31b07b5ac7c83dc3a009242fa915afccddb9eMindy Pereira            doSave(!mAddingAttachment /* show toast */);
2459eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
2460eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
2461eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
2462eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
2463eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void onAttachmentDeleted() {
2464eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsChanged = true;
2465eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        updateSaveUi();
2466eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
246775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
246875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
246975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    /**
247075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira     * This is called any time one of our text fields changes.
247175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira     */
24720bbc858506c95005f2840fa5dff495a9216d2665Marc Blank    @Override
247375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void afterTextChanged(Editable s) {
247475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mTextChanged = true;
247575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        updateSaveUi();
247675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
247775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
247875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    @Override
247975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
248075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        // Do nothing.
248175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
248275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
24830bbc858506c95005f2840fa5dff495a9216d2665Marc Blank    @Override
248475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void onTextChanged(CharSequence s, int start, int before, int count) {
248575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        // Do nothing.
248675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
248775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
248875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
248975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // There is a big difference between the text associated with an address changing
249075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // to add the display name or to format properly and a recipient being added or deleted.
249175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // Make sure we only notify of changes when a recipient has been added or deleted.
249275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    private class RecipientTextWatcher implements TextWatcher {
249375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private HashMap<String, Integer> mContent = new HashMap<String, Integer>();
249475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
249575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private RecipientEditTextView mView;
249675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
249775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private TextWatcher mListener;
249875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
249975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public RecipientTextWatcher(RecipientEditTextView view, TextWatcher listener) {
250075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mView = view;
250175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mListener = listener;
250275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
250375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
250475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
250575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void afterTextChanged(Editable s) {
250675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            if (hasChanged()) {
250775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                mListener.afterTextChanged(s);
250875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
250975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
251075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
251175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private boolean hasChanged() {
251275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] currRecips = tokenizeRecips(getAddressesFromList(mView));
251375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            int totalCount = currRecips.length;
251475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            int totalPrevCount = 0;
251575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (Entry<String, Integer> entry : mContent.entrySet()) {
251675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                totalPrevCount += entry.getValue();
251775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
251875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            if (totalCount != totalPrevCount) {
251975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                return true;
252075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
252175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
252275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (String recip : currRecips) {
252375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                if (!mContent.containsKey(recip)) {
252475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    return true;
252575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                } else {
252675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    int count = mContent.get(recip) - 1;
252775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    if (count < 0) {
252875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                        return true;
252975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    } else {
253075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                        mContent.put(recip, count);
253175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    }
253275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                }
253375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
253475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            return false;
253575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
253675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
253775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private String[] tokenizeRecips(String[] recips) {
253875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            // Tokenize them all and put them in the list.
253975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] recipAddresses = new String[recips.length];
254075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (int i = 0; i < recips.length; i++) {
254175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                recipAddresses[i] = Rfc822Tokenizer.tokenize(recips[i])[0].getAddress();
254275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
254375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            return recipAddresses;
254475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
254575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
254675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
254775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
254875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] recips = tokenizeRecips(getAddressesFromList(mView));
254975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (String recip : recips) {
255075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                if (!mContent.containsKey(recip)) {
255175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    mContent.put(recip, 1);
255275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                } else {
255375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    mContent.put(recip, (mContent.get(recip)) + 1);
255475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                }
255575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
255675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
255775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
255875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
255975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void onTextChanged(CharSequence s, int start, int before, int count) {
256075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            // Do nothing.
256175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
256275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
2563e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira
2564e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    public static void registerTestSendOrSaveCallback(SendOrSaveCallback testCallback) {
2565e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        if (sTestSendOrSaveCallback != null && testCallback != null) {
2566e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira            throw new IllegalStateException("Attempting to register more than one test callback");
2567e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        }
2568e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira        sTestSendOrSaveCallback = testCallback;
2569e011b1d1c8e2e674dfdf1985d9d13da8cc06d803Mindy Pereira    }
2570bddd6f39f34aa15fed2e757f7d3e4163272ca02cMindy Pereira
2571bddd6f39f34aa15fed2e757f7d3e4163272ca02cMindy Pereira    @VisibleForTesting
2572bddd6f39f34aa15fed2e757f7d3e4163272ca02cMindy Pereira    protected ArrayList<Attachment> getAttachments() {
2573bddd6f39f34aa15fed2e757f7d3e4163272ca02cMindy Pereira        return mAttachmentsView.getAttachments();
2574bddd6f39f34aa15fed2e757f7d3e4163272ca02cMindy Pereira    }
25758183d4519ac4b576c0af41f787267db6eff2efb1Vikram Aggarwal}
2576