ComposeActivity.java revision 181df7851bd1eba4b6a3255073a8b731de23ae8f
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;
2082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.app.ActivityManager;
2182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.app.AlertDialog;
2282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.app.Dialog;
23326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.app.ActionBar.OnNavigationListener;
248e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.app.Activity;
25181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereiraimport android.app.LoaderManager.LoaderCallbacks;
266349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.content.ContentResolver;
2782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.content.ContentValues;
286349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.content.Context;
29181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereiraimport android.content.CursorLoader;
3082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.content.DialogInterface;
316349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.content.Intent;
32181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereiraimport android.content.Loader;
3382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.content.pm.ActivityInfo;
347ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereiraimport android.database.Cursor;
356349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.net.Uri;
368e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.os.Bundle;
3782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.os.Handler;
3882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.os.HandlerThread;
3982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.provider.BaseColumns;
4046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport android.text.Editable;
4182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.Html;
4282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.Spanned;
43c182762df95fd382faf8e4a25e5cf7783ea10176Paul Westbrookimport android.text.TextUtils;
4482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.TextWatcher;
4546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport android.text.util.Rfc822Token;
46c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport android.text.util.Rfc822Tokenizer;
47326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.LayoutInflater;
48b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.Menu;
49b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.MenuInflater;
50b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.MenuItem;
518e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.view.View;
52326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.ViewGroup;
538e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.view.View.OnClickListener;
54eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereiraimport android.view.inputmethod.BaseInputConnection;
55326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.widget.ArrayAdapter;
568e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.widget.Button;
576349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.widget.TextView;
58013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereiraimport android.widget.Toast;
597b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira
60c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport com.android.common.Rfc822Validator;
61eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereiraimport com.android.mail.compose.AttachmentsView.AttachmentDeletedListener;
629932dee248718d2cec993d93b5115973d1389d0aMindy Pereiraimport com.android.mail.compose.AttachmentsView.AttachmentFailureException;
635a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereiraimport com.android.mail.compose.FromAddressSpinner.OnAccountChangedListener;
6430e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.compose.QuotedTextView.RespondInlineListener;
6533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereiraimport com.android.mail.providers.Account;
6630e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.Address;
6730e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.Attachment;
683ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereiraimport com.android.mail.providers.Message;
6982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.android.mail.providers.MessageModification;
70181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereiraimport com.android.mail.providers.Settings;
7130e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.UIProvider;
7282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.android.mail.providers.UIProvider.MessageColumns;
7330e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.R;
7430e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.utils.LogUtils;
7530e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.utils.Utils;
76c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport com.android.ex.chips.RecipientEditTextView;
77181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereiraimport com.google.android.gm.persistence.Persistence;
7846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport com.google.common.annotations.VisibleForTesting;
7982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.google.common.collect.Lists;
804a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereiraimport com.google.common.collect.Sets;
818e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
8246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.ArrayList;
8346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.Collection;
8475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereiraimport java.util.HashMap;
8546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.HashSet;
8646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.List;
874a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereiraimport java.util.Set;
8875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereiraimport java.util.Map.Entry;
8982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport java.util.concurrent.ConcurrentHashMap;
9046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
9146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereirapublic class ComposeActivity extends Activity implements OnClickListener, OnNavigationListener,
925a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        RespondInlineListener, DialogInterface.OnClickListener, TextWatcher,
93181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        AttachmentDeletedListener, OnAccountChangedListener, LoaderCallbacks<Cursor> {
946349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    // Identifiers for which type of composition this is
956349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int COMPOSE = -1;  // also used for editing a draft
966349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int REPLY = 0;
976349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int REPLY_ALL = 1;
986349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int FORWARD = 2;
996349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1006349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    // Integer extra holding one of the above compose action
1016349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private static final String EXTRA_ACTION = "action";
1026349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
10382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static SendOrSaveCallback sTestSendOrSaveCallback = null;
10482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Map containing information about requests to create new messages, and the id of the
10582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // messages that were the result of those requests.
10682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    //
10782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // This map is used when the activity that initiated the save a of a new message, is killed
10882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // before the save has completed (and when we know the id of the newly created message).  When
10982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // a save is completed, the service that is running in the background, will update the map
11082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    //
11182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // When a new ComposeActivity instance is created, it will attempt to use the information in
11282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // the previously instantiated map.  If ComposeActivity.onCreate() is called, with a bundle
11382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // (restoring data from a previous instance), and the map hasn't been created, we will attempt
11482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // to populate the map with data stored in shared preferences.
11582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static ConcurrentHashMap<Integer, Long> sRequestMessageIdMap = null;
11682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Key used to store the above map
11782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static final String CACHED_MESSAGE_REQUEST_IDS_KEY = "cache-message-request-ids";
1186349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1196349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Notifies the {@code Activity} that the caller is an Email
1206349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * {@code Activity}, so that the back behavior may be modified accordingly.
1216349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     *
1226349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * @see #onAppUpPressed
1236349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
1246349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private static final String EXTRA_FROM_EMAIL_TASK = "fromemail";
1256349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1263ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    //  If this is a reply/forward then this extra will hold the original message
1273ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private static final String EXTRA_IN_REFERENCE_TO_MESSAGE = "in-reference-to-message";
1284a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private static final String END_TOKEN = ", ";
129013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final String LOG_TAG = new LogUtils().getLogTag();
130013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    // Request numbers for activities we start
131013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final int RESULT_PICK_ATTACHMENT = 1;
132013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final int RESULT_CREATE_ACCOUNT = 2;
133181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private static final int ACCOUNT_SETTINGS_LOADER = 0;
1348e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
13582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
13682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * A single thread for running tasks in the background.
13782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
13882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Handler mSendSaveTaskHandler = null;
139c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mTo;
140c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mCc;
141c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mBcc;
1428e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    private Button mCcBccButton;
1438e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    private CcBccView mCcBccView;
1447b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira    private AttachmentsView mAttachmentsView;
14533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private Account mAccount;
146181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private Settings mCachedSettings;
14782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Rfc822Validator mValidator;
1486349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private TextView mSubject;
1496349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
150326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private ComposeModeAdapter mComposeModeAdapter;
151326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private int mComposeMode = -1;
15246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private boolean mForward;
15346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private String mRecipient;
15446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private QuotedTextView mQuotedTextView;
155eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private TextView mBodyView;
1561a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    private View mFromStatic;
157eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private View mFromSpinnerWrapper;
1585a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    private FromAddressSpinner mFromSpinner;
159013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private boolean mAddingAttachment;
160eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private boolean mAttachmentsChanged;
16182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean mTextChanged;
16282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean mReplyFromChanged;
16382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private MenuItem mSave;
16482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private MenuItem mSend;
1653ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private String mRefMessageId;
16682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private AlertDialog mRecipientErrorDialog;
167eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private AlertDialog mSendConfirmDialog;
1683ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private Message mRefMessage;
1697ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira    private long mDraftId = UIProvider.INVALID_MESSAGE_ID;
1707ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira    private Message mDraft;
1717ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira    private Object mDraftLock = new Object();
1723ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira
173326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    /**
174326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira     * Can be called from a non-UI thread.
175326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira     */
1763ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void editDraft(Context launcher, Account account, Message message) {
177326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
178326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1796349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1806349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1816349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
18233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void compose(Context launcher, Account account) {
1836349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, null, COMPOSE);
1846349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1856349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1866349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1876349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1886349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
1893ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void reply(Context launcher, Account account, Message message) {
1903ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        launch(launcher, account, message, REPLY);
1916349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1926349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1936349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1946349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1956349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
1963ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void replyAll(Context launcher, Account account, Message message) {
1973ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        launch(launcher, account, message, REPLY_ALL);
1986349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1996349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
2006349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
2016349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
2026349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
2033ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void forward(Context launcher, Account account, Message message) {
2043ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        launch(launcher, account, message, FORWARD);
2056349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2066349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
2073ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private static void launch(Context launcher, Account account, Message message, int action) {
2086349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        Intent intent = new Intent(launcher, ComposeActivity.class);
2096349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
2106349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_ACTION, action);
2116349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account);
2123ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        intent.putExtra(EXTRA_IN_REFERENCE_TO_MESSAGE, message);
2136349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launcher.startActivity(intent);
2146349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2158e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
2168e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    @Override
2178e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    public void onCreate(Bundle savedInstanceState) {
2188e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        super.onCreate(savedInstanceState);
2198e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        setContentView(R.layout.compose);
22046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        findViews();
221818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        Intent intent = getIntent();
222818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        setAccount((Account)intent.getParcelableExtra(Utils.EXTRA_ACCOUNT));
223818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        if (mAccount == null) {
224818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira            return;
225818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        }
22646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        int action = intent.getIntExtra(EXTRA_ACTION, COMPOSE);
2273ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        mRefMessage = (Message) intent.getParcelableExtra(EXTRA_IN_REFERENCE_TO_MESSAGE);
22829ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        if ((action == REPLY || action == REPLY_ALL || action == FORWARD)) {
22933fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            initFromRefMessage(action, mAccount.name);
23046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
231eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mQuotedTextView.setVisibility(View.GONE);
23246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
233818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        initRecipients();
2341a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        initActionBar(action);
2355a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        initFromSpinner();
23675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        initChangeListeners();
2371a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
2381a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
2391a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    @Override
2401a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    protected void onResume() {
2411a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        super.onResume();
2421a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        // Update the from spinner as other accounts
2431a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        // may now be available.
244818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        if (mFromSpinner != null && mAccount != null) {
245818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira            mFromSpinner.asyncInitFromSpinner();
246818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        }
2471a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
2481a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
249eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
250eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected void onPause() {
251eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        super.onPause();
252eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
253eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mSendConfirmDialog != null) {
254eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mSendConfirmDialog.dismiss();
255eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
256eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mRecipientErrorDialog != null) {
257eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mRecipientErrorDialog.dismiss();
258eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
259eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
260eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveIfNeeded();
261eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
262eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
263eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
264eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected final void onActivityResult(int request, int result, Intent data) {
265eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAddingAttachment = false;
266eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
267eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (result == RESULT_OK && request == RESULT_PICK_ATTACHMENT) {
268eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            addAttachmentAndUpdateView(data);
269eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
270eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
271eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
272eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
273eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public final void onSaveInstanceState(Bundle state) {
274eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        super.onSaveInstanceState(state);
275eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
276eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // onSaveInstanceState is only called if the user might come back to this activity so it is
277eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // not an ideal location to save the draft. However, if we have never saved the draft before
278eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // we have to save it here in order to have an id to save in the bundle.
279eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveIfNeededOnOrientationChanged();
280eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
281eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
282818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    @VisibleForTesting
283818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    void setAccount(Account account) {
284818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mAccount = account;
285181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        getLoaderManager().restartLoader(ACCOUNT_SETTINGS_LOADER, null, this);
286818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    }
287818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira
2881a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    private void initFromSpinner() {
2895a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.setCurrentAccount(mAccount);
2905a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.asyncInitFromSpinner();
291eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        boolean showSpinner = mFromSpinner.getCount() > 1;
292eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // If there is only 1 account, just show that account.
293eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // Otherwise, give the user the ability to choose which account to send
294eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // mail from / save drafts to.
295eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromStatic.setVisibility(
296eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                showSpinner ? View.GONE : View.VISIBLE);
297eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromSpinnerWrapper.setVisibility(
298eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                showSpinner ? View.VISIBLE : View.GONE);
29946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
30046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
30146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void findViews() {
302a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        mCcBccButton = (Button) findViewById(R.id.add_cc_bcc);
3038e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        if (mCcBccButton != null) {
3048e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira            mCcBccButton.setOnClickListener(this);
3058e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        }
3068e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        mCcBccView = (CcBccView) findViewById(R.id.cc_bcc_wrapper);
3077b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira        mAttachmentsView = (AttachmentsView)findViewById(R.id.attachments);
308818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mTo = (RecipientEditTextView) findViewById(R.id.to);
309818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mCc = (RecipientEditTextView) findViewById(R.id.cc);
310818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mBcc = (RecipientEditTextView) findViewById(R.id.bcc);
31182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // TODO: add special chips text change watchers before adding
31282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // this as a text changed watcher to the to, cc, bcc fields.
3136349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        mSubject = (TextView) findViewById(R.id.subject);
31446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mQuotedTextView = (QuotedTextView) findViewById(R.id.quoted_text_view);
31546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mQuotedTextView.setRespondInlineListener(this);
316eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBodyView = (TextView) findViewById(R.id.body);
3171a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        mFromStatic = findViewById(R.id.static_from_content);
318eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromSpinnerWrapper = findViewById(R.id.spinner_from_content);
3195a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner = (FromAddressSpinner) findViewById(R.id.from_picker);
320326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
321326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
32275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // Now that the message has been initialized from any existing draft or
32375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // ref message data, set up listeners for any changes that occur to the
32475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // message.
32575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    private void initChangeListeners() {
32675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mSubject.addTextChangedListener(this);
32775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mBodyView.addTextChangedListener(this);
32875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mTo.addTextChangedListener(new RecipientTextWatcher(mTo, this));
32975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mCc.addTextChangedListener(new RecipientTextWatcher(mCc, this));
33075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mBcc.addTextChangedListener(new RecipientTextWatcher(mBcc, this));
33175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mFromSpinner.setOnAccountChangedListener(this);
332818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mAttachmentsView.setAttachmentChangesListener(this);
33375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
33475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
335326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private void initActionBar(int action) {
336326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        mComposeMode = action;
337eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        ActionBar actionBar = getActionBar();
338326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        if (action == ComposeActivity.COMPOSE) {
339eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
340eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setTitle(R.string.compose);
341326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else {
342eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setTitle(null);
343326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (mComposeModeAdapter == null) {
344326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                mComposeModeAdapter = new ComposeModeAdapter(this);
345326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
346eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
347eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setListNavigationCallbacks(mComposeModeAdapter, this);
348326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            switch (action) {
349326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.REPLY:
350eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(0);
351326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
352326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.REPLY_ALL:
353eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(1);
354326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
355326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.FORWARD:
356eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(2);
357326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
358326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
3596349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        }
3606349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
3616349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
36246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void initFromRefMessage(int action, String recipientAddress) {
3633ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        if (mRefMessage != null) {
3643ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            mRefMessageId = mRefMessage.refMessageId;
3653ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            setSubject(mRefMessage, action);
3663ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            // Setup recipients
3673ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            if (action == FORWARD) {
3683ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                mForward = true;
3693ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            }
3703ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            initRecipientsFromRefMessage(recipientAddress, mRefMessage, action);
3713ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            initBodyFromRefMessage(mRefMessage, action);
3723ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            if (action == ComposeActivity.FORWARD || mAttachmentsChanged) {
3733ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                initAttachments(mRefMessage);
37446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            }
3753ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            updateHideOrShowCcBcc();
3766349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        }
377c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    }
378c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira
3793ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private void initAttachments(Message refMessage) {
3807a07fb4955e34250225287212b07d1b6f12bf671Mindy Pereira        mAttachmentsView.addAttachments(mAccount, refMessage);
381eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
382eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
3833ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private void initBodyFromRefMessage(Message refMessage, int action) {
3849932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        if (action == REPLY || action == REPLY_ALL || action == FORWARD) {
3859932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            mQuotedTextView.setQuotedText(action, refMessage, action != FORWARD);
3869932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        }
38746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
38846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
38946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void updateHideOrShowCcBcc() {
390ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        // Its possible there is a menu item OR a button.
391a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        boolean ccVisible = !TextUtils.isEmpty(mCc.getText());
392a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        boolean bccVisible = !TextUtils.isEmpty(mBcc.getText());
393a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (ccVisible || bccVisible) {
394a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            mCcBccView.show(false, ccVisible, bccVisible);
395a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        }
396ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (mCcBccButton != null) {
397ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            if (!mCc.isShown() || !mBcc.isShown()) {
398ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setVisibility(View.VISIBLE);
399ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setText(getString(!mCc.isShown() ? R.string.add_cc_label
400ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                        : R.string.add_bcc_label));
401ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            } else {
402ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setVisibility(View.GONE);
403ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            }
404ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
40546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
40646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
407013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    /**
408013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     * Add attachment and update the compose area appropriately.
409013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     * @param data
410013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     */
411013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    public void addAttachmentAndUpdateView(Intent data) {
412013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        Uri uri = data != null ? data.getData() : null;
413013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        try {
414f944e9655562e321aff52fe5f437e1d2fa2950a9Mindy Pereira            long size =  mAttachmentsView.addAttachment(mAccount, uri, false /* doSave */,
415f944e9655562e321aff52fe5f437e1d2fa2950a9Mindy Pereira                    true /* local file */);
4169932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            if (size > 0) {
4179932dee248718d2cec993d93b5115973d1389d0aMindy Pereira                mAttachmentsChanged = true;
4189932dee248718d2cec993d93b5115973d1389d0aMindy Pereira                updateSaveUi();
419013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            }
4209932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        } catch (AttachmentFailureException e) {
4219932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            // A toast has already been shown to the user, no need to do
4229932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            // anything.
4239932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            LogUtils.e(LOG_TAG, e, "Error adding attachment");
424013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        }
425013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    }
426013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira
4273ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    void initRecipientsFromRefMessage(String recipientAddress, Message refMessage,
42846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            int action) {
4294a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // Don't populate the address if this is a forward.
4304a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        if (action == ComposeActivity.FORWARD) {
4314a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            return;
4324a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
43333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        initReplyRecipients(mAccount.name, refMessage, action);
4344a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4354a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
436818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    @VisibleForTesting
4373ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    void initReplyRecipients(String account, Message refMessage, int action) {
4384a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // This is the email address of the current user, i.e. the one composing
4394a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // the reply.
4404a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira        final String accountEmail = Address.getEmailAddress(account).getAddress();
4413ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String fromAddress = refMessage.from;
4423ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String[] sentToAddresses = Utils.splitCommaSeparatedString(refMessage.to);
4433ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String replytoAddress = refMessage.replyTo;
444a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        final Collection<String> toAddresses;
4454a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4464a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // If this is a reply, the Cc list is empty. If this is a reply-all, the
4474a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // Cc list is the union of the To and Cc recipients of the original
4484a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // message, excluding the current user's email address and any addresses
449a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        // already on the To list.
450a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (action == ComposeActivity.REPLY) {
4513ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses = initToRecipients(account, accountEmail, fromAddress, replytoAddress,
4523ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                    new String[0]);
453a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            addToAddresses(toAddresses);
454a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        } else if (action == ComposeActivity.REPLY_ALL) {
4554a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            final Set<String> ccAddresses = Sets.newHashSet();
4563ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses = initToRecipients(account, accountEmail, fromAddress, replytoAddress,
4573ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                    new String[0]);
458154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            addToAddresses(toAddresses);
4594a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addRecipients(accountEmail, ccAddresses, sentToAddresses);
4603ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            addRecipients(accountEmail, ccAddresses,
4613ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                    Utils.splitCommaSeparatedString(refMessage.cc));
4624a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addCcAddresses(ccAddresses, toAddresses);
4634a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4644a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4654a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4664a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addToAddresses(Collection<String> addresses) {
4674a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        addAddressesToList(addresses, mTo);
4684a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4694a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4704a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addCcAddresses(Collection<String> addresses, Collection<String> toAddresses) {
4714a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        addCcAddressesToList(tokenizeAddressList(addresses), tokenizeAddressList(toAddresses),
4724a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                mCc);
4734a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4744a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4754a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
4764a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected void addCcAddressesToList(List<Rfc822Token[]> addresses,
4774a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            List<Rfc822Token[]> compareToList, RecipientEditTextView list) {
4784a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String address;
4794a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4804a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        HashSet<String> compareTo = convertToHashSet(compareToList);
4814a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (Rfc822Token[] tokens : addresses) {
4824a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            for (int i = 0; i < tokens.length; i++) {
4834a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                address = tokens[i].toString();
4844a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // Check if this is a duplicate:
4854a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                if (!compareTo.contains(tokens[i].getAddress())) {
4864a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    // Get the address here
4874a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    list.append(address + END_TOKEN);
4884a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                }
4894a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
4904a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4914a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4924a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4934a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private HashSet<String> convertToHashSet(List<Rfc822Token[]> list) {
4944a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        HashSet<String> hash = new HashSet<String>();
4954a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (Rfc822Token[] tokens : list) {
4964a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            for (int i = 0; i < tokens.length; i++) {
4974a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                hash.add(tokens[i].getAddress());
4984a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
4994a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5004a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return hash;
5014a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5024a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5034a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected List<Rfc822Token[]> tokenizeAddressList(Collection<String> addresses) {
5044a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        @VisibleForTesting
5054a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        List<Rfc822Token[]> tokenized = new ArrayList<Rfc822Token[]>();
5064a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5074a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String address: addresses) {
5084a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            tokenized.add(Rfc822Tokenizer.tokenize(address));
5094a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5104a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return tokenized;
5114a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5124a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5134a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
5144a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    void addAddressesToList(Collection<String> addresses, RecipientEditTextView list) {
5154a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String address : addresses) {
5164a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addAddressToList(address, list);
5174a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5184a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5194a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5204a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addAddressToList(String address, RecipientEditTextView list) {
5214a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        if (address == null || list == null)
5224a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            return;
5234a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5244a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);
5254a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5264a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (int i = 0; i < tokens.length; i++) {
5274a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            list.append(tokens[i] + END_TOKEN);
5284a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5294a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5304a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5314a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
5324a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected Collection<String> initToRecipients(String account, String accountEmail,
5333ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            String senderAddress, String replyToAddress, String[] inToAddresses) {
5344a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // The To recipient is the reply-to address specified in the original
5354a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // message, unless it is:
5364a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // the current user OR a custom from of the current user, in which case
5374a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // it's the To recipient list of the original message.
5384a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // OR missing, in which case use the sender of the original message
5394a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        Set<String> toAddresses = Sets.newHashSet();
5403ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        if (!TextUtils.isEmpty(replyToAddress)) {
5413ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses.add(replyToAddress);
5424a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        } else {
5433ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses.add(senderAddress);
5444a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5454a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return toAddresses;
5464a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5474a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5484a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private static void addRecipients(String account, Set<String> recipients, String[] addresses) {
5494a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String email : addresses) {
5504a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // Do not add this account, or any of the custom froms, to the list
5514a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // of recipients.
5524a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira            final String recipientAddress = Address.getEmailAddress(email).getAddress();
5534a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            if (!account.equalsIgnoreCase(recipientAddress)) {
5544a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                recipients.add(email.replace("\"\"", ""));
5554a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
5564a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
55746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
55846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
5593ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private void setSubject(Message refMessage, int action) {
5603ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String subject = refMessage.subject;
56146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String prefix;
56246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String correctedSubject = null;
56346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (action == ComposeActivity.COMPOSE) {
56446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = "";
56546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else if (action == ComposeActivity.FORWARD) {
56646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = getString(R.string.forward_subject_label);
56746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
56846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = getString(R.string.reply_subject_label);
56946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
57046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
57146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        // Don't duplicate the prefix
57246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (subject.toLowerCase().startsWith(prefix.toLowerCase())) {
57346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            correctedSubject = subject;
57446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
57546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            correctedSubject = String
57646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                    .format(getString(R.string.formatted_subject), prefix, subject);
57746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
57846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mSubject.setText(correctedSubject);
57946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
58046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
581818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    private void initRecipients() {
582818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        setupRecipients(mTo);
583818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        setupRecipients(mCc);
584818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        setupRecipients(mBcc);
585eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
586eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
587818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    private void setupRecipients(RecipientEditTextView view) {
588679a8cc895ec75d3b578dbc77db1e9c04dd7f8b0Paul Westbrook        view.setAdapter(new RecipientAdapter(this, mAccount));
589c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        view.setTokenizer(new Rfc822Tokenizer());
59082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mValidator == null) {
591679a8cc895ec75d3b578dbc77db1e9c04dd7f8b0Paul Westbrook            final String accountName = mAccount.name;
59233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            int offset = accountName.indexOf("@") + 1;
59333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            String account = accountName;
594c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira            if (offset > -1) {
59533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                account = account.substring(accountName.indexOf("@") + 1);
596c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira            }
59782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mValidator = new Rfc822Validator(account);
598c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        }
59982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        view.setValidator(mValidator);
6008e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    }
6018e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
6028e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    @Override
6038e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    public void onClick(View v) {
6048e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        int id = v.getId();
6058e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        switch (id) {
606a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            case R.id.add_cc_bcc:
6078e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                // Verify that cc/ bcc aren't showing.
6088e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                // Animate in cc/bcc.
609a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira                showCcBccViews();
6108e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                break;
6118e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        }
6128e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    }
613b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira
614b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    @Override
615b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    public boolean onCreateOptionsMenu(Menu menu) {
616b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        super.onCreateOptionsMenu(menu);
617b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        MenuInflater inflater = getMenuInflater();
618b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        inflater.inflate(R.menu.compose_menu, menu);
61982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSave = menu.findItem(R.id.save);
62082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSend = menu.findItem(R.id.send);
621b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        return true;
622b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    }
623b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira
624b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    @Override
625ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    public boolean onPrepareOptionsMenu(Menu menu) {
626ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        MenuItem ccBcc = menu.findItem(R.id.add_cc_bcc);
627818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        if (ccBcc != null && mCc != null) {
628ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            // Its possible there is a menu item OR a button.
629ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            boolean ccFieldVisible = mCc.isShown();
630ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            boolean bccFieldVisible = mBcc.isShown();
631ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            if (!ccFieldVisible || !bccFieldVisible) {
632ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setVisible(true);
633ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setTitle(getString(!ccFieldVisible ? R.string.add_cc_label
634ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                        : R.string.add_bcc_label));
635ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            } else {
636ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setVisible(false);
637ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            }
638ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
63975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        if (mSave != null) {
64075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mSave.setEnabled(shouldSave());
64175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
642ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        return true;
643ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    }
644ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira
645ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    @Override
646b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    public boolean onOptionsItemSelected(MenuItem item) {
647b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        int id = item.getItemId();
64875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        boolean handled = true;
649b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        switch (id) {
6507b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira            case R.id.add_attachment:
651013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira                doAttach();
6527b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira                break;
653ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            case R.id.add_cc_bcc:
654ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                showCcBccViews();
655b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira                break;
65633fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            case R.id.save:
657eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                doSave(true, false);
65833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                break;
65933fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            case R.id.send:
66033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                doSend();
66175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                break;
66275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            default:
66375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                handled = false;
66433fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                break;
665b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        }
666b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        return !handled ? super.onOptionsItemSelected(item) : handled;
667b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    }
668326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
66933fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private void doSend() {
67082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        sendOrSaveWithSanityChecks(false, true, false);
67133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    }
67233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira
673eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void doSave(boolean showToast, boolean resetIME) {
674eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        sendOrSaveWithSanityChecks(true, showToast, false);
675eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (resetIME) {
676eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // Clear the IME composing suggestions from the body.
677eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            BaseInputConnection.removeComposingSpans(mBodyView.getEditableText());
678eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
67982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
68082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
68182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ interface SendOrSaveCallback {
68282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask);
6837ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        public void notifyMessageIdAllocated(SendOrSaveMessage sendOrSaveMessage, Message message);
6847ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        public Message getMessage();
68582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void sendOrSaveFinished(SendOrSaveTask sendOrSaveTask, boolean success);
68682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
68782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
68882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ static class SendOrSaveTask implements Runnable {
68982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        private final Context mContext;
69082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        private final SendOrSaveCallback mSendOrSaveCallback;
69182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        @VisibleForTesting
69282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final SendOrSaveMessage mSendOrSaveMessage;
69382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
69482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public SendOrSaveTask(Context context, SendOrSaveMessage message,
69582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                SendOrSaveCallback callback) {
69682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mContext = context;
69782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveCallback = callback;
69882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveMessage = message;
69982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
70082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
70182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        @Override
70282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void run() {
7037ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            final SendOrSaveMessage sendOrSaveMessage = mSendOrSaveMessage;
70482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
7057ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            final Account selectedAccount = sendOrSaveMessage.mSelectedAccount;
7067ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            Message message = mSendOrSaveCallback.getMessage();
7077ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            long messageId = message != null ? message.id : UIProvider.INVALID_MESSAGE_ID;
70882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // If a previous draft has been saved, in an account that is different
70982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // than what the user wants to send from, remove the old draft, and treat this
71082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // as a new message
7117ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            if (!selectedAccount.equals(sendOrSaveMessage.mAccount)) {
71282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                if (messageId != UIProvider.INVALID_MESSAGE_ID) {
71382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    ContentResolver resolver = mContext.getContentResolver();
71482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    ContentValues values = new ContentValues();
71582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    values.put(BaseColumns._ID, messageId);
716cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    if (selectedAccount.expungeMessageUri != null) {
717cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                        resolver.update(selectedAccount.expungeMessageUri, values, null,
718eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                                null);
719cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    } else {
720cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                        // TODO(mindyp) delete the conversation.
721eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    }
72282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    // reset messageId to 0, so a new message will be created
72382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    messageId = UIProvider.INVALID_MESSAGE_ID;
72482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                }
72582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
72682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
72782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            final long messageIdToSave = messageId;
72882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (messageIdToSave != UIProvider.INVALID_MESSAGE_ID) {
7297ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                sendOrSaveMessage.mValues.put(BaseColumns._ID, messageIdToSave);
73082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mContext.getContentResolver().update(
7317ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        Uri.parse(sendOrSaveMessage.mSave ? message.saveUri : message.sendUri),
7327ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        sendOrSaveMessage.mValues, null, null);
73382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            } else {
7347ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                ContentResolver resolver = mContext.getContentResolver();
7357ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                Uri messageUri = resolver.insert(
736cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                        sendOrSaveMessage.mSave ? selectedAccount.saveDraftUri
737cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                                : selectedAccount.sendMessageUri, sendOrSaveMessage.mValues);
7387ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (sendOrSaveMessage.mSave && messageUri != null) {
7397ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    Cursor messageCursor = resolver.query(messageUri,
7407ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                            UIProvider.MESSAGE_PROJECTION, null, null, null);
7417ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    if (messageCursor != null && messageCursor.moveToFirst()) {
7427ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        // Broadcast notification that a new message has
7437ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        // been allocated
7447ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        mSendOrSaveCallback.notifyMessageIdAllocated(sendOrSaveMessage,
745a831b2f1ba6d3c18d5e54563df8bae1f589c4257Mindy Pereira                                new Message(messageCursor));
7467ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    }
7477ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
74882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
74982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
7507ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            if (!sendOrSaveMessage.mSave) {
75182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
7527ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.TO));
75382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
7547ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.CC));
75582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
7567ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.BCC));
75782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
75882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveCallback.sendOrSaveFinished(SendOrSaveTask.this, true);
75982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
76082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
76182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
76282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Array of the outstanding send or save tasks.  Access is synchronized
76382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // with the object itself
76482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /* package for testing */
76582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    ArrayList<SendOrSaveTask> mActiveTasks = Lists.newArrayList();
76682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private int mRequestId;
76782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
76882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ static class SendOrSaveMessage {
76982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final Account mAccount;
77082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final Account mSelectedAccount;
77182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final ContentValues mValues;
7723ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        final String mRefMessageId;
77382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final boolean mSave;
77482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final int mRequestId;
77582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
77682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public SendOrSaveMessage(Account account, Account selectedAccount, ContentValues values,
7773ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                String refMessageId, boolean save) {
77882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mAccount = account;
77982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSelectedAccount = selectedAccount;
78082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mValues = values;
78182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRefMessageId = refMessageId;
78282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave = save;
78382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRequestId = mValues.hashCode() ^ hashCode();
78482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
78582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
78682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        int requestId() {
78782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            return mRequestId;
78882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
78982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
79082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
79182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
79282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the to recipients.
79382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
79482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getToAddresses() {
79582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mTo);
79682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
79782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
79882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
79982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the cc recipients.
80082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
80182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getCcAddresses() {
80282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mCc);
80382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
80482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
80582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
80682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the bcc recipients.
80782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
80882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getBccAddresses() {
80982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mBcc);
81082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
81182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
81282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getAddressesFromList(RecipientEditTextView list) {
81382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (list == null) {
81482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            return new String[0];
81582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
81682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(list.getText());
81782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        int count = tokens.length;
81882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        String[] result = new String[count];
81982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        for (int i = 0; i < count; i++) {
82082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            result[i] = tokens[i].toString();
82182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
82282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return result;
82382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
82482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
82582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
82682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Check for invalid email addresses.
82782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param to String array of email addresses to check.
82882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param wrongEmailsOut Emails addresses that were invalid.
82982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
83082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void checkInvalidEmails(String[] to, List<String> wrongEmailsOut) {
83182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        for (String email : to) {
83282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (!mValidator.isValid(email)) {
83382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                wrongEmailsOut.add(email);
83482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
83582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
83682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
83782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
83882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
83982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Show an error because the user has entered an invalid recipient.
84082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param message
84182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
84282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void showRecipientErrorDialog(String message) {
84382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // Only 1 invalid recipients error dialog should be allowed up at a
84482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // time.
84582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mRecipientErrorDialog != null) {
84682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRecipientErrorDialog.dismiss();
84782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
84882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mRecipientErrorDialog = new AlertDialog.Builder(this).setMessage(message).setTitle(
84982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                R.string.recipient_error_dialog_title)
85082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setIconAttribute(android.R.attr.alertDialogIcon)
85182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setCancelable(false)
85282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setPositiveButton(
85382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        R.string.ok, new Dialog.OnClickListener() {
85482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                            public void onClick(DialogInterface dialog, int which) {
85582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // after the user dismisses the recipient error
85682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // dialog we want to make sure to refocus the
85782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // recipient to field so they can fix the issue
85882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // easily
85982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                if (mTo != null) {
86082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                    mTo.requestFocus();
86182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                }
86282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                mRecipientErrorDialog = null;
86382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                            }
86482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        }).show();
86582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
86682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
86782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
86882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Update the state of the UI based on whether or not the current draft
86982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * needs to be saved and the message is not empty.
87082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
871eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void updateSaveUi() {
87282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSave != null) {
87382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave.setEnabled((shouldSave() && !isBlank()));
87482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
87582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
87682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
87782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
87882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Returns true if we need to save the current draft.
87982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
88082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean shouldSave() {
8817ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        synchronized (mDraftLock) {
88282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // The message should only be saved if:
88382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // It hasn't been sent AND
88482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // Some text has been added to the message OR
88582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // an attachment has been added or removed
886eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return (mTextChanged || mAttachmentsChanged ||
88782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    (mReplyFromChanged && !isBlank()));
88882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
88982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
89082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
89182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
89282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Check if the ComposeArea believes all fields are blank.
89382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @return boolean
89482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
89582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public boolean isBlank() {
89682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return mSubject.getText().length() == 0
897eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira               && mBodyView.getText().length() == 0
89882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mTo.length() == 0
89982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mCc.length() == 0
90082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mBcc.length() == 0
90182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mAttachmentsView.getAttachments().size() == 0;
90282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
90382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
90482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
90582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Allows any changes made by the user to be ignored. Called when the user
90682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * decides to discard a draft.
90782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
90882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private void discardChanges() {
90982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mTextChanged = false;
910eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsChanged = false;
91182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mReplyFromChanged = false;
91233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    }
91333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira
91482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
915181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @param body
916181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @param save
917181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @param showToast
918181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return Whether the send or save succeeded.
919181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
920181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    protected boolean sendOrSaveWithSanityChecks(final boolean save, final boolean showToast,
921181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            final boolean orientationChanged) {
922181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        String[] to, cc, bcc;
923181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        Editable body = mBodyView.getEditableText();
924181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
925181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (orientationChanged) {
926181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = cc = bcc = new String[0];
927181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        } else {
928181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = getToAddresses();
929181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            cc = getCcAddresses();
930181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            bcc = getBccAddresses();
931181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
932181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
933181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Don't let the user send to nobody (but it's okay to save a message
934181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // with no recipients)
935181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save && (to.length == 0 && cc.length == 0 && bcc.length == 0)) {
936181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            showRecipientErrorDialog(getString(R.string.recipient_needed));
937181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            return false;
938181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
939181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
940181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        List<String> wrongEmails = new ArrayList<String>();
941181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save) {
942181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            checkInvalidEmails(to, wrongEmails);
943181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            checkInvalidEmails(cc, wrongEmails);
944181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            checkInvalidEmails(bcc, wrongEmails);
945181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
946181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
947181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Don't let the user send an email with invalid recipients
948181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (wrongEmails.size() > 0) {
949181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            String errorText = String.format(getString(R.string.invalid_recipient),
950181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    wrongEmails.get(0));
951181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            showRecipientErrorDialog(errorText);
952181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            return false;
953181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
954181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
955181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
956181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            public void onClick(DialogInterface dialog, int which) {
957181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                sendOrSave(mBodyView.getEditableText(), save, showToast, orientationChanged);
958181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
959181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        };
960181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
961181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Show a warning before sending only if there are no attachments.
962181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save) {
963181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (mAttachmentsView.getAttachments().isEmpty() && showEmptyTextWarnings()) {
964181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                boolean warnAboutEmptySubject = isSubjectEmpty();
965181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                boolean emptyBody = TextUtils.getTrimmedLength(body) == 0;
966181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
967181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // A warning about an empty body may not be warranted when
968181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // forwarding mails, since a common use case is to forward
969181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // quoted text and not append any more text.
970181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                boolean warnAboutEmptyBody = emptyBody && (!mForward || isBodyEmpty());
971181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
972181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // When we bring up a dialog warning the user about a send,
973181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // assume that they accept sending the message. If they do not,
974181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // the dialog listener is required to enable sending again.
975181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (warnAboutEmptySubject) {
976181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    showSendConfirmDialog(R.string.confirm_send_message_with_no_subject, listener);
977181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    return true;
978181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
979181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
980181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (warnAboutEmptyBody) {
981181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    showSendConfirmDialog(R.string.confirm_send_message_with_no_body, listener);
982181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    return true;
983181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
984181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
985181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // Ask for confirmation to send (if always required)
986181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (showSendConfirmation()) {
987181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                showSendConfirmDialog(R.string.confirm_send_message, listener);
988181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                return true;
989181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
990181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
991181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
992181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        sendOrSave(body, save, showToast, false);
993181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return true;
994181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
995181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
996181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
997181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Returns a boolean indicating whether warnings should be shown for empty
998181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * subject and body fields
999181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     *
1000181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return True if a warning should be shown for empty text fields
1001181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1002181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    protected boolean showEmptyTextWarnings() {
1003181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return mAttachmentsView.getAttachments().size() == 0;
1004181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1005181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1006181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1007181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Returns a boolean indicating whether the user should confirm each send
1008181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     *
1009181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return True if a warning should be on each send
1010181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1011181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    protected boolean showSendConfirmation() {
1012181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return mCachedSettings != null ? mCachedSettings.confirmSend : false;
1013181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1014181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1015181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private void showSendConfirmDialog(int messageId, DialogInterface.OnClickListener listener) {
1016181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (mSendConfirmDialog != null) {
1017181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mSendConfirmDialog.dismiss();
1018181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mSendConfirmDialog = null;
1019181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1020181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        mSendConfirmDialog = new AlertDialog.Builder(this).setMessage(messageId)
1021181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setTitle(R.string.confirm_send_title)
1022181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setIconAttribute(android.R.attr.alertDialogIcon)
1023181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setPositiveButton(R.string.send, listener)
1024181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setNegativeButton(R.string.cancel, this).setCancelable(false).show();
1025181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1026181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1027181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1028181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Returns whether the ComposeArea believes there is any text in the body of
1029181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * the composition. TODO: When ComposeArea controls the Body as well, add
1030181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * that here.
1031181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1032181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public boolean isBodyEmpty() {
1033181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return !mQuotedTextView.isTextIncluded();
1034181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1035181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1036181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1037181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Test to see if the subject is empty.
1038181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     *
1039181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return boolean.
1040181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1041181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    // TODO: this will likely go away when composeArea.focus() is implemented
1042181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    // after all the widget control is moved over.
1043181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public boolean isSubjectEmpty() {
1044181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return TextUtils.getTrimmedLength(mSubject.getText()) == 0;
1045181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1046181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1047181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /* package */
104829ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira    static int sendOrSaveInternal(Context context, final Account account,
104929ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            final Account selectedAccount, String fromAddress, final Spanned body,
105029ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            final String[] to, final String[] cc, final String[] bcc, final String subject,
105129ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            final CharSequence quotedText, final List<Attachment> attachments,
10523ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            final String refMessageId, SendOrSaveCallback callback, Handler handler, boolean save,
105329ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            boolean forward) {
105429ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        ContentValues values = new ContentValues();
105529ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira
105629ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putToAddresses(values, to);
105729ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putCcAddresses(values, cc);
105829ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putBccAddresses(values, bcc);
105929ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira
106029ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putSubject(values, subject);
106129ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        String htmlBody = Html.toHtml(body);
106229ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        boolean includeQuotedText = !TextUtils.isEmpty(quotedText);
106329ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        StringBuilder fullBody = new StringBuilder(htmlBody);
106429ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        if (includeQuotedText) {
106529ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            if (forward) {
106629ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                // forwarded messages get full text in HTML from client
106729ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                fullBody.append(quotedText);
106829ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                MessageModification.putForward(values, forward);
106929ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            } else {
107029ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                // replies get full quoted text from server - HTMl gets
107129ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                // converted to text for now
107229ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                final String text = quotedText.toString();
107329ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                if (QuotedTextView.containsQuotedText(text)) {
107429ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    int pos = QuotedTextView.getQuotedTextOffset(text);
107529ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    fullBody.append(text.substring(0, pos));
107629ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    MessageModification.putForward(values, forward);
10773ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                    MessageModification.putAppendRefMessageContent(values, includeQuotedText);
107829ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                } else {
107929ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    LogUtils.w(LOG_TAG, "Couldn't find quoted text");
108029ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    // This shouldn't happen, but just use what we have,
108129ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    // and don't do server-side expansion
108229ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    fullBody.append(text);
108329ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                }
108429ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            }
108529ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        }
108629ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putBody(values, Html.fromHtml(fullBody.toString()).toString());
108729ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putBodyHtml(values, fullBody.toString());
1088f944e9655562e321aff52fe5f437e1d2fa2950a9Mindy Pereira        MessageModification.putAttachments(values, attachments);
108982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1090181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        SendOrSaveMessage sendOrSaveMessage = new SendOrSaveMessage(account, selectedAccount,
1091181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                values, refMessageId, save);
1092181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        SendOrSaveTask sendOrSaveTask = new SendOrSaveTask(context, sendOrSaveMessage, callback);
109382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1094181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        callback.initializeSendOrSave(sendOrSaveTask);
109582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1096181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Do the send/save action on the specified handler to avoid possible
1097181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // ANRs
1098181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        handler.post(sendOrSaveTask);
109982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1100181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return sendOrSaveMessage.requestId();
1101181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
110282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1103181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private void sendOrSave(Spanned body, boolean save, boolean showToast,
1104181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            boolean orientationChanged) {
1105181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Check if user is a monkey. Monkeys can compose and hit send
1106181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // button but are not allowed to send anything off the device.
1107181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save && ActivityManager.isUserAMonkey()) {
1108181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            return;
1109181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
111082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1111181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        String[] to, cc, bcc;
1112181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (orientationChanged) {
1113181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = cc = bcc = new String[0];
1114181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        } else {
1115181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = getToAddresses();
1116181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            cc = getCcAddresses();
1117181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            bcc = getBccAddresses();
1118181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
111982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1120181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        SendOrSaveCallback callback = new SendOrSaveCallback() {
112182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            private int mRestoredRequestId;
112282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
112382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask) {
1124181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                synchronized (mActiveTasks) {
1125181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    int numTasks = mActiveTasks.size();
1126181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    if (numTasks == 0) {
1127181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        // Start service so we won't be killed if this app is
1128181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        // put in the background.
1129181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        startService(new Intent(ComposeActivity.this, EmptyService.class));
1130181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    }
1131181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1132181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    mActiveTasks.add(sendOrSaveTask);
1133181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1134181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (sTestSendOrSaveCallback != null) {
1135181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    sTestSendOrSaveCallback.initializeSendOrSave(sendOrSaveTask);
1136181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1137181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
113882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
11397ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            public void notifyMessageIdAllocated(SendOrSaveMessage sendOrSaveMessage,
11407ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    Message message) {
1141181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                synchronized (mDraftLock) {
1142181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    mDraftId = message.id;
1143181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    mDraft = message;
11447ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    if (sRequestMessageIdMap != null) {
11457ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        sRequestMessageIdMap.put(sendOrSaveMessage.requestId(), mDraftId);
11467ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    }
1147181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    // Cache request message map, in case the process is killed
1148181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    saveRequestMap();
1149181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1150181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (sTestSendOrSaveCallback != null) {
11517ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    sTestSendOrSaveCallback.notifyMessageIdAllocated(sendOrSaveMessage, message);
1152181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1153181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
115482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
11557ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            public Message getMessage() {
11567ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                synchronized (mDraftLock) {
11577ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    return mDraft;
11587ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
11597ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            }
116082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
11617ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            public void sendOrSaveFinished(SendOrSaveTask task, boolean success) {
11627ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (success) {
11637ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // Successfully sent or saved so reset change markers
11647ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    discardChanges();
11657ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                } else {
11667ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // A failure happened with saving/sending the draft
11677ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // TODO(pwestbro): add a better string that should be used
11687ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // when failing to send or save
11697ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    Toast.makeText(ComposeActivity.this, R.string.send_failed, Toast.LENGTH_SHORT)
11707ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                            .show();
11717ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
117282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
11737ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                int numTasks;
11747ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                synchronized (mActiveTasks) {
11757ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // Remove the task from the list of active tasks
11767ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    mActiveTasks.remove(task);
11777ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    numTasks = mActiveTasks.size();
11787ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
117982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
11807ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (numTasks == 0) {
11817ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // Stop service so we can be killed.
11827ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    stopService(new Intent(ComposeActivity.this, EmptyService.class));
11837ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
11847ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (sTestSendOrSaveCallback != null) {
11857ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    sTestSendOrSaveCallback.sendOrSaveFinished(task, success);
11867ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
11877ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            }
1188181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        };
1189181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1190181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Get the selected account if the from spinner has been setup.
1191181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        Account selectedAccount = mAccount;
1192181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        String fromAddress = selectedAccount.name;
1193181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (selectedAccount == null || fromAddress == null) {
1194181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // We don't have either the selected account or from address,
1195181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // use mAccount.
1196181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            selectedAccount = mAccount;
1197181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            fromAddress = mAccount.name;
1198181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1199181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1200181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (mSendSaveTaskHandler == null) {
1201181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            HandlerThread handlerThread = new HandlerThread("Send Message Task Thread");
1202181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            handlerThread.start();
1203181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1204181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mSendSaveTaskHandler = new Handler(handlerThread.getLooper());
1205181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1206181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1207181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        mRequestId = sendOrSaveInternal(this, mAccount, selectedAccount, fromAddress, body, to, cc,
1208181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                bcc, mSubject.getText().toString(), mQuotedTextView.getQuotedText(),
1209181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                mAttachmentsView.getAttachments(), mRefMessageId, callback, mSendSaveTaskHandler,
1210181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                save, mForward);
1211181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1212181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (mRecipient != null && mRecipient.equals(mAccount.name)) {
1213181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mRecipient = selectedAccount.name;
1214181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1215181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        mAccount = selectedAccount;
1216181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1217181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Don't display the toast if the user is just changing the orientation,
1218181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // but we still need to save the draft to the cursor because this is how we restore
1219181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // the attachments when the configuration change completes.
1220181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (showToast && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) {
1221181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            Toast.makeText(this, save ? R.string.message_saved : R.string.sending_message,
1222181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    Toast.LENGTH_LONG).show();
1223181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1224181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1225181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Need to update variables here because the send or save completes
1226181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // asynchronously even though the toast shows right away.
1227181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        discardChanges();
1228181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        updateSaveUi();
1229181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1230181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // If we are sending, finish the activity
1231181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save) {
1232181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            finish();
1233181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1234181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1235181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1236181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1237181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Save the state of the request messageid map. This allows for the Gmail
1238181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * process to be killed, but and still allow for ComposeActivity instances
1239181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * to be recreated correctly.
1240181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1241181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private void saveRequestMap() {
1242181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // TODO: store the request map in user preferences.
1243181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
124482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1245013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    public void doAttach() {
1246013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
1247013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        i.addCategory(Intent.CATEGORY_OPENABLE);
1248181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (android.provider.Settings.System.getInt(getContentResolver(),
1249181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                UIProvider.getAttachmentTypeSetting(), 0) != 0) {
1250013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            i.setType("*/*");
1251013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        } else {
1252013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            i.setType("image/*");
1253013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        }
1254013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        mAddingAttachment = true;
1255181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        startActivityForResult(Intent.createChooser(i, getText(R.string.select_attachment_type)),
1256181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                RESULT_PICK_ATTACHMENT);
1257013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    }
1258013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira
1259ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    private void showCcBccViews() {
1260a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        mCcBccView.show(true, true, true);
1261ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (mCcBccButton != null) {
1262ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            mCcBccButton.setVisibility(View.GONE);
1263ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
1264ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    }
1265ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira
1266326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    @Override
1267326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    public boolean onNavigationItemSelected(int position, long itemId) {
1268a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        int initialComposeMode = mComposeMode;
1269326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        if (position == ComposeActivity.REPLY) {
1270326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.REPLY;
1271326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else if (position == ComposeActivity.REPLY_ALL) {
1272326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.REPLY_ALL;
1273326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else if (position == ComposeActivity.FORWARD) {
1274326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.FORWARD;
1275326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1276a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (initialComposeMode != mComposeMode) {
1277154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            resetMessageForModeChange();
127833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            initFromRefMessage(mComposeMode, mAccount.name);
1279a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        }
1280326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        return true;
1281326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
1282326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1283154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira    private void resetMessageForModeChange() {
1284154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // When switching between reply, reply all, forward,
1285154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // follow the behavior of webview.
1286154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // The contents of the following fields are cleared
1287154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // so that they can be populated directly from the
1288154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // ref message:
1289154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 1) Any recipient fields
1290154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 2) The subject
1291154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mTo.setText("");
1292154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mCc.setText("");
1293154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mBcc.setText("");
1294154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // Any edits to the subject are replaced with the original subject.
1295154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mSubject.setText("");
1296154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira
1297154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // Any changes to the contents of the following fields are kept:
1298154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 1) Body
1299154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 2) Attachments
1300154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // If the user made changes to attachments, keep their changes.
1301154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        if (!mAttachmentsChanged) {
1302154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            mAttachmentsView.deleteAllAttachments();
1303154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        }
1304154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira    }
1305154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira
1306326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private class ComposeModeAdapter extends ArrayAdapter<String> {
1307326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1308326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        private LayoutInflater mInflater;
1309326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1310326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        public ComposeModeAdapter(Context context) {
1311326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            super(context, R.layout.compose_mode_item, R.id.mode, getResources()
1312326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    .getStringArray(R.array.compose_modes));
1313326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1314326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1315326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        private LayoutInflater getInflater() {
1316326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (mInflater == null) {
1317326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                mInflater = LayoutInflater.from(getContext());
1318326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
1319326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            return mInflater;
1320326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1321326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1322326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        @Override
1323326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        public View getView(int position, View convertView, ViewGroup parent) {
1324326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (convertView == null) {
1325326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                convertView = getInflater().inflate(R.layout.compose_mode_display_item, null);
1326326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
1327326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            ((TextView) convertView.findViewById(R.id.mode)).setText(getItem(position));
1328326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            return super.getView(position, convertView, parent);
1329326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1330326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
133146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
133246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    @Override
133346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void onRespondInline(String text) {
133446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        appendToBody(text, false);
133546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
133646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
133746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
133846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Append text to the body of the message. If there is no existing body
133946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * text, just sets the body to text.
134046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *
134146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param text
134246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param withSignature True to append a signature.
134346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
134446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void appendToBody(CharSequence text, boolean withSignature) {
1345eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        Editable bodyText = mBodyView.getEditableText();
134646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (bodyText != null && bodyText.length() > 0) {
134746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            bodyText.append(text);
134846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
134946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            setBody(text, withSignature);
135046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
135146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
135246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
135346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
135446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Set the body of the message.
135546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param text
135646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param withSignature True to append a signature.
135746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
135846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void setBody(CharSequence text, boolean withSignature) {
1359eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBodyView.setText(text);
136046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
13611a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
13625a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    @Override
13635a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    public void onAccountChanged() {
13645a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        Account selectedAccountInfo = mFromSpinner.getCurrentAccount();
1365181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!mAccount.equals(selectedAccountInfo)) {
1366181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mAccount = selectedAccountInfo;
1367181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mCachedSettings = null;
1368181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            getLoaderManager().restartLoader(ACCOUNT_SETTINGS_LOADER, null, this);
1369181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // TODO: handle discarding attachments when switching accounts.
1370181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // Only enable save for this draft if there is any other content
1371181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // in the message.
1372181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (!isBlank()) {
1373181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                enableSave(true);
1374181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1375181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mReplyFromChanged = true;
1376181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            initRecipients();
137782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
13781a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
137982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
138082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void enableSave(boolean enabled) {
138182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSave != null) {
138282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave.setEnabled(enabled);
138382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
138482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
138582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
138682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void enableSend(boolean enabled) {
138782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSend != null) {
138882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSend.setEnabled(enabled);
138982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
139082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
139182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
139282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
139382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Handles button clicks from any error dialogs dealing with sending
139482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * a message.
139582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
139682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    @Override
139782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void onClick(DialogInterface dialog, int which) {
139882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        switch (which) {
139982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            case DialogInterface.BUTTON_POSITIVE: {
140082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                doDiscardWithoutConfirmation(true /* show toast */ );
140182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                break;
140282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
140382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            case DialogInterface.BUTTON_NEGATIVE: {
140482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // If the user cancels the send, re-enable the send button.
140582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                enableSend(true);
140682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                break;
140782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
140882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
140982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
141082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
141182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
141282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
141382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Effectively discard the current message.
141482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     *
141582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * This method is either invoked from the menu or from the dialog
141682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * once the user has confirmed that they want to discard the message.
141782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param showToast show "Message discarded" toast if true
141882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
141982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private void doDiscardWithoutConfirmation(boolean showToast) {
14207ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        synchronized (mDraftLock) {
142182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (mDraftId != UIProvider.INVALID_MESSAGE_ID) {
142282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                ContentValues values = new ContentValues();
142382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                values.put(MessageColumns.SERVER_ID, mDraftId);
1424cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                if (mAccount.expungeMessageUri != null) {
1425cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    getContentResolver().update(mAccount.expungeMessageUri, values, null, null);
1426cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                } else {
1427cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    // TODO(mindyp): call delete on this conversation instead.
1428cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                }
142982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // This is not strictly necessary (since we should not try to
143082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // save the draft after calling this) but it ensures that if we
143182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // do save again for some reason we make a new draft rather than
143282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // trying to resave an expunged draft.
143382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mDraftId = UIProvider.INVALID_MESSAGE_ID;
143482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
143582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
143682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
143782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (showToast) {
143882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // Display a toast to let the user know
143982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            Toast.makeText(this, R.string.message_discarded, Toast.LENGTH_SHORT).show();
144082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
144182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
144282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // This prevents the draft from being saved in onPause().
144382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        discardChanges();
144482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        finish();
144582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
144682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1447eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void saveIfNeeded() {
1448eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mAccount == null) {
1449eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // We have not chosen an account yet so there's no way that we can save. This is ok,
1450eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // though, since we are saving our state before AccountsActivity is activated. Thus, the
1451eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // user has not interacted with us yet and there is no real state to save.
1452eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return;
1453eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1454eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1455eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (shouldSave()) {
1456eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            doSave(!mAddingAttachment /* show toast */, true /* reset IME */);
1457eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1458eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1459eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1460eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void saveIfNeededOnOrientationChanged() {
1461eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mAccount == null) {
1462eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // We have not chosen an account yet so there's no way that we can save. This is ok,
1463eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // though, since we are saving our state before AccountsActivity is activated. Thus, the
1464eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // user has not interacted with us yet and there is no real state to save.
1465eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return;
1466eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1467eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1468eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (shouldSave()) {
1469eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            doSaveOrientationChanged(!mAddingAttachment /* show toast */, true /* reset IME */);
1470eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1471eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1472eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1473eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    /**
1474eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira     * Save a draft if a draft already exists or the message is not empty.
1475eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira     */
1476eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void doSaveOrientationChanged(boolean showToast, boolean resetIME) {
1477eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveOnOrientationChanged();
1478eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (resetIME) {
1479eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // Clear the IME composing suggestions from the body.
1480eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            BaseInputConnection.removeComposingSpans(mBodyView.getEditableText());
1481eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1482eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1483eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1484eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected boolean saveOnOrientationChanged() {
1485eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        return sendOrSaveWithSanityChecks(true, false, true);
1486eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1487eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1488eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
1489eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void onAttachmentDeleted() {
1490eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsChanged = true;
1491eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        updateSaveUi();
1492eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
149375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
149475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
149575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    /**
149675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira     * This is called any time one of our text fields changes.
149775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira     */
149875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void afterTextChanged(Editable s) {
149975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mTextChanged = true;
150075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        updateSaveUi();
150175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
150275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
150375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    @Override
150475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
150575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        // Do nothing.
150675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
150775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
150875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void onTextChanged(CharSequence s, int start, int before, int count) {
150975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        // Do nothing.
151075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
151175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
151275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
151375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // There is a big difference between the text associated with an address changing
151475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // to add the display name or to format properly and a recipient being added or deleted.
151575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // Make sure we only notify of changes when a recipient has been added or deleted.
151675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    private class RecipientTextWatcher implements TextWatcher {
151775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private HashMap<String, Integer> mContent = new HashMap<String, Integer>();
151875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
151975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private RecipientEditTextView mView;
152075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
152175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private TextWatcher mListener;
152275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
152375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public RecipientTextWatcher(RecipientEditTextView view, TextWatcher listener) {
152475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mView = view;
152575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mListener = listener;
152675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
152775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
152875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
152975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void afterTextChanged(Editable s) {
153075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            if (hasChanged()) {
153175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                mListener.afterTextChanged(s);
153275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
153375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
153475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
153575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private boolean hasChanged() {
153675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] currRecips = tokenizeRecips(getAddressesFromList(mView));
153775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            int totalCount = currRecips.length;
153875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            int totalPrevCount = 0;
153975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (Entry<String, Integer> entry : mContent.entrySet()) {
154075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                totalPrevCount += entry.getValue();
154175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
154275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            if (totalCount != totalPrevCount) {
154375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                return true;
154475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
154575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
154675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (String recip : currRecips) {
154775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                if (!mContent.containsKey(recip)) {
154875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    return true;
154975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                } else {
155075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    int count = mContent.get(recip) - 1;
155175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    if (count < 0) {
155275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                        return true;
155375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    } else {
155475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                        mContent.put(recip, count);
155575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    }
155675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                }
155775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
155875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            return false;
155975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
156075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
156175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private String[] tokenizeRecips(String[] recips) {
156275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            // Tokenize them all and put them in the list.
156375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] recipAddresses = new String[recips.length];
156475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (int i = 0; i < recips.length; i++) {
156575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                recipAddresses[i] = Rfc822Tokenizer.tokenize(recips[i])[0].getAddress();
156675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
156775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            return recipAddresses;
156875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
156975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
157075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
157175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
157275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] recips = tokenizeRecips(getAddressesFromList(mView));
157375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (String recip : recips) {
157475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                if (!mContent.containsKey(recip)) {
157575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    mContent.put(recip, 1);
157675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                } else {
157775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    mContent.put(recip, (mContent.get(recip)) + 1);
157875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                }
157975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
158075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
158175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
158275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
158375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void onTextChanged(CharSequence s, int start, int before, int count) {
158475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            // Do nothing.
158575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
158675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
1587181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1588181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    @Override
1589181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
1590181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (id == ACCOUNT_SETTINGS_LOADER) {
1591181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (mAccount.settingsQueryUri != null) {
1592181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                return new CursorLoader(this, mAccount.settingsQueryUri,
1593181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        UIProvider.SETTINGS_PROJECTION, null, null, null);
1594181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1595181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1596181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return null;
1597181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1598181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1599181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    @Override
1600181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
1601181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (loader.getId() == ACCOUNT_SETTINGS_LOADER) {
1602181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (data != null) {
1603181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                data.moveToFirst();
1604181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                mCachedSettings = new Settings(data);
1605181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1606181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1607181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1608181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1609181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    @Override
1610181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public void onLoaderReset(Loader<Cursor> loader) {
1611181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Do nothing.
1612181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
16138e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira}