ComposeActivity.java revision 23e9fdec6872f9b4d0fbe675745cb25becf7878c
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;
39f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrookimport android.os.Parcelable;
4082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.provider.BaseColumns;
4146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport android.text.Editable;
4282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.Html;
4382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.Spanned;
44c182762df95fd382faf8e4a25e5cf7783ea10176Paul Westbrookimport android.text.TextUtils;
4582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.TextWatcher;
4646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport android.text.util.Rfc822Token;
47c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport android.text.util.Rfc822Tokenizer;
48326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.LayoutInflater;
49b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.Menu;
50b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.MenuInflater;
51b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.MenuItem;
528e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.view.View;
53326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.ViewGroup;
548e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.view.View.OnClickListener;
55eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereiraimport android.view.inputmethod.BaseInputConnection;
56326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.widget.ArrayAdapter;
578e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.widget.Button;
581f93668e1186d48b507207841c1ca0529c3de292Mindy Pereiraimport android.widget.ImageView;
596349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.widget.TextView;
60013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereiraimport android.widget.Toast;
617b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira
62c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport com.android.common.Rfc822Validator;
63eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereiraimport com.android.mail.compose.AttachmentsView.AttachmentDeletedListener;
649932dee248718d2cec993d93b5115973d1389d0aMindy Pereiraimport com.android.mail.compose.AttachmentsView.AttachmentFailureException;
655a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereiraimport com.android.mail.compose.FromAddressSpinner.OnAccountChangedListener;
6630e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.compose.QuotedTextView.RespondInlineListener;
6733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereiraimport com.android.mail.providers.Account;
6830e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.Address;
6930e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.Attachment;
703ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereiraimport com.android.mail.providers.Message;
7182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.android.mail.providers.MessageModification;
72181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereiraimport com.android.mail.providers.Settings;
7330e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.UIProvider;
7482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.android.mail.providers.UIProvider.MessageColumns;
7530e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.R;
7692227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrookimport com.android.mail.utils.AccountUtils;
7730e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.utils.LogUtils;
7830e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.utils.Utils;
79c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport com.android.ex.chips.RecipientEditTextView;
8046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport com.google.common.annotations.VisibleForTesting;
8182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.google.common.collect.Lists;
824a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereiraimport com.google.common.collect.Sets;
838e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
8446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.ArrayList;
8546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.Collection;
8675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereiraimport java.util.HashMap;
8746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.HashSet;
8846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.List;
894a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereiraimport java.util.Set;
9075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereiraimport java.util.Map.Entry;
9182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport java.util.concurrent.ConcurrentHashMap;
9246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
9346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereirapublic class ComposeActivity extends Activity implements OnClickListener, OnNavigationListener,
945a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        RespondInlineListener, DialogInterface.OnClickListener, TextWatcher,
95181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        AttachmentDeletedListener, OnAccountChangedListener, LoaderCallbacks<Cursor> {
966349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    // Identifiers for which type of composition this is
976349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int COMPOSE = -1;  // also used for editing a draft
986349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int REPLY = 0;
996349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int REPLY_ALL = 1;
1006349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int FORWARD = 2;
1016349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1026349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    // Integer extra holding one of the above compose action
1036349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private static final String EXTRA_ACTION = "action";
1046349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
10582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static SendOrSaveCallback sTestSendOrSaveCallback = null;
10682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Map containing information about requests to create new messages, and the id of the
10782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // messages that were the result of those requests.
10882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    //
10982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // This map is used when the activity that initiated the save a of a new message, is killed
11082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // before the save has completed (and when we know the id of the newly created message).  When
11182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // a save is completed, the service that is running in the background, will update the map
11282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    //
11382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // When a new ComposeActivity instance is created, it will attempt to use the information in
11482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // the previously instantiated map.  If ComposeActivity.onCreate() is called, with a bundle
11582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // (restoring data from a previous instance), and the map hasn't been created, we will attempt
11682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // to populate the map with data stored in shared preferences.
11782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static ConcurrentHashMap<Integer, Long> sRequestMessageIdMap = null;
11882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Key used to store the above map
11982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static final String CACHED_MESSAGE_REQUEST_IDS_KEY = "cache-message-request-ids";
1206349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1216349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Notifies the {@code Activity} that the caller is an Email
1226349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * {@code Activity}, so that the back behavior may be modified accordingly.
1236349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     *
1246349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * @see #onAppUpPressed
1256349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
1266349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private static final String EXTRA_FROM_EMAIL_TASK = "fromemail";
1276349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
128f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook    static final String EXTRA_ATTACHMENTS = "attachments";
129f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook
1303ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    //  If this is a reply/forward then this extra will hold the original message
1313ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private static final String EXTRA_IN_REFERENCE_TO_MESSAGE = "in-reference-to-message";
1324a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private static final String END_TOKEN = ", ";
133013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final String LOG_TAG = new LogUtils().getLogTag();
134013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    // Request numbers for activities we start
135013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final int RESULT_PICK_ATTACHMENT = 1;
136013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final int RESULT_CREATE_ACCOUNT = 2;
137181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private static final int ACCOUNT_SETTINGS_LOADER = 0;
1388e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
13982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
14082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * A single thread for running tasks in the background.
14182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
14282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Handler mSendSaveTaskHandler = null;
143c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mTo;
144c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mCc;
145c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mBcc;
1468e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    private Button mCcBccButton;
1478e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    private CcBccView mCcBccView;
1487b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira    private AttachmentsView mAttachmentsView;
14933fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private Account mAccount;
150181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private Settings mCachedSettings;
15182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Rfc822Validator mValidator;
1526349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private TextView mSubject;
1536349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
154326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private ComposeModeAdapter mComposeModeAdapter;
155326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private int mComposeMode = -1;
15646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private boolean mForward;
15746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private String mRecipient;
15846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private QuotedTextView mQuotedTextView;
159eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private TextView mBodyView;
1601a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    private View mFromStatic;
1612eb173211741adf7454bbf1990ba1b9d1e1f6a64Mindy Pereira    private TextView mFromStaticText;
162eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private View mFromSpinnerWrapper;
1635a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    private FromAddressSpinner mFromSpinner;
164013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private boolean mAddingAttachment;
165eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private boolean mAttachmentsChanged;
16682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean mTextChanged;
16782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean mReplyFromChanged;
16882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private MenuItem mSave;
16982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private MenuItem mSend;
1703ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private String mRefMessageId;
17182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private AlertDialog mRecipientErrorDialog;
172eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private AlertDialog mSendConfirmDialog;
1733ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private Message mRefMessage;
1747ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira    private long mDraftId = UIProvider.INVALID_MESSAGE_ID;
1757ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira    private Message mDraft;
1767ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira    private Object mDraftLock = new Object();
1771f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira    private ImageView mAttachmentsButton;
1783ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira
179326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    /**
180326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira     * Can be called from a non-UI thread.
181326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira     */
1823ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void editDraft(Context launcher, Account account, Message message) {
183326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
184326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1856349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1866349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1876349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
18833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void compose(Context launcher, Account account) {
1896349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, null, COMPOSE);
1906349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1916349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1926349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1936349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1946349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
1953ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void reply(Context launcher, Account account, Message message) {
1963ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        launch(launcher, account, message, REPLY);
1976349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1986349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1996349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
2006349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
2016349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
2023ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void replyAll(Context launcher, Account account, Message message) {
2033ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        launch(launcher, account, message, REPLY_ALL);
2046349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2056349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
2066349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
2076349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
2086349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
2093ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    public static void forward(Context launcher, Account account, Message message) {
2103ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        launch(launcher, account, message, FORWARD);
2116349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2126349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
2133ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private static void launch(Context launcher, Account account, Message message, int action) {
2146349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        Intent intent = new Intent(launcher, ComposeActivity.class);
2156349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
2166349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_ACTION, action);
2176349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account);
2183ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        intent.putExtra(EXTRA_IN_REFERENCE_TO_MESSAGE, message);
2196349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launcher.startActivity(intent);
2206349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2218e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
2228e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    @Override
2238e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    public void onCreate(Bundle savedInstanceState) {
2248e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        super.onCreate(savedInstanceState);
2258e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        setContentView(R.layout.compose);
22646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        findViews();
227818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        Intent intent = getIntent();
22892227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook
22992227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook        Account account = (Account)intent.getParcelableExtra(Utils.EXTRA_ACCOUNT);
23092227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook        if (account == null) {
23192227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook            final Account[] syncingAccounts = AccountUtils.getSyncingAccounts(this);
23292227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook            if (syncingAccounts.length > 0) {
23392227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook                account = syncingAccounts[0];
23492227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook            }
23592227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook        }
23692227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook
23792227f6b35b3bec54609dc0feb9dd6756c2a43e6Paul Westbrook        setAccount(account);
238818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        if (mAccount == null) {
239818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira            return;
240818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        }
24146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        int action = intent.getIntExtra(EXTRA_ACTION, COMPOSE);
2423ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        mRefMessage = (Message) intent.getParcelableExtra(EXTRA_IN_REFERENCE_TO_MESSAGE);
24329ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        if ((action == REPLY || action == REPLY_ALL || action == FORWARD)) {
24433fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            initFromRefMessage(action, mAccount.name);
24546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
246eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mQuotedTextView.setVisibility(View.GONE);
24746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
248818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        initRecipients();
249f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook        initAttachmentsFromIntent(intent);
2501a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        initActionBar(action);
2515a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        initFromSpinner();
25275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        initChangeListeners();
2531a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
2541a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
2551a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    @Override
2561a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    protected void onResume() {
2571a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        super.onResume();
2581a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        // Update the from spinner as other accounts
2591a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        // may now be available.
260818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        if (mFromSpinner != null && mAccount != null) {
261818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira            mFromSpinner.asyncInitFromSpinner();
262818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        }
2631a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
2641a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
265eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
266eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected void onPause() {
267eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        super.onPause();
268eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
269eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mSendConfirmDialog != null) {
270eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mSendConfirmDialog.dismiss();
271eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
272eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mRecipientErrorDialog != null) {
273eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mRecipientErrorDialog.dismiss();
274eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
275eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
276eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveIfNeeded();
277eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
278eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
279eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
280eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected final void onActivityResult(int request, int result, Intent data) {
281eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAddingAttachment = false;
282eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
283eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (result == RESULT_OK && request == RESULT_PICK_ATTACHMENT) {
284eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            addAttachmentAndUpdateView(data);
285eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
286eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
287eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
288eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
289eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public final void onSaveInstanceState(Bundle state) {
290eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        super.onSaveInstanceState(state);
291eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
292eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // onSaveInstanceState is only called if the user might come back to this activity so it is
293eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // not an ideal location to save the draft. However, if we have never saved the draft before
294eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // we have to save it here in order to have an id to save in the bundle.
295eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveIfNeededOnOrientationChanged();
296eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
297eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
298818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    @VisibleForTesting
299818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    void setAccount(Account account) {
30023e9fdec6872f9b4d0fbe675745cb25becf7878cMindy Pereira        assert(account != null);
30123e9fdec6872f9b4d0fbe675745cb25becf7878cMindy Pereira        if (!account.equals(mAccount)) {
30223e9fdec6872f9b4d0fbe675745cb25becf7878cMindy Pereira            mAccount = account;
30323e9fdec6872f9b4d0fbe675745cb25becf7878cMindy Pereira        }
304181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        getLoaderManager().restartLoader(ACCOUNT_SETTINGS_LOADER, null, this);
305818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    }
306818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira
3071a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    private void initFromSpinner() {
3085a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.setCurrentAccount(mAccount);
3095a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.asyncInitFromSpinner();
310eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        boolean showSpinner = mFromSpinner.getCount() > 1;
311eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // If there is only 1 account, just show that account.
312eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // Otherwise, give the user the ability to choose which account to send
313eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // mail from / save drafts to.
314eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromStatic.setVisibility(
315eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                showSpinner ? View.GONE : View.VISIBLE);
3162eb173211741adf7454bbf1990ba1b9d1e1f6a64Mindy Pereira        mFromStaticText.setText(mAccount.name);
317eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromSpinnerWrapper.setVisibility(
318eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                showSpinner ? View.VISIBLE : View.GONE);
31946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
32046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
32146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void findViews() {
322a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        mCcBccButton = (Button) findViewById(R.id.add_cc_bcc);
3238e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        if (mCcBccButton != null) {
3248e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira            mCcBccButton.setOnClickListener(this);
3258e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        }
3268e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        mCcBccView = (CcBccView) findViewById(R.id.cc_bcc_wrapper);
3277b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira        mAttachmentsView = (AttachmentsView)findViewById(R.id.attachments);
3281f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        mAttachmentsButton = (ImageView) findViewById(R.id.add_attachment);
3291f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        if (mAttachmentsButton != null) {
3301f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira            mAttachmentsButton.setOnClickListener(this);
3311f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira        }
332818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mTo = (RecipientEditTextView) findViewById(R.id.to);
333818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mCc = (RecipientEditTextView) findViewById(R.id.cc);
334818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mBcc = (RecipientEditTextView) findViewById(R.id.bcc);
33582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // TODO: add special chips text change watchers before adding
33682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // this as a text changed watcher to the to, cc, bcc fields.
3376349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        mSubject = (TextView) findViewById(R.id.subject);
33846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mQuotedTextView = (QuotedTextView) findViewById(R.id.quoted_text_view);
33946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mQuotedTextView.setRespondInlineListener(this);
340eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBodyView = (TextView) findViewById(R.id.body);
3411a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        mFromStatic = findViewById(R.id.static_from_content);
3422eb173211741adf7454bbf1990ba1b9d1e1f6a64Mindy Pereira        mFromStaticText = (TextView) findViewById(R.id.from_account_name);
343eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromSpinnerWrapper = findViewById(R.id.spinner_from_content);
3445a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner = (FromAddressSpinner) findViewById(R.id.from_picker);
345326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
346326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
34775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // Now that the message has been initialized from any existing draft or
34875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // ref message data, set up listeners for any changes that occur to the
34975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // message.
35075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    private void initChangeListeners() {
35175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mSubject.addTextChangedListener(this);
35275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mBodyView.addTextChangedListener(this);
35375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mTo.addTextChangedListener(new RecipientTextWatcher(mTo, this));
35475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mCc.addTextChangedListener(new RecipientTextWatcher(mCc, this));
35575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mBcc.addTextChangedListener(new RecipientTextWatcher(mBcc, this));
35675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mFromSpinner.setOnAccountChangedListener(this);
357818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        mAttachmentsView.setAttachmentChangesListener(this);
35875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
35975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
360326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private void initActionBar(int action) {
361326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        mComposeMode = action;
362eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        ActionBar actionBar = getActionBar();
363326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        if (action == ComposeActivity.COMPOSE) {
364eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
365eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setTitle(R.string.compose);
366326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else {
367eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setTitle(null);
368326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (mComposeModeAdapter == null) {
369326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                mComposeModeAdapter = new ComposeModeAdapter(this);
370326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
371eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
372eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setListNavigationCallbacks(mComposeModeAdapter, this);
373326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            switch (action) {
374326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.REPLY:
375eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(0);
376326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
377326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.REPLY_ALL:
378eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(1);
379326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
380326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.FORWARD:
381eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(2);
382326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
383326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
3846349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        }
385fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME,
386fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME);
387fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        actionBar.setHomeButtonEnabled(true);
3886349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
3896349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
39046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void initFromRefMessage(int action, String recipientAddress) {
3913ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        if (mRefMessage != null) {
3923ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            mRefMessageId = mRefMessage.refMessageId;
3933ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            setSubject(mRefMessage, action);
3943ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            // Setup recipients
3953ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            if (action == FORWARD) {
3963ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                mForward = true;
3973ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            }
3983ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            initRecipientsFromRefMessage(recipientAddress, mRefMessage, action);
3993ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            initBodyFromRefMessage(mRefMessage, action);
4003ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            if (action == ComposeActivity.FORWARD || mAttachmentsChanged) {
4013ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                initAttachments(mRefMessage);
40246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            }
4033ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            updateHideOrShowCcBcc();
4046349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        }
405c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    }
406c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira
4073ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private void initAttachments(Message refMessage) {
4087a07fb4955e34250225287212b07d1b6f12bf671Mindy Pereira        mAttachmentsView.addAttachments(mAccount, refMessage);
409eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
410eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
411f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook    private void initAttachmentsFromIntent(Intent intent) {
412f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook        final Bundle extras = intent.getExtras();
413f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook        final String action = intent.getAction();
414f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook        if (!mAttachmentsChanged) {
415f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            long totalSize = 0;
416f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            if (extras.containsKey(EXTRA_ATTACHMENTS)) {
417f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                String[] uris = (String[]) extras.getSerializable(EXTRA_ATTACHMENTS);
418f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                for (String uriString : uris) {
419f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    final Uri uri = Uri.parse(uriString);
420f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    long size = 0;
421f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    try {
422f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        size =  mAttachmentsView.addAttachment(mAccount, uri, false /* doSave */,
423f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                                true /* local file */);
424f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    } catch (AttachmentFailureException e) {
425f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        // A toast has already been shown to the user,
426f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        // just break out of the loop.
427f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        LogUtils.e(LOG_TAG, e, "Error adding attachment");
428f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    }
429f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    totalSize += size;
430f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                }
431f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            }
432f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            if (Intent.ACTION_SEND.equals(action) && extras.containsKey(Intent.EXTRA_STREAM)) {
433f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                final Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);
434f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                long size = 0;
435f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                try {
436f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    size =  mAttachmentsView.addAttachment(mAccount, uri, false /* doSave */,
437f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                            true /* local file */);
438f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                } catch (AttachmentFailureException e) {
439f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    // A toast has already been shown to the user, so just
440f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    // exit.
441f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    LogUtils.e(LOG_TAG, e, "Error adding attachment");
442f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                }
443f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                totalSize += size;
444f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            }
445f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook
446f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            if (Intent.ACTION_SEND_MULTIPLE.equals(action)
447f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    && extras.containsKey(Intent.EXTRA_STREAM)) {
448f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                ArrayList<Parcelable> uris = extras.getParcelableArrayList(Intent.EXTRA_STREAM);
449f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                for (Parcelable uri : uris) {
450f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    long size = 0;
451f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    try {
452f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        size = mAttachmentsView.addAttachment(mAccount, (Uri)uri,
453f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                                false /* doSave */, true /* local file */);
454f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    } catch (AttachmentFailureException e) {
455f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        // A toast has already been shown to the user,
456f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        // just break out of the loop.
457f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                        LogUtils.e(LOG_TAG, e, "Error adding attachment");
458f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    }
459f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                    totalSize += size;
460f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                }
461f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            }
462f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook
463f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            if (totalSize > 0) {
464f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                mAttachmentsChanged = true;
465f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook                updateSaveUi();
466f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook            }
467f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook        }
468f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook    }
469f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook
470f97588bd03fdcd318e24a9d74a283e73d1fbd37cPaul Westbrook
4713ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private void initBodyFromRefMessage(Message refMessage, int action) {
4729932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        if (action == REPLY || action == REPLY_ALL || action == FORWARD) {
4739932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            mQuotedTextView.setQuotedText(action, refMessage, action != FORWARD);
4749932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        }
47546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
47646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
47746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void updateHideOrShowCcBcc() {
478ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        // Its possible there is a menu item OR a button.
479a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        boolean ccVisible = !TextUtils.isEmpty(mCc.getText());
480a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        boolean bccVisible = !TextUtils.isEmpty(mBcc.getText());
481a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (ccVisible || bccVisible) {
482a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            mCcBccView.show(false, ccVisible, bccVisible);
483a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        }
484ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (mCcBccButton != null) {
485ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            if (!mCc.isShown() || !mBcc.isShown()) {
486ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setVisibility(View.VISIBLE);
487ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setText(getString(!mCc.isShown() ? R.string.add_cc_label
488ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                        : R.string.add_bcc_label));
489ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            } else {
490ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setVisibility(View.GONE);
491ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            }
492ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
49346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
49446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
495013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    /**
496013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     * Add attachment and update the compose area appropriately.
497013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     * @param data
498013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     */
499013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    public void addAttachmentAndUpdateView(Intent data) {
500013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        Uri uri = data != null ? data.getData() : null;
501013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        try {
502f944e9655562e321aff52fe5f437e1d2fa2950a9Mindy Pereira            long size =  mAttachmentsView.addAttachment(mAccount, uri, false /* doSave */,
503f944e9655562e321aff52fe5f437e1d2fa2950a9Mindy Pereira                    true /* local file */);
5049932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            if (size > 0) {
5059932dee248718d2cec993d93b5115973d1389d0aMindy Pereira                mAttachmentsChanged = true;
5069932dee248718d2cec993d93b5115973d1389d0aMindy Pereira                updateSaveUi();
507013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            }
5089932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        } catch (AttachmentFailureException e) {
5099932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            // A toast has already been shown to the user, no need to do
5109932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            // anything.
5119932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            LogUtils.e(LOG_TAG, e, "Error adding attachment");
512013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        }
513013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    }
514013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira
5153ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    void initRecipientsFromRefMessage(String recipientAddress, Message refMessage,
51646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            int action) {
5174a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // Don't populate the address if this is a forward.
5184a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        if (action == ComposeActivity.FORWARD) {
5194a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            return;
5204a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
52133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        initReplyRecipients(mAccount.name, refMessage, action);
5224a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5234a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
524818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    @VisibleForTesting
5253ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    void initReplyRecipients(String account, Message refMessage, int action) {
5264a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // This is the email address of the current user, i.e. the one composing
5274a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // the reply.
5284a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira        final String accountEmail = Address.getEmailAddress(account).getAddress();
5293ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String fromAddress = refMessage.from;
5303ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String[] sentToAddresses = Utils.splitCommaSeparatedString(refMessage.to);
5313ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String replytoAddress = refMessage.replyTo;
532a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        final Collection<String> toAddresses;
5334a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5344a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // If this is a reply, the Cc list is empty. If this is a reply-all, the
5354a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // Cc list is the union of the To and Cc recipients of the original
5364a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // message, excluding the current user's email address and any addresses
537a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        // already on the To list.
538a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (action == ComposeActivity.REPLY) {
5393ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses = initToRecipients(account, accountEmail, fromAddress, replytoAddress,
5403ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                    new String[0]);
541a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            addToAddresses(toAddresses);
542a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        } else if (action == ComposeActivity.REPLY_ALL) {
5434a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            final Set<String> ccAddresses = Sets.newHashSet();
5443ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses = initToRecipients(account, accountEmail, fromAddress, replytoAddress,
5453ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                    new String[0]);
546154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            addToAddresses(toAddresses);
5474a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addRecipients(accountEmail, ccAddresses, sentToAddresses);
5483ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            addRecipients(accountEmail, ccAddresses,
5493ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                    Utils.splitCommaSeparatedString(refMessage.cc));
5504a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addCcAddresses(ccAddresses, toAddresses);
5514a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5524a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5534a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5544a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addToAddresses(Collection<String> addresses) {
5554a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        addAddressesToList(addresses, mTo);
5564a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5574a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5584a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addCcAddresses(Collection<String> addresses, Collection<String> toAddresses) {
5594a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        addCcAddressesToList(tokenizeAddressList(addresses), tokenizeAddressList(toAddresses),
5604a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                mCc);
5614a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5624a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5634a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
5644a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected void addCcAddressesToList(List<Rfc822Token[]> addresses,
5654a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            List<Rfc822Token[]> compareToList, RecipientEditTextView list) {
5664a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String address;
5674a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5684a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        HashSet<String> compareTo = convertToHashSet(compareToList);
5694a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (Rfc822Token[] tokens : addresses) {
5704a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            for (int i = 0; i < tokens.length; i++) {
5714a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                address = tokens[i].toString();
5724a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // Check if this is a duplicate:
5734a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                if (!compareTo.contains(tokens[i].getAddress())) {
5744a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    // Get the address here
5754a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    list.append(address + END_TOKEN);
5764a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                }
5774a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
5784a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5794a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5804a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5814a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private HashSet<String> convertToHashSet(List<Rfc822Token[]> list) {
5824a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        HashSet<String> hash = new HashSet<String>();
5834a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (Rfc822Token[] tokens : list) {
5844a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            for (int i = 0; i < tokens.length; i++) {
5854a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                hash.add(tokens[i].getAddress());
5864a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
5874a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5884a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return hash;
5894a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5904a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5914a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected List<Rfc822Token[]> tokenizeAddressList(Collection<String> addresses) {
5924a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        @VisibleForTesting
5934a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        List<Rfc822Token[]> tokenized = new ArrayList<Rfc822Token[]>();
5944a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5954a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String address: addresses) {
5964a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            tokenized.add(Rfc822Tokenizer.tokenize(address));
5974a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5984a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return tokenized;
5994a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
6004a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
6014a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
6024a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    void addAddressesToList(Collection<String> addresses, RecipientEditTextView list) {
6034a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String address : addresses) {
6044a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addAddressToList(address, list);
6054a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
6064a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
6074a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
6084a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addAddressToList(String address, RecipientEditTextView list) {
6094a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        if (address == null || list == null)
6104a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            return;
6114a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
6124a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);
6134a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
6144a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (int i = 0; i < tokens.length; i++) {
6154a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            list.append(tokens[i] + END_TOKEN);
6164a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
6174a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
6184a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
6194a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
6204a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected Collection<String> initToRecipients(String account, String accountEmail,
6213ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            String senderAddress, String replyToAddress, String[] inToAddresses) {
6224a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // The To recipient is the reply-to address specified in the original
6234a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // message, unless it is:
6244a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // the current user OR a custom from of the current user, in which case
6254a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // it's the To recipient list of the original message.
6264a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // OR missing, in which case use the sender of the original message
6274a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        Set<String> toAddresses = Sets.newHashSet();
6283ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        if (!TextUtils.isEmpty(replyToAddress)) {
6293ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses.add(replyToAddress);
6304a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        } else {
6313ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            toAddresses.add(senderAddress);
6324a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
6334a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return toAddresses;
6344a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
6354a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
6364a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private static void addRecipients(String account, Set<String> recipients, String[] addresses) {
6374a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String email : addresses) {
6384a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // Do not add this account, or any of the custom froms, to the list
6394a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // of recipients.
6404a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira            final String recipientAddress = Address.getEmailAddress(email).getAddress();
6414a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            if (!account.equalsIgnoreCase(recipientAddress)) {
6424a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                recipients.add(email.replace("\"\"", ""));
6434a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
6444a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
64546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
64646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
6473ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira    private void setSubject(Message refMessage, int action) {
6483ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        String subject = refMessage.subject;
64946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String prefix;
65046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String correctedSubject = null;
65146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (action == ComposeActivity.COMPOSE) {
65246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = "";
65346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else if (action == ComposeActivity.FORWARD) {
65446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = getString(R.string.forward_subject_label);
65546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
65646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = getString(R.string.reply_subject_label);
65746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
65846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
65946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        // Don't duplicate the prefix
66046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (subject.toLowerCase().startsWith(prefix.toLowerCase())) {
66146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            correctedSubject = subject;
66246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
66346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            correctedSubject = String
66446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                    .format(getString(R.string.formatted_subject), prefix, subject);
66546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
66646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mSubject.setText(correctedSubject);
66746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
66846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
669818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    private void initRecipients() {
670818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        setupRecipients(mTo);
671818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        setupRecipients(mCc);
672818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        setupRecipients(mBcc);
673eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
674eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
675818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira    private void setupRecipients(RecipientEditTextView view) {
676679a8cc895ec75d3b578dbc77db1e9c04dd7f8b0Paul Westbrook        view.setAdapter(new RecipientAdapter(this, mAccount));
677c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        view.setTokenizer(new Rfc822Tokenizer());
67882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mValidator == null) {
679679a8cc895ec75d3b578dbc77db1e9c04dd7f8b0Paul Westbrook            final String accountName = mAccount.name;
68033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            int offset = accountName.indexOf("@") + 1;
68133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            String account = accountName;
682c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira            if (offset > -1) {
68333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                account = account.substring(accountName.indexOf("@") + 1);
684c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira            }
68582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mValidator = new Rfc822Validator(account);
686c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        }
68782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        view.setValidator(mValidator);
6888e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    }
6898e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
6908e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    @Override
6918e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    public void onClick(View v) {
6928e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        int id = v.getId();
6938e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        switch (id) {
694a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            case R.id.add_cc_bcc:
6958e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                // Verify that cc/ bcc aren't showing.
6968e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                // Animate in cc/bcc.
697a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira                showCcBccViews();
6988e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                break;
6991f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira            case R.id.add_attachment:
7001f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira                doAttach();
7011f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira                break;
7028e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        }
7038e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    }
704b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira
705b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    @Override
706b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    public boolean onCreateOptionsMenu(Menu menu) {
707b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        super.onCreateOptionsMenu(menu);
708b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        MenuInflater inflater = getMenuInflater();
709b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        inflater.inflate(R.menu.compose_menu, menu);
71082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSave = menu.findItem(R.id.save);
71182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSend = menu.findItem(R.id.send);
712b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        return true;
713b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    }
714b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira
715b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    @Override
716ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    public boolean onPrepareOptionsMenu(Menu menu) {
717ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        MenuItem ccBcc = menu.findItem(R.id.add_cc_bcc);
718818143e2163648acc0c91f123b84777d65cd70cfMindy Pereira        if (ccBcc != null && mCc != null) {
719ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            // Its possible there is a menu item OR a button.
720ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            boolean ccFieldVisible = mCc.isShown();
721ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            boolean bccFieldVisible = mBcc.isShown();
722ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            if (!ccFieldVisible || !bccFieldVisible) {
723ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setVisible(true);
724ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setTitle(getString(!ccFieldVisible ? R.string.add_cc_label
725ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                        : R.string.add_bcc_label));
726ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            } else {
727ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setVisible(false);
728ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            }
729ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
73075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        if (mSave != null) {
73175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mSave.setEnabled(shouldSave());
73275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
733ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        return true;
734ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    }
735ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira
736ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    @Override
737b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    public boolean onOptionsItemSelected(MenuItem item) {
738b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        int id = item.getItemId();
73975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        boolean handled = true;
740b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        switch (id) {
7417b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira            case R.id.add_attachment:
742013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira                doAttach();
7437b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira                break;
744ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            case R.id.add_cc_bcc:
745ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                showCcBccViews();
746b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira                break;
74733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            case R.id.save:
748eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                doSave(true, false);
74933fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                break;
75033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            case R.id.send:
75133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                doSend();
75275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                break;
753efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira            case R.id.discard:
754efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira                doDiscard();
755efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira                break;
7561f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira            case R.id.settings:
7571f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira                Utils.showSettings(this, mAccount);
7581f93668e1186d48b507207841c1ca0529c3de292Mindy Pereira                break;
759fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira            case android.R.id.home:
760fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                finish();
761fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                break;
762fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira            case R.id.help_info_menu_item:
763fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                // TODO: enable context sensitive help
764fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                Utils.showHelp(this, mAccount.helpIntentUri, null);
765fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                break;
766fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira            case R.id.feedback_menu_item:
767fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                Utils.sendFeedback(this, mAccount);
768fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira                break;
76975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            default:
77075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                handled = false;
77133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                break;
772b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        }
773b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        return !handled ? super.onOptionsItemSelected(item) : handled;
774b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    }
775326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
77633fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private void doSend() {
77782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        sendOrSaveWithSanityChecks(false, true, false);
77833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    }
77933fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira
780eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void doSave(boolean showToast, boolean resetIME) {
781eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        sendOrSaveWithSanityChecks(true, showToast, false);
782eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (resetIME) {
783eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // Clear the IME composing suggestions from the body.
784eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            BaseInputConnection.removeComposingSpans(mBodyView.getEditableText());
785eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
78682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
78782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
78882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ interface SendOrSaveCallback {
78982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask);
7907ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        public void notifyMessageIdAllocated(SendOrSaveMessage sendOrSaveMessage, Message message);
7917ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        public Message getMessage();
79282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void sendOrSaveFinished(SendOrSaveTask sendOrSaveTask, boolean success);
79382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
79482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
79582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ static class SendOrSaveTask implements Runnable {
79682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        private final Context mContext;
79782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        private final SendOrSaveCallback mSendOrSaveCallback;
79882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        @VisibleForTesting
79982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final SendOrSaveMessage mSendOrSaveMessage;
80082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
80182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public SendOrSaveTask(Context context, SendOrSaveMessage message,
80282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                SendOrSaveCallback callback) {
80382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mContext = context;
80482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveCallback = callback;
80582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveMessage = message;
80682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
80782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
80882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        @Override
80982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void run() {
8107ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            final SendOrSaveMessage sendOrSaveMessage = mSendOrSaveMessage;
81182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
8127ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            final Account selectedAccount = sendOrSaveMessage.mSelectedAccount;
8137ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            Message message = mSendOrSaveCallback.getMessage();
8147ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            long messageId = message != null ? message.id : UIProvider.INVALID_MESSAGE_ID;
81582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // If a previous draft has been saved, in an account that is different
81682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // than what the user wants to send from, remove the old draft, and treat this
81782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // as a new message
8187ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            if (!selectedAccount.equals(sendOrSaveMessage.mAccount)) {
81982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                if (messageId != UIProvider.INVALID_MESSAGE_ID) {
82082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    ContentResolver resolver = mContext.getContentResolver();
82182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    ContentValues values = new ContentValues();
82282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    values.put(BaseColumns._ID, messageId);
823cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    if (selectedAccount.expungeMessageUri != null) {
824cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                        resolver.update(selectedAccount.expungeMessageUri, values, null,
825eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                                null);
826cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    } else {
827cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                        // TODO(mindyp) delete the conversation.
828eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    }
82982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    // reset messageId to 0, so a new message will be created
83082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    messageId = UIProvider.INVALID_MESSAGE_ID;
83182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                }
83282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
83382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
83482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            final long messageIdToSave = messageId;
83582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (messageIdToSave != UIProvider.INVALID_MESSAGE_ID) {
8367ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                sendOrSaveMessage.mValues.put(BaseColumns._ID, messageIdToSave);
83782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mContext.getContentResolver().update(
8387ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        Uri.parse(sendOrSaveMessage.mSave ? message.saveUri : message.sendUri),
8397ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        sendOrSaveMessage.mValues, null, null);
84082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            } else {
8417ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                ContentResolver resolver = mContext.getContentResolver();
8427ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                Uri messageUri = resolver.insert(
843cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                        sendOrSaveMessage.mSave ? selectedAccount.saveDraftUri
844cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                                : selectedAccount.sendMessageUri, sendOrSaveMessage.mValues);
8457ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (sendOrSaveMessage.mSave && messageUri != null) {
8467ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    Cursor messageCursor = resolver.query(messageUri,
8477ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                            UIProvider.MESSAGE_PROJECTION, null, null, null);
848ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                    if (messageCursor != null) {
849ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                        try {
850ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                            if (messageCursor.moveToFirst()) {
851ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                                // Broadcast notification that a new message has
852ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                                // been allocated
853ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                                mSendOrSaveCallback.notifyMessageIdAllocated(sendOrSaveMessage,
854ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                                        new Message(messageCursor));
855ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                            }
856ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                        } finally {
857ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                            messageCursor.close();
858ba5584836c0a63c8dac96b10b569465bd56d5f7aPaul Westbrook                        }
8597ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    }
8607ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
86182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
86282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
8637ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            if (!sendOrSaveMessage.mSave) {
86482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
8657ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.TO));
86682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
8677ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.CC));
86882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
8697ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        (String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.BCC));
87082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
87182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveCallback.sendOrSaveFinished(SendOrSaveTask.this, true);
87282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
87382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
87482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
87582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Array of the outstanding send or save tasks.  Access is synchronized
87682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // with the object itself
87782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /* package for testing */
87882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    ArrayList<SendOrSaveTask> mActiveTasks = Lists.newArrayList();
87982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private int mRequestId;
880bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    private String mSignature;
88182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
88282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ static class SendOrSaveMessage {
88382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final Account mAccount;
88482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final Account mSelectedAccount;
88582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final ContentValues mValues;
8863ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira        final String mRefMessageId;
88782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final boolean mSave;
88882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final int mRequestId;
88982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
89082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public SendOrSaveMessage(Account account, Account selectedAccount, ContentValues values,
8913ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                String refMessageId, boolean save) {
89282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mAccount = account;
89382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSelectedAccount = selectedAccount;
89482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mValues = values;
89582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRefMessageId = refMessageId;
89682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave = save;
89782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRequestId = mValues.hashCode() ^ hashCode();
89882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
89982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
90082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        int requestId() {
90182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            return mRequestId;
90282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
90382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
90482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
90582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
90682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the to recipients.
90782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
90882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getToAddresses() {
90982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mTo);
91082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
91182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
91282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
91382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the cc recipients.
91482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
91582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getCcAddresses() {
91682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mCc);
91782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
91882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
91982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
92082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the bcc recipients.
92182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
92282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getBccAddresses() {
92382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mBcc);
92482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
92582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
92682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getAddressesFromList(RecipientEditTextView list) {
92782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (list == null) {
92882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            return new String[0];
92982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
93082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(list.getText());
93182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        int count = tokens.length;
93282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        String[] result = new String[count];
93382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        for (int i = 0; i < count; i++) {
93482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            result[i] = tokens[i].toString();
93582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
93682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return result;
93782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
93882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
93982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
94082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Check for invalid email addresses.
94182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param to String array of email addresses to check.
94282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param wrongEmailsOut Emails addresses that were invalid.
94382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
94482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void checkInvalidEmails(String[] to, List<String> wrongEmailsOut) {
94582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        for (String email : to) {
94682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (!mValidator.isValid(email)) {
94782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                wrongEmailsOut.add(email);
94882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
94982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
95082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
95182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
95282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
95382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Show an error because the user has entered an invalid recipient.
95482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param message
95582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
95682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void showRecipientErrorDialog(String message) {
95782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // Only 1 invalid recipients error dialog should be allowed up at a
95882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // time.
95982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mRecipientErrorDialog != null) {
96082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRecipientErrorDialog.dismiss();
96182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
96282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mRecipientErrorDialog = new AlertDialog.Builder(this).setMessage(message).setTitle(
96382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                R.string.recipient_error_dialog_title)
96482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setIconAttribute(android.R.attr.alertDialogIcon)
96582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setCancelable(false)
96682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setPositiveButton(
96782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        R.string.ok, new Dialog.OnClickListener() {
96882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                            public void onClick(DialogInterface dialog, int which) {
96982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // after the user dismisses the recipient error
97082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // dialog we want to make sure to refocus the
97182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // recipient to field so they can fix the issue
97282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // easily
97382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                if (mTo != null) {
97482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                    mTo.requestFocus();
97582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                }
97682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                mRecipientErrorDialog = null;
97782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                            }
97882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        }).show();
97982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
98082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
98182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
98282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Update the state of the UI based on whether or not the current draft
98382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * needs to be saved and the message is not empty.
98482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
985eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void updateSaveUi() {
98682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSave != null) {
98782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave.setEnabled((shouldSave() && !isBlank()));
98882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
98982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
99082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
99182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
99282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Returns true if we need to save the current draft.
99382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
99482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean shouldSave() {
9957ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        synchronized (mDraftLock) {
99682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // The message should only be saved if:
99782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // It hasn't been sent AND
99882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // Some text has been added to the message OR
99982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // an attachment has been added or removed
1000eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return (mTextChanged || mAttachmentsChanged ||
100182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    (mReplyFromChanged && !isBlank()));
100282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
100382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
100482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
100582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
1006bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira     * Check if all fields are blank.
100782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @return boolean
100882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
100982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public boolean isBlank() {
101082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return mSubject.getText().length() == 0
1011bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && (mBodyView.getText().length() == 0 || getSignatureStartPosition(mSignature,
1012bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                        mBodyView.getText().toString()) == 0)
1013bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && mTo.length() == 0
1014bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && mCc.length() == 0 && mBcc.length() == 0
1015bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && mAttachmentsView.getAttachments().size() == 0;
1016bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    }
1017bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
1018bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    @VisibleForTesting
1019bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    protected int getSignatureStartPosition(String signature, String bodyText) {
1020bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        int startPos = -1;
1021bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
1022bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        if (TextUtils.isEmpty(signature) || TextUtils.isEmpty(bodyText)) {
1023bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            return startPos;
1024bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        }
1025bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
1026bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        int bodyLength = bodyText.length();
1027bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        int signatureLength = signature.length();
1028bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        String printableVersion = convertToPrintableSignature(signature);
1029bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        int printableLength = printableVersion.length();
1030bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
1031bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        if (bodyLength >= printableLength
1032bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && bodyText.substring(bodyLength - printableLength)
1033bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                .equals(printableVersion)) {
1034bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            startPos = bodyLength - printableLength;
1035bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        } else if (bodyLength >= signatureLength
1036bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                && bodyText.substring(bodyLength - signatureLength)
1037bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                .equals(signature)) {
1038bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            startPos = bodyLength - signatureLength;
1039bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        }
1040bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        return startPos;
104182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
104282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
104382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
104482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Allows any changes made by the user to be ignored. Called when the user
104582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * decides to discard a draft.
104682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
104782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private void discardChanges() {
104882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mTextChanged = false;
1049eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsChanged = false;
105082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mReplyFromChanged = false;
105133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    }
105233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira
105382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
1054181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @param body
1055181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @param save
1056181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @param showToast
1057181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return Whether the send or save succeeded.
1058181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1059181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    protected boolean sendOrSaveWithSanityChecks(final boolean save, final boolean showToast,
1060181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            final boolean orientationChanged) {
1061181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        String[] to, cc, bcc;
1062181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        Editable body = mBodyView.getEditableText();
1063181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1064181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (orientationChanged) {
1065181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = cc = bcc = new String[0];
1066181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        } else {
1067181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = getToAddresses();
1068181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            cc = getCcAddresses();
1069181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            bcc = getBccAddresses();
1070181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1071181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1072181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Don't let the user send to nobody (but it's okay to save a message
1073181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // with no recipients)
1074181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save && (to.length == 0 && cc.length == 0 && bcc.length == 0)) {
1075181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            showRecipientErrorDialog(getString(R.string.recipient_needed));
1076181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            return false;
1077181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1078181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1079181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        List<String> wrongEmails = new ArrayList<String>();
1080181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save) {
1081181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            checkInvalidEmails(to, wrongEmails);
1082181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            checkInvalidEmails(cc, wrongEmails);
1083181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            checkInvalidEmails(bcc, wrongEmails);
1084181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1085181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1086181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Don't let the user send an email with invalid recipients
1087181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (wrongEmails.size() > 0) {
1088181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            String errorText = String.format(getString(R.string.invalid_recipient),
1089181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    wrongEmails.get(0));
1090181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            showRecipientErrorDialog(errorText);
1091181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            return false;
1092181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1093181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1094181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
1095181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            public void onClick(DialogInterface dialog, int which) {
1096181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                sendOrSave(mBodyView.getEditableText(), save, showToast, orientationChanged);
1097181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1098181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        };
1099181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1100181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Show a warning before sending only if there are no attachments.
1101181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save) {
1102181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (mAttachmentsView.getAttachments().isEmpty() && showEmptyTextWarnings()) {
1103181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                boolean warnAboutEmptySubject = isSubjectEmpty();
1104181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                boolean emptyBody = TextUtils.getTrimmedLength(body) == 0;
1105181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1106181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // A warning about an empty body may not be warranted when
1107181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // forwarding mails, since a common use case is to forward
1108181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // quoted text and not append any more text.
1109181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                boolean warnAboutEmptyBody = emptyBody && (!mForward || isBodyEmpty());
1110181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1111181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // When we bring up a dialog warning the user about a send,
1112181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // assume that they accept sending the message. If they do not,
1113181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                // the dialog listener is required to enable sending again.
1114181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (warnAboutEmptySubject) {
1115181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    showSendConfirmDialog(R.string.confirm_send_message_with_no_subject, listener);
1116181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    return true;
1117181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1118181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1119181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (warnAboutEmptyBody) {
1120181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    showSendConfirmDialog(R.string.confirm_send_message_with_no_body, listener);
1121181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    return true;
1122181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1123181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1124181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // Ask for confirmation to send (if always required)
1125181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (showSendConfirmation()) {
1126181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                showSendConfirmDialog(R.string.confirm_send_message, listener);
1127181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                return true;
1128181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1129181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1130181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1131181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        sendOrSave(body, save, showToast, false);
1132181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return true;
1133181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1134181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1135181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1136181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Returns a boolean indicating whether warnings should be shown for empty
1137181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * subject and body fields
1138181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     *
1139181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return True if a warning should be shown for empty text fields
1140181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1141181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    protected boolean showEmptyTextWarnings() {
1142181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return mAttachmentsView.getAttachments().size() == 0;
1143181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1144181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1145181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1146181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Returns a boolean indicating whether the user should confirm each send
1147181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     *
1148181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return True if a warning should be on each send
1149181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1150181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    protected boolean showSendConfirmation() {
1151181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return mCachedSettings != null ? mCachedSettings.confirmSend : false;
1152181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1153181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1154181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private void showSendConfirmDialog(int messageId, DialogInterface.OnClickListener listener) {
1155181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (mSendConfirmDialog != null) {
1156181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mSendConfirmDialog.dismiss();
1157181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mSendConfirmDialog = null;
1158181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1159181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        mSendConfirmDialog = new AlertDialog.Builder(this).setMessage(messageId)
1160181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setTitle(R.string.confirm_send_title)
1161181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setIconAttribute(android.R.attr.alertDialogIcon)
1162181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setPositiveButton(R.string.send, listener)
1163181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                .setNegativeButton(R.string.cancel, this).setCancelable(false).show();
1164181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1165181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1166181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1167181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Returns whether the ComposeArea believes there is any text in the body of
1168181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * the composition. TODO: When ComposeArea controls the Body as well, add
1169181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * that here.
1170181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1171181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public boolean isBodyEmpty() {
1172181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return !mQuotedTextView.isTextIncluded();
1173181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1174181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1175181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1176181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Test to see if the subject is empty.
1177181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     *
1178181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * @return boolean.
1179181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1180181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    // TODO: this will likely go away when composeArea.focus() is implemented
1181181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    // after all the widget control is moved over.
1182181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public boolean isSubjectEmpty() {
1183181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return TextUtils.getTrimmedLength(mSubject.getText()) == 0;
1184181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1185181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1186181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /* package */
118729ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira    static int sendOrSaveInternal(Context context, final Account account,
118829ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            final Account selectedAccount, String fromAddress, final Spanned body,
118929ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            final String[] to, final String[] cc, final String[] bcc, final String subject,
119029ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            final CharSequence quotedText, final List<Attachment> attachments,
11913ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira            final String refMessageId, SendOrSaveCallback callback, Handler handler, boolean save,
119229ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            boolean forward) {
119329ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        ContentValues values = new ContentValues();
119429ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira
119529ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putToAddresses(values, to);
119629ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putCcAddresses(values, cc);
119729ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putBccAddresses(values, bcc);
119829ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira
119929ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putSubject(values, subject);
120029ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        String htmlBody = Html.toHtml(body);
120129ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        boolean includeQuotedText = !TextUtils.isEmpty(quotedText);
120229ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        StringBuilder fullBody = new StringBuilder(htmlBody);
120329ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        if (includeQuotedText) {
120429ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            if (forward) {
120529ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                // forwarded messages get full text in HTML from client
120629ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                fullBody.append(quotedText);
120729ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                MessageModification.putForward(values, forward);
120829ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            } else {
120929ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                // replies get full quoted text from server - HTMl gets
121029ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                // converted to text for now
121129ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                final String text = quotedText.toString();
121229ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                if (QuotedTextView.containsQuotedText(text)) {
121329ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    int pos = QuotedTextView.getQuotedTextOffset(text);
121429ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    fullBody.append(text.substring(0, pos));
121529ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    MessageModification.putForward(values, forward);
12163ce64e7ceb6246130342385d91550bcf33b31cc8Mindy Pereira                    MessageModification.putAppendRefMessageContent(values, includeQuotedText);
121729ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                } else {
121829ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    LogUtils.w(LOG_TAG, "Couldn't find quoted text");
121929ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    // This shouldn't happen, but just use what we have,
122029ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    // and don't do server-side expansion
122129ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                    fullBody.append(text);
122229ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira                }
122329ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira            }
122429ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        }
122529ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putBody(values, Html.fromHtml(fullBody.toString()).toString());
122629ef1b89b158281ffb693dd5f2c5291a894109fcMindy Pereira        MessageModification.putBodyHtml(values, fullBody.toString());
1227f944e9655562e321aff52fe5f437e1d2fa2950a9Mindy Pereira        MessageModification.putAttachments(values, attachments);
122882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1229181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        SendOrSaveMessage sendOrSaveMessage = new SendOrSaveMessage(account, selectedAccount,
1230181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                values, refMessageId, save);
1231181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        SendOrSaveTask sendOrSaveTask = new SendOrSaveTask(context, sendOrSaveMessage, callback);
123282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1233181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        callback.initializeSendOrSave(sendOrSaveTask);
123482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1235181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Do the send/save action on the specified handler to avoid possible
1236181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // ANRs
1237181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        handler.post(sendOrSaveTask);
123882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1239181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return sendOrSaveMessage.requestId();
1240181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
124182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1242181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private void sendOrSave(Spanned body, boolean save, boolean showToast,
1243181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            boolean orientationChanged) {
1244181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Check if user is a monkey. Monkeys can compose and hit send
1245181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // button but are not allowed to send anything off the device.
1246181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save && ActivityManager.isUserAMonkey()) {
1247181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            return;
1248181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
124982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1250181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        String[] to, cc, bcc;
1251181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (orientationChanged) {
1252181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = cc = bcc = new String[0];
1253181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        } else {
1254181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            to = getToAddresses();
1255181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            cc = getCcAddresses();
1256181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            bcc = getBccAddresses();
1257181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
125882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1259181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        SendOrSaveCallback callback = new SendOrSaveCallback() {
126082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            private int mRestoredRequestId;
126182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
126282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask) {
1263181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                synchronized (mActiveTasks) {
1264181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    int numTasks = mActiveTasks.size();
1265181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    if (numTasks == 0) {
1266181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        // Start service so we won't be killed if this app is
1267181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        // put in the background.
1268181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        startService(new Intent(ComposeActivity.this, EmptyService.class));
1269181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    }
1270181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1271181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    mActiveTasks.add(sendOrSaveTask);
1272181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1273181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (sTestSendOrSaveCallback != null) {
1274181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    sTestSendOrSaveCallback.initializeSendOrSave(sendOrSaveTask);
1275181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1276181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
127782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
12787ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            public void notifyMessageIdAllocated(SendOrSaveMessage sendOrSaveMessage,
12797ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    Message message) {
1280181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                synchronized (mDraftLock) {
1281181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    mDraftId = message.id;
1282181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    mDraft = message;
12837ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    if (sRequestMessageIdMap != null) {
12847ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                        sRequestMessageIdMap.put(sendOrSaveMessage.requestId(), mDraftId);
12857ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    }
1286181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    // Cache request message map, in case the process is killed
1287181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    saveRequestMap();
1288181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1289181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                if (sTestSendOrSaveCallback != null) {
12907ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    sTestSendOrSaveCallback.notifyMessageIdAllocated(sendOrSaveMessage, message);
1291181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                }
1292181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
129382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
12947ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            public Message getMessage() {
12957ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                synchronized (mDraftLock) {
12967ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    return mDraft;
12977ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
12987ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            }
129982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
13007ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            public void sendOrSaveFinished(SendOrSaveTask task, boolean success) {
13017ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (success) {
13027ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // Successfully sent or saved so reset change markers
13037ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    discardChanges();
13047ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                } else {
13057ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // A failure happened with saving/sending the draft
13067ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // TODO(pwestbro): add a better string that should be used
13077ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // when failing to send or save
13087ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    Toast.makeText(ComposeActivity.this, R.string.send_failed, Toast.LENGTH_SHORT)
13097ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                            .show();
13107ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
131182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
13127ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                int numTasks;
13137ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                synchronized (mActiveTasks) {
13147ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // Remove the task from the list of active tasks
13157ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    mActiveTasks.remove(task);
13167ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    numTasks = mActiveTasks.size();
13177ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
131882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
13197ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (numTasks == 0) {
13207ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    // Stop service so we can be killed.
13217ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    stopService(new Intent(ComposeActivity.this, EmptyService.class));
13227ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
13237ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                if (sTestSendOrSaveCallback != null) {
13247ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                    sTestSendOrSaveCallback.sendOrSaveFinished(task, success);
13257ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira                }
13267ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira            }
1327181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        };
1328181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1329181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Get the selected account if the from spinner has been setup.
1330181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        Account selectedAccount = mAccount;
1331181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        String fromAddress = selectedAccount.name;
1332181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (selectedAccount == null || fromAddress == null) {
1333181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // We don't have either the selected account or from address,
1334181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // use mAccount.
1335181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            selectedAccount = mAccount;
1336181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            fromAddress = mAccount.name;
1337181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1338181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1339181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (mSendSaveTaskHandler == null) {
1340181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            HandlerThread handlerThread = new HandlerThread("Send Message Task Thread");
1341181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            handlerThread.start();
1342181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1343181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mSendSaveTaskHandler = new Handler(handlerThread.getLooper());
1344181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1345181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1346181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        mRequestId = sendOrSaveInternal(this, mAccount, selectedAccount, fromAddress, body, to, cc,
1347181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                bcc, mSubject.getText().toString(), mQuotedTextView.getQuotedText(),
1348181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                mAttachmentsView.getAttachments(), mRefMessageId, callback, mSendSaveTaskHandler,
1349181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                save, mForward);
1350181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1351181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (mRecipient != null && mRecipient.equals(mAccount.name)) {
1352181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mRecipient = selectedAccount.name;
1353181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1354181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        mAccount = selectedAccount;
1355181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1356181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Don't display the toast if the user is just changing the orientation,
1357181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // but we still need to save the draft to the cursor because this is how we restore
1358181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // the attachments when the configuration change completes.
1359181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (showToast && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) {
1360181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            Toast.makeText(this, save ? R.string.message_saved : R.string.sending_message,
1361181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                    Toast.LENGTH_LONG).show();
1362181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1363181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1364181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Need to update variables here because the send or save completes
1365181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // asynchronously even though the toast shows right away.
1366181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        discardChanges();
1367181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        updateSaveUi();
1368181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1369181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // If we are sending, finish the activity
1370181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!save) {
1371181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            finish();
1372181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1373181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1374181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1375181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    /**
1376181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * Save the state of the request messageid map. This allows for the Gmail
1377181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * process to be killed, but and still allow for ComposeActivity instances
1378181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     * to be recreated correctly.
1379181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira     */
1380181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    private void saveRequestMap() {
1381181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // TODO: store the request map in user preferences.
1382181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
138382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1384013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    public void doAttach() {
1385013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
1386013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        i.addCategory(Intent.CATEGORY_OPENABLE);
1387181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (android.provider.Settings.System.getInt(getContentResolver(),
1388181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                UIProvider.getAttachmentTypeSetting(), 0) != 0) {
1389013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            i.setType("*/*");
1390013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        } else {
1391013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            i.setType("image/*");
1392013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        }
1393013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        mAddingAttachment = true;
1394181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        startActivityForResult(Intent.createChooser(i, getText(R.string.select_attachment_type)),
1395181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                RESULT_PICK_ATTACHMENT);
1396013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    }
1397013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira
1398ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    private void showCcBccViews() {
1399a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        mCcBccView.show(true, true, true);
1400ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (mCcBccButton != null) {
1401ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            mCcBccButton.setVisibility(View.GONE);
1402ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
1403ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    }
1404ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira
1405326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    @Override
1406326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    public boolean onNavigationItemSelected(int position, long itemId) {
1407a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        int initialComposeMode = mComposeMode;
1408326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        if (position == ComposeActivity.REPLY) {
1409326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.REPLY;
1410326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else if (position == ComposeActivity.REPLY_ALL) {
1411326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.REPLY_ALL;
1412326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else if (position == ComposeActivity.FORWARD) {
1413326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.FORWARD;
1414326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1415a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (initialComposeMode != mComposeMode) {
1416154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            resetMessageForModeChange();
141733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            initFromRefMessage(mComposeMode, mAccount.name);
1418a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        }
1419326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        return true;
1420326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
1421326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1422154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira    private void resetMessageForModeChange() {
1423154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // When switching between reply, reply all, forward,
1424154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // follow the behavior of webview.
1425154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // The contents of the following fields are cleared
1426154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // so that they can be populated directly from the
1427154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // ref message:
1428154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 1) Any recipient fields
1429154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 2) The subject
1430154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mTo.setText("");
1431154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mCc.setText("");
1432154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mBcc.setText("");
1433154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // Any edits to the subject are replaced with the original subject.
1434154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        mSubject.setText("");
1435154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira
1436154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // Any changes to the contents of the following fields are kept:
1437154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 1) Body
1438154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // 2) Attachments
1439154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        // If the user made changes to attachments, keep their changes.
1440154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        if (!mAttachmentsChanged) {
1441154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira            mAttachmentsView.deleteAllAttachments();
1442154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira        }
1443154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira    }
1444154386a287a9bfffb2eb3a4c05445f2774e1d4f0Mindy Pereira
1445326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private class ComposeModeAdapter extends ArrayAdapter<String> {
1446326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1447326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        private LayoutInflater mInflater;
1448326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1449326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        public ComposeModeAdapter(Context context) {
1450326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            super(context, R.layout.compose_mode_item, R.id.mode, getResources()
1451326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    .getStringArray(R.array.compose_modes));
1452326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1453326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1454326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        private LayoutInflater getInflater() {
1455326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (mInflater == null) {
1456326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                mInflater = LayoutInflater.from(getContext());
1457326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
1458326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            return mInflater;
1459326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1460326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1461326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        @Override
1462326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        public View getView(int position, View convertView, ViewGroup parent) {
1463326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (convertView == null) {
1464326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                convertView = getInflater().inflate(R.layout.compose_mode_display_item, null);
1465326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
1466326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            ((TextView) convertView.findViewById(R.id.mode)).setText(getItem(position));
1467326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            return super.getView(position, convertView, parent);
1468326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1469326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
147046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
147146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    @Override
147246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void onRespondInline(String text) {
147346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        appendToBody(text, false);
147446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
147546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
147646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
147746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Append text to the body of the message. If there is no existing body
147846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * text, just sets the body to text.
147946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *
148046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param text
148146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param withSignature True to append a signature.
148246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
148346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void appendToBody(CharSequence text, boolean withSignature) {
1484eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        Editable bodyText = mBodyView.getEditableText();
148546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (bodyText != null && bodyText.length() > 0) {
148646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            bodyText.append(text);
148746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
148846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            setBody(text, withSignature);
148946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
149046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
149146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
149246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
149346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Set the body of the message.
1494bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira     *
149546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param text
149646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param withSignature True to append a signature.
149746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
149846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void setBody(CharSequence text, boolean withSignature) {
1499eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBodyView.setText(text);
1500bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        if (withSignature) {
1501bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            appendSignature();
1502bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        }
1503bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    }
1504bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
1505bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    private void appendSignature() {
1506bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        mSignature = mCachedSettings != null ? mCachedSettings.signature : null;
1507bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        if (!TextUtils.isEmpty(mSignature)) {
1508bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            // Appending a signature does not count as changing text.
1509bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            mBodyView.removeTextChangedListener(this);
1510bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            mBodyView.append(convertToPrintableSignature(mSignature));
1511bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            mBodyView.addTextChangedListener(this);
1512bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        }
1513bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    }
1514bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira
1515bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira    private String convertToPrintableSignature(String signature) {
1516bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        String signatureResource = getResources().getString(R.string.signature);
1517bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        if (signature == null) {
1518bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira            signature = "";
1519bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        }
1520bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira        return String.format(signatureResource, signature);
152146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
15221a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
15235a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    @Override
15245a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    public void onAccountChanged() {
15255a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        Account selectedAccountInfo = mFromSpinner.getCurrentAccount();
1526181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (!mAccount.equals(selectedAccountInfo)) {
1527181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mAccount = selectedAccountInfo;
1528181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mCachedSettings = null;
1529181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            getLoaderManager().restartLoader(ACCOUNT_SETTINGS_LOADER, null, this);
1530181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // TODO: handle discarding attachments when switching accounts.
1531181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // Only enable save for this draft if there is any other content
1532181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            // in the message.
1533181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (!isBlank()) {
1534181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                enableSave(true);
1535181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1536181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            mReplyFromChanged = true;
1537181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            initRecipients();
153882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
15391a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
154082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
154182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void enableSave(boolean enabled) {
154282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSave != null) {
154382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave.setEnabled(enabled);
154482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
154582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
154682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
154782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void enableSend(boolean enabled) {
154882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSend != null) {
154982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSend.setEnabled(enabled);
155082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
155182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
155282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
155382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
155482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Handles button clicks from any error dialogs dealing with sending
155582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * a message.
155682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
155782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    @Override
155882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void onClick(DialogInterface dialog, int which) {
155982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        switch (which) {
156082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            case DialogInterface.BUTTON_POSITIVE: {
156182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                doDiscardWithoutConfirmation(true /* show toast */ );
156282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                break;
156382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
156482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            case DialogInterface.BUTTON_NEGATIVE: {
156582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // If the user cancels the send, re-enable the send button.
156682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                enableSend(true);
156782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                break;
156882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
156982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
157082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
157182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
157282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1573efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira    private void doDiscard() {
1574efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira        new AlertDialog.Builder(this).setMessage(R.string.confirm_discard_text)
1575efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira                .setPositiveButton(R.string.ok, this)
1576efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira                .setNegativeButton(R.string.cancel, null)
1577efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira                .create().show();
1578efe3d25aa19da172fd5c3ec39539cdd91f29e927Mindy Pereira    }
157982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
158082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Effectively discard the current message.
158182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     *
158282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * This method is either invoked from the menu or from the dialog
158382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * once the user has confirmed that they want to discard the message.
158482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param showToast show "Message discarded" toast if true
158582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
158682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private void doDiscardWithoutConfirmation(boolean showToast) {
15877ed1c1122c5677344a1ce176be2c03972fed26f5Mindy Pereira        synchronized (mDraftLock) {
158882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (mDraftId != UIProvider.INVALID_MESSAGE_ID) {
158982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                ContentValues values = new ContentValues();
1590b7050e62aab7133dea300d7fb3266311da99ec80Paul Westbrook                values.put(BaseColumns._ID, mDraftId);
1591cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                if (mAccount.expungeMessageUri != null) {
1592cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    getContentResolver().update(mAccount.expungeMessageUri, values, null, null);
1593cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                } else {
1594cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    // TODO(mindyp): call delete on this conversation instead.
1595cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                }
159682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // This is not strictly necessary (since we should not try to
159782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // save the draft after calling this) but it ensures that if we
159882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // do save again for some reason we make a new draft rather than
159982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // trying to resave an expunged draft.
160082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mDraftId = UIProvider.INVALID_MESSAGE_ID;
160182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
160282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
160382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
160482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (showToast) {
160582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // Display a toast to let the user know
160682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            Toast.makeText(this, R.string.message_discarded, Toast.LENGTH_SHORT).show();
160782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
160882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
160982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // This prevents the draft from being saved in onPause().
161082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        discardChanges();
161182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        finish();
161282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
161382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1614eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void saveIfNeeded() {
1615eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mAccount == null) {
1616eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // We have not chosen an account yet so there's no way that we can save. This is ok,
1617eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // though, since we are saving our state before AccountsActivity is activated. Thus, the
1618eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // user has not interacted with us yet and there is no real state to save.
1619eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return;
1620eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1621eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1622eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (shouldSave()) {
1623eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            doSave(!mAddingAttachment /* show toast */, true /* reset IME */);
1624eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1625eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1626eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1627eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void saveIfNeededOnOrientationChanged() {
1628eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mAccount == null) {
1629eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // We have not chosen an account yet so there's no way that we can save. This is ok,
1630eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // though, since we are saving our state before AccountsActivity is activated. Thus, the
1631eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // user has not interacted with us yet and there is no real state to save.
1632eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return;
1633eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1634eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1635eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (shouldSave()) {
1636eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            doSaveOrientationChanged(!mAddingAttachment /* show toast */, true /* reset IME */);
1637eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1638eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1639eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1640eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    /**
1641eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira     * Save a draft if a draft already exists or the message is not empty.
1642eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira     */
1643eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void doSaveOrientationChanged(boolean showToast, boolean resetIME) {
1644eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveOnOrientationChanged();
1645eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (resetIME) {
1646eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // Clear the IME composing suggestions from the body.
1647eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            BaseInputConnection.removeComposingSpans(mBodyView.getEditableText());
1648eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1649eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1650eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1651eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected boolean saveOnOrientationChanged() {
1652eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        return sendOrSaveWithSanityChecks(true, false, true);
1653eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1654eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1655eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
1656eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void onAttachmentDeleted() {
1657eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsChanged = true;
1658eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        updateSaveUi();
1659eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
166075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
166175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
166275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    /**
166375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira     * This is called any time one of our text fields changes.
166475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira     */
166575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void afterTextChanged(Editable s) {
166675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        mTextChanged = true;
166775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        updateSaveUi();
166875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
166975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
167075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    @Override
167175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
167275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        // Do nothing.
167375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
167475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
167575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    public void onTextChanged(CharSequence s, int start, int before, int count) {
167675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        // Do nothing.
167775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
167875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
167975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
168075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // There is a big difference between the text associated with an address changing
168175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // to add the display name or to format properly and a recipient being added or deleted.
168275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    // Make sure we only notify of changes when a recipient has been added or deleted.
168375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    private class RecipientTextWatcher implements TextWatcher {
168475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private HashMap<String, Integer> mContent = new HashMap<String, Integer>();
168575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
168675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private RecipientEditTextView mView;
168775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
168875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private TextWatcher mListener;
168975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
169075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public RecipientTextWatcher(RecipientEditTextView view, TextWatcher listener) {
169175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mView = view;
169275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            mListener = listener;
169375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
169475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
169575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
169675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void afterTextChanged(Editable s) {
169775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            if (hasChanged()) {
169875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                mListener.afterTextChanged(s);
169975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
170075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
170175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
170275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private boolean hasChanged() {
170375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] currRecips = tokenizeRecips(getAddressesFromList(mView));
170475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            int totalCount = currRecips.length;
170575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            int totalPrevCount = 0;
170675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (Entry<String, Integer> entry : mContent.entrySet()) {
170775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                totalPrevCount += entry.getValue();
170875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
170975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            if (totalCount != totalPrevCount) {
171075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                return true;
171175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
171275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
171375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (String recip : currRecips) {
171475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                if (!mContent.containsKey(recip)) {
171575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    return true;
171675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                } else {
171775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    int count = mContent.get(recip) - 1;
171875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    if (count < 0) {
171975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                        return true;
172075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    } else {
172175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                        mContent.put(recip, count);
172275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    }
172375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                }
172475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
172575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            return false;
172675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
172775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
172875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        private String[] tokenizeRecips(String[] recips) {
172975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            // Tokenize them all and put them in the list.
173075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] recipAddresses = new String[recips.length];
173175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (int i = 0; i < recips.length; i++) {
173275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                recipAddresses[i] = Rfc822Tokenizer.tokenize(recips[i])[0].getAddress();
173375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
173475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            return recipAddresses;
173575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
173675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
173775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
173875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
173975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            String[] recips = tokenizeRecips(getAddressesFromList(mView));
174075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            for (String recip : recips) {
174175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                if (!mContent.containsKey(recip)) {
174275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    mContent.put(recip, 1);
174375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                } else {
174475f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                    mContent.put(recip, (mContent.get(recip)) + 1);
174575f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira                }
174675f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            }
174775f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
174875f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira
174975f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        @Override
175075f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        public void onTextChanged(CharSequence s, int start, int before, int count) {
175175f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira            // Do nothing.
175275f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira        }
175375f66634dd18cc8d8e6f40c4573c0edf0a0343efMindy Pereira    }
1754181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1755181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    @Override
1756181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
1757181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (id == ACCOUNT_SETTINGS_LOADER) {
175823e9fdec6872f9b4d0fbe675745cb25becf7878cMindy Pereira            if (mAccount != null && mAccount.settingsQueryUri != null) {
1759181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                return new CursorLoader(this, mAccount.settingsQueryUri,
1760181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                        UIProvider.SETTINGS_PROJECTION, null, null, null);
1761181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1762181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1763181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        return null;
1764181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1765181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1766181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    @Override
1767181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
1768181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        if (loader.getId() == ACCOUNT_SETTINGS_LOADER) {
1769181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            if (data != null) {
1770181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                data.moveToFirst();
1771181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira                mCachedSettings = new Settings(data);
1772bdf7a40583381ae26d11dcd0fca40bca230f96f3Mindy Pereira                appendSignature();
1773181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira            }
1774181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        }
1775181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
1776181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira
1777181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    @Override
1778181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    public void onLoaderReset(Loader<Cursor> loader) {
1779181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira        // Do nothing.
1780181df7851bd1eba4b6a3255073a8b731de23ae8fMindy Pereira    }
17818e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira}