ComposeActivity.java revision 7a07fb4955e34250225287212b07d1b6f12bf671
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;
256349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.content.ContentResolver;
2682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.content.ContentValues;
276349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.content.Context;
2882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.content.DialogInterface;
296349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.content.Intent;
3082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.content.pm.ActivityInfo;
316349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.database.Cursor;
326349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.net.Uri;
338e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.os.Bundle;
3482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.os.Handler;
3582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.os.HandlerThread;
3682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.provider.BaseColumns;
37013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereiraimport android.provider.Settings;
3846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport android.text.Editable;
3982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.Html;
4082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.Spanned;
41c182762df95fd382faf8e4a25e5cf7783ea10176Paul Westbrookimport android.text.TextUtils;
4282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport android.text.TextWatcher;
4346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport android.text.util.Rfc822Token;
44c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport android.text.util.Rfc822Tokenizer;
45326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.LayoutInflater;
46b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.Menu;
47b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.MenuInflater;
48b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereiraimport android.view.MenuItem;
498e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.view.View;
50326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.view.ViewGroup;
518e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.view.View.OnClickListener;
52eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereiraimport android.view.inputmethod.BaseInputConnection;
53326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.widget.ArrayAdapter;
548e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereiraimport android.widget.Button;
556349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereiraimport android.widget.TextView;
56013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereiraimport android.widget.Toast;
577b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira
58c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport com.android.common.Rfc822Validator;
59eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereiraimport com.android.mail.compose.AttachmentsView.AttachmentDeletedListener;
609932dee248718d2cec993d93b5115973d1389d0aMindy Pereiraimport com.android.mail.compose.AttachmentsView.AttachmentFailureException;
615a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereiraimport com.android.mail.compose.FromAddressSpinner.OnAccountChangedListener;
6230e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.compose.QuotedTextView.RespondInlineListener;
6333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereiraimport com.android.mail.providers.Account;
6430e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.Address;
6530e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.Attachment;
6682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.android.mail.providers.MessageModification;
6730e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.providers.UIProvider;
6882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.android.mail.providers.UIProvider.MessageColumns;
6930e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.R;
7030e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.utils.LogUtils;
7130e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangimport com.android.mail.utils.Utils;
72c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereiraimport com.android.ex.chips.RecipientEditTextView;
7346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport com.google.common.annotations.VisibleForTesting;
7482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport com.google.common.collect.Lists;
754a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereiraimport com.google.common.collect.Sets;
768e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
7746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.ArrayList;
7846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.Arrays;
7946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.Collection;
8046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.HashSet;
8146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereiraimport java.util.List;
824a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereiraimport java.util.Set;
8382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereiraimport java.util.concurrent.ConcurrentHashMap;
8446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
8546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereirapublic class ComposeActivity extends Activity implements OnClickListener, OnNavigationListener,
865a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        RespondInlineListener, DialogInterface.OnClickListener, TextWatcher,
875a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        AttachmentDeletedListener, OnAccountChangedListener {
886349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    // Identifiers for which type of composition this is
896349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int COMPOSE = -1;  // also used for editing a draft
906349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int REPLY = 0;
916349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int REPLY_ALL = 1;
926349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    static final int FORWARD = 2;
936349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
946349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    // Integer extra holding one of the above compose action
956349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private static final String EXTRA_ACTION = "action";
966349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
9782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static SendOrSaveCallback sTestSendOrSaveCallback = null;
9882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Map containing information about requests to create new messages, and the id of the
9982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // messages that were the result of those requests.
10082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    //
10182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // This map is used when the activity that initiated the save a of a new message, is killed
10282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // before the save has completed (and when we know the id of the newly created message).  When
10382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // a save is completed, the service that is running in the background, will update the map
10482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    //
10582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // When a new ComposeActivity instance is created, it will attempt to use the information in
10682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // the previously instantiated map.  If ComposeActivity.onCreate() is called, with a bundle
10782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // (restoring data from a previous instance), and the map hasn't been created, we will attempt
10882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // to populate the map with data stored in shared preferences.
10982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static ConcurrentHashMap<Integer, Long> sRequestMessageIdMap = null;
11082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Key used to store the above map
11182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private static final String CACHED_MESSAGE_REQUEST_IDS_KEY = "cache-message-request-ids";
1126349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1136349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Notifies the {@code Activity} that the caller is an Email
1146349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * {@code Activity}, so that the back behavior may be modified accordingly.
1156349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     *
1166349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * @see #onAppUpPressed
1176349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
1186349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private static final String EXTRA_FROM_EMAIL_TASK = "fromemail";
1196349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1206349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    //  If this is a reply/forward then this extra will hold the original message uri
1216349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private static final String EXTRA_IN_REFERENCE_TO_MESSAGE_URI = "in-reference-to-uri";
1224a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private static final String END_TOKEN = ", ";
123013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final String LOG_TAG = new LogUtils().getLogTag();
124013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    // Request numbers for activities we start
125013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final int RESULT_PICK_ATTACHMENT = 1;
126013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private static final int RESULT_CREATE_ACCOUNT = 2;
1278e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
12882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
12982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * A single thread for running tasks in the background.
13082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
13182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Handler mSendSaveTaskHandler = null;
132c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mTo;
133c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mCc;
134c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView mBcc;
1358e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    private Button mCcBccButton;
1368e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    private CcBccView mCcBccView;
1377b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira    private AttachmentsView mAttachmentsView;
13833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private Account mAccount;
13982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Rfc822Validator mValidator;
1406349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private Uri mRefMessageUri;
1416349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    private TextView mSubject;
1426349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
143326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private ComposeModeAdapter mComposeModeAdapter;
144326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private int mComposeMode = -1;
14546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private boolean mForward;
14646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private String mRecipient;
14746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private QuotedTextView mQuotedTextView;
148eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private TextView mBodyView;
1491a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    private View mFromStatic;
150eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private View mFromSpinnerWrapper;
1515a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    private FromAddressSpinner mFromSpinner;
152013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    private boolean mAddingAttachment;
153eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private boolean mAttachmentsChanged;
15482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean mTextChanged;
15582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean mReplyFromChanged;
15682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private MenuItem mSave;
15782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private MenuItem mSend;
15882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private Object mDraftIdLock = new Object();
15982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private long mRefMessageId;
16082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private AlertDialog mRecipientErrorDialog;
161eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private AlertDialog mSendConfirmDialog;
162326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    /**
163326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira     * Can be called from a non-UI thread.
164326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira     */
16582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public static void editDraft(Context launcher, Account account, long localMessageId) {
166326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
167326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1686349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1696349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1706349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
17133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void compose(Context launcher, Account account) {
1726349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, null, COMPOSE);
1736349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1746349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1756349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1766349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1776349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
17833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void reply(Context launcher, Account account, String uri) {
1796349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, uri, REPLY);
1806349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1816349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1826349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1836349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1846349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
18533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void replyAll(Context launcher, Account account, String uri) {
1866349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, uri, REPLY_ALL);
1876349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1886349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
1896349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    /**
1906349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     * Can be called from a non-UI thread.
1916349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira     */
19233fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    public static void forward(Context launcher, Account account, String uri) {
1936349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launch(launcher, account, uri, FORWARD);
1946349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
1956349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
19633fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private static void launch(Context launcher, Account account, String uri, int action) {
1976349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        Intent intent = new Intent(launcher, ComposeActivity.class);
1986349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_FROM_EMAIL_TASK, true);
1996349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_ACTION, action);
2006349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, account);
2016349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        intent.putExtra(EXTRA_IN_REFERENCE_TO_MESSAGE_URI, uri);
2026349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        launcher.startActivity(intent);
2036349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
2048e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
2058e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    @Override
2068e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    public void onCreate(Bundle savedInstanceState) {
2078e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        super.onCreate(savedInstanceState);
2083528d3657bd8f6f128e2b9b18acd018c4b3e5130Mindy Pereira        Intent intent = getIntent();
20933fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        mAccount = (Account)intent.getParcelableExtra(Utils.EXTRA_ACCOUNT);
2108e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        setContentView(R.layout.compose);
21146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        findViews();
21246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        int action = intent.getIntExtra(EXTRA_ACTION, COMPOSE);
21346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (action == REPLY || action == REPLY_ALL || action == FORWARD) {
21446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            mRefMessageUri = Uri.parse(intent.getStringExtra(EXTRA_IN_REFERENCE_TO_MESSAGE_URI));
21533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            initFromRefMessage(action, mAccount.name);
21646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
217eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mQuotedTextView.setVisibility(View.GONE);
21846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
2191a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        initActionBar(action);
2205a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        initFromSpinner();
2211a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
2221a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
2231a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    @Override
2241a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    protected void onResume() {
2251a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        super.onResume();
2261a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        // Update the from spinner as other accounts
2271a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        // may now be available.
2285a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.asyncInitFromSpinner();
2291a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
2301a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
231eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
232eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected void onPause() {
233eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        super.onPause();
234eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
235eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mSendConfirmDialog != null) {
236eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mSendConfirmDialog.dismiss();
237eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
238eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mRecipientErrorDialog != null) {
239eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            mRecipientErrorDialog.dismiss();
240eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
241eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
242eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveIfNeeded();
243eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
244eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
245eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
246eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected final void onActivityResult(int request, int result, Intent data) {
247eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAddingAttachment = false;
248eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
249eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (result == RESULT_OK && request == RESULT_PICK_ATTACHMENT) {
250eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            addAttachmentAndUpdateView(data);
251eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
252eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
253eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
254eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
255eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public final void onSaveInstanceState(Bundle state) {
256eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        super.onSaveInstanceState(state);
257eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
258eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // onSaveInstanceState is only called if the user might come back to this activity so it is
259eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // not an ideal location to save the draft. However, if we have never saved the draft before
260eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // we have to save it here in order to have an id to save in the bundle.
261eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveIfNeededOnOrientationChanged();
262eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
263eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
2641a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    private void initFromSpinner() {
2655a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.setCurrentAccount(mAccount);
2665a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.asyncInitFromSpinner();
2675a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner.setOnAccountChangedListener(this);
268eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        boolean showSpinner = mFromSpinner.getCount() > 1;
269eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // If there is only 1 account, just show that account.
270eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // Otherwise, give the user the ability to choose which account to send
271eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // mail from / save drafts to.
272eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromStatic.setVisibility(
273eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                showSpinner ? View.GONE : View.VISIBLE);
274eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromSpinnerWrapper.setVisibility(
275eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                showSpinner ? View.VISIBLE : View.GONE);
27646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
27746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
27846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void findViews() {
279a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        mCcBccButton = (Button) findViewById(R.id.add_cc_bcc);
2808e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        if (mCcBccButton != null) {
2818e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira            mCcBccButton.setOnClickListener(this);
2828e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        }
2838e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        mCcBccView = (CcBccView) findViewById(R.id.cc_bcc_wrapper);
2847b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira        mAttachmentsView = (AttachmentsView)findViewById(R.id.attachments);
285eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsView.setAttachmentChangesListener(this);
286eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        setupRecipients();
28782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // TODO: add special chips text change watchers before adding
28882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // this as a text changed watcher to the to, cc, bcc fields.
2896349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        mSubject = (TextView) findViewById(R.id.subject);
29082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSubject.addTextChangedListener(this);
29146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mQuotedTextView = (QuotedTextView) findViewById(R.id.quoted_text_view);
29246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mQuotedTextView.setRespondInlineListener(this);
293eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBodyView = (TextView) findViewById(R.id.body);
294eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBodyView.addTextChangedListener(this);
2951a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira        mFromStatic = findViewById(R.id.static_from_content);
296eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mFromSpinnerWrapper = findViewById(R.id.spinner_from_content);
2975a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mFromSpinner = (FromAddressSpinner) findViewById(R.id.from_picker);
298326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
299326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
300326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private void initActionBar(int action) {
301326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        mComposeMode = action;
302eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        ActionBar actionBar = getActionBar();
303326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        if (action == ComposeActivity.COMPOSE) {
304eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
305eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setTitle(R.string.compose);
306326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else {
307eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setTitle(null);
308326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (mComposeModeAdapter == null) {
309326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                mComposeModeAdapter = new ComposeModeAdapter(this);
310326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
311eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
312eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            actionBar.setListNavigationCallbacks(mComposeModeAdapter, this);
313326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            switch (action) {
314326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.REPLY:
315eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(0);
316326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
317326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.REPLY_ALL:
318eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(1);
319326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
320326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                case ComposeActivity.FORWARD:
321eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    actionBar.setSelectedNavigationItem(2);
322326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    break;
323326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
3246349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        }
3256349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira    }
3266349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira
32746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void initFromRefMessage(int action, String recipientAddress) {
3286349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        ContentResolver resolver = getContentResolver();
3296349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        Cursor refMessage = resolver.query(mRefMessageUri, UIProvider.MESSAGE_PROJECTION, null,
3306349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira                null, null);
3316349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        if (refMessage != null) {
33246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            try {
33346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                refMessage.moveToFirst();
33482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mRefMessageId = refMessage.getLong(UIProvider.MESSAGE_ID_COLUMN);
33546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                setSubject(refMessage, action);
33646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                // Setup recipients
33746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                if (action == FORWARD) {
33846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                    mForward = true;
33946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                }
34046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                initRecipientsFromRefMessageCursor(recipientAddress, refMessage, action);
34146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                initBodyFromRefMessage(refMessage, action);
34246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                if (action == ComposeActivity.FORWARD || mAttachmentsChanged) {
3437a07fb4955e34250225287212b07d1b6f12bf671Mindy Pereira                    initAttachments(refMessage);
34446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                } else {
34546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                    // Clear the attachments.
34646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                    removeAllAttachments();
34746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                }
34846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                updateHideOrShowCcBcc();
34946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            } finally {
35046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                refMessage.close();
35146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            }
3526349a043bce28ec999d72635222ae0eb576eb9cdMindy Pereira        }
353c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    }
354c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira
3557a07fb4955e34250225287212b07d1b6f12bf671Mindy Pereira    private void initAttachments(Cursor refMessage) {
3567a07fb4955e34250225287212b07d1b6f12bf671Mindy Pereira        mAttachmentsView.addAttachments(mAccount, refMessage);
357eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
358eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
35946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void initBodyFromRefMessage(Cursor refMessage, int action) {
3609932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        if (action == REPLY || action == REPLY_ALL || action == FORWARD) {
3619932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            mQuotedTextView.setVisibility(View.VISIBLE);
3629932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            mQuotedTextView.setQuotedText(action, refMessage, action != FORWARD);
3639932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        }
36446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
36546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
36646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void updateHideOrShowCcBcc() {
367ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        // Its possible there is a menu item OR a button.
368a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        boolean ccVisible = !TextUtils.isEmpty(mCc.getText());
369a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        boolean bccVisible = !TextUtils.isEmpty(mBcc.getText());
370a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (ccVisible || bccVisible) {
371a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            mCcBccView.show(false, ccVisible, bccVisible);
372a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        }
373ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (mCcBccButton != null) {
374ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            if (!mCc.isShown() || !mBcc.isShown()) {
375ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setVisibility(View.VISIBLE);
376ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setText(getString(!mCc.isShown() ? R.string.add_cc_label
377ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                        : R.string.add_bcc_label));
378ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            } else {
379ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                mCcBccButton.setVisibility(View.GONE);
380ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            }
381ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
38246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
38346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
38446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void removeAllAttachments() {
38546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mAttachmentsView.removeAllViews();
38646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
38746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
388013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    /**
389013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     * Add attachment and update the compose area appropriately.
390013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     * @param data
391013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira     */
392013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    public void addAttachmentAndUpdateView(Intent data) {
393013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        Uri uri = data != null ? data.getData() : null;
394013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        try {
3957aee8f75f07eb39ecacdf0630d2199211aea5718Mindy Pereira            long size =  mAttachmentsView.addAttachment(mAccount, uri, false /* doSave */);
3969932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            if (size > 0) {
3979932dee248718d2cec993d93b5115973d1389d0aMindy Pereira                mAttachmentsChanged = true;
3989932dee248718d2cec993d93b5115973d1389d0aMindy Pereira                updateSaveUi();
399013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            }
4009932dee248718d2cec993d93b5115973d1389d0aMindy Pereira        } catch (AttachmentFailureException e) {
4019932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            // A toast has already been shown to the user, no need to do
4029932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            // anything.
4039932dee248718d2cec993d93b5115973d1389d0aMindy Pereira            LogUtils.e(LOG_TAG, e, "Error adding attachment");
404013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        }
405013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    }
406013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira
40746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void initRecipientsFromRefMessageCursor(String recipientAddress, Cursor refMessage,
40846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            int action) {
4094a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // Don't populate the address if this is a forward.
4104a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        if (action == ComposeActivity.FORWARD) {
4114a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            return;
4124a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
41333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        initReplyRecipients(mAccount.name, refMessage, action);
4144a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4154a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4164a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void initReplyRecipients(String account, Cursor refMessage, int action) {
4174a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // This is the email address of the current user, i.e. the one composing
4184a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // the reply.
4194a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira        final String accountEmail = Address.getEmailAddress(account).getAddress();
4204a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String fromAddress = refMessage.getString(UIProvider.MESSAGE_FROM_COLUMN);
4214a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String[] sentToAddresses = Utils.splitCommaSeparatedString(refMessage
4224a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                .getString(UIProvider.MESSAGE_TO_COLUMN));
4234a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String[] replytoAddresses = Utils.splitCommaSeparatedString(refMessage
4244a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                .getString(UIProvider.MESSAGE_REPLY_TO_COLUMN));
425a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        final Collection<String> toAddresses;
4264a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4274a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // If this is a reply, the Cc list is empty. If this is a reply-all, the
4284a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // Cc list is the union of the To and Cc recipients of the original
4294a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // message, excluding the current user's email address and any addresses
430a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        // already on the To list.
431a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (action == ComposeActivity.REPLY) {
432a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            toAddresses = initToRecipients(account, accountEmail, fromAddress,
433a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira                    replytoAddresses, new String[0]);
434a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            addToAddresses(toAddresses);
435a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        } else if (action == ComposeActivity.REPLY_ALL) {
4364a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            final Set<String> ccAddresses = Sets.newHashSet();
437a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            toAddresses = initToRecipients(account, accountEmail, fromAddress,
438a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira                    replytoAddresses, new String[0]);
4394a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addRecipients(accountEmail, ccAddresses, sentToAddresses);
4404a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addRecipients(accountEmail, ccAddresses, Utils.splitCommaSeparatedString(refMessage
4414a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    .getString(UIProvider.MESSAGE_CC_COLUMN)));
4424a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addCcAddresses(ccAddresses, toAddresses);
4434a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4444a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4454a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4464a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addToAddresses(Collection<String> addresses) {
4474a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        addAddressesToList(addresses, mTo);
4484a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4494a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4504a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addCcAddresses(Collection<String> addresses, Collection<String> toAddresses) {
4514a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        addCcAddressesToList(tokenizeAddressList(addresses), tokenizeAddressList(toAddresses),
4524a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                mCc);
4534a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4544a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4554a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
4564a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected void addCcAddressesToList(List<Rfc822Token[]> addresses,
4574a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            List<Rfc822Token[]> compareToList, RecipientEditTextView list) {
4584a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        String address;
4594a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4604a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        HashSet<String> compareTo = convertToHashSet(compareToList);
4614a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (Rfc822Token[] tokens : addresses) {
4624a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            for (int i = 0; i < tokens.length; i++) {
4634a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                address = tokens[i].toString();
4644a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // Check if this is a duplicate:
4654a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                if (!compareTo.contains(tokens[i].getAddress())) {
4664a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    // Get the address here
4674a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                    list.append(address + END_TOKEN);
4684a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                }
4694a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
4704a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4714a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4724a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4734a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private HashSet<String> convertToHashSet(List<Rfc822Token[]> list) {
4744a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        HashSet<String> hash = new HashSet<String>();
4754a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (Rfc822Token[] tokens : list) {
4764a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            for (int i = 0; i < tokens.length; i++) {
4774a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                hash.add(tokens[i].getAddress());
4784a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
4794a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4804a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return hash;
4814a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4824a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4834a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected List<Rfc822Token[]> tokenizeAddressList(Collection<String> addresses) {
4844a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        @VisibleForTesting
4854a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        List<Rfc822Token[]> tokenized = new ArrayList<Rfc822Token[]>();
4864a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4874a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String address: addresses) {
4884a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            tokenized.add(Rfc822Tokenizer.tokenize(address));
4894a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4904a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return tokenized;
4914a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4924a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
4934a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
4944a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    void addAddressesToList(Collection<String> addresses, RecipientEditTextView list) {
4954a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String address : addresses) {
4964a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            addAddressToList(address, list);
4974a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
4984a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
4994a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5004a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private void addAddressToList(String address, RecipientEditTextView list) {
5014a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        if (address == null || list == null)
5024a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            return;
5034a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5044a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);
5054a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5064a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (int i = 0; i < tokens.length; i++) {
5074a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            list.append(tokens[i] + END_TOKEN);
5084a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5094a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5104a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5114a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    @VisibleForTesting
5124a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    protected Collection<String> initToRecipients(String account, String accountEmail,
5134a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            String senderAddress, String[] replyToAddresses, String[] inToAddresses) {
5144a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // The To recipient is the reply-to address specified in the original
5154a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // message, unless it is:
5164a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // the current user OR a custom from of the current user, in which case
5174a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // it's the To recipient list of the original message.
5184a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        // OR missing, in which case use the sender of the original message
5194a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        Set<String> toAddresses = Sets.newHashSet();
5204a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira        Address sender = Address.getEmailAddress(senderAddress);
5214a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira        if (sender != null && sender.getAddress().equalsIgnoreCase(account)) {
5224a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // The sender address is this account, so reply acts like reply all.
5234a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            toAddresses.addAll(Arrays.asList(inToAddresses));
5244a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        } else if (replyToAddresses != null && replyToAddresses.length != 0) {
5254a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            toAddresses.addAll(Arrays.asList(replyToAddresses));
5264a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        } else {
5274a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // Check to see if the sender address is one of the user's custom
5284a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // from addresses.
5294a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira            if (senderAddress != null && sender != null
5304a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira                    && !accountEmail.equalsIgnoreCase(sender.getAddress())) {
5314a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // Replying to the sender of the original message is the most
5324a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // common case.
5334a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                toAddresses.add(senderAddress);
5344a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            } else {
5354a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // This happens if the user replies to a message they originally
5364a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // wrote. In this case, "reply" really means "re-send," so we
5374a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // target the original recipients. This works as expected even
5384a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                // if the user sent the original message to themselves.
5394a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                toAddresses.addAll(Arrays.asList(inToAddresses));
5404a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
5414a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
5424a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        return toAddresses;
5434a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    }
5444a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira
5454a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira    private static void addRecipients(String account, Set<String> recipients, String[] addresses) {
5464a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        for (String email : addresses) {
5474a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // Do not add this account, or any of the custom froms, to the list
5484a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            // of recipients.
5494a20b708dd9ff3fb2b96f29b0b59012194c6fa34Mindy Pereira            final String recipientAddress = Address.getEmailAddress(email).getAddress();
5504a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            if (!account.equalsIgnoreCase(recipientAddress)) {
5514a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira                recipients.add(email.replace("\"\"", ""));
5524a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira            }
5534a27ea93a9eb05558f97a05a5342afb10f3d92ddMindy Pereira        }
55446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
55546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
55646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    private void setSubject(Cursor refMessage, int action) {
55746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String subject = refMessage.getString(UIProvider.MESSAGE_SUBJECT_COLUMN);
55846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String prefix;
55946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        String correctedSubject = null;
56046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (action == ComposeActivity.COMPOSE) {
56146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = "";
56246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else if (action == ComposeActivity.FORWARD) {
56346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = getString(R.string.forward_subject_label);
56446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
56546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            prefix = getString(R.string.reply_subject_label);
56646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
56746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
56846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        // Don't duplicate the prefix
56946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (subject.toLowerCase().startsWith(prefix.toLowerCase())) {
57046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            correctedSubject = subject;
57146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
57246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            correctedSubject = String
57346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira                    .format(getString(R.string.formatted_subject), prefix, subject);
57446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
57546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        mSubject.setText(correctedSubject);
57646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
57746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
578eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void setupRecipients() {
579eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mTo = setupRecipients(R.id.to);
580eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mCc = setupRecipients(R.id.cc);
581eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBcc = setupRecipients(R.id.bcc);
582eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
583eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
584c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira    private RecipientEditTextView setupRecipients(int id) {
585c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        RecipientEditTextView view = (RecipientEditTextView) findViewById(id);
58633fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        String accountName = mAccount.name;
58733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira        view.setAdapter(new RecipientAdapter(this, accountName));
588c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        view.setTokenizer(new Rfc822Tokenizer());
58982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mValidator == null) {
59033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            int offset = accountName.indexOf("@") + 1;
59133fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            String account = accountName;
592c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira            if (offset > -1) {
59333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                account = account.substring(accountName.indexOf("@") + 1);
594c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira            }
59582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mValidator = new Rfc822Validator(account);
596c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        }
59782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        view.setValidator(mValidator);
598c17d073dd43871df36ea70b3dea55963bdb98384Mindy Pereira        return view;
5998e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    }
6008e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira
6018e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    @Override
6028e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    public void onClick(View v) {
6038e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        int id = v.getId();
6048e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        switch (id) {
605a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira            case R.id.add_cc_bcc:
6068e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                // Verify that cc/ bcc aren't showing.
6078e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                // Animate in cc/bcc.
608a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira                showCcBccViews();
6098e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira                break;
6108e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira        }
6118e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira    }
612b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira
613b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    @Override
614b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    public boolean onCreateOptionsMenu(Menu menu) {
615b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        super.onCreateOptionsMenu(menu);
616b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        MenuInflater inflater = getMenuInflater();
617b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        inflater.inflate(R.menu.compose_menu, menu);
61882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSave = menu.findItem(R.id.save);
61982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mSend = menu.findItem(R.id.send);
620b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        return true;
621b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    }
622b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira
623b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    @Override
624ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    public boolean onPrepareOptionsMenu(Menu menu) {
625ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        MenuItem ccBcc = menu.findItem(R.id.add_cc_bcc);
626ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (ccBcc != null) {
627ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            // Its possible there is a menu item OR a button.
628ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            boolean ccFieldVisible = mCc.isShown();
629ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            boolean bccFieldVisible = mBcc.isShown();
630ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            if (!ccFieldVisible || !bccFieldVisible) {
631ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setVisible(true);
632ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setTitle(getString(!ccFieldVisible ? R.string.add_cc_label
633ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                        : R.string.add_bcc_label));
634ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            } else {
635ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                ccBcc.setVisible(false);
636ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            }
637ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
638ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        return true;
639ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    }
640ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira
641ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    @Override
642b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    public boolean onOptionsItemSelected(MenuItem item) {
643b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        int id = item.getItemId();
644b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        boolean handled = false;
645b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        switch (id) {
6467b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira            case R.id.add_attachment:
647013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira                doAttach();
6487b56a61174eeb202eea468b7f68b79729737ded2Mindy Pereira                break;
649ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            case R.id.add_cc_bcc:
650ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira                showCcBccViews();
651b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira                handled = true;
652b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira                break;
65333fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            case R.id.save:
654eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                doSave(true, false);
65533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                handled = true;
65633fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                break;
65733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            case R.id.send:
65833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                doSend();
65933fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                handled = true;
66033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira                break;
661b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        }
662b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira        return !handled ? super.onOptionsItemSelected(item) : handled;
663b47f3e286bcf78e85ab2d3389e38e99020d0634fMindy Pereira    }
664326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
66533fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    private void doSend() {
66682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        sendOrSaveWithSanityChecks(false, true, false);
66733fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    }
66833fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira
669eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void doSave(boolean showToast, boolean resetIME) {
670eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        sendOrSaveWithSanityChecks(true, showToast, false);
671eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (resetIME) {
672eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // Clear the IME composing suggestions from the body.
673eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            BaseInputConnection.removeComposingSpans(mBodyView.getEditableText());
674eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
67582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
67682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
67782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ interface SendOrSaveCallback {
67882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask);
67982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void notifyMessageIdAllocated(SendOrSaveMessage message, long messageId);
68082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public long getMessageId();
68182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void sendOrSaveFinished(SendOrSaveTask sendOrSaveTask, boolean success);
68282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
68382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
68482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ static class SendOrSaveTask implements Runnable {
68582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        private final Context mContext;
68682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        private final SendOrSaveCallback mSendOrSaveCallback;
68782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        @VisibleForTesting
68882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final SendOrSaveMessage mSendOrSaveMessage;
68982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
69082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public SendOrSaveTask(Context context, SendOrSaveMessage message,
69182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                SendOrSaveCallback callback) {
69282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mContext = context;
69382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveCallback = callback;
69482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveMessage = message;
69582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
69682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
69782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        @Override
69882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public void run() {
69982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            final SendOrSaveMessage message = mSendOrSaveMessage;
70082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
70182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            final Account selectedAccount = message.mSelectedAccount;
70282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            long messageId = mSendOrSaveCallback.getMessageId();
70382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // If a previous draft has been saved, in an account that is different
70482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // than what the user wants to send from, remove the old draft, and treat this
70582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // as a new message
70682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (!selectedAccount.equals(message.mAccount)) {
70782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                if (messageId != UIProvider.INVALID_MESSAGE_ID) {
70882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    ContentResolver resolver = mContext.getContentResolver();
70982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    ContentValues values = new ContentValues();
71082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    values.put(BaseColumns._ID, messageId);
711eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    if (!TextUtils.isEmpty(selectedAccount.expungeMessageUri)) {
712eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                        resolver.update(Uri.parse(selectedAccount.expungeMessageUri), values, null,
713eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                                null);
714eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                    }
71582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    // reset messageId to 0, so a new message will be created
71682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    messageId = UIProvider.INVALID_MESSAGE_ID;
71782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                }
71882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
71982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
72082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            final long messageIdToSave = messageId;
72182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            int newDraftId = -1;
72282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (messageIdToSave != UIProvider.INVALID_MESSAGE_ID) {
72382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mContext.getContentResolver().update(
72482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        Uri.parse(message.mSave ? selectedAccount.saveDraftUri
72582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                : selectedAccount.sendMessageUri), message.mValues, null, null);
72682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            } else {
72782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                newDraftId = mContext.getContentResolver().update(
72882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        Uri.parse(message.mSave ? selectedAccount.saveDraftUri
72982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                : selectedAccount.sendMessageUri), message.mValues, null, null);
73082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
73182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // Broadcast notification that a new message id has been
73282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // allocated
73382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mSendOrSaveCallback.notifyMessageIdAllocated(message, newDraftId);
73482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
73582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
73682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (!message.mSave) {
73782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
73882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        (String) message.mValues.get(UIProvider.MessageColumns.TO));
73982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
74082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        (String) message.mValues.get(UIProvider.MessageColumns.CC));
74182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                UIProvider.incrementRecipientsTimesContacted(mContext,
74282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        (String) message.mValues.get(UIProvider.MessageColumns.BCC));
74382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
74482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSendOrSaveCallback.sendOrSaveFinished(SendOrSaveTask.this, true);
74582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
74682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
74782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
74882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // Array of the outstanding send or save tasks.  Access is synchronized
74982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    // with the object itself
75082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /* package for testing */
75182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    ArrayList<SendOrSaveTask> mActiveTasks = Lists.newArrayList();
75282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private int mRequestId;
75382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private long mDraftId;
75482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
75582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /*package*/ static class SendOrSaveMessage {
75682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final Account mAccount;
75782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final Account mSelectedAccount;
75882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final ContentValues mValues;
75982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final long mRefMessageId;
76082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final boolean mSave;
76182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        final int mRequestId;
76282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
76382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        public SendOrSaveMessage(Account account, Account selectedAccount, ContentValues values,
76482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                long refMessageId, boolean save) {
76582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mAccount = account;
76682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSelectedAccount = selectedAccount;
76782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mValues = values;
76882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRefMessageId = refMessageId;
76982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave = save;
77082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRequestId = mValues.hashCode() ^ hashCode();
77182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
77282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
77382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        int requestId() {
77482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            return mRequestId;
77582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
77682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
77782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
77882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
77982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the to recipients.
78082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
78182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getToAddresses() {
78282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mTo);
78382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
78482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
78582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
78682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the cc recipients.
78782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
78882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getCcAddresses() {
78982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mCc);
79082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
79182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
79282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
79382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Get the bcc recipients.
79482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
79582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getBccAddresses() {
79682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return getAddressesFromList(mBcc);
79782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
79882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
79982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public String[] getAddressesFromList(RecipientEditTextView list) {
80082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (list == null) {
80182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            return new String[0];
80282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
80382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(list.getText());
80482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        int count = tokens.length;
80582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        String[] result = new String[count];
80682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        for (int i = 0; i < count; i++) {
80782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            result[i] = tokens[i].toString();
80882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
80982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return result;
81082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
81182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
81282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
81382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Check for invalid email addresses.
81482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param to String array of email addresses to check.
81582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param wrongEmailsOut Emails addresses that were invalid.
81682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
81782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void checkInvalidEmails(String[] to, List<String> wrongEmailsOut) {
81882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        for (String email : to) {
81982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (!mValidator.isValid(email)) {
82082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                wrongEmailsOut.add(email);
82182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
82282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
82382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
82482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
82582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
82682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Show an error because the user has entered an invalid recipient.
82782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param message
82882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
82982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void showRecipientErrorDialog(String message) {
83082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // Only 1 invalid recipients error dialog should be allowed up at a
83182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // time.
83282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mRecipientErrorDialog != null) {
83382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mRecipientErrorDialog.dismiss();
83482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
83582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mRecipientErrorDialog = new AlertDialog.Builder(this).setMessage(message).setTitle(
83682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                R.string.recipient_error_dialog_title)
83782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setIconAttribute(android.R.attr.alertDialogIcon)
83882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setCancelable(false)
83982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                .setPositiveButton(
84082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        R.string.ok, new Dialog.OnClickListener() {
84182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                            public void onClick(DialogInterface dialog, int which) {
84282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // after the user dismisses the recipient error
84382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // dialog we want to make sure to refocus the
84482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // recipient to field so they can fix the issue
84582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                // easily
84682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                if (mTo != null) {
84782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                    mTo.requestFocus();
84882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                }
84982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                mRecipientErrorDialog = null;
85082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                            }
85182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        }).show();
85282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
85382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
85482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
85582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Update the state of the UI based on whether or not the current draft
85682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * needs to be saved and the message is not empty.
85782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
858eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void updateSaveUi() {
85982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSave != null) {
86082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave.setEnabled((shouldSave() && !isBlank()));
86182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
86282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
86382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
86482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
86582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Returns true if we need to save the current draft.
86682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
86782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private boolean shouldSave() {
86882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        synchronized (mDraftIdLock) {
86982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // The message should only be saved if:
87082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // It hasn't been sent AND
87182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // Some text has been added to the message OR
87282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // an attachment has been added or removed
873eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return (mTextChanged || mAttachmentsChanged ||
87482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                    (mReplyFromChanged && !isBlank()));
87582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
87682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
87782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
87882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
87982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Check if the ComposeArea believes all fields are blank.
88082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @return boolean
88182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
88282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public boolean isBlank() {
88382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        return mSubject.getText().length() == 0
884eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira               && mBodyView.getText().length() == 0
88582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mTo.length() == 0
88682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mCc.length() == 0
88782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mBcc.length() == 0
88882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               && mAttachmentsView.getAttachments().size() == 0;
88982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
89082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
89182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
89282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Allows any changes made by the user to be ignored. Called when the user
89382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * decides to discard a draft.
89482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
89582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private void discardChanges() {
89682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mTextChanged = false;
897eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsChanged = false;
89882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mReplyFromChanged = false;
89933fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira    }
90033fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira
90182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
90282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    *
90382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @param body
90482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @param save
90582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @param showToast
90682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @return Whether the send or save succeeded.
90782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
90882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   protected boolean sendOrSaveWithSanityChecks(final boolean save,
90982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               final boolean showToast, final boolean orientationChanged) {
91082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       String[] to, cc, bcc;
911eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira       Editable body = mBodyView.getEditableText();
91282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
91382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (orientationChanged) {
91482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           to = cc = bcc = new String[0];
91582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       } else {
91682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           to = getToAddresses();
91782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           cc = getCcAddresses();
91882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           bcc = getBccAddresses();
91982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
92082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
92182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Don't let the user send to nobody (but it's okay to save a message with no recipients)
92282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (!save && (to.length == 0 && cc.length == 0 && bcc.length == 0)) {
92382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           showRecipientErrorDialog(getString(R.string.recipient_needed));
92482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           return false;
92582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
92682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
92782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       List<String> wrongEmails = new ArrayList<String>();
92882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (!save) {
92982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           checkInvalidEmails(to, wrongEmails);
93082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           checkInvalidEmails(cc, wrongEmails);
93182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           checkInvalidEmails(bcc, wrongEmails);
93282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
93382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
93482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Don't let the user send an email with invalid recipients
93582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (wrongEmails.size() > 0) {
93682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           String errorText =
93782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               String.format(getString(R.string.invalid_recipient), wrongEmails.get(0));
93882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           showRecipientErrorDialog(errorText);
93982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           return false;
94082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
94182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
94282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
94382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           public void onClick(DialogInterface dialog, int which) {
944eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira               sendOrSave(mBodyView.getEditableText(), save, showToast, orientationChanged);
94582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           }
94682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       };
94782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
94882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Show a warning before sending only if there are no attachments.
94982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (!save) {
95082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           if (mAttachmentsView.getAttachments().isEmpty() && showEmptyTextWarnings()) {
95182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               boolean warnAboutEmptySubject = isSubjectEmpty();
95282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               boolean emptyBody = TextUtils.getTrimmedLength(body) == 0;
95382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
95482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // A warning about an empty body may not be warranted when
95582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // forwarding mails, since a common use case is to forward
95682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // quoted text and not append any more text.
95782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               boolean warnAboutEmptyBody = emptyBody && (!mForward || isBodyEmpty());
95882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
95982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // When we bring up a dialog warning the user about a send,
96082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // assume that they accept sending the message. If they do not, the dialog
96182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // listener is required to enable sending again.
96282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               if (warnAboutEmptySubject) {
96382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   showSendConfirmDialog(R.string.confirm_send_message_with_no_subject, listener);
96482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   return true;
96582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
96682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
96782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               if (warnAboutEmptyBody) {
96882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   showSendConfirmDialog(R.string.confirm_send_message_with_no_body, listener);
96982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   return true;
97082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
97182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           }
97282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           // Ask for confirmation to send (if always required)
97382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           if (showSendConfirmation()) {
97482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               showSendConfirmDialog(R.string.confirm_send_message, listener);
97582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               return true;
97682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           }
97782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
97882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
97982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       sendOrSave(body, save, showToast, false);
98082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return true;
98182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
98282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
98382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /**
98482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * Returns a boolean indicating whether warnings should be shown for empty
98582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * subject and body fields
98682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    *
98782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @return True if a warning should be shown for empty text fields
98882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
98982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   protected boolean showEmptyTextWarnings() {
99082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return mAttachmentsView.getAttachments().size() == 0;
99182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
99282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
99382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /**
99482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * Returns a boolean indicating whether the user should confirm each send
99582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    *
99682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @return True if a warning should be on each send
99782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
99882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   protected boolean showSendConfirmation() {
99982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // TODO: read user preference for whether or not to show confirm send dialog.
100082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return true;
100182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
100282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
100382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   private void showSendConfirmDialog(int messageId, DialogInterface.OnClickListener listener) {
100482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (mSendConfirmDialog != null) {
100582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           mSendConfirmDialog.dismiss();
100682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           mSendConfirmDialog = null;
100782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
100882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       mSendConfirmDialog = new AlertDialog.Builder(this)
100982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setMessage(messageId)
101082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setTitle(R.string.confirm_send_title)
101182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setIconAttribute(android.R.attr.alertDialogIcon)
101282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setPositiveButton(R.string.send, listener)
101382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setNegativeButton(R.string.cancel, this)
101482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .setCancelable(false)
101582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               .show();
101682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
101782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
101882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /**
101982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * Returns whether the ComposeArea believes there is any text in the body of
102082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * the composition. TODO: When ComposeArea controls the Body as well, add
102182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * that here.
102282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
102382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   public boolean isBodyEmpty() {
102482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return !mQuotedTextView.isTextIncluded();
102582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
102682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
102782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /**
102882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * Test to see if the subject is empty.
102982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * @return boolean.
103082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
103182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   // TODO: this will likely go away when composeArea.focus() is implemented
103282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   // after all the widget control is moved over.
103382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   public boolean isSubjectEmpty() {
103482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return TextUtils.getTrimmedLength(mSubject.getText()) == 0;
103582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
103682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
103782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /* package */
103882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   static int sendOrSaveInternal(Context context, final Account account,
103982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           final Account selectedAccount, String fromAddress, final Spanned body, final String[] to,
104082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           final String[] cc, final String[] bcc, final String subject,
104182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           final CharSequence quotedText, final List<Attachment> attachments,
104282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           final long refMessageId, SendOrSaveCallback callback, Handler handler, boolean save,
104382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           boolean forward) {
104482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       ContentValues values = new ContentValues();
104582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
104682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       MessageModification.putToAddresses(values, to);
104782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       MessageModification.putCcAddresses(values, cc);
104882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       MessageModification.putBccAddresses(values, bcc);
104982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
105082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       MessageModification.putSubject(values, subject);
105182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       String htmlBody = Html.toHtml(body);
105282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       boolean includeQuotedText = !TextUtils.isEmpty(quotedText);
105382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       StringBuilder fullBody = new StringBuilder(htmlBody);
105482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (includeQuotedText) {
105582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           if (forward) {
105682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // forwarded messages get full text in HTML from client
105782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               fullBody.append(quotedText);
105882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               MessageModification.putForward(values, forward);
105982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           } else {
106082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               // replies get full quoted text from server - HTMl gets converted to text for now
106182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               final String text = quotedText.toString();
1062913897e6879f5f2713b56f13022bd784cf58a585Mindy Pereira               if (QuotedTextView.containsQuotedText(text)) {
1063913897e6879f5f2713b56f13022bd784cf58a585Mindy Pereira                   int pos = QuotedTextView.getQuotedTextOffset(text);
106482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   fullBody.append(text.substring(0, pos));
106582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   int quoteStartPos = fullBody.length();
106682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   MessageModification.putForward(values, forward);
106782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   MessageModification.putIncludeQuotedText(values, includeQuotedText);
106882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   MessageModification.putQuoteStartPos(values, quoteStartPos);
106982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               } else {
107082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   LogUtils.w(LOG_TAG, "Couldn't find quoted text");
107182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   // This shouldn't happen, but just use what we have,
107282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   //  and don't do server-side expansion
107382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   fullBody.append(text);
107482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
107582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           }
107682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
107782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       MessageModification.putBody(values, fullBody.toString());
107882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
107982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       SendOrSaveMessage sendOrSaveMessage = new SendOrSaveMessage(account, selectedAccount,
108082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               values, refMessageId, save);
108182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       SendOrSaveTask sendOrSaveTask = new SendOrSaveTask(context, sendOrSaveMessage, callback);
108282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
108382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       callback.initializeSendOrSave(sendOrSaveTask);
108482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
108582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Do the send/save action on the specified handler to avoid possible ANRs
108682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       handler.post(sendOrSaveTask);
108782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
108882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       return sendOrSaveMessage.requestId();
108982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
109082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
109182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   private void sendOrSave(Spanned body, boolean save, boolean showToast,
109282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           boolean orientationChanged) {
109382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Check if user is a monkey. Monkeys can compose and hit send
109482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // button but are not allowed to send anything off the device.
109582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (!save && ActivityManager.isUserAMonkey()) {
109682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           return;
109782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
109882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
109982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       String[] to, cc, bcc;
110082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (orientationChanged) {
110182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           to = cc = bcc = new String[0];
110282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       } else {
110382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           to = getToAddresses();
110482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           cc = getCcAddresses();
110582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           bcc = getBccAddresses();
110682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
110782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
110882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
110982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       SendOrSaveCallback callback = new SendOrSaveCallback() {
111082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               private long mDraftId;
111182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            private int mRestoredRequestId;
111282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
111382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask) {
111482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   synchronized(mActiveTasks) {
111582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       int numTasks = mActiveTasks.size();
111682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       if (numTasks == 0) {
111782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           // Start service so we won't be killed if this app is put in the
111882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           // background.
111982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           startService(new Intent(ComposeActivity.this, EmptyService.class));
112082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       }
112182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
112282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       mActiveTasks.add(sendOrSaveTask);
112382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
112482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   if (sTestSendOrSaveCallback != null) {
112582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       sTestSendOrSaveCallback.initializeSendOrSave(sendOrSaveTask);
112682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
112782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
112882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
112982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               public void notifyMessageIdAllocated(SendOrSaveMessage message, long messageId) {
113082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   synchronized(mDraftIdLock) {
113182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       mDraftId = messageId;
113282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       sRequestMessageIdMap.put(message.requestId(), messageId);
113382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
113482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // Cache request message map, in case the process is killed
113582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       saveRequestMap();
113682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
113782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   if (sTestSendOrSaveCallback != null) {
113882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       sTestSendOrSaveCallback.notifyMessageIdAllocated(message, messageId);
113982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
114082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
114182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
114282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               public long getMessageId() {
114382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   synchronized(mDraftIdLock) {
114482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       if (mDraftId == UIProvider.INVALID_MESSAGE_ID) {
114582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           // We don't have the message Id, check to see if we have a restored
114682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           // request id, and see if we have a message for that request.
114782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           if (mRestoredRequestId != 0) {
114882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                               Long retrievedMessageId =
114982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                       sRequestMessageIdMap.get(mRestoredRequestId);
115082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                               if (retrievedMessageId != null) {
115182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                                   mDraftId = retrievedMessageId.longValue();
115282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                               }
115382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                           }
115482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       }
115582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       return mDraftId;
115682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
115782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
115882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
115982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               public void sendOrSaveFinished(SendOrSaveTask task, boolean success) {
116082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   if (success) {
116182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // Successfully sent or saved so reset change markers
116282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       discardChanges();
116382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   } else {
116482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // A failure happened with saving/sending the draft
116582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // TODO(pwestbro): add a better string that should be used when failing to
116682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // send or save
116782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       Toast.makeText(ComposeActivity.this, R.string.send_failed,
116882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                               Toast.LENGTH_SHORT).show();
116982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
117082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
117182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   int numTasks;
117282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   synchronized(mActiveTasks) {
117382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // Remove the task from the list of active tasks
117482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       mActiveTasks.remove(task);
117582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       numTasks = mActiveTasks.size();
117682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
117782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
117882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   if (numTasks == 0) {
117982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       // Stop service so we can be killed.
118082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       stopService(new Intent(ComposeActivity.this, EmptyService.class));
118182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
118282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   if (sTestSendOrSaveCallback != null) {
118382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                       sTestSendOrSaveCallback.sendOrSaveFinished(task, success);
118482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   }
118582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               }
118682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira         };
118782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
118882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Get the selected account if the from spinner has been setup.
1189eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira       Account selectedAccount = mAccount;
1190eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira       String fromAddress = selectedAccount.name;
119182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (selectedAccount == null || fromAddress == null) {
119282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           // We don't have either the selected account or from address,
119382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           // use mAccount.
1194eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira           selectedAccount = mAccount;
1195eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira           fromAddress = mAccount.name;
119682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
119782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
119882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (mSendSaveTaskHandler == null) {
119982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           HandlerThread handlerThread = new HandlerThread("Send Message Task Thread");
120082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           handlerThread.start();
120182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
120282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           mSendSaveTaskHandler = new Handler(handlerThread.getLooper());
120382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
120482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
120582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       mRequestId = sendOrSaveInternal(this, mAccount, selectedAccount, fromAddress, body,
120682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               to, cc, bcc, mSubject.getText().toString(), mQuotedTextView.getQuotedText(),
120782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               mAttachmentsView.getAttachments(), mRefMessageId, callback, mSendSaveTaskHandler,
120882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira               save, mForward);
120982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
121082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (mRecipient != null && mRecipient.equals(mAccount.name)) {
121182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           mRecipient = selectedAccount.name;
121282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
121382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       mAccount = selectedAccount;
121482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
121582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Don't display the toast if the user is just changing the orientation, but we still
121682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // need to save the draft to the cursor because this is how we restore the attachments
121782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // when the configuration change completes.
121882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (showToast && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) {
121982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           Toast.makeText(this, save ? R.string.message_saved : R.string.sending_message,
122082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                   Toast.LENGTH_LONG).show();
122182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
122282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
122382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // Need to update variables here
122482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // because the send or save completes asynchronously even though the
122582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // toast shows right away.
122682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       discardChanges();
1227eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira       updateSaveUi();
122882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
122982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // If we are sending, finish the activity
123082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       if (!save) {
123182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira           finish();
123282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       }
123382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
123482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
123582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   /**
123682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * Save the state of the request messageid map.  This allows for the Gmail process
123782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * to be killed, but and still allow for ComposeActivity instances to be recreated
123882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    * correctly.
123982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    */
124082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   private void saveRequestMap() {
124182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira       // TODO: store the request map in user preferences.
124282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira   }
124382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1244013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    public void doAttach() {
1245013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
1246013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        i.addCategory(Intent.CATEGORY_OPENABLE);
1247013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        if (Settings.System.getInt(
1248013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira                getContentResolver(), UIProvider.getAttachmentTypeSetting(), 0) != 0) {
1249013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            i.setType("*/*");
1250013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        } else {
1251013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira            i.setType("image/*");
1252013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        }
1253013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        mAddingAttachment = true;
1254013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira        startActivityForResult(Intent.createChooser(i,
1255013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira                getText(R.string.select_attachment_type)), RESULT_PICK_ATTACHMENT);
1256013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira    }
1257013194caeba8848ba89a68c77843bed2bf7a38e5Mindy Pereira
1258ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    private void showCcBccViews() {
1259a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        mCcBccView.show(true, true, true);
1260ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        if (mCcBccButton != null) {
1261ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira            mCcBccButton.setVisibility(View.GONE);
1262ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira        }
1263ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira    }
1264ec8b0edb23c0e0b7f1a8ce9558a919be31169d45Mindy Pereira
1265326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    @Override
1266326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    public boolean onNavigationItemSelected(int position, long itemId) {
1267a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        int initialComposeMode = mComposeMode;
1268326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        if (position == ComposeActivity.REPLY) {
1269326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.REPLY;
1270326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else if (position == ComposeActivity.REPLY_ALL) {
1271326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.REPLY_ALL;
1272326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        } else if (position == ComposeActivity.FORWARD) {
1273326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            mComposeMode = ComposeActivity.FORWARD;
1274326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1275a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        if (initialComposeMode != mComposeMode) {
127633fe908589dca6a7f58aa9f56d73f74a48ac42f6Mindy Pereira            initFromRefMessage(mComposeMode, mAccount.name);
1277a26b54e3a858de409be3746e65591bb8a0d6ea9aMindy Pereira        }
1278326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        return true;
1279326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
1280326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1281326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    private class ComposeModeAdapter extends ArrayAdapter<String> {
1282326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1283326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        private LayoutInflater mInflater;
1284326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1285326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        public ComposeModeAdapter(Context context) {
1286326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            super(context, R.layout.compose_mode_item, R.id.mode, getResources()
1287326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                    .getStringArray(R.array.compose_modes));
1288326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1289326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1290326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        private LayoutInflater getInflater() {
1291326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (mInflater == null) {
1292326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                mInflater = LayoutInflater.from(getContext());
1293326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
1294326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            return mInflater;
1295326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1296326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
1297326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        @Override
1298326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        public View getView(int position, View convertView, ViewGroup parent) {
1299326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            if (convertView == null) {
1300326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira                convertView = getInflater().inflate(R.layout.compose_mode_display_item, null);
1301326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            }
1302326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            ((TextView) convertView.findViewById(R.id.mode)).setText(getItem(position));
1303326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira            return super.getView(position, convertView, parent);
1304326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        }
1305326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
130646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
130746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    @Override
130846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void onRespondInline(String text) {
130946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        appendToBody(text, false);
131046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
131146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
131246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
131346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Append text to the body of the message. If there is no existing body
131446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * text, just sets the body to text.
131546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     *
131646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param text
131746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param withSignature True to append a signature.
131846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
131946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void appendToBody(CharSequence text, boolean withSignature) {
1320eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        Editable bodyText = mBodyView.getEditableText();
132146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        if (bodyText != null && bodyText.length() > 0) {
132246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            bodyText.append(text);
132346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        } else {
132446ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira            setBody(text, withSignature);
132546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira        }
132646ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
132746ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira
132846ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    /**
132946ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * Set the body of the message.
133046ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param text
133146ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     * @param withSignature True to append a signature.
133246ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira     */
133346ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    public void setBody(CharSequence text, boolean withSignature) {
1334eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mBodyView.setText(text);
133546ce0b13c33d1ade0e4537e780b41fea5e2a6be9Mindy Pereira    }
13361a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira
13375a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    @Override
13385a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira    public void onAccountChanged() {
13395a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        Account selectedAccountInfo = mFromSpinner.getCurrentAccount();
1340eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAccount = selectedAccountInfo;
134182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
1342eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        // TODO: handle discarding attachments when switching accounts.
13435a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        // Only enable save for this draft if there is any other content
13445a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        // in the message.
13455a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        if (!isBlank()) {
13465a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira            enableSave(true);
134782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
13485a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        mReplyFromChanged = true;
13495a85e2b7485a5c407c581219fe1b44417f050529Mindy Pereira        setupRecipients();
13501a95a57f39da84cd7daf4e2161e50e518df51f13Mindy Pereira    }
135182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
135282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void enableSave(boolean enabled) {
135382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSave != null) {
135482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSave.setEnabled(enabled);
135582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
135682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
135782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
135882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void enableSend(boolean enabled) {
135982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (mSend != null) {
136082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            mSend.setEnabled(enabled);
136182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
136282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
136382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
136482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
136582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Handles button clicks from any error dialogs dealing with sending
136682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * a message.
136782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
136882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    @Override
136982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void onClick(DialogInterface dialog, int which) {
137082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        switch (which) {
137182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            case DialogInterface.BUTTON_POSITIVE: {
137282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                doDiscardWithoutConfirmation(true /* show toast */ );
137382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                break;
137482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
137582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            case DialogInterface.BUTTON_NEGATIVE: {
137682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // If the user cancels the send, re-enable the send button.
137782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                enableSend(true);
137882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                break;
137982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
138082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
138182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
138282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
138382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
138482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
138582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * Effectively discard the current message.
138682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     *
138782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * This method is either invoked from the menu or from the dialog
138882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * once the user has confirmed that they want to discard the message.
138982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * @param showToast show "Message discarded" toast if true
139082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
139182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    private void doDiscardWithoutConfirmation(boolean showToast) {
139282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        synchronized (mDraftIdLock) {
139382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            if (mDraftId != UIProvider.INVALID_MESSAGE_ID) {
139482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                ContentValues values = new ContentValues();
139582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                values.put(MessageColumns.SERVER_ID, mDraftId);
1396eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira                getContentResolver().update(Uri.parse(mAccount.expungeMessageUri),
139782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                        values, null, null);
139882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // This is not strictly necessary (since we should not try to
139982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // save the draft after calling this) but it ensures that if we
140082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // do save again for some reason we make a new draft rather than
140182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                // trying to resave an expunged draft.
140282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira                mDraftId = UIProvider.INVALID_MESSAGE_ID;
140382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            }
140482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
140582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
140682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        if (showToast) {
140782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            // Display a toast to let the user know
140882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira            Toast.makeText(this, R.string.message_discarded, Toast.LENGTH_SHORT).show();
140982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        }
141082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
141182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // This prevents the draft from being saved in onPause().
141282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        discardChanges();
141382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        finish();
141482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
141582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
141682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    /**
141782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     * This is called any time one of our text fields changes.
141882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira     */
141982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    @Override
142082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void afterTextChanged(Editable s) {
142182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        mTextChanged = true;
1422eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        updateSaveUi();
142382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
142482cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
142582cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    @Override
142682cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
142782cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // Do nothing.
142882cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
142982cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira
143082cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    @Override
143182cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    public void onTextChanged(CharSequence s, int start, int before, int count) {
143282cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira        // Do nothing.
143382cc5665cb18801022cd60cd52abcd16a50debdcMindy Pereira    }
1434eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1435eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void saveIfNeeded() {
1436eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mAccount == null) {
1437eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // We have not chosen an account yet so there's no way that we can save. This is ok,
1438eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // though, since we are saving our state before AccountsActivity is activated. Thus, the
1439eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // user has not interacted with us yet and there is no real state to save.
1440eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return;
1441eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1442eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1443eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (shouldSave()) {
1444eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            doSave(!mAddingAttachment /* show toast */, true /* reset IME */);
1445eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1446eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1447eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1448eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    private void saveIfNeededOnOrientationChanged() {
1449eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (mAccount == null) {
1450eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // We have not chosen an account yet so there's no way that we can save. This is ok,
1451eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // though, since we are saving our state before AccountsActivity is activated. Thus, the
1452eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // user has not interacted with us yet and there is no real state to save.
1453eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            return;
1454eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1455eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1456eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (shouldSave()) {
1457eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            doSaveOrientationChanged(!mAddingAttachment /* show toast */, true /* reset IME */);
1458eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1459eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1460eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1461eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    /**
1462eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira     * Save a draft if a draft already exists or the message is not empty.
1463eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira     */
1464eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void doSaveOrientationChanged(boolean showToast, boolean resetIME) {
1465eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        saveOnOrientationChanged();
1466eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        if (resetIME) {
1467eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            // Clear the IME composing suggestions from the body.
1468eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira            BaseInputConnection.removeComposingSpans(mBodyView.getEditableText());
1469eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        }
1470eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1471eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1472eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    protected boolean saveOnOrientationChanged() {
1473eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        return sendOrSaveWithSanityChecks(true, false, true);
1474eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
1475eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira
1476eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    @Override
1477eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    public void onAttachmentDeleted() {
1478eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        mAttachmentsChanged = true;
1479eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira        updateSaveUi();
1480eaea9f16b2b61eed6b62fe9a5c567bfbe0d75d80Mindy Pereira    }
14818e9305ec0bbe23cec01eb34a2aee76c55199a10aMindy Pereira}